Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F156744075
D51260.id158347.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
11 KB
Referenced Files
None
Subscribers
None
D51260.id158347.diff
View Options
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,172 @@
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;
+
+ NET_EPOCH_ASSERT();
+
+ bifp = sc->sc_ifp;
+
+ /* 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 frame has a VLAN tag, it's intended for an SVI, so pass it to
+ * vlan_input() to handle.
+ *
+ * If there's no VLAN trunk configured, we can't handle this frame
+ * locally, so just drop it.
+ */
+ if (VLANTAGOF(m) != 0) {
+ if (bifp->if_vlantrunk != NULL)
+ (*vlan_input_p)(ifp, m);
+ else
+ m_freem(m);
+
+ return (NULL);
+ }
+
+ /*
+ * Return the frame to ether_input().
+ */
+ return (m);
+}
+
+/*
+ * Given an mbuf and the incoming interface, determine if this frame should be
+ * handled locally by comparing the destination Ethernet address of the frame
+ * to the interface's Ethernet address. If the frame is destined for that
+ * interface, pass it to bridge_handle_for_host() and return true.
+ */
+static bool
+bridge_try_local_if(struct bridge_softc *sc, struct ifnet *iface,
+ struct mbuf **mp)
+{
+ struct ether_header *eh;
+
+ NET_EPOCH_ASSERT();
+
+ /*
+ * Frames received on a gif(4) interface are never considered local.
+ * XXX: Why not? (This was the behaviour of bridge before we added
+ * member_ifaddrs, so it's probably correct, and will become moot
+ * once member_ifaddrs is removed.)
+ */
+ if (iface->if_type == IFT_GIF)
+ return (false);
+
+ eh = mtod(*mp, struct ether_header *);
+
+ /*
+ * If the destination address is the interface's address, this frame
+ * is for us. Also check CARP's destination address.
+ */
+ if (memcmp(IF_LLADDR(iface), eh->ether_dhost, ETHER_ADDR_LEN) == 0
+#if defined(INET) || defined(INET6)
+ || (iface->if_carp && (*carp_forus_p)(iface, eh->ether_dhost))
+#endif
+ ) {
+ /*
+ * rcvif will have been previously set to the interface this
+ * frame was actually received on. Change it to the interface
+ * we matched here, because IP expects that the rcvif of an
+ * incoming packet will match the interface the address is
+ * assigned to.
+ *
+ * This is required in two cases:
+ *
+ * - When the IP address is assigned to the bridge;
+ *
+ * - When the IP address is assigned to a bridge member, but
+ * the frame was received on a different member.
+ * (Only if member_ifaddrs=1.)
+ */
+ (*mp)->m_pkthdr.rcvif = iface;
+
+ *mp = bridge_handle_for_host(sc, iface, *mp);
+ return (true);
+ }
+
+ /*
+ * If the source address is the interface's address, this frame was
+ * sent by us, so drop it.
+ */
+ if (memcmp(IF_LLADDR(iface), eh->ether_shost, ETHER_ADDR_LEN) == 0
+#if defined(INET) || defined(INET6)
+ || (iface->if_carp && (*carp_forus_p)(iface, eh->ether_shost))
+#endif
+ ) {
+ 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. The behaviour depends on how member_ifaddrs is set:
+ *
+ * - If it's enabled, check the bridge itself, then the interface we
+ * received the frame on (to optimise the common case), then all other
+ * bridge members. This means that a frame with the destination address
+ * of member A can arrive on member B and still be considered local,
+ * which is the legacy behaviour of bridge before member_ifaddrs.
+ *
+ * - If member_ifaddrs is disabled, only check the bridge interface; any
+ * other frames will not be considered local.
+ *
+ * Once member_ifaddrs is removed, this function can be removed too.
+ */
+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 +3019,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 +3051,6 @@
}
eh = mtod(m, struct ether_header *);
- vlan = VLANTAGOF(m);
bif = ifp->if_bridge;
if (bif)
@@ -2908,8 +3072,13 @@
m_freem(m);
return (NULL);
}
+ bif = ifp->if_bridge;
m->m_pkthdr.rcvif = ifp;
}
+
+ KASSERT(bif != NULL,
+ ("%s: no bif for packet from %s", __func__, ifp->if_xname));
+
bifp = sc->sc_ifp;
if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
return (m);
@@ -2938,8 +3107,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 +3155,8 @@
if ((bif->bif_flags & IFBIF_STP) &&
bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) {
- return (m);
+ m_freem(m);
+ return (NULL);
}
/*
@@ -3004,112 +3210,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);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, May 17, 1:43 AM (7 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33165730
Default Alt Text
D51260.id158347.diff (11 KB)
Attached To
Mode
D51260: bridge: refactor local packet handling
Attached
Detach File
Event Timeline
Log In to Comment