Index | Thread | Search

From:
David Gwynne <david@gwynne.id.au>
Subject:
trunk(4): deprecate LACP mode
To:
tech@openbsd.org
Date:
Tue, 2 Dec 2025 17:14:58 +1000

Download raw body.

Thread
aggr(4) is a better option for LACP for several reasons:

- in my experience aggr is better at following the standard

it doesn't get into weird states that blackhole traffic at the worst
possible times.

- aggr is mpsafe

the last time i looked at trying to make the lacp code mpsafe in
trunk(4) i ended up writing aggr(4). this lack of mpsafety is now
making it hard to unlock more of the network stack.

- aggr is faster than trunk

looking at the benchmarking we did when i first introduced aggr,
it looks like using trunk(4) more than halved the pps you can
forward through a box, while aggr was more like a 10 to 15% hit.
switching from trunk to aggr in our environment doubled our firewall
performance.

apart from these implementation differences, there's a few operational
ones too that sthen@ has pointed out:

- aggr uses a random MAC by default, trunk uses the MAC of the first-
  added child port.

- aggr does not bring the interface up automatically, you must use "up".

anyone still using trunk(4) for LACP links will need to do at least the
following:

# mv hostname.trunk0 hostname.aggr0
# echo up >> hostname.aggr0

if you want a predictable MAC address on an aggr interface, you'll need
to specify the lladdr before bringing it up. for example, my configs
looks like this:

lladdr fe:e1:ba:d0:e8:43
trunkport ixl0
trunkport ixl1
up

a bunch of vlan interfaces sit on top of this, which is why it doesnt
have address config of its own.

an example with ips:

lladdr fe:e1:ba:d0:7c:ff
trunkproto lacp
trunkport ix0
trunkport ix1
inet 192.0.2.216 255.255.255.0
inet6 2001:db8:230f:bc24:d3d8:7b58:f17c:f96e 64
up

Index: share/man/man4/trunk.4
===================================================================
RCS file: /cvs/src/share/man/man4/trunk.4,v
diff -u -p -r1.31 trunk.4
--- share/man/man4/trunk.4	24 Aug 2020 07:34:00 -0000	1.31
+++ share/man/man4/trunk.4	2 Dec 2025 05:05:16 -0000
@@ -38,7 +38,6 @@ command.
 The driver currently supports the trunk protocols
 .Ic broadcast ,
 .Ic failover ,
-.Ic lacp ,
 .Ic loadbalance ,
 .Ic none ,
 and
@@ -58,17 +57,6 @@ If the master port becomes unavailable,
 the next active port is used.
 The first interface added is the master port;
 any interfaces added after that are used as failover devices.
-.It Ic lacp
-Uses the IEEE 802.3ad (renamed to 802.1AX in 2014)
-Link Aggregation Control Protocol (LACP)
-and the Marker Protocol
-to increase link speed and provide redundancy.
-LACP trunk groups are composed of ports of the same speed,
-set to full-duplex operation.
-This protocol requires a switch which supports LACP.
-By default, the LACP implementation uses active-mode LACP,
-slow timeout, and 0x8000 (medium) priority as system and port
-priorities.
 .It Ic loadbalance
 Distributes outgoing traffic through all active ports
 and accepts incoming traffic from any active port.
@@ -90,6 +78,12 @@ The configuration can be done at runtime
 .Xr hostname.if 5
 configuration file for
 .Xr netstart 8 .
+.Pp
+.Nm
+does not implement
+IEEE 802.1AX (formerly 802.3ad) Link Aggregation,
+it is supported by
+.Xr aggr 4 .
 .Sh EXAMPLES
 Create a simple round robin trunk with two
 .Xr bge 4
@@ -113,6 +107,7 @@ device will be used:
 	192.168.1.1 netmask 255.255.255.0
 .Ed
 .Sh SEE ALSO
+.Xr aggr 4 ,
 .Xr inet 4 ,
 .Xr hostname.if 5 ,
 .Xr ifconfig 8 ,
@@ -122,6 +117,10 @@ The
 .Nm
 device first appeared in
 .Ox 3.8 .
+Support for 802.3ad Link Aggregation was added in
+.Ox 4.4
+and removed in
+.Ox 7.9 .
 .Sh AUTHORS
 The
 .Nm
Index: sys/conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
diff -u -p -r1.748 files
--- sys/conf/files	14 Nov 2025 01:55:07 -0000	1.748
+++ sys/conf/files	2 Dec 2025 05:05:16 -0000
@@ -868,7 +872,6 @@ file net/slcompress.c			ppp
 file net/if_enc.c			enc
 file net/if_gre.c			gre			needs-count
 file net/if_trunk.c			trunk
-file net/trunklacp.c			trunk
 file net/if_aggr.c			aggr
 file net/if_tpmr.c			tpmr
 file net/if_mpe.c			mpe
Index: sys/net/if_trunk.c
===================================================================
RCS file: /cvs/src/sys/net/if_trunk.c,v
diff -u -p -r1.159 if_trunk.c
--- sys/net/if_trunk.c	2 Dec 2025 03:24:19 -0000	1.159
+++ sys/net/if_trunk.c	2 Dec 2025 05:05:16 -0000
@@ -42,7 +42,6 @@
 #endif
 
 #include <net/if_trunk.h>
-#include <net/trunklacp.h>
 
 #include "bpfilter.h"
 #if NBPFILTER > 0
@@ -125,13 +124,6 @@ int	 trunk_bcast_start(struct trunk_soft
 int	 trunk_bcast_input(struct trunk_softc *, struct trunk_port *,
 	    struct mbuf *);
 
-/* 802.3ad LACP */
-int	 trunk_lacp_attach(struct trunk_softc *);
-int	 trunk_lacp_detach(struct trunk_softc *);
-int	 trunk_lacp_start(struct trunk_softc *, struct mbuf *);
-int	 trunk_lacp_input(struct trunk_softc *, struct trunk_port *,
-	    struct mbuf *);
-
 /* Trunk protocol table */
 static const struct {
 	enum trunk_proto	ti_proto;
@@ -141,7 +133,6 @@ static const struct {
 	{ TRUNK_PROTO_FAILOVER,		trunk_fail_attach },
 	{ TRUNK_PROTO_LOADBALANCE,	trunk_lb_attach },
 	{ TRUNK_PROTO_BROADCAST,	trunk_bcast_attach },
-	{ TRUNK_PROTO_LACP,		trunk_lacp_attach },
 	{ TRUNK_PROTO_NONE,		NULL }
 };
 
@@ -606,11 +597,6 @@ trunk_port2req(struct trunk_port *tp, st
 		if (TRUNK_PORTACTIVE(tp))
 			rp->rp_flags |= TRUNK_PORT_ACTIVE;
 		break;
-
-	case TRUNK_PROTO_LACP:
-		/* LACP has a different definition of active */
-		rp->rp_flags = lacp_port_status(tp);
-		break;
 	default:
 		break;
 	}
@@ -622,11 +608,8 @@ trunk_ioctl(struct ifnet *ifp, u_long cm
 	struct trunk_softc *tr = (struct trunk_softc *)ifp->if_softc;
 	struct trunk_reqall *ra = (struct trunk_reqall *)data;
 	struct trunk_reqport *rp = (struct trunk_reqport *)data, rpbuf;
-	struct trunk_opts *tro = (struct trunk_opts *)data;
 	struct ifreq *ifr = (struct ifreq *)data;
-	struct lacp_softc *lsc;
 	struct trunk_port *tp;
-	struct lacp_port *lp;
 	struct ifnet *tpif;
 	int i, error = 0;
 
@@ -710,16 +693,7 @@ trunk_ioctl(struct ifnet *ifp, u_long cm
 		break;
 	case SIOCGTRUNKOPTS:
 		/* Only LACP trunks have options atm */
-		if (tro->to_proto != TRUNK_PROTO_LACP) {
-			error = EPROTONOSUPPORT;
-			break;
-		}
-		lsc = LACP_SOFTC(tr);
-		tro->to_lacpopts.lacp_mode = lsc->lsc_mode;
-		tro->to_lacpopts.lacp_timeout = lsc->lsc_timeout;
-		tro->to_lacpopts.lacp_prio = lsc->lsc_sys_prio;
-		tro->to_lacpopts.lacp_portprio = lsc->lsc_port_prio;
-		tro->to_lacpopts.lacp_ifqprio = lsc->lsc_ifq_prio;
+		error = EPROTONOSUPPORT;
 		break;
 	case SIOCSTRUNKOPTS:
 		if ((error = suser(curproc)) != 0) {
@@ -727,74 +701,7 @@ trunk_ioctl(struct ifnet *ifp, u_long cm
 			break;
 		}
 		/* Only LACP trunks have options atm */
-		if (tro->to_proto != TRUNK_PROTO_LACP) {
-			error = EPROTONOSUPPORT;
-			break;
-		}
-		lsc = LACP_SOFTC(tr);
-		switch(tro->to_opts) {
-			case TRUNK_OPT_LACP_MODE:
-				/*
-				 * Ensure mode changes occur immediately
-				 * on all ports
-				 */
-				lsc->lsc_mode = tro->to_lacpopts.lacp_mode;
-				if (lsc->lsc_mode == 0) {
-					LIST_FOREACH(lp, &lsc->lsc_ports,
-					    lp_next)
-						lp->lp_state &=
-						    ~LACP_STATE_ACTIVITY;
-				} else {
-					LIST_FOREACH(lp, &lsc->lsc_ports,
-					    lp_next)
-						lp->lp_state |=
-						    LACP_STATE_ACTIVITY;
-				}
-				break;
-			case TRUNK_OPT_LACP_TIMEOUT:
-				/*
-				 * Ensure timeout changes occur immediately
-				 * on all ports
-				 */
-				lsc->lsc_timeout =
-				    tro->to_lacpopts.lacp_timeout;
-				if (lsc->lsc_timeout == 0) {
-					LIST_FOREACH(lp, &lsc->lsc_ports,
-					    lp_next)
-						lp->lp_state &=
-						    ~LACP_STATE_TIMEOUT;
-				} else {
-					LIST_FOREACH(lp, &lsc->lsc_ports,
-					    lp_next)
-						lp->lp_state |=
-						    LACP_STATE_TIMEOUT;
-				}
-				break;
-			case TRUNK_OPT_LACP_SYS_PRIO:
-				if (tro->to_lacpopts.lacp_prio == 0) {
-					error = EINVAL;	
-					break;
-				}
-				lsc->lsc_sys_prio = tro->to_lacpopts.lacp_prio;
-				break;
-			case TRUNK_OPT_LACP_PORT_PRIO:
-				if (tro->to_lacpopts.lacp_portprio == 0) {
-					error = EINVAL;	
-					break;
-				}
-				lsc->lsc_port_prio =
-				    tro->to_lacpopts.lacp_portprio;
-				break;
-			case TRUNK_OPT_LACP_IFQ_PRIO:
-				if (tro->to_lacpopts.lacp_ifqprio >
-				    IFQ_MAXPRIO) {
-					error = EINVAL;	
-					break;
-				}
-				lsc->lsc_ifq_prio =
-				    tro->to_lacpopts.lacp_ifqprio;
-				break;
-		}
+		error = EPROTONOSUPPORT;
 		break;
 	case SIOCGTRUNKPORT:
 		if (rp->rp_portname[0] == '\0' ||
@@ -1681,70 +1588,4 @@ int
 trunk_bcast_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
 {
 	return (0);
-}
-
-/*
- * 802.3ad LACP
- */
-
-int
-trunk_lacp_attach(struct trunk_softc *tr)
-{
-	struct trunk_port *tp;
-	int error;
-
-	tr->tr_detach = trunk_lacp_detach;
-	tr->tr_port_create = lacp_port_create;
-	tr->tr_port_destroy = lacp_port_destroy;
-	tr->tr_linkstate = lacp_linkstate;
-	tr->tr_start = trunk_lacp_start;
-	tr->tr_input = trunk_lacp_input;
-	tr->tr_init = lacp_init;
-	tr->tr_stop = lacp_stop;
-	tr->tr_req = lacp_req;
-	tr->tr_portreq = lacp_portreq;
-
-	error = lacp_attach(tr);
-	if (error)
-		return (error);
-
-	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
-		lacp_port_create(tp);
-
-	return (error);
-}
-
-int
-trunk_lacp_detach(struct trunk_softc *tr)
-{
-	struct trunk_port *tp;
-	int error;
-
-	SLIST_FOREACH(tp, &tr->tr_ports, tp_entries)
-		lacp_port_destroy(tp);
-
-	/* unlocking is safe here */
-	error = lacp_detach(tr);
-
-	return (error);
-}
-
-int
-trunk_lacp_start(struct trunk_softc *tr, struct mbuf *m)
-{
-	struct trunk_port *tp;
-
-	tp = lacp_select_tx_port(tr, m);
-	if (tp == NULL) {
-		m_freem(m);
-		return (EBUSY);
-	}
-
-	return (if_enqueue(tp->tp_if, m));
-}
-
-int
-trunk_lacp_input(struct trunk_softc *tr, struct trunk_port *tp, struct mbuf *m)
-{
-	return (lacp_input(tp, m));
 }