Download raw body.
trunk(4): deprecate LACP mode
On Tue, Dec 02, 2025 at 05:14:58PM +1000, David Gwynne wrote:
> 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));
> }
>
Hello,
Do you know of any cases where aggr is _not_ a complete replacement for
trunk? A few years ago I setup a Dell PowerConnect 5224 in LACP and was
unable to get aggr to work, however, the following does work for me:
cat /etc/hostname.trunk0
trunkproto lacp
trunkport bnx0
trunkport bnx1
trunkport bge0
up
I can give it a shot again tonight to see if I missed something when I
initially tried aggr.
--
Chaz
trunk(4): deprecate LACP mode