git: 9f69446d4548 - main - lacp: fix link state with multiple aggregators

From: Andrew Gallatin <gallatin_at_FreeBSD.org>
Date: Tue, 12 May 2026 13:02:00 UTC
The branch main has been updated by gallatin:

URL: https://cgit.FreeBSD.org/src/commit/?id=9f69446d45484c16d2b9b9bf71bbb2fca86cb6e5

commit 9f69446d45484c16d2b9b9bf71bbb2fca86cb6e5
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2026-05-12 12:52:24 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2026-05-12 12:52:24 +0000

    lacp: fix link state with multiple aggregators
    
    When we have multiple aggregators, the link state should reflect the
    state of the active aggregator.
    
    This change was prompted by a script pruning 10GbE interfaces from an
    lacp bundle with 100GbE interfaces. Mixing speeds like this creates multiple
    aggregators.  When the last 10GbE interface was removed, lagg0 would loose
    link because the current aggregator's port count would drop to 0, even
    though the 100GbE aggregator had active ports. This left the system in a
    hard to diagnose state where lagg0 reported "active", but all outgoing
    IP traffic was dropped, due to the RT_LINK_IS_UP() check noticing lagg0's
    if_link_state was marked as down.
    
    Reviewed by: zlei
    Sponsored by: Netflix
    Differential Revision: https://reviews.freebsd.org/D56579
---
 sys/net/ieee8023ad_lacp.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/sys/net/ieee8023ad_lacp.c b/sys/net/ieee8023ad_lacp.c
index 281d16bc6c05..8c695f71cc0f 100644
--- a/sys/net/ieee8023ad_lacp.c
+++ b/sys/net/ieee8023ad_lacp.c
@@ -682,6 +682,7 @@ static void
 lacp_disable_distributing(struct lacp_port *lp)
 {
 	struct lacp_aggregator *la = lp->lp_aggregator;
+	struct lacp_aggregator *la_active;
 	struct lacp_softc *lsc = lp->lp_lsc;
 	struct lagg_softc *sc = lsc->lsc_softc;
 	char buf[LACP_LAGIDSTR_MAX+1];
@@ -703,7 +704,6 @@ lacp_disable_distributing(struct lacp_port *lp)
 
 	TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q);
 	la->la_nports--;
-	sc->sc_active = la->la_nports;
 
 	if (lsc->lsc_active_aggregator == la) {
 		lacp_suppress_distributing(lsc, la);
@@ -713,6 +713,8 @@ lacp_disable_distributing(struct lacp_port *lp)
 	}
 
 	lp->lp_state &= ~LACP_STATE_DISTRIBUTING;
+	la_active = lsc->lsc_active_aggregator;
+	sc->sc_active = la_active != NULL ? la_active->la_nports : 0;
 	if_link_state_change(sc->sc_ifp,
 	    sc->sc_active ? LINK_STATE_UP : LINK_STATE_DOWN);
 }
@@ -721,6 +723,7 @@ static void
 lacp_enable_distributing(struct lacp_port *lp)
 {
 	struct lacp_aggregator *la = lp->lp_aggregator;
+	struct lacp_aggregator *la_active;
 	struct lacp_softc *lsc = lp->lp_lsc;
 	struct lagg_softc *sc = lsc->lsc_softc;
 	char buf[LACP_LAGIDSTR_MAX+1];
@@ -739,7 +742,6 @@ lacp_enable_distributing(struct lacp_port *lp)
 	KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid"));
 	TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q);
 	la->la_nports++;
-	sc->sc_active = la->la_nports;
 
 	lp->lp_state |= LACP_STATE_DISTRIBUTING;
 
@@ -750,6 +752,8 @@ lacp_enable_distributing(struct lacp_port *lp)
 		/* try to become the active aggregator */
 		lacp_select_active_aggregator(lsc);
 
+	la_active = lsc->lsc_active_aggregator;
+	sc->sc_active = la_active != NULL ? la_active->la_nports : 0;
 	if_link_state_change(sc->sc_ifp,
 	    sc->sc_active ? LINK_STATE_UP : LINK_STATE_DOWN);
 }