Download raw body.
trunk(4): deprecate LACP mode
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));
}
trunk(4): deprecate LACP mode