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 33b47888ceSAndrew Thompson #include <sys/param.h> 34b47888ceSAndrew Thompson #include <sys/callout.h> 35c3322cb9SGleb Smirnoff #include <sys/eventhandler.h> 36b47888ceSAndrew Thompson #include <sys/mbuf.h> 37b47888ceSAndrew Thompson #include <sys/systm.h> 38b47888ceSAndrew Thompson #include <sys/malloc.h> 39b47888ceSAndrew Thompson #include <sys/kernel.h> /* hz */ 40b47888ceSAndrew Thompson #include <sys/socket.h> /* for net/if.h */ 41b47888ceSAndrew Thompson #include <sys/sockio.h> 425fc4c149SAndrew Thompson #include <sys/sysctl.h> 43b47888ceSAndrew Thompson #include <machine/stdarg.h> 443bf517e3SAndrew Thompson #include <sys/lock.h> 453bf517e3SAndrew Thompson #include <sys/rwlock.h> 4676039bc8SGleb Smirnoff #include <sys/taskqueue.h> 47b47888ceSAndrew Thompson 48b47888ceSAndrew Thompson #include <net/if.h> 4976039bc8SGleb Smirnoff #include <net/if_var.h> 50b47888ceSAndrew Thompson #include <net/if_dl.h> 51b47888ceSAndrew Thompson #include <net/ethernet.h> 52b47888ceSAndrew Thompson #include <net/if_media.h> 53b47888ceSAndrew Thompson #include <net/if_types.h> 54b47888ceSAndrew Thompson 5518242d3bSAndrew Thompson #include <net/if_lagg.h> 56b47888ceSAndrew Thompson #include <net/ieee8023ad_lacp.h> 57b47888ceSAndrew Thompson 58b47888ceSAndrew Thompson /* 59b47888ceSAndrew Thompson * actor system priority and port priority. 60b47888ceSAndrew Thompson * XXX should be configurable. 61b47888ceSAndrew Thompson */ 62b47888ceSAndrew Thompson 63b47888ceSAndrew Thompson #define LACP_SYSTEM_PRIO 0x8000 64b47888ceSAndrew Thompson #define LACP_PORT_PRIO 0x8000 65b47888ceSAndrew Thompson 66b47888ceSAndrew Thompson const uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 67b47888ceSAndrew Thompson { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 68b47888ceSAndrew Thompson 69b47888ceSAndrew Thompson static const struct tlv_template lacp_info_tlv_template[] = { 70b47888ceSAndrew Thompson { LACP_TYPE_ACTORINFO, 71b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 72b47888ceSAndrew Thompson { LACP_TYPE_PARTNERINFO, 73b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 74b47888ceSAndrew Thompson { LACP_TYPE_COLLECTORINFO, 75b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 76b47888ceSAndrew Thompson { 0, 0 }, 77b47888ceSAndrew Thompson }; 78b47888ceSAndrew Thompson 79b47888ceSAndrew Thompson static const struct tlv_template marker_info_tlv_template[] = { 80998971a7SAndrew Thompson { MARKER_TYPE_INFO, 81998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 82b47888ceSAndrew Thompson { 0, 0 }, 83b47888ceSAndrew Thompson }; 84b47888ceSAndrew Thompson 85b47888ceSAndrew Thompson static const struct tlv_template marker_response_tlv_template[] = { 86998971a7SAndrew Thompson { MARKER_TYPE_RESPONSE, 87998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 88b47888ceSAndrew Thompson { 0, 0 }, 89b47888ceSAndrew Thompson }; 90b47888ceSAndrew Thompson 913de18008SAndrew Thompson typedef void (*lacp_timer_func_t)(struct lacp_port *); 923de18008SAndrew Thompson 93b47888ceSAndrew Thompson static void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 94998971a7SAndrew Thompson static void lacp_fill_markerinfo(struct lacp_port *, 95998971a7SAndrew Thompson struct lacp_markerinfo *); 96b47888ceSAndrew Thompson 97b47888ceSAndrew Thompson static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 98b47888ceSAndrew Thompson static void lacp_suppress_distributing(struct lacp_softc *, 99b47888ceSAndrew Thompson struct lacp_aggregator *); 100b47888ceSAndrew Thompson static void lacp_transit_expire(void *); 1013de18008SAndrew Thompson static void lacp_update_portmap(struct lacp_softc *); 102b47888ceSAndrew Thompson static void lacp_select_active_aggregator(struct lacp_softc *); 103b47888ceSAndrew Thompson static uint16_t lacp_compose_key(struct lacp_port *); 104b47888ceSAndrew Thompson static int tlv_check(const void *, size_t, const struct tlvhdr *, 105b47888ceSAndrew Thompson const struct tlv_template *, boolean_t); 106b47888ceSAndrew Thompson static void lacp_tick(void *); 107b47888ceSAndrew Thompson 108b47888ceSAndrew Thompson static void lacp_fill_aggregator_id(struct lacp_aggregator *, 109b47888ceSAndrew Thompson const struct lacp_port *); 110b47888ceSAndrew Thompson static void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 111b47888ceSAndrew Thompson const struct lacp_peerinfo *); 112b47888ceSAndrew Thompson static int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 113b47888ceSAndrew Thompson const struct lacp_port *); 114b47888ceSAndrew Thompson static int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 115b47888ceSAndrew Thompson const struct lacp_peerinfo *); 116b47888ceSAndrew Thompson 117b47888ceSAndrew Thompson static struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 118b47888ceSAndrew Thompson struct lacp_port *); 119b47888ceSAndrew Thompson static void lacp_aggregator_addref(struct lacp_softc *, 120b47888ceSAndrew Thompson struct lacp_aggregator *); 121b47888ceSAndrew Thompson static void lacp_aggregator_delref(struct lacp_softc *, 122b47888ceSAndrew Thompson struct lacp_aggregator *); 123b47888ceSAndrew Thompson 124b47888ceSAndrew Thompson /* receive machine */ 125b47888ceSAndrew Thompson 1263de18008SAndrew Thompson static int lacp_pdu_input(struct lacp_port *, struct mbuf *); 1273de18008SAndrew Thompson static int lacp_marker_input(struct lacp_port *, struct mbuf *); 128b47888ceSAndrew Thompson static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 129b47888ceSAndrew Thompson static void lacp_sm_rx_timer(struct lacp_port *); 130b47888ceSAndrew Thompson static void lacp_sm_rx_set_expired(struct lacp_port *); 131b47888ceSAndrew Thompson static void lacp_sm_rx_update_ntt(struct lacp_port *, 132b47888ceSAndrew Thompson const struct lacpdu *); 133b47888ceSAndrew Thompson static void lacp_sm_rx_record_pdu(struct lacp_port *, 134b47888ceSAndrew Thompson const struct lacpdu *); 135b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected(struct lacp_port *, 136b47888ceSAndrew Thompson const struct lacpdu *); 137b47888ceSAndrew Thompson static void lacp_sm_rx_record_default(struct lacp_port *); 138b47888ceSAndrew Thompson static void lacp_sm_rx_update_default_selected(struct lacp_port *); 139b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 140b47888ceSAndrew Thompson const struct lacp_peerinfo *); 141b47888ceSAndrew Thompson 142b47888ceSAndrew Thompson /* mux machine */ 143b47888ceSAndrew Thompson 144b47888ceSAndrew Thompson static void lacp_sm_mux(struct lacp_port *); 145b47888ceSAndrew Thompson static void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 146b47888ceSAndrew Thompson static void lacp_sm_mux_timer(struct lacp_port *); 147b47888ceSAndrew Thompson 148b47888ceSAndrew Thompson /* periodic transmit machine */ 149b47888ceSAndrew Thompson 150b47888ceSAndrew Thompson static void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 151b47888ceSAndrew Thompson static void lacp_sm_ptx_tx_schedule(struct lacp_port *); 152b47888ceSAndrew Thompson static void lacp_sm_ptx_timer(struct lacp_port *); 153b47888ceSAndrew Thompson 154b47888ceSAndrew Thompson /* transmit machine */ 155b47888ceSAndrew Thompson 156b47888ceSAndrew Thompson static void lacp_sm_tx(struct lacp_port *); 157b47888ceSAndrew Thompson static void lacp_sm_assert_ntt(struct lacp_port *); 158b47888ceSAndrew Thompson 159b47888ceSAndrew Thompson static void lacp_run_timers(struct lacp_port *); 160b47888ceSAndrew Thompson static int lacp_compare_peerinfo(const struct lacp_peerinfo *, 161b47888ceSAndrew Thompson const struct lacp_peerinfo *); 162b47888ceSAndrew Thompson static int lacp_compare_systemid(const struct lacp_systemid *, 163b47888ceSAndrew Thompson const struct lacp_systemid *); 164b47888ceSAndrew Thompson static void lacp_port_enable(struct lacp_port *); 165b47888ceSAndrew Thompson static void lacp_port_disable(struct lacp_port *); 166b47888ceSAndrew Thompson static void lacp_select(struct lacp_port *); 167b47888ceSAndrew Thompson static void lacp_unselect(struct lacp_port *); 168b47888ceSAndrew Thompson static void lacp_disable_collecting(struct lacp_port *); 169b47888ceSAndrew Thompson static void lacp_enable_collecting(struct lacp_port *); 170b47888ceSAndrew Thompson static void lacp_disable_distributing(struct lacp_port *); 171b47888ceSAndrew Thompson static void lacp_enable_distributing(struct lacp_port *); 172b47888ceSAndrew Thompson static int lacp_xmit_lacpdu(struct lacp_port *); 173998971a7SAndrew Thompson static int lacp_xmit_marker(struct lacp_port *); 174b47888ceSAndrew Thompson 1755fc4c149SAndrew Thompson /* Debugging */ 1765fc4c149SAndrew Thompson 177b47888ceSAndrew Thompson static void lacp_dump_lacpdu(const struct lacpdu *); 178b47888ceSAndrew Thompson static const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 179b47888ceSAndrew Thompson size_t); 180b47888ceSAndrew Thompson static const char *lacp_format_lagid(const struct lacp_peerinfo *, 181b47888ceSAndrew Thompson const struct lacp_peerinfo *, char *, size_t); 182b47888ceSAndrew Thompson static const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 183b47888ceSAndrew Thompson char *, size_t); 184b47888ceSAndrew Thompson static const char *lacp_format_state(uint8_t, char *, size_t); 185b47888ceSAndrew Thompson static const char *lacp_format_mac(const uint8_t *, char *, size_t); 186b47888ceSAndrew Thompson static const char *lacp_format_systemid(const struct lacp_systemid *, char *, 187b47888ceSAndrew Thompson size_t); 188b47888ceSAndrew Thompson static const char *lacp_format_portid(const struct lacp_portid *, char *, 189b47888ceSAndrew Thompson size_t); 190b47888ceSAndrew Thompson static void lacp_dprintf(const struct lacp_port *, const char *, ...) 191b47888ceSAndrew Thompson __attribute__((__format__(__printf__, 2, 3))); 1925fc4c149SAndrew Thompson 193939a050aSHiroki Sato static VNET_DEFINE(int, lacp_debug); 194939a050aSHiroki Sato #define V_lacp_debug VNET(lacp_debug) 19531402c27SAdrian Chadd SYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD, 0, "ieee802.3ad"); 196939a050aSHiroki Sato SYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RWTUN | CTLFLAG_VNET, 197939a050aSHiroki Sato &VNET_NAME(lacp_debug), 0, "Enable LACP debug logging (1=debug, 2=trace)"); 1985fc4c149SAndrew Thompson 199939a050aSHiroki Sato #define LACP_DPRINTF(a) if (V_lacp_debug & 0x01) { lacp_dprintf a ; } 200939a050aSHiroki Sato #define LACP_TRACE(a) if (V_lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); } 201939a050aSHiroki Sato #define LACP_TPRINTF(a) if (V_lacp_debug & 0x04) { lacp_dprintf a ; } 202b47888ceSAndrew Thompson 203b47888ceSAndrew Thompson /* 204b47888ceSAndrew Thompson * partner administration variables. 205b47888ceSAndrew Thompson * XXX should be configurable. 206b47888ceSAndrew Thompson */ 207b47888ceSAndrew Thompson 20831402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_optimistic = { 209b47888ceSAndrew Thompson .lip_systemid = { .lsi_prio = 0xffff }, 210b47888ceSAndrew Thompson .lip_portid = { .lpi_prio = 0xffff }, 211b47888ceSAndrew Thompson .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 212b47888ceSAndrew Thompson LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 21331402c27SAdrian Chadd }; 21431402c27SAdrian Chadd 21531402c27SAdrian Chadd static const struct lacp_peerinfo lacp_partner_admin_strict = { 21631402c27SAdrian Chadd .lip_systemid = { .lsi_prio = 0xffff }, 21731402c27SAdrian Chadd .lip_portid = { .lpi_prio = 0xffff }, 218b47888ceSAndrew Thompson .lip_state = 0, 219b47888ceSAndrew Thompson }; 220b47888ceSAndrew Thompson 221b47888ceSAndrew Thompson static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 222b47888ceSAndrew Thompson [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 223b47888ceSAndrew Thompson [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 224b47888ceSAndrew Thompson [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 225b47888ceSAndrew Thompson }; 226b47888ceSAndrew Thompson 227af0084c9SAndrew Thompson struct mbuf * 22818242d3bSAndrew Thompson lacp_input(struct lagg_port *lgp, struct mbuf *m) 229b47888ceSAndrew Thompson { 2303de18008SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 2313bf517e3SAndrew Thompson uint8_t subtype; 2323bf517e3SAndrew Thompson 2333bf517e3SAndrew Thompson if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 2343bf517e3SAndrew Thompson m_freem(m); 235af0084c9SAndrew Thompson return (NULL); 2363bf517e3SAndrew Thompson } 2373bf517e3SAndrew Thompson 2383bf517e3SAndrew Thompson m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 2393bf517e3SAndrew Thompson switch (subtype) { 2403bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_LACP: 2413de18008SAndrew Thompson lacp_pdu_input(lp, m); 2423de18008SAndrew Thompson return (NULL); 2433bf517e3SAndrew Thompson 2443bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_MARKER: 2453de18008SAndrew Thompson lacp_marker_input(lp, m); 246af0084c9SAndrew Thompson return (NULL); 2473bf517e3SAndrew Thompson } 2483bf517e3SAndrew Thompson 2493de18008SAndrew Thompson /* Not a subtype we are interested in */ 2503de18008SAndrew Thompson return (m); 2513bf517e3SAndrew Thompson } 2523bf517e3SAndrew Thompson 2533bf517e3SAndrew Thompson /* 2543bf517e3SAndrew Thompson * lacp_pdu_input: process lacpdu 2553bf517e3SAndrew Thompson */ 2563bf517e3SAndrew Thompson static int 2573de18008SAndrew Thompson lacp_pdu_input(struct lacp_port *lp, struct mbuf *m) 2583bf517e3SAndrew Thompson { 2593de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 260b47888ceSAndrew Thompson struct lacpdu *du; 261b47888ceSAndrew Thompson int error = 0; 262b47888ceSAndrew Thompson 263b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*du)) { 264b47888ceSAndrew Thompson goto bad; 265b47888ceSAndrew Thompson } 266b47888ceSAndrew Thompson 267b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 268b47888ceSAndrew Thompson goto bad; 269b47888ceSAndrew Thompson } 270b47888ceSAndrew Thompson 271b47888ceSAndrew Thompson if (m->m_len < sizeof(*du)) { 272b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*du)); 273b47888ceSAndrew Thompson if (m == NULL) { 274b47888ceSAndrew Thompson return (ENOMEM); 275b47888ceSAndrew Thompson } 276b47888ceSAndrew Thompson } 277b47888ceSAndrew Thompson 278b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 279b47888ceSAndrew Thompson 280b47888ceSAndrew Thompson if (memcmp(&du->ldu_eh.ether_dhost, 281b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 282b47888ceSAndrew Thompson goto bad; 283b47888ceSAndrew Thompson } 284b47888ceSAndrew Thompson 285b47888ceSAndrew Thompson /* 286b47888ceSAndrew Thompson * ignore the version for compatibility with 287b47888ceSAndrew Thompson * the future protocol revisions. 288b47888ceSAndrew Thompson */ 289b47888ceSAndrew Thompson #if 0 290b47888ceSAndrew Thompson if (du->ldu_sph.sph_version != 1) { 291b47888ceSAndrew Thompson goto bad; 292b47888ceSAndrew Thompson } 293b47888ceSAndrew Thompson #endif 294b47888ceSAndrew Thompson 295b47888ceSAndrew Thompson /* 296b47888ceSAndrew Thompson * ignore tlv types for compatibility with 297b47888ceSAndrew Thompson * the future protocol revisions. 298b47888ceSAndrew Thompson */ 299b47888ceSAndrew Thompson if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 300b47888ceSAndrew Thompson lacp_info_tlv_template, FALSE)) { 301b47888ceSAndrew Thompson goto bad; 302b47888ceSAndrew Thompson } 303b47888ceSAndrew Thompson 304939a050aSHiroki Sato if (V_lacp_debug > 0) { 3055fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu receive\n"); 306b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 3075fc4c149SAndrew Thompson } 3083de18008SAndrew Thompson 30949de4f22SAdrian Chadd if ((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_rx_test) { 31031402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping RX PDU\n")); 31131402c27SAdrian Chadd goto bad; 31231402c27SAdrian Chadd } 31331402c27SAdrian Chadd 3143de18008SAndrew Thompson LACP_LOCK(lsc); 315b47888ceSAndrew Thompson lacp_sm_rx(lp, du); 3163de18008SAndrew Thompson LACP_UNLOCK(lsc); 317b47888ceSAndrew Thompson 318b47888ceSAndrew Thompson m_freem(m); 319b47888ceSAndrew Thompson return (error); 320b47888ceSAndrew Thompson 321b47888ceSAndrew Thompson bad: 322b47888ceSAndrew Thompson m_freem(m); 323b47888ceSAndrew Thompson return (EINVAL); 324b47888ceSAndrew Thompson } 325b47888ceSAndrew Thompson 326b47888ceSAndrew Thompson static void 327b47888ceSAndrew Thompson lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 328b47888ceSAndrew Thompson { 32918242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 330ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 331b47888ceSAndrew Thompson 332b47888ceSAndrew Thompson info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 333b47888ceSAndrew Thompson memcpy(&info->lip_systemid.lsi_mac, 334ec32b37eSAndrew Thompson IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 335b47888ceSAndrew Thompson info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 336b47888ceSAndrew Thompson info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 337b47888ceSAndrew Thompson info->lip_state = lp->lp_state; 338b47888ceSAndrew Thompson } 339b47888ceSAndrew Thompson 340998971a7SAndrew Thompson static void 341998971a7SAndrew Thompson lacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 342998971a7SAndrew Thompson { 343998971a7SAndrew Thompson struct ifnet *ifp = lp->lp_ifp; 344998971a7SAndrew Thompson 345998971a7SAndrew Thompson /* Fill in the port index and system id (encoded as the MAC) */ 346998971a7SAndrew Thompson info->mi_rq_port = htons(ifp->if_index); 347998971a7SAndrew Thompson memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 348998971a7SAndrew Thompson info->mi_rq_xid = htonl(0); 349998971a7SAndrew Thompson } 350998971a7SAndrew Thompson 351b47888ceSAndrew Thompson static int 352b47888ceSAndrew Thompson lacp_xmit_lacpdu(struct lacp_port *lp) 353b47888ceSAndrew Thompson { 35418242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 355b47888ceSAndrew Thompson struct mbuf *m; 356b47888ceSAndrew Thompson struct lacpdu *du; 357b47888ceSAndrew Thompson int error; 358b47888ceSAndrew Thompson 3593de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 360b47888ceSAndrew Thompson 361eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 362b47888ceSAndrew Thompson if (m == NULL) { 363b47888ceSAndrew Thompson return (ENOMEM); 364b47888ceSAndrew Thompson } 365b47888ceSAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*du); 366b47888ceSAndrew Thompson 367b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 368b47888ceSAndrew Thompson memset(du, 0, sizeof(*du)); 369b47888ceSAndrew Thompson 370b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 371b47888ceSAndrew Thompson ETHER_ADDR_LEN); 37218242d3bSAndrew Thompson memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 373b47888ceSAndrew Thompson du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 374b47888ceSAndrew Thompson 375b47888ceSAndrew Thompson du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 376b47888ceSAndrew Thompson du->ldu_sph.sph_version = 1; 377b47888ceSAndrew Thompson 378b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 379b47888ceSAndrew Thompson du->ldu_actor = lp->lp_actor; 380b47888ceSAndrew Thompson 381b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 382b47888ceSAndrew Thompson sizeof(du->ldu_partner)); 383b47888ceSAndrew Thompson du->ldu_partner = lp->lp_partner; 384b47888ceSAndrew Thompson 385b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 386b47888ceSAndrew Thompson sizeof(du->ldu_collector)); 387b47888ceSAndrew Thompson du->ldu_collector.lci_maxdelay = 0; 388b47888ceSAndrew Thompson 389939a050aSHiroki Sato if (V_lacp_debug > 0) { 3905fc4c149SAndrew Thompson lacp_dprintf(lp, "lacpdu transmit\n"); 391b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 3925fc4c149SAndrew Thompson } 393b47888ceSAndrew Thompson 394b47888ceSAndrew Thompson m->m_flags |= M_MCAST; 395b47888ceSAndrew Thompson 396b47888ceSAndrew Thompson /* 397b47888ceSAndrew Thompson * XXX should use higher priority queue. 398b47888ceSAndrew Thompson * otherwise network congestion can break aggregation. 399b47888ceSAndrew Thompson */ 400b47888ceSAndrew Thompson 40118242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 402b47888ceSAndrew Thompson return (error); 403b47888ceSAndrew Thompson } 404b47888ceSAndrew Thompson 405998971a7SAndrew Thompson static int 406998971a7SAndrew Thompson lacp_xmit_marker(struct lacp_port *lp) 407998971a7SAndrew Thompson { 408998971a7SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 409998971a7SAndrew Thompson struct mbuf *m; 410998971a7SAndrew Thompson struct markerdu *mdu; 411998971a7SAndrew Thompson int error; 412998971a7SAndrew Thompson 4133de18008SAndrew Thompson LACP_LOCK_ASSERT(lp->lp_lsc); 414998971a7SAndrew Thompson 415eb1b1807SGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA); 416998971a7SAndrew Thompson if (m == NULL) { 417998971a7SAndrew Thompson return (ENOMEM); 418998971a7SAndrew Thompson } 419998971a7SAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*mdu); 420998971a7SAndrew Thompson 421998971a7SAndrew Thompson mdu = mtod(m, struct markerdu *); 422998971a7SAndrew Thompson memset(mdu, 0, sizeof(*mdu)); 423998971a7SAndrew Thompson 424998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 425998971a7SAndrew Thompson ETHER_ADDR_LEN); 426998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 427998971a7SAndrew Thompson mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 428998971a7SAndrew Thompson 429998971a7SAndrew Thompson mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 430998971a7SAndrew Thompson mdu->mdu_sph.sph_version = 1; 431998971a7SAndrew Thompson 432998971a7SAndrew Thompson /* Bump the transaction id and copy over the marker info */ 433998971a7SAndrew Thompson lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 434998971a7SAndrew Thompson TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 435998971a7SAndrew Thompson mdu->mdu_info = lp->lp_marker; 436998971a7SAndrew Thompson 437998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 438998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 439998971a7SAndrew Thompson ntohl(mdu->mdu_info.mi_rq_xid))); 440998971a7SAndrew Thompson 441998971a7SAndrew Thompson m->m_flags |= M_MCAST; 442998971a7SAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 443998971a7SAndrew Thompson return (error); 444998971a7SAndrew Thompson } 4453de18008SAndrew Thompson 446b47888ceSAndrew Thompson void 44718242d3bSAndrew Thompson lacp_linkstate(struct lagg_port *lgp) 448b47888ceSAndrew Thompson { 44918242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 4503de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 45118242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 452b47888ceSAndrew Thompson struct ifmediareq ifmr; 453b47888ceSAndrew Thompson int error = 0; 454b47888ceSAndrew Thompson u_int media; 455b47888ceSAndrew Thompson uint8_t old_state; 456b47888ceSAndrew Thompson uint16_t old_key; 457b47888ceSAndrew Thompson 458b47888ceSAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr)); 459b47888ceSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 460b47888ceSAndrew Thompson if (error != 0) 461b47888ceSAndrew Thompson return; 462b47888ceSAndrew Thompson 4633de18008SAndrew Thompson LACP_LOCK(lsc); 464b47888ceSAndrew Thompson media = ifmr.ifm_active; 465e3163ef6SAndrew Thompson LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 466e3163ef6SAndrew Thompson "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 467e3163ef6SAndrew Thompson (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 468b47888ceSAndrew Thompson old_state = lp->lp_state; 469b47888ceSAndrew Thompson old_key = lp->lp_key; 470b47888ceSAndrew Thompson 471b47888ceSAndrew Thompson lp->lp_media = media; 472e3163ef6SAndrew Thompson /* 473e3163ef6SAndrew Thompson * If the port is not an active full duplex Ethernet link then it can 474e3163ef6SAndrew Thompson * not be aggregated. 475e3163ef6SAndrew Thompson */ 476e3163ef6SAndrew Thompson if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 477e3163ef6SAndrew Thompson ifp->if_link_state != LINK_STATE_UP) { 478b47888ceSAndrew Thompson lacp_port_disable(lp); 479b47888ceSAndrew Thompson } else { 480b47888ceSAndrew Thompson lacp_port_enable(lp); 481b47888ceSAndrew Thompson } 482b47888ceSAndrew Thompson lp->lp_key = lacp_compose_key(lp); 483b47888ceSAndrew Thompson 484b47888ceSAndrew Thompson if (old_state != lp->lp_state || old_key != lp->lp_key) { 485b47888ceSAndrew Thompson LACP_DPRINTF((lp, "-> UNSELECTED\n")); 486b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 487b47888ceSAndrew Thompson } 4883de18008SAndrew Thompson LACP_UNLOCK(lsc); 489b47888ceSAndrew Thompson } 490b47888ceSAndrew Thompson 491b47888ceSAndrew Thompson static void 492b47888ceSAndrew Thompson lacp_tick(void *arg) 493b47888ceSAndrew Thompson { 494b47888ceSAndrew Thompson struct lacp_softc *lsc = arg; 495b47888ceSAndrew Thompson struct lacp_port *lp; 496b47888ceSAndrew Thompson 497b47888ceSAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 498b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 499b47888ceSAndrew Thompson continue; 500b47888ceSAndrew Thompson 501939a050aSHiroki Sato CURVNET_SET(lp->lp_ifp->if_vnet); 502b47888ceSAndrew Thompson lacp_run_timers(lp); 503b47888ceSAndrew Thompson 504b47888ceSAndrew Thompson lacp_select(lp); 505b47888ceSAndrew Thompson lacp_sm_mux(lp); 506b47888ceSAndrew Thompson lacp_sm_tx(lp); 507b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(lp); 508939a050aSHiroki Sato CURVNET_RESTORE(); 509b47888ceSAndrew Thompson } 510b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 511b47888ceSAndrew Thompson } 512b47888ceSAndrew Thompson 513b47888ceSAndrew Thompson int 51418242d3bSAndrew Thompson lacp_port_create(struct lagg_port *lgp) 515b47888ceSAndrew Thompson { 516ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 517ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 518b47888ceSAndrew Thompson struct lacp_port *lp; 51918242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 520b47888ceSAndrew Thompson struct sockaddr_dl sdl; 521b47888ceSAndrew Thompson struct ifmultiaddr *rifma = NULL; 522b47888ceSAndrew Thompson int error; 523b47888ceSAndrew Thompson 524b47888ceSAndrew Thompson boolean_t active = TRUE; /* XXX should be configurable */ 525b47888ceSAndrew Thompson boolean_t fast = FALSE; /* XXX should be configurable */ 526b47888ceSAndrew Thompson 52795fbe4d0SAlexander V. Chernikov link_init_sdl(ifp, (struct sockaddr *)&sdl, IFT_ETHER); 528b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 529b47888ceSAndrew Thompson 530b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 531b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 532b47888ceSAndrew Thompson error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 533b47888ceSAndrew Thompson if (error) { 534eade13f9SGleb Smirnoff printf("%s: ADDMULTI failed on %s\n", __func__, 535eade13f9SGleb Smirnoff lgp->lp_ifp->if_xname); 536b47888ceSAndrew Thompson return (error); 537b47888ceSAndrew Thompson } 538b47888ceSAndrew Thompson 539b47888ceSAndrew Thompson lp = malloc(sizeof(struct lacp_port), 540b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 541b47888ceSAndrew Thompson if (lp == NULL) 542b47888ceSAndrew Thompson return (ENOMEM); 543b47888ceSAndrew Thompson 5443de18008SAndrew Thompson LACP_LOCK(lsc); 5456900d0d3SGleb Smirnoff lgp->lp_psc = lp; 546b47888ceSAndrew Thompson lp->lp_ifp = ifp; 54718242d3bSAndrew Thompson lp->lp_lagg = lgp; 548b47888ceSAndrew Thompson lp->lp_lsc = lsc; 549d74fd345SAndrew Thompson lp->lp_ifma = rifma; 550b47888ceSAndrew Thompson 551b47888ceSAndrew Thompson LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 552b47888ceSAndrew Thompson 553b47888ceSAndrew Thompson lacp_fill_actorinfo(lp, &lp->lp_actor); 554998971a7SAndrew Thompson lacp_fill_markerinfo(lp, &lp->lp_marker); 555b47888ceSAndrew Thompson lp->lp_state = 556b47888ceSAndrew Thompson (active ? LACP_STATE_ACTIVITY : 0) | 557b47888ceSAndrew Thompson (fast ? LACP_STATE_TIMEOUT : 0); 558b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 559b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 5603de18008SAndrew Thompson LACP_UNLOCK(lsc); 5613de18008SAndrew Thompson lacp_linkstate(lgp); 562b47888ceSAndrew Thompson 563b47888ceSAndrew Thompson return (0); 564b47888ceSAndrew Thompson } 565b47888ceSAndrew Thompson 566b47888ceSAndrew Thompson void 56718242d3bSAndrew Thompson lacp_port_destroy(struct lagg_port *lgp) 568b47888ceSAndrew Thompson { 56918242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 5703de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 571d74fd345SAndrew Thompson int i; 572b47888ceSAndrew Thompson 5733de18008SAndrew Thompson LACP_LOCK(lsc); 574b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 575b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, i); 576b47888ceSAndrew Thompson } 577b47888ceSAndrew Thompson 578b47888ceSAndrew Thompson lacp_disable_collecting(lp); 579b47888ceSAndrew Thompson lacp_disable_distributing(lp); 580b47888ceSAndrew Thompson lacp_unselect(lp); 581b47888ceSAndrew Thompson 582108fe96aSAndrew Thompson /* The address may have already been removed by if_purgemaddrs() */ 583108fe96aSAndrew Thompson if (!lgp->lp_detaching) 584d74fd345SAndrew Thompson if_delmulti_ifma(lp->lp_ifma); 585b47888ceSAndrew Thompson 586b47888ceSAndrew Thompson LIST_REMOVE(lp, lp_next); 5873de18008SAndrew Thompson LACP_UNLOCK(lsc); 588b47888ceSAndrew Thompson free(lp, M_DEVBUF); 589b47888ceSAndrew Thompson } 590b47888ceSAndrew Thompson 591b3d37ca5SAndrew Thompson void 5926900d0d3SGleb Smirnoff lacp_req(struct lagg_softc *sc, void *data) 593b3d37ca5SAndrew Thompson { 594b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 595b3d37ca5SAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 596f544a748SAlan Somers struct lacp_aggregator *la; 597b3d37ca5SAndrew Thompson 598b3d37ca5SAndrew Thompson bzero(req, sizeof(struct lacp_opreq)); 599f544a748SAlan Somers 600f544a748SAlan Somers /* 601f544a748SAlan Somers * If the LACP softc is NULL, return with the opreq structure full of 602f544a748SAlan Somers * zeros. It is normal for the softc to be NULL while the lagg is 603f544a748SAlan Somers * being destroyed. 604f544a748SAlan Somers */ 605f544a748SAlan Somers if (NULL == lsc) 606f544a748SAlan Somers return; 607f544a748SAlan Somers 608f544a748SAlan Somers la = lsc->lsc_active_aggregator; 609f544a748SAlan Somers LACP_LOCK(lsc); 610b3d37ca5SAndrew Thompson if (la != NULL) { 611b3d37ca5SAndrew Thompson req->actor_prio = ntohs(la->la_actor.lip_systemid.lsi_prio); 612b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &la->la_actor.lip_systemid.lsi_mac, 613b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 614b3d37ca5SAndrew Thompson req->actor_key = ntohs(la->la_actor.lip_key); 615b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(la->la_actor.lip_portid.lpi_prio); 616b3d37ca5SAndrew Thompson req->actor_portno = ntohs(la->la_actor.lip_portid.lpi_portno); 617b3d37ca5SAndrew Thompson req->actor_state = la->la_actor.lip_state; 618b3d37ca5SAndrew Thompson 619b3d37ca5SAndrew Thompson req->partner_prio = ntohs(la->la_partner.lip_systemid.lsi_prio); 620b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &la->la_partner.lip_systemid.lsi_mac, 621b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 622b3d37ca5SAndrew Thompson req->partner_key = ntohs(la->la_partner.lip_key); 623b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(la->la_partner.lip_portid.lpi_prio); 624b3d37ca5SAndrew Thompson req->partner_portno = ntohs(la->la_partner.lip_portid.lpi_portno); 625b3d37ca5SAndrew Thompson req->partner_state = la->la_partner.lip_state; 626b3d37ca5SAndrew Thompson } 6273de18008SAndrew Thompson LACP_UNLOCK(lsc); 628b3d37ca5SAndrew Thompson } 629b3d37ca5SAndrew Thompson 630b3d37ca5SAndrew Thompson void 6316900d0d3SGleb Smirnoff lacp_portreq(struct lagg_port *lgp, void *data) 632b3d37ca5SAndrew Thompson { 633b3d37ca5SAndrew Thompson struct lacp_opreq *req = (struct lacp_opreq *)data; 634b3d37ca5SAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 6353de18008SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 636b3d37ca5SAndrew Thompson 6373de18008SAndrew Thompson LACP_LOCK(lsc); 638b3d37ca5SAndrew Thompson req->actor_prio = ntohs(lp->lp_actor.lip_systemid.lsi_prio); 639b3d37ca5SAndrew Thompson memcpy(&req->actor_mac, &lp->lp_actor.lip_systemid.lsi_mac, 640b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 641b3d37ca5SAndrew Thompson req->actor_key = ntohs(lp->lp_actor.lip_key); 642b3d37ca5SAndrew Thompson req->actor_portprio = ntohs(lp->lp_actor.lip_portid.lpi_prio); 643b3d37ca5SAndrew Thompson req->actor_portno = ntohs(lp->lp_actor.lip_portid.lpi_portno); 644b3d37ca5SAndrew Thompson req->actor_state = lp->lp_actor.lip_state; 645b3d37ca5SAndrew Thompson 646b3d37ca5SAndrew Thompson req->partner_prio = ntohs(lp->lp_partner.lip_systemid.lsi_prio); 647b3d37ca5SAndrew Thompson memcpy(&req->partner_mac, &lp->lp_partner.lip_systemid.lsi_mac, 648b3d37ca5SAndrew Thompson ETHER_ADDR_LEN); 649b3d37ca5SAndrew Thompson req->partner_key = ntohs(lp->lp_partner.lip_key); 650b3d37ca5SAndrew Thompson req->partner_portprio = ntohs(lp->lp_partner.lip_portid.lpi_prio); 651b3d37ca5SAndrew Thompson req->partner_portno = ntohs(lp->lp_partner.lip_portid.lpi_portno); 652b3d37ca5SAndrew Thompson req->partner_state = lp->lp_partner.lip_state; 6533de18008SAndrew Thompson LACP_UNLOCK(lsc); 654b3d37ca5SAndrew Thompson } 655b3d37ca5SAndrew Thompson 656b47888ceSAndrew Thompson static void 657b47888ceSAndrew Thompson lacp_disable_collecting(struct lacp_port *lp) 658b47888ceSAndrew Thompson { 659b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting disabled\n")); 660b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_COLLECTING; 661b47888ceSAndrew Thompson } 662b47888ceSAndrew Thompson 663b47888ceSAndrew Thompson static void 664b47888ceSAndrew Thompson lacp_enable_collecting(struct lacp_port *lp) 665b47888ceSAndrew Thompson { 666b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting enabled\n")); 667b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_COLLECTING; 668b47888ceSAndrew Thompson } 669b47888ceSAndrew Thompson 670b47888ceSAndrew Thompson static void 671b47888ceSAndrew Thompson lacp_disable_distributing(struct lacp_port *lp) 672b47888ceSAndrew Thompson { 673b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 674b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 67531402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 676b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 677b47888ceSAndrew Thompson 6783de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 679b47888ceSAndrew Thompson 680b47888ceSAndrew Thompson if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 681b47888ceSAndrew Thompson return; 682b47888ceSAndrew Thompson } 683b47888ceSAndrew Thompson 684b47888ceSAndrew Thompson KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 685b47888ceSAndrew Thompson KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 686b47888ceSAndrew Thompson KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 687b47888ceSAndrew Thompson 688b47888ceSAndrew Thompson LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 689b47888ceSAndrew Thompson "nports %d -> %d\n", 690b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 691b47888ceSAndrew Thompson la->la_nports, la->la_nports - 1)); 692b47888ceSAndrew Thompson 693b47888ceSAndrew Thompson TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 694b47888ceSAndrew Thompson la->la_nports--; 69531402c27SAdrian Chadd sc->sc_active = la->la_nports; 696b47888ceSAndrew Thompson 6973de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 698b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 6993de18008SAndrew Thompson lacp_select_active_aggregator(lsc); 7003de18008SAndrew Thompson /* regenerate the port map, the active aggregator has changed */ 7013de18008SAndrew Thompson lacp_update_portmap(lsc); 7023de18008SAndrew Thompson } 703b47888ceSAndrew Thompson 704b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 705b47888ceSAndrew Thompson } 706b47888ceSAndrew Thompson 707b47888ceSAndrew Thompson static void 708b47888ceSAndrew Thompson lacp_enable_distributing(struct lacp_port *lp) 709b47888ceSAndrew Thompson { 710b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 711b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 71231402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 713b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 714b47888ceSAndrew Thompson 7153de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 716b47888ceSAndrew Thompson 717b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 718b47888ceSAndrew Thompson return; 719b47888ceSAndrew Thompson } 720b47888ceSAndrew Thompson 721b47888ceSAndrew Thompson LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 722b47888ceSAndrew Thompson "nports %d -> %d\n", 723b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 724b47888ceSAndrew Thompson la->la_nports, la->la_nports + 1)); 725b47888ceSAndrew Thompson 726b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 727b47888ceSAndrew Thompson TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 728b47888ceSAndrew Thompson la->la_nports++; 72931402c27SAdrian Chadd sc->sc_active = la->la_nports; 730b47888ceSAndrew Thompson 731b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DISTRIBUTING; 732b47888ceSAndrew Thompson 7333de18008SAndrew Thompson if (lsc->lsc_active_aggregator == la) { 7343de18008SAndrew Thompson lacp_suppress_distributing(lsc, la); 7353de18008SAndrew Thompson lacp_update_portmap(lsc); 7363de18008SAndrew Thompson } else 7373de18008SAndrew Thompson /* try to become the active aggregator */ 738b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 739b47888ceSAndrew Thompson } 740b47888ceSAndrew Thompson 741b47888ceSAndrew Thompson static void 742b47888ceSAndrew Thompson lacp_transit_expire(void *vp) 743b47888ceSAndrew Thompson { 744b47888ceSAndrew Thompson struct lacp_softc *lsc = vp; 745b47888ceSAndrew Thompson 7463de18008SAndrew Thompson LACP_LOCK_ASSERT(lsc); 7473de18008SAndrew Thompson 7485fc4c149SAndrew Thompson LACP_TRACE(NULL); 7495fc4c149SAndrew Thompson 750b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 751b47888ceSAndrew Thompson } 752b47888ceSAndrew Thompson 75309c7577eSGleb Smirnoff void 754ec32b37eSAndrew Thompson lacp_attach(struct lagg_softc *sc) 755b47888ceSAndrew Thompson { 756b47888ceSAndrew Thompson struct lacp_softc *lsc; 757b47888ceSAndrew Thompson 75809c7577eSGleb Smirnoff lsc = malloc(sizeof(struct lacp_softc), M_DEVBUF, M_WAITOK | M_ZERO); 759b47888ceSAndrew Thompson 7606900d0d3SGleb Smirnoff sc->sc_psc = lsc; 761ec32b37eSAndrew Thompson lsc->lsc_softc = sc; 762b47888ceSAndrew Thompson 763b47888ceSAndrew Thompson lsc->lsc_hashkey = arc4random(); 764b47888ceSAndrew Thompson lsc->lsc_active_aggregator = NULL; 76549de4f22SAdrian Chadd lsc->lsc_strict_mode = 1; 7663de18008SAndrew Thompson LACP_LOCK_INIT(lsc); 767b47888ceSAndrew Thompson TAILQ_INIT(&lsc->lsc_aggregators); 768b47888ceSAndrew Thompson LIST_INIT(&lsc->lsc_ports); 769b47888ceSAndrew Thompson 7703de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0); 7713de18008SAndrew Thompson callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0); 772b47888ceSAndrew Thompson 77318242d3bSAndrew Thompson /* if the lagg is already up then do the same */ 774ec32b37eSAndrew Thompson if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 775ec32b37eSAndrew Thompson lacp_init(sc); 776b47888ceSAndrew Thompson } 777b47888ceSAndrew Thompson 778b1bbc5b3SGleb Smirnoff void 77909c7577eSGleb Smirnoff lacp_detach(void *psc) 780b47888ceSAndrew Thompson { 78109c7577eSGleb Smirnoff struct lacp_softc *lsc = (struct lacp_softc *)psc; 782b47888ceSAndrew Thompson 783b47888ceSAndrew Thompson KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 784b47888ceSAndrew Thompson ("aggregators still active")); 785b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator == NULL, 786b47888ceSAndrew Thompson ("aggregator still attached")); 787b47888ceSAndrew Thompson 788b47888ceSAndrew Thompson callout_drain(&lsc->lsc_transit_callout); 789b47888ceSAndrew Thompson callout_drain(&lsc->lsc_callout); 790b47888ceSAndrew Thompson 7913de18008SAndrew Thompson LACP_LOCK_DESTROY(lsc); 792b47888ceSAndrew Thompson free(lsc, M_DEVBUF); 793b47888ceSAndrew Thompson } 794b47888ceSAndrew Thompson 795b47888ceSAndrew Thompson void 796ec32b37eSAndrew Thompson lacp_init(struct lagg_softc *sc) 797b47888ceSAndrew Thompson { 798ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 799b47888ceSAndrew Thompson 8003de18008SAndrew Thompson LACP_LOCK(lsc); 801b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 8023de18008SAndrew Thompson LACP_UNLOCK(lsc); 803b47888ceSAndrew Thompson } 804b47888ceSAndrew Thompson 805b47888ceSAndrew Thompson void 806ec32b37eSAndrew Thompson lacp_stop(struct lagg_softc *sc) 807b47888ceSAndrew Thompson { 808ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 809b47888ceSAndrew Thompson 8103de18008SAndrew Thompson LACP_LOCK(lsc); 811b47888ceSAndrew Thompson callout_stop(&lsc->lsc_transit_callout); 812b47888ceSAndrew Thompson callout_stop(&lsc->lsc_callout); 8133de18008SAndrew Thompson LACP_UNLOCK(lsc); 814b47888ceSAndrew Thompson } 815b47888ceSAndrew Thompson 81618242d3bSAndrew Thompson struct lagg_port * 817ec32b37eSAndrew Thompson lacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m) 818b47888ceSAndrew Thompson { 819ec32b37eSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(sc); 8203de18008SAndrew Thompson struct lacp_portmap *pm; 821b47888ceSAndrew Thompson struct lacp_port *lp; 822b47888ceSAndrew Thompson uint32_t hash; 823b47888ceSAndrew Thompson 824b47888ceSAndrew Thompson if (__predict_false(lsc->lsc_suppress_distributing)) { 825b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 826b47888ceSAndrew Thompson return (NULL); 827b47888ceSAndrew Thompson } 828b47888ceSAndrew Thompson 8293de18008SAndrew Thompson pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 8303de18008SAndrew Thompson if (pm->pm_count == 0) { 831b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 832b47888ceSAndrew Thompson return (NULL); 833b47888ceSAndrew Thompson } 834b47888ceSAndrew Thompson 835939a050aSHiroki Sato if ((sc->sc_opts & LAGG_OPT_USE_FLOWID) && (m->m_flags & M_FLOWID)) 8361a8959daSScott Long hash = m->m_pkthdr.flowid >> sc->flowid_shift; 8375c6026e9SAndrew Thompson else 83886f67641SAndrew Thompson hash = lagg_hashmbuf(sc, m, lsc->lsc_hashkey); 8393de18008SAndrew Thompson hash %= pm->pm_count; 8403de18008SAndrew Thompson lp = pm->pm_map[hash]; 841b47888ceSAndrew Thompson 842b47888ceSAndrew Thompson KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 843b47888ceSAndrew Thompson ("aggregated port is not distributing")); 844b47888ceSAndrew Thompson 84518242d3bSAndrew Thompson return (lp->lp_lagg); 846b47888ceSAndrew Thompson } 847b47888ceSAndrew Thompson /* 848b47888ceSAndrew Thompson * lacp_suppress_distributing: drop transmit packets for a while 849b47888ceSAndrew Thompson * to preserve packet ordering. 850b47888ceSAndrew Thompson */ 851b47888ceSAndrew Thompson 852b47888ceSAndrew Thompson static void 853b47888ceSAndrew Thompson lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 854b47888ceSAndrew Thompson { 855998971a7SAndrew Thompson struct lacp_port *lp; 856998971a7SAndrew Thompson 857b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 858b47888ceSAndrew Thompson return; 859b47888ceSAndrew Thompson } 860b47888ceSAndrew Thompson 8615fc4c149SAndrew Thompson LACP_TRACE(NULL); 8625fc4c149SAndrew Thompson 863b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = TRUE; 864998971a7SAndrew Thompson 865998971a7SAndrew Thompson /* send a marker frame down each port to verify the queues are empty */ 866998971a7SAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 867998971a7SAndrew Thompson lp->lp_flags |= LACP_PORT_MARK; 868998971a7SAndrew Thompson lacp_xmit_marker(lp); 869998971a7SAndrew Thompson } 870998971a7SAndrew Thompson 871998971a7SAndrew Thompson /* set a timeout for the marker frames */ 872b47888ceSAndrew Thompson callout_reset(&lsc->lsc_transit_callout, 873b47888ceSAndrew Thompson LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 874b47888ceSAndrew Thompson } 875b47888ceSAndrew Thompson 876b47888ceSAndrew Thompson static int 877b47888ceSAndrew Thompson lacp_compare_peerinfo(const struct lacp_peerinfo *a, 878b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 879b47888ceSAndrew Thompson { 880b47888ceSAndrew Thompson return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 881b47888ceSAndrew Thompson } 882b47888ceSAndrew Thompson 883b47888ceSAndrew Thompson static int 884b47888ceSAndrew Thompson lacp_compare_systemid(const struct lacp_systemid *a, 885b47888ceSAndrew Thompson const struct lacp_systemid *b) 886b47888ceSAndrew Thompson { 887b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 888b47888ceSAndrew Thompson } 889b47888ceSAndrew Thompson 890b47888ceSAndrew Thompson #if 0 /* unused */ 891b47888ceSAndrew Thompson static int 892b47888ceSAndrew Thompson lacp_compare_portid(const struct lacp_portid *a, 893b47888ceSAndrew Thompson const struct lacp_portid *b) 894b47888ceSAndrew Thompson { 895b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 896b47888ceSAndrew Thompson } 897b47888ceSAndrew Thompson #endif 898b47888ceSAndrew Thompson 899b47888ceSAndrew Thompson static uint64_t 900b47888ceSAndrew Thompson lacp_aggregator_bandwidth(struct lacp_aggregator *la) 901b47888ceSAndrew Thompson { 902b47888ceSAndrew Thompson struct lacp_port *lp; 903b47888ceSAndrew Thompson uint64_t speed; 904b47888ceSAndrew Thompson 905b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 906b47888ceSAndrew Thompson if (lp == NULL) { 907b47888ceSAndrew Thompson return (0); 908b47888ceSAndrew Thompson } 909b47888ceSAndrew Thompson 910b47888ceSAndrew Thompson speed = ifmedia_baudrate(lp->lp_media); 911b47888ceSAndrew Thompson speed *= la->la_nports; 912b47888ceSAndrew Thompson if (speed == 0) { 913b47888ceSAndrew Thompson LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 914b47888ceSAndrew Thompson lp->lp_media, la->la_nports)); 915b47888ceSAndrew Thompson } 916b47888ceSAndrew Thompson 917b47888ceSAndrew Thompson return (speed); 918b47888ceSAndrew Thompson } 919b47888ceSAndrew Thompson 920b47888ceSAndrew Thompson /* 921b47888ceSAndrew Thompson * lacp_select_active_aggregator: select an aggregator to be used to transmit 92218242d3bSAndrew Thompson * packets from lagg(4) interface. 923b47888ceSAndrew Thompson */ 924b47888ceSAndrew Thompson 925b47888ceSAndrew Thompson static void 926b47888ceSAndrew Thompson lacp_select_active_aggregator(struct lacp_softc *lsc) 927b47888ceSAndrew Thompson { 928b47888ceSAndrew Thompson struct lacp_aggregator *la; 929b47888ceSAndrew Thompson struct lacp_aggregator *best_la = NULL; 930b47888ceSAndrew Thompson uint64_t best_speed = 0; 931b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 932b47888ceSAndrew Thompson 9335fc4c149SAndrew Thompson LACP_TRACE(NULL); 934b47888ceSAndrew Thompson 935b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 936b47888ceSAndrew Thompson uint64_t speed; 937b47888ceSAndrew Thompson 938b47888ceSAndrew Thompson if (la->la_nports == 0) { 939b47888ceSAndrew Thompson continue; 940b47888ceSAndrew Thompson } 941b47888ceSAndrew Thompson 942b47888ceSAndrew Thompson speed = lacp_aggregator_bandwidth(la); 943b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 944b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 945b47888ceSAndrew Thompson speed, la->la_nports)); 946fe45e65fSAndrew Thompson 9476900d0d3SGleb Smirnoff /* 9486900d0d3SGleb Smirnoff * This aggregator is chosen if the partner has a better 9496900d0d3SGleb Smirnoff * system priority or, the total aggregated speed is higher 950fe45e65fSAndrew Thompson * or, it is already the chosen aggregator 951fe45e65fSAndrew Thompson */ 952fe45e65fSAndrew Thompson if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 953fe45e65fSAndrew Thompson LACP_SYS_PRI(best_la->la_partner)) || 954fe45e65fSAndrew Thompson speed > best_speed || 955b47888ceSAndrew Thompson (speed == best_speed && 956b47888ceSAndrew Thompson la == lsc->lsc_active_aggregator)) { 957b47888ceSAndrew Thompson best_la = la; 958b47888ceSAndrew Thompson best_speed = speed; 959b47888ceSAndrew Thompson } 960b47888ceSAndrew Thompson } 961b47888ceSAndrew Thompson 962b47888ceSAndrew Thompson KASSERT(best_la == NULL || best_la->la_nports > 0, 963b47888ceSAndrew Thompson ("invalid aggregator refcnt")); 964b47888ceSAndrew Thompson KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 965b47888ceSAndrew Thompson ("invalid aggregator list")); 966b47888ceSAndrew Thompson 967b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 968b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator changed\n")); 969b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "old %s\n", 970b47888ceSAndrew Thompson lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 971b47888ceSAndrew Thompson buf, sizeof(buf)))); 972b47888ceSAndrew Thompson } else { 973b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator not changed\n")); 974b47888ceSAndrew Thompson } 975b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "new %s\n", 976b47888ceSAndrew Thompson lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 977b47888ceSAndrew Thompson 978b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 979b47888ceSAndrew Thompson lsc->lsc_active_aggregator = best_la; 9803de18008SAndrew Thompson lacp_update_portmap(lsc); 981b47888ceSAndrew Thompson if (best_la) { 982b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, best_la); 983b47888ceSAndrew Thompson } 984b47888ceSAndrew Thompson } 985b47888ceSAndrew Thompson } 986b47888ceSAndrew Thompson 9873de18008SAndrew Thompson /* 9883de18008SAndrew Thompson * Updated the inactive portmap array with the new list of ports and 9893de18008SAndrew Thompson * make it live. 9903de18008SAndrew Thompson */ 9913de18008SAndrew Thompson static void 9923de18008SAndrew Thompson lacp_update_portmap(struct lacp_softc *lsc) 9933de18008SAndrew Thompson { 99431402c27SAdrian Chadd struct lagg_softc *sc = lsc->lsc_softc; 9953de18008SAndrew Thompson struct lacp_aggregator *la; 9963de18008SAndrew Thompson struct lacp_portmap *p; 9973de18008SAndrew Thompson struct lacp_port *lp; 99831402c27SAdrian Chadd uint64_t speed; 9993de18008SAndrew Thompson u_int newmap; 10003de18008SAndrew Thompson int i; 10013de18008SAndrew Thompson 10023de18008SAndrew Thompson newmap = lsc->lsc_activemap == 0 ? 1 : 0; 10033de18008SAndrew Thompson p = &lsc->lsc_pmap[newmap]; 10043de18008SAndrew Thompson la = lsc->lsc_active_aggregator; 100531402c27SAdrian Chadd speed = 0; 10063de18008SAndrew Thompson bzero(p, sizeof(struct lacp_portmap)); 10073de18008SAndrew Thompson 10083de18008SAndrew Thompson if (la != NULL && la->la_nports > 0) { 10093de18008SAndrew Thompson p->pm_count = la->la_nports; 10103de18008SAndrew Thompson i = 0; 10113de18008SAndrew Thompson TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q) 10123de18008SAndrew Thompson p->pm_map[i++] = lp; 10133de18008SAndrew Thompson KASSERT(i == p->pm_count, ("Invalid port count")); 101431402c27SAdrian Chadd speed = lacp_aggregator_bandwidth(la); 10153de18008SAndrew Thompson } 101631402c27SAdrian Chadd sc->sc_ifp->if_baudrate = speed; 10173de18008SAndrew Thompson 10183de18008SAndrew Thompson /* switch the active portmap over */ 10193de18008SAndrew Thompson atomic_store_rel_int(&lsc->lsc_activemap, newmap); 10203de18008SAndrew Thompson LACP_DPRINTF((NULL, "Set table %d with %d ports\n", 10213de18008SAndrew Thompson lsc->lsc_activemap, 10223de18008SAndrew Thompson lsc->lsc_pmap[lsc->lsc_activemap].pm_count)); 10233de18008SAndrew Thompson } 10243de18008SAndrew Thompson 1025b47888ceSAndrew Thompson static uint16_t 1026b47888ceSAndrew Thompson lacp_compose_key(struct lacp_port *lp) 1027b47888ceSAndrew Thompson { 102818242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 1029ec32b37eSAndrew Thompson struct lagg_softc *sc = lgp->lp_softc; 1030b47888ceSAndrew Thompson u_int media = lp->lp_media; 1031b47888ceSAndrew Thompson uint16_t key; 1032b47888ceSAndrew Thompson 1033b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 1034b47888ceSAndrew Thompson 1035b47888ceSAndrew Thompson /* 1036b47888ceSAndrew Thompson * non-aggregatable links should have unique keys. 1037b47888ceSAndrew Thompson * 1038b47888ceSAndrew Thompson * XXX this isn't really unique as if_index is 16 bit. 1039b47888ceSAndrew Thompson */ 1040b47888ceSAndrew Thompson 1041b47888ceSAndrew Thompson /* bit 0..14: (some bits of) if_index of this port */ 1042b47888ceSAndrew Thompson key = lp->lp_ifp->if_index; 1043b47888ceSAndrew Thompson /* bit 15: 1 */ 1044b47888ceSAndrew Thompson key |= 0x8000; 1045b47888ceSAndrew Thompson } else { 1046b47888ceSAndrew Thompson u_int subtype = IFM_SUBTYPE(media); 1047b47888ceSAndrew Thompson 1048e3163ef6SAndrew Thompson KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1049e3163ef6SAndrew Thompson KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1050b47888ceSAndrew Thompson 1051d5773da8SAndrey V. Elsukov /* bit 0..4: IFM_SUBTYPE modulo speed */ 1052d5773da8SAndrey V. Elsukov switch (subtype) { 1053d5773da8SAndrey V. Elsukov case IFM_10_T: 1054d5773da8SAndrey V. Elsukov case IFM_10_2: 1055d5773da8SAndrey V. Elsukov case IFM_10_5: 1056d5773da8SAndrey V. Elsukov case IFM_10_STP: 1057d5773da8SAndrey V. Elsukov case IFM_10_FL: 1058d5773da8SAndrey V. Elsukov key = IFM_10_T; 1059d5773da8SAndrey V. Elsukov break; 1060d5773da8SAndrey V. Elsukov case IFM_100_TX: 1061d5773da8SAndrey V. Elsukov case IFM_100_FX: 1062d5773da8SAndrey V. Elsukov case IFM_100_T4: 1063d5773da8SAndrey V. Elsukov case IFM_100_VG: 1064d5773da8SAndrey V. Elsukov case IFM_100_T2: 1065d5773da8SAndrey V. Elsukov key = IFM_100_TX; 1066d5773da8SAndrey V. Elsukov break; 1067d5773da8SAndrey V. Elsukov case IFM_1000_SX: 1068d5773da8SAndrey V. Elsukov case IFM_1000_LX: 1069d5773da8SAndrey V. Elsukov case IFM_1000_CX: 1070d5773da8SAndrey V. Elsukov case IFM_1000_T: 1071d5773da8SAndrey V. Elsukov key = IFM_1000_SX; 1072d5773da8SAndrey V. Elsukov break; 1073d5773da8SAndrey V. Elsukov case IFM_10G_LR: 1074d5773da8SAndrey V. Elsukov case IFM_10G_SR: 1075d5773da8SAndrey V. Elsukov case IFM_10G_CX4: 1076d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX: 1077d5773da8SAndrey V. Elsukov case IFM_10G_TWINAX_LONG: 1078d5773da8SAndrey V. Elsukov case IFM_10G_LRM: 1079d5773da8SAndrey V. Elsukov case IFM_10G_T: 1080d5773da8SAndrey V. Elsukov key = IFM_10G_LR; 1081d5773da8SAndrey V. Elsukov break; 1082d5773da8SAndrey V. Elsukov case IFM_40G_CR4: 1083d5773da8SAndrey V. Elsukov case IFM_40G_SR4: 1084d5773da8SAndrey V. Elsukov case IFM_40G_LR4: 1085d5773da8SAndrey V. Elsukov key = IFM_40G_CR4; 1086d5773da8SAndrey V. Elsukov break; 1087d5773da8SAndrey V. Elsukov default: 1088b47888ceSAndrew Thompson key = subtype; 1089d5773da8SAndrey V. Elsukov } 109018242d3bSAndrew Thompson /* bit 5..14: (some bits of) if_index of lagg device */ 1091ec32b37eSAndrew Thompson key |= 0x7fe0 & ((sc->sc_ifp->if_index) << 5); 1092b47888ceSAndrew Thompson /* bit 15: 0 */ 1093b47888ceSAndrew Thompson } 1094b47888ceSAndrew Thompson return (htons(key)); 1095b47888ceSAndrew Thompson } 1096b47888ceSAndrew Thompson 1097b47888ceSAndrew Thompson static void 1098b47888ceSAndrew Thompson lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1099b47888ceSAndrew Thompson { 1100b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1101b47888ceSAndrew Thompson 1102b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1103b47888ceSAndrew Thompson __func__, 1104b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1105b47888ceSAndrew Thompson buf, sizeof(buf)), 1106b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt + 1)); 1107b47888ceSAndrew Thompson 1108b47888ceSAndrew Thompson KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1109b47888ceSAndrew Thompson la->la_refcnt++; 1110b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1111b47888ceSAndrew Thompson } 1112b47888ceSAndrew Thompson 1113b47888ceSAndrew Thompson static void 1114b47888ceSAndrew Thompson lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1115b47888ceSAndrew Thompson { 1116b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1117b47888ceSAndrew Thompson 1118b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1119b47888ceSAndrew Thompson __func__, 1120b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1121b47888ceSAndrew Thompson buf, sizeof(buf)), 1122b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt - 1)); 1123b47888ceSAndrew Thompson 1124b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1125b47888ceSAndrew Thompson la->la_refcnt--; 1126b47888ceSAndrew Thompson if (la->la_refcnt > 0) { 1127b47888ceSAndrew Thompson return; 1128b47888ceSAndrew Thompson } 1129b47888ceSAndrew Thompson 1130b47888ceSAndrew Thompson KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1131b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1132b47888ceSAndrew Thompson 1133b47888ceSAndrew Thompson TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1134b47888ceSAndrew Thompson 1135b47888ceSAndrew Thompson free(la, M_DEVBUF); 1136b47888ceSAndrew Thompson } 1137b47888ceSAndrew Thompson 1138b47888ceSAndrew Thompson /* 1139b47888ceSAndrew Thompson * lacp_aggregator_get: allocate an aggregator. 1140b47888ceSAndrew Thompson */ 1141b47888ceSAndrew Thompson 1142b47888ceSAndrew Thompson static struct lacp_aggregator * 1143b47888ceSAndrew Thompson lacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1144b47888ceSAndrew Thompson { 1145b47888ceSAndrew Thompson struct lacp_aggregator *la; 1146b47888ceSAndrew Thompson 1147b47888ceSAndrew Thompson la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1148b47888ceSAndrew Thompson if (la) { 1149b47888ceSAndrew Thompson la->la_refcnt = 1; 1150b47888ceSAndrew Thompson la->la_nports = 0; 1151b47888ceSAndrew Thompson TAILQ_INIT(&la->la_ports); 1152b47888ceSAndrew Thompson la->la_pending = 0; 1153b47888ceSAndrew Thompson TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1154b47888ceSAndrew Thompson } 1155b47888ceSAndrew Thompson 1156b47888ceSAndrew Thompson return (la); 1157b47888ceSAndrew Thompson } 1158b47888ceSAndrew Thompson 1159b47888ceSAndrew Thompson /* 1160b47888ceSAndrew Thompson * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1161b47888ceSAndrew Thompson */ 1162b47888ceSAndrew Thompson 1163b47888ceSAndrew Thompson static void 1164b47888ceSAndrew Thompson lacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1165b47888ceSAndrew Thompson { 1166b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1167b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1168b47888ceSAndrew Thompson 1169b47888ceSAndrew Thompson la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1170b47888ceSAndrew Thompson } 1171b47888ceSAndrew Thompson 1172b47888ceSAndrew Thompson static void 1173b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1174b47888ceSAndrew Thompson const struct lacp_peerinfo *lpi_port) 1175b47888ceSAndrew Thompson { 1176b47888ceSAndrew Thompson memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1177b47888ceSAndrew Thompson lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1178b47888ceSAndrew Thompson lpi_aggr->lip_key = lpi_port->lip_key; 1179b47888ceSAndrew Thompson } 1180b47888ceSAndrew Thompson 1181b47888ceSAndrew Thompson /* 1182b47888ceSAndrew Thompson * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1183b47888ceSAndrew Thompson */ 1184b47888ceSAndrew Thompson 1185b47888ceSAndrew Thompson static int 1186b47888ceSAndrew Thompson lacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1187b47888ceSAndrew Thompson const struct lacp_port *lp) 1188b47888ceSAndrew Thompson { 1189b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1190b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1191b47888ceSAndrew Thompson return (0); 1192b47888ceSAndrew Thompson } 1193b47888ceSAndrew Thompson 1194b47888ceSAndrew Thompson if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1195b47888ceSAndrew Thompson return (0); 1196b47888ceSAndrew Thompson } 1197b47888ceSAndrew Thompson 1198b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1199b47888ceSAndrew Thompson return (0); 1200b47888ceSAndrew Thompson } 1201b47888ceSAndrew Thompson 1202b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1203b47888ceSAndrew Thompson return (0); 1204b47888ceSAndrew Thompson } 1205b47888ceSAndrew Thompson 1206b47888ceSAndrew Thompson return (1); 1207b47888ceSAndrew Thompson } 1208b47888ceSAndrew Thompson 1209b47888ceSAndrew Thompson static int 1210b47888ceSAndrew Thompson lacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1211b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 1212b47888ceSAndrew Thompson { 1213b47888ceSAndrew Thompson if (memcmp(&a->lip_systemid, &b->lip_systemid, 1214b47888ceSAndrew Thompson sizeof(a->lip_systemid))) { 1215b47888ceSAndrew Thompson return (0); 1216b47888ceSAndrew Thompson } 1217b47888ceSAndrew Thompson 1218b47888ceSAndrew Thompson if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1219b47888ceSAndrew Thompson return (0); 1220b47888ceSAndrew Thompson } 1221b47888ceSAndrew Thompson 1222b47888ceSAndrew Thompson return (1); 1223b47888ceSAndrew Thompson } 1224b47888ceSAndrew Thompson 1225b47888ceSAndrew Thompson static void 1226b47888ceSAndrew Thompson lacp_port_enable(struct lacp_port *lp) 1227b47888ceSAndrew Thompson { 1228b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_AGGREGATION; 1229b47888ceSAndrew Thompson } 1230b47888ceSAndrew Thompson 1231b47888ceSAndrew Thompson static void 1232b47888ceSAndrew Thompson lacp_port_disable(struct lacp_port *lp) 1233b47888ceSAndrew Thompson { 1234b47888ceSAndrew Thompson lacp_set_mux(lp, LACP_MUX_DETACHED); 1235b47888ceSAndrew Thompson 1236b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_AGGREGATION; 1237b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1238b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1239b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1240b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1241b47888ceSAndrew Thompson } 1242b47888ceSAndrew Thompson 1243b47888ceSAndrew Thompson /* 1244b47888ceSAndrew Thompson * lacp_select: select an aggregator. create one if necessary. 1245b47888ceSAndrew Thompson */ 1246b47888ceSAndrew Thompson static void 1247b47888ceSAndrew Thompson lacp_select(struct lacp_port *lp) 1248b47888ceSAndrew Thompson { 1249b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1250b47888ceSAndrew Thompson struct lacp_aggregator *la; 1251b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1252b47888ceSAndrew Thompson 1253b47888ceSAndrew Thompson if (lp->lp_aggregator) { 1254b47888ceSAndrew Thompson return; 1255b47888ceSAndrew Thompson } 1256b47888ceSAndrew Thompson 1257b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1258b47888ceSAndrew Thompson ("timer_wait_while still active")); 1259b47888ceSAndrew Thompson 1260b47888ceSAndrew Thompson LACP_DPRINTF((lp, "port lagid=%s\n", 1261b47888ceSAndrew Thompson lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1262b47888ceSAndrew Thompson buf, sizeof(buf)))); 1263b47888ceSAndrew Thompson 1264b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1265b47888ceSAndrew Thompson if (lacp_aggregator_is_compatible(la, lp)) { 1266b47888ceSAndrew Thompson break; 1267b47888ceSAndrew Thompson } 1268b47888ceSAndrew Thompson } 1269b47888ceSAndrew Thompson 1270b47888ceSAndrew Thompson if (la == NULL) { 1271b47888ceSAndrew Thompson la = lacp_aggregator_get(lsc, lp); 1272b47888ceSAndrew Thompson if (la == NULL) { 1273b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator creation failed\n")); 1274b47888ceSAndrew Thompson 1275b47888ceSAndrew Thompson /* 1276b47888ceSAndrew Thompson * will retry on the next tick. 1277b47888ceSAndrew Thompson */ 1278b47888ceSAndrew Thompson 1279b47888ceSAndrew Thompson return; 1280b47888ceSAndrew Thompson } 1281b47888ceSAndrew Thompson lacp_fill_aggregator_id(la, lp); 1282b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator created\n")); 1283b47888ceSAndrew Thompson } else { 1284b47888ceSAndrew Thompson LACP_DPRINTF((lp, "compatible aggregator found\n")); 12853de18008SAndrew Thompson if (la->la_refcnt == LACP_MAX_PORTS) 12863de18008SAndrew Thompson return; 1287b47888ceSAndrew Thompson lacp_aggregator_addref(lsc, la); 1288b47888ceSAndrew Thompson } 1289b47888ceSAndrew Thompson 1290b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1291b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1292b47888ceSAndrew Thompson buf, sizeof(buf)))); 1293b47888ceSAndrew Thompson 1294b47888ceSAndrew Thompson lp->lp_aggregator = la; 1295b47888ceSAndrew Thompson lp->lp_selected = LACP_SELECTED; 1296b47888ceSAndrew Thompson } 1297b47888ceSAndrew Thompson 1298b47888ceSAndrew Thompson /* 1299b47888ceSAndrew Thompson * lacp_unselect: finish unselect/detach process. 1300b47888ceSAndrew Thompson */ 1301b47888ceSAndrew Thompson 1302b47888ceSAndrew Thompson static void 1303b47888ceSAndrew Thompson lacp_unselect(struct lacp_port *lp) 1304b47888ceSAndrew Thompson { 1305b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1306b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1307b47888ceSAndrew Thompson 1308b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1309b47888ceSAndrew Thompson ("timer_wait_while still active")); 1310b47888ceSAndrew Thompson 1311b47888ceSAndrew Thompson if (la == NULL) { 1312b47888ceSAndrew Thompson return; 1313b47888ceSAndrew Thompson } 1314b47888ceSAndrew Thompson 1315b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 1316b47888ceSAndrew Thompson lacp_aggregator_delref(lsc, la); 1317b47888ceSAndrew Thompson } 1318b47888ceSAndrew Thompson 1319b47888ceSAndrew Thompson /* mux machine */ 1320b47888ceSAndrew Thompson 1321b47888ceSAndrew Thompson static void 1322b47888ceSAndrew Thompson lacp_sm_mux(struct lacp_port *lp) 1323b47888ceSAndrew Thompson { 132431402c27SAdrian Chadd struct lagg_port *lgp = lp->lp_lagg; 132531402c27SAdrian Chadd struct lagg_softc *sc = lgp->lp_softc; 1326b47888ceSAndrew Thompson enum lacp_mux_state new_state; 1327b47888ceSAndrew Thompson boolean_t p_sync = 1328b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1329b47888ceSAndrew Thompson boolean_t p_collecting = 1330b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1331b47888ceSAndrew Thompson enum lacp_selected selected = lp->lp_selected; 1332b47888ceSAndrew Thompson struct lacp_aggregator *la; 1333b47888ceSAndrew Thompson 1334939a050aSHiroki Sato if (V_lacp_debug > 1) 133531402c27SAdrian Chadd lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, " 133631402c27SAdrian Chadd "p_sync= 0x%x, p_collecting= 0x%x\n", __func__, 133731402c27SAdrian Chadd lp->lp_mux_state, selected, p_sync, p_collecting); 1338b47888ceSAndrew Thompson 1339b47888ceSAndrew Thompson re_eval: 1340b47888ceSAndrew Thompson la = lp->lp_aggregator; 1341b47888ceSAndrew Thompson KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1342b47888ceSAndrew Thompson ("MUX not detached")); 1343b47888ceSAndrew Thompson new_state = lp->lp_mux_state; 1344b47888ceSAndrew Thompson switch (lp->lp_mux_state) { 1345b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1346b47888ceSAndrew Thompson if (selected != LACP_UNSELECTED) { 1347b47888ceSAndrew Thompson new_state = LACP_MUX_WAITING; 1348b47888ceSAndrew Thompson } 1349b47888ceSAndrew Thompson break; 1350b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1351b47888ceSAndrew Thompson KASSERT(la->la_pending > 0 || 1352b47888ceSAndrew Thompson !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1353b47888ceSAndrew Thompson ("timer_wait_while still active")); 1354b47888ceSAndrew Thompson if (selected == LACP_SELECTED && la->la_pending == 0) { 1355b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1356b47888ceSAndrew Thompson } else if (selected == LACP_UNSELECTED) { 1357b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1358b47888ceSAndrew Thompson } 1359b47888ceSAndrew Thompson break; 1360b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1361b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync) { 1362b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1363b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED) { 1364b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1365b47888ceSAndrew Thompson } 1366b47888ceSAndrew Thompson break; 1367b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1368b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync && p_collecting) { 1369b47888ceSAndrew Thompson new_state = LACP_MUX_DISTRIBUTING; 1370b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED || !p_sync) { 1371b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1372b47888ceSAndrew Thompson } 1373b47888ceSAndrew Thompson break; 1374b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1375b47888ceSAndrew Thompson if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1376b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1377387e754aSAdrian Chadd lacp_dprintf(lp, "Interface stopped DISTRIBUTING, possible flapping\n"); 137831402c27SAdrian Chadd sc->sc_flapping++; 1379b47888ceSAndrew Thompson } 1380b47888ceSAndrew Thompson break; 1381b47888ceSAndrew Thompson default: 1382b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1383b47888ceSAndrew Thompson } 1384b47888ceSAndrew Thompson 1385b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1386b47888ceSAndrew Thompson return; 1387b47888ceSAndrew Thompson } 1388b47888ceSAndrew Thompson 1389b47888ceSAndrew Thompson lacp_set_mux(lp, new_state); 1390b47888ceSAndrew Thompson goto re_eval; 1391b47888ceSAndrew Thompson } 1392b47888ceSAndrew Thompson 1393b47888ceSAndrew Thompson static void 1394b47888ceSAndrew Thompson lacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1395b47888ceSAndrew Thompson { 1396b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1397b47888ceSAndrew Thompson 1398b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1399b47888ceSAndrew Thompson return; 1400b47888ceSAndrew Thompson } 1401b47888ceSAndrew Thompson 1402b47888ceSAndrew Thompson switch (new_state) { 1403b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1404b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_SYNC; 1405b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1406b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1407b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1408b47888ceSAndrew Thompson /* cancel timer */ 1409b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1410b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, 1411b47888ceSAndrew Thompson ("timer_wait_while not active")); 1412b47888ceSAndrew Thompson la->la_pending--; 1413b47888ceSAndrew Thompson } 1414b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1415b47888ceSAndrew Thompson lacp_unselect(lp); 1416b47888ceSAndrew Thompson break; 1417b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1418b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1419b47888ceSAndrew Thompson LACP_AGGREGATE_WAIT_TIME); 1420b47888ceSAndrew Thompson la->la_pending++; 1421b47888ceSAndrew Thompson break; 1422b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1423b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_SYNC; 1424b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1425b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1426b47888ceSAndrew Thompson break; 1427b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1428b47888ceSAndrew Thompson lacp_enable_collecting(lp); 1429b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1430b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1431b47888ceSAndrew Thompson break; 1432b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1433b47888ceSAndrew Thompson lacp_enable_distributing(lp); 1434b47888ceSAndrew Thompson break; 1435b47888ceSAndrew Thompson default: 1436b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1437b47888ceSAndrew Thompson } 1438b47888ceSAndrew Thompson 1439b47888ceSAndrew Thompson LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1440b47888ceSAndrew Thompson 1441b47888ceSAndrew Thompson lp->lp_mux_state = new_state; 1442b47888ceSAndrew Thompson } 1443b47888ceSAndrew Thompson 1444b47888ceSAndrew Thompson static void 1445b47888ceSAndrew Thompson lacp_sm_mux_timer(struct lacp_port *lp) 1446b47888ceSAndrew Thompson { 1447b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1448b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1449b47888ceSAndrew Thompson 1450b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, ("no pending event")); 1451b47888ceSAndrew Thompson 1452b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1453b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1454b47888ceSAndrew Thompson buf, sizeof(buf)), 1455b47888ceSAndrew Thompson la->la_pending, la->la_pending - 1)); 1456b47888ceSAndrew Thompson 1457b47888ceSAndrew Thompson la->la_pending--; 1458b47888ceSAndrew Thompson } 1459b47888ceSAndrew Thompson 1460b47888ceSAndrew Thompson /* periodic transmit machine */ 1461b47888ceSAndrew Thompson 1462b47888ceSAndrew Thompson static void 1463b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1464b47888ceSAndrew Thompson { 1465b47888ceSAndrew Thompson if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1466b47888ceSAndrew Thompson LACP_STATE_TIMEOUT)) { 1467b47888ceSAndrew Thompson return; 1468b47888ceSAndrew Thompson } 1469b47888ceSAndrew Thompson 1470b47888ceSAndrew Thompson LACP_DPRINTF((lp, "partner timeout changed\n")); 1471b47888ceSAndrew Thompson 1472b47888ceSAndrew Thompson /* 1473b47888ceSAndrew Thompson * FAST_PERIODIC -> SLOW_PERIODIC 1474b47888ceSAndrew Thompson * or 1475b47888ceSAndrew Thompson * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1476b47888ceSAndrew Thompson * 1477b47888ceSAndrew Thompson * let lacp_sm_ptx_tx_schedule to update timeout. 1478b47888ceSAndrew Thompson */ 1479b47888ceSAndrew Thompson 1480b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1481b47888ceSAndrew Thompson 1482b47888ceSAndrew Thompson /* 1483b47888ceSAndrew Thompson * if timeout has been shortened, assert NTT. 1484b47888ceSAndrew Thompson */ 1485b47888ceSAndrew Thompson 1486b47888ceSAndrew Thompson if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1487b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1488b47888ceSAndrew Thompson } 1489b47888ceSAndrew Thompson } 1490b47888ceSAndrew Thompson 1491b47888ceSAndrew Thompson static void 1492b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1493b47888ceSAndrew Thompson { 1494b47888ceSAndrew Thompson int timeout; 1495b47888ceSAndrew Thompson 1496b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1497b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1498b47888ceSAndrew Thompson 1499b47888ceSAndrew Thompson /* 1500b47888ceSAndrew Thompson * NO_PERIODIC 1501b47888ceSAndrew Thompson */ 1502b47888ceSAndrew Thompson 1503b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1504b47888ceSAndrew Thompson return; 1505b47888ceSAndrew Thompson } 1506b47888ceSAndrew Thompson 1507b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1508b47888ceSAndrew Thompson return; 1509b47888ceSAndrew Thompson } 1510b47888ceSAndrew Thompson 1511b47888ceSAndrew Thompson timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1512b47888ceSAndrew Thompson LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1513b47888ceSAndrew Thompson 1514b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1515b47888ceSAndrew Thompson } 1516b47888ceSAndrew Thompson 1517b47888ceSAndrew Thompson static void 1518b47888ceSAndrew Thompson lacp_sm_ptx_timer(struct lacp_port *lp) 1519b47888ceSAndrew Thompson { 1520b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1521b47888ceSAndrew Thompson } 1522b47888ceSAndrew Thompson 1523b47888ceSAndrew Thompson static void 1524b47888ceSAndrew Thompson lacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1525b47888ceSAndrew Thompson { 1526b47888ceSAndrew Thompson int timeout; 1527b47888ceSAndrew Thompson 1528b47888ceSAndrew Thompson /* 1529b47888ceSAndrew Thompson * check LACP_DISABLED first 1530b47888ceSAndrew Thompson */ 1531b47888ceSAndrew Thompson 1532b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1533b47888ceSAndrew Thompson return; 1534b47888ceSAndrew Thompson } 1535b47888ceSAndrew Thompson 1536b47888ceSAndrew Thompson /* 1537b47888ceSAndrew Thompson * check loopback condition. 1538b47888ceSAndrew Thompson */ 1539b47888ceSAndrew Thompson 1540b47888ceSAndrew Thompson if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1541b47888ceSAndrew Thompson &lp->lp_actor.lip_systemid)) { 1542b47888ceSAndrew Thompson return; 1543b47888ceSAndrew Thompson } 1544b47888ceSAndrew Thompson 1545b47888ceSAndrew Thompson /* 1546b47888ceSAndrew Thompson * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1547b47888ceSAndrew Thompson */ 1548b47888ceSAndrew Thompson 1549b47888ceSAndrew Thompson lacp_sm_rx_update_selected(lp, du); 1550b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(lp, du); 1551b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(lp, du); 1552b47888ceSAndrew Thompson 1553b47888ceSAndrew Thompson timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1554b47888ceSAndrew Thompson LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1555b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1556b47888ceSAndrew Thompson 1557b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1558b47888ceSAndrew Thompson 1559b47888ceSAndrew Thompson /* 1560b47888ceSAndrew Thompson * kick transmit machine without waiting the next tick. 1561b47888ceSAndrew Thompson */ 1562b47888ceSAndrew Thompson 1563b47888ceSAndrew Thompson lacp_sm_tx(lp); 1564b47888ceSAndrew Thompson } 1565b47888ceSAndrew Thompson 1566b47888ceSAndrew Thompson static void 1567b47888ceSAndrew Thompson lacp_sm_rx_set_expired(struct lacp_port *lp) 1568b47888ceSAndrew Thompson { 1569b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1570b47888ceSAndrew Thompson lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1571b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1572b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_EXPIRED; 1573b47888ceSAndrew Thompson } 1574b47888ceSAndrew Thompson 1575b47888ceSAndrew Thompson static void 1576b47888ceSAndrew Thompson lacp_sm_rx_timer(struct lacp_port *lp) 1577b47888ceSAndrew Thompson { 1578b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1579b47888ceSAndrew Thompson /* CURRENT -> EXPIRED */ 1580b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1581b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 1582b47888ceSAndrew Thompson } else { 1583b47888ceSAndrew Thompson /* EXPIRED -> DEFAULTED */ 1584b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1585b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(lp); 1586b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1587b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1588b47888ceSAndrew Thompson } 1589b47888ceSAndrew Thompson } 1590b47888ceSAndrew Thompson 1591b47888ceSAndrew Thompson static void 1592b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1593b47888ceSAndrew Thompson { 1594b47888ceSAndrew Thompson boolean_t active; 1595b47888ceSAndrew Thompson uint8_t oldpstate; 1596b47888ceSAndrew Thompson char buf[LACP_STATESTR_MAX+1]; 1597b47888ceSAndrew Thompson 15985fc4c149SAndrew Thompson LACP_TRACE(lp); 1599b47888ceSAndrew Thompson 1600b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1601b47888ceSAndrew Thompson 1602b47888ceSAndrew Thompson active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1603b47888ceSAndrew Thompson || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1604b47888ceSAndrew Thompson (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1605b47888ceSAndrew Thompson 1606b47888ceSAndrew Thompson lp->lp_partner = du->ldu_actor; 1607b47888ceSAndrew Thompson if (active && 1608b47888ceSAndrew Thompson ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1609b47888ceSAndrew Thompson LACP_STATE_AGGREGATION) && 1610b47888ceSAndrew Thompson !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1611b47888ceSAndrew Thompson || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1612b47888ceSAndrew Thompson /* XXX nothing? */ 1613b47888ceSAndrew Thompson } else { 1614b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1615b47888ceSAndrew Thompson } 1616b47888ceSAndrew Thompson 1617b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DEFAULTED; 1618b47888ceSAndrew Thompson 1619b47888ceSAndrew Thompson if (oldpstate != lp->lp_partner.lip_state) { 1620b47888ceSAndrew Thompson LACP_DPRINTF((lp, "old pstate %s\n", 1621b47888ceSAndrew Thompson lacp_format_state(oldpstate, buf, sizeof(buf)))); 1622b47888ceSAndrew Thompson LACP_DPRINTF((lp, "new pstate %s\n", 1623b47888ceSAndrew Thompson lacp_format_state(lp->lp_partner.lip_state, buf, 1624b47888ceSAndrew Thompson sizeof(buf)))); 1625b47888ceSAndrew Thompson } 1626b47888ceSAndrew Thompson 162731402c27SAdrian Chadd /* XXX Hack, still need to implement 5.4.9 para 2,3,4 */ 162849de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 162931402c27SAdrian Chadd lp->lp_partner.lip_state |= LACP_STATE_SYNC; 163031402c27SAdrian Chadd 1631b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1632b47888ceSAndrew Thompson } 1633b47888ceSAndrew Thompson 1634b47888ceSAndrew Thompson static void 1635b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1636b47888ceSAndrew Thompson { 16375fc4c149SAndrew Thompson 16385fc4c149SAndrew Thompson LACP_TRACE(lp); 1639b47888ceSAndrew Thompson 1640b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1641b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1642b47888ceSAndrew Thompson LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1643b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1644b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1645b47888ceSAndrew Thompson } 1646b47888ceSAndrew Thompson } 1647b47888ceSAndrew Thompson 1648b47888ceSAndrew Thompson static void 1649b47888ceSAndrew Thompson lacp_sm_rx_record_default(struct lacp_port *lp) 1650b47888ceSAndrew Thompson { 1651b47888ceSAndrew Thompson uint8_t oldpstate; 1652b47888ceSAndrew Thompson 16535fc4c149SAndrew Thompson LACP_TRACE(lp); 1654b47888ceSAndrew Thompson 1655b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 165649de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 165731402c27SAdrian Chadd lp->lp_partner = lacp_partner_admin_strict; 165831402c27SAdrian Chadd else 165931402c27SAdrian Chadd lp->lp_partner = lacp_partner_admin_optimistic;; 1660b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DEFAULTED; 1661b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1662b47888ceSAndrew Thompson } 1663b47888ceSAndrew Thompson 1664b47888ceSAndrew Thompson static void 1665b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1666b47888ceSAndrew Thompson const struct lacp_peerinfo *info) 1667b47888ceSAndrew Thompson { 16685fc4c149SAndrew Thompson 16695fc4c149SAndrew Thompson LACP_TRACE(lp); 1670b47888ceSAndrew Thompson 1671b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1672b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1673b47888ceSAndrew Thompson LACP_STATE_AGGREGATION)) { 1674b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1675b47888ceSAndrew Thompson /* mux machine will clean up lp->lp_aggregator */ 1676b47888ceSAndrew Thompson } 1677b47888ceSAndrew Thompson } 1678b47888ceSAndrew Thompson 1679b47888ceSAndrew Thompson static void 1680b47888ceSAndrew Thompson lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1681b47888ceSAndrew Thompson { 16825fc4c149SAndrew Thompson 16835fc4c149SAndrew Thompson LACP_TRACE(lp); 1684b47888ceSAndrew Thompson 1685b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1686b47888ceSAndrew Thompson } 1687b47888ceSAndrew Thompson 1688b47888ceSAndrew Thompson static void 1689b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(struct lacp_port *lp) 1690b47888ceSAndrew Thompson { 16915fc4c149SAndrew Thompson 16925fc4c149SAndrew Thompson LACP_TRACE(lp); 1693b47888ceSAndrew Thompson 169449de4f22SAdrian Chadd if (lp->lp_lsc->lsc_strict_mode) 169531402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 169631402c27SAdrian Chadd &lacp_partner_admin_strict); 169731402c27SAdrian Chadd else 169831402c27SAdrian Chadd lacp_sm_rx_update_selected_from_peerinfo(lp, 169931402c27SAdrian Chadd &lacp_partner_admin_optimistic); 1700b47888ceSAndrew Thompson } 1701b47888ceSAndrew Thompson 1702b47888ceSAndrew Thompson /* transmit machine */ 1703b47888ceSAndrew Thompson 1704b47888ceSAndrew Thompson static void 1705b47888ceSAndrew Thompson lacp_sm_tx(struct lacp_port *lp) 1706b47888ceSAndrew Thompson { 170731402c27SAdrian Chadd int error = 0; 1708b47888ceSAndrew Thompson 1709b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1710b47888ceSAndrew Thompson #if 1 1711b47888ceSAndrew Thompson || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1712b47888ceSAndrew Thompson && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1713b47888ceSAndrew Thompson #endif 1714b47888ceSAndrew Thompson ) { 1715b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1716b47888ceSAndrew Thompson } 1717b47888ceSAndrew Thompson 1718b47888ceSAndrew Thompson if (!(lp->lp_flags & LACP_PORT_NTT)) { 1719b47888ceSAndrew Thompson return; 1720b47888ceSAndrew Thompson } 1721b47888ceSAndrew Thompson 1722b47888ceSAndrew Thompson /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1723b47888ceSAndrew Thompson if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1724b47888ceSAndrew Thompson (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1725b47888ceSAndrew Thompson LACP_DPRINTF((lp, "rate limited pdu\n")); 1726b47888ceSAndrew Thompson return; 1727b47888ceSAndrew Thompson } 1728b47888ceSAndrew Thompson 172949de4f22SAdrian Chadd if (((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_tx_test) == 0) { 1730b47888ceSAndrew Thompson error = lacp_xmit_lacpdu(lp); 173149de4f22SAdrian Chadd } else { 173231402c27SAdrian Chadd LACP_TPRINTF((lp, "Dropping TX PDU\n")); 173349de4f22SAdrian Chadd } 1734b47888ceSAndrew Thompson 1735b47888ceSAndrew Thompson if (error == 0) { 1736b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1737b47888ceSAndrew Thompson } else { 1738b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1739b47888ceSAndrew Thompson error)); 1740b47888ceSAndrew Thompson } 1741b47888ceSAndrew Thompson } 1742b47888ceSAndrew Thompson 1743b47888ceSAndrew Thompson static void 1744b47888ceSAndrew Thompson lacp_sm_assert_ntt(struct lacp_port *lp) 1745b47888ceSAndrew Thompson { 1746b47888ceSAndrew Thompson 1747b47888ceSAndrew Thompson lp->lp_flags |= LACP_PORT_NTT; 1748b47888ceSAndrew Thompson } 1749b47888ceSAndrew Thompson 1750b47888ceSAndrew Thompson static void 1751b47888ceSAndrew Thompson lacp_run_timers(struct lacp_port *lp) 1752b47888ceSAndrew Thompson { 1753b47888ceSAndrew Thompson int i; 1754b47888ceSAndrew Thompson 1755b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 1756b47888ceSAndrew Thompson KASSERT(lp->lp_timer[i] >= 0, 1757b47888ceSAndrew Thompson ("invalid timer value %d", lp->lp_timer[i])); 1758b47888ceSAndrew Thompson if (lp->lp_timer[i] == 0) { 1759b47888ceSAndrew Thompson continue; 1760b47888ceSAndrew Thompson } else if (--lp->lp_timer[i] <= 0) { 1761b47888ceSAndrew Thompson if (lacp_timer_funcs[i]) { 1762b47888ceSAndrew Thompson (*lacp_timer_funcs[i])(lp); 1763b47888ceSAndrew Thompson } 1764b47888ceSAndrew Thompson } 1765b47888ceSAndrew Thompson } 1766b47888ceSAndrew Thompson } 1767b47888ceSAndrew Thompson 1768b47888ceSAndrew Thompson int 17693de18008SAndrew Thompson lacp_marker_input(struct lacp_port *lp, struct mbuf *m) 1770b47888ceSAndrew Thompson { 1771998971a7SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 17723de18008SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 17733de18008SAndrew Thompson struct lacp_port *lp2; 1774b47888ceSAndrew Thompson struct markerdu *mdu; 1775b47888ceSAndrew Thompson int error = 0; 1776998971a7SAndrew Thompson int pending = 0; 1777b47888ceSAndrew Thompson 1778b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*mdu)) { 1779b47888ceSAndrew Thompson goto bad; 1780b47888ceSAndrew Thompson } 1781b47888ceSAndrew Thompson 1782b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 1783b47888ceSAndrew Thompson goto bad; 1784b47888ceSAndrew Thompson } 1785b47888ceSAndrew Thompson 1786b47888ceSAndrew Thompson if (m->m_len < sizeof(*mdu)) { 1787b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*mdu)); 1788b47888ceSAndrew Thompson if (m == NULL) { 1789b47888ceSAndrew Thompson return (ENOMEM); 1790b47888ceSAndrew Thompson } 1791b47888ceSAndrew Thompson } 1792b47888ceSAndrew Thompson 1793b47888ceSAndrew Thompson mdu = mtod(m, struct markerdu *); 1794b47888ceSAndrew Thompson 1795b47888ceSAndrew Thompson if (memcmp(&mdu->mdu_eh.ether_dhost, 1796b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1797b47888ceSAndrew Thompson goto bad; 1798b47888ceSAndrew Thompson } 1799b47888ceSAndrew Thompson 1800b47888ceSAndrew Thompson if (mdu->mdu_sph.sph_version != 1) { 1801b47888ceSAndrew Thompson goto bad; 1802b47888ceSAndrew Thompson } 1803b47888ceSAndrew Thompson 1804b47888ceSAndrew Thompson switch (mdu->mdu_tlv.tlv_type) { 1805b47888ceSAndrew Thompson case MARKER_TYPE_INFO: 1806b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1807b47888ceSAndrew Thompson marker_info_tlv_template, TRUE)) { 1808b47888ceSAndrew Thompson goto bad; 1809b47888ceSAndrew Thompson } 1810b47888ceSAndrew Thompson mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1811b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, 1812b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1813b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, 181418242d3bSAndrew Thompson lgp->lp_lladdr, ETHER_ADDR_LEN); 181518242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 1816b47888ceSAndrew Thompson break; 1817b47888ceSAndrew Thompson 1818b47888ceSAndrew Thompson case MARKER_TYPE_RESPONSE: 1819b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1820b47888ceSAndrew Thompson marker_response_tlv_template, TRUE)) { 1821b47888ceSAndrew Thompson goto bad; 1822b47888ceSAndrew Thompson } 1823998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 1824998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 1825998971a7SAndrew Thompson ":", ntohl(mdu->mdu_info.mi_rq_xid))); 1826998971a7SAndrew Thompson 1827998971a7SAndrew Thompson /* Verify that it is the last marker we sent out */ 1828998971a7SAndrew Thompson if (memcmp(&mdu->mdu_info, &lp->lp_marker, 1829998971a7SAndrew Thompson sizeof(struct lacp_markerinfo))) 1830998971a7SAndrew Thompson goto bad; 1831998971a7SAndrew Thompson 18323de18008SAndrew Thompson LACP_LOCK(lsc); 1833998971a7SAndrew Thompson lp->lp_flags &= ~LACP_PORT_MARK; 1834998971a7SAndrew Thompson 1835998971a7SAndrew Thompson if (lsc->lsc_suppress_distributing) { 1836998971a7SAndrew Thompson /* Check if any ports are waiting for a response */ 1837998971a7SAndrew Thompson LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 1838998971a7SAndrew Thompson if (lp2->lp_flags & LACP_PORT_MARK) { 1839998971a7SAndrew Thompson pending = 1; 1840998971a7SAndrew Thompson break; 1841998971a7SAndrew Thompson } 1842998971a7SAndrew Thompson } 1843998971a7SAndrew Thompson 1844998971a7SAndrew Thompson if (pending == 0) { 1845998971a7SAndrew Thompson /* All interface queues are clear */ 1846998971a7SAndrew Thompson LACP_DPRINTF((NULL, "queue flush complete\n")); 1847998971a7SAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 1848998971a7SAndrew Thompson } 1849998971a7SAndrew Thompson } 18503de18008SAndrew Thompson LACP_UNLOCK(lsc); 1851998971a7SAndrew Thompson m_freem(m); 1852998971a7SAndrew Thompson break; 1853998971a7SAndrew Thompson 1854b47888ceSAndrew Thompson default: 1855b47888ceSAndrew Thompson goto bad; 1856b47888ceSAndrew Thompson } 1857b47888ceSAndrew Thompson 1858b47888ceSAndrew Thompson return (error); 1859b47888ceSAndrew Thompson 1860b47888ceSAndrew Thompson bad: 1861998971a7SAndrew Thompson LACP_DPRINTF((lp, "bad marker frame\n")); 1862b47888ceSAndrew Thompson m_freem(m); 1863b47888ceSAndrew Thompson return (EINVAL); 1864b47888ceSAndrew Thompson } 1865b47888ceSAndrew Thompson 1866b47888ceSAndrew Thompson static int 1867b47888ceSAndrew Thompson tlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1868b47888ceSAndrew Thompson const struct tlv_template *tmpl, boolean_t check_type) 1869b47888ceSAndrew Thompson { 1870b47888ceSAndrew Thompson while (/* CONSTCOND */ 1) { 1871b47888ceSAndrew Thompson if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1872b47888ceSAndrew Thompson return (EINVAL); 1873b47888ceSAndrew Thompson } 1874b47888ceSAndrew Thompson if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1875b47888ceSAndrew Thompson tlv->tlv_length != tmpl->tmpl_length) { 1876b47888ceSAndrew Thompson return (EINVAL); 1877b47888ceSAndrew Thompson } 1878b47888ceSAndrew Thompson if (tmpl->tmpl_type == 0) { 1879b47888ceSAndrew Thompson break; 1880b47888ceSAndrew Thompson } 1881b47888ceSAndrew Thompson tlv = (const struct tlvhdr *) 1882b47888ceSAndrew Thompson ((const char *)tlv + tlv->tlv_length); 1883b47888ceSAndrew Thompson tmpl++; 1884b47888ceSAndrew Thompson } 1885b47888ceSAndrew Thompson 1886b47888ceSAndrew Thompson return (0); 1887b47888ceSAndrew Thompson } 1888b47888ceSAndrew Thompson 18895fc4c149SAndrew Thompson /* Debugging */ 1890b47888ceSAndrew Thompson const char * 1891b47888ceSAndrew Thompson lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1892b47888ceSAndrew Thompson { 1893b47888ceSAndrew Thompson snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1894b47888ceSAndrew Thompson (int)mac[0], 1895b47888ceSAndrew Thompson (int)mac[1], 1896b47888ceSAndrew Thompson (int)mac[2], 1897b47888ceSAndrew Thompson (int)mac[3], 1898b47888ceSAndrew Thompson (int)mac[4], 1899b47888ceSAndrew Thompson (int)mac[5]); 1900b47888ceSAndrew Thompson 1901b47888ceSAndrew Thompson return (buf); 1902b47888ceSAndrew Thompson } 1903b47888ceSAndrew Thompson 1904b47888ceSAndrew Thompson const char * 1905b47888ceSAndrew Thompson lacp_format_systemid(const struct lacp_systemid *sysid, 1906b47888ceSAndrew Thompson char *buf, size_t buflen) 1907b47888ceSAndrew Thompson { 1908b47888ceSAndrew Thompson char macbuf[LACP_MACSTR_MAX+1]; 1909b47888ceSAndrew Thompson 1910b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%s", 1911b47888ceSAndrew Thompson ntohs(sysid->lsi_prio), 1912b47888ceSAndrew Thompson lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1913b47888ceSAndrew Thompson 1914b47888ceSAndrew Thompson return (buf); 1915b47888ceSAndrew Thompson } 1916b47888ceSAndrew Thompson 1917b47888ceSAndrew Thompson const char * 1918b47888ceSAndrew Thompson lacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1919b47888ceSAndrew Thompson { 1920b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%04X", 1921b47888ceSAndrew Thompson ntohs(portid->lpi_prio), 1922b47888ceSAndrew Thompson ntohs(portid->lpi_portno)); 1923b47888ceSAndrew Thompson 1924b47888ceSAndrew Thompson return (buf); 1925b47888ceSAndrew Thompson } 1926b47888ceSAndrew Thompson 1927b47888ceSAndrew Thompson const char * 1928b47888ceSAndrew Thompson lacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1929b47888ceSAndrew Thompson { 1930b47888ceSAndrew Thompson char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1931b47888ceSAndrew Thompson char portid[LACP_PORTIDSTR_MAX+1]; 1932b47888ceSAndrew Thompson 1933b47888ceSAndrew Thompson snprintf(buf, buflen, "(%s,%04X,%s)", 1934b47888ceSAndrew Thompson lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1935b47888ceSAndrew Thompson ntohs(peer->lip_key), 1936b47888ceSAndrew Thompson lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1937b47888ceSAndrew Thompson 1938b47888ceSAndrew Thompson return (buf); 1939b47888ceSAndrew Thompson } 1940b47888ceSAndrew Thompson 1941b47888ceSAndrew Thompson const char * 1942b47888ceSAndrew Thompson lacp_format_lagid(const struct lacp_peerinfo *a, 1943b47888ceSAndrew Thompson const struct lacp_peerinfo *b, char *buf, size_t buflen) 1944b47888ceSAndrew Thompson { 1945b47888ceSAndrew Thompson char astr[LACP_PARTNERSTR_MAX+1]; 1946b47888ceSAndrew Thompson char bstr[LACP_PARTNERSTR_MAX+1]; 1947b47888ceSAndrew Thompson 1948b47888ceSAndrew Thompson #if 0 1949b47888ceSAndrew Thompson /* 1950b47888ceSAndrew Thompson * there's a convention to display small numbered peer 1951b47888ceSAndrew Thompson * in the left. 1952b47888ceSAndrew Thompson */ 1953b47888ceSAndrew Thompson 1954b47888ceSAndrew Thompson if (lacp_compare_peerinfo(a, b) > 0) { 1955b47888ceSAndrew Thompson const struct lacp_peerinfo *t; 1956b47888ceSAndrew Thompson 1957b47888ceSAndrew Thompson t = a; 1958b47888ceSAndrew Thompson a = b; 1959b47888ceSAndrew Thompson b = t; 1960b47888ceSAndrew Thompson } 1961b47888ceSAndrew Thompson #endif 1962b47888ceSAndrew Thompson 1963b47888ceSAndrew Thompson snprintf(buf, buflen, "[%s,%s]", 1964b47888ceSAndrew Thompson lacp_format_partner(a, astr, sizeof(astr)), 1965b47888ceSAndrew Thompson lacp_format_partner(b, bstr, sizeof(bstr))); 1966b47888ceSAndrew Thompson 1967b47888ceSAndrew Thompson return (buf); 1968b47888ceSAndrew Thompson } 1969b47888ceSAndrew Thompson 1970b47888ceSAndrew Thompson const char * 1971b47888ceSAndrew Thompson lacp_format_lagid_aggregator(const struct lacp_aggregator *la, 1972b47888ceSAndrew Thompson char *buf, size_t buflen) 1973b47888ceSAndrew Thompson { 1974b47888ceSAndrew Thompson if (la == NULL) { 1975b47888ceSAndrew Thompson return ("(none)"); 1976b47888ceSAndrew Thompson } 1977b47888ceSAndrew Thompson 1978b47888ceSAndrew Thompson return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 1979b47888ceSAndrew Thompson } 1980b47888ceSAndrew Thompson 1981b47888ceSAndrew Thompson const char * 1982b47888ceSAndrew Thompson lacp_format_state(uint8_t state, char *buf, size_t buflen) 1983b47888ceSAndrew Thompson { 1984b47888ceSAndrew Thompson snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 1985b47888ceSAndrew Thompson return (buf); 1986b47888ceSAndrew Thompson } 1987b47888ceSAndrew Thompson 1988b47888ceSAndrew Thompson static void 1989b47888ceSAndrew Thompson lacp_dump_lacpdu(const struct lacpdu *du) 1990b47888ceSAndrew Thompson { 1991b47888ceSAndrew Thompson char buf[LACP_PARTNERSTR_MAX+1]; 1992b47888ceSAndrew Thompson char buf2[LACP_STATESTR_MAX+1]; 1993b47888ceSAndrew Thompson 1994b47888ceSAndrew Thompson printf("actor=%s\n", 1995b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 1996b47888ceSAndrew Thompson printf("actor.state=%s\n", 1997b47888ceSAndrew Thompson lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 1998b47888ceSAndrew Thompson printf("partner=%s\n", 1999b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 2000b47888ceSAndrew Thompson printf("partner.state=%s\n", 2001b47888ceSAndrew Thompson lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 2002b47888ceSAndrew Thompson 2003b47888ceSAndrew Thompson printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 2004b47888ceSAndrew Thompson } 2005b47888ceSAndrew Thompson 2006b47888ceSAndrew Thompson static void 2007b47888ceSAndrew Thompson lacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 2008b47888ceSAndrew Thompson { 2009b47888ceSAndrew Thompson va_list va; 2010b47888ceSAndrew Thompson 2011b47888ceSAndrew Thompson if (lp) { 2012b47888ceSAndrew Thompson printf("%s: ", lp->lp_ifp->if_xname); 2013b47888ceSAndrew Thompson } 2014b47888ceSAndrew Thompson 2015b47888ceSAndrew Thompson va_start(va, fmt); 2016b47888ceSAndrew Thompson vprintf(fmt, va); 2017b47888ceSAndrew Thompson va_end(va); 2018b47888ceSAndrew Thompson } 2019