Date: Sun, 23 May 2021 00:04:17 GMT From: Eugene Grosbein <eugen@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Subject: git: 18fa0cbfc4e9 - stable/12 - MFC r351629: sys/net/if_vlan.c: Wrap a vlan's parent's if_output in a separate function. Message-ID: <202105230004.14N04HMk060520@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch stable/12 has been updated by eugen: URL: https://cgit.FreeBSD.org/src/commit/?id=18fa0cbfc4e906fbf824651140f68d0a85c1d08f commit 18fa0cbfc4e906fbf824651140f68d0a85c1d08f Author: Matt Joras <mjoras@FreeBSD.org> AuthorDate: 2019-08-30 20:19:43 +0000 Commit: Eugene Grosbein <eugen@FreeBSD.org> CommitDate: 2021-05-22 23:59:40 +0000 MFC r351629: sys/net/if_vlan.c: Wrap a vlan's parent's if_output in a separate function. The merge is done in preparation of another merge to support 802.1ad (qinq). Original commit log follows. When a vlan interface is created, its if_output is set directly to the parent interface's if_output. This is fine in the normal case but has an unfortunate consequence if you end up with a certain combination of vlan and lagg interfaces. Consider you have a lagg interface with a single laggport member. When an interface is added to a lagg its if_output is set to lagg_port_output, which blackholes traffic from the normal networking stack but not certain frames from BPF (pseudo_AF_HDRCMPLT). If you now create a vlan with the laggport member (not the lagg interface) as its parent, its if_output is set to lagg_port_output as well. While this is confusing conceptually and likely represents a misconfigured system, it is not itself a problem. The problem arises when you then remove the lagg interface. Doing this resets the if_output of the laggport member back to its original state, but the vlan's if_output is left pointing to lagg_port_output. This gives rise to the possibility that the system will panic when e.g. bpf is used to send any frames on the vlan interface. Fix this by creating a new function, vlan_output, which simply wraps the parent's current if_output. That way when the parent's if_output is restored there is no stale usage of lagg_port_output. Reviewed by: rstone Differential Revision: D21209 (cherry picked from commit 16cf6bdbb6cb18a5af7b499034b2176a1fa0a503) --- sys/net/if_vlan.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/sys/net/if_vlan.c b/sys/net/if_vlan.c index 1f5a95cdda7c..450c4b9bbe3b 100644 --- a/sys/net/if_vlan.c +++ b/sys/net/if_vlan.c @@ -291,6 +291,8 @@ static int vlan_setflag(struct ifnet *ifp, int flag, int status, static int vlan_setflags(struct ifnet *ifp, int status); static int vlan_setmulti(struct ifnet *ifp); static int vlan_transmit(struct ifnet *ifp, struct mbuf *m); +static int vlan_output(struct ifnet *ifp, struct mbuf *m, + const struct sockaddr *dst, struct route *ro); static void vlan_unconfig(struct ifnet *ifp); static void vlan_unconfig_locked(struct ifnet *ifp, int departing); static int vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t tag); @@ -1179,6 +1181,27 @@ vlan_transmit(struct ifnet *ifp, struct mbuf *m) return (error); } +static int +vlan_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst, + struct route *ro) +{ + struct epoch_tracker et; + struct ifvlan *ifv; + struct ifnet *p; + + NET_EPOCH_ENTER(et); + ifv = ifp->if_softc; + if (TRUNK(ifv) == NULL) { + NET_EPOCH_EXIT(et); + m_freem(m); + return (ENETDOWN); + } + p = PARENT(ifv); + NET_EPOCH_EXIT(et); + return p->if_output(ifp, m, dst, ro); +} + + /* * The ifp->if_qflush entry point for vlan(4) is a no-op. */ @@ -1392,13 +1415,18 @@ vlan_config(struct ifvlan *ifv, struct ifnet *p, uint16_t vid) */ ifp->if_mtu = p->if_mtu - ifv->ifv_mtufudge; ifp->if_baudrate = p->if_baudrate; - ifp->if_output = p->if_output; ifp->if_input = p->if_input; ifp->if_resolvemulti = p->if_resolvemulti; ifp->if_addrlen = p->if_addrlen; ifp->if_broadcastaddr = p->if_broadcastaddr; ifp->if_pcp = ifv->ifv_pcp; + /* + * We wrap the parent's if_output using vlan_output to ensure that it + * can't become stale. + */ + ifp->if_output = vlan_output; + /* * Copy only a selected subset of flags from the parent. * Other flags are none of our business.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202105230004.14N04HMk060520>