diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -2715,7 +2715,6 @@ struct ifnet *src_if, *dst_if, *ifp; struct ether_header *eh; uint8_t *dst; - int error; ether_vlanid_t vlan; NET_EPOCH_ASSERT(); @@ -2734,18 +2733,6 @@ eh = mtod(m, struct ether_header *); dst = eh->ether_dhost; - /* If the interface is learning, record the address. */ - if (sbif->bif_flags & IFBIF_LEARNING) { - error = bridge_rtupdate(sc, eh->ether_shost, vlan, - sbif, 0, IFBAF_DYNAMIC); - /* - * If the interface has addresses limits then deny any source - * that is not in the cache. - */ - if (error && sbif->bif_addrmax) - goto drop; - } - if ((sbif->bif_flags & IFBIF_STP) != 0 && sbif->bif_stp.bp_state == BSTP_IFSTATE_LEARNING) goto drop; @@ -2856,6 +2843,147 @@ m_freem(m); } +/* + * Handle a frame which is destined for the host. If a non-NULL mbuf is + * returned, it should be passed back to ether_input(). + */ +static struct mbuf * +bridge_handle_for_host(struct bridge_softc *sc, struct ifnet *ifp, + struct mbuf *m) +{ + struct ifnet *bifp; + ether_vlanid_t vlan; + + NET_EPOCH_ASSERT(); + + bifp = sc->sc_ifp; + vlan = VLANTAGOF(m); + + /* Tap the frame on the bridge interface. */ + ETHER_BPF_MTAP(bifp, m); + + if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); + if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); + +#ifdef DEV_NETMAP + /* Hand the packet over to netmap if necessary. */ + if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0 && + ((m)->m_flags & M_BRIDGE_INJECT) == 0) { + (ifp)->if_input(ifp, m); + return (NULL); + } +#endif + + /* + * If the packet has a VLAN tag and there's a VLAN trunk configured + * on the bridge, this packet is intended for an SVI, so pass it to + * vlan_input() to handle. + */ + if (vlan != 0) { + if (bifp->if_vlantrunk == NULL) { + m_freem(m); + return (NULL); + } + + (*vlan_input_p)(ifp, m); + return (NULL); + } + + /* + * Return the frame to ether_input(). + */ + return (m); +} + +/* + * Given an mbuf and the incoming interface, determine if this frame should be + * handler locally, and if so, handle it. Returns true if the frame was local. + */ +static bool +bridge_try_local_if(struct bridge_softc *sc, struct ifnet *iface, + struct mbuf **mp) +{ + struct ether_header *eh; + bool local_dst, local_src; + + NET_EPOCH_ASSERT(); + + /* + * Frames received on a gif(4) interface are never considered local. + * XXX: Why not? + */ + if (iface->if_type == IFT_GIF) + return (false); + + eh = mtod(*mp, struct ether_header *); + + /* See if this frame is destined for the given interface. */ + if (memcmp(IF_LLADDR(iface), eh->ether_dhost, ETHER_ADDR_LEN) == 0) + local_dst = true; +#if defined(INET) || defined(INET6) + else if (iface->if_carp && (*carp_forus_p)(iface, eh->ether_dhost)) + local_dst = true; +#endif + else + local_dst = false; + + if (local_dst) { + (*mp)->m_pkthdr.rcvif = iface; + *mp = bridge_handle_for_host(sc, iface, *mp); + return true; + } + + /* See if this frame was sent from the given interface. */ + if (memcmp(IF_LLADDR(iface), eh->ether_shost, ETHER_ADDR_LEN) == 0) + local_src = true; +#if defined(INET) || defined(INET6) + else if (iface->if_carp && (*carp_forus_p)(iface, eh->ether_shost)) + local_src = true; +#endif + else + local_src = false; + + if (local_src) { + m_freem(*mp); + *mp = NULL; + return true; + } + + return (false); +} + +/* + * Try to handle this as a local packet by checking all the interfaces it + * might be local for. + */ +static bool +bridge_try_local(struct bridge_softc *sc, struct ifnet *iface, + struct mbuf **mp) +{ + struct bridge_iflist *bif; + + /* Check the bridge interface itself first. */ + if (bridge_try_local_if(sc, sc->sc_ifp, mp)) + return (true); + + /* If member_ifaddrs is disabled, that's it; it's not local. */ + if (!V_member_ifaddrs) + return (false); + + /* Check the interface the frame arrived on. */ + if (bridge_try_local_if(sc, iface, mp)) + return (true); + + /* Check the other bridge members. */ + CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { + if (bridge_try_local_if(sc, bif->bif_ifp, mp)) + return (true); + } + + /* It's not local. */ + return (false); +} + /* * bridge_input: * @@ -2866,15 +2994,27 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc = NULL; - struct bridge_iflist *bif, *bif2; + struct bridge_iflist *bif; struct ifnet *bifp; struct ether_header *eh; struct mbuf *mc, *mc2; ether_vlanid_t vlan; - int error; NET_EPOCH_ASSERT(); + vlan = VLANTAGOF(m); + + /* + * If this frame has a VLAN tag, and it was not received on a bridge + * interface, and the receiving interface has a vlan(4) trunk, then + * it is is destined for vlan(4), not for us. This means if vlan(4) + * and bridge(4) are configured on the same interface, vlan(4) is + * preferred, which is what users typically expect. + */ + if (vlan != 0 && ifp->if_type != IFT_BRIDGE && + ifp->if_vlantrunk != NULL) + return (m); + /* We need the Ethernet header later, so make sure we have it now. */ if (m->m_len < ETHER_HDR_LEN) { m = m_pullup(m, ETHER_HDR_LEN); @@ -2886,7 +3026,6 @@ } eh = mtod(m, struct ether_header *); - vlan = VLANTAGOF(m); bif = ifp->if_bridge; if (bif) @@ -2910,6 +3049,7 @@ } m->m_pkthdr.rcvif = ifp; } + bifp = sc->sc_ifp; if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (m); @@ -2938,8 +3078,44 @@ /* bridge_vfilter_in() may add a tag */ vlan = VLANTAGOF(m); + /* Pass this frame to our span ports */ bridge_span(sc, m); + /* + * Filter on the physical interface. Do this early so that we don't + * do any other processing (like address learning) for filtered + * packets. + */ + if (V_pfil_local_phys && PFIL_HOOKED_IN_46) { + int error = bridge_pfil(&m, NULL, ifp, PFIL_IN); + if (error != 0 || m == NULL) { + if (m != NULL) + m_freem(m); + return (NULL); + } + } + + /* Learn the source address of this frame */ + if (bif->bif_flags & IFBIF_LEARNING) { + int error; + + error = bridge_rtupdate(sc, eh->ether_shost, vlan, bif, 0, + IFBAF_DYNAMIC); + + /* + * Ignore errors, unless the error was caused by the port + * exceeding its address limit, in which case the frame should + * be dropped because this is a security policy violation. + */ + if (error && bif->bif_addrmax) { + m_freem(m); + return (NULL); + } + } + + /* + * Handle broadcast and multicast frames. + */ if (m->m_flags & (M_BCAST|M_MCAST)) { /* Tap off 802.1D packets; they do not get forwarded. */ if (memcmp(eh->ether_dhost, bstp_etheraddr, @@ -2950,7 +3126,8 @@ if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { - return (m); + m_freem(m); + return (NULL); } /* @@ -3004,112 +3181,13 @@ if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { - return (m); - } - -#if defined(INET) || defined(INET6) -#define CARP_CHECK_WE_ARE_DST(iface) \ - ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_dhost)) -#define CARP_CHECK_WE_ARE_SRC(iface) \ - ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_shost)) -#else -#define CARP_CHECK_WE_ARE_DST(iface) false -#define CARP_CHECK_WE_ARE_SRC(iface) false -#endif - -#ifdef DEV_NETMAP -#define GRAB_FOR_NETMAP(ifp, m) do { \ - if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0 && \ - ((m)->m_flags & M_BRIDGE_INJECT) == 0) { \ - (ifp)->if_input(ifp, m); \ - return (NULL); \ - } \ -} while (0) -#else -#define GRAB_FOR_NETMAP(ifp, m) -#endif - -#define GRAB_OUR_PACKETS(iface) \ - if ((iface)->if_type == IFT_GIF) \ - continue; \ - /* It is destined for us. */ \ - if (memcmp(IF_LLADDR(iface), eh->ether_dhost, ETHER_ADDR_LEN) == 0 || \ - CARP_CHECK_WE_ARE_DST(iface)) { \ - if (bif->bif_flags & IFBIF_LEARNING) { \ - error = bridge_rtupdate(sc, eh->ether_shost, \ - vlan, bif, 0, IFBAF_DYNAMIC); \ - if (error && bif->bif_addrmax) { \ - m_freem(m); \ - return (NULL); \ - } \ - } \ - m->m_pkthdr.rcvif = iface; \ - if ((iface) == ifp) { \ - /* Skip bridge processing... src == dest */ \ - return (m); \ - } \ - /* It's passing over or to the bridge, locally. */ \ - ETHER_BPF_MTAP(bifp, m); \ - if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); \ - if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);\ - /* Hand the packet over to netmap if necessary. */ \ - GRAB_FOR_NETMAP(bifp, m); \ - /* Filter on the physical interface. */ \ - if (V_pfil_local_phys && PFIL_HOOKED_IN_46) { \ - if (bridge_pfil(&m, NULL, ifp, \ - PFIL_IN) != 0 || m == NULL) { \ - return (NULL); \ - } \ - } \ - if ((iface) != bifp) \ - ETHER_BPF_MTAP(iface, m); \ - /* Pass tagged packets to if_vlan, if it's loaded */ \ - if (VLANTAGOF(m) != 0) { \ - if (bifp->if_vlantrunk == NULL) { \ - m_freem(m); \ - return (NULL); \ - } \ - (*vlan_input_p)(bifp, m); \ - return (NULL); \ - } \ - return (m); \ - } \ - \ - /* We just received a packet that we sent out. */ \ - if (memcmp(IF_LLADDR(iface), eh->ether_shost, ETHER_ADDR_LEN) == 0 || \ - CARP_CHECK_WE_ARE_SRC(iface)) { \ - m_freem(m); \ - return (NULL); \ - } - - /* - * Unicast. Make sure it's not for the bridge. - */ - do { GRAB_OUR_PACKETS(bifp) } while (0); - - /* - * Check the interface the packet arrived on. For tagged frames, - * we need to do this even if member_ifaddrs is disabled because - * vlan(4) might need to handle the traffic. - */ - if (V_member_ifaddrs || (vlan && ifp->if_vlantrunk)) - do { GRAB_OUR_PACKETS(ifp) } while (0); - - /* - * We only need to check other members interface if member_ifaddrs - * is enabled; otherwise we should have never traffic destined for - * a member's lladdr. - */ - if (V_member_ifaddrs) { - CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { - GRAB_OUR_PACKETS(bif2->bif_ifp) - } + m_freem(m); + return (NULL); } -#undef CARP_CHECK_WE_ARE_DST -#undef CARP_CHECK_WE_ARE_SRC -#undef GRAB_FOR_NETMAP -#undef GRAB_OUR_PACKETS + /* See if this packet should be handled locally. */ + if (bridge_try_local(sc, ifp, &m)) + return (m); /* Perform the bridge forwarding function. */ bridge_forward(sc, bif, m);