From nobody Mon Apr 10 16:14:32 2023 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4PwDZJ2tg4z44RDy; Mon, 10 Apr 2023 16:14:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4PwDZJ1s9Rz4FcS; Mon, 10 Apr 2023 16:14:32 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1681143272; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=plNGWUkExVMp6hq9t7Gl7WJxUtx8HvcTP7kMZ5m0wFQ=; b=Xx7saLxEu6C5unjwSjeyxf8KQ9juoenwnDkFcIB1EQeOMRCECTnFfOAkQGd24WSG39X+tn A5P+oFogi+quPWlreP9QoA6kJmLA8+0vkWuygJpVUIBI5UoLGXOVX0M1T1wbfhF9+juCj7 TzxSYpCy/+B9y4g/bphcYVsAMCkTnz6JHE93LrEU4jKqqzxjkEsuCZmioHAyRnqPIfvp83 hEq0UK3Af476hjvSoSuboaw6rVUah7KE1z53IVzklMtsmnUFIos9KITLT4XgNSEp8NSjjQ XAZ4ulRVEUgAQnegfWvKdg6MuqDs194dwogG7FDFiHdnCXYTlT9DOtZbB/8jXA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1681143272; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=plNGWUkExVMp6hq9t7Gl7WJxUtx8HvcTP7kMZ5m0wFQ=; b=gr58oPlQEWYE4FpN7JFF7TuPuI+CdjErQP+5IeagY3G1tiY+Vzv9nNwPUQs9U29J4vMkEK sYyVxVEyidOMiJin2cxuzsQ9WH3K7bBcdxa3y6xR9FiDrIgrnvJJ83AFu4DmPAImLenHdH aPCego6ot+vULyNMof3SvIzgVhh6w45bJr1zSEULg7cszTRoCJ2Ykjh4ZQfL9s3q7/CUq8 aUhfzBzAcWn0ce88JMOJlykIAYejhpMuz4nIhDWdomZdDaNp6GbjBROTCa1opcEzKx90q9 QfsuwJ/6SdXI2jPysP5TptFd0Jz/LjKc41xSL+fCRmfwnqhEmUBBv5c2KDnWJg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1681143272; a=rsa-sha256; cv=none; b=apT+HpqRSA5kfFs/mvI7bOwkp8pxup3zwCyNT33WkEPWqO2YhpnVFhSJvqOfDIOHKrbIKc Y/zCMqwD7+KbL/UrRtM/Rly0pRSsDm/2ZcEwUcZsaRzHbnwc13BZhRhKhN17LX2ztLXQa1 CxfkKUyHwhEk8q2j0UFuN7gBWyi4t0hetM9ocWbAIBnFyckEr6YfuPE/VIob9p+zGfG2PG 3swNS2Nwd+aQ+Gux0xM9LCWcQ2HMb8E7tojY7sS0mNaigH9lPwK9qSI11vXeb3wPX5hPgX flpT1KvyNJmXw4RWJvqxXEpF1XI6YokR3vldDFo2xg7OYEAL2Et16zqnJM/3kA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4PwDZJ0yvPzxCR; Mon, 10 Apr 2023 16:14:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 33AGEW3Q025985; Mon, 10 Apr 2023 16:14:32 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 33AGEWDD025984; Mon, 10 Apr 2023 16:14:32 GMT (envelope-from git) Date: Mon, 10 Apr 2023 16:14:32 GMT Message-Id: <202304101614.33AGEWDD025984@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Mark Johnston Subject: git: d862b165a6d3 - main - bridge: Add support for emulated netmap mode List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d862b165a6d3436d01c8ae63ab7879d17a5d143a Auto-Submitted: auto-generated X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=d862b165a6d3436d01c8ae63ab7879d17a5d143a commit d862b165a6d3436d01c8ae63ab7879d17a5d143a Author: Mark Johnston AuthorDate: 2023-04-10 15:18:25 +0000 Commit: Mark Johnston 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) {