From: David Gwynne Subject: move aggr/trunk input processing into ether_input To: tech@openbsd.org Date: Sun, 23 Nov 2025 15:55:05 +1000 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? 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 *);