From 838c8c47860a0203130bd558d507a0565a2eba8f Mon Sep 17 00:00:00 2001 From: Zhenlei Huang Date: Wed, 23 Aug 2023 17:48:12 +0800 Subject: [PATCH] net: Do not overwrite if_vlan's PCP In commit c7cffd65c5d8 the function ether_8021q_frame() was slightly refactored to use pointer of struct ether_8021q_tag as parameter qtag to include the new option proto. It is wrong to write to qtag->pcp as it will effectively change the memory that qtag points to. Unfortunately the transmit routine of if_vlan parses pointer of the member ifv_qtag of its softc which stores vlan interface's PCP internally, when transmitting mbufs that contains PCP the vlan interface's PCP will get overwritten. Fix by operating on a local copy of qtag->pcp. Also mark 'struct ether_8021q_tag' as const so that compilers can pick up such kind of bug. PR: 273304 Reviewed by: kp Fixes: c7cffd65c5d85 Add support for stacked VLANs (IEEE 802.1ad, AKA Q-in-Q) MFC after: 3 days Differential Revision: https://reviews.freebsd.org/D39505 --- sys/net/ethernet.h | 2 +- sys/net/if_ethersubr.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h index 8a4b9336b9cc..fca6748a0da7 100644 --- a/sys/net/ethernet.h +++ b/sys/net/ethernet.h @@ -446,7 +446,7 @@ void ether_vlan_mtap(struct bpf_if *, struct mbuf *, void *, u_int); struct mbuf *ether_vlanencap_proto(struct mbuf *, uint16_t, uint16_t); bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, - struct ifnet *p, struct ether_8021q_tag *); + struct ifnet *p, const struct ether_8021q_tag *); void ether_gen_addr(struct ifnet *ifp, struct ether_addr *hwaddr); static __inline struct mbuf *ether_vlanencap(struct mbuf *m, uint16_t tag) diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 25f4d151866e..63ea5e7591c2 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -1400,11 +1400,12 @@ SYSCTL_INT(_net_link_vlan, OID_AUTO, mtag_pcp, CTLFLAG_RW | CTLFLAG_VNET, bool ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, - struct ether_8021q_tag *qtag) + const struct ether_8021q_tag *qtag) { struct m_tag *mtag; int n; uint16_t tag; + uint8_t pcp = qtag->pcp; static const char pad[8]; /* just zeros */ /* @@ -1437,7 +1438,7 @@ ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, * If PCP is set in mbuf, use it */ if ((*mp)->m_flags & M_VLANTAG) { - qtag->pcp = EVL_PRIOFTAG((*mp)->m_pkthdr.ether_vtag); + pcp = EVL_PRIOFTAG((*mp)->m_pkthdr.ether_vtag); } /* @@ -1451,7 +1452,7 @@ ether_8021q_frame(struct mbuf **mp, struct ifnet *ife, struct ifnet *p, MTAG_8021Q_PCP_OUT, NULL)) != NULL) tag = EVL_MAKETAG(qtag->vid, *(uint8_t *)(mtag + 1), 0); else - tag = EVL_MAKETAG(qtag->vid, qtag->pcp, 0); + tag = EVL_MAKETAG(qtag->vid, pcp, 0); if ((p->if_capenable & IFCAP_VLAN_HWTAGGING) && (qtag->proto == ETHERTYPE_VLAN)) { (*mp)->m_pkthdr.ether_vtag = tag;