1b47888ceSAndrew Thompson /* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */ 2b47888ceSAndrew Thompson 3b47888ceSAndrew Thompson /*- 4fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-NetBSD 5fe267a55SPedro F. Giffuni * 6b47888ceSAndrew Thompson * Copyright (c)2005 YAMAMOTO Takashi, 73de18008SAndrew Thompson * Copyright (c)2008 Andrew Thompson <thompsa@FreeBSD.org> 8b47888ceSAndrew Thompson * All rights reserved. 9b47888ceSAndrew Thompson * 10b47888ceSAndrew Thompson * Redistribution and use in source and binary forms, with or without 11b47888ceSAndrew Thompson * modification, are permitted provided that the following conditions 12b47888ceSAndrew Thompson * are met: 13b47888ceSAndrew Thompson * 1. Redistributions of source code must retain the above copyright 14b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer. 15b47888ceSAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 16b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer in the 17b47888ceSAndrew Thompson * documentation and/or other materials provided with the distribution. 18b47888ceSAndrew Thompson * 19b47888ceSAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20b47888ceSAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21b47888ceSAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22b47888ceSAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23b47888ceSAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24b47888ceSAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25b47888ceSAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26b47888ceSAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27b47888ceSAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28b47888ceSAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29b47888ceSAndrew Thompson * SUCH DAMAGE. 30b47888ceSAndrew Thompson */ 31b47888ceSAndrew Thompson 32b47888ceSAndrew Thompson #include <sys/cdefs.h> 33b47888ceSAndrew Thompson __FBSDID("$FreeBSD$"); 34b47888ceSAndrew Thompson 35b2e60773SJohn Baldwin #include "opt_kern_tls.h" 36f3e7afe2SHans Petter Selasky #include "opt_ratelimit.h" 37f3e7afe2SHans Petter Selasky 38b47888ceSAndrew Thompson #include <sys/param.h> 39b47888ceSAndrew Thompson #include <sys/callout.h> 40c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 41b47888ceSAndrew Thompson #include <sys/mbuf.h> 42b47888ceSAndrew Thompson #include <sys/systm.h> 43b47888ceSAndrew Thompson #include <sys/malloc.h> 44b47888ceSAndrew Thompson #include <sys/kernel.h> /* hz */ 45b47888ceSAndrew Thompson #include <sys/socket.h> /* for net/if.h */ 46b47888ceSAndrew Thompson #include <sys/sockio.h> 475fc4c149SAndrew Thompson #include <sys/sysctl.h> 48b47888ceSAndrew Thompson #include <machine/stdarg.h> 493bf517e3SAndrew Thompson #include <sys/lock.h> 503bf517e3SAndrew Thompson #include <sys/rwlock.h> 5176039bc8SGleb Smirnoff #include <sys/taskqueue.h> 5200a80538SGreg Foster #include <sys/time.h> 53b47888ceSAndrew Thompson 54b47888ceSAndrew Thompson #include <net/if.h> 5576039bc8SGleb Smirnoff #include <net/if_var.h> 56b47888ceSAndrew Thompson #include <net/if_dl.h> 57b47888ceSAndrew Thompson #include <net/ethernet.h> 58a92c4bb6SHans Petter Selasky #include <net/infiniband.h> 59b47888ceSAndrew Thompson #include <net/if_media.h> 60b47888ceSAndrew Thompson #include <net/if_types.h> 61b47888ceSAndrew Thompson 6218242d3bSAndrew Thompson #include <net/if_lagg.h> 63b47888ceSAndrew Thompson #include <net/ieee8023ad_lacp.h> 64b47888ceSAndrew Thompson 65b47888ceSAndrew Thompson /* 66b47888ceSAndrew Thompson * actor system priority and port priority. 67b47888ceSAndrew Thompson * XXX should be configurable. 68b47888ceSAndrew Thompson */ 69b47888ceSAndrew Thompson 70b47888ceSAndrew Thompson #define LACP_SYSTEM_PRIO 0x8000 71b47888ceSAndrew Thompson #define LACP_PORT_PRIO 0x8000 72b47888ceSAndrew Thompson 73b47888ceSAndrew Thompson const uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 74b47888ceSAndrew Thompson { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 75b47888ceSAndrew Thompson 76b47888ceSAndrew Thompson static const struct tlv_template lacp_info_tlv_template[] = { 77b47888ceSAndrew Thompson { LACP_TYPE_ACTORINFO, 78b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 79b47888ceSAndrew Thompson { LACP_TYPE_PARTNERINFO, 80b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 81b47888ceSAndrew Thompson { LACP_TYPE_COLLECTORINFO, 82b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 83b47888ceSAndrew Thompson { 0, 0 }, 84b47888ceSAndrew Thompson }; 85b47888ceSAndrew Thompson 86b47888ceSAndrew Thompson static const struct tlv_template marker_info_tlv_template[] = { 87998971a7SAndrew Thompson { MARKER_TYPE_INFO, 88998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 89b47888ceSAndrew Thompson { 0, 0 }, 90b47888ceSAndrew Thompson }; 91b47888ceSAndrew Thompson 92b47888ceSAndrew Thompson static const struct tlv_template marker_response_tlv_template[] = { 93998971a7SAndrew Thompson { MARKER_TYPE_RESPONSE, 94998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 95b47888ceSAndrew Thompson { 0, 0 }, 96b47888ceSAndrew Thompson }; 97b47888ceSAndrew Thompson 983de18008SAndrew Thompson typedef void (*lacp_timer_func_t)(struct lacp_port *); 993de18008SAndrew Thompson 100b47888ceSAndrew Thompson static void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 101998971a7SAndrew Thompson static void lacp_fill_markerinfo(struct lacp_port *, 102998971a7SAndrew Thompson struct lacp_markerinfo *); 103b47888ceSAndrew Thompson 104b47888ceSAndrew Thompson static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 105b47888ceSAndrew Thompson static void lacp_suppress_distributing(struct lacp_softc *, 106b47888ceSAndrew Thompson struct lacp_aggregator *); 107b47888ceSAndrew Thompson static void lacp_transit_expire(void *); 1083de18008SAndrew Thompson static void lacp_update_portmap(struct lacp_softc *); 109b47888ceSAndrew Thompson static void lacp_select_active_aggregator(struct lacp_softc *); 110b47888ceSAndrew Thompson static uint16_t lacp_compose_key(struct lacp_port *); 111b47888ceSAndrew Thompson static int tlv_check(const void *, size_t, const struct tlvhdr *, 112b47888ceSAndrew Thompson const struct tlv_template *, boolean_t); 113b47888ceSAndrew Thompson static void lacp_tick(void *); 114b47888ceSAndrew Thompson 115b47888ceSAndrew Thompson static void lacp_fill_aggregator_id(struct lacp_aggregator *, 116b47888ceSAndrew Thompson const struct lacp_port *); 117b47888ceSAndrew Thompson static void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 118b47888ceSAndrew Thompson const struct lacp_peerinfo *); 119b47888ceSAndrew Thompson static int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 120b47888ceSAndrew Thompson const struct lacp_port *); 121b47888ceSAndrew Thompson static int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 122b47888ceSAndrew Thompson const struct lacp_peerinfo *); 123b47888ceSAndrew Thompson 124b47888ceSAndrew Thompson static struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 125b47888ceSAndrew Thompson struct lacp_port *); 126b47888ceSAndrew Thompson static void lacp_aggregator_addref(struct lacp_softc *, 127b47888ceSAndrew Thompson struct lacp_aggregator *); 128b47888ceSAndrew Thompson static void lacp_aggregator_delref(struct lacp_softc *, 129b47888ceSAndrew Thompson struct lacp_aggregator *); 130b47888ceSAndrew Thompson 131b47888ceSAndrew Thompson /* receive machine */ 132b47888ceSAndrew Thompson 1333de18008SAndrew Thompson static int lacp_pdu_input(struct lacp_port *, struct mbuf *); 1343de18008SAndrew Thompson static int lacp_marker_input(struct lacp_port *, struct mbuf *); 135b47888ceSAndrew Thompson static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 136b47888ceSAndrew Thompson static void lacp_sm_rx_timer(struct lacp_port *); 137b47888ceSAndrew Thompson static void lacp_sm_rx_set_expired(struct lacp_port *); 138b47888ceSAndrew Thompson static void lacp_sm_rx_update_ntt(struct lacp_port *, 139b47888ceSAndrew Thompson const struct lacpdu *); 140b47888ceSAndrew Thompson static void lacp_sm_rx_record_pdu(struct lacp_port *, 141b47888ceSAndrew Thompson const struct lacpdu *); 142b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected(struct lacp_port *, 143b47888ceSAndrew Thompson const struct lacpdu *); 144b47888ceSAndrew Thompson static void lacp_sm_rx_record_default(struct lacp_port *); 145b47888ceSAndrew Thompson static void lacp_sm_rx_update_default_selected(struct lacp_port *); 146b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 147b47888ceSAndrew Thompson const struct lacp_peerinfo *); 148b47888ceSAndrew Thompson 149b47888ceSAndrew Thompson /* mux machine */ 150b47888ceSAndrew Thompson 151b47888ceSAndrew Thompson static void lacp_sm_mux(struct lacp_port *); 152b47888ceSAndrew Thompson static void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 153b47888ceSAndrew Thompson static void lacp_sm_mux_timer(struct lacp_port *); 154b47888ceSAndrew Thompson 155b47888ceSAndrew Thompson /* periodic transmit machine */ 156b47888ceSAndrew Thompson 157b47888ceSAndrew Thompson static void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 158b47888ceSAndrew Thompson static void lacp_sm_ptx_tx_schedule(struct lacp_port *); 159b47888ceSAndrew Thompson static void lacp_sm_ptx_timer(struct lacp_port *); 160b47888ceSAndrew Thompson 161b47888ceSAndrew Thompson /* transmit machine */ 162b47888ceSAndrew Thompson 163b47888ceSAndrew Thompson static void lacp_sm_tx(struct lacp_port *); 164b47888ceSAndrew Thompson static void lacp_sm_assert_ntt(struct lacp_port *); 165b47888ceSAndrew Thompson 166b47888ceSAndrew Thompson static void lacp_run_timers(struct lacp_port *); 167b47888ceSAndrew Thompson static int lacp_compare_peerinfo(const struct lacp_peerinfo *, 168b47888ceSAndrew Thompson const struct lacp_peerinfo *); 169b47888ceSAndrew Thompson static int lacp_compare_systemid(const struct lacp_systemid *, 170b47888ceSAndrew Thompson const struct lacp_systemid *); 171b47888ceSAndrew Thompson static void lacp_port_enable(struct lacp_port *); 172b47888ceSAndrew Thompson static void lacp_port_disable(struct lacp_port *); 173b47888ceSAndrew Thompson static void lacp_select(struct lacp_port *); 174b47888ceSAndrew Thompson static void lacp_unselect(struct lacp_port *); 175b47888ceSAndrew Thompson static void lacp_disable_collecting(struct lacp_port *); 176b47888ceSAndrew Thompson static void lacp_enable_collecting(struct lacp_port *); 177b47888ceSAndrew Thompson static void lacp_disable_distributing(struct lacp_port *); 178b47888ceSAndrew Thompson static void lacp_enable_distributing(struct lacp_port *); 179b47888ceSAndrew Thompson static int lacp_xmit_lacpdu(struct lacp_port *); 180998971a7SAndrew Thompson static int lacp_xmit_marker(struct lacp_port *); 181b47888ceSAndrew Thompson 1825fc4c149SAndrew Thompson /* Debugging */ 1835fc4c149SAndrew Thompson 184b47888ceSAndrew Thompson static void lacp_dump_lacpdu(const struct lacpdu *); 185b47888ceSAndrew Thompson static const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 186b47888ceSAndrew Thompson size_t); 187b47888ceSAndrew Thompson static const char *lacp_format_lagid(const struct lacp_peerinfo *, 188b47888ceSAndrew Thompson const struct lacp_peerinfo *, char *, size_t); 189b47888ceSAndrew Thompson static const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 190b47888ceSAndrew Thompson char *, size_t); 191b47888ceSAndrew Thompson static const char *lacp_format_state(uint8_t, char *, size_t); 192b47888ceSAndrew Thompson static const char *lacp_format_mac(const uint8_t *, char *, size_t); 193b47888ceSAndrew Thompson static const char *lacp_format_systemid(const struct lacp_systemid *, char *, 194b47888ceSAndrew Thompson size_t); 195b47888ceSAndrew Thompson static const char *lacp_format_portid(const struct lacp_portid *, char *, 196b47888ceSAndrew Thompson size_t); 197b47888ceSAndrew Thompson static void lacp_dprintf(const struct lacp_port *, const char *, ...) 198b47888ceSAndrew Thompson __attribute__((__format__(__printf__, 2, 3))); 1995fc4c149SAndrew Thompson 2005f901c92SAndrew Turner VNET_DEFINE_STATIC(int, lacp_debug); 201939a050aSHiroki Sato #define V_lacp_debug VNET(lacp_debug) 2027029da5cSPawel Biernacki SYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 2037029da5cSPawel Biernacki "ieee802.3ad"); 204939a050aSHiroki Sato SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RWTUN | CTLFLAG_VNET, 205939a050aSHiroki Sato &VNET_NAME(lacp_debug), 0, "Enable LACP debug logging (1=debug, 2=trace)"); 2065fc4c149SAndrew Thompson 2075f901c92SAndrew Turner VNET_DEFINE_STATIC(int, lacp_default_strict_mode) = 1; 2086fb1399aSSteven Hartland SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, default_strict_mode, 2096fb1399aSSteven Hartland CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(lacp_default_strict_mode), 0, 210c1be893cSSteven Hartland "LACP strict protocol compliance default"); 211939a050aSHiroki Sato #define LACP_DPRINTF(a) if (V_lacp_debug & 0x01) { lacp_dprintf a ; } 212939a050aSHiroki Sato #define LACP_TRACE(a) if (V_lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); } 213939a050aSHiroki Sato #define LACP_TPRINTF(a) if (V_lacp_debug & 0x04) { lacp_dprintf a ; } 214b47888ceSAndrew Thompson 215b47888ceSAndrew Thompson /* 216b47888ceSAndrew Thompson * partner administration variables. 217b47888ceSAndrew Thompson * XXX should be configurable. 218b47888ceSAndrew Thompson */ 219b47888ceSAndrew Thompson 22031402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_optimistic = { 221b47888ceSAndrew Thompson .lip_systemid = { .lsi_prio = 0xffff }, 222b47888ceSAndrew Thompson .lip_portid = { .lpi_prio = 0xffff }, 223b47888ceSAndrew Thompson .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 224b47888ceSAndrew Thompson LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 22531402c27SAdrian Chadd }; 22631402c27SAdrian Chadd 22731402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_strict = { 22831402c27SAdrian Chadd .lip_systemid = { .lsi_prio = 0xffff }, 22931402c27SAdrian Chadd .lip_portid = { .lpi_prio = 0xffff }, 230b47888ceSAndrew Thompson .lip_state = 0, 231b47888ceSAndrew Thompson }; 232b47888ceSAndrew Thompson 233b47888ceSAndrew Thompson static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 234b47888ceSAndrew Thompson [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 235b47888ceSAndrew Thompson [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 236b47888ceSAndrew Thompson [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 237b47888ceSAndrew Thompson }; 238b47888ceSAndrew Thompson 239af0084c9SAndrew Thompson struct mbuf * 24018242d3bSAndrew Thompson lacp_input(struct lagg_port *lgp, struct mbuf *m) 241b47888ceSAndrew Thompson { 2423de18008SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 2433bf517e3SAndrew Thompson uint8_t subtype; 2443bf517e3SAndrew Thompson 2453bf517e3SAndrew Thompson if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 2463bf517e3SAndrew Thompson m_freem(m); 247af0084c9SAndrew Thompson return (NULL); 2483bf517e3SAndrew Thompson } 2493bf517e3SAndrew Thompson 2503bf517e3SAndrew Thompson m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 2513bf517e3SAndrew Thompson switch (subtype) { 2523bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_LACP: 2533de18008SAndrew Thompson lacp_pdu_input(lp, m); 2543de18008SAndrew Thompson return (NULL); 2553bf517e3SAndrew Thompson 2563bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_MARKER: 2573de18008SAndrew Thompson lacp_marker_input(lp, m); 258af0084c9SAndrew Thompson return (NULL); 2593bf517e3SAndrew Thompson } 2603bf517e3SAndrew Thompson 2613de18008SAndrew Thompson /* Not a subtype we are interested in */ 2623de18008SAndrew Thompson return (m); 2633bf517e3SAndrew Thompson } 2643bf517e3SAndrew Thompson 2653bf517e3SAndrew Thompson /* 2663bf517e3SAndrew Thompson * lacp_pdu_input: process lacpdu 2673bf517e3SAndrew Thompson */ 2683bf517e3SAndrew Thompson static int 2693de18008SAndrew Thompson lacp_pdu_input(struct lacp_port *lp, struct mbuf *m) 2703bf517e3SAndrew Thompson { 2713de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 272b47888ceSAndrew Thompson struct lacpdu *du; 273b47888ceSAndrew Thompson int error = 0; 274b47888ceSAndrew Thompson 275b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*du)) { 276b47888ceSAndrew Thompson goto bad; 277b47888ceSAndrew Thompson } 278b47888ceSAndrew Thompson 279b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 280b47888ceSAndrew Thompson goto bad; 281b47888ceSAndrew Thompson } 282b47888ceSAndrew Thompson 283b47888ceSAndrew Thompson if (m->m_len < sizeof(*du)) { 284b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*du)); 285b47888ceSAndrew Thompson if (m == NULL) { 286b47888ceSAndrew Thompson return (ENOMEM); 287b47888ceSAndrew Thompson } 288b47888ceSAndrew Thompson } 289b47888ceSAndrew Thompson 290b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 291b47888ceSAndrew Thompson 292b47888ceSAndrew Thompson if (memcmp(&du->ldu_eh.ether_dhost, 293b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 294b47888ceSAndrew Thompson goto bad; 295b47888ceSAndrew Thompson } 296b47888ceSAndrew Thompson 297b47888ceSAndrew Thompson /* 298b47888ceSAndrew Thompson * ignore the version for compatibility with 299b47888ceSAndrew Thompson * the future protocol revisions. 300b47888ceSAndrew Thompson */ 301b47888ceSAndrew Thompson #if 0 302b47888ceSAndrew Thompson if (du->ldu_sph.sph_version != 1) { 303b47888ceSAndrew Thompson goto bad; 304b47888ceSAndrew Thompson } 305b47888ceSAndrew Thompson #endif 306b47888ceSAndrew Thompson 307b47888ceSAndrew Thompson /* 308b47888ceSAndrew Thompson * ignore tlv types for compatibility with 309b47888ceSAndrew Thompson * the future protocol revisions. 310b47888ceSAndrew Thompson */ 311b47888ceSAndrew Thompson if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 312b47888ceSAndrew Thompson lacp_info_tlv_template, FALSE)) { 313b47888ceSAndrew Thompson goto bad; 314b47888ceSAndrew Thompson } 315b47888ceSAndrew Thompson 316939a050aSHiroki Sato if (V_lacp_debug > 0) { 3175fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu receive\n"); 318b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 3195fc4c149SAndrew Thompson } 3203de18008SAndrew Thompson 32149de4f22SAdrian Chadd if ((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_rx_test) { 32231402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping RX PDU\n")); 32331402c27SAdrian Chadd goto bad; 32431402c27SAdrian Chadd } 32531402c27SAdrian Chadd 3263de18008SAndrew Thompson LACP_LOCK(lsc); 327b47888ceSAndrew Thompson lacp_sm_rx(lp, du); 3283de18008SAndrew Thompson LACP_UNLOCK(lsc); 329b47888ceSAndrew Thompson 330b47888ceSAndrew Thompson m_freem(m); 331b47888ceSAndrew Thompson return (error); 332b47888ceSAndrew Thompson 333b47888ceSAndrew Thompson bad: 334b47888ceSAndrew Thompson m_freem(m); 335b47888ceSAndrew Thompson return (EINVAL); 336b47888ceSAndrew Thompson } 337b47888ceSAndrew Thompson 338b47888ceSAndrew Thompson static void 339b47888ceSAndrew Thompson lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 340b47888ceSAndrew Thompson { 34118242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 342ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 343b47888ceSAndrew Thompson 344b47888ceSAndrew Thompson info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 345b47888ceSAndrew Thompson memcpy(&info->lip_systemid.lsi_mac, 346ec32b37eSAndrew Thompson IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 347b47888ceSAndrew Thompson info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 348b47888ceSAndrew Thompson info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 349b47888ceSAndrew Thompson info->lip_state = lp->lp_state; 350b47888ceSAndrew Thompson } 351b47888ceSAndrew Thompson 352998971a7SAndrew Thompson static void 353998971a7SAndrew Thompson lacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 354998971a7SAndrew Thompson { 355998971a7SAndrew Thompson struct ifnet *ifp = lp->lp_ifp; 356998971a7SAndrew Thompson 357998971a7SAndrew Thompson /* Fill in the port index and system id (encoded as the MAC) */ 358998971a7SAndrew Thompson info->mi_rq_port = htons(ifp->if_index); 359998971a7SAndrew Thompson memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 360998971a7SAndrew Thompson info->mi_rq_xid = htonl(0); 361998971a7SAndrew Thompson } 362998971a7SAndrew Thompson 363b47888ceSAndrew Thompson static int 364b47888ceSAndrew Thompson lacp_xmit_lacpdu(struct lacp_port *lp) 365b47888ceSAndrew Thompson { 36618242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 367b47888ceSAndrew Thompson struct mbuf *m; 368b47888ceSAndrew Thompson struct lacpdu *du; 369b47888ceSAndrew Thompson int error; 370b47888ceSAndrew Thompson 3713de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 372b47888ceSAndrew Thompson 373eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 374b47888ceSAndrew Thompson if (m == NULL) { 375b47888ceSAndrew Thompson return (ENOMEM); 376b47888ceSAndrew Thompson } 377b47888ceSAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*du); 378b47888ceSAndrew Thompson 379b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 380b47888ceSAndrew Thompson memset(du, 0, sizeof(*du)); 381b47888ceSAndrew Thompson 382b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 383b47888ceSAndrew Thompson ETHER_ADDR_LEN); 38418242d3bSAndrew Thompson memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 385b47888ceSAndrew Thompson du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 386b47888ceSAndrew Thompson 387b47888ceSAndrew Thompson du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 388b47888ceSAndrew Thompson du->ldu_sph.sph_version = 1; 389b47888ceSAndrew Thompson 390b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 391b47888ceSAndrew Thompson du->ldu_actor = lp->lp_actor; 392b47888ceSAndrew Thompson 393b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 394b47888ceSAndrew Thompson sizeof(du->ldu_partner)); 395b47888ceSAndrew Thompson du->ldu_partner = lp->lp_partner; 396b47888ceSAndrew Thompson 397b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 398b47888ceSAndrew Thompson sizeof(du->ldu_collector)); 399b47888ceSAndrew Thompson du->ldu_collector.lci_maxdelay = 0; 400b47888ceSAndrew Thompson 401939a050aSHiroki Sato if (V_lacp_debug > 0) { 4025fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu transmit\n"); 403b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 4045fc4c149SAndrew Thompson } 405b47888ceSAndrew Thompson 406b47888ceSAndrew Thompson m->m_flags |= M_MCAST; 407b47888ceSAndrew Thompson 408b47888ceSAndrew Thompson /* 409b47888ceSAndrew Thompson * XXX should use higher priority queue. 410b47888ceSAndrew Thompson * otherwise network congestion can break aggregation. 411b47888ceSAndrew Thompson */ 412b47888ceSAndrew Thompson 41318242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 414b47888ceSAndrew Thompson return (error); 415b47888ceSAndrew Thompson } 416b47888ceSAndrew Thompson 417998971a7SAndrew Thompson static int 418998971a7SAndrew Thompson lacp_xmit_marker(struct lacp_port *lp) 419998971a7SAndrew Thompson { 420998971a7SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 421998971a7SAndrew Thompson struct mbuf *m; 422998971a7SAndrew Thompson struct markerdu *mdu; 423998971a7SAndrew Thompson int error; 424998971a7SAndrew Thompson 4253de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 426998971a7SAndrew Thompson 427eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 428998971a7SAndrew Thompson if (m == NULL) { 429998971a7SAndrew Thompson return (ENOMEM); 430998971a7SAndrew Thompson } 431998971a7SAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*mdu); 432998971a7SAndrew Thompson 433998971a7SAndrew Thompson mdu = mtod(m, struct markerdu *); 434998971a7SAndrew Thompson memset(mdu, 0, sizeof(*mdu)); 435998971a7SAndrew Thompson 436998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 437998971a7SAndrew Thompson ETHER_ADDR_LEN); 438998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 439998971a7SAndrew Thompson mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 440998971a7SAndrew Thompson 441998971a7SAndrew Thompson mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 442998971a7SAndrew Thompson mdu->mdu_sph.sph_version = 1; 443998971a7SAndrew Thompson 444998971a7SAndrew Thompson /* Bump the transaction id and copy over the marker info */ 445998971a7SAndrew Thompson lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 446998971a7SAndrew Thompson TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 447998971a7SAndrew Thompson mdu->mdu_info = lp->lp_marker; 448998971a7SAndrew Thompson 449998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 450998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 451998971a7SAndrew Thompson ntohl(mdu->mdu_info.mi_rq_xid))); 452998971a7SAndrew Thompson 453998971a7SAndrew Thompson m->m_flags |= M_MCAST; 454998971a7SAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 455998971a7SAndrew Thompson return (error); 456998971a7SAndrew Thompson } 4573de18008SAndrew Thompson 458b47888ceSAndrew Thompson void 45918242d3bSAndrew Thompson lacp_linkstate(struct lagg_port *lgp) 460b47888ceSAndrew Thompson { 46118242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 4623de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 46318242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 464b47888ceSAndrew Thompson struct ifmediareq ifmr; 465b47888ceSAndrew Thompson int error = 0; 466b47888ceSAndrew Thompson u_int media; 467b47888ceSAndrew Thompson uint8_t old_state; 468b47888ceSAndrew Thompson uint16_t old_key; 469b47888ceSAndrew Thompson 470b47888ceSAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr)); 471aa0186bcSNavdeep Parhar error = (*ifp->if_ioctl)(ifp, SIOCGIFXMEDIA, (caddr_t)&ifmr); 472aa0186bcSNavdeep Parhar if (error != 0) { 473aa0186bcSNavdeep Parhar bzero((char *)&ifmr, sizeof(ifmr)); 474b47888ceSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 475aa0186bcSNavdeep Parhar } 476b47888ceSAndrew Thompson if (error != 0) 477b47888ceSAndrew Thompson return; 478b47888ceSAndrew Thompson 4793de18008SAndrew Thompson LACP_LOCK(lsc); 480b47888ceSAndrew Thompson media = ifmr.ifm_active; 481e3163ef6SAndrew Thompson LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 482e3163ef6SAndrew Thompson "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 483e3163ef6SAndrew Thompson (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 484b47888ceSAndrew Thompson old_state = lp->lp_state; 485b47888ceSAndrew Thompson old_key = lp->lp_key; 486b47888ceSAndrew Thompson 487b47888ceSAndrew Thompson lp->lp_media = media; 488e3163ef6SAndrew Thompson /* 489e3163ef6SAndrew Thompson * If the port is not an active full duplex Ethernet link then it can 490e3163ef6SAndrew Thompson * not be aggregated. 491e3163ef6SAndrew Thompson */ 492e3163ef6SAndrew Thompson if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 493e3163ef6SAndrew Thompson ifp->if_link_state != LINK_STATE_UP) { 494b47888ceSAndrew Thompson lacp_port_disable(lp); 495b47888ceSAndrew Thompson } else { 496b47888ceSAndrew Thompson lacp_port_enable(lp); 497b47888ceSAndrew Thompson } 498b47888ceSAndrew Thompson lp->lp_key = lacp_compose_key(lp); 499b47888ceSAndrew Thompson 500b47888ceSAndrew Thompson if (old_state != lp->lp_state || old_key != lp->lp_key) { 501b47888ceSAndrew Thompson LACP_DPRINTF((lp, "-> UNSELECTED\n")); 502b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 503b47888ceSAndrew Thompson } 5043de18008SAndrew Thompson LACP_UNLOCK(lsc); 505b47888ceSAndrew Thompson } 506b47888ceSAndrew Thompson 507b47888ceSAndrew Thompson static void 508b47888ceSAndrew Thompson lacp_tick(void *arg) 509b47888ceSAndrew Thompson { 510b47888ceSAndrew Thompson struct lacp_softc *lsc = arg; 511b47888ceSAndrew Thompson struct lacp_port *lp; 512b47888ceSAndrew Thompson 513b47888ceSAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 514b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 515b47888ceSAndrew Thompson continue; 516b47888ceSAndrew Thompson 517939a050aSHiroki Sato CURVNET_SET(lp->lp_ifp->if_vnet); 518b47888ceSAndrew Thompson lacp_run_timers(lp); 519b47888ceSAndrew Thompson 520b47888ceSAndrew Thompson lacp_select(lp); 521b47888ceSAndrew Thompson lacp_sm_mux(lp); 522b47888ceSAndrew Thompson lacp_sm_tx(lp); 523b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(lp); 524939a050aSHiroki Sato CURVNET_RESTORE(); 525b47888ceSAndrew Thompson } 526b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 527b47888ceSAndrew Thompson } 528b47888ceSAndrew Thompson 529b47888ceSAndrew Thompson int 53018242d3bSAndrew Thompson lacp_port_create(struct lagg_port *lgp) 531b47888ceSAndrew Thompson { 532ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 533ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 534b47888ceSAndrew Thompson struct lacp_port *lp; 53518242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 536b47888ceSAndrew Thompson struct sockaddr_dl sdl; 537b47888ceSAndrew Thompson struct ifmultiaddr *rifma = NULL; 538b47888ceSAndrew Thompson int error; 539b47888ceSAndrew Thompson 54095fbe4d0SAlexander V. Chernikov link_init_sdl(ifp, (struct sockaddr *)&sdl, IFT_ETHER); 541b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 542b47888ceSAndrew Thompson 543b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 544b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 545b47888ceSAndrew Thompson error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 546b47888ceSAndrew Thompson if (error) { 547eade13f9SGleb Smirnoff printf("%s: ADDMULTI failed on %s\n", __func__, 548eade13f9SGleb Smirnoff lgp->lp_ifp->if_xname); 549b47888ceSAndrew Thompson return (error); 550b47888ceSAndrew Thompson } 551b47888ceSAndrew Thompson 552b47888ceSAndrew Thompson lp = malloc(sizeof(struct lacp_port), 553b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 554b47888ceSAndrew Thompson if (lp == NULL) 555b47888ceSAndrew Thompson return (ENOMEM); 556b47888ceSAndrew Thompson 5573de18008SAndrew Thompson LACP_LOCK(lsc); 5586900d0d3SGleb Smirnoff lgp->lp_psc = lp; 559b47888ceSAndrew Thompson lp->lp_ifp = ifp; 56018242d3bSAndrew Thompson lp->lp_lagg = lgp; 561b47888ceSAndrew Thompson lp->lp_lsc = lsc; 562d74fd345SAndrew Thompson lp->lp_ifma = rifma; 563b47888ceSAndrew Thompson 564b47888ceSAndrew Thompson LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 565b47888ceSAndrew Thompson 566b47888ceSAndrew Thompson lacp_fill_actorinfo(lp, &lp->lp_actor); 567998971a7SAndrew Thompson lacp_fill_markerinfo(lp, &lp->lp_marker); 568d592868eSRavi Pokala lp->lp_state = LACP_STATE_ACTIVITY; 569b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 570b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 5713de18008SAndrew Thompson LACP_UNLOCK(lsc); 5723de18008SAndrew Thompson lacp_linkstate(lgp); 573b47888ceSAndrew Thompson 574b47888ceSAndrew Thompson return (0); 575b47888ceSAndrew Thompson } 576b47888ceSAndrew Thompson 577b47888ceSAndrew Thompson void 57818242d3bSAndrew Thompson lacp_port_destroy(struct lagg_port *lgp) 579b47888ceSAndrew Thompson { 58018242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 5813de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 582d74fd345SAndrew Thompson int i; 583b47888ceSAndrew Thompson 5843de18008SAndrew Thompson LACP_LOCK(lsc); 585b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 586b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, i); 587b47888ceSAndrew Thompson } 588b47888ceSAndrew Thompson 589b47888ceSAndrew Thompson lacp_disable_collecting(lp); 590b47888ceSAndrew Thompson lacp_disable_distributing(lp); 591b47888ceSAndrew Thompson lacp_unselect(lp); 592b47888ceSAndrew Thompson 5936d478167SHiroki Sato LIST_REMOVE(lp, lp_next); 5946d478167SHiroki Sato LACP_UNLOCK(lsc); 5956d478167SHiroki Sato 596108fe96aSAndrew Thompson /* The address may have already been removed by if_purgemaddrs() */ 597108fe96aSAndrew Thompson if (!lgp->lp_detaching) 598d74fd345SAndrew Thompson if_delmulti_ifma(lp->lp_ifma); 599b47888ceSAndrew Thompson 600b47888ceSAndrew Thompson free(lp, M_DEVBUF); 601b47888ceSAndrew Thompson } 602b47888ceSAndrew Thompson 603b3d37ca5SAndrew Thompson void 6046900d0d3SGleb Smirnoff lacp_req(struct lagg_softc *sc, void *data) 605b3d37ca5SAndrew Thompson { 606b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 607b3d37ca5SAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 608f544a748SAlan Somers struct lacp_aggregator *la; 609b3d37ca5SAndrew Thompson 610b3d37ca5SAndrew Thompson bzero(req, sizeof(struct lacp_opreq)); 611f544a748SAlan Somers 612f544a748SAlan Somers /* 613f544a748SAlan Somers * If the LACP softc is NULL, return with the opreq structure full of 614f544a748SAlan Somers * zeros. It is normal for the softc to be NULL while the lagg is 615f544a748SAlan Somers * being destroyed. 616f544a748SAlan Somers */ 617f544a748SAlan Somers if (NULL == lsc) 618f544a748SAlan Somers return; 619f544a748SAlan Somers 620f544a748SAlan Somers la = lsc->lsc_active_aggregator; 621f544a748SAlan Somers LACP_LOCK(lsc); 622b3d37ca5SAndrew Thompson if (la != NULL) { 623b3d37ca5SAndrew Thompson req->actor_prio = ntohs(la->la_actor.lip_systemid.lsi_prio); 624b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &la->la_actor.lip_systemid.lsi_mac, 625b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 626b3d37ca5SAndrew Thompson req->actor_key = ntohs(la->la_actor.lip_key); 627b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(la->la_actor.lip_portid.lpi_prio); 628b3d37ca5SAndrew Thompson req->actor_portno = ntohs(la->la_actor.lip_portid.lpi_portno); 629b3d37ca5SAndrew Thompson req->actor_state = la->la_actor.lip_state; 630b3d37ca5SAndrew Thompson 631b3d37ca5SAndrew Thompson req->partner_prio = ntohs(la->la_partner.lip_systemid.lsi_prio); 632b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &la->la_partner.lip_systemid.lsi_mac, 633b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 634b3d37ca5SAndrew Thompson req->partner_key = ntohs(la->la_partner.lip_key); 635b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(la->la_partner.lip_portid.lpi_prio); 636b3d37ca5SAndrew Thompson req->partner_portno = ntohs(la->la_partner.lip_portid.lpi_portno); 637b3d37ca5SAndrew Thompson req->partner_state = la->la_partner.lip_state; 638b3d37ca5SAndrew Thompson } 6393de18008SAndrew Thompson LACP_UNLOCK(lsc); 640b3d37ca5SAndrew Thompson } 641b3d37ca5SAndrew Thompson 642b3d37ca5SAndrew Thompson void 6436900d0d3SGleb Smirnoff lacp_portreq(struct lagg_port *lgp, void *data) 644b3d37ca5SAndrew Thompson { 645b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 646b3d37ca5SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 6473de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 648b3d37ca5SAndrew Thompson 6493de18008SAndrew Thompson LACP_LOCK(lsc); 650b3d37ca5SAndrew Thompson req->actor_prio = ntohs(lp->lp_actor.lip_systemid.lsi_prio); 651b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &lp->lp_actor.lip_systemid.lsi_mac, 652b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 653b3d37ca5SAndrew Thompson req->actor_key = ntohs(lp->lp_actor.lip_key); 654b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(lp->lp_actor.lip_portid.lpi_prio); 655b3d37ca5SAndrew Thompson req->actor_portno = ntohs(lp->lp_actor.lip_portid.lpi_portno); 656b3d37ca5SAndrew Thompson req->actor_state = lp->lp_actor.lip_state; 657b3d37ca5SAndrew Thompson 658b3d37ca5SAndrew Thompson req->partner_prio = ntohs(lp->lp_partner.lip_systemid.lsi_prio); 659b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &lp->lp_partner.lip_systemid.lsi_mac, 660b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 661b3d37ca5SAndrew Thompson req->partner_key = ntohs(lp->lp_partner.lip_key); 662b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(lp->lp_partner.lip_portid.lpi_prio); 663b3d37ca5SAndrew Thompson req->partner_portno = ntohs(lp->lp_partner.lip_portid.lpi_portno); 664b3d37ca5SAndrew Thompson req->partner_state = lp->lp_partner.lip_state; 6653de18008SAndrew Thompson LACP_UNLOCK(lsc); 666b3d37ca5SAndrew Thompson } 667b3d37ca5SAndrew Thompson 668b47888ceSAndrew Thompson static void 669b47888ceSAndrew Thompson lacp_disable_collecting(struct lacp_port *lp) 670b47888ceSAndrew Thompson { 671b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting disabled\n")); 672b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_COLLECTING; 673b47888ceSAndrew Thompson } 674b47888ceSAndrew Thompson 675b47888ceSAndrew Thompson static void 676b47888ceSAndrew Thompson lacp_enable_collecting(struct lacp_port *lp) 677b47888ceSAndrew Thompson { 678b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting enabled\n")); 679b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_COLLECTING; 680b47888ceSAndrew Thompson } 681b47888ceSAndrew Thompson 682b47888ceSAndrew Thompson static void 683b47888ceSAndrew Thompson lacp_disable_distributing(struct lacp_port *lp) 684b47888ceSAndrew Thompson { 685b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 686b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 68731402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 688b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 689b47888ceSAndrew Thompson 6903de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 691b47888ceSAndrew Thompson 692b47888ceSAndrew Thompson if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 693b47888ceSAndrew Thompson return; 694b47888ceSAndrew Thompson } 695b47888ceSAndrew Thompson 696b47888ceSAndrew Thompson KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 697b47888ceSAndrew Thompson KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 698b47888ceSAndrew Thompson KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 699b47888ceSAndrew Thompson 700b47888ceSAndrew Thompson LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 701b47888ceSAndrew Thompson "nports %d -> %d\n", 702b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 703b47888ceSAndrew Thompson la->la_nports, la->la_nports - 1)); 704b47888ceSAndrew Thompson 705b47888ceSAndrew Thompson TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 706b47888ceSAndrew Thompson la->la_nports--; 70731402c27SAdrian Chadd sc->sc_active = la->la_nports; 708b47888ceSAndrew Thompson 7093de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 710b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 7113de18008SAndrew Thompson lacp_select_active_aggregator(lsc); 7123de18008SAndrew Thompson /* regenerate the port map, the active aggregator has changed */ 7133de18008SAndrew Thompson lacp_update_portmap(lsc); 7143de18008SAndrew Thompson } 715b47888ceSAndrew Thompson 716b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 7175ccac9f9SAndrew Gallatin if_link_state_change(sc->sc_ifp, 7185ccac9f9SAndrew Gallatin sc->sc_active ? LINK_STATE_UP : LINK_STATE_DOWN); 719b47888ceSAndrew Thompson } 720b47888ceSAndrew Thompson 721b47888ceSAndrew Thompson static void 722b47888ceSAndrew Thompson lacp_enable_distributing(struct lacp_port *lp) 723b47888ceSAndrew Thompson { 724b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 725b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 72631402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 727b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 728b47888ceSAndrew Thompson 7293de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 730b47888ceSAndrew Thompson 731b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 732b47888ceSAndrew Thompson return; 733b47888ceSAndrew Thompson } 734b47888ceSAndrew Thompson 735b47888ceSAndrew Thompson LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 736b47888ceSAndrew Thompson "nports %d -> %d\n", 737b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 738b47888ceSAndrew Thompson la->la_nports, la->la_nports + 1)); 739b47888ceSAndrew Thompson 740b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 741b47888ceSAndrew Thompson TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 742b47888ceSAndrew Thompson la->la_nports++; 74331402c27SAdrian Chadd sc->sc_active = la->la_nports; 744b47888ceSAndrew Thompson 745b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DISTRIBUTING; 746b47888ceSAndrew Thompson 7473de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 7483de18008SAndrew Thompson lacp_suppress_distributing(lsc, la); 7493de18008SAndrew Thompson lacp_update_portmap(lsc); 7503de18008SAndrew Thompson } else 7513de18008SAndrew Thompson /* try to become the active aggregator */ 752b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 7535ccac9f9SAndrew Gallatin 7545ccac9f9SAndrew Gallatin if_link_state_change(sc->sc_ifp, 7555ccac9f9SAndrew Gallatin sc->sc_active ? LINK_STATE_UP : LINK_STATE_DOWN); 756b47888ceSAndrew Thompson } 757b47888ceSAndrew Thompson 758b47888ceSAndrew Thompson static void 759b47888ceSAndrew Thompson lacp_transit_expire(void *vp) 760b47888ceSAndrew Thompson { 761b47888ceSAndrew Thompson struct lacp_softc *lsc = vp; 762b47888ceSAndrew Thompson 7633de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 7643de18008SAndrew Thompson 7656d478167SHiroki Sato CURVNET_SET(lsc->lsc_softc->sc_ifp->if_vnet); 7665fc4c149SAndrew Thompson LACP_TRACE(NULL); 7676d478167SHiroki Sato CURVNET_RESTORE(); 7685fc4c149SAndrew Thompson 769b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 770b47888ceSAndrew Thompson } 771b47888ceSAndrew Thompson 77209c7577eSGleb Smirnoff void 773ec32b37eSAndrew Thompson lacp_attach(struct lagg_softc *sc) 774b47888ceSAndrew Thompson { 775b47888ceSAndrew Thompson struct lacp_softc *lsc; 776b47888ceSAndrew Thompson 77709c7577eSGleb Smirnoff lsc = malloc(sizeof(struct lacp_softc), M_DEVBUF, M_WAITOK | M_ZERO); 778b47888ceSAndrew Thompson 7796900d0d3SGleb Smirnoff sc->sc_psc = lsc; 780ec32b37eSAndrew Thompson lsc->lsc_softc = sc; 781b47888ceSAndrew Thompson 782b7ba031fSHans Petter Selasky lsc->lsc_hashkey = m_ether_tcpip_hash_init(); 783b47888ceSAndrew Thompson lsc->lsc_active_aggregator = NULL; 784c1be893cSSteven Hartland lsc->lsc_strict_mode = VNET(lacp_default_strict_mode); 7853de18008SAndrew Thompson LACP_LOCK_INIT(lsc); 786b47888ceSAndrew Thompson TAILQ_INIT(&lsc->lsc_aggregators); 787b47888ceSAndrew Thompson LIST_INIT(&lsc->lsc_ports); 788b47888ceSAndrew Thompson 7893de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0); 7903de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0); 791b47888ceSAndrew Thompson 79218242d3bSAndrew Thompson /* if the lagg is already up then do the same */ 793ec32b37eSAndrew Thompson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 794ec32b37eSAndrew Thompson lacp_init(sc); 795b47888ceSAndrew Thompson } 796b47888ceSAndrew Thompson 797b1bbc5b3SGleb Smirnoff void 79809c7577eSGleb Smirnoff lacp_detach(void *psc) 799b47888ceSAndrew Thompson { 80009c7577eSGleb Smirnoff struct lacp_softc *lsc = (struct lacp_softc *)psc; 801b47888ceSAndrew Thompson 802b47888ceSAndrew Thompson KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 803b47888ceSAndrew Thompson ("aggregators still active")); 804b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator == NULL, 805b47888ceSAndrew Thompson ("aggregator still attached")); 806b47888ceSAndrew Thompson 807b47888ceSAndrew Thompson callout_drain(&lsc->lsc_transit_callout); 808b47888ceSAndrew Thompson callout_drain(&lsc->lsc_callout); 809b47888ceSAndrew Thompson 8103de18008SAndrew Thompson LACP_LOCK_DESTROY(lsc); 811b47888ceSAndrew Thompson free(lsc, M_DEVBUF); 812b47888ceSAndrew Thompson } 813b47888ceSAndrew Thompson 814b47888ceSAndrew Thompson void 815ec32b37eSAndrew Thompson lacp_init(struct lagg_softc *sc) 816b47888ceSAndrew Thompson { 817ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 818b47888ceSAndrew Thompson 8193de18008SAndrew Thompson LACP_LOCK(lsc); 820b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 8213de18008SAndrew Thompson LACP_UNLOCK(lsc); 822b47888ceSAndrew Thompson } 823b47888ceSAndrew Thompson 824b47888ceSAndrew Thompson void 825ec32b37eSAndrew Thompson lacp_stop(struct lagg_softc *sc) 826b47888ceSAndrew Thompson { 827ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 828b47888ceSAndrew Thompson 8293de18008SAndrew Thompson LACP_LOCK(lsc); 830b47888ceSAndrew Thompson callout_stop(&lsc->lsc_transit_callout); 831b47888ceSAndrew Thompson callout_stop(&lsc->lsc_callout); 8323de18008SAndrew Thompson LACP_UNLOCK(lsc); 833b47888ceSAndrew Thompson } 834b47888ceSAndrew Thompson 83518242d3bSAndrew Thompson struct lagg_port * 8368732245dSAndrew Gallatin lacp_select_tx_port_by_hash(struct lagg_softc *sc, uint32_t hash, 8378732245dSAndrew Gallatin uint8_t numa_domain, int *err) 838b47888ceSAndrew Thompson { 839ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 8403de18008SAndrew Thompson struct lacp_portmap *pm; 841b47888ceSAndrew Thompson struct lacp_port *lp; 84235961dceSAndrew Gallatin struct lacp_port **map; 84335961dceSAndrew Gallatin int count; 844b47888ceSAndrew Thompson 845b47888ceSAndrew Thompson if (__predict_false(lsc->lsc_suppress_distributing)) { 846b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 8478732245dSAndrew Gallatin *err = ENOBUFS; 848b47888ceSAndrew Thompson return (NULL); 849b47888ceSAndrew Thompson } 850b47888ceSAndrew Thompson 8513de18008SAndrew Thompson pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 8523de18008SAndrew Thompson if (pm->pm_count == 0) { 853b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 8548732245dSAndrew Gallatin *err = ENETDOWN; 855b47888ceSAndrew Thompson return (NULL); 856b47888ceSAndrew Thompson } 857b47888ceSAndrew Thompson 85835961dceSAndrew Gallatin #ifdef NUMA 85935961dceSAndrew Gallatin if ((sc->sc_opts & LAGG_OPT_USE_NUMA) && 86098085baeSAndrew Gallatin pm->pm_num_dom > 1 && numa_domain < MAXMEMDOM) { 86198085baeSAndrew Gallatin count = pm->pm_numa[numa_domain].count; 86235961dceSAndrew Gallatin if (count > 0) { 86398085baeSAndrew Gallatin map = pm->pm_numa[numa_domain].map; 86435961dceSAndrew Gallatin } else { 86535961dceSAndrew Gallatin /* No ports on this domain; use global hash. */ 86635961dceSAndrew Gallatin map = pm->pm_map; 86735961dceSAndrew Gallatin count = pm->pm_count; 86835961dceSAndrew Gallatin } 86935961dceSAndrew Gallatin } else 87035961dceSAndrew Gallatin #endif 87135961dceSAndrew Gallatin { 87235961dceSAndrew Gallatin map = pm->pm_map; 87335961dceSAndrew Gallatin count = pm->pm_count; 87435961dceSAndrew Gallatin } 87535961dceSAndrew Gallatin 87635961dceSAndrew Gallatin hash %= count; 87735961dceSAndrew Gallatin lp = map[hash]; 878b47888ceSAndrew Thompson 879b47888ceSAndrew Thompson KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 880b47888ceSAndrew Thompson ("aggregated port is not distributing")); 881b47888ceSAndrew Thompson 88218242d3bSAndrew Thompson return (lp->lp_lagg); 883b47888ceSAndrew Thompson } 884f3e7afe2SHans Petter Selasky 885f3e7afe2SHans Petter Selasky struct lagg_port * 8868732245dSAndrew Gallatin lacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m, int *err) 887f3e7afe2SHans Petter Selasky { 888f3e7afe2SHans Petter Selasky struct lacp_softc *lsc = LACP_SOFTC(sc); 889f3e7afe2SHans Petter Selasky uint32_t hash; 89098085baeSAndrew Gallatin uint8_t numa_domain; 891f3e7afe2SHans Petter Selasky 89298085baeSAndrew Gallatin if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && 89398085baeSAndrew Gallatin M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) 89498085baeSAndrew Gallatin hash = m->m_pkthdr.flowid >> sc->flowid_shift; 89598085baeSAndrew Gallatin else 89698085baeSAndrew Gallatin hash = m_ether_tcpip_hash(sc->sc_flags, m, lsc->lsc_hashkey); 89798085baeSAndrew Gallatin 89898085baeSAndrew Gallatin numa_domain = m->m_pkthdr.numa_domain; 8998732245dSAndrew Gallatin return (lacp_select_tx_port_by_hash(sc, hash, numa_domain, err)); 900f3e7afe2SHans Petter Selasky } 901f3e7afe2SHans Petter Selasky 902b47888ceSAndrew Thompson /* 903b47888ceSAndrew Thompson * lacp_suppress_distributing: drop transmit packets for a while 904b47888ceSAndrew Thompson * to preserve packet ordering. 905b47888ceSAndrew Thompson */ 906b47888ceSAndrew Thompson 907b47888ceSAndrew Thompson static void 908b47888ceSAndrew Thompson lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 909b47888ceSAndrew Thompson { 910998971a7SAndrew Thompson struct lacp_port *lp; 911998971a7SAndrew Thompson 912b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 913b47888ceSAndrew Thompson return; 914b47888ceSAndrew Thompson } 915b47888ceSAndrew Thompson 9165fc4c149SAndrew Thompson LACP_TRACE(NULL); 9175fc4c149SAndrew Thompson 918b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = TRUE; 919998971a7SAndrew Thompson 920998971a7SAndrew Thompson /* send a marker frame down each port to verify the queues are empty */ 921998971a7SAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 922998971a7SAndrew Thompson lp->lp_flags |= LACP_PORT_MARK; 9230b92a7feSArnaud Ysmal if (lacp_xmit_marker(lp) != 0) 9240b92a7feSArnaud Ysmal lp->lp_flags &= ~LACP_PORT_MARK; 925998971a7SAndrew Thompson } 926998971a7SAndrew Thompson 927998971a7SAndrew Thompson /* set a timeout for the marker frames */ 928b47888ceSAndrew Thompson callout_reset(&lsc->lsc_transit_callout, 929b47888ceSAndrew Thompson LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 930b47888ceSAndrew Thompson } 931b47888ceSAndrew Thompson 932b47888ceSAndrew Thompson static int 933b47888ceSAndrew Thompson lacp_compare_peerinfo(const struct lacp_peerinfo *a, 934b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 935b47888ceSAndrew Thompson { 936b47888ceSAndrew Thompson return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 937b47888ceSAndrew Thompson } 938b47888ceSAndrew Thompson 939b47888ceSAndrew Thompson static int 940b47888ceSAndrew Thompson lacp_compare_systemid(const struct lacp_systemid *a, 941b47888ceSAndrew Thompson const struct lacp_systemid *b) 942b47888ceSAndrew Thompson { 943b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 944b47888ceSAndrew Thompson } 945b47888ceSAndrew Thompson 946b47888ceSAndrew Thompson #if 0 /* unused */ 947b47888ceSAndrew Thompson static int 948b47888ceSAndrew Thompson lacp_compare_portid(const struct lacp_portid *a, 949b47888ceSAndrew Thompson const struct lacp_portid *b) 950b47888ceSAndrew Thompson { 951b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 952b47888ceSAndrew Thompson } 953b47888ceSAndrew Thompson #endif 954b47888ceSAndrew Thompson 955b47888ceSAndrew Thompson static uint64_t 956b47888ceSAndrew Thompson lacp_aggregator_bandwidth(struct lacp_aggregator *la) 957b47888ceSAndrew Thompson { 958b47888ceSAndrew Thompson struct lacp_port *lp; 959b47888ceSAndrew Thompson uint64_t speed; 960b47888ceSAndrew Thompson 961b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 962b47888ceSAndrew Thompson if (lp == NULL) { 963b47888ceSAndrew Thompson return (0); 964b47888ceSAndrew Thompson } 965b47888ceSAndrew Thompson 966b47888ceSAndrew Thompson speed = ifmedia_baudrate(lp->lp_media); 967b47888ceSAndrew Thompson speed *= la->la_nports; 968b47888ceSAndrew Thompson if (speed == 0) { 969b47888ceSAndrew Thompson LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 970b47888ceSAndrew Thompson lp->lp_media, la->la_nports)); 971b47888ceSAndrew Thompson } 972b47888ceSAndrew Thompson 973b47888ceSAndrew Thompson return (speed); 974b47888ceSAndrew Thompson } 975b47888ceSAndrew Thompson 976b47888ceSAndrew Thompson /* 977b47888ceSAndrew Thompson * lacp_select_active_aggregator: select an aggregator to be used to transmit 97818242d3bSAndrew Thompson * packets from lagg(4) interface. 979b47888ceSAndrew Thompson */ 980b47888ceSAndrew Thompson 981b47888ceSAndrew Thompson static void 982b47888ceSAndrew Thompson lacp_select_active_aggregator(struct lacp_softc *lsc) 983b47888ceSAndrew Thompson { 984b47888ceSAndrew Thompson struct lacp_aggregator *la; 985b47888ceSAndrew Thompson struct lacp_aggregator *best_la = NULL; 986b47888ceSAndrew Thompson uint64_t best_speed = 0; 987b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 988b47888ceSAndrew Thompson 9895fc4c149SAndrew Thompson LACP_TRACE(NULL); 990b47888ceSAndrew Thompson 991b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 992b47888ceSAndrew Thompson uint64_t speed; 993b47888ceSAndrew Thompson 994b47888ceSAndrew Thompson if (la->la_nports == 0) { 995b47888ceSAndrew Thompson continue; 996b47888ceSAndrew Thompson } 997b47888ceSAndrew Thompson 998b47888ceSAndrew Thompson speed = lacp_aggregator_bandwidth(la); 999b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 1000b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 1001b47888ceSAndrew Thompson speed, la->la_nports)); 1002fe45e65fSAndrew Thompson 10036900d0d3SGleb Smirnoff /* 10046900d0d3SGleb Smirnoff * This aggregator is chosen if the partner has a better 10056900d0d3SGleb Smirnoff * system priority or, the total aggregated speed is higher 1006fe45e65fSAndrew Thompson * or, it is already the chosen aggregator 1007fe45e65fSAndrew Thompson */ 1008fe45e65fSAndrew Thompson if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 1009fe45e65fSAndrew Thompson LACP_SYS_PRI(best_la->la_partner)) || 1010fe45e65fSAndrew Thompson speed > best_speed || 1011b47888ceSAndrew Thompson (speed == best_speed && 1012b47888ceSAndrew Thompson la == lsc->lsc_active_aggregator)) { 1013b47888ceSAndrew Thompson best_la = la; 1014b47888ceSAndrew Thompson best_speed = speed; 1015b47888ceSAndrew Thompson } 1016b47888ceSAndrew Thompson } 1017b47888ceSAndrew Thompson 1018b47888ceSAndrew Thompson KASSERT(best_la == NULL || best_la->la_nports > 0, 1019b47888ceSAndrew Thompson ("invalid aggregator refcnt")); 1020b47888ceSAndrew Thompson KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 1021b47888ceSAndrew Thompson ("invalid aggregator list")); 1022b47888ceSAndrew Thompson 1023b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 1024b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator changed\n")); 1025b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "old %s\n", 1026b47888ceSAndrew Thompson lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 1027b47888ceSAndrew Thompson buf, sizeof(buf)))); 1028b47888ceSAndrew Thompson } else { 1029b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator not changed\n")); 1030b47888ceSAndrew Thompson } 1031b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "new %s\n", 1032b47888ceSAndrew Thompson lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 1033b47888ceSAndrew Thompson 1034b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 1035b47888ceSAndrew Thompson lsc->lsc_active_aggregator = best_la; 10363de18008SAndrew Thompson lacp_update_portmap(lsc); 1037b47888ceSAndrew Thompson if (best_la) { 1038b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, best_la); 1039b47888ceSAndrew Thompson } 1040b47888ceSAndrew Thompson } 1041b47888ceSAndrew Thompson } 1042b47888ceSAndrew Thompson 10433de18008SAndrew Thompson /* 10443de18008SAndrew Thompson * Updated the inactive portmap array with the new list of ports and 10453de18008SAndrew Thompson * make it live. 10463de18008SAndrew Thompson */ 10473de18008SAndrew Thompson static void 10483de18008SAndrew Thompson lacp_update_portmap(struct lacp_softc *lsc) 10493de18008SAndrew Thompson { 105031402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 10513de18008SAndrew Thompson struct lacp_aggregator *la; 10523de18008SAndrew Thompson struct lacp_portmap *p; 10533de18008SAndrew Thompson struct lacp_port *lp; 105431402c27SAdrian Chadd uint64_t speed; 10553de18008SAndrew Thompson u_int newmap; 10563de18008SAndrew Thompson int i; 105735961dceSAndrew Gallatin #ifdef NUMA 105835961dceSAndrew Gallatin int count; 105935961dceSAndrew Gallatin uint8_t domain; 106035961dceSAndrew Gallatin #endif 10613de18008SAndrew Thompson 10623de18008SAndrew Thompson newmap = lsc->lsc_activemap == 0 ? 1 : 0; 10633de18008SAndrew Thompson p = &lsc->lsc_pmap[newmap]; 10643de18008SAndrew Thompson la = lsc->lsc_active_aggregator; 106531402c27SAdrian Chadd speed = 0; 10663de18008SAndrew Thompson bzero(p, sizeof(struct lacp_portmap)); 10673de18008SAndrew Thompson 10683de18008SAndrew Thompson if (la != NULL && la->la_nports > 0) { 10693de18008SAndrew Thompson p->pm_count = la->la_nports; 10703de18008SAndrew Thompson i = 0; 107135961dceSAndrew Gallatin TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q) { 10723de18008SAndrew Thompson p->pm_map[i++] = lp; 107335961dceSAndrew Gallatin #ifdef NUMA 107435961dceSAndrew Gallatin domain = lp->lp_ifp->if_numa_domain; 107535961dceSAndrew Gallatin if (domain >= MAXMEMDOM) 107635961dceSAndrew Gallatin continue; 107735961dceSAndrew Gallatin count = p->pm_numa[domain].count; 107835961dceSAndrew Gallatin p->pm_numa[domain].map[count] = lp; 107935961dceSAndrew Gallatin p->pm_numa[domain].count++; 108035961dceSAndrew Gallatin #endif 108135961dceSAndrew Gallatin } 10823de18008SAndrew Thompson KASSERT(i == p->pm_count, ("Invalid port count")); 108335961dceSAndrew Gallatin 108435961dceSAndrew Gallatin #ifdef NUMA 108535961dceSAndrew Gallatin for (i = 0; i < MAXMEMDOM; i++) { 108635961dceSAndrew Gallatin if (p->pm_numa[i].count != 0) 108735961dceSAndrew Gallatin p->pm_num_dom++; 108835961dceSAndrew Gallatin } 108935961dceSAndrew Gallatin #endif 109031402c27SAdrian Chadd speed = lacp_aggregator_bandwidth(la); 10913de18008SAndrew Thompson } 109231402c27SAdrian Chadd sc->sc_ifp->if_baudrate = speed; 1093f2ab9160SAndrey V. Elsukov EVENTHANDLER_INVOKE(ifnet_event, sc->sc_ifp, 1094f2ab9160SAndrey V. Elsukov IFNET_EVENT_UPDATE_BAUDRATE); 10953de18008SAndrew Thompson 10963de18008SAndrew Thompson /* switch the active portmap over */ 10973de18008SAndrew Thompson atomic_store_rel_int(&lsc->lsc_activemap, newmap); 10983de18008SAndrew Thompson LACP_DPRINTF((NULL, "Set table %d with %d ports\n", 10993de18008SAndrew Thompson lsc->lsc_activemap, 11003de18008SAndrew Thompson lsc->lsc_pmap[lsc->lsc_activemap].pm_count)); 11013de18008SAndrew Thompson } 11023de18008SAndrew Thompson 1103b47888ceSAndrew Thompson static uint16_t 1104b47888ceSAndrew Thompson lacp_compose_key(struct lacp_port *lp) 1105b47888ceSAndrew Thompson { 110618242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 1107ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 1108b47888ceSAndrew Thompson u_int media = lp->lp_media; 1109b47888ceSAndrew Thompson uint16_t key; 1110b47888ceSAndrew Thompson 1111b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 1112b47888ceSAndrew Thompson /* 1113b47888ceSAndrew Thompson * non-aggregatable links should have unique keys. 1114b47888ceSAndrew Thompson * 1115b47888ceSAndrew Thompson * XXX this isn't really unique as if_index is 16 bit. 1116b47888ceSAndrew Thompson */ 1117b47888ceSAndrew Thompson 1118b47888ceSAndrew Thompson /* bit 0..14: (some bits of) if_index of this port */ 1119b47888ceSAndrew Thompson key = lp->lp_ifp->if_index; 1120b47888ceSAndrew Thompson /* bit 15: 1 */ 1121b47888ceSAndrew Thompson key |= 0x8000; 1122b47888ceSAndrew Thompson } else { 1123b47888ceSAndrew Thompson u_int subtype = IFM_SUBTYPE(media); 1124b47888ceSAndrew Thompson 1125e3163ef6SAndrew Thompson KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1126e3163ef6SAndrew Thompson KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1127b47888ceSAndrew Thompson 1128d5773da8SAndrey V. Elsukov /* bit 0..4: IFM_SUBTYPE modulo speed */ 1129d5773da8SAndrey V. Elsukov switch (subtype) { 1130d5773da8SAndrey V. Elsukov case IFM_10_T: 1131d5773da8SAndrey V. Elsukov case IFM_10_2: 1132d5773da8SAndrey V. Elsukov case IFM_10_5: 1133d5773da8SAndrey V. Elsukov case IFM_10_STP: 1134d5773da8SAndrey V. Elsukov case IFM_10_FL: 1135d5773da8SAndrey V. Elsukov key = IFM_10_T; 1136d5773da8SAndrey V. Elsukov break; 1137d5773da8SAndrey V. Elsukov case IFM_100_TX: 1138d5773da8SAndrey V. Elsukov case IFM_100_FX: 1139d5773da8SAndrey V. Elsukov case IFM_100_T4: 1140d5773da8SAndrey V. Elsukov case IFM_100_VG: 1141d5773da8SAndrey V. Elsukov case IFM_100_T2: 1142eb7e25b2SEric Joyner case IFM_100_T: 1143fe2bf351SEric Joyner case IFM_100_SGMII: 1144d5773da8SAndrey V. Elsukov key = IFM_100_TX; 1145d5773da8SAndrey V. Elsukov break; 1146d5773da8SAndrey V. Elsukov case IFM_1000_SX: 1147d5773da8SAndrey V. Elsukov case IFM_1000_LX: 1148d5773da8SAndrey V. Elsukov case IFM_1000_CX: 1149d5773da8SAndrey V. Elsukov case IFM_1000_T: 1150eb7e25b2SEric Joyner case IFM_1000_KX: 1151eb7e25b2SEric Joyner case IFM_1000_SGMII: 1152eb7e25b2SEric Joyner case IFM_1000_CX_SGMII: 1153d5773da8SAndrey V. Elsukov key = IFM_1000_SX; 1154d5773da8SAndrey V. Elsukov break; 1155d5773da8SAndrey V. Elsukov case IFM_10G_LR: 1156d5773da8SAndrey V. Elsukov case IFM_10G_SR: 1157d5773da8SAndrey V. Elsukov case IFM_10G_CX4: 1158d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX: 1159d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX_LONG: 1160d5773da8SAndrey V. Elsukov case IFM_10G_LRM: 1161d5773da8SAndrey V. Elsukov case IFM_10G_T: 1162eb7e25b2SEric Joyner case IFM_10G_KX4: 1163eb7e25b2SEric Joyner case IFM_10G_KR: 1164eb7e25b2SEric Joyner case IFM_10G_CR1: 1165eb7e25b2SEric Joyner case IFM_10G_ER: 1166eb7e25b2SEric Joyner case IFM_10G_SFI: 11676e105d4eSEric Joyner case IFM_10G_AOC: 1168d5773da8SAndrey V. Elsukov key = IFM_10G_LR; 1169d5773da8SAndrey V. Elsukov break; 1170eb7e25b2SEric Joyner case IFM_20G_KR2: 1171eb7e25b2SEric Joyner key = IFM_20G_KR2; 1172eb7e25b2SEric Joyner break; 1173eb7e25b2SEric Joyner case IFM_2500_KX: 1174eb7e25b2SEric Joyner case IFM_2500_T: 1175fe2bf351SEric Joyner case IFM_2500_X: 1176eb7e25b2SEric Joyner key = IFM_2500_KX; 1177eb7e25b2SEric Joyner break; 1178eb7e25b2SEric Joyner case IFM_5000_T: 1179fe2bf351SEric Joyner case IFM_5000_KR: 1180fe2bf351SEric Joyner case IFM_5000_KR_S: 1181fe2bf351SEric Joyner case IFM_5000_KR1: 1182eb7e25b2SEric Joyner key = IFM_5000_T; 1183eb7e25b2SEric Joyner break; 1184eb7e25b2SEric Joyner case IFM_50G_PCIE: 1185eb7e25b2SEric Joyner case IFM_50G_CR2: 1186eb7e25b2SEric Joyner case IFM_50G_KR2: 11875d1277caSKonstantin Belousov case IFM_50G_KR4: 1188fe2bf351SEric Joyner case IFM_50G_SR2: 1189fe2bf351SEric Joyner case IFM_50G_LR2: 1190fe2bf351SEric Joyner case IFM_50G_LAUI2_AC: 1191fe2bf351SEric Joyner case IFM_50G_LAUI2: 1192fe2bf351SEric Joyner case IFM_50G_AUI2_AC: 1193fe2bf351SEric Joyner case IFM_50G_AUI2: 1194fe2bf351SEric Joyner case IFM_50G_CP: 1195fe2bf351SEric Joyner case IFM_50G_SR: 1196fe2bf351SEric Joyner case IFM_50G_LR: 1197fe2bf351SEric Joyner case IFM_50G_FR: 1198fe2bf351SEric Joyner case IFM_50G_KR_PAM4: 1199fe2bf351SEric Joyner case IFM_50G_AUI1_AC: 1200fe2bf351SEric Joyner case IFM_50G_AUI1: 1201eb7e25b2SEric Joyner key = IFM_50G_PCIE; 1202eb7e25b2SEric Joyner break; 1203eb7e25b2SEric Joyner case IFM_56G_R4: 1204eb7e25b2SEric Joyner key = IFM_56G_R4; 1205eb7e25b2SEric Joyner break; 1206eb7e25b2SEric Joyner case IFM_25G_PCIE: 1207eb7e25b2SEric Joyner case IFM_25G_CR: 1208eb7e25b2SEric Joyner case IFM_25G_KR: 1209eb7e25b2SEric Joyner case IFM_25G_SR: 12106e105d4eSEric Joyner case IFM_25G_LR: 12116e105d4eSEric Joyner case IFM_25G_ACC: 12126e105d4eSEric Joyner case IFM_25G_AOC: 1213fe2bf351SEric Joyner case IFM_25G_T: 1214fe2bf351SEric Joyner case IFM_25G_CR_S: 1215fe2bf351SEric Joyner case IFM_25G_CR1: 1216fe2bf351SEric Joyner case IFM_25G_KR_S: 1217fe2bf351SEric Joyner case IFM_25G_AUI: 1218fe2bf351SEric Joyner case IFM_25G_KR1: 1219eb7e25b2SEric Joyner key = IFM_25G_PCIE; 1220eb7e25b2SEric Joyner break; 1221d5773da8SAndrey V. Elsukov case IFM_40G_CR4: 1222d5773da8SAndrey V. Elsukov case IFM_40G_SR4: 1223d5773da8SAndrey V. Elsukov case IFM_40G_LR4: 1224ceff9b9dSMitchell Horne case IFM_40G_LM4: 1225eb7e25b2SEric Joyner case IFM_40G_XLPPI: 1226eb7e25b2SEric Joyner case IFM_40G_KR4: 1227fe2bf351SEric Joyner case IFM_40G_XLAUI: 1228fe2bf351SEric Joyner case IFM_40G_XLAUI_AC: 1229fe2bf351SEric Joyner case IFM_40G_ER4: 1230d5773da8SAndrey V. Elsukov key = IFM_40G_CR4; 1231d5773da8SAndrey V. Elsukov break; 1232eb7e25b2SEric Joyner case IFM_100G_CR4: 1233eb7e25b2SEric Joyner case IFM_100G_SR4: 1234eb7e25b2SEric Joyner case IFM_100G_KR4: 1235eb7e25b2SEric Joyner case IFM_100G_LR4: 1236fe2bf351SEric Joyner case IFM_100G_CAUI4_AC: 1237fe2bf351SEric Joyner case IFM_100G_CAUI4: 1238fe2bf351SEric Joyner case IFM_100G_AUI4_AC: 1239fe2bf351SEric Joyner case IFM_100G_AUI4: 1240fe2bf351SEric Joyner case IFM_100G_CR_PAM4: 1241fe2bf351SEric Joyner case IFM_100G_KR_PAM4: 1242fe2bf351SEric Joyner case IFM_100G_CP2: 1243fe2bf351SEric Joyner case IFM_100G_SR2: 1244fe2bf351SEric Joyner case IFM_100G_DR: 1245fe2bf351SEric Joyner case IFM_100G_KR2_PAM4: 1246fe2bf351SEric Joyner case IFM_100G_CAUI2_AC: 1247fe2bf351SEric Joyner case IFM_100G_CAUI2: 1248fe2bf351SEric Joyner case IFM_100G_AUI2_AC: 1249fe2bf351SEric Joyner case IFM_100G_AUI2: 1250eb7e25b2SEric Joyner key = IFM_100G_CR4; 1251eb7e25b2SEric Joyner break; 1252fe2bf351SEric Joyner case IFM_200G_CR4_PAM4: 1253fe2bf351SEric Joyner case IFM_200G_SR4: 1254fe2bf351SEric Joyner case IFM_200G_FR4: 1255fe2bf351SEric Joyner case IFM_200G_LR4: 1256fe2bf351SEric Joyner case IFM_200G_DR4: 1257fe2bf351SEric Joyner case IFM_200G_KR4_PAM4: 1258fe2bf351SEric Joyner case IFM_200G_AUI4_AC: 1259fe2bf351SEric Joyner case IFM_200G_AUI4: 1260fe2bf351SEric Joyner case IFM_200G_AUI8_AC: 1261fe2bf351SEric Joyner case IFM_200G_AUI8: 1262fe2bf351SEric Joyner key = IFM_200G_CR4_PAM4; 1263fe2bf351SEric Joyner break; 1264fe2bf351SEric Joyner case IFM_400G_FR8: 1265fe2bf351SEric Joyner case IFM_400G_LR8: 1266fe2bf351SEric Joyner case IFM_400G_DR4: 1267fe2bf351SEric Joyner case IFM_400G_AUI8_AC: 1268fe2bf351SEric Joyner case IFM_400G_AUI8: 1269fe2bf351SEric Joyner key = IFM_400G_FR8; 1270fe2bf351SEric Joyner break; 1271d5773da8SAndrey V. Elsukov default: 1272b47888ceSAndrew Thompson key = subtype; 1273eb7e25b2SEric Joyner break; 1274d5773da8SAndrey V. Elsukov } 127518242d3bSAndrew Thompson /* bit 5..14: (some bits of) if_index of lagg device */ 1276ec32b37eSAndrew Thompson key |= 0x7fe0 & ((sc->sc_ifp->if_index) << 5); 1277b47888ceSAndrew Thompson /* bit 15: 0 */ 1278b47888ceSAndrew Thompson } 1279b47888ceSAndrew Thompson return (htons(key)); 1280b47888ceSAndrew Thompson } 1281b47888ceSAndrew Thompson 1282b47888ceSAndrew Thompson static void 1283b47888ceSAndrew Thompson lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1284b47888ceSAndrew Thompson { 1285b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1286b47888ceSAndrew Thompson 1287b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1288b47888ceSAndrew Thompson __func__, 1289b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1290b47888ceSAndrew Thompson buf, sizeof(buf)), 1291b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt + 1)); 1292b47888ceSAndrew Thompson 1293b47888ceSAndrew Thompson KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1294b47888ceSAndrew Thompson la->la_refcnt++; 1295b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1296b47888ceSAndrew Thompson } 1297b47888ceSAndrew Thompson 1298b47888ceSAndrew Thompson static void 1299b47888ceSAndrew Thompson lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1300b47888ceSAndrew Thompson { 1301b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1302b47888ceSAndrew Thompson 1303b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1304b47888ceSAndrew Thompson __func__, 1305b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1306b47888ceSAndrew Thompson buf, sizeof(buf)), 1307b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt - 1)); 1308b47888ceSAndrew Thompson 1309b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1310b47888ceSAndrew Thompson la->la_refcnt--; 1311b47888ceSAndrew Thompson if (la->la_refcnt > 0) { 1312b47888ceSAndrew Thompson return; 1313b47888ceSAndrew Thompson } 1314b47888ceSAndrew Thompson 1315b47888ceSAndrew Thompson KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1316b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1317b47888ceSAndrew Thompson 1318b47888ceSAndrew Thompson TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1319b47888ceSAndrew Thompson 1320b47888ceSAndrew Thompson free(la, M_DEVBUF); 1321b47888ceSAndrew Thompson } 1322b47888ceSAndrew Thompson 1323b47888ceSAndrew Thompson /* 1324b47888ceSAndrew Thompson * lacp_aggregator_get: allocate an aggregator. 1325b47888ceSAndrew Thompson */ 1326b47888ceSAndrew Thompson 1327b47888ceSAndrew Thompson static struct lacp_aggregator * 1328b47888ceSAndrew Thompson lacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1329b47888ceSAndrew Thompson { 1330b47888ceSAndrew Thompson struct lacp_aggregator *la; 1331b47888ceSAndrew Thompson 1332b47888ceSAndrew Thompson la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1333b47888ceSAndrew Thompson if (la) { 1334b47888ceSAndrew Thompson la->la_refcnt = 1; 1335b47888ceSAndrew Thompson la->la_nports = 0; 1336b47888ceSAndrew Thompson TAILQ_INIT(&la->la_ports); 1337b47888ceSAndrew Thompson la->la_pending = 0; 1338b47888ceSAndrew Thompson TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1339b47888ceSAndrew Thompson } 1340b47888ceSAndrew Thompson 1341b47888ceSAndrew Thompson return (la); 1342b47888ceSAndrew Thompson } 1343b47888ceSAndrew Thompson 1344b47888ceSAndrew Thompson /* 1345b47888ceSAndrew Thompson * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1346b47888ceSAndrew Thompson */ 1347b47888ceSAndrew Thompson 1348b47888ceSAndrew Thompson static void 1349b47888ceSAndrew Thompson lacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1350b47888ceSAndrew Thompson { 1351b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1352b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1353b47888ceSAndrew Thompson 1354b47888ceSAndrew Thompson la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1355b47888ceSAndrew Thompson } 1356b47888ceSAndrew Thompson 1357b47888ceSAndrew Thompson static void 1358b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1359b47888ceSAndrew Thompson const struct lacp_peerinfo *lpi_port) 1360b47888ceSAndrew Thompson { 1361b47888ceSAndrew Thompson memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1362b47888ceSAndrew Thompson lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1363b47888ceSAndrew Thompson lpi_aggr->lip_key = lpi_port->lip_key; 1364b47888ceSAndrew Thompson } 1365b47888ceSAndrew Thompson 1366b47888ceSAndrew Thompson /* 1367b47888ceSAndrew Thompson * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1368b47888ceSAndrew Thompson */ 1369b47888ceSAndrew Thompson 1370b47888ceSAndrew Thompson static int 1371b47888ceSAndrew Thompson lacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1372b47888ceSAndrew Thompson const struct lacp_port *lp) 1373b47888ceSAndrew Thompson { 1374b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1375b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1376b47888ceSAndrew Thompson return (0); 1377b47888ceSAndrew Thompson } 1378b47888ceSAndrew Thompson 1379b47888ceSAndrew Thompson if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1380b47888ceSAndrew Thompson return (0); 1381b47888ceSAndrew Thompson } 1382b47888ceSAndrew Thompson 1383b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1384b47888ceSAndrew Thompson return (0); 1385b47888ceSAndrew Thompson } 1386b47888ceSAndrew Thompson 1387b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1388b47888ceSAndrew Thompson return (0); 1389b47888ceSAndrew Thompson } 1390b47888ceSAndrew Thompson 1391b47888ceSAndrew Thompson return (1); 1392b47888ceSAndrew Thompson } 1393b47888ceSAndrew Thompson 1394b47888ceSAndrew Thompson static int 1395b47888ceSAndrew Thompson lacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1396b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 1397b47888ceSAndrew Thompson { 1398b47888ceSAndrew Thompson if (memcmp(&a->lip_systemid, &b->lip_systemid, 1399b47888ceSAndrew Thompson sizeof(a->lip_systemid))) { 1400b47888ceSAndrew Thompson return (0); 1401b47888ceSAndrew Thompson } 1402b47888ceSAndrew Thompson 1403b47888ceSAndrew Thompson if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1404b47888ceSAndrew Thompson return (0); 1405b47888ceSAndrew Thompson } 1406b47888ceSAndrew Thompson 1407b47888ceSAndrew Thompson return (1); 1408b47888ceSAndrew Thompson } 1409b47888ceSAndrew Thompson 1410b47888ceSAndrew Thompson static void 1411b47888ceSAndrew Thompson lacp_port_enable(struct lacp_port *lp) 1412b47888ceSAndrew Thompson { 1413b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_AGGREGATION; 1414b47888ceSAndrew Thompson } 1415b47888ceSAndrew Thompson 1416b47888ceSAndrew Thompson static void 1417b47888ceSAndrew Thompson lacp_port_disable(struct lacp_port *lp) 1418b47888ceSAndrew Thompson { 1419b47888ceSAndrew Thompson lacp_set_mux(lp, LACP_MUX_DETACHED); 1420b47888ceSAndrew Thompson 1421b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_AGGREGATION; 1422b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1423b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1424b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1425b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1426b47888ceSAndrew Thompson } 1427b47888ceSAndrew Thompson 1428b47888ceSAndrew Thompson /* 1429b47888ceSAndrew Thompson * lacp_select: select an aggregator. create one if necessary. 1430b47888ceSAndrew Thompson */ 1431b47888ceSAndrew Thompson static void 1432b47888ceSAndrew Thompson lacp_select(struct lacp_port *lp) 1433b47888ceSAndrew Thompson { 1434b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1435b47888ceSAndrew Thompson struct lacp_aggregator *la; 1436b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1437b47888ceSAndrew Thompson 1438b47888ceSAndrew Thompson if (lp->lp_aggregator) { 1439b47888ceSAndrew Thompson return; 1440b47888ceSAndrew Thompson } 1441b47888ceSAndrew Thompson 1442e8003900SJonathan T. Looney /* If we haven't heard from our peer, skip this step. */ 1443e8003900SJonathan T. Looney if (lp->lp_state & LACP_STATE_DEFAULTED) 1444e8003900SJonathan T. Looney return; 1445e8003900SJonathan T. Looney 1446b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1447b47888ceSAndrew Thompson ("timer_wait_while still active")); 1448b47888ceSAndrew Thompson 1449b47888ceSAndrew Thompson LACP_DPRINTF((lp, "port lagid=%s\n", 1450b47888ceSAndrew Thompson lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1451b47888ceSAndrew Thompson buf, sizeof(buf)))); 1452b47888ceSAndrew Thompson 1453b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1454b47888ceSAndrew Thompson if (lacp_aggregator_is_compatible(la, lp)) { 1455b47888ceSAndrew Thompson break; 1456b47888ceSAndrew Thompson } 1457b47888ceSAndrew Thompson } 1458b47888ceSAndrew Thompson 1459b47888ceSAndrew Thompson if (la == NULL) { 1460b47888ceSAndrew Thompson la = lacp_aggregator_get(lsc, lp); 1461b47888ceSAndrew Thompson if (la == NULL) { 1462b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator creation failed\n")); 1463b47888ceSAndrew Thompson 1464b47888ceSAndrew Thompson /* 1465b47888ceSAndrew Thompson * will retry on the next tick. 1466b47888ceSAndrew Thompson */ 1467b47888ceSAndrew Thompson 1468b47888ceSAndrew Thompson return; 1469b47888ceSAndrew Thompson } 1470b47888ceSAndrew Thompson lacp_fill_aggregator_id(la, lp); 1471b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator created\n")); 1472b47888ceSAndrew Thompson } else { 1473b47888ceSAndrew Thompson LACP_DPRINTF((lp, "compatible aggregator found\n")); 14743de18008SAndrew Thompson if (la->la_refcnt == LACP_MAX_PORTS) 14753de18008SAndrew Thompson return; 1476b47888ceSAndrew Thompson lacp_aggregator_addref(lsc, la); 1477b47888ceSAndrew Thompson } 1478b47888ceSAndrew Thompson 1479b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1480b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1481b47888ceSAndrew Thompson buf, sizeof(buf)))); 1482b47888ceSAndrew Thompson 1483b47888ceSAndrew Thompson lp->lp_aggregator = la; 1484b47888ceSAndrew Thompson lp->lp_selected = LACP_SELECTED; 1485b47888ceSAndrew Thompson } 1486b47888ceSAndrew Thompson 1487b47888ceSAndrew Thompson /* 1488b47888ceSAndrew Thompson * lacp_unselect: finish unselect/detach process. 1489b47888ceSAndrew Thompson */ 1490b47888ceSAndrew Thompson 1491b47888ceSAndrew Thompson static void 1492b47888ceSAndrew Thompson lacp_unselect(struct lacp_port *lp) 1493b47888ceSAndrew Thompson { 1494b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1495b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1496b47888ceSAndrew Thompson 1497b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1498b47888ceSAndrew Thompson ("timer_wait_while still active")); 1499b47888ceSAndrew Thompson 1500b47888ceSAndrew Thompson if (la == NULL) { 1501b47888ceSAndrew Thompson return; 1502b47888ceSAndrew Thompson } 1503b47888ceSAndrew Thompson 1504b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 1505b47888ceSAndrew Thompson lacp_aggregator_delref(lsc, la); 1506b47888ceSAndrew Thompson } 1507b47888ceSAndrew Thompson 1508b47888ceSAndrew Thompson /* mux machine */ 1509b47888ceSAndrew Thompson 1510b47888ceSAndrew Thompson static void 1511b47888ceSAndrew Thompson lacp_sm_mux(struct lacp_port *lp) 1512b47888ceSAndrew Thompson { 151331402c27SAdrian Chadd struct lagg_port *lgp = lp->lp_lagg; 151431402c27SAdrian Chadd struct lagg_softc *sc = lgp->lp_softc; 1515b47888ceSAndrew Thompson enum lacp_mux_state new_state; 1516b47888ceSAndrew Thompson boolean_t p_sync = 1517b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1518b47888ceSAndrew Thompson boolean_t p_collecting = 1519b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1520b47888ceSAndrew Thompson enum lacp_selected selected = lp->lp_selected; 1521b47888ceSAndrew Thompson struct lacp_aggregator *la; 1522b47888ceSAndrew Thompson 1523939a050aSHiroki Sato if (V_lacp_debug > 1) 152431402c27SAdrian Chadd lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, " 152531402c27SAdrian Chadd "p_sync= 0x%x, p_collecting= 0x%x\n", __func__, 152631402c27SAdrian Chadd lp->lp_mux_state, selected, p_sync, p_collecting); 1527b47888ceSAndrew Thompson 1528b47888ceSAndrew Thompson re_eval: 1529b47888ceSAndrew Thompson la = lp->lp_aggregator; 1530b47888ceSAndrew Thompson KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1531b47888ceSAndrew Thompson ("MUX not detached")); 1532b47888ceSAndrew Thompson new_state = lp->lp_mux_state; 1533b47888ceSAndrew Thompson switch (lp->lp_mux_state) { 1534b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1535b47888ceSAndrew Thompson if (selected != LACP_UNSELECTED) { 1536b47888ceSAndrew Thompson new_state = LACP_MUX_WAITING; 1537b47888ceSAndrew Thompson } 1538b47888ceSAndrew Thompson break; 1539b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1540b47888ceSAndrew Thompson KASSERT(la->la_pending > 0 || 1541b47888ceSAndrew Thompson !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1542b47888ceSAndrew Thompson ("timer_wait_while still active")); 1543b47888ceSAndrew Thompson if (selected == LACP_SELECTED && la->la_pending == 0) { 1544b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1545b47888ceSAndrew Thompson } else if (selected == LACP_UNSELECTED) { 1546b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1547b47888ceSAndrew Thompson } 1548b47888ceSAndrew Thompson break; 1549b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1550b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync) { 1551b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1552b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED) { 1553b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1554b47888ceSAndrew Thompson } 1555b47888ceSAndrew Thompson break; 1556b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1557b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync && p_collecting) { 1558b47888ceSAndrew Thompson new_state = LACP_MUX_DISTRIBUTING; 1559b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED || !p_sync) { 1560b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1561b47888ceSAndrew Thompson } 1562b47888ceSAndrew Thompson break; 1563b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1564b47888ceSAndrew Thompson if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1565b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1566387e754aSAdrian Chadd lacp_dprintf(lp, "Interface stopped DISTRIBUTING, possible flapping\n"); 156731402c27SAdrian Chadd sc->sc_flapping++; 1568b47888ceSAndrew Thompson } 1569b47888ceSAndrew Thompson break; 1570b47888ceSAndrew Thompson default: 1571b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1572b47888ceSAndrew Thompson } 1573b47888ceSAndrew Thompson 1574b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1575b47888ceSAndrew Thompson return; 1576b47888ceSAndrew Thompson } 1577b47888ceSAndrew Thompson 1578b47888ceSAndrew Thompson lacp_set_mux(lp, new_state); 1579b47888ceSAndrew Thompson goto re_eval; 1580b47888ceSAndrew Thompson } 1581b47888ceSAndrew Thompson 1582b47888ceSAndrew Thompson static void 1583b47888ceSAndrew Thompson lacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1584b47888ceSAndrew Thompson { 1585b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1586b47888ceSAndrew Thompson 1587b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1588b47888ceSAndrew Thompson return; 1589b47888ceSAndrew Thompson } 1590b47888ceSAndrew Thompson 1591b47888ceSAndrew Thompson switch (new_state) { 1592b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1593b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_SYNC; 1594b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1595b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1596b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1597b47888ceSAndrew Thompson /* cancel timer */ 1598b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1599b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, 1600b47888ceSAndrew Thompson ("timer_wait_while not active")); 1601b47888ceSAndrew Thompson la->la_pending--; 1602b47888ceSAndrew Thompson } 1603b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1604b47888ceSAndrew Thompson lacp_unselect(lp); 1605b47888ceSAndrew Thompson break; 1606b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1607b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1608b47888ceSAndrew Thompson LACP_AGGREGATE_WAIT_TIME); 1609b47888ceSAndrew Thompson la->la_pending++; 1610b47888ceSAndrew Thompson break; 1611b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1612b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_SYNC; 1613b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1614b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1615b47888ceSAndrew Thompson break; 1616b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1617b47888ceSAndrew Thompson lacp_enable_collecting(lp); 1618b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1619b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1620b47888ceSAndrew Thompson break; 1621b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1622b47888ceSAndrew Thompson lacp_enable_distributing(lp); 1623b47888ceSAndrew Thompson break; 1624b47888ceSAndrew Thompson default: 1625b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1626b47888ceSAndrew Thompson } 1627b47888ceSAndrew Thompson 1628b47888ceSAndrew Thompson LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1629b47888ceSAndrew Thompson 1630b47888ceSAndrew Thompson lp->lp_mux_state = new_state; 1631b47888ceSAndrew Thompson } 1632b47888ceSAndrew Thompson 1633b47888ceSAndrew Thompson static void 1634b47888ceSAndrew Thompson lacp_sm_mux_timer(struct lacp_port *lp) 1635b47888ceSAndrew Thompson { 1636b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1637b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1638b47888ceSAndrew Thompson 1639b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, ("no pending event")); 1640b47888ceSAndrew Thompson 1641b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1642b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1643b47888ceSAndrew Thompson buf, sizeof(buf)), 1644b47888ceSAndrew Thompson la->la_pending, la->la_pending - 1)); 1645b47888ceSAndrew Thompson 1646b47888ceSAndrew Thompson la->la_pending--; 1647b47888ceSAndrew Thompson } 1648b47888ceSAndrew Thompson 1649b47888ceSAndrew Thompson /* periodic transmit machine */ 1650b47888ceSAndrew Thompson 1651b47888ceSAndrew Thompson static void 1652b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1653b47888ceSAndrew Thompson { 1654b47888ceSAndrew Thompson if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1655b47888ceSAndrew Thompson LACP_STATE_TIMEOUT)) { 1656b47888ceSAndrew Thompson return; 1657b47888ceSAndrew Thompson } 1658b47888ceSAndrew Thompson 1659b47888ceSAndrew Thompson LACP_DPRINTF((lp, "partner timeout changed\n")); 1660b47888ceSAndrew Thompson 1661b47888ceSAndrew Thompson /* 1662b47888ceSAndrew Thompson * FAST_PERIODIC -> SLOW_PERIODIC 1663b47888ceSAndrew Thompson * or 1664b47888ceSAndrew Thompson * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1665b47888ceSAndrew Thompson * 1666b47888ceSAndrew Thompson * let lacp_sm_ptx_tx_schedule to update timeout. 1667b47888ceSAndrew Thompson */ 1668b47888ceSAndrew Thompson 1669b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1670b47888ceSAndrew Thompson 1671b47888ceSAndrew Thompson /* 1672b47888ceSAndrew Thompson * if timeout has been shortened, assert NTT. 1673b47888ceSAndrew Thompson */ 1674b47888ceSAndrew Thompson 1675b47888ceSAndrew Thompson if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1676b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1677b47888ceSAndrew Thompson } 1678b47888ceSAndrew Thompson } 1679b47888ceSAndrew Thompson 1680b47888ceSAndrew Thompson static void 1681b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1682b47888ceSAndrew Thompson { 1683b47888ceSAndrew Thompson int timeout; 1684b47888ceSAndrew Thompson 1685b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1686b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1687b47888ceSAndrew Thompson /* 1688b47888ceSAndrew Thompson * NO_PERIODIC 1689b47888ceSAndrew Thompson */ 1690b47888ceSAndrew Thompson 1691b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1692b47888ceSAndrew Thompson return; 1693b47888ceSAndrew Thompson } 1694b47888ceSAndrew Thompson 1695b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1696b47888ceSAndrew Thompson return; 1697b47888ceSAndrew Thompson } 1698b47888ceSAndrew Thompson 1699b47888ceSAndrew Thompson timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1700b47888ceSAndrew Thompson LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1701b47888ceSAndrew Thompson 1702b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1703b47888ceSAndrew Thompson } 1704b47888ceSAndrew Thompson 1705b47888ceSAndrew Thompson static void 1706b47888ceSAndrew Thompson lacp_sm_ptx_timer(struct lacp_port *lp) 1707b47888ceSAndrew Thompson { 1708b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1709b47888ceSAndrew Thompson } 1710b47888ceSAndrew Thompson 1711b47888ceSAndrew Thompson static void 1712b47888ceSAndrew Thompson lacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1713b47888ceSAndrew Thompson { 1714b47888ceSAndrew Thompson int timeout; 1715b47888ceSAndrew Thompson 1716b47888ceSAndrew Thompson /* 1717b47888ceSAndrew Thompson * check LACP_DISABLED first 1718b47888ceSAndrew Thompson */ 1719b47888ceSAndrew Thompson 1720b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1721b47888ceSAndrew Thompson return; 1722b47888ceSAndrew Thompson } 1723b47888ceSAndrew Thompson 1724b47888ceSAndrew Thompson /* 1725b47888ceSAndrew Thompson * check loopback condition. 1726b47888ceSAndrew Thompson */ 1727b47888ceSAndrew Thompson 1728b47888ceSAndrew Thompson if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1729b47888ceSAndrew Thompson &lp->lp_actor.lip_systemid)) { 1730b47888ceSAndrew Thompson return; 1731b47888ceSAndrew Thompson } 1732b47888ceSAndrew Thompson 1733b47888ceSAndrew Thompson /* 1734b47888ceSAndrew Thompson * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1735b47888ceSAndrew Thompson */ 1736b47888ceSAndrew Thompson 173700a80538SGreg Foster microuptime(&lp->lp_last_lacpdu_rx); 1738b47888ceSAndrew Thompson lacp_sm_rx_update_selected(lp, du); 1739b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(lp, du); 1740b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(lp, du); 1741b47888ceSAndrew Thompson 1742b47888ceSAndrew Thompson timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1743b47888ceSAndrew Thompson LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1744b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1745b47888ceSAndrew Thompson 1746b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1747b47888ceSAndrew Thompson 1748b47888ceSAndrew Thompson /* 1749b47888ceSAndrew Thompson * kick transmit machine without waiting the next tick. 1750b47888ceSAndrew Thompson */ 1751b47888ceSAndrew Thompson 1752b47888ceSAndrew Thompson lacp_sm_tx(lp); 1753b47888ceSAndrew Thompson } 1754b47888ceSAndrew Thompson 1755b47888ceSAndrew Thompson static void 1756b47888ceSAndrew Thompson lacp_sm_rx_set_expired(struct lacp_port *lp) 1757b47888ceSAndrew Thompson { 1758b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1759b47888ceSAndrew Thompson lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1760b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1761b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_EXPIRED; 1762b47888ceSAndrew Thompson } 1763b47888ceSAndrew Thompson 1764b47888ceSAndrew Thompson static void 1765b47888ceSAndrew Thompson lacp_sm_rx_timer(struct lacp_port *lp) 1766b47888ceSAndrew Thompson { 1767b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1768b47888ceSAndrew Thompson /* CURRENT -> EXPIRED */ 1769b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1770b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 1771b47888ceSAndrew Thompson } else { 1772b47888ceSAndrew Thompson /* EXPIRED -> DEFAULTED */ 1773b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1774b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(lp); 1775b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1776b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1777b47888ceSAndrew Thompson } 1778b47888ceSAndrew Thompson } 1779b47888ceSAndrew Thompson 1780b47888ceSAndrew Thompson static void 1781b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1782b47888ceSAndrew Thompson { 1783b47888ceSAndrew Thompson boolean_t active; 1784b47888ceSAndrew Thompson uint8_t oldpstate; 1785b47888ceSAndrew Thompson char buf[LACP_STATESTR_MAX+1]; 1786b47888ceSAndrew Thompson 17875fc4c149SAndrew Thompson LACP_TRACE(lp); 1788b47888ceSAndrew Thompson 1789b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1790b47888ceSAndrew Thompson 1791b47888ceSAndrew Thompson active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1792b47888ceSAndrew Thompson || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1793b47888ceSAndrew Thompson (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1794b47888ceSAndrew Thompson 1795b47888ceSAndrew Thompson lp->lp_partner = du->ldu_actor; 1796b47888ceSAndrew Thompson if (active && 1797b47888ceSAndrew Thompson ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1798b47888ceSAndrew Thompson LACP_STATE_AGGREGATION) && 1799b47888ceSAndrew Thompson !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1800b47888ceSAndrew Thompson || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1801e8003900SJonathan T. Looney /* 1802e8003900SJonathan T. Looney * XXX Maintain legacy behavior of leaving the 1803e8003900SJonathan T. Looney * LACP_STATE_SYNC bit unchanged from the partner's 1804e8003900SJonathan T. Looney * advertisement if lsc_strict_mode is false. 1805e8003900SJonathan T. Looney * TODO: We should re-examine the concept of the "strict mode" 1806e8003900SJonathan T. Looney * to ensure it makes sense to maintain a non-strict mode. 1807e8003900SJonathan T. Looney */ 1808e8003900SJonathan T. Looney if (lp->lp_lsc->lsc_strict_mode) 1809e8003900SJonathan T. Looney lp->lp_partner.lip_state |= LACP_STATE_SYNC; 1810b47888ceSAndrew Thompson } else { 1811b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1812b47888ceSAndrew Thompson } 1813b47888ceSAndrew Thompson 1814b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DEFAULTED; 1815b47888ceSAndrew Thompson 1816b47888ceSAndrew Thompson if (oldpstate != lp->lp_partner.lip_state) { 1817b47888ceSAndrew Thompson LACP_DPRINTF((lp, "old pstate %s\n", 1818b47888ceSAndrew Thompson lacp_format_state(oldpstate, buf, sizeof(buf)))); 1819b47888ceSAndrew Thompson LACP_DPRINTF((lp, "new pstate %s\n", 1820b47888ceSAndrew Thompson lacp_format_state(lp->lp_partner.lip_state, buf, 1821b47888ceSAndrew Thompson sizeof(buf)))); 1822b47888ceSAndrew Thompson } 1823b47888ceSAndrew Thompson 1824b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1825b47888ceSAndrew Thompson } 1826b47888ceSAndrew Thompson 1827b47888ceSAndrew Thompson static void 1828b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1829b47888ceSAndrew Thompson { 18305fc4c149SAndrew Thompson 18315fc4c149SAndrew Thompson LACP_TRACE(lp); 1832b47888ceSAndrew Thompson 1833b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1834b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1835b47888ceSAndrew Thompson LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1836b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1837b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1838b47888ceSAndrew Thompson } 1839b47888ceSAndrew Thompson } 1840b47888ceSAndrew Thompson 1841b47888ceSAndrew Thompson static void 1842b47888ceSAndrew Thompson lacp_sm_rx_record_default(struct lacp_port *lp) 1843b47888ceSAndrew Thompson { 1844b47888ceSAndrew Thompson uint8_t oldpstate; 1845b47888ceSAndrew Thompson 18465fc4c149SAndrew Thompson LACP_TRACE(lp); 1847b47888ceSAndrew Thompson 1848b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 184949de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 185031402c27SAdrian Chadd lp->lp_partner = lacp_partner_admin_strict; 185131402c27SAdrian Chadd else 1852c1be893cSSteven Hartland lp->lp_partner = lacp_partner_admin_optimistic; 1853b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DEFAULTED; 1854b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1855b47888ceSAndrew Thompson } 1856b47888ceSAndrew Thompson 1857b47888ceSAndrew Thompson static void 1858b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1859b47888ceSAndrew Thompson const struct lacp_peerinfo *info) 1860b47888ceSAndrew Thompson { 18615fc4c149SAndrew Thompson 18625fc4c149SAndrew Thompson LACP_TRACE(lp); 1863b47888ceSAndrew Thompson 1864b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1865b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1866b47888ceSAndrew Thompson LACP_STATE_AGGREGATION)) { 1867b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1868b47888ceSAndrew Thompson /* mux machine will clean up lp->lp_aggregator */ 1869b47888ceSAndrew Thompson } 1870b47888ceSAndrew Thompson } 1871b47888ceSAndrew Thompson 1872b47888ceSAndrew Thompson static void 1873b47888ceSAndrew Thompson lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1874b47888ceSAndrew Thompson { 18755fc4c149SAndrew Thompson 18765fc4c149SAndrew Thompson LACP_TRACE(lp); 1877b47888ceSAndrew Thompson 1878b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1879b47888ceSAndrew Thompson } 1880b47888ceSAndrew Thompson 1881b47888ceSAndrew Thompson static void 1882b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(struct lacp_port *lp) 1883b47888ceSAndrew Thompson { 18845fc4c149SAndrew Thompson 18855fc4c149SAndrew Thompson LACP_TRACE(lp); 1886b47888ceSAndrew Thompson 188749de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 188831402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 188931402c27SAdrian Chadd &lacp_partner_admin_strict); 189031402c27SAdrian Chadd else 189131402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 189231402c27SAdrian Chadd &lacp_partner_admin_optimistic); 1893b47888ceSAndrew Thompson } 1894b47888ceSAndrew Thompson 1895b47888ceSAndrew Thompson /* transmit machine */ 1896b47888ceSAndrew Thompson 1897b47888ceSAndrew Thompson static void 1898b47888ceSAndrew Thompson lacp_sm_tx(struct lacp_port *lp) 1899b47888ceSAndrew Thompson { 190031402c27SAdrian Chadd int error = 0; 1901b47888ceSAndrew Thompson 1902b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1903b47888ceSAndrew Thompson #if 1 1904b47888ceSAndrew Thompson || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1905b47888ceSAndrew Thompson && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1906b47888ceSAndrew Thompson #endif 1907b47888ceSAndrew Thompson ) { 1908b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1909b47888ceSAndrew Thompson } 1910b47888ceSAndrew Thompson 1911b47888ceSAndrew Thompson if (!(lp->lp_flags & LACP_PORT_NTT)) { 1912b47888ceSAndrew Thompson return; 1913b47888ceSAndrew Thompson } 1914b47888ceSAndrew Thompson 1915b47888ceSAndrew Thompson /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1916b47888ceSAndrew Thompson if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1917b47888ceSAndrew Thompson (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1918b47888ceSAndrew Thompson LACP_DPRINTF((lp, "rate limited pdu\n")); 1919b47888ceSAndrew Thompson return; 1920b47888ceSAndrew Thompson } 1921b47888ceSAndrew Thompson 192249de4f22SAdrian Chadd if (((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_tx_test) == 0) { 1923b47888ceSAndrew Thompson error = lacp_xmit_lacpdu(lp); 192449de4f22SAdrian Chadd } else { 192531402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping TX PDU\n")); 192649de4f22SAdrian Chadd } 1927b47888ceSAndrew Thompson 1928b47888ceSAndrew Thompson if (error == 0) { 1929b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1930b47888ceSAndrew Thompson } else { 1931b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1932b47888ceSAndrew Thompson error)); 1933b47888ceSAndrew Thompson } 1934b47888ceSAndrew Thompson } 1935b47888ceSAndrew Thompson 1936b47888ceSAndrew Thompson static void 1937b47888ceSAndrew Thompson lacp_sm_assert_ntt(struct lacp_port *lp) 1938b47888ceSAndrew Thompson { 1939b47888ceSAndrew Thompson 1940b47888ceSAndrew Thompson lp->lp_flags |= LACP_PORT_NTT; 1941b47888ceSAndrew Thompson } 1942b47888ceSAndrew Thompson 1943b47888ceSAndrew Thompson static void 1944b47888ceSAndrew Thompson lacp_run_timers(struct lacp_port *lp) 1945b47888ceSAndrew Thompson { 1946b47888ceSAndrew Thompson int i; 194700a80538SGreg Foster struct timeval time_diff; 1948b47888ceSAndrew Thompson 1949b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 1950b47888ceSAndrew Thompson KASSERT(lp->lp_timer[i] >= 0, 1951b47888ceSAndrew Thompson ("invalid timer value %d", lp->lp_timer[i])); 1952b47888ceSAndrew Thompson if (lp->lp_timer[i] == 0) { 1953b47888ceSAndrew Thompson continue; 195400a80538SGreg Foster } else { 195500a80538SGreg Foster if (i == LACP_TIMER_CURRENT_WHILE) { 195600a80538SGreg Foster microuptime(&time_diff); 195700a80538SGreg Foster timevalsub(&time_diff, &lp->lp_last_lacpdu_rx); 195800a80538SGreg Foster if (time_diff.tv_sec) { 195900a80538SGreg Foster /* At least one sec has elapsed since last LACP packet. */ 196000a80538SGreg Foster --lp->lp_timer[i]; 196100a80538SGreg Foster } 196200a80538SGreg Foster } else { 196300a80538SGreg Foster --lp->lp_timer[i]; 196400a80538SGreg Foster } 196500a80538SGreg Foster 196600a80538SGreg Foster if ((lp->lp_timer[i] <= 0) && (lacp_timer_funcs[i])) { 1967b47888ceSAndrew Thompson (*lacp_timer_funcs[i])(lp); 1968b47888ceSAndrew Thompson } 1969b47888ceSAndrew Thompson } 1970b47888ceSAndrew Thompson } 1971b47888ceSAndrew Thompson } 1972b47888ceSAndrew Thompson 1973b47888ceSAndrew Thompson int 19743de18008SAndrew Thompson lacp_marker_input(struct lacp_port *lp, struct mbuf *m) 1975b47888ceSAndrew Thompson { 1976998971a7SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 19773de18008SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 19783de18008SAndrew Thompson struct lacp_port *lp2; 1979b47888ceSAndrew Thompson struct markerdu *mdu; 1980b47888ceSAndrew Thompson int error = 0; 1981998971a7SAndrew Thompson int pending = 0; 1982b47888ceSAndrew Thompson 1983b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*mdu)) { 1984b47888ceSAndrew Thompson goto bad; 1985b47888ceSAndrew Thompson } 1986b47888ceSAndrew Thompson 1987b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 1988b47888ceSAndrew Thompson goto bad; 1989b47888ceSAndrew Thompson } 1990b47888ceSAndrew Thompson 1991b47888ceSAndrew Thompson if (m->m_len < sizeof(*mdu)) { 1992b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*mdu)); 1993b47888ceSAndrew Thompson if (m == NULL) { 1994b47888ceSAndrew Thompson return (ENOMEM); 1995b47888ceSAndrew Thompson } 1996b47888ceSAndrew Thompson } 1997b47888ceSAndrew Thompson 1998b47888ceSAndrew Thompson mdu = mtod(m, struct markerdu *); 1999b47888ceSAndrew Thompson 2000b47888ceSAndrew Thompson if (memcmp(&mdu->mdu_eh.ether_dhost, 2001b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 2002b47888ceSAndrew Thompson goto bad; 2003b47888ceSAndrew Thompson } 2004b47888ceSAndrew Thompson 2005b47888ceSAndrew Thompson if (mdu->mdu_sph.sph_version != 1) { 2006b47888ceSAndrew Thompson goto bad; 2007b47888ceSAndrew Thompson } 2008b47888ceSAndrew Thompson 2009b47888ceSAndrew Thompson switch (mdu->mdu_tlv.tlv_type) { 2010b47888ceSAndrew Thompson case MARKER_TYPE_INFO: 2011b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 2012b47888ceSAndrew Thompson marker_info_tlv_template, TRUE)) { 2013b47888ceSAndrew Thompson goto bad; 2014b47888ceSAndrew Thompson } 2015b47888ceSAndrew Thompson mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 2016b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, 2017b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 2018b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, 201918242d3bSAndrew Thompson lgp->lp_lladdr, ETHER_ADDR_LEN); 202018242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 2021b47888ceSAndrew Thompson break; 2022b47888ceSAndrew Thompson 2023b47888ceSAndrew Thompson case MARKER_TYPE_RESPONSE: 2024b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 2025b47888ceSAndrew Thompson marker_response_tlv_template, TRUE)) { 2026b47888ceSAndrew Thompson goto bad; 2027b47888ceSAndrew Thompson } 2028998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 2029998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 2030998971a7SAndrew Thompson ":", ntohl(mdu->mdu_info.mi_rq_xid))); 2031998971a7SAndrew Thompson 2032998971a7SAndrew Thompson /* Verify that it is the last marker we sent out */ 2033998971a7SAndrew Thompson if (memcmp(&mdu->mdu_info, &lp->lp_marker, 2034998971a7SAndrew Thompson sizeof(struct lacp_markerinfo))) 2035998971a7SAndrew Thompson goto bad; 2036998971a7SAndrew Thompson 20373de18008SAndrew Thompson LACP_LOCK(lsc); 2038998971a7SAndrew Thompson lp->lp_flags &= ~LACP_PORT_MARK; 2039998971a7SAndrew Thompson 2040998971a7SAndrew Thompson if (lsc->lsc_suppress_distributing) { 2041998971a7SAndrew Thompson /* Check if any ports are waiting for a response */ 2042998971a7SAndrew Thompson LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 2043998971a7SAndrew Thompson if (lp2->lp_flags & LACP_PORT_MARK) { 2044998971a7SAndrew Thompson pending = 1; 2045998971a7SAndrew Thompson break; 2046998971a7SAndrew Thompson } 2047998971a7SAndrew Thompson } 2048998971a7SAndrew Thompson 2049998971a7SAndrew Thompson if (pending == 0) { 2050998971a7SAndrew Thompson /* All interface queues are clear */ 2051998971a7SAndrew Thompson LACP_DPRINTF((NULL, "queue flush complete\n")); 2052998971a7SAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 2053998971a7SAndrew Thompson } 2054998971a7SAndrew Thompson } 20553de18008SAndrew Thompson LACP_UNLOCK(lsc); 2056998971a7SAndrew Thompson m_freem(m); 2057998971a7SAndrew Thompson break; 2058998971a7SAndrew Thompson 2059b47888ceSAndrew Thompson default: 2060b47888ceSAndrew Thompson goto bad; 2061b47888ceSAndrew Thompson } 2062b47888ceSAndrew Thompson 2063b47888ceSAndrew Thompson return (error); 2064b47888ceSAndrew Thompson 2065b47888ceSAndrew Thompson bad: 2066998971a7SAndrew Thompson LACP_DPRINTF((lp, "bad marker frame\n")); 2067b47888ceSAndrew Thompson m_freem(m); 2068b47888ceSAndrew Thompson return (EINVAL); 2069b47888ceSAndrew Thompson } 2070b47888ceSAndrew Thompson 2071b47888ceSAndrew Thompson static int 2072b47888ceSAndrew Thompson tlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 2073b47888ceSAndrew Thompson const struct tlv_template *tmpl, boolean_t check_type) 2074b47888ceSAndrew Thompson { 2075b47888ceSAndrew Thompson while (/* CONSTCOND */ 1) { 2076b47888ceSAndrew Thompson if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 2077b47888ceSAndrew Thompson return (EINVAL); 2078b47888ceSAndrew Thompson } 2079b47888ceSAndrew Thompson if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 2080b47888ceSAndrew Thompson tlv->tlv_length != tmpl->tmpl_length) { 2081b47888ceSAndrew Thompson return (EINVAL); 2082b47888ceSAndrew Thompson } 2083b47888ceSAndrew Thompson if (tmpl->tmpl_type == 0) { 2084b47888ceSAndrew Thompson break; 2085b47888ceSAndrew Thompson } 2086b47888ceSAndrew Thompson tlv = (const struct tlvhdr *) 2087b47888ceSAndrew Thompson ((const char *)tlv + tlv->tlv_length); 2088b47888ceSAndrew Thompson tmpl++; 2089b47888ceSAndrew Thompson } 2090b47888ceSAndrew Thompson 2091b47888ceSAndrew Thompson return (0); 2092b47888ceSAndrew Thompson } 2093b47888ceSAndrew Thompson 20945fc4c149SAndrew Thompson /* Debugging */ 2095b47888ceSAndrew Thompson const char * 2096b47888ceSAndrew Thompson lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 2097b47888ceSAndrew Thompson { 2098b47888ceSAndrew Thompson snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 2099b47888ceSAndrew Thompson (int)mac[0], 2100b47888ceSAndrew Thompson (int)mac[1], 2101b47888ceSAndrew Thompson (int)mac[2], 2102b47888ceSAndrew Thompson (int)mac[3], 2103b47888ceSAndrew Thompson (int)mac[4], 2104b47888ceSAndrew Thompson (int)mac[5]); 2105b47888ceSAndrew Thompson 2106b47888ceSAndrew Thompson return (buf); 2107b47888ceSAndrew Thompson } 2108b47888ceSAndrew Thompson 2109b47888ceSAndrew Thompson const char * 2110b47888ceSAndrew Thompson lacp_format_systemid(const struct lacp_systemid *sysid, 2111b47888ceSAndrew Thompson char *buf, size_t buflen) 2112b47888ceSAndrew Thompson { 2113b47888ceSAndrew Thompson char macbuf[LACP_MACSTR_MAX+1]; 2114b47888ceSAndrew Thompson 2115b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%s", 2116b47888ceSAndrew Thompson ntohs(sysid->lsi_prio), 2117b47888ceSAndrew Thompson lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 2118b47888ceSAndrew Thompson 2119b47888ceSAndrew Thompson return (buf); 2120b47888ceSAndrew Thompson } 2121b47888ceSAndrew Thompson 2122b47888ceSAndrew Thompson const char * 2123b47888ceSAndrew Thompson lacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 2124b47888ceSAndrew Thompson { 2125b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%04X", 2126b47888ceSAndrew Thompson ntohs(portid->lpi_prio), 2127b47888ceSAndrew Thompson ntohs(portid->lpi_portno)); 2128b47888ceSAndrew Thompson 2129b47888ceSAndrew Thompson return (buf); 2130b47888ceSAndrew Thompson } 2131b47888ceSAndrew Thompson 2132b47888ceSAndrew Thompson const char * 2133b47888ceSAndrew Thompson lacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 2134b47888ceSAndrew Thompson { 2135b47888ceSAndrew Thompson char sysid[LACP_SYSTEMIDSTR_MAX+1]; 2136b47888ceSAndrew Thompson char portid[LACP_PORTIDSTR_MAX+1]; 2137b47888ceSAndrew Thompson 2138b47888ceSAndrew Thompson snprintf(buf, buflen, "(%s,%04X,%s)", 2139b47888ceSAndrew Thompson lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 2140b47888ceSAndrew Thompson ntohs(peer->lip_key), 2141b47888ceSAndrew Thompson lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 2142b47888ceSAndrew Thompson 2143b47888ceSAndrew Thompson return (buf); 2144b47888ceSAndrew Thompson } 2145b47888ceSAndrew Thompson 2146b47888ceSAndrew Thompson const char * 2147b47888ceSAndrew Thompson lacp_format_lagid(const struct lacp_peerinfo *a, 2148b47888ceSAndrew Thompson const struct lacp_peerinfo *b, char *buf, size_t buflen) 2149b47888ceSAndrew Thompson { 2150b47888ceSAndrew Thompson char astr[LACP_PARTNERSTR_MAX+1]; 2151b47888ceSAndrew Thompson char bstr[LACP_PARTNERSTR_MAX+1]; 2152b47888ceSAndrew Thompson 2153b47888ceSAndrew Thompson #if 0 2154b47888ceSAndrew Thompson /* 2155b47888ceSAndrew Thompson * there's a convention to display small numbered peer 2156b47888ceSAndrew Thompson * in the left. 2157b47888ceSAndrew Thompson */ 2158b47888ceSAndrew Thompson 2159b47888ceSAndrew Thompson if (lacp_compare_peerinfo(a, b) > 0) { 2160b47888ceSAndrew Thompson const struct lacp_peerinfo *t; 2161b47888ceSAndrew Thompson 2162b47888ceSAndrew Thompson t = a; 2163b47888ceSAndrew Thompson a = b; 2164b47888ceSAndrew Thompson b = t; 2165b47888ceSAndrew Thompson } 2166b47888ceSAndrew Thompson #endif 2167b47888ceSAndrew Thompson 2168b47888ceSAndrew Thompson snprintf(buf, buflen, "[%s,%s]", 2169b47888ceSAndrew Thompson lacp_format_partner(a, astr, sizeof(astr)), 2170b47888ceSAndrew Thompson lacp_format_partner(b, bstr, sizeof(bstr))); 2171b47888ceSAndrew Thompson 2172b47888ceSAndrew Thompson return (buf); 2173b47888ceSAndrew Thompson } 2174b47888ceSAndrew Thompson 2175b47888ceSAndrew Thompson const char * 2176b47888ceSAndrew Thompson lacp_format_lagid_aggregator(const struct lacp_aggregator *la, 2177b47888ceSAndrew Thompson char *buf, size_t buflen) 2178b47888ceSAndrew Thompson { 2179b47888ceSAndrew Thompson if (la == NULL) { 2180b47888ceSAndrew Thompson return ("(none)"); 2181b47888ceSAndrew Thompson } 2182b47888ceSAndrew Thompson 2183b47888ceSAndrew Thompson return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 2184b47888ceSAndrew Thompson } 2185b47888ceSAndrew Thompson 2186b47888ceSAndrew Thompson const char * 2187b47888ceSAndrew Thompson lacp_format_state(uint8_t state, char *buf, size_t buflen) 2188b47888ceSAndrew Thompson { 2189b47888ceSAndrew Thompson snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 2190b47888ceSAndrew Thompson return (buf); 2191b47888ceSAndrew Thompson } 2192b47888ceSAndrew Thompson 2193b47888ceSAndrew Thompson static void 2194b47888ceSAndrew Thompson lacp_dump_lacpdu(const struct lacpdu *du) 2195b47888ceSAndrew Thompson { 2196b47888ceSAndrew Thompson char buf[LACP_PARTNERSTR_MAX+1]; 2197b47888ceSAndrew Thompson char buf2[LACP_STATESTR_MAX+1]; 2198b47888ceSAndrew Thompson 2199b47888ceSAndrew Thompson printf("actor=%s\n", 2200b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 2201b47888ceSAndrew Thompson printf("actor.state=%s\n", 2202b47888ceSAndrew Thompson lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 2203b47888ceSAndrew Thompson printf("partner=%s\n", 2204b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 2205b47888ceSAndrew Thompson printf("partner.state=%s\n", 2206b47888ceSAndrew Thompson lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 2207b47888ceSAndrew Thompson 2208b47888ceSAndrew Thompson printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 2209b47888ceSAndrew Thompson } 2210b47888ceSAndrew Thompson 2211b47888ceSAndrew Thompson static void 2212b47888ceSAndrew Thompson lacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 2213b47888ceSAndrew Thompson { 2214b47888ceSAndrew Thompson va_list va; 2215b47888ceSAndrew Thompson 2216b47888ceSAndrew Thompson if (lp) { 2217b47888ceSAndrew Thompson printf("%s: ", lp->lp_ifp->if_xname); 2218b47888ceSAndrew Thompson } 2219b47888ceSAndrew Thompson 2220b47888ceSAndrew Thompson va_start(va, fmt); 2221b47888ceSAndrew Thompson vprintf(fmt, va); 2222b47888ceSAndrew Thompson va_end(va); 2223b47888ceSAndrew Thompson } 2224