From: Alexander Bluhm Subject: Re: move aggr/trunk input processing into ether_input To: David Gwynne Cc: tech@openbsd.org Date: Tue, 25 Nov 2025 21:05:17 +0100 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 > #include > #include > +#include > > #include > > @@ -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 /* 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 *);