From: David Gwynne Subject: trunk(4): deprecate LACP mode To: tech@openbsd.org Date: Tue, 2 Dec 2025 17:14:58 +1000 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 -#include #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)); }