1b47888ceSAndrew Thompson /* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */ 2b47888ceSAndrew Thompson 3b47888ceSAndrew Thompson /*- 4b47888ceSAndrew Thompson * Copyright (c)2005 YAMAMOTO Takashi, 53de18008SAndrew Thompson * Copyright (c)2008 Andrew Thompson <thompsa@FreeBSD.org> 6b47888ceSAndrew Thompson * All rights reserved. 7b47888ceSAndrew Thompson * 8b47888ceSAndrew Thompson * Redistribution and use in source and binary forms, with or without 9b47888ceSAndrew Thompson * modification, are permitted provided that the following conditions 10b47888ceSAndrew Thompson * are met: 11b47888ceSAndrew Thompson * 1. Redistributions of source code must retain the above copyright 12b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer. 13b47888ceSAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 14b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer in the 15b47888ceSAndrew Thompson * documentation and/or other materials provided with the distribution. 16b47888ceSAndrew Thompson * 17b47888ceSAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18b47888ceSAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19b47888ceSAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20b47888ceSAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21b47888ceSAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22b47888ceSAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23b47888ceSAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24b47888ceSAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25b47888ceSAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26b47888ceSAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27b47888ceSAndrew Thompson * SUCH DAMAGE. 28b47888ceSAndrew Thompson */ 29b47888ceSAndrew Thompson 30b47888ceSAndrew Thompson #include <sys/cdefs.h> 31b47888ceSAndrew Thompson __FBSDID("$FreeBSD$"); 32b47888ceSAndrew Thompson 33f3e7afe2SHans Petter Selasky #include "opt_ratelimit.h" 34f3e7afe2SHans Petter Selasky 35b47888ceSAndrew Thompson #include <sys/param.h> 36b47888ceSAndrew Thompson #include <sys/callout.h> 37c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 38b47888ceSAndrew Thompson #include <sys/mbuf.h> 39b47888ceSAndrew Thompson #include <sys/systm.h> 40b47888ceSAndrew Thompson #include <sys/malloc.h> 41b47888ceSAndrew Thompson #include <sys/kernel.h> /* hz */ 42b47888ceSAndrew Thompson #include <sys/socket.h> /* for net/if.h */ 43b47888ceSAndrew Thompson #include <sys/sockio.h> 445fc4c149SAndrew Thompson #include <sys/sysctl.h> 45b47888ceSAndrew Thompson #include <machine/stdarg.h> 463bf517e3SAndrew Thompson #include <sys/lock.h> 473bf517e3SAndrew Thompson #include <sys/rwlock.h> 4876039bc8SGleb Smirnoff #include <sys/taskqueue.h> 49b47888ceSAndrew Thompson 50b47888ceSAndrew Thompson #include <net/if.h> 5176039bc8SGleb Smirnoff #include <net/if_var.h> 52b47888ceSAndrew Thompson #include <net/if_dl.h> 53b47888ceSAndrew Thompson #include <net/ethernet.h> 54b47888ceSAndrew Thompson #include <net/if_media.h> 55b47888ceSAndrew Thompson #include <net/if_types.h> 56b47888ceSAndrew Thompson 5718242d3bSAndrew Thompson #include <net/if_lagg.h> 58b47888ceSAndrew Thompson #include <net/ieee8023ad_lacp.h> 59b47888ceSAndrew Thompson 60b47888ceSAndrew Thompson /* 61b47888ceSAndrew Thompson * actor system priority and port priority. 62b47888ceSAndrew Thompson * XXX should be configurable. 63b47888ceSAndrew Thompson */ 64b47888ceSAndrew Thompson 65b47888ceSAndrew Thompson #define LACP_SYSTEM_PRIO 0x8000 66b47888ceSAndrew Thompson #define LACP_PORT_PRIO 0x8000 67b47888ceSAndrew Thompson 68b47888ceSAndrew Thompson const uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 69b47888ceSAndrew Thompson { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 70b47888ceSAndrew Thompson 71b47888ceSAndrew Thompson static const struct tlv_template lacp_info_tlv_template[] = { 72b47888ceSAndrew Thompson { LACP_TYPE_ACTORINFO, 73b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 74b47888ceSAndrew Thompson { LACP_TYPE_PARTNERINFO, 75b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 76b47888ceSAndrew Thompson { LACP_TYPE_COLLECTORINFO, 77b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 78b47888ceSAndrew Thompson { 0, 0 }, 79b47888ceSAndrew Thompson }; 80b47888ceSAndrew Thompson 81b47888ceSAndrew Thompson static const struct tlv_template marker_info_tlv_template[] = { 82998971a7SAndrew Thompson { MARKER_TYPE_INFO, 83998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 84b47888ceSAndrew Thompson { 0, 0 }, 85b47888ceSAndrew Thompson }; 86b47888ceSAndrew Thompson 87b47888ceSAndrew Thompson static const struct tlv_template marker_response_tlv_template[] = { 88998971a7SAndrew Thompson { MARKER_TYPE_RESPONSE, 89998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 90b47888ceSAndrew Thompson { 0, 0 }, 91b47888ceSAndrew Thompson }; 92b47888ceSAndrew Thompson 933de18008SAndrew Thompson typedef void (*lacp_timer_func_t)(struct lacp_port *); 943de18008SAndrew Thompson 95b47888ceSAndrew Thompson static void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 96998971a7SAndrew Thompson static void lacp_fill_markerinfo(struct lacp_port *, 97998971a7SAndrew Thompson struct lacp_markerinfo *); 98b47888ceSAndrew Thompson 99b47888ceSAndrew Thompson static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 100b47888ceSAndrew Thompson static void lacp_suppress_distributing(struct lacp_softc *, 101b47888ceSAndrew Thompson struct lacp_aggregator *); 102b47888ceSAndrew Thompson static void lacp_transit_expire(void *); 1033de18008SAndrew Thompson static void lacp_update_portmap(struct lacp_softc *); 104b47888ceSAndrew Thompson static void lacp_select_active_aggregator(struct lacp_softc *); 105b47888ceSAndrew Thompson static uint16_t lacp_compose_key(struct lacp_port *); 106b47888ceSAndrew Thompson static int tlv_check(const void *, size_t, const struct tlvhdr *, 107b47888ceSAndrew Thompson const struct tlv_template *, boolean_t); 108b47888ceSAndrew Thompson static void lacp_tick(void *); 109b47888ceSAndrew Thompson 110b47888ceSAndrew Thompson static void lacp_fill_aggregator_id(struct lacp_aggregator *, 111b47888ceSAndrew Thompson const struct lacp_port *); 112b47888ceSAndrew Thompson static void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 113b47888ceSAndrew Thompson const struct lacp_peerinfo *); 114b47888ceSAndrew Thompson static int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 115b47888ceSAndrew Thompson const struct lacp_port *); 116b47888ceSAndrew Thompson static int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 117b47888ceSAndrew Thompson const struct lacp_peerinfo *); 118b47888ceSAndrew Thompson 119b47888ceSAndrew Thompson static struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 120b47888ceSAndrew Thompson struct lacp_port *); 121b47888ceSAndrew Thompson static void lacp_aggregator_addref(struct lacp_softc *, 122b47888ceSAndrew Thompson struct lacp_aggregator *); 123b47888ceSAndrew Thompson static void lacp_aggregator_delref(struct lacp_softc *, 124b47888ceSAndrew Thompson struct lacp_aggregator *); 125b47888ceSAndrew Thompson 126b47888ceSAndrew Thompson /* receive machine */ 127b47888ceSAndrew Thompson 1283de18008SAndrew Thompson static int lacp_pdu_input(struct lacp_port *, struct mbuf *); 1293de18008SAndrew Thompson static int lacp_marker_input(struct lacp_port *, struct mbuf *); 130b47888ceSAndrew Thompson static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 131b47888ceSAndrew Thompson static void lacp_sm_rx_timer(struct lacp_port *); 132b47888ceSAndrew Thompson static void lacp_sm_rx_set_expired(struct lacp_port *); 133b47888ceSAndrew Thompson static void lacp_sm_rx_update_ntt(struct lacp_port *, 134b47888ceSAndrew Thompson const struct lacpdu *); 135b47888ceSAndrew Thompson static void lacp_sm_rx_record_pdu(struct lacp_port *, 136b47888ceSAndrew Thompson const struct lacpdu *); 137b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected(struct lacp_port *, 138b47888ceSAndrew Thompson const struct lacpdu *); 139b47888ceSAndrew Thompson static void lacp_sm_rx_record_default(struct lacp_port *); 140b47888ceSAndrew Thompson static void lacp_sm_rx_update_default_selected(struct lacp_port *); 141b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 142b47888ceSAndrew Thompson const struct lacp_peerinfo *); 143b47888ceSAndrew Thompson 144b47888ceSAndrew Thompson /* mux machine */ 145b47888ceSAndrew Thompson 146b47888ceSAndrew Thompson static void lacp_sm_mux(struct lacp_port *); 147b47888ceSAndrew Thompson static void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 148b47888ceSAndrew Thompson static void lacp_sm_mux_timer(struct lacp_port *); 149b47888ceSAndrew Thompson 150b47888ceSAndrew Thompson /* periodic transmit machine */ 151b47888ceSAndrew Thompson 152b47888ceSAndrew Thompson static void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 153b47888ceSAndrew Thompson static void lacp_sm_ptx_tx_schedule(struct lacp_port *); 154b47888ceSAndrew Thompson static void lacp_sm_ptx_timer(struct lacp_port *); 155b47888ceSAndrew Thompson 156b47888ceSAndrew Thompson /* transmit machine */ 157b47888ceSAndrew Thompson 158b47888ceSAndrew Thompson static void lacp_sm_tx(struct lacp_port *); 159b47888ceSAndrew Thompson static void lacp_sm_assert_ntt(struct lacp_port *); 160b47888ceSAndrew Thompson 161b47888ceSAndrew Thompson static void lacp_run_timers(struct lacp_port *); 162b47888ceSAndrew Thompson static int lacp_compare_peerinfo(const struct lacp_peerinfo *, 163b47888ceSAndrew Thompson const struct lacp_peerinfo *); 164b47888ceSAndrew Thompson static int lacp_compare_systemid(const struct lacp_systemid *, 165b47888ceSAndrew Thompson const struct lacp_systemid *); 166b47888ceSAndrew Thompson static void lacp_port_enable(struct lacp_port *); 167b47888ceSAndrew Thompson static void lacp_port_disable(struct lacp_port *); 168b47888ceSAndrew Thompson static void lacp_select(struct lacp_port *); 169b47888ceSAndrew Thompson static void lacp_unselect(struct lacp_port *); 170b47888ceSAndrew Thompson static void lacp_disable_collecting(struct lacp_port *); 171b47888ceSAndrew Thompson static void lacp_enable_collecting(struct lacp_port *); 172b47888ceSAndrew Thompson static void lacp_disable_distributing(struct lacp_port *); 173b47888ceSAndrew Thompson static void lacp_enable_distributing(struct lacp_port *); 174b47888ceSAndrew Thompson static int lacp_xmit_lacpdu(struct lacp_port *); 175998971a7SAndrew Thompson static int lacp_xmit_marker(struct lacp_port *); 176b47888ceSAndrew Thompson 1775fc4c149SAndrew Thompson /* Debugging */ 1785fc4c149SAndrew Thompson 179b47888ceSAndrew Thompson static void lacp_dump_lacpdu(const struct lacpdu *); 180b47888ceSAndrew Thompson static const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 181b47888ceSAndrew Thompson size_t); 182b47888ceSAndrew Thompson static const char *lacp_format_lagid(const struct lacp_peerinfo *, 183b47888ceSAndrew Thompson const struct lacp_peerinfo *, char *, size_t); 184b47888ceSAndrew Thompson static const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 185b47888ceSAndrew Thompson char *, size_t); 186b47888ceSAndrew Thompson static const char *lacp_format_state(uint8_t, char *, size_t); 187b47888ceSAndrew Thompson static const char *lacp_format_mac(const uint8_t *, char *, size_t); 188b47888ceSAndrew Thompson static const char *lacp_format_systemid(const struct lacp_systemid *, char *, 189b47888ceSAndrew Thompson size_t); 190b47888ceSAndrew Thompson static const char *lacp_format_portid(const struct lacp_portid *, char *, 191b47888ceSAndrew Thompson size_t); 192b47888ceSAndrew Thompson static void lacp_dprintf(const struct lacp_port *, const char *, ...) 193b47888ceSAndrew Thompson __attribute__((__format__(__printf__, 2, 3))); 1945fc4c149SAndrew Thompson 195939a050aSHiroki Sato static VNET_DEFINE(int, lacp_debug); 196939a050aSHiroki Sato #define V_lacp_debug VNET(lacp_debug) 19731402c27SAdrian Chadd SYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD, 0, "ieee802.3ad"); 198939a050aSHiroki Sato SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RWTUN | CTLFLAG_VNET, 199939a050aSHiroki Sato &VNET_NAME(lacp_debug), 0, "Enable LACP debug logging (1=debug, 2=trace)"); 2005fc4c149SAndrew Thompson 201c1be893cSSteven Hartland static VNET_DEFINE(int, lacp_default_strict_mode) = 1; 202c1be893cSSteven Hartland SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, default_strict_mode, CTLFLAG_RWTUN, 203c1be893cSSteven Hartland &VNET_NAME(lacp_default_strict_mode), 0, 204c1be893cSSteven Hartland "LACP strict protocol compliance default"); 205c1be893cSSteven Hartland 206939a050aSHiroki Sato #define LACP_DPRINTF(a) if (V_lacp_debug & 0x01) { lacp_dprintf a ; } 207939a050aSHiroki Sato #define LACP_TRACE(a) if (V_lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); } 208939a050aSHiroki Sato #define LACP_TPRINTF(a) if (V_lacp_debug & 0x04) { lacp_dprintf a ; } 209b47888ceSAndrew Thompson 210b47888ceSAndrew Thompson /* 211b47888ceSAndrew Thompson * partner administration variables. 212b47888ceSAndrew Thompson * XXX should be configurable. 213b47888ceSAndrew Thompson */ 214b47888ceSAndrew Thompson 21531402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_optimistic = { 216b47888ceSAndrew Thompson .lip_systemid = { .lsi_prio = 0xffff }, 217b47888ceSAndrew Thompson .lip_portid = { .lpi_prio = 0xffff }, 218b47888ceSAndrew Thompson .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 219b47888ceSAndrew Thompson LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 22031402c27SAdrian Chadd }; 22131402c27SAdrian Chadd 22231402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_strict = { 22331402c27SAdrian Chadd .lip_systemid = { .lsi_prio = 0xffff }, 22431402c27SAdrian Chadd .lip_portid = { .lpi_prio = 0xffff }, 225b47888ceSAndrew Thompson .lip_state = 0, 226b47888ceSAndrew Thompson }; 227b47888ceSAndrew Thompson 228b47888ceSAndrew Thompson static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 229b47888ceSAndrew Thompson [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 230b47888ceSAndrew Thompson [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 231b47888ceSAndrew Thompson [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 232b47888ceSAndrew Thompson }; 233b47888ceSAndrew Thompson 234af0084c9SAndrew Thompson struct mbuf * 23518242d3bSAndrew Thompson lacp_input(struct lagg_port *lgp, struct mbuf *m) 236b47888ceSAndrew Thompson { 2373de18008SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 2383bf517e3SAndrew Thompson uint8_t subtype; 2393bf517e3SAndrew Thompson 2403bf517e3SAndrew Thompson if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 2413bf517e3SAndrew Thompson m_freem(m); 242af0084c9SAndrew Thompson return (NULL); 2433bf517e3SAndrew Thompson } 2443bf517e3SAndrew Thompson 2453bf517e3SAndrew Thompson m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 2463bf517e3SAndrew Thompson switch (subtype) { 2473bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_LACP: 2483de18008SAndrew Thompson lacp_pdu_input(lp, m); 2493de18008SAndrew Thompson return (NULL); 2503bf517e3SAndrew Thompson 2513bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_MARKER: 2523de18008SAndrew Thompson lacp_marker_input(lp, m); 253af0084c9SAndrew Thompson return (NULL); 2543bf517e3SAndrew Thompson } 2553bf517e3SAndrew Thompson 2563de18008SAndrew Thompson /* Not a subtype we are interested in */ 2573de18008SAndrew Thompson return (m); 2583bf517e3SAndrew Thompson } 2593bf517e3SAndrew Thompson 2603bf517e3SAndrew Thompson /* 2613bf517e3SAndrew Thompson * lacp_pdu_input: process lacpdu 2623bf517e3SAndrew Thompson */ 2633bf517e3SAndrew Thompson static int 2643de18008SAndrew Thompson lacp_pdu_input(struct lacp_port *lp, struct mbuf *m) 2653bf517e3SAndrew Thompson { 2663de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 267b47888ceSAndrew Thompson struct lacpdu *du; 268b47888ceSAndrew Thompson int error = 0; 269b47888ceSAndrew Thompson 270b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*du)) { 271b47888ceSAndrew Thompson goto bad; 272b47888ceSAndrew Thompson } 273b47888ceSAndrew Thompson 274b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 275b47888ceSAndrew Thompson goto bad; 276b47888ceSAndrew Thompson } 277b47888ceSAndrew Thompson 278b47888ceSAndrew Thompson if (m->m_len < sizeof(*du)) { 279b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*du)); 280b47888ceSAndrew Thompson if (m == NULL) { 281b47888ceSAndrew Thompson return (ENOMEM); 282b47888ceSAndrew Thompson } 283b47888ceSAndrew Thompson } 284b47888ceSAndrew Thompson 285b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 286b47888ceSAndrew Thompson 287b47888ceSAndrew Thompson if (memcmp(&du->ldu_eh.ether_dhost, 288b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 289b47888ceSAndrew Thompson goto bad; 290b47888ceSAndrew Thompson } 291b47888ceSAndrew Thompson 292b47888ceSAndrew Thompson /* 293b47888ceSAndrew Thompson * ignore the version for compatibility with 294b47888ceSAndrew Thompson * the future protocol revisions. 295b47888ceSAndrew Thompson */ 296b47888ceSAndrew Thompson #if 0 297b47888ceSAndrew Thompson if (du->ldu_sph.sph_version != 1) { 298b47888ceSAndrew Thompson goto bad; 299b47888ceSAndrew Thompson } 300b47888ceSAndrew Thompson #endif 301b47888ceSAndrew Thompson 302b47888ceSAndrew Thompson /* 303b47888ceSAndrew Thompson * ignore tlv types for compatibility with 304b47888ceSAndrew Thompson * the future protocol revisions. 305b47888ceSAndrew Thompson */ 306b47888ceSAndrew Thompson if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 307b47888ceSAndrew Thompson lacp_info_tlv_template, FALSE)) { 308b47888ceSAndrew Thompson goto bad; 309b47888ceSAndrew Thompson } 310b47888ceSAndrew Thompson 311939a050aSHiroki Sato if (V_lacp_debug > 0) { 3125fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu receive\n"); 313b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 3145fc4c149SAndrew Thompson } 3153de18008SAndrew Thompson 31649de4f22SAdrian Chadd if ((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_rx_test) { 31731402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping RX PDU\n")); 31831402c27SAdrian Chadd goto bad; 31931402c27SAdrian Chadd } 32031402c27SAdrian Chadd 3213de18008SAndrew Thompson LACP_LOCK(lsc); 322b47888ceSAndrew Thompson lacp_sm_rx(lp, du); 3233de18008SAndrew Thompson LACP_UNLOCK(lsc); 324b47888ceSAndrew Thompson 325b47888ceSAndrew Thompson m_freem(m); 326b47888ceSAndrew Thompson return (error); 327b47888ceSAndrew Thompson 328b47888ceSAndrew Thompson bad: 329b47888ceSAndrew Thompson m_freem(m); 330b47888ceSAndrew Thompson return (EINVAL); 331b47888ceSAndrew Thompson } 332b47888ceSAndrew Thompson 333b47888ceSAndrew Thompson static void 334b47888ceSAndrew Thompson lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 335b47888ceSAndrew Thompson { 33618242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 337ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 338b47888ceSAndrew Thompson 339b47888ceSAndrew Thompson info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 340b47888ceSAndrew Thompson memcpy(&info->lip_systemid.lsi_mac, 341ec32b37eSAndrew Thompson IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 342b47888ceSAndrew Thompson info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 343b47888ceSAndrew Thompson info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 344b47888ceSAndrew Thompson info->lip_state = lp->lp_state; 345b47888ceSAndrew Thompson } 346b47888ceSAndrew Thompson 347998971a7SAndrew Thompson static void 348998971a7SAndrew Thompson lacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 349998971a7SAndrew Thompson { 350998971a7SAndrew Thompson struct ifnet *ifp = lp->lp_ifp; 351998971a7SAndrew Thompson 352998971a7SAndrew Thompson /* Fill in the port index and system id (encoded as the MAC) */ 353998971a7SAndrew Thompson info->mi_rq_port = htons(ifp->if_index); 354998971a7SAndrew Thompson memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 355998971a7SAndrew Thompson info->mi_rq_xid = htonl(0); 356998971a7SAndrew Thompson } 357998971a7SAndrew Thompson 358b47888ceSAndrew Thompson static int 359b47888ceSAndrew Thompson lacp_xmit_lacpdu(struct lacp_port *lp) 360b47888ceSAndrew Thompson { 36118242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 362b47888ceSAndrew Thompson struct mbuf *m; 363b47888ceSAndrew Thompson struct lacpdu *du; 364b47888ceSAndrew Thompson int error; 365b47888ceSAndrew Thompson 3663de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 367b47888ceSAndrew Thompson 368eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 369b47888ceSAndrew Thompson if (m == NULL) { 370b47888ceSAndrew Thompson return (ENOMEM); 371b47888ceSAndrew Thompson } 372b47888ceSAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*du); 373b47888ceSAndrew Thompson 374b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 375b47888ceSAndrew Thompson memset(du, 0, sizeof(*du)); 376b47888ceSAndrew Thompson 377b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 378b47888ceSAndrew Thompson ETHER_ADDR_LEN); 37918242d3bSAndrew Thompson memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 380b47888ceSAndrew Thompson du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 381b47888ceSAndrew Thompson 382b47888ceSAndrew Thompson du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 383b47888ceSAndrew Thompson du->ldu_sph.sph_version = 1; 384b47888ceSAndrew Thompson 385b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 386b47888ceSAndrew Thompson du->ldu_actor = lp->lp_actor; 387b47888ceSAndrew Thompson 388b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 389b47888ceSAndrew Thompson sizeof(du->ldu_partner)); 390b47888ceSAndrew Thompson du->ldu_partner = lp->lp_partner; 391b47888ceSAndrew Thompson 392b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 393b47888ceSAndrew Thompson sizeof(du->ldu_collector)); 394b47888ceSAndrew Thompson du->ldu_collector.lci_maxdelay = 0; 395b47888ceSAndrew Thompson 396939a050aSHiroki Sato if (V_lacp_debug > 0) { 3975fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu transmit\n"); 398b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 3995fc4c149SAndrew Thompson } 400b47888ceSAndrew Thompson 401b47888ceSAndrew Thompson m->m_flags |= M_MCAST; 402b47888ceSAndrew Thompson 403b47888ceSAndrew Thompson /* 404b47888ceSAndrew Thompson * XXX should use higher priority queue. 405b47888ceSAndrew Thompson * otherwise network congestion can break aggregation. 406b47888ceSAndrew Thompson */ 407b47888ceSAndrew Thompson 40818242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 409b47888ceSAndrew Thompson return (error); 410b47888ceSAndrew Thompson } 411b47888ceSAndrew Thompson 412998971a7SAndrew Thompson static int 413998971a7SAndrew Thompson lacp_xmit_marker(struct lacp_port *lp) 414998971a7SAndrew Thompson { 415998971a7SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 416998971a7SAndrew Thompson struct mbuf *m; 417998971a7SAndrew Thompson struct markerdu *mdu; 418998971a7SAndrew Thompson int error; 419998971a7SAndrew Thompson 4203de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 421998971a7SAndrew Thompson 422eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 423998971a7SAndrew Thompson if (m == NULL) { 424998971a7SAndrew Thompson return (ENOMEM); 425998971a7SAndrew Thompson } 426998971a7SAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*mdu); 427998971a7SAndrew Thompson 428998971a7SAndrew Thompson mdu = mtod(m, struct markerdu *); 429998971a7SAndrew Thompson memset(mdu, 0, sizeof(*mdu)); 430998971a7SAndrew Thompson 431998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 432998971a7SAndrew Thompson ETHER_ADDR_LEN); 433998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 434998971a7SAndrew Thompson mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 435998971a7SAndrew Thompson 436998971a7SAndrew Thompson mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 437998971a7SAndrew Thompson mdu->mdu_sph.sph_version = 1; 438998971a7SAndrew Thompson 439998971a7SAndrew Thompson /* Bump the transaction id and copy over the marker info */ 440998971a7SAndrew Thompson lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 441998971a7SAndrew Thompson TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 442998971a7SAndrew Thompson mdu->mdu_info = lp->lp_marker; 443998971a7SAndrew Thompson 444998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 445998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 446998971a7SAndrew Thompson ntohl(mdu->mdu_info.mi_rq_xid))); 447998971a7SAndrew Thompson 448998971a7SAndrew Thompson m->m_flags |= M_MCAST; 449998971a7SAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 450998971a7SAndrew Thompson return (error); 451998971a7SAndrew Thompson } 4523de18008SAndrew Thompson 453b47888ceSAndrew Thompson void 45418242d3bSAndrew Thompson lacp_linkstate(struct lagg_port *lgp) 455b47888ceSAndrew Thompson { 45618242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 4573de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 45818242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 459b47888ceSAndrew Thompson struct ifmediareq ifmr; 460b47888ceSAndrew Thompson int error = 0; 461b47888ceSAndrew Thompson u_int media; 462b47888ceSAndrew Thompson uint8_t old_state; 463b47888ceSAndrew Thompson uint16_t old_key; 464b47888ceSAndrew Thompson 465b47888ceSAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr)); 466b47888ceSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 467b47888ceSAndrew Thompson if (error != 0) 468b47888ceSAndrew Thompson return; 469b47888ceSAndrew Thompson 4703de18008SAndrew Thompson LACP_LOCK(lsc); 471b47888ceSAndrew Thompson media = ifmr.ifm_active; 472e3163ef6SAndrew Thompson LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 473e3163ef6SAndrew Thompson "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 474e3163ef6SAndrew Thompson (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 475b47888ceSAndrew Thompson old_state = lp->lp_state; 476b47888ceSAndrew Thompson old_key = lp->lp_key; 477b47888ceSAndrew Thompson 478b47888ceSAndrew Thompson lp->lp_media = media; 479e3163ef6SAndrew Thompson /* 480e3163ef6SAndrew Thompson * If the port is not an active full duplex Ethernet link then it can 481e3163ef6SAndrew Thompson * not be aggregated. 482e3163ef6SAndrew Thompson */ 483e3163ef6SAndrew Thompson if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 484e3163ef6SAndrew Thompson ifp->if_link_state != LINK_STATE_UP) { 485b47888ceSAndrew Thompson lacp_port_disable(lp); 486b47888ceSAndrew Thompson } else { 487b47888ceSAndrew Thompson lacp_port_enable(lp); 488b47888ceSAndrew Thompson } 489b47888ceSAndrew Thompson lp->lp_key = lacp_compose_key(lp); 490b47888ceSAndrew Thompson 491b47888ceSAndrew Thompson if (old_state != lp->lp_state || old_key != lp->lp_key) { 492b47888ceSAndrew Thompson LACP_DPRINTF((lp, "-> UNSELECTED\n")); 493b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 494b47888ceSAndrew Thompson } 4953de18008SAndrew Thompson LACP_UNLOCK(lsc); 496b47888ceSAndrew Thompson } 497b47888ceSAndrew Thompson 498b47888ceSAndrew Thompson static void 499b47888ceSAndrew Thompson lacp_tick(void *arg) 500b47888ceSAndrew Thompson { 501b47888ceSAndrew Thompson struct lacp_softc *lsc = arg; 502b47888ceSAndrew Thompson struct lacp_port *lp; 503b47888ceSAndrew Thompson 504b47888ceSAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 505b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 506b47888ceSAndrew Thompson continue; 507b47888ceSAndrew Thompson 508939a050aSHiroki Sato CURVNET_SET(lp->lp_ifp->if_vnet); 509b47888ceSAndrew Thompson lacp_run_timers(lp); 510b47888ceSAndrew Thompson 511b47888ceSAndrew Thompson lacp_select(lp); 512b47888ceSAndrew Thompson lacp_sm_mux(lp); 513b47888ceSAndrew Thompson lacp_sm_tx(lp); 514b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(lp); 515939a050aSHiroki Sato CURVNET_RESTORE(); 516b47888ceSAndrew Thompson } 517b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 518b47888ceSAndrew Thompson } 519b47888ceSAndrew Thompson 520b47888ceSAndrew Thompson int 52118242d3bSAndrew Thompson lacp_port_create(struct lagg_port *lgp) 522b47888ceSAndrew Thompson { 523ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 524ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 525b47888ceSAndrew Thompson struct lacp_port *lp; 52618242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 527b47888ceSAndrew Thompson struct sockaddr_dl sdl; 528b47888ceSAndrew Thompson struct ifmultiaddr *rifma = NULL; 529b47888ceSAndrew Thompson int error; 530b47888ceSAndrew Thompson 53195fbe4d0SAlexander V. Chernikov link_init_sdl(ifp, (struct sockaddr *)&sdl, IFT_ETHER); 532b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 533b47888ceSAndrew Thompson 534b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 535b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 536b47888ceSAndrew Thompson error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 537b47888ceSAndrew Thompson if (error) { 538eade13f9SGleb Smirnoff printf("%s: ADDMULTI failed on %s\n", __func__, 539eade13f9SGleb Smirnoff lgp->lp_ifp->if_xname); 540b47888ceSAndrew Thompson return (error); 541b47888ceSAndrew Thompson } 542b47888ceSAndrew Thompson 543b47888ceSAndrew Thompson lp = malloc(sizeof(struct lacp_port), 544b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 545b47888ceSAndrew Thompson if (lp == NULL) 546b47888ceSAndrew Thompson return (ENOMEM); 547b47888ceSAndrew Thompson 5483de18008SAndrew Thompson LACP_LOCK(lsc); 5496900d0d3SGleb Smirnoff lgp->lp_psc = lp; 550b47888ceSAndrew Thompson lp->lp_ifp = ifp; 55118242d3bSAndrew Thompson lp->lp_lagg = lgp; 552b47888ceSAndrew Thompson lp->lp_lsc = lsc; 553d74fd345SAndrew Thompson lp->lp_ifma = rifma; 554b47888ceSAndrew Thompson 555b47888ceSAndrew Thompson LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 556b47888ceSAndrew Thompson 557b47888ceSAndrew Thompson lacp_fill_actorinfo(lp, &lp->lp_actor); 558998971a7SAndrew Thompson lacp_fill_markerinfo(lp, &lp->lp_marker); 559d592868eSRavi Pokala lp->lp_state = LACP_STATE_ACTIVITY; 560b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 561b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 5623de18008SAndrew Thompson LACP_UNLOCK(lsc); 5633de18008SAndrew Thompson lacp_linkstate(lgp); 564b47888ceSAndrew Thompson 565b47888ceSAndrew Thompson return (0); 566b47888ceSAndrew Thompson } 567b47888ceSAndrew Thompson 568b47888ceSAndrew Thompson void 56918242d3bSAndrew Thompson lacp_port_destroy(struct lagg_port *lgp) 570b47888ceSAndrew Thompson { 57118242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 5723de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 573d74fd345SAndrew Thompson int i; 574b47888ceSAndrew Thompson 5753de18008SAndrew Thompson LACP_LOCK(lsc); 576b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 577b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, i); 578b47888ceSAndrew Thompson } 579b47888ceSAndrew Thompson 580b47888ceSAndrew Thompson lacp_disable_collecting(lp); 581b47888ceSAndrew Thompson lacp_disable_distributing(lp); 582b47888ceSAndrew Thompson lacp_unselect(lp); 583b47888ceSAndrew Thompson 5846d478167SHiroki Sato LIST_REMOVE(lp, lp_next); 5856d478167SHiroki Sato LACP_UNLOCK(lsc); 5866d478167SHiroki Sato 587108fe96aSAndrew Thompson /* The address may have already been removed by if_purgemaddrs() */ 588108fe96aSAndrew Thompson if (!lgp->lp_detaching) 589d74fd345SAndrew Thompson if_delmulti_ifma(lp->lp_ifma); 590b47888ceSAndrew Thompson 591b47888ceSAndrew Thompson free(lp, M_DEVBUF); 592b47888ceSAndrew Thompson } 593b47888ceSAndrew Thompson 594b3d37ca5SAndrew Thompson void 5956900d0d3SGleb Smirnoff lacp_req(struct lagg_softc *sc, void *data) 596b3d37ca5SAndrew Thompson { 597b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 598b3d37ca5SAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 599f544a748SAlan Somers struct lacp_aggregator *la; 600b3d37ca5SAndrew Thompson 601b3d37ca5SAndrew Thompson bzero(req, sizeof(struct lacp_opreq)); 602f544a748SAlan Somers 603f544a748SAlan Somers /* 604f544a748SAlan Somers * If the LACP softc is NULL, return with the opreq structure full of 605f544a748SAlan Somers * zeros. It is normal for the softc to be NULL while the lagg is 606f544a748SAlan Somers * being destroyed. 607f544a748SAlan Somers */ 608f544a748SAlan Somers if (NULL == lsc) 609f544a748SAlan Somers return; 610f544a748SAlan Somers 611f544a748SAlan Somers la = lsc->lsc_active_aggregator; 612f544a748SAlan Somers LACP_LOCK(lsc); 613b3d37ca5SAndrew Thompson if (la != NULL) { 614b3d37ca5SAndrew Thompson req->actor_prio = ntohs(la->la_actor.lip_systemid.lsi_prio); 615b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &la->la_actor.lip_systemid.lsi_mac, 616b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 617b3d37ca5SAndrew Thompson req->actor_key = ntohs(la->la_actor.lip_key); 618b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(la->la_actor.lip_portid.lpi_prio); 619b3d37ca5SAndrew Thompson req->actor_portno = ntohs(la->la_actor.lip_portid.lpi_portno); 620b3d37ca5SAndrew Thompson req->actor_state = la->la_actor.lip_state; 621b3d37ca5SAndrew Thompson 622b3d37ca5SAndrew Thompson req->partner_prio = ntohs(la->la_partner.lip_systemid.lsi_prio); 623b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &la->la_partner.lip_systemid.lsi_mac, 624b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 625b3d37ca5SAndrew Thompson req->partner_key = ntohs(la->la_partner.lip_key); 626b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(la->la_partner.lip_portid.lpi_prio); 627b3d37ca5SAndrew Thompson req->partner_portno = ntohs(la->la_partner.lip_portid.lpi_portno); 628b3d37ca5SAndrew Thompson req->partner_state = la->la_partner.lip_state; 629b3d37ca5SAndrew Thompson } 6303de18008SAndrew Thompson LACP_UNLOCK(lsc); 631b3d37ca5SAndrew Thompson } 632b3d37ca5SAndrew Thompson 633b3d37ca5SAndrew Thompson void 6346900d0d3SGleb Smirnoff lacp_portreq(struct lagg_port *lgp, void *data) 635b3d37ca5SAndrew Thompson { 636b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 637b3d37ca5SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 6383de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 639b3d37ca5SAndrew Thompson 6403de18008SAndrew Thompson LACP_LOCK(lsc); 641b3d37ca5SAndrew Thompson req->actor_prio = ntohs(lp->lp_actor.lip_systemid.lsi_prio); 642b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &lp->lp_actor.lip_systemid.lsi_mac, 643b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 644b3d37ca5SAndrew Thompson req->actor_key = ntohs(lp->lp_actor.lip_key); 645b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(lp->lp_actor.lip_portid.lpi_prio); 646b3d37ca5SAndrew Thompson req->actor_portno = ntohs(lp->lp_actor.lip_portid.lpi_portno); 647b3d37ca5SAndrew Thompson req->actor_state = lp->lp_actor.lip_state; 648b3d37ca5SAndrew Thompson 649b3d37ca5SAndrew Thompson req->partner_prio = ntohs(lp->lp_partner.lip_systemid.lsi_prio); 650b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &lp->lp_partner.lip_systemid.lsi_mac, 651b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 652b3d37ca5SAndrew Thompson req->partner_key = ntohs(lp->lp_partner.lip_key); 653b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(lp->lp_partner.lip_portid.lpi_prio); 654b3d37ca5SAndrew Thompson req->partner_portno = ntohs(lp->lp_partner.lip_portid.lpi_portno); 655b3d37ca5SAndrew Thompson req->partner_state = lp->lp_partner.lip_state; 6563de18008SAndrew Thompson LACP_UNLOCK(lsc); 657b3d37ca5SAndrew Thompson } 658b3d37ca5SAndrew Thompson 659b47888ceSAndrew Thompson static void 660b47888ceSAndrew Thompson lacp_disable_collecting(struct lacp_port *lp) 661b47888ceSAndrew Thompson { 662b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting disabled\n")); 663b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_COLLECTING; 664b47888ceSAndrew Thompson } 665b47888ceSAndrew Thompson 666b47888ceSAndrew Thompson static void 667b47888ceSAndrew Thompson lacp_enable_collecting(struct lacp_port *lp) 668b47888ceSAndrew Thompson { 669b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting enabled\n")); 670b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_COLLECTING; 671b47888ceSAndrew Thompson } 672b47888ceSAndrew Thompson 673b47888ceSAndrew Thompson static void 674b47888ceSAndrew Thompson lacp_disable_distributing(struct lacp_port *lp) 675b47888ceSAndrew Thompson { 676b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 677b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 67831402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 679b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 680b47888ceSAndrew Thompson 6813de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 682b47888ceSAndrew Thompson 683b47888ceSAndrew Thompson if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 684b47888ceSAndrew Thompson return; 685b47888ceSAndrew Thompson } 686b47888ceSAndrew Thompson 687b47888ceSAndrew Thompson KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 688b47888ceSAndrew Thompson KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 689b47888ceSAndrew Thompson KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 690b47888ceSAndrew Thompson 691b47888ceSAndrew Thompson LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 692b47888ceSAndrew Thompson "nports %d -> %d\n", 693b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 694b47888ceSAndrew Thompson la->la_nports, la->la_nports - 1)); 695b47888ceSAndrew Thompson 696b47888ceSAndrew Thompson TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 697b47888ceSAndrew Thompson la->la_nports--; 69831402c27SAdrian Chadd sc->sc_active = la->la_nports; 699b47888ceSAndrew Thompson 7003de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 701b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 7023de18008SAndrew Thompson lacp_select_active_aggregator(lsc); 7033de18008SAndrew Thompson /* regenerate the port map, the active aggregator has changed */ 7043de18008SAndrew Thompson lacp_update_portmap(lsc); 7053de18008SAndrew Thompson } 706b47888ceSAndrew Thompson 707b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 708b47888ceSAndrew Thompson } 709b47888ceSAndrew Thompson 710b47888ceSAndrew Thompson static void 711b47888ceSAndrew Thompson lacp_enable_distributing(struct lacp_port *lp) 712b47888ceSAndrew Thompson { 713b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 714b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 71531402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 716b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 717b47888ceSAndrew Thompson 7183de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 719b47888ceSAndrew Thompson 720b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 721b47888ceSAndrew Thompson return; 722b47888ceSAndrew Thompson } 723b47888ceSAndrew Thompson 724b47888ceSAndrew Thompson LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 725b47888ceSAndrew Thompson "nports %d -> %d\n", 726b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 727b47888ceSAndrew Thompson la->la_nports, la->la_nports + 1)); 728b47888ceSAndrew Thompson 729b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 730b47888ceSAndrew Thompson TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 731b47888ceSAndrew Thompson la->la_nports++; 73231402c27SAdrian Chadd sc->sc_active = la->la_nports; 733b47888ceSAndrew Thompson 734b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DISTRIBUTING; 735b47888ceSAndrew Thompson 7363de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 7373de18008SAndrew Thompson lacp_suppress_distributing(lsc, la); 7383de18008SAndrew Thompson lacp_update_portmap(lsc); 7393de18008SAndrew Thompson } else 7403de18008SAndrew Thompson /* try to become the active aggregator */ 741b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 742b47888ceSAndrew Thompson } 743b47888ceSAndrew Thompson 744b47888ceSAndrew Thompson static void 745b47888ceSAndrew Thompson lacp_transit_expire(void *vp) 746b47888ceSAndrew Thompson { 747b47888ceSAndrew Thompson struct lacp_softc *lsc = vp; 748b47888ceSAndrew Thompson 7493de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 7503de18008SAndrew Thompson 7516d478167SHiroki Sato CURVNET_SET(lsc->lsc_softc->sc_ifp->if_vnet); 7525fc4c149SAndrew Thompson LACP_TRACE(NULL); 7536d478167SHiroki Sato CURVNET_RESTORE(); 7545fc4c149SAndrew Thompson 755b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 756b47888ceSAndrew Thompson } 757b47888ceSAndrew Thompson 75809c7577eSGleb Smirnoff void 759ec32b37eSAndrew Thompson lacp_attach(struct lagg_softc *sc) 760b47888ceSAndrew Thompson { 761b47888ceSAndrew Thompson struct lacp_softc *lsc; 762b47888ceSAndrew Thompson 76309c7577eSGleb Smirnoff lsc = malloc(sizeof(struct lacp_softc), M_DEVBUF, M_WAITOK | M_ZERO); 764b47888ceSAndrew Thompson 7656900d0d3SGleb Smirnoff sc->sc_psc = lsc; 766ec32b37eSAndrew Thompson lsc->lsc_softc = sc; 767b47888ceSAndrew Thompson 768b7ba031fSHans Petter Selasky lsc->lsc_hashkey = m_ether_tcpip_hash_init(); 769b47888ceSAndrew Thompson lsc->lsc_active_aggregator = NULL; 770c1be893cSSteven Hartland lsc->lsc_strict_mode = VNET(lacp_default_strict_mode); 7713de18008SAndrew Thompson LACP_LOCK_INIT(lsc); 772b47888ceSAndrew Thompson TAILQ_INIT(&lsc->lsc_aggregators); 773b47888ceSAndrew Thompson LIST_INIT(&lsc->lsc_ports); 774b47888ceSAndrew Thompson 7753de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0); 7763de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0); 777b47888ceSAndrew Thompson 77818242d3bSAndrew Thompson /* if the lagg is already up then do the same */ 779ec32b37eSAndrew Thompson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 780ec32b37eSAndrew Thompson lacp_init(sc); 781b47888ceSAndrew Thompson } 782b47888ceSAndrew Thompson 783b1bbc5b3SGleb Smirnoff void 78409c7577eSGleb Smirnoff lacp_detach(void *psc) 785b47888ceSAndrew Thompson { 78609c7577eSGleb Smirnoff struct lacp_softc *lsc = (struct lacp_softc *)psc; 787b47888ceSAndrew Thompson 788b47888ceSAndrew Thompson KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 789b47888ceSAndrew Thompson ("aggregators still active")); 790b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator == NULL, 791b47888ceSAndrew Thompson ("aggregator still attached")); 792b47888ceSAndrew Thompson 793b47888ceSAndrew Thompson callout_drain(&lsc->lsc_transit_callout); 794b47888ceSAndrew Thompson callout_drain(&lsc->lsc_callout); 795b47888ceSAndrew Thompson 7963de18008SAndrew Thompson LACP_LOCK_DESTROY(lsc); 797b47888ceSAndrew Thompson free(lsc, M_DEVBUF); 798b47888ceSAndrew Thompson } 799b47888ceSAndrew Thompson 800b47888ceSAndrew Thompson void 801ec32b37eSAndrew Thompson lacp_init(struct lagg_softc *sc) 802b47888ceSAndrew Thompson { 803ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 804b47888ceSAndrew Thompson 8053de18008SAndrew Thompson LACP_LOCK(lsc); 806b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 8073de18008SAndrew Thompson LACP_UNLOCK(lsc); 808b47888ceSAndrew Thompson } 809b47888ceSAndrew Thompson 810b47888ceSAndrew Thompson void 811ec32b37eSAndrew Thompson lacp_stop(struct lagg_softc *sc) 812b47888ceSAndrew Thompson { 813ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 814b47888ceSAndrew Thompson 8153de18008SAndrew Thompson LACP_LOCK(lsc); 816b47888ceSAndrew Thompson callout_stop(&lsc->lsc_transit_callout); 817b47888ceSAndrew Thompson callout_stop(&lsc->lsc_callout); 8183de18008SAndrew Thompson LACP_UNLOCK(lsc); 819b47888ceSAndrew Thompson } 820b47888ceSAndrew Thompson 82118242d3bSAndrew Thompson struct lagg_port * 822ec32b37eSAndrew Thompson lacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m) 823b47888ceSAndrew Thompson { 824ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 8253de18008SAndrew Thompson struct lacp_portmap *pm; 826b47888ceSAndrew Thompson struct lacp_port *lp; 827b47888ceSAndrew Thompson uint32_t hash; 828b47888ceSAndrew Thompson 829b47888ceSAndrew Thompson if (__predict_false(lsc->lsc_suppress_distributing)) { 830b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 831b47888ceSAndrew Thompson return (NULL); 832b47888ceSAndrew Thompson } 833b47888ceSAndrew Thompson 8343de18008SAndrew Thompson pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 8353de18008SAndrew Thompson if (pm->pm_count == 0) { 836b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 837b47888ceSAndrew Thompson return (NULL); 838b47888ceSAndrew Thompson } 839b47888ceSAndrew Thompson 840c2529042SHans Petter Selasky if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && 841c2529042SHans Petter Selasky M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) 8421a8959daSScott Long hash = m->m_pkthdr.flowid >> sc->flowid_shift; 8435c6026e9SAndrew Thompson else 844b7ba031fSHans Petter Selasky hash = m_ether_tcpip_hash(sc->sc_flags, m, lsc->lsc_hashkey); 8453de18008SAndrew Thompson hash %= pm->pm_count; 8463de18008SAndrew Thompson lp = pm->pm_map[hash]; 847b47888ceSAndrew Thompson 848b47888ceSAndrew Thompson KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 849b47888ceSAndrew Thompson ("aggregated port is not distributing")); 850b47888ceSAndrew Thompson 85118242d3bSAndrew Thompson return (lp->lp_lagg); 852b47888ceSAndrew Thompson } 853f3e7afe2SHans Petter Selasky 854f3e7afe2SHans Petter Selasky #ifdef RATELIMIT 855f3e7afe2SHans Petter Selasky struct lagg_port * 856f3e7afe2SHans Petter Selasky lacp_select_tx_port_by_hash(struct lagg_softc *sc, uint32_t flowid) 857f3e7afe2SHans Petter Selasky { 858f3e7afe2SHans Petter Selasky struct lacp_softc *lsc = LACP_SOFTC(sc); 859f3e7afe2SHans Petter Selasky struct lacp_portmap *pm; 860f3e7afe2SHans Petter Selasky struct lacp_port *lp; 861f3e7afe2SHans Petter Selasky uint32_t hash; 862f3e7afe2SHans Petter Selasky 863f3e7afe2SHans Petter Selasky if (__predict_false(lsc->lsc_suppress_distributing)) { 864f3e7afe2SHans Petter Selasky LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 865f3e7afe2SHans Petter Selasky return (NULL); 866f3e7afe2SHans Petter Selasky } 867f3e7afe2SHans Petter Selasky 868f3e7afe2SHans Petter Selasky pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 869f3e7afe2SHans Petter Selasky if (pm->pm_count == 0) { 870f3e7afe2SHans Petter Selasky LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 871f3e7afe2SHans Petter Selasky return (NULL); 872f3e7afe2SHans Petter Selasky } 873f3e7afe2SHans Petter Selasky 874f3e7afe2SHans Petter Selasky hash = flowid >> sc->flowid_shift; 875f3e7afe2SHans Petter Selasky hash %= pm->pm_count; 876f3e7afe2SHans Petter Selasky lp = pm->pm_map[hash]; 877f3e7afe2SHans Petter Selasky 878f3e7afe2SHans Petter Selasky return (lp->lp_lagg); 879f3e7afe2SHans Petter Selasky } 880f3e7afe2SHans Petter Selasky #endif 881f3e7afe2SHans Petter Selasky 882b47888ceSAndrew Thompson /* 883b47888ceSAndrew Thompson * lacp_suppress_distributing: drop transmit packets for a while 884b47888ceSAndrew Thompson * to preserve packet ordering. 885b47888ceSAndrew Thompson */ 886b47888ceSAndrew Thompson 887b47888ceSAndrew Thompson static void 888b47888ceSAndrew Thompson lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 889b47888ceSAndrew Thompson { 890998971a7SAndrew Thompson struct lacp_port *lp; 891998971a7SAndrew Thompson 892b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 893b47888ceSAndrew Thompson return; 894b47888ceSAndrew Thompson } 895b47888ceSAndrew Thompson 8965fc4c149SAndrew Thompson LACP_TRACE(NULL); 8975fc4c149SAndrew Thompson 898b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = TRUE; 899998971a7SAndrew Thompson 900998971a7SAndrew Thompson /* send a marker frame down each port to verify the queues are empty */ 901998971a7SAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 902998971a7SAndrew Thompson lp->lp_flags |= LACP_PORT_MARK; 903998971a7SAndrew Thompson lacp_xmit_marker(lp); 904998971a7SAndrew Thompson } 905998971a7SAndrew Thompson 906998971a7SAndrew Thompson /* set a timeout for the marker frames */ 907b47888ceSAndrew Thompson callout_reset(&lsc->lsc_transit_callout, 908b47888ceSAndrew Thompson LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 909b47888ceSAndrew Thompson } 910b47888ceSAndrew Thompson 911b47888ceSAndrew Thompson static int 912b47888ceSAndrew Thompson lacp_compare_peerinfo(const struct lacp_peerinfo *a, 913b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 914b47888ceSAndrew Thompson { 915b47888ceSAndrew Thompson return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 916b47888ceSAndrew Thompson } 917b47888ceSAndrew Thompson 918b47888ceSAndrew Thompson static int 919b47888ceSAndrew Thompson lacp_compare_systemid(const struct lacp_systemid *a, 920b47888ceSAndrew Thompson const struct lacp_systemid *b) 921b47888ceSAndrew Thompson { 922b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 923b47888ceSAndrew Thompson } 924b47888ceSAndrew Thompson 925b47888ceSAndrew Thompson #if 0 /* unused */ 926b47888ceSAndrew Thompson static int 927b47888ceSAndrew Thompson lacp_compare_portid(const struct lacp_portid *a, 928b47888ceSAndrew Thompson const struct lacp_portid *b) 929b47888ceSAndrew Thompson { 930b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 931b47888ceSAndrew Thompson } 932b47888ceSAndrew Thompson #endif 933b47888ceSAndrew Thompson 934b47888ceSAndrew Thompson static uint64_t 935b47888ceSAndrew Thompson lacp_aggregator_bandwidth(struct lacp_aggregator *la) 936b47888ceSAndrew Thompson { 937b47888ceSAndrew Thompson struct lacp_port *lp; 938b47888ceSAndrew Thompson uint64_t speed; 939b47888ceSAndrew Thompson 940b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 941b47888ceSAndrew Thompson if (lp == NULL) { 942b47888ceSAndrew Thompson return (0); 943b47888ceSAndrew Thompson } 944b47888ceSAndrew Thompson 945b47888ceSAndrew Thompson speed = ifmedia_baudrate(lp->lp_media); 946b47888ceSAndrew Thompson speed *= la->la_nports; 947b47888ceSAndrew Thompson if (speed == 0) { 948b47888ceSAndrew Thompson LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 949b47888ceSAndrew Thompson lp->lp_media, la->la_nports)); 950b47888ceSAndrew Thompson } 951b47888ceSAndrew Thompson 952b47888ceSAndrew Thompson return (speed); 953b47888ceSAndrew Thompson } 954b47888ceSAndrew Thompson 955b47888ceSAndrew Thompson /* 956b47888ceSAndrew Thompson * lacp_select_active_aggregator: select an aggregator to be used to transmit 95718242d3bSAndrew Thompson * packets from lagg(4) interface. 958b47888ceSAndrew Thompson */ 959b47888ceSAndrew Thompson 960b47888ceSAndrew Thompson static void 961b47888ceSAndrew Thompson lacp_select_active_aggregator(struct lacp_softc *lsc) 962b47888ceSAndrew Thompson { 963b47888ceSAndrew Thompson struct lacp_aggregator *la; 964b47888ceSAndrew Thompson struct lacp_aggregator *best_la = NULL; 965b47888ceSAndrew Thompson uint64_t best_speed = 0; 966b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 967b47888ceSAndrew Thompson 9685fc4c149SAndrew Thompson LACP_TRACE(NULL); 969b47888ceSAndrew Thompson 970b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 971b47888ceSAndrew Thompson uint64_t speed; 972b47888ceSAndrew Thompson 973b47888ceSAndrew Thompson if (la->la_nports == 0) { 974b47888ceSAndrew Thompson continue; 975b47888ceSAndrew Thompson } 976b47888ceSAndrew Thompson 977b47888ceSAndrew Thompson speed = lacp_aggregator_bandwidth(la); 978b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 979b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 980b47888ceSAndrew Thompson speed, la->la_nports)); 981fe45e65fSAndrew Thompson 9826900d0d3SGleb Smirnoff /* 9836900d0d3SGleb Smirnoff * This aggregator is chosen if the partner has a better 9846900d0d3SGleb Smirnoff * system priority or, the total aggregated speed is higher 985fe45e65fSAndrew Thompson * or, it is already the chosen aggregator 986fe45e65fSAndrew Thompson */ 987fe45e65fSAndrew Thompson if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 988fe45e65fSAndrew Thompson LACP_SYS_PRI(best_la->la_partner)) || 989fe45e65fSAndrew Thompson speed > best_speed || 990b47888ceSAndrew Thompson (speed == best_speed && 991b47888ceSAndrew Thompson la == lsc->lsc_active_aggregator)) { 992b47888ceSAndrew Thompson best_la = la; 993b47888ceSAndrew Thompson best_speed = speed; 994b47888ceSAndrew Thompson } 995b47888ceSAndrew Thompson } 996b47888ceSAndrew Thompson 997b47888ceSAndrew Thompson KASSERT(best_la == NULL || best_la->la_nports > 0, 998b47888ceSAndrew Thompson ("invalid aggregator refcnt")); 999b47888ceSAndrew Thompson KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 1000b47888ceSAndrew Thompson ("invalid aggregator list")); 1001b47888ceSAndrew Thompson 1002b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 1003b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator changed\n")); 1004b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "old %s\n", 1005b47888ceSAndrew Thompson lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 1006b47888ceSAndrew Thompson buf, sizeof(buf)))); 1007b47888ceSAndrew Thompson } else { 1008b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator not changed\n")); 1009b47888ceSAndrew Thompson } 1010b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "new %s\n", 1011b47888ceSAndrew Thompson lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 1012b47888ceSAndrew Thompson 1013b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 1014b47888ceSAndrew Thompson lsc->lsc_active_aggregator = best_la; 10153de18008SAndrew Thompson lacp_update_portmap(lsc); 1016b47888ceSAndrew Thompson if (best_la) { 1017b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, best_la); 1018b47888ceSAndrew Thompson } 1019b47888ceSAndrew Thompson } 1020b47888ceSAndrew Thompson } 1021b47888ceSAndrew Thompson 10223de18008SAndrew Thompson /* 10233de18008SAndrew Thompson * Updated the inactive portmap array with the new list of ports and 10243de18008SAndrew Thompson * make it live. 10253de18008SAndrew Thompson */ 10263de18008SAndrew Thompson static void 10273de18008SAndrew Thompson lacp_update_portmap(struct lacp_softc *lsc) 10283de18008SAndrew Thompson { 102931402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 10303de18008SAndrew Thompson struct lacp_aggregator *la; 10313de18008SAndrew Thompson struct lacp_portmap *p; 10323de18008SAndrew Thompson struct lacp_port *lp; 103331402c27SAdrian Chadd uint64_t speed; 10343de18008SAndrew Thompson u_int newmap; 10353de18008SAndrew Thompson int i; 10363de18008SAndrew Thompson 10373de18008SAndrew Thompson newmap = lsc->lsc_activemap == 0 ? 1 : 0; 10383de18008SAndrew Thompson p = &lsc->lsc_pmap[newmap]; 10393de18008SAndrew Thompson la = lsc->lsc_active_aggregator; 104031402c27SAdrian Chadd speed = 0; 10413de18008SAndrew Thompson bzero(p, sizeof(struct lacp_portmap)); 10423de18008SAndrew Thompson 10433de18008SAndrew Thompson if (la != NULL && la->la_nports > 0) { 10443de18008SAndrew Thompson p->pm_count = la->la_nports; 10453de18008SAndrew Thompson i = 0; 10463de18008SAndrew Thompson TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q) 10473de18008SAndrew Thompson p->pm_map[i++] = lp; 10483de18008SAndrew Thompson KASSERT(i == p->pm_count, ("Invalid port count")); 104931402c27SAdrian Chadd speed = lacp_aggregator_bandwidth(la); 10503de18008SAndrew Thompson } 105131402c27SAdrian Chadd sc->sc_ifp->if_baudrate = speed; 10523de18008SAndrew Thompson 10533de18008SAndrew Thompson /* switch the active portmap over */ 10543de18008SAndrew Thompson atomic_store_rel_int(&lsc->lsc_activemap, newmap); 10553de18008SAndrew Thompson LACP_DPRINTF((NULL, "Set table %d with %d ports\n", 10563de18008SAndrew Thompson lsc->lsc_activemap, 10573de18008SAndrew Thompson lsc->lsc_pmap[lsc->lsc_activemap].pm_count)); 10583de18008SAndrew Thompson } 10593de18008SAndrew Thompson 1060b47888ceSAndrew Thompson static uint16_t 1061b47888ceSAndrew Thompson lacp_compose_key(struct lacp_port *lp) 1062b47888ceSAndrew Thompson { 106318242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 1064ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 1065b47888ceSAndrew Thompson u_int media = lp->lp_media; 1066b47888ceSAndrew Thompson uint16_t key; 1067b47888ceSAndrew Thompson 1068b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 1069b47888ceSAndrew Thompson 1070b47888ceSAndrew Thompson /* 1071b47888ceSAndrew Thompson * non-aggregatable links should have unique keys. 1072b47888ceSAndrew Thompson * 1073b47888ceSAndrew Thompson * XXX this isn't really unique as if_index is 16 bit. 1074b47888ceSAndrew Thompson */ 1075b47888ceSAndrew Thompson 1076b47888ceSAndrew Thompson /* bit 0..14: (some bits of) if_index of this port */ 1077b47888ceSAndrew Thompson key = lp->lp_ifp->if_index; 1078b47888ceSAndrew Thompson /* bit 15: 1 */ 1079b47888ceSAndrew Thompson key |= 0x8000; 1080b47888ceSAndrew Thompson } else { 1081b47888ceSAndrew Thompson u_int subtype = IFM_SUBTYPE(media); 1082b47888ceSAndrew Thompson 1083e3163ef6SAndrew Thompson KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1084e3163ef6SAndrew Thompson KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1085b47888ceSAndrew Thompson 1086d5773da8SAndrey V. Elsukov /* bit 0..4: IFM_SUBTYPE modulo speed */ 1087d5773da8SAndrey V. Elsukov switch (subtype) { 1088d5773da8SAndrey V. Elsukov case IFM_10_T: 1089d5773da8SAndrey V. Elsukov case IFM_10_2: 1090d5773da8SAndrey V. Elsukov case IFM_10_5: 1091d5773da8SAndrey V. Elsukov case IFM_10_STP: 1092d5773da8SAndrey V. Elsukov case IFM_10_FL: 1093d5773da8SAndrey V. Elsukov key = IFM_10_T; 1094d5773da8SAndrey V. Elsukov break; 1095d5773da8SAndrey V. Elsukov case IFM_100_TX: 1096d5773da8SAndrey V. Elsukov case IFM_100_FX: 1097d5773da8SAndrey V. Elsukov case IFM_100_T4: 1098d5773da8SAndrey V. Elsukov case IFM_100_VG: 1099d5773da8SAndrey V. Elsukov case IFM_100_T2: 1100eb7e25b2SEric Joyner case IFM_100_T: 1101d5773da8SAndrey V. Elsukov key = IFM_100_TX; 1102d5773da8SAndrey V. Elsukov break; 1103d5773da8SAndrey V. Elsukov case IFM_1000_SX: 1104d5773da8SAndrey V. Elsukov case IFM_1000_LX: 1105d5773da8SAndrey V. Elsukov case IFM_1000_CX: 1106d5773da8SAndrey V. Elsukov case IFM_1000_T: 1107eb7e25b2SEric Joyner case IFM_1000_KX: 1108eb7e25b2SEric Joyner case IFM_1000_SGMII: 1109eb7e25b2SEric Joyner case IFM_1000_CX_SGMII: 1110d5773da8SAndrey V. Elsukov key = IFM_1000_SX; 1111d5773da8SAndrey V. Elsukov break; 1112d5773da8SAndrey V. Elsukov case IFM_10G_LR: 1113d5773da8SAndrey V. Elsukov case IFM_10G_SR: 1114d5773da8SAndrey V. Elsukov case IFM_10G_CX4: 1115d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX: 1116d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX_LONG: 1117d5773da8SAndrey V. Elsukov case IFM_10G_LRM: 1118d5773da8SAndrey V. Elsukov case IFM_10G_T: 1119eb7e25b2SEric Joyner case IFM_10G_KX4: 1120eb7e25b2SEric Joyner case IFM_10G_KR: 1121eb7e25b2SEric Joyner case IFM_10G_CR1: 1122eb7e25b2SEric Joyner case IFM_10G_ER: 1123eb7e25b2SEric Joyner case IFM_10G_SFI: 1124d5773da8SAndrey V. Elsukov key = IFM_10G_LR; 1125d5773da8SAndrey V. Elsukov break; 1126eb7e25b2SEric Joyner case IFM_20G_KR2: 1127eb7e25b2SEric Joyner key = IFM_20G_KR2; 1128eb7e25b2SEric Joyner break; 1129eb7e25b2SEric Joyner case IFM_2500_KX: 1130eb7e25b2SEric Joyner case IFM_2500_T: 1131eb7e25b2SEric Joyner key = IFM_2500_KX; 1132eb7e25b2SEric Joyner break; 1133eb7e25b2SEric Joyner case IFM_5000_T: 1134eb7e25b2SEric Joyner key = IFM_5000_T; 1135eb7e25b2SEric Joyner break; 1136eb7e25b2SEric Joyner case IFM_50G_PCIE: 1137eb7e25b2SEric Joyner case IFM_50G_CR2: 1138eb7e25b2SEric Joyner case IFM_50G_KR2: 1139eb7e25b2SEric Joyner key = IFM_50G_PCIE; 1140eb7e25b2SEric Joyner break; 1141eb7e25b2SEric Joyner case IFM_56G_R4: 1142eb7e25b2SEric Joyner key = IFM_56G_R4; 1143eb7e25b2SEric Joyner break; 1144eb7e25b2SEric Joyner case IFM_25G_PCIE: 1145eb7e25b2SEric Joyner case IFM_25G_CR: 1146eb7e25b2SEric Joyner case IFM_25G_KR: 1147eb7e25b2SEric Joyner case IFM_25G_SR: 1148eb7e25b2SEric Joyner key = IFM_25G_PCIE; 1149eb7e25b2SEric Joyner break; 1150d5773da8SAndrey V. Elsukov case IFM_40G_CR4: 1151d5773da8SAndrey V. Elsukov case IFM_40G_SR4: 1152d5773da8SAndrey V. Elsukov case IFM_40G_LR4: 1153eb7e25b2SEric Joyner case IFM_40G_XLPPI: 1154eb7e25b2SEric Joyner case IFM_40G_KR4: 1155d5773da8SAndrey V. Elsukov key = IFM_40G_CR4; 1156d5773da8SAndrey V. Elsukov break; 1157eb7e25b2SEric Joyner case IFM_100G_CR4: 1158eb7e25b2SEric Joyner case IFM_100G_SR4: 1159eb7e25b2SEric Joyner case IFM_100G_KR4: 1160eb7e25b2SEric Joyner case IFM_100G_LR4: 1161eb7e25b2SEric Joyner key = IFM_100G_CR4; 1162eb7e25b2SEric Joyner break; 1163d5773da8SAndrey V. Elsukov default: 1164b47888ceSAndrew Thompson key = subtype; 1165eb7e25b2SEric Joyner break; 1166d5773da8SAndrey V. Elsukov } 116718242d3bSAndrew Thompson /* bit 5..14: (some bits of) if_index of lagg device */ 1168ec32b37eSAndrew Thompson key |= 0x7fe0 & ((sc->sc_ifp->if_index) << 5); 1169b47888ceSAndrew Thompson /* bit 15: 0 */ 1170b47888ceSAndrew Thompson } 1171b47888ceSAndrew Thompson return (htons(key)); 1172b47888ceSAndrew Thompson } 1173b47888ceSAndrew Thompson 1174b47888ceSAndrew Thompson static void 1175b47888ceSAndrew Thompson lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1176b47888ceSAndrew Thompson { 1177b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1178b47888ceSAndrew Thompson 1179b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1180b47888ceSAndrew Thompson __func__, 1181b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1182b47888ceSAndrew Thompson buf, sizeof(buf)), 1183b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt + 1)); 1184b47888ceSAndrew Thompson 1185b47888ceSAndrew Thompson KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1186b47888ceSAndrew Thompson la->la_refcnt++; 1187b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1188b47888ceSAndrew Thompson } 1189b47888ceSAndrew Thompson 1190b47888ceSAndrew Thompson static void 1191b47888ceSAndrew Thompson lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1192b47888ceSAndrew Thompson { 1193b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1194b47888ceSAndrew Thompson 1195b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1196b47888ceSAndrew Thompson __func__, 1197b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1198b47888ceSAndrew Thompson buf, sizeof(buf)), 1199b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt - 1)); 1200b47888ceSAndrew Thompson 1201b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1202b47888ceSAndrew Thompson la->la_refcnt--; 1203b47888ceSAndrew Thompson if (la->la_refcnt > 0) { 1204b47888ceSAndrew Thompson return; 1205b47888ceSAndrew Thompson } 1206b47888ceSAndrew Thompson 1207b47888ceSAndrew Thompson KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1208b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1209b47888ceSAndrew Thompson 1210b47888ceSAndrew Thompson TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1211b47888ceSAndrew Thompson 1212b47888ceSAndrew Thompson free(la, M_DEVBUF); 1213b47888ceSAndrew Thompson } 1214b47888ceSAndrew Thompson 1215b47888ceSAndrew Thompson /* 1216b47888ceSAndrew Thompson * lacp_aggregator_get: allocate an aggregator. 1217b47888ceSAndrew Thompson */ 1218b47888ceSAndrew Thompson 1219b47888ceSAndrew Thompson static struct lacp_aggregator * 1220b47888ceSAndrew Thompson lacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1221b47888ceSAndrew Thompson { 1222b47888ceSAndrew Thompson struct lacp_aggregator *la; 1223b47888ceSAndrew Thompson 1224b47888ceSAndrew Thompson la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1225b47888ceSAndrew Thompson if (la) { 1226b47888ceSAndrew Thompson la->la_refcnt = 1; 1227b47888ceSAndrew Thompson la->la_nports = 0; 1228b47888ceSAndrew Thompson TAILQ_INIT(&la->la_ports); 1229b47888ceSAndrew Thompson la->la_pending = 0; 1230b47888ceSAndrew Thompson TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1231b47888ceSAndrew Thompson } 1232b47888ceSAndrew Thompson 1233b47888ceSAndrew Thompson return (la); 1234b47888ceSAndrew Thompson } 1235b47888ceSAndrew Thompson 1236b47888ceSAndrew Thompson /* 1237b47888ceSAndrew Thompson * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1238b47888ceSAndrew Thompson */ 1239b47888ceSAndrew Thompson 1240b47888ceSAndrew Thompson static void 1241b47888ceSAndrew Thompson lacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1242b47888ceSAndrew Thompson { 1243b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1244b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1245b47888ceSAndrew Thompson 1246b47888ceSAndrew Thompson la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1247b47888ceSAndrew Thompson } 1248b47888ceSAndrew Thompson 1249b47888ceSAndrew Thompson static void 1250b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1251b47888ceSAndrew Thompson const struct lacp_peerinfo *lpi_port) 1252b47888ceSAndrew Thompson { 1253b47888ceSAndrew Thompson memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1254b47888ceSAndrew Thompson lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1255b47888ceSAndrew Thompson lpi_aggr->lip_key = lpi_port->lip_key; 1256b47888ceSAndrew Thompson } 1257b47888ceSAndrew Thompson 1258b47888ceSAndrew Thompson /* 1259b47888ceSAndrew Thompson * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1260b47888ceSAndrew Thompson */ 1261b47888ceSAndrew Thompson 1262b47888ceSAndrew Thompson static int 1263b47888ceSAndrew Thompson lacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1264b47888ceSAndrew Thompson const struct lacp_port *lp) 1265b47888ceSAndrew Thompson { 1266b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1267b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1268b47888ceSAndrew Thompson return (0); 1269b47888ceSAndrew Thompson } 1270b47888ceSAndrew Thompson 1271b47888ceSAndrew Thompson if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1272b47888ceSAndrew Thompson return (0); 1273b47888ceSAndrew Thompson } 1274b47888ceSAndrew Thompson 1275b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1276b47888ceSAndrew Thompson return (0); 1277b47888ceSAndrew Thompson } 1278b47888ceSAndrew Thompson 1279b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1280b47888ceSAndrew Thompson return (0); 1281b47888ceSAndrew Thompson } 1282b47888ceSAndrew Thompson 1283b47888ceSAndrew Thompson return (1); 1284b47888ceSAndrew Thompson } 1285b47888ceSAndrew Thompson 1286b47888ceSAndrew Thompson static int 1287b47888ceSAndrew Thompson lacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1288b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 1289b47888ceSAndrew Thompson { 1290b47888ceSAndrew Thompson if (memcmp(&a->lip_systemid, &b->lip_systemid, 1291b47888ceSAndrew Thompson sizeof(a->lip_systemid))) { 1292b47888ceSAndrew Thompson return (0); 1293b47888ceSAndrew Thompson } 1294b47888ceSAndrew Thompson 1295b47888ceSAndrew Thompson if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1296b47888ceSAndrew Thompson return (0); 1297b47888ceSAndrew Thompson } 1298b47888ceSAndrew Thompson 1299b47888ceSAndrew Thompson return (1); 1300b47888ceSAndrew Thompson } 1301b47888ceSAndrew Thompson 1302b47888ceSAndrew Thompson static void 1303b47888ceSAndrew Thompson lacp_port_enable(struct lacp_port *lp) 1304b47888ceSAndrew Thompson { 1305b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_AGGREGATION; 1306b47888ceSAndrew Thompson } 1307b47888ceSAndrew Thompson 1308b47888ceSAndrew Thompson static void 1309b47888ceSAndrew Thompson lacp_port_disable(struct lacp_port *lp) 1310b47888ceSAndrew Thompson { 1311b47888ceSAndrew Thompson lacp_set_mux(lp, LACP_MUX_DETACHED); 1312b47888ceSAndrew Thompson 1313b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_AGGREGATION; 1314b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1315b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1316b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1317b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1318b47888ceSAndrew Thompson } 1319b47888ceSAndrew Thompson 1320b47888ceSAndrew Thompson /* 1321b47888ceSAndrew Thompson * lacp_select: select an aggregator. create one if necessary. 1322b47888ceSAndrew Thompson */ 1323b47888ceSAndrew Thompson static void 1324b47888ceSAndrew Thompson lacp_select(struct lacp_port *lp) 1325b47888ceSAndrew Thompson { 1326b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1327b47888ceSAndrew Thompson struct lacp_aggregator *la; 1328b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1329b47888ceSAndrew Thompson 1330b47888ceSAndrew Thompson if (lp->lp_aggregator) { 1331b47888ceSAndrew Thompson return; 1332b47888ceSAndrew Thompson } 1333b47888ceSAndrew Thompson 1334b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1335b47888ceSAndrew Thompson ("timer_wait_while still active")); 1336b47888ceSAndrew Thompson 1337b47888ceSAndrew Thompson LACP_DPRINTF((lp, "port lagid=%s\n", 1338b47888ceSAndrew Thompson lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1339b47888ceSAndrew Thompson buf, sizeof(buf)))); 1340b47888ceSAndrew Thompson 1341b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1342b47888ceSAndrew Thompson if (lacp_aggregator_is_compatible(la, lp)) { 1343b47888ceSAndrew Thompson break; 1344b47888ceSAndrew Thompson } 1345b47888ceSAndrew Thompson } 1346b47888ceSAndrew Thompson 1347b47888ceSAndrew Thompson if (la == NULL) { 1348b47888ceSAndrew Thompson la = lacp_aggregator_get(lsc, lp); 1349b47888ceSAndrew Thompson if (la == NULL) { 1350b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator creation failed\n")); 1351b47888ceSAndrew Thompson 1352b47888ceSAndrew Thompson /* 1353b47888ceSAndrew Thompson * will retry on the next tick. 1354b47888ceSAndrew Thompson */ 1355b47888ceSAndrew Thompson 1356b47888ceSAndrew Thompson return; 1357b47888ceSAndrew Thompson } 1358b47888ceSAndrew Thompson lacp_fill_aggregator_id(la, lp); 1359b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator created\n")); 1360b47888ceSAndrew Thompson } else { 1361b47888ceSAndrew Thompson LACP_DPRINTF((lp, "compatible aggregator found\n")); 13623de18008SAndrew Thompson if (la->la_refcnt == LACP_MAX_PORTS) 13633de18008SAndrew Thompson return; 1364b47888ceSAndrew Thompson lacp_aggregator_addref(lsc, la); 1365b47888ceSAndrew Thompson } 1366b47888ceSAndrew Thompson 1367b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1368b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1369b47888ceSAndrew Thompson buf, sizeof(buf)))); 1370b47888ceSAndrew Thompson 1371b47888ceSAndrew Thompson lp->lp_aggregator = la; 1372b47888ceSAndrew Thompson lp->lp_selected = LACP_SELECTED; 1373b47888ceSAndrew Thompson } 1374b47888ceSAndrew Thompson 1375b47888ceSAndrew Thompson /* 1376b47888ceSAndrew Thompson * lacp_unselect: finish unselect/detach process. 1377b47888ceSAndrew Thompson */ 1378b47888ceSAndrew Thompson 1379b47888ceSAndrew Thompson static void 1380b47888ceSAndrew Thompson lacp_unselect(struct lacp_port *lp) 1381b47888ceSAndrew Thompson { 1382b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1383b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1384b47888ceSAndrew Thompson 1385b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1386b47888ceSAndrew Thompson ("timer_wait_while still active")); 1387b47888ceSAndrew Thompson 1388b47888ceSAndrew Thompson if (la == NULL) { 1389b47888ceSAndrew Thompson return; 1390b47888ceSAndrew Thompson } 1391b47888ceSAndrew Thompson 1392b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 1393b47888ceSAndrew Thompson lacp_aggregator_delref(lsc, la); 1394b47888ceSAndrew Thompson } 1395b47888ceSAndrew Thompson 1396b47888ceSAndrew Thompson /* mux machine */ 1397b47888ceSAndrew Thompson 1398b47888ceSAndrew Thompson static void 1399b47888ceSAndrew Thompson lacp_sm_mux(struct lacp_port *lp) 1400b47888ceSAndrew Thompson { 140131402c27SAdrian Chadd struct lagg_port *lgp = lp->lp_lagg; 140231402c27SAdrian Chadd struct lagg_softc *sc = lgp->lp_softc; 1403b47888ceSAndrew Thompson enum lacp_mux_state new_state; 1404b47888ceSAndrew Thompson boolean_t p_sync = 1405b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1406b47888ceSAndrew Thompson boolean_t p_collecting = 1407b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1408b47888ceSAndrew Thompson enum lacp_selected selected = lp->lp_selected; 1409b47888ceSAndrew Thompson struct lacp_aggregator *la; 1410b47888ceSAndrew Thompson 1411939a050aSHiroki Sato if (V_lacp_debug > 1) 141231402c27SAdrian Chadd lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, " 141331402c27SAdrian Chadd "p_sync= 0x%x, p_collecting= 0x%x\n", __func__, 141431402c27SAdrian Chadd lp->lp_mux_state, selected, p_sync, p_collecting); 1415b47888ceSAndrew Thompson 1416b47888ceSAndrew Thompson re_eval: 1417b47888ceSAndrew Thompson la = lp->lp_aggregator; 1418b47888ceSAndrew Thompson KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1419b47888ceSAndrew Thompson ("MUX not detached")); 1420b47888ceSAndrew Thompson new_state = lp->lp_mux_state; 1421b47888ceSAndrew Thompson switch (lp->lp_mux_state) { 1422b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1423b47888ceSAndrew Thompson if (selected != LACP_UNSELECTED) { 1424b47888ceSAndrew Thompson new_state = LACP_MUX_WAITING; 1425b47888ceSAndrew Thompson } 1426b47888ceSAndrew Thompson break; 1427b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1428b47888ceSAndrew Thompson KASSERT(la->la_pending > 0 || 1429b47888ceSAndrew Thompson !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1430b47888ceSAndrew Thompson ("timer_wait_while still active")); 1431b47888ceSAndrew Thompson if (selected == LACP_SELECTED && la->la_pending == 0) { 1432b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1433b47888ceSAndrew Thompson } else if (selected == LACP_UNSELECTED) { 1434b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1435b47888ceSAndrew Thompson } 1436b47888ceSAndrew Thompson break; 1437b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1438b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync) { 1439b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1440b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED) { 1441b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1442b47888ceSAndrew Thompson } 1443b47888ceSAndrew Thompson break; 1444b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1445b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync && p_collecting) { 1446b47888ceSAndrew Thompson new_state = LACP_MUX_DISTRIBUTING; 1447b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED || !p_sync) { 1448b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1449b47888ceSAndrew Thompson } 1450b47888ceSAndrew Thompson break; 1451b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1452b47888ceSAndrew Thompson if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1453b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1454387e754aSAdrian Chadd lacp_dprintf(lp, "Interface stopped DISTRIBUTING, possible flapping\n"); 145531402c27SAdrian Chadd sc->sc_flapping++; 1456b47888ceSAndrew Thompson } 1457b47888ceSAndrew Thompson break; 1458b47888ceSAndrew Thompson default: 1459b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1460b47888ceSAndrew Thompson } 1461b47888ceSAndrew Thompson 1462b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1463b47888ceSAndrew Thompson return; 1464b47888ceSAndrew Thompson } 1465b47888ceSAndrew Thompson 1466b47888ceSAndrew Thompson lacp_set_mux(lp, new_state); 1467b47888ceSAndrew Thompson goto re_eval; 1468b47888ceSAndrew Thompson } 1469b47888ceSAndrew Thompson 1470b47888ceSAndrew Thompson static void 1471b47888ceSAndrew Thompson lacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1472b47888ceSAndrew Thompson { 1473b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1474b47888ceSAndrew Thompson 1475b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1476b47888ceSAndrew Thompson return; 1477b47888ceSAndrew Thompson } 1478b47888ceSAndrew Thompson 1479b47888ceSAndrew Thompson switch (new_state) { 1480b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1481b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_SYNC; 1482b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1483b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1484b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1485b47888ceSAndrew Thompson /* cancel timer */ 1486b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1487b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, 1488b47888ceSAndrew Thompson ("timer_wait_while not active")); 1489b47888ceSAndrew Thompson la->la_pending--; 1490b47888ceSAndrew Thompson } 1491b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1492b47888ceSAndrew Thompson lacp_unselect(lp); 1493b47888ceSAndrew Thompson break; 1494b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1495b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1496b47888ceSAndrew Thompson LACP_AGGREGATE_WAIT_TIME); 1497b47888ceSAndrew Thompson la->la_pending++; 1498b47888ceSAndrew Thompson break; 1499b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1500b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_SYNC; 1501b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1502b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1503b47888ceSAndrew Thompson break; 1504b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1505b47888ceSAndrew Thompson lacp_enable_collecting(lp); 1506b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1507b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1508b47888ceSAndrew Thompson break; 1509b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1510b47888ceSAndrew Thompson lacp_enable_distributing(lp); 1511b47888ceSAndrew Thompson break; 1512b47888ceSAndrew Thompson default: 1513b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1514b47888ceSAndrew Thompson } 1515b47888ceSAndrew Thompson 1516b47888ceSAndrew Thompson LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1517b47888ceSAndrew Thompson 1518b47888ceSAndrew Thompson lp->lp_mux_state = new_state; 1519b47888ceSAndrew Thompson } 1520b47888ceSAndrew Thompson 1521b47888ceSAndrew Thompson static void 1522b47888ceSAndrew Thompson lacp_sm_mux_timer(struct lacp_port *lp) 1523b47888ceSAndrew Thompson { 1524b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1525b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1526b47888ceSAndrew Thompson 1527b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, ("no pending event")); 1528b47888ceSAndrew Thompson 1529b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1530b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1531b47888ceSAndrew Thompson buf, sizeof(buf)), 1532b47888ceSAndrew Thompson la->la_pending, la->la_pending - 1)); 1533b47888ceSAndrew Thompson 1534b47888ceSAndrew Thompson la->la_pending--; 1535b47888ceSAndrew Thompson } 1536b47888ceSAndrew Thompson 1537b47888ceSAndrew Thompson /* periodic transmit machine */ 1538b47888ceSAndrew Thompson 1539b47888ceSAndrew Thompson static void 1540b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1541b47888ceSAndrew Thompson { 1542b47888ceSAndrew Thompson if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1543b47888ceSAndrew Thompson LACP_STATE_TIMEOUT)) { 1544b47888ceSAndrew Thompson return; 1545b47888ceSAndrew Thompson } 1546b47888ceSAndrew Thompson 1547b47888ceSAndrew Thompson LACP_DPRINTF((lp, "partner timeout changed\n")); 1548b47888ceSAndrew Thompson 1549b47888ceSAndrew Thompson /* 1550b47888ceSAndrew Thompson * FAST_PERIODIC -> SLOW_PERIODIC 1551b47888ceSAndrew Thompson * or 1552b47888ceSAndrew Thompson * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1553b47888ceSAndrew Thompson * 1554b47888ceSAndrew Thompson * let lacp_sm_ptx_tx_schedule to update timeout. 1555b47888ceSAndrew Thompson */ 1556b47888ceSAndrew Thompson 1557b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1558b47888ceSAndrew Thompson 1559b47888ceSAndrew Thompson /* 1560b47888ceSAndrew Thompson * if timeout has been shortened, assert NTT. 1561b47888ceSAndrew Thompson */ 1562b47888ceSAndrew Thompson 1563b47888ceSAndrew Thompson if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1564b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1565b47888ceSAndrew Thompson } 1566b47888ceSAndrew Thompson } 1567b47888ceSAndrew Thompson 1568b47888ceSAndrew Thompson static void 1569b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1570b47888ceSAndrew Thompson { 1571b47888ceSAndrew Thompson int timeout; 1572b47888ceSAndrew Thompson 1573b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1574b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1575b47888ceSAndrew Thompson 1576b47888ceSAndrew Thompson /* 1577b47888ceSAndrew Thompson * NO_PERIODIC 1578b47888ceSAndrew Thompson */ 1579b47888ceSAndrew Thompson 1580b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1581b47888ceSAndrew Thompson return; 1582b47888ceSAndrew Thompson } 1583b47888ceSAndrew Thompson 1584b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1585b47888ceSAndrew Thompson return; 1586b47888ceSAndrew Thompson } 1587b47888ceSAndrew Thompson 1588b47888ceSAndrew Thompson timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1589b47888ceSAndrew Thompson LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1590b47888ceSAndrew Thompson 1591b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1592b47888ceSAndrew Thompson } 1593b47888ceSAndrew Thompson 1594b47888ceSAndrew Thompson static void 1595b47888ceSAndrew Thompson lacp_sm_ptx_timer(struct lacp_port *lp) 1596b47888ceSAndrew Thompson { 1597b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1598b47888ceSAndrew Thompson } 1599b47888ceSAndrew Thompson 1600b47888ceSAndrew Thompson static void 1601b47888ceSAndrew Thompson lacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1602b47888ceSAndrew Thompson { 1603b47888ceSAndrew Thompson int timeout; 1604b47888ceSAndrew Thompson 1605b47888ceSAndrew Thompson /* 1606b47888ceSAndrew Thompson * check LACP_DISABLED first 1607b47888ceSAndrew Thompson */ 1608b47888ceSAndrew Thompson 1609b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1610b47888ceSAndrew Thompson return; 1611b47888ceSAndrew Thompson } 1612b47888ceSAndrew Thompson 1613b47888ceSAndrew Thompson /* 1614b47888ceSAndrew Thompson * check loopback condition. 1615b47888ceSAndrew Thompson */ 1616b47888ceSAndrew Thompson 1617b47888ceSAndrew Thompson if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1618b47888ceSAndrew Thompson &lp->lp_actor.lip_systemid)) { 1619b47888ceSAndrew Thompson return; 1620b47888ceSAndrew Thompson } 1621b47888ceSAndrew Thompson 1622b47888ceSAndrew Thompson /* 1623b47888ceSAndrew Thompson * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1624b47888ceSAndrew Thompson */ 1625b47888ceSAndrew Thompson 1626b47888ceSAndrew Thompson lacp_sm_rx_update_selected(lp, du); 1627b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(lp, du); 1628b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(lp, du); 1629b47888ceSAndrew Thompson 1630b47888ceSAndrew Thompson timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1631b47888ceSAndrew Thompson LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1632b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1633b47888ceSAndrew Thompson 1634b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1635b47888ceSAndrew Thompson 1636b47888ceSAndrew Thompson /* 1637b47888ceSAndrew Thompson * kick transmit machine without waiting the next tick. 1638b47888ceSAndrew Thompson */ 1639b47888ceSAndrew Thompson 1640b47888ceSAndrew Thompson lacp_sm_tx(lp); 1641b47888ceSAndrew Thompson } 1642b47888ceSAndrew Thompson 1643b47888ceSAndrew Thompson static void 1644b47888ceSAndrew Thompson lacp_sm_rx_set_expired(struct lacp_port *lp) 1645b47888ceSAndrew Thompson { 1646b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1647b47888ceSAndrew Thompson lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1648b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1649b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_EXPIRED; 1650b47888ceSAndrew Thompson } 1651b47888ceSAndrew Thompson 1652b47888ceSAndrew Thompson static void 1653b47888ceSAndrew Thompson lacp_sm_rx_timer(struct lacp_port *lp) 1654b47888ceSAndrew Thompson { 1655b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1656b47888ceSAndrew Thompson /* CURRENT -> EXPIRED */ 1657b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1658b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 1659b47888ceSAndrew Thompson } else { 1660b47888ceSAndrew Thompson /* EXPIRED -> DEFAULTED */ 1661b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1662b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(lp); 1663b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1664b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1665b47888ceSAndrew Thompson } 1666b47888ceSAndrew Thompson } 1667b47888ceSAndrew Thompson 1668b47888ceSAndrew Thompson static void 1669b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1670b47888ceSAndrew Thompson { 1671b47888ceSAndrew Thompson boolean_t active; 1672b47888ceSAndrew Thompson uint8_t oldpstate; 1673b47888ceSAndrew Thompson char buf[LACP_STATESTR_MAX+1]; 1674b47888ceSAndrew Thompson 16755fc4c149SAndrew Thompson LACP_TRACE(lp); 1676b47888ceSAndrew Thompson 1677b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1678b47888ceSAndrew Thompson 1679b47888ceSAndrew Thompson active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1680b47888ceSAndrew Thompson || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1681b47888ceSAndrew Thompson (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1682b47888ceSAndrew Thompson 1683b47888ceSAndrew Thompson lp->lp_partner = du->ldu_actor; 1684b47888ceSAndrew Thompson if (active && 1685b47888ceSAndrew Thompson ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1686b47888ceSAndrew Thompson LACP_STATE_AGGREGATION) && 1687b47888ceSAndrew Thompson !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1688b47888ceSAndrew Thompson || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1689b47888ceSAndrew Thompson /* XXX nothing? */ 1690b47888ceSAndrew Thompson } else { 1691b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1692b47888ceSAndrew Thompson } 1693b47888ceSAndrew Thompson 1694b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DEFAULTED; 1695b47888ceSAndrew Thompson 1696b47888ceSAndrew Thompson if (oldpstate != lp->lp_partner.lip_state) { 1697b47888ceSAndrew Thompson LACP_DPRINTF((lp, "old pstate %s\n", 1698b47888ceSAndrew Thompson lacp_format_state(oldpstate, buf, sizeof(buf)))); 1699b47888ceSAndrew Thompson LACP_DPRINTF((lp, "new pstate %s\n", 1700b47888ceSAndrew Thompson lacp_format_state(lp->lp_partner.lip_state, buf, 1701b47888ceSAndrew Thompson sizeof(buf)))); 1702b47888ceSAndrew Thompson } 1703b47888ceSAndrew Thompson 170431402c27SAdrian Chadd /* XXX Hack, still need to implement 5.4.9 para 2,3,4 */ 170549de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 170631402c27SAdrian Chadd lp->lp_partner.lip_state |= LACP_STATE_SYNC; 170731402c27SAdrian Chadd 1708b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1709b47888ceSAndrew Thompson } 1710b47888ceSAndrew Thompson 1711b47888ceSAndrew Thompson static void 1712b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1713b47888ceSAndrew Thompson { 17145fc4c149SAndrew Thompson 17155fc4c149SAndrew Thompson LACP_TRACE(lp); 1716b47888ceSAndrew Thompson 1717b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1718b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1719b47888ceSAndrew Thompson LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1720b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1721b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1722b47888ceSAndrew Thompson } 1723b47888ceSAndrew Thompson } 1724b47888ceSAndrew Thompson 1725b47888ceSAndrew Thompson static void 1726b47888ceSAndrew Thompson lacp_sm_rx_record_default(struct lacp_port *lp) 1727b47888ceSAndrew Thompson { 1728b47888ceSAndrew Thompson uint8_t oldpstate; 1729b47888ceSAndrew Thompson 17305fc4c149SAndrew Thompson LACP_TRACE(lp); 1731b47888ceSAndrew Thompson 1732b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 173349de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 173431402c27SAdrian Chadd lp->lp_partner = lacp_partner_admin_strict; 173531402c27SAdrian Chadd else 1736c1be893cSSteven Hartland lp->lp_partner = lacp_partner_admin_optimistic; 1737b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DEFAULTED; 1738b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1739b47888ceSAndrew Thompson } 1740b47888ceSAndrew Thompson 1741b47888ceSAndrew Thompson static void 1742b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1743b47888ceSAndrew Thompson const struct lacp_peerinfo *info) 1744b47888ceSAndrew Thompson { 17455fc4c149SAndrew Thompson 17465fc4c149SAndrew Thompson LACP_TRACE(lp); 1747b47888ceSAndrew Thompson 1748b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1749b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1750b47888ceSAndrew Thompson LACP_STATE_AGGREGATION)) { 1751b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1752b47888ceSAndrew Thompson /* mux machine will clean up lp->lp_aggregator */ 1753b47888ceSAndrew Thompson } 1754b47888ceSAndrew Thompson } 1755b47888ceSAndrew Thompson 1756b47888ceSAndrew Thompson static void 1757b47888ceSAndrew Thompson lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1758b47888ceSAndrew Thompson { 17595fc4c149SAndrew Thompson 17605fc4c149SAndrew Thompson LACP_TRACE(lp); 1761b47888ceSAndrew Thompson 1762b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1763b47888ceSAndrew Thompson } 1764b47888ceSAndrew Thompson 1765b47888ceSAndrew Thompson static void 1766b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(struct lacp_port *lp) 1767b47888ceSAndrew Thompson { 17685fc4c149SAndrew Thompson 17695fc4c149SAndrew Thompson LACP_TRACE(lp); 1770b47888ceSAndrew Thompson 177149de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 177231402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 177331402c27SAdrian Chadd &lacp_partner_admin_strict); 177431402c27SAdrian Chadd else 177531402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 177631402c27SAdrian Chadd &lacp_partner_admin_optimistic); 1777b47888ceSAndrew Thompson } 1778b47888ceSAndrew Thompson 1779b47888ceSAndrew Thompson /* transmit machine */ 1780b47888ceSAndrew Thompson 1781b47888ceSAndrew Thompson static void 1782b47888ceSAndrew Thompson lacp_sm_tx(struct lacp_port *lp) 1783b47888ceSAndrew Thompson { 178431402c27SAdrian Chadd int error = 0; 1785b47888ceSAndrew Thompson 1786b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1787b47888ceSAndrew Thompson #if 1 1788b47888ceSAndrew Thompson || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1789b47888ceSAndrew Thompson && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1790b47888ceSAndrew Thompson #endif 1791b47888ceSAndrew Thompson ) { 1792b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1793b47888ceSAndrew Thompson } 1794b47888ceSAndrew Thompson 1795b47888ceSAndrew Thompson if (!(lp->lp_flags & LACP_PORT_NTT)) { 1796b47888ceSAndrew Thompson return; 1797b47888ceSAndrew Thompson } 1798b47888ceSAndrew Thompson 1799b47888ceSAndrew Thompson /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1800b47888ceSAndrew Thompson if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1801b47888ceSAndrew Thompson (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1802b47888ceSAndrew Thompson LACP_DPRINTF((lp, "rate limited pdu\n")); 1803b47888ceSAndrew Thompson return; 1804b47888ceSAndrew Thompson } 1805b47888ceSAndrew Thompson 180649de4f22SAdrian Chadd if (((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_tx_test) == 0) { 1807b47888ceSAndrew Thompson error = lacp_xmit_lacpdu(lp); 180849de4f22SAdrian Chadd } else { 180931402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping TX PDU\n")); 181049de4f22SAdrian Chadd } 1811b47888ceSAndrew Thompson 1812b47888ceSAndrew Thompson if (error == 0) { 1813b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1814b47888ceSAndrew Thompson } else { 1815b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1816b47888ceSAndrew Thompson error)); 1817b47888ceSAndrew Thompson } 1818b47888ceSAndrew Thompson } 1819b47888ceSAndrew Thompson 1820b47888ceSAndrew Thompson static void 1821b47888ceSAndrew Thompson lacp_sm_assert_ntt(struct lacp_port *lp) 1822b47888ceSAndrew Thompson { 1823b47888ceSAndrew Thompson 1824b47888ceSAndrew Thompson lp->lp_flags |= LACP_PORT_NTT; 1825b47888ceSAndrew Thompson } 1826b47888ceSAndrew Thompson 1827b47888ceSAndrew Thompson static void 1828b47888ceSAndrew Thompson lacp_run_timers(struct lacp_port *lp) 1829b47888ceSAndrew Thompson { 1830b47888ceSAndrew Thompson int i; 1831b47888ceSAndrew Thompson 1832b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 1833b47888ceSAndrew Thompson KASSERT(lp->lp_timer[i] >= 0, 1834b47888ceSAndrew Thompson ("invalid timer value %d", lp->lp_timer[i])); 1835b47888ceSAndrew Thompson if (lp->lp_timer[i] == 0) { 1836b47888ceSAndrew Thompson continue; 1837b47888ceSAndrew Thompson } else if (--lp->lp_timer[i] <= 0) { 1838b47888ceSAndrew Thompson if (lacp_timer_funcs[i]) { 1839b47888ceSAndrew Thompson (*lacp_timer_funcs[i])(lp); 1840b47888ceSAndrew Thompson } 1841b47888ceSAndrew Thompson } 1842b47888ceSAndrew Thompson } 1843b47888ceSAndrew Thompson } 1844b47888ceSAndrew Thompson 1845b47888ceSAndrew Thompson int 18463de18008SAndrew Thompson lacp_marker_input(struct lacp_port *lp, struct mbuf *m) 1847b47888ceSAndrew Thompson { 1848998971a7SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 18493de18008SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 18503de18008SAndrew Thompson struct lacp_port *lp2; 1851b47888ceSAndrew Thompson struct markerdu *mdu; 1852b47888ceSAndrew Thompson int error = 0; 1853998971a7SAndrew Thompson int pending = 0; 1854b47888ceSAndrew Thompson 1855b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*mdu)) { 1856b47888ceSAndrew Thompson goto bad; 1857b47888ceSAndrew Thompson } 1858b47888ceSAndrew Thompson 1859b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 1860b47888ceSAndrew Thompson goto bad; 1861b47888ceSAndrew Thompson } 1862b47888ceSAndrew Thompson 1863b47888ceSAndrew Thompson if (m->m_len < sizeof(*mdu)) { 1864b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*mdu)); 1865b47888ceSAndrew Thompson if (m == NULL) { 1866b47888ceSAndrew Thompson return (ENOMEM); 1867b47888ceSAndrew Thompson } 1868b47888ceSAndrew Thompson } 1869b47888ceSAndrew Thompson 1870b47888ceSAndrew Thompson mdu = mtod(m, struct markerdu *); 1871b47888ceSAndrew Thompson 1872b47888ceSAndrew Thompson if (memcmp(&mdu->mdu_eh.ether_dhost, 1873b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1874b47888ceSAndrew Thompson goto bad; 1875b47888ceSAndrew Thompson } 1876b47888ceSAndrew Thompson 1877b47888ceSAndrew Thompson if (mdu->mdu_sph.sph_version != 1) { 1878b47888ceSAndrew Thompson goto bad; 1879b47888ceSAndrew Thompson } 1880b47888ceSAndrew Thompson 1881b47888ceSAndrew Thompson switch (mdu->mdu_tlv.tlv_type) { 1882b47888ceSAndrew Thompson case MARKER_TYPE_INFO: 1883b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1884b47888ceSAndrew Thompson marker_info_tlv_template, TRUE)) { 1885b47888ceSAndrew Thompson goto bad; 1886b47888ceSAndrew Thompson } 1887b47888ceSAndrew Thompson mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1888b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, 1889b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1890b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, 189118242d3bSAndrew Thompson lgp->lp_lladdr, ETHER_ADDR_LEN); 189218242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 1893b47888ceSAndrew Thompson break; 1894b47888ceSAndrew Thompson 1895b47888ceSAndrew Thompson case MARKER_TYPE_RESPONSE: 1896b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1897b47888ceSAndrew Thompson marker_response_tlv_template, TRUE)) { 1898b47888ceSAndrew Thompson goto bad; 1899b47888ceSAndrew Thompson } 1900998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 1901998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 1902998971a7SAndrew Thompson ":", ntohl(mdu->mdu_info.mi_rq_xid))); 1903998971a7SAndrew Thompson 1904998971a7SAndrew Thompson /* Verify that it is the last marker we sent out */ 1905998971a7SAndrew Thompson if (memcmp(&mdu->mdu_info, &lp->lp_marker, 1906998971a7SAndrew Thompson sizeof(struct lacp_markerinfo))) 1907998971a7SAndrew Thompson goto bad; 1908998971a7SAndrew Thompson 19093de18008SAndrew Thompson LACP_LOCK(lsc); 1910998971a7SAndrew Thompson lp->lp_flags &= ~LACP_PORT_MARK; 1911998971a7SAndrew Thompson 1912998971a7SAndrew Thompson if (lsc->lsc_suppress_distributing) { 1913998971a7SAndrew Thompson /* Check if any ports are waiting for a response */ 1914998971a7SAndrew Thompson LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 1915998971a7SAndrew Thompson if (lp2->lp_flags & LACP_PORT_MARK) { 1916998971a7SAndrew Thompson pending = 1; 1917998971a7SAndrew Thompson break; 1918998971a7SAndrew Thompson } 1919998971a7SAndrew Thompson } 1920998971a7SAndrew Thompson 1921998971a7SAndrew Thompson if (pending == 0) { 1922998971a7SAndrew Thompson /* All interface queues are clear */ 1923998971a7SAndrew Thompson LACP_DPRINTF((NULL, "queue flush complete\n")); 1924998971a7SAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 1925998971a7SAndrew Thompson } 1926998971a7SAndrew Thompson } 19273de18008SAndrew Thompson LACP_UNLOCK(lsc); 1928998971a7SAndrew Thompson m_freem(m); 1929998971a7SAndrew Thompson break; 1930998971a7SAndrew Thompson 1931b47888ceSAndrew Thompson default: 1932b47888ceSAndrew Thompson goto bad; 1933b47888ceSAndrew Thompson } 1934b47888ceSAndrew Thompson 1935b47888ceSAndrew Thompson return (error); 1936b47888ceSAndrew Thompson 1937b47888ceSAndrew Thompson bad: 1938998971a7SAndrew Thompson LACP_DPRINTF((lp, "bad marker frame\n")); 1939b47888ceSAndrew Thompson m_freem(m); 1940b47888ceSAndrew Thompson return (EINVAL); 1941b47888ceSAndrew Thompson } 1942b47888ceSAndrew Thompson 1943b47888ceSAndrew Thompson static int 1944b47888ceSAndrew Thompson tlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1945b47888ceSAndrew Thompson const struct tlv_template *tmpl, boolean_t check_type) 1946b47888ceSAndrew Thompson { 1947b47888ceSAndrew Thompson while (/* CONSTCOND */ 1) { 1948b47888ceSAndrew Thompson if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1949b47888ceSAndrew Thompson return (EINVAL); 1950b47888ceSAndrew Thompson } 1951b47888ceSAndrew Thompson if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1952b47888ceSAndrew Thompson tlv->tlv_length != tmpl->tmpl_length) { 1953b47888ceSAndrew Thompson return (EINVAL); 1954b47888ceSAndrew Thompson } 1955b47888ceSAndrew Thompson if (tmpl->tmpl_type == 0) { 1956b47888ceSAndrew Thompson break; 1957b47888ceSAndrew Thompson } 1958b47888ceSAndrew Thompson tlv = (const struct tlvhdr *) 1959b47888ceSAndrew Thompson ((const char *)tlv + tlv->tlv_length); 1960b47888ceSAndrew Thompson tmpl++; 1961b47888ceSAndrew Thompson } 1962b47888ceSAndrew Thompson 1963b47888ceSAndrew Thompson return (0); 1964b47888ceSAndrew Thompson } 1965b47888ceSAndrew Thompson 19665fc4c149SAndrew Thompson /* Debugging */ 1967b47888ceSAndrew Thompson const char * 1968b47888ceSAndrew Thompson lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1969b47888ceSAndrew Thompson { 1970b47888ceSAndrew Thompson snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1971b47888ceSAndrew Thompson (int)mac[0], 1972b47888ceSAndrew Thompson (int)mac[1], 1973b47888ceSAndrew Thompson (int)mac[2], 1974b47888ceSAndrew Thompson (int)mac[3], 1975b47888ceSAndrew Thompson (int)mac[4], 1976b47888ceSAndrew Thompson (int)mac[5]); 1977b47888ceSAndrew Thompson 1978b47888ceSAndrew Thompson return (buf); 1979b47888ceSAndrew Thompson } 1980b47888ceSAndrew Thompson 1981b47888ceSAndrew Thompson const char * 1982b47888ceSAndrew Thompson lacp_format_systemid(const struct lacp_systemid *sysid, 1983b47888ceSAndrew Thompson char *buf, size_t buflen) 1984b47888ceSAndrew Thompson { 1985b47888ceSAndrew Thompson char macbuf[LACP_MACSTR_MAX+1]; 1986b47888ceSAndrew Thompson 1987b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%s", 1988b47888ceSAndrew Thompson ntohs(sysid->lsi_prio), 1989b47888ceSAndrew Thompson lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1990b47888ceSAndrew Thompson 1991b47888ceSAndrew Thompson return (buf); 1992b47888ceSAndrew Thompson } 1993b47888ceSAndrew Thompson 1994b47888ceSAndrew Thompson const char * 1995b47888ceSAndrew Thompson lacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1996b47888ceSAndrew Thompson { 1997b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%04X", 1998b47888ceSAndrew Thompson ntohs(portid->lpi_prio), 1999b47888ceSAndrew Thompson ntohs(portid->lpi_portno)); 2000b47888ceSAndrew Thompson 2001b47888ceSAndrew Thompson return (buf); 2002b47888ceSAndrew Thompson } 2003b47888ceSAndrew Thompson 2004b47888ceSAndrew Thompson const char * 2005b47888ceSAndrew Thompson lacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 2006b47888ceSAndrew Thompson { 2007b47888ceSAndrew Thompson char sysid[LACP_SYSTEMIDSTR_MAX+1]; 2008b47888ceSAndrew Thompson char portid[LACP_PORTIDSTR_MAX+1]; 2009b47888ceSAndrew Thompson 2010b47888ceSAndrew Thompson snprintf(buf, buflen, "(%s,%04X,%s)", 2011b47888ceSAndrew Thompson lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 2012b47888ceSAndrew Thompson ntohs(peer->lip_key), 2013b47888ceSAndrew Thompson lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 2014b47888ceSAndrew Thompson 2015b47888ceSAndrew Thompson return (buf); 2016b47888ceSAndrew Thompson } 2017b47888ceSAndrew Thompson 2018b47888ceSAndrew Thompson const char * 2019b47888ceSAndrew Thompson lacp_format_lagid(const struct lacp_peerinfo *a, 2020b47888ceSAndrew Thompson const struct lacp_peerinfo *b, char *buf, size_t buflen) 2021b47888ceSAndrew Thompson { 2022b47888ceSAndrew Thompson char astr[LACP_PARTNERSTR_MAX+1]; 2023b47888ceSAndrew Thompson char bstr[LACP_PARTNERSTR_MAX+1]; 2024b47888ceSAndrew Thompson 2025b47888ceSAndrew Thompson #if 0 2026b47888ceSAndrew Thompson /* 2027b47888ceSAndrew Thompson * there's a convention to display small numbered peer 2028b47888ceSAndrew Thompson * in the left. 2029b47888ceSAndrew Thompson */ 2030b47888ceSAndrew Thompson 2031b47888ceSAndrew Thompson if (lacp_compare_peerinfo(a, b) > 0) { 2032b47888ceSAndrew Thompson const struct lacp_peerinfo *t; 2033b47888ceSAndrew Thompson 2034b47888ceSAndrew Thompson t = a; 2035b47888ceSAndrew Thompson a = b; 2036b47888ceSAndrew Thompson b = t; 2037b47888ceSAndrew Thompson } 2038b47888ceSAndrew Thompson #endif 2039b47888ceSAndrew Thompson 2040b47888ceSAndrew Thompson snprintf(buf, buflen, "[%s,%s]", 2041b47888ceSAndrew Thompson lacp_format_partner(a, astr, sizeof(astr)), 2042b47888ceSAndrew Thompson lacp_format_partner(b, bstr, sizeof(bstr))); 2043b47888ceSAndrew Thompson 2044b47888ceSAndrew Thompson return (buf); 2045b47888ceSAndrew Thompson } 2046b47888ceSAndrew Thompson 2047b47888ceSAndrew Thompson const char * 2048b47888ceSAndrew Thompson lacp_format_lagid_aggregator(const struct lacp_aggregator *la, 2049b47888ceSAndrew Thompson char *buf, size_t buflen) 2050b47888ceSAndrew Thompson { 2051b47888ceSAndrew Thompson if (la == NULL) { 2052b47888ceSAndrew Thompson return ("(none)"); 2053b47888ceSAndrew Thompson } 2054b47888ceSAndrew Thompson 2055b47888ceSAndrew Thompson return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 2056b47888ceSAndrew Thompson } 2057b47888ceSAndrew Thompson 2058b47888ceSAndrew Thompson const char * 2059b47888ceSAndrew Thompson lacp_format_state(uint8_t state, char *buf, size_t buflen) 2060b47888ceSAndrew Thompson { 2061b47888ceSAndrew Thompson snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 2062b47888ceSAndrew Thompson return (buf); 2063b47888ceSAndrew Thompson } 2064b47888ceSAndrew Thompson 2065b47888ceSAndrew Thompson static void 2066b47888ceSAndrew Thompson lacp_dump_lacpdu(const struct lacpdu *du) 2067b47888ceSAndrew Thompson { 2068b47888ceSAndrew Thompson char buf[LACP_PARTNERSTR_MAX+1]; 2069b47888ceSAndrew Thompson char buf2[LACP_STATESTR_MAX+1]; 2070b47888ceSAndrew Thompson 2071b47888ceSAndrew Thompson printf("actor=%s\n", 2072b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 2073b47888ceSAndrew Thompson printf("actor.state=%s\n", 2074b47888ceSAndrew Thompson lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 2075b47888ceSAndrew Thompson printf("partner=%s\n", 2076b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 2077b47888ceSAndrew Thompson printf("partner.state=%s\n", 2078b47888ceSAndrew Thompson lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 2079b47888ceSAndrew Thompson 2080b47888ceSAndrew Thompson printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 2081b47888ceSAndrew Thompson } 2082b47888ceSAndrew Thompson 2083b47888ceSAndrew Thompson static void 2084b47888ceSAndrew Thompson lacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 2085b47888ceSAndrew Thompson { 2086b47888ceSAndrew Thompson va_list va; 2087b47888ceSAndrew Thompson 2088b47888ceSAndrew Thompson if (lp) { 2089b47888ceSAndrew Thompson printf("%s: ", lp->lp_ifp->if_xname); 2090b47888ceSAndrew Thompson } 2091b47888ceSAndrew Thompson 2092b47888ceSAndrew Thompson va_start(va, fmt); 2093b47888ceSAndrew Thompson vprintf(fmt, va); 2094b47888ceSAndrew Thompson va_end(va); 2095b47888ceSAndrew Thompson } 2096