Index | Thread | Search

From:
David Gwynne <david@gwynne.id.au>
Subject:
move aggr/trunk input processing into ether_input
To:
tech@openbsd.org
Date:
Sun, 23 Nov 2025 15:55:05 +1000

Download raw body.

Thread
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 <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 *);