Download raw body.
move aggr/trunk input processing into ether_input
On Sun, Nov 23, 2025 at 03:55:05PM +1000, David Gwynne wrote:
> currently aggr and trunk (and my switch chip drivers) replace the
> if_input handler (which is ether_input) on interfaces they take over.
>
> this changes it so these drivers operate like the bridge drivers where
> an "ether_port" struct is given to arpcom that ether_input will look at
> and allow input packets to be filtered with.
>
> i want this for two reasons. firstly, the old "swap if_input" semantic
> assumes that if_input is run while holding NET_LOCK in some way, and i
> want the freedom to call it without NET_LOCK in the future. secondly, it
> makes it hard to make an ethernet driver that has some extra encap you
> have to remove before calling ether_input. such a driver will mess up if
> you try to use aggr or trunk with them and their own if_input handling
> has been swapped with aggr_input or trunk_input.
>
> ive tested aggr and veb with this change. anyone want to test the trunk
> changes before i put this in?
I have automated trunk tests. They pass.
It is trunk only, no veb, vlan or carp involved.
bluhm
> Index: net/if_aggr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_aggr.c,v
> diff -u -p -r1.50 if_aggr.c
> --- net/if_aggr.c 7 Jul 2025 02:28:50 -0000 1.50
> +++ net/if_aggr.c 22 Nov 2025 09:28:42 -0000
> @@ -346,12 +346,13 @@ struct aggr_port {
> struct ifnet *p_ifp0;
> struct kstat *p_kstat;
> struct mutex p_mtx;
> + struct ether_port p_ether_port;
> + struct refcnt p_refs;
>
> uint8_t p_lladdr[ETHER_ADDR_LEN];
> uint32_t p_mtu;
>
> int (*p_ioctl)(struct ifnet *, u_long, caddr_t);
> - void (*p_input)(struct ifnet *, struct mbuf *, struct netstack *);
> int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
> struct rtentry *);
>
> @@ -743,75 +744,93 @@ aggr_start(struct ifqueue *ifq)
> smr_read_leave();
> }
>
> +static void
> +aggr_port_take(void *port)
> +{
> + struct aggr_port *p = port;
> + refcnt_take(&p->p_refs);
> +}
> +
> +static void
> +aggr_port_rele(void *port)
> +{
> + struct aggr_port *p = port;
> + refcnt_rele_wake(&p->p_refs);
> +}
> +
> +static inline int
> +aggr_is_lldp(struct mbuf *m, uint64_t dst)
> +{
> + struct ether_header *eh = mtod(m, struct ether_header *);
> +
> + if (eh->ether_type == htons(ETHERTYPE_LLDP) &&
> + ETH64_IS_8021_RSVD(dst)) {
> + /* look at the last nibble of the 802.1 reserved address */
> + switch (dst & 0xf) {
> + case 0x0: /* Nearest Customer Bridge */
> + case 0x3: /* Non-TPMR Bridge */
> + case 0xe: /* Nearest Bridge */
> + return (1);
> + default:
> + break;
> + }
> + }
> +
> + return (0);
> +}
> +
> static inline struct mbuf *
> -aggr_input_control(struct aggr_port *p, struct mbuf *m, struct netstack *ns)
> +aggr_input_control(struct aggr_port *p, struct mbuf *m, uint64_t dst)
> {
> struct ether_header *eh;
> int hlen = sizeof(*eh);
> - uint16_t etype;
> - uint64_t dst;
> + unsigned int rx_proto = AGGR_PROTO_RX_LACP;
> + struct ether_slowproto_hdr *sph;
> + int drop = 0;
>
> if (ISSET(m->m_flags, M_VLANTAG))
> return (m);
>
> eh = mtod(m, struct ether_header *);
> - etype = eh->ether_type;
> - dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost);
> -
> - if (__predict_false(etype == htons(ETHERTYPE_SLOW) &&
> - dst == LACP_ADDR_SLOW_E64)) {
> - unsigned int rx_proto = AGGR_PROTO_RX_LACP;
> - struct ether_slowproto_hdr *sph;
> - int drop = 0;
> -
> - hlen += sizeof(*sph);
> - if (m->m_len < hlen) {
> - m = m_pullup(m, hlen);
> - if (m == NULL) {
> - /* short++ */
> - return (NULL);
> - }
> - eh = mtod(m, struct ether_header *);
> - }
> -
> - sph = (struct ether_slowproto_hdr *)(eh + 1);
> - switch (sph->sph_subtype) {
> - case SLOWPROTOCOLS_SUBTYPE_LACP_MARKER:
> - rx_proto = AGGR_PROTO_RX_MARKER;
> - /* FALLTHROUGH */
> - case SLOWPROTOCOLS_SUBTYPE_LACP:
> - mtx_enter(&p->p_mtx);
> - p->p_proto_counts[rx_proto].c_pkts++;
> - p->p_proto_counts[rx_proto].c_bytes += m->m_pkthdr.len;
> -
> - if (ml_len(&p->p_rxm_ml) < AGGR_MAX_SLOW_PKTS)
> - ml_enqueue(&p->p_rxm_ml, m);
> - else {
> - p->p_rx_drops++;
> - drop = 1;
> - }
> - mtx_leave(&p->p_mtx);
> + if (__predict_true(eh->ether_type != htons(ETHERTYPE_SLOW) ||
> + dst != LACP_ADDR_SLOW_E64))
> + return (m);
>
> - if (drop)
> - goto drop;
> - else
> - task_add(systq, &p->p_rxm_task);
> + hlen += sizeof(*sph);
> + if (m->m_len < hlen) {
> + m = m_pullup(m, hlen);
> + if (m == NULL) {
> + /* short++ */
> return (NULL);
> - default:
> - break;
> }
> - } else if (__predict_false(etype == htons(ETHERTYPE_LLDP) &&
> - ETH64_IS_8021_RSVD(dst))) {
> - /* look at the last nibble of the 802.1 reserved address */
> - switch (dst & 0xf) {
> - case 0x0: /* Nearest Customer Bridge */
> - case 0x3: /* Non-TPMR Bridge */
> - case 0xe: /* Nearest Bridge */
> - p->p_input(p->p_ifp0, m, ns);
> - return (NULL);
> - default:
> - break;
> + eh = mtod(m, struct ether_header *);
> + }
> +
> + sph = (struct ether_slowproto_hdr *)(eh + 1);
> + switch (sph->sph_subtype) {
> + case SLOWPROTOCOLS_SUBTYPE_LACP_MARKER:
> + rx_proto = AGGR_PROTO_RX_MARKER;
> + /* FALLTHROUGH */
> + case SLOWPROTOCOLS_SUBTYPE_LACP:
> + mtx_enter(&p->p_mtx);
> + p->p_proto_counts[rx_proto].c_pkts++;
> + p->p_proto_counts[rx_proto].c_bytes += m->m_pkthdr.len;
> +
> + if (ml_len(&p->p_rxm_ml) < AGGR_MAX_SLOW_PKTS)
> + ml_enqueue(&p->p_rxm_ml, m);
> + else {
> + p->p_rx_drops++;
> + drop = 1;
> }
> + mtx_leave(&p->p_mtx);
> +
> + if (drop)
> + goto drop;
> + else
> + task_add(systq, &p->p_rxm_task);
> + return (NULL);
> + default:
> + break;
> }
>
> return (m);
> @@ -821,20 +840,23 @@ drop:
> return (NULL);
> }
>
> -static void
> -aggr_input(struct ifnet *ifp0, struct mbuf *m, struct netstack *ns)
> +static struct mbuf *
> +aggr_port_input(struct ifnet *ifp0, struct mbuf *m, uint64_t dst, void *port,
> + struct netstack *ns)
> {
> - struct arpcom *ac0 = (struct arpcom *)ifp0;
> - struct aggr_port *p = ac0->ac_trunkport;
> + struct aggr_port *p = port;
> struct aggr_softc *sc = p->p_aggr;
> struct ifnet *ifp = &sc->sc_if;
>
> + if (__predict_false(aggr_is_lldp(m, dst)))
> + return (m);
> +
> if (!ISSET(ifp->if_flags, IFF_RUNNING))
> goto drop;
>
> - m = aggr_input_control(p, m, ns);
> + m = aggr_input_control(p, m, dst);
> if (m == NULL)
> - return;
> + return (NULL);
>
> if (__predict_false(!p->p_collecting))
> goto drop;
> @@ -844,10 +866,11 @@ aggr_input(struct ifnet *ifp0, struct mb
>
> if_vinput(ifp, m, ns);
>
> - return;
> + return (NULL);
>
> drop:
> m_freem(m);
> + return (NULL);
> }
>
> static int
> @@ -1168,7 +1191,7 @@ aggr_add_port(struct aggr_softc *sc, con
> }
>
> ac0 = (struct arpcom *)ifp0;
> - if (ac0->ac_trunkport != NULL) {
> + if (SMR_PTR_GET_LOCKED(&ac0->ac_trport) != NULL) {
> error = EBUSY;
> goto put;
> }
> @@ -1188,11 +1211,16 @@ aggr_add_port(struct aggr_softc *sc, con
> p->p_aggr = sc;
> p->p_mtu = ifp0->if_mtu;
> mtx_init(&p->p_mtx, IPL_SOFTNET);
> + refcnt_init(&p->p_refs);
> +
> + p->p_ether_port.ep_input = aggr_port_input;
> + p->p_ether_port.ep_port_take = aggr_port_take;
> + p->p_ether_port.ep_port_rele = aggr_port_rele;
> + p->p_ether_port.ep_port = p;
>
> CTASSERT(sizeof(p->p_lladdr) == sizeof(ac0->ac_enaddr));
> memcpy(p->p_lladdr, ac0->ac_enaddr, sizeof(p->p_lladdr));
> p->p_ioctl = ifp0->if_ioctl;
> - p->p_input = ifp0->if_input;
> p->p_output = ifp0->if_output;
>
> error = aggr_group(sc, p, SIOCADDMULTI);
> @@ -1256,16 +1284,16 @@ aggr_add_port(struct aggr_softc *sc, con
> aggr_update_capabilities(sc);
>
> /*
> - * use (and modification) of ifp->if_input and ac->ac_trunkport
> - * is protected by NET_LOCK.
> + * modification of ac->ac_trport is protected by NET_LOCK.
> */
>
> - ac0->ac_trunkport = p;
> + KASSERT(SMR_PTR_GET_LOCKED(&ac0->ac_trport) == NULL);
> + aggr_port_take(p); /* for the SMR ptr */
> + SMR_PTR_SET_LOCKED(&ac0->ac_trport, &p->p_ether_port);
>
> /* make sure p is visible before handlers can run */
> membar_producer();
> ifp0->if_ioctl = aggr_p_ioctl;
> - ifp0->if_input = aggr_input;
> ifp0->if_output = aggr_p_output;
>
> aggr_mux(sc, p, LACP_MUX_E_BEGIN);
> @@ -1399,10 +1427,20 @@ static int
> aggr_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data)
> {
> struct arpcom *ac0 = (struct arpcom *)ifp0;
> - struct aggr_port *p = ac0->ac_trunkport;
> + const struct ether_port *ep = SMR_PTR_GET_LOCKED(&ac0->ac_trport);
> + struct aggr_port *p;
> struct ifreq *ifr = (struct ifreq *)data;
> int error = 0;
>
> + KASSERTMSG(ep != NULL,
> + "%s: %s called without an ether_port set",
> + ifp0->if_xname, __func__);
> + KASSERTMSG(ep->ep_input == aggr_port_input,
> + "%s called %s, but ep_input (%p) seems wrong",
> + ifp0->if_xname, __func__, ep->ep_input);
> +
> + p = ep->ep_port;
> +
> switch (cmd) {
> case SIOCGTRUNKPORT: {
> struct trunk_reqport *rp = (struct trunk_reqport *)data;
> @@ -1449,8 +1487,10 @@ static int
> aggr_p_output(struct ifnet *ifp0, struct mbuf *m, struct sockaddr *dst,
> struct rtentry *rt)
> {
> + int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
> + struct rtentry *) = NULL;
> struct arpcom *ac0 = (struct arpcom *)ifp0;
> - struct aggr_port *p = ac0->ac_trunkport;
> + const struct ether_port *ep;
>
> /* restrict transmission to bpf only */
> if (m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL) {
> @@ -1458,7 +1498,20 @@ aggr_p_output(struct ifnet *ifp0, struct
> return (EBUSY);
> }
>
> - return ((*p->p_output)(ifp0, m, dst, rt));
> + smr_read_enter();
> + ep = SMR_PTR_GET(&ac0->ac_trport);
> + if (ep != NULL && ep->ep_input == aggr_port_input) {
> + struct aggr_port *p = ep->ep_port;
> + p_output = p->p_output; /* code doesn't go away */
> + }
> + smr_read_leave();
> +
> + if (p_output == NULL) {
> + m_freem(m);
> + return (ENXIO);
> + }
> +
> + return ((*p_output)(ifp0, m, dst, rt));
> }
>
> static void
> @@ -1469,6 +1522,7 @@ aggr_p_dtor(struct aggr_softc *sc, struc
> struct arpcom *ac0 = (struct arpcom *)ifp0;
> struct aggr_multiaddr *ma;
> enum aggr_port_selected selected;
> + struct smr_entry smrdtor;
> int error;
>
> DPRINTF(sc, "%s %s %s: destroying port\n",
> @@ -1486,14 +1540,19 @@ aggr_p_dtor(struct aggr_softc *sc, struc
> timeout_del(&p->p_wait_while_timer);
>
> /*
> - * use (and modification) of ifp->if_input and ac->ac_trunkport
> - * is protected by NET_LOCK.
> + * modification of ac->ac_trport is protected by NET_LOCK.
> */
>
> - ac0->ac_trunkport = NULL;
> - ifp0->if_input = p->p_input;
> + NET_ASSERT_LOCKED();
> + KASSERT(SMR_PTR_GET_LOCKED(&ac0->ac_trport) == &p->p_ether_port);
> + smr_init(&smrdtor);
> +
> ifp0->if_ioctl = p->p_ioctl;
> ifp0->if_output = p->p_output;
> + SMR_PTR_SET_LOCKED(&ac0->ac_trport, NULL);
> + smr_call(&smrdtor, aggr_port_rele, p);
> +
> + refcnt_finalize(&p->p_refs, "aggrdtor");
>
> #if NKSTAT > 0
> aggr_port_kstat_detach(p);
> Index: net/if_bridge.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_bridge.c,v
> diff -u -p -r1.380 if_bridge.c
> --- net/if_bridge.c 3 Nov 2025 23:50:57 -0000 1.380
> +++ net/if_bridge.c 22 Nov 2025 09:28:42 -0000
> @@ -149,7 +149,7 @@ struct niqueue bridgeintrq = NIQUEUE_INI
> struct if_clone bridge_cloner =
> IF_CLONE_INITIALIZER("bridge", bridge_clone_create, bridge_clone_destroy);
>
> -const struct ether_brport bridge_brport = {
> +const struct ether_port bridge_brport = {
> bridge_input,
> bridge_take,
> bridge_rele,
> Index: net/if_ethersubr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_ethersubr.c,v
> diff -u -p -r1.304 if_ethersubr.c
> --- net/if_ethersubr.c 3 Nov 2025 23:50:57 -0000 1.304
> +++ net/if_ethersubr.c 22 Nov 2025 09:28:42 -0000
> @@ -375,20 +375,31 @@ ether_output(struct ifnet *ifp, struct m
> return (if_enqueue(ifp, m));
> }
>
> +static struct mbuf *
> +ether_port_input(struct ifnet *ifp, struct mbuf *m, uint64_t dst,
> + const struct ether_port **epp, struct netstack *ns)
> +{
> + const struct ether_port *ep;
> +
> + smr_read_enter();
> + ep = SMR_PTR_GET(epp);
> + if (ep != NULL)
> + ep->ep_port_take(ep->ep_port);
> + smr_read_leave();
> + if (ep != NULL) {
> + m = (*ep->ep_input)(ifp, m, dst, ep->ep_port, ns);
> + ep->ep_port_rele(ep->ep_port);
> + }
> +
> + return (m);
> +}
> +
> /*
> * Process a received Ethernet packet.
> *
> * Ethernet input has several "phases" of filtering packets to
> * support virtual/pseudo interfaces before actual layer 3 protocol
> * handling.
> - *
> - * First phase:
> - *
> - * The first phase supports drivers that aggregate multiple Ethernet
> - * ports into a single logical interface, ie, aggr(4) and trunk(4).
> - * These drivers intercept packets by swapping out the if_input handler
> - * on the "port" interfaces to steal the packets before they get here
> - * to ether_input().
> */
> void
> ether_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
> @@ -396,8 +407,7 @@ ether_input(struct ifnet *ifp, struct mb
> struct ether_header *eh;
> void (*input)(struct ifnet *, struct mbuf *, struct netstack *);
> u_int16_t etype;
> - struct arpcom *ac;
> - const struct ether_brport *eb;
> + struct arpcom *ac = (struct arpcom *)ifp;
> unsigned int sdelim = 0;
> uint64_t dst, self;
>
> @@ -405,6 +415,20 @@ ether_input(struct ifnet *ifp, struct mb
> if (m->m_len < ETHER_HDR_LEN)
> goto dropanyway;
>
> + eh = mtod(m, struct ether_header *);
> + dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost);
> +
> + /*
> + * First phase:
> + *
> + * The first phase supports drivers that aggregate multiple
> + * Ethernet ports into a single logical interface, ie, aggr(4)
> + * and trunk(4).
> + */
> + m = ether_port_input(ifp, m, dst, &ac->ac_trport, ns);
> + if (m == NULL)
> + return;
> +
> /*
> * Second phase: service delimited packet filtering.
> *
> @@ -414,8 +438,6 @@ ether_input(struct ifnet *ifp, struct mb
> * bridge can have a go at forwarding them.
> */
>
> - eh = mtod(m, struct ether_header *);
> - dst = ether_addr_to_e64((struct ether_addr *)eh->ether_dhost);
> etype = ntohs(eh->ether_type);
>
> if (ISSET(m->m_flags, M_VLANTAG) ||
> @@ -438,21 +460,9 @@ ether_input(struct ifnet *ifp, struct mb
> * may return it here to ether_input() to support local
> * delivery to this port.
> */
> -
> - ac = (struct arpcom *)ifp;
> -
> - smr_read_enter();
> - eb = SMR_PTR_GET(&ac->ac_brport);
> - if (eb != NULL)
> - eb->eb_port_take(eb->eb_port);
> - smr_read_leave();
> - if (eb != NULL) {
> - m = (*eb->eb_input)(ifp, m, dst, eb->eb_port, ns);
> - eb->eb_port_rele(eb->eb_port);
> - if (m == NULL) {
> - return;
> - }
> - }
> + m = ether_port_input(ifp, m, dst, &ac->ac_brport, ns);
> + if (m == NULL)
> + return;
>
> /*
> * Fourth phase: drop service delimited packets.
> @@ -606,7 +616,7 @@ ether_brport_isset(struct ifnet *ifp)
> }
>
> void
> -ether_brport_set(struct ifnet *ifp, const struct ether_brport *eb)
> +ether_brport_set(struct ifnet *ifp, const struct ether_port *ep)
> {
> struct arpcom *ac = (struct arpcom *)ifp;
>
> @@ -614,7 +624,7 @@ ether_brport_set(struct ifnet *ifp, cons
> KASSERTMSG(SMR_PTR_GET_LOCKED(&ac->ac_brport) == NULL,
> "%s setting an already set brport", ifp->if_xname);
>
> - SMR_PTR_SET_LOCKED(&ac->ac_brport, eb);
> + SMR_PTR_SET_LOCKED(&ac->ac_brport, ep);
> }
>
> void
> @@ -629,7 +639,7 @@ ether_brport_clr(struct ifnet *ifp)
> SMR_PTR_SET_LOCKED(&ac->ac_brport, NULL);
> }
>
> -const struct ether_brport *
> +const struct ether_port *
> ether_brport_get(struct ifnet *ifp)
> {
> struct arpcom *ac = (struct arpcom *)ifp;
> @@ -637,7 +647,7 @@ ether_brport_get(struct ifnet *ifp)
> return (SMR_PTR_GET(&ac->ac_brport));
> }
>
> -const struct ether_brport *
> +const struct ether_port *
> ether_brport_get_locked(struct ifnet *ifp)
> {
> struct arpcom *ac = (struct arpcom *)ifp;
> Index: net/if_tpmr.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_tpmr.c,v
> diff -u -p -r1.38 if_tpmr.c
> --- net/if_tpmr.c 4 Nov 2025 12:12:00 -0000 1.38
> +++ net/if_tpmr.c 22 Nov 2025 09:28:42 -0000
> @@ -81,7 +81,7 @@ struct tpmr_port {
>
> int p_refcnt;
>
> - struct ether_brport p_brport;
> + struct ether_port p_brport;
> };
>
> struct tpmr_softc {
> @@ -545,10 +545,10 @@ tpmr_add_port(struct tpmr_softc *sc, con
> task_set(&p->p_dtask, tpmr_p_detach, p);
> if_detachhook_add(ifp0, &p->p_dtask);
>
> - p->p_brport.eb_input = tpmr_input;
> - p->p_brport.eb_port_take = tpmr_p_take;
> - p->p_brport.eb_port_rele = tpmr_p_rele;
> - p->p_brport.eb_port = p;
> + p->p_brport.ep_input = tpmr_input;
> + p->p_brport.ep_port_take = tpmr_p_take;
> + p->p_brport.ep_port_rele = tpmr_p_rele;
> + p->p_brport.ep_port = p;
>
> /* commit */
> DPRINTF(sc, "%s %s trunkport: creating port\n",
> @@ -661,18 +661,18 @@ done:
> static int
> tpmr_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data)
> {
> - const struct ether_brport *eb = ether_brport_get_locked(ifp0);
> + const struct ether_port *ep = ether_brport_get_locked(ifp0);
> struct tpmr_port *p;
> int error = 0;
>
> - KASSERTMSG(eb != NULL,
> + KASSERTMSG(ep != NULL,
> "%s: %s called without an ether_brport set",
> ifp0->if_xname, __func__);
> - KASSERTMSG(eb->eb_input == tpmr_input,
> - "%s: %s called, but eb_input seems wrong (%p != tpmr_input())",
> - ifp0->if_xname, __func__, eb->eb_input);
> + KASSERTMSG(ep->ep_input == tpmr_input,
> + "%s: %s called, but ep_input seems wrong (%p != tpmr_input())",
> + ifp0->if_xname, __func__, ep->ep_input);
>
> - p = eb->eb_port;
> + p = ep->ep_port;
>
> switch (cmd) {
> case SIOCSIFADDR:
> @@ -693,7 +693,7 @@ tpmr_p_output(struct ifnet *ifp0, struct
> {
> int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
> struct rtentry *) = NULL;
> - const struct ether_brport *eb;
> + const struct ether_port *ep;
>
> /* restrict transmission to bpf only */
> if ((m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
> @@ -702,9 +702,9 @@ tpmr_p_output(struct ifnet *ifp0, struct
> }
>
> smr_read_enter();
> - eb = ether_brport_get(ifp0);
> - if (eb != NULL && eb->eb_input == tpmr_input) {
> - struct tpmr_port *p = eb->eb_port;
> + ep = ether_brport_get(ifp0);
> + if (ep != NULL && ep->ep_input == tpmr_input) {
> + struct tpmr_port *p = ep->ep_port;
> p_output = p->p_output; /* code doesn't go away */
> }
> smr_read_leave();
> Index: net/if_trunk.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_trunk.c,v
> diff -u -p -r1.157 if_trunk.c
> --- net/if_trunk.c 7 Jul 2025 02:28:50 -0000 1.157
> +++ net/if_trunk.c 22 Nov 2025 09:28:42 -0000
> @@ -24,6 +24,7 @@
> #include <sys/sockio.h>
> #include <sys/systm.h>
> #include <sys/task.h>
> +#include <sys/smr.h>
>
> #include <crypto/siphash.h>
>
> @@ -72,7 +73,11 @@ int trunk_ether_delmulti(struct trunk_s
> void trunk_ether_purgemulti(struct trunk_softc *);
> int trunk_ether_cmdmulti(struct trunk_port *, u_long);
> int trunk_ioctl_allports(struct trunk_softc *, u_long, caddr_t);
> -void trunk_input(struct ifnet *, struct mbuf *, struct netstack *);
> +void trunk_port_take(void *);
> +void trunk_port_rele(void *);
> +struct mbuf *
> + trunk_input(struct ifnet *, struct mbuf *, uint64_t, void *,
> + struct netstack *);
> void trunk_start(struct ifnet *);
> void trunk_init(struct ifnet *);
> void trunk_stop(struct ifnet *);
> @@ -296,7 +301,7 @@ trunk_port_create(struct trunk_softc *tr
> return (EPROTONOSUPPORT);
>
> ac0 = (struct arpcom *)ifp;
> - if (ac0->ac_trunkport != NULL)
> + if (SMR_PTR_GET_LOCKED(&ac0->ac_trport) != NULL)
> return (EBUSY);
>
> /* Take MTU from the first member port */
> @@ -318,6 +323,12 @@ trunk_port_create(struct trunk_softc *tr
> if ((tp = malloc(sizeof *tp, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL)
> return (ENOMEM);
>
> + refcnt_init(&tp->tp_refs);
> + tp->tp_ether_port.ep_input = trunk_input;
> + tp->tp_ether_port.ep_port_take = trunk_port_take;
> + tp->tp_ether_port.ep_port_rele = trunk_port_rele;
> + tp->tp_ether_port.ep_port = tp;
> +
> /* Check if port is a stacked trunk */
> SLIST_FOREACH(tr_ptr, &trunk_list, tr_entries) {
> if (ifp == &tr_ptr->tr_ac.ac_if) {
> @@ -377,11 +388,9 @@ trunk_port_create(struct trunk_softc *tr
> if (tr->tr_port_create != NULL)
> error = (*tr->tr_port_create)(tp);
>
> - /* Change input handler of the physical interface. */
> - tp->tp_input = ifp->if_input;
> + /* Assing input handler of the physical interface. */
> NET_ASSERT_LOCKED();
> - ac0->ac_trunkport = tp;
> - ifp->if_input = trunk_input;
> + SMR_PTR_SET_LOCKED(&ac0->ac_trport, &tp->tp_ether_port);
>
> return (error);
> }
> @@ -413,8 +422,10 @@ trunk_port_destroy(struct trunk_port *tp
>
> /* Restore previous input handler. */
> NET_ASSERT_LOCKED();
> - ifp->if_input = tp->tp_input;
> - ac0->ac_trunkport = NULL;
> + KASSERT(SMR_PTR_GET_LOCKED(&ac0->ac_trport) == &tp->tp_ether_port);
> + SMR_PTR_SET_LOCKED(&ac0->ac_trport, NULL);
> + smr_barrier();
> + refcnt_finalize(&tp->tp_refs, "trdtor");
>
> /* Remove multicast addresses from this port */
> trunk_ether_cmdmulti(tp, SIOCDELMULTI);
> @@ -664,9 +675,12 @@ trunk_ioctl(struct ifnet *ifp, u_long cm
> */
> NET_ASSERT_LOCKED();
> SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
> - /* if_ih_remove(tp->tp_if, trunk_input, tp); */
> - tp->tp_if->if_input = tp->tp_input;
> + struct arpcom *ac = (struct arpcom *)tp->tp_if;
> + SMR_PTR_SET_LOCKED(&ac->ac_trport, NULL);
> }
> + SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
> + refcnt_finalize(&tp->tp_refs, "trunkset");
> + smr_barrier();
> if (tr->tr_proto != TRUNK_PROTO_NONE)
> error = tr->tr_detach(tr);
> if (error != 0)
> @@ -681,9 +695,11 @@ trunk_ioctl(struct ifnet *ifp, u_long cm
> if (tr->tr_proto != TRUNK_PROTO_NONE)
> error = trunk_protos[i].ti_attach(tr);
> SLIST_FOREACH(tp, &tr->tr_ports, tp_entries) {
> - /* if_ih_insert(tp->tp_if,
> - trunk_input, tp); */
> - tp->tp_if->if_input = trunk_input;
> + struct arpcom *ac =
> + (struct arpcom *)tp->tp_if;
> + refcnt_init(&tp->tp_refs);
> + SMR_PTR_SET_LOCKED(&ac->ac_trport,
> + &tp->tp_ether_port);
> }
> /* Update trunk capabilities */
> tr->tr_capabilities = trunk_capabilities(tr);
> @@ -1144,10 +1160,24 @@ trunk_stop(struct ifnet *ifp)
> }
>
> void
> -trunk_input(struct ifnet *ifp, struct mbuf *m, struct netstack *ns)
> +trunk_port_take(void *port)
> {
> - struct arpcom *ac0 = (struct arpcom *)ifp;
> - struct trunk_port *tp;
> + struct trunk_port *tp = port;
> + refcnt_take(&tp->tp_refs);
> +}
> +
> +void
> +trunk_port_rele(void *port)
> +{
> + struct trunk_port *tp = port;
> + refcnt_rele_wake(&tp->tp_refs);
> +}
> +
> +struct mbuf *
> +trunk_input(struct ifnet *ifp, struct mbuf *m, uint64_t dst, void *port,
> + struct netstack *ns)
> +{
> + struct trunk_port *tp = port;
> struct trunk_softc *tr;
> struct ifnet *trifp = NULL;
> struct ether_header *eh;
> @@ -1163,7 +1193,6 @@ trunk_input(struct ifnet *ifp, struct mb
> if (ifp->if_type != IFT_IEEE8023ADLAG)
> goto bad;
>
> - tp = (struct trunk_port *)ac0->ac_trunkport;
> if ((tr = (struct trunk_softc *)tp->tp_trunk) == NULL)
> goto bad;
>
> @@ -1176,7 +1205,7 @@ trunk_input(struct ifnet *ifp, struct mb
> * We stop here if the packet has been consumed
> * by the protocol routine.
> */
> - return;
> + return (NULL);
> }
>
> if ((trifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
> @@ -1190,20 +1219,19 @@ trunk_input(struct ifnet *ifp, struct mb
> (ifp->if_flags & IFF_PROMISC) &&
> (trifp->if_flags & IFF_PROMISC) == 0) {
> if (bcmp(&tr->tr_ac.ac_enaddr, eh->ether_dhost,
> - ETHER_ADDR_LEN)) {
> - m_freem(m);
> - return;
> - }
> + ETHER_ADDR_LEN))
> + goto drop;
> }
>
> -
> if_vinput(trifp, m, ns);
> - return;
> + return (NULL);
>
> bad:
> if (trifp != NULL)
> trifp->if_ierrors++;
> +drop:
> m_freem(m);
> + return (NULL);
> }
>
> int
> Index: net/if_trunk.h
> ===================================================================
> RCS file: /cvs/src/sys/net/if_trunk.h,v
> diff -u -p -r1.32 if_trunk.h
> --- net/if_trunk.h 2 Mar 2025 21:28:32 -0000 1.32
> +++ net/if_trunk.h 22 Nov 2025 09:28:42 -0000
> @@ -159,6 +159,8 @@ struct trunk_softc;
> struct trunk_port {
> struct ifnet *tp_if; /* physical interface */
> struct trunk_softc *tp_trunk; /* parent trunk */
> + struct refcnt tp_refs;
> + struct ether_port tp_ether_port;
> u_int8_t tp_lladdr[ETHER_ADDR_LEN];
> caddr_t tp_psc; /* protocol data */
>
> @@ -172,7 +174,6 @@ struct trunk_port {
> int (*tp_ioctl)(struct ifnet *, u_long, caddr_t);
> int (*tp_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
> struct rtentry *);
> - void (*tp_input)(struct ifnet *, struct mbuf *, struct netstack *);
>
> SLIST_ENTRY(trunk_port) tp_entries;
> };
> Index: net/if_veb.c
> ===================================================================
> RCS file: /cvs/src/sys/net/if_veb.c,v
> diff -u -p -r1.54 if_veb.c
> --- net/if_veb.c 22 Nov 2025 06:07:36 -0000 1.54
> +++ net/if_veb.c 22 Nov 2025 09:28:42 -0000
> @@ -117,7 +117,7 @@ struct veb_port {
>
> struct veb_softc *p_veb;
>
> - struct ether_brport p_brport;
> + struct ether_port p_brport;
>
> unsigned int p_link_state;
> unsigned int p_bif_flags;
> @@ -289,8 +289,8 @@ static void veb_eb_port_rele(void *, vo
> static size_t veb_eb_port_ifname(void *, char *, size_t, void *);
> static void veb_eb_port_sa(void *, struct sockaddr_storage *, void *);
>
> -static void veb_eb_brport_take(void *);
> -static void veb_eb_brport_rele(void *);
> +static void veb_ep_brport_take(void *);
> +static void veb_ep_brport_rele(void *);
>
> static const struct etherbridge_ops veb_etherbridge_ops = {
> veb_eb_port_cmp,
> @@ -1660,6 +1660,10 @@ veb_add_port(struct veb_softc *sc, const
> p->p_ioctl = ifp0->if_ioctl;
> p->p_output = ifp0->if_output;
>
> + p->p_brport.ep_port = p;
> + p->p_brport.ep_port_take = veb_ep_brport_take;
> + p->p_brport.ep_port_rele = veb_ep_brport_rele;
> +
> if (span) {
> ports_ptr = &sc->sc_spans;
>
> @@ -1668,7 +1672,7 @@ veb_add_port(struct veb_softc *sc, const
> goto free;
> }
>
> - p->p_brport.eb_input = veb_span_input;
> + p->p_brport.ep_input = veb_span_input;
> p->p_bif_flags = IFBIF_SPAN;
> } else {
> ports_ptr = &sc->sc_ports;
> @@ -1678,13 +1682,10 @@ veb_add_port(struct veb_softc *sc, const
> goto free;
>
> p->p_bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER;
> - p->p_brport.eb_input = isvport ?
> + p->p_brport.ep_input = isvport ?
> veb_vport_input : veb_port_input;
> }
>
> - p->p_brport.eb_port_take = veb_eb_brport_take;
> - p->p_brport.eb_port_rele = veb_eb_brport_rele;
> -
> om = SMR_PTR_GET_LOCKED(ports_ptr);
> nm = veb_ports_insert(om, p);
>
> @@ -1699,8 +1700,6 @@ veb_add_port(struct veb_softc *sc, const
> task_set(&p->p_dtask, veb_p_detach, p);
> if_detachhook_add(ifp0, &p->p_dtask);
>
> - p->p_brport.eb_port = p;
> -
> /* commit */
> SMR_PTR_SET_LOCKED(ports_ptr, nm);
>
> @@ -2970,19 +2969,19 @@ veb_del_vid_addr(struct veb_softc *sc, c
> static int
> veb_p_ioctl(struct ifnet *ifp0, u_long cmd, caddr_t data)
> {
> - const struct ether_brport *eb = ether_brport_get_locked(ifp0);
> + const struct ether_port *ep = ether_brport_get_locked(ifp0);
> struct veb_port *p;
> int error = 0;
>
> - KASSERTMSG(eb != NULL,
> + KASSERTMSG(ep != NULL,
> "%s: %s called without an ether_brport set",
> ifp0->if_xname, __func__);
> - KASSERTMSG((eb->eb_input == veb_port_input) ||
> - (eb->eb_input == veb_span_input),
> - "%s called %s, but eb_input (%p) seems wrong",
> - ifp0->if_xname, __func__, eb->eb_input);
> + KASSERTMSG((ep->ep_input == veb_port_input) ||
> + (ep->ep_input == veb_span_input),
> + "%s called %s, but ep_input (%p) seems wrong",
> + ifp0->if_xname, __func__, ep->ep_input);
>
> - p = eb->eb_port;
> + p = ep->ep_port;
>
> switch (cmd) {
> case SIOCSIFADDR:
> @@ -3003,7 +3002,7 @@ veb_p_output(struct ifnet *ifp0, struct
> {
> int (*p_output)(struct ifnet *, struct mbuf *, struct sockaddr *,
> struct rtentry *) = NULL;
> - const struct ether_brport *eb;
> + const struct ether_port *ep;
>
> /* restrict transmission to bpf only */
> if ((m_tag_find(m, PACKET_TAG_DLT, NULL) == NULL)) {
> @@ -3012,9 +3011,9 @@ veb_p_output(struct ifnet *ifp0, struct
> }
>
> smr_read_enter();
> - eb = ether_brport_get(ifp0);
> - if (eb != NULL && eb->eb_input == veb_port_input) {
> - struct veb_port *p = eb->eb_port;
> + ep = ether_brport_get(ifp0);
> + if (ep != NULL && ep->ep_input == veb_port_input) {
> + struct veb_port *p = ep->ep_port;
> p_output = p->p_output; /* code doesn't go away */
> }
> smr_read_leave();
> @@ -3189,13 +3188,13 @@ veb_eb_port_rele(void *arg, void *port)
> }
>
> static void
> -veb_eb_brport_take(void *port)
> +veb_ep_brport_take(void *port)
> {
> veb_eb_port_take(NULL, port);
> }
>
> static void
> -veb_eb_brport_rele(void *port)
> +veb_ep_brport_rele(void *port)
> {
> veb_eb_port_rele(NULL, port);
> }
> @@ -3377,7 +3376,7 @@ static int
> vport_enqueue(struct ifnet *ifp, struct mbuf *m)
> {
> struct arpcom *ac;
> - const struct ether_brport *eb;
> + const struct ether_port *ep;
> int error = ENETDOWN;
> #if NBPFILTER > 0
> caddr_t if_bpf;
> @@ -3399,13 +3398,13 @@ vport_enqueue(struct ifnet *ifp, struct
> ac = (struct arpcom *)ifp;
>
> smr_read_enter();
> - eb = SMR_PTR_GET(&ac->ac_brport);
> - if (eb != NULL)
> - eb->eb_port_take(eb->eb_port);
> + ep = SMR_PTR_GET(&ac->ac_brport);
> + if (ep != NULL)
> + ep->ep_port_take(ep->ep_port);
> smr_read_leave();
> - if (eb != NULL) {
> + if (ep != NULL) {
> struct mbuf *(*input)(struct ifnet *, struct mbuf *,
> - uint64_t, void *, struct netstack *) = eb->eb_input;
> + uint64_t, void *, struct netstack *) = ep->ep_input;
> struct ether_header *eh;
> uint64_t dst;
>
> @@ -3423,11 +3422,11 @@ vport_enqueue(struct ifnet *ifp, struct
>
> if (input == veb_vport_input)
> input = veb_port_input;
> - m = (*input)(ifp, m, dst, eb->eb_port, NULL);
> + m = (*input)(ifp, m, dst, ep->ep_port, NULL);
>
> error = 0;
>
> - eb->eb_port_rele(eb->eb_port);
> + ep->ep_port_rele(ep->ep_port);
> }
>
> m_freem(m);
> Index: netinet/if_ether.h
> ===================================================================
> RCS file: /cvs/src/sys/netinet/if_ether.h,v
> diff -u -p -r1.97 if_ether.h
> --- netinet/if_ether.h 3 Nov 2025 23:50:57 -0000 1.97
> +++ netinet/if_ether.h 22 Nov 2025 09:28:42 -0000
> @@ -131,6 +131,10 @@ struct ether_vlan_header {
> #define ETH64_IS_8021_RSVD(_e64) \
> (((_e64) & ETH64_8021_RSVD_MASK) == ETH64_8021_RSVD_PREFIX)
>
> +#define ETH64_8021_NEAREST_CUSTOMER_BR 0x0180c2000000ULL
> +#define ETH64_8021_NON_TPMR_BR 0x0180c2000003ULL
> +#define ETH64_8021_NEAREST_BR 0x0180c200000eULL
> +
> /*
> * Ethernet MTU constants.
> */
> @@ -220,12 +224,12 @@ do { \
>
> #include <net/if_var.h> /* for "struct ifnet" */
>
> -struct ether_brport {
> - struct mbuf *(*eb_input)(struct ifnet *, struct mbuf *,
> +struct ether_port {
> + struct mbuf *(*ep_input)(struct ifnet *, struct mbuf *,
> uint64_t, void *, struct netstack *);
> - void (*eb_port_take)(void *);
> - void (*eb_port_rele)(void *);
> - void *eb_port;
> + void (*ep_port_take)(void *);
> + void (*ep_port_rele)(void *);
> + void *ep_port;
> };
>
> /*
> @@ -241,8 +245,8 @@ struct arpcom {
> int ac_multicnt; /* length of ac_multiaddrs */
> int ac_multirangecnt; /* number of mcast ranges */
>
> - void *ac_trunkport;
> - const struct ether_brport *ac_brport;
> + const struct ether_port *ac_trport;
> + const struct ether_port *ac_brport;
> };
>
> extern int arpt_keep; /* arp resolved cache expire */
> @@ -289,11 +293,11 @@ void ether_rtrequest(struct ifnet *, int
> char *ether_sprintf(u_char *);
>
> int ether_brport_isset(struct ifnet *);
> -void ether_brport_set(struct ifnet *, const struct ether_brport *);
> +void ether_brport_set(struct ifnet *, const struct ether_port *);
> void ether_brport_clr(struct ifnet *);
> -const struct ether_brport *
> +const struct ether_port *
> ether_brport_get(struct ifnet *);
> -const struct ether_brport *
> +const struct ether_port *
> ether_brport_get_locked(struct ifnet *);
>
> uint64_t ether_addr_to_e64(const struct ether_addr *);
move aggr/trunk input processing into ether_input