The branch main has been updated by markj:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=d862b165a6d3436d01c8ae63ab7879d17a5d143a

commit d862b165a6d3436d01c8ae63ab7879d17a5d143a
Author:     Mark Johnston <[email protected]>
AuthorDate: 2023-04-10 15:18:25 +0000
Commit:     Mark Johnston <[email protected]>
CommitDate: 2023-04-10 16:14:11 +0000

    bridge: Add support for emulated netmap mode
    
    if_bridge receives packets via a special interface, if_bridge_input,
    rather than by if_input.  Thus, netmap's usual hooking of ifnet routines
    does not work as expected.  Instead, modify bridge_input() to pass
    packets directly to netmap when it is enabled.  This applies to both
    locally delivered packets and forwarded packets.
    
    When a netmap application transmits a packet by writing it to the host
    TX ring, the mbuf chain is passed to if_input, which ordinarily points
    to ether_input().  However, when transmitting via if_bridge,
    bridge_input() needs to see the packet again in order to decide whether
    to deliver or forward.  Thus, introduce a new protocol flag,
    M_BRIDGE_INJECT, which 1) causes the packet to be passed to
    bridge_input() again after Ethernet processing, and 2) avoids passing
    the packet back to netmap.  The source MAC address of the packet is used
    to determine the original "receiving" interface.
    
    Reviewed by:    vmaffione
    MFC after:      2 months
    Sponsored by:   Zenarmor
    Sponsored by:   OPNsense
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D38066
---
 share/man/man4/bridge.4 |  24 +++++++++++
 sys/net/ethernet.h      |   1 +
 sys/net/if_bridge.c     | 108 ++++++++++++++++++++++++++++++++++++++++++++----
 sys/net/if_bridgevar.h  |   4 +-
 sys/net/if_ethersubr.c  |   7 +++-
 5 files changed, 134 insertions(+), 10 deletions(-)

diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4
index b4dabcbb79bc..2671bfaf73cc 100644
--- a/share/man/man4/bridge.4
+++ b/share/man/man4/bridge.4
@@ -421,6 +421,29 @@ interface and not to the bridge members.
 Enabling
 .Va net.link.bridge.pfil_local_phys
 will let you do the additional filtering on the physical interface.
+.Sh NETMAP
+.Xr netmap 4
+applications may open a bridge interface in emulated mode.
+The netmap application will receive all packets which arrive from member
+interfaces.
+In particular, packets which would otherwise be forwarded to another
+member interface will be received by the netmap application.
+.Pp
+When the
+.Xr netmap 4
+application transmits a packet to the host stack via the bridge interface,
+.Nm
+receive it and attempts to determine its
+.Ql source
+interface by looking up the source MAC address in the interface's learning
+tables.
+Packets for which no matching source interface is found are dropped and the
+input error counter is incremented.
+If a matching source interface is found,
+.Nm
+treats the packet as though it was received from the corresponding interface
+and handles it normally without passing the packet back to
+.Xr netmap 4 .
 .Sh EXAMPLES
 The following when placed in the file
 .Pa /etc/rc.conf
@@ -495,6 +518,7 @@ ifconfig bridge0 addm fxp0 addm gif0 up
 .Xr gif 4 ,
 .Xr ipf 4 ,
 .Xr ipfw 4 ,
+.Xr netmap 4 ,
 .Xr pf 4 ,
 .Xr ifconfig 8
 .Sh HISTORY
diff --git a/sys/net/ethernet.h b/sys/net/ethernet.h
index 98b02d71da50..e259ab53e77e 100644
--- a/sys/net/ethernet.h
+++ b/sys/net/ethernet.h
@@ -43,6 +43,7 @@
  * Ethernet-specific mbuf flags.
  */
 #define        M_HASFCS        M_PROTO5        /* FCS included at end of frame 
*/
+#define        M_BRIDGE_INJECT M_PROTO6        /* if_bridge-injected frame */
 
 /*
  * Ethernet CRC32 polynomials (big- and little-endian versions).
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 78be987d681b..f71d7c0cdfd1 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -267,6 +267,7 @@ struct bridge_softc {
        uint32_t                sc_brtexceeded; /* # of cache drops */
        struct ifnet            *sc_ifaddr;     /* member mac copied from */
        struct ether_addr       sc_defaddr;     /* Default MAC address */
+       if_input_fn_t           sc_if_input;    /* Saved copy of if_input */
        struct epoch_context    sc_epoch_ctx;
 };
 
@@ -298,6 +299,7 @@ static int  bridge_altq_transmit(if_t, struct mbuf *);
 #endif
 static void    bridge_qflush(struct ifnet *);
 static struct mbuf *bridge_input(struct ifnet *, struct mbuf *);
+static void    bridge_inject(struct ifnet *, struct mbuf *);
 static int     bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *,
                    struct rtentry *);
 static int     bridge_enqueue(struct bridge_softc *, struct ifnet *,
@@ -768,6 +770,15 @@ bridge_clone_create(struct if_clone *ifc, char *name, 
size_t len,
 #ifdef VIMAGE
        ifp->if_reassign = bridge_reassign;
 #endif
+       sc->sc_if_input = ifp->if_input;        /* ether_input */
+       ifp->if_input = bridge_inject;
+
+       /*
+        * Allow BRIDGE_INPUT() to pass in packets originating from the bridge
+        * itself via bridge_inject().  This is required for netmap but
+        * otherwise has no effect.
+        */
+       ifp->if_bridge_input = bridge_input;
 
        BRIDGE_LIST_LOCK();
        LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list);
@@ -2355,6 +2366,19 @@ bridge_forward(struct bridge_softc *sc, struct 
bridge_iflist *sbif,
            sbif->bif_stp.bp_state == BSTP_IFSTATE_LEARNING)
                goto drop;
 
+#ifdef DEV_NETMAP
+       /*
+        * Hand the packet to netmap only if it wasn't injected by netmap
+        * itself.
+        */
+       if ((m->m_flags & M_BRIDGE_INJECT) == 0 &&
+           (if_getcapenable(ifp) & IFCAP_NETMAP) != 0) {
+               ifp->if_input(ifp, m);
+               return;
+       }
+       m->m_flags &= ~M_BRIDGE_INJECT;
+#endif
+
        /*
         * At this point, the port either doesn't participate
         * in spanning tree or it is in the forwarding state.
@@ -2461,7 +2485,7 @@ drop:
 static struct mbuf *
 bridge_input(struct ifnet *ifp, struct mbuf *m)
 {
-       struct bridge_softc *sc = ifp->if_bridge;
+       struct bridge_softc *sc;
        struct bridge_iflist *bif, *bif2;
        struct ifnet *bifp;
        struct ether_header *eh;
@@ -2471,11 +2495,31 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 
        NET_EPOCH_ASSERT();
 
-       if ((sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
-               return (m);
+       eh = mtod(m, struct ether_header *);
+       vlan = VLANTAGOF(m);
 
+       sc = ifp->if_bridge;
+       if (sc == NULL) {
+               /*
+                * This packet originated from the bridge itself, so it must
+                * have been transmitted by netmap.  Derive the "source"
+                * interface from the source address and drop the packet if the
+                * source address isn't known.
+                */
+               KASSERT((m->m_flags & M_BRIDGE_INJECT) != 0,
+                   ("%s: ifnet %p missing a bridge softc", __func__, ifp));
+               sc = if_getsoftc(ifp);
+               ifp = bridge_rtlookup(sc, eh->ether_shost, vlan);
+               if (ifp == NULL) {
+                       if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1);
+                       m_freem(m);
+                       return (NULL);
+               }
+               m->m_pkthdr.rcvif = ifp;
+       }
        bifp = sc->sc_ifp;
-       vlan = VLANTAGOF(m);
+       if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+               return (m);
 
        /*
         * Implement support for bridge monitoring. If this flag has been
@@ -2496,8 +2540,6 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                return (m);
        }
 
-       eh = mtod(m, struct ether_header *);
-
        bridge_span(sc, m);
 
        if (m->m_flags & (M_BCAST|M_MCAST)) {
@@ -2526,6 +2568,18 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                /* Perform the bridge forwarding function with the copy. */
                bridge_forward(sc, bif, mc);
 
+#ifdef DEV_NETMAP
+               /*
+                * If netmap is enabled and has not already seen this packet,
+                * then it will be consumed by bridge_forward().
+                */
+               if ((if_getcapenable(bifp) & IFCAP_NETMAP) != 0 &&
+                   (m->m_flags & M_BRIDGE_INJECT) == 0) {
+                       m_freem(m);
+                       return (NULL);
+               }
+#endif
+
                /*
                 * Reinject the mbuf as arriving on the bridge so we have a
                 * chance at claiming multicast packets. We can not loop back
@@ -2542,7 +2596,8 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
                }
                if (mc2 != NULL) {
                        mc2->m_pkthdr.rcvif = bifp;
-                       (*bifp->if_input)(bifp, mc2);
+                       mc2->m_flags &= ~M_BRIDGE_INJECT;
+                       sc->sc_if_input(bifp, mc2);
                }
 
                /* Return the original packet for local processing. */
@@ -2570,6 +2625,18 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 #define        PFIL_HOOKED_INET6       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;                                               \
@@ -2592,7 +2659,9 @@ bridge_input(struct ifnet *ifp, struct mbuf *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); \
+               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(V_inet_pfil_head) || \
                    PFIL_HOOKED_INET6)) {                               \
@@ -2635,6 +2704,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
 #undef CARP_CHECK_WE_ARE_DST
 #undef CARP_CHECK_WE_ARE_SRC
 #undef PFIL_HOOKED_INET6
+#undef GRAB_FOR_NETMAP
 #undef GRAB_OUR_PACKETS
 
        /* Perform the bridge forwarding function. */
@@ -2643,6 +2713,28 @@ bridge_input(struct ifnet *ifp, struct mbuf *m)
        return (NULL);
 }
 
+/*
+ * Inject a packet back into the host ethernet stack.  This will generally only
+ * be used by netmap when an application writes to the host TX ring.  The
+ * M_BRIDGE_INJECT flag ensures that the packet is re-routed to the bridge
+ * interface after ethernet processing.
+ */
+static void
+bridge_inject(struct ifnet *ifp, struct mbuf *m)
+{
+       struct bridge_softc *sc;
+
+       KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0,
+           ("%s: iface %s is not running in netmap mode",
+           __func__, if_name(ifp)));
+       KASSERT((m->m_flags & M_BRIDGE_INJECT) == 0,
+           ("%s: mbuf %p has M_BRIDGE_INJECT set", __func__, m));
+
+       m->m_flags |= M_BRIDGE_INJECT;
+       sc = if_getsoftc(ifp);
+       sc->sc_if_input(ifp, m);
+}
+
 /*
  * bridge_broadcast:
  *
diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h
index 69b9aa33f133..5696e8a62ae0 100644
--- a/sys/net/if_bridgevar.h
+++ b/sys/net/if_bridgevar.h
@@ -309,8 +309,10 @@ struct ifbpstpconf {
        KASSERT((_ifp)->if_bridge_input != NULL,        \
            ("%s: if_bridge not loaded!", __func__));   \
        _m = (*(_ifp)->if_bridge_input)(_ifp, _m);      \
-       if (_m != NULL)                                 \
+       if (_m != NULL) {                               \
                _ifp = _m->m_pkthdr.rcvif;              \
+               m->m_flags &= ~M_BRIDGE_INJECT;         \
+       }                                               \
 } while (0)
 
 #define BRIDGE_OUTPUT(_ifp, _m, _err)  do {            \
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c
index 34ff4ac22e7f..839bae8e9d43 100644
--- a/sys/net/if_ethersubr.c
+++ b/sys/net/if_ethersubr.c
@@ -667,10 +667,15 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
 
        /*
         * Allow if_bridge(4) to claim this frame.
+        *
         * The BRIDGE_INPUT() macro will update ifp if the bridge changed it
         * and the frame should be delivered locally.
+        *
+        * If M_BRIDGE_INJECT is set, the packet was received directly by the
+        * bridge via netmap, so "ifp" is the bridge itself and the packet
+        * should be re-examined.
         */
-       if (ifp->if_bridge != NULL) {
+       if (ifp->if_bridge != NULL || (m->m_flags & M_BRIDGE_INJECT) != 0) {
                m->m_flags &= ~M_PROMISC;
                BRIDGE_INPUT(ifp, m);
                if (m == NULL) {

Reply via email to