1168561Sthompsa/* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */ 2168561Sthompsa 3168561Sthompsa/*- 4168561Sthompsa * Copyright (c)2005 YAMAMOTO Takashi, 5177274Sthompsa * Copyright (c)2008 Andrew Thompson <thompsa@FreeBSD.org> 6168561Sthompsa * All rights reserved. 7168561Sthompsa * 8168561Sthompsa * Redistribution and use in source and binary forms, with or without 9168561Sthompsa * modification, are permitted provided that the following conditions 10168561Sthompsa * are met: 11168561Sthompsa * 1. Redistributions of source code must retain the above copyright 12168561Sthompsa * notice, this list of conditions and the following disclaimer. 13168561Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 14168561Sthompsa * notice, this list of conditions and the following disclaimer in the 15168561Sthompsa * documentation and/or other materials provided with the distribution. 16168561Sthompsa * 17168561Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18168561Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19168561Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20168561Sthompsa * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21168561Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22168561Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23168561Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24168561Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25168561Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26168561Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27168561Sthompsa * SUCH DAMAGE. 28168561Sthompsa */ 29168561Sthompsa 30168561Sthompsa#include <sys/cdefs.h> 31168561Sthompsa__FBSDID("$FreeBSD$"); 32168561Sthompsa 33168561Sthompsa#include <sys/param.h> 34168561Sthompsa#include <sys/callout.h> 35168561Sthompsa#include <sys/mbuf.h> 36168561Sthompsa#include <sys/systm.h> 37168561Sthompsa#include <sys/malloc.h> 38168561Sthompsa#include <sys/kernel.h> /* hz */ 39168561Sthompsa#include <sys/socket.h> /* for net/if.h */ 40168561Sthompsa#include <sys/sockio.h> 41236062Sthompsa#include <sys/sysctl.h> 42168561Sthompsa#include <machine/stdarg.h> 43169569Sthompsa#include <sys/lock.h> 44169569Sthompsa#include <sys/rwlock.h> 45168561Sthompsa 46168561Sthompsa#include <net/if.h> 47168561Sthompsa#include <net/if_dl.h> 48168561Sthompsa#include <net/ethernet.h> 49168561Sthompsa#include <net/if_media.h> 50168561Sthompsa#include <net/if_types.h> 51168561Sthompsa 52168793Sthompsa#include <net/if_lagg.h> 53168561Sthompsa#include <net/ieee8023ad_lacp.h> 54168561Sthompsa 55168561Sthompsa/* 56168561Sthompsa * actor system priority and port priority. 57168561Sthompsa * XXX should be configurable. 58168561Sthompsa */ 59168561Sthompsa 60168561Sthompsa#define LACP_SYSTEM_PRIO 0x8000 61168561Sthompsa#define LACP_PORT_PRIO 0x8000 62168561Sthompsa 63168561Sthompsaconst uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 64168561Sthompsa { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 65168561Sthompsa 66168561Sthompsastatic const struct tlv_template lacp_info_tlv_template[] = { 67168561Sthompsa { LACP_TYPE_ACTORINFO, 68168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 69168561Sthompsa { LACP_TYPE_PARTNERINFO, 70168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 71168561Sthompsa { LACP_TYPE_COLLECTORINFO, 72168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 73168561Sthompsa { 0, 0 }, 74168561Sthompsa}; 75168561Sthompsa 76168561Sthompsastatic const struct tlv_template marker_info_tlv_template[] = { 77169739Sthompsa { MARKER_TYPE_INFO, 78169739Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 79168561Sthompsa { 0, 0 }, 80168561Sthompsa}; 81168561Sthompsa 82168561Sthompsastatic const struct tlv_template marker_response_tlv_template[] = { 83169739Sthompsa { MARKER_TYPE_RESPONSE, 84169739Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 85168561Sthompsa { 0, 0 }, 86168561Sthompsa}; 87168561Sthompsa 88177274Sthompsatypedef void (*lacp_timer_func_t)(struct lacp_port *); 89177274Sthompsa 90168561Sthompsastatic void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 91169739Sthompsastatic void lacp_fill_markerinfo(struct lacp_port *, 92169739Sthompsa struct lacp_markerinfo *); 93168561Sthompsa 94168561Sthompsastatic uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 95168561Sthompsastatic void lacp_suppress_distributing(struct lacp_softc *, 96168561Sthompsa struct lacp_aggregator *); 97168561Sthompsastatic void lacp_transit_expire(void *); 98177274Sthompsastatic void lacp_update_portmap(struct lacp_softc *); 99168561Sthompsastatic void lacp_select_active_aggregator(struct lacp_softc *); 100168561Sthompsastatic uint16_t lacp_compose_key(struct lacp_port *); 101168561Sthompsastatic int tlv_check(const void *, size_t, const struct tlvhdr *, 102168561Sthompsa const struct tlv_template *, boolean_t); 103168561Sthompsastatic void lacp_tick(void *); 104168561Sthompsa 105168561Sthompsastatic void lacp_fill_aggregator_id(struct lacp_aggregator *, 106168561Sthompsa const struct lacp_port *); 107168561Sthompsastatic void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 108168561Sthompsa const struct lacp_peerinfo *); 109168561Sthompsastatic int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 110168561Sthompsa const struct lacp_port *); 111168561Sthompsastatic int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 112168561Sthompsa const struct lacp_peerinfo *); 113168561Sthompsa 114168561Sthompsastatic struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 115168561Sthompsa struct lacp_port *); 116168561Sthompsastatic void lacp_aggregator_addref(struct lacp_softc *, 117168561Sthompsa struct lacp_aggregator *); 118168561Sthompsastatic void lacp_aggregator_delref(struct lacp_softc *, 119168561Sthompsa struct lacp_aggregator *); 120168561Sthompsa 121168561Sthompsa/* receive machine */ 122168561Sthompsa 123177274Sthompsastatic int lacp_pdu_input(struct lacp_port *, struct mbuf *); 124177274Sthompsastatic int lacp_marker_input(struct lacp_port *, struct mbuf *); 125168561Sthompsastatic void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 126168561Sthompsastatic void lacp_sm_rx_timer(struct lacp_port *); 127168561Sthompsastatic void lacp_sm_rx_set_expired(struct lacp_port *); 128168561Sthompsastatic void lacp_sm_rx_update_ntt(struct lacp_port *, 129168561Sthompsa const struct lacpdu *); 130168561Sthompsastatic void lacp_sm_rx_record_pdu(struct lacp_port *, 131168561Sthompsa const struct lacpdu *); 132168561Sthompsastatic void lacp_sm_rx_update_selected(struct lacp_port *, 133168561Sthompsa const struct lacpdu *); 134168561Sthompsastatic void lacp_sm_rx_record_default(struct lacp_port *); 135168561Sthompsastatic void lacp_sm_rx_update_default_selected(struct lacp_port *); 136168561Sthompsastatic void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 137168561Sthompsa const struct lacp_peerinfo *); 138168561Sthompsa 139168561Sthompsa/* mux machine */ 140168561Sthompsa 141168561Sthompsastatic void lacp_sm_mux(struct lacp_port *); 142168561Sthompsastatic void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 143168561Sthompsastatic void lacp_sm_mux_timer(struct lacp_port *); 144168561Sthompsa 145168561Sthompsa/* periodic transmit machine */ 146168561Sthompsa 147168561Sthompsastatic void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 148168561Sthompsastatic void lacp_sm_ptx_tx_schedule(struct lacp_port *); 149168561Sthompsastatic void lacp_sm_ptx_timer(struct lacp_port *); 150168561Sthompsa 151168561Sthompsa/* transmit machine */ 152168561Sthompsa 153168561Sthompsastatic void lacp_sm_tx(struct lacp_port *); 154168561Sthompsastatic void lacp_sm_assert_ntt(struct lacp_port *); 155168561Sthompsa 156168561Sthompsastatic void lacp_run_timers(struct lacp_port *); 157168561Sthompsastatic int lacp_compare_peerinfo(const struct lacp_peerinfo *, 158168561Sthompsa const struct lacp_peerinfo *); 159168561Sthompsastatic int lacp_compare_systemid(const struct lacp_systemid *, 160168561Sthompsa const struct lacp_systemid *); 161168561Sthompsastatic void lacp_port_enable(struct lacp_port *); 162168561Sthompsastatic void lacp_port_disable(struct lacp_port *); 163168561Sthompsastatic void lacp_select(struct lacp_port *); 164168561Sthompsastatic void lacp_unselect(struct lacp_port *); 165168561Sthompsastatic void lacp_disable_collecting(struct lacp_port *); 166168561Sthompsastatic void lacp_enable_collecting(struct lacp_port *); 167168561Sthompsastatic void lacp_disable_distributing(struct lacp_port *); 168168561Sthompsastatic void lacp_enable_distributing(struct lacp_port *); 169168561Sthompsastatic int lacp_xmit_lacpdu(struct lacp_port *); 170169739Sthompsastatic int lacp_xmit_marker(struct lacp_port *); 171168561Sthompsa 172236062Sthompsa/* Debugging */ 173236062Sthompsa 174168561Sthompsastatic void lacp_dump_lacpdu(const struct lacpdu *); 175168561Sthompsastatic const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 176168561Sthompsa size_t); 177168561Sthompsastatic const char *lacp_format_lagid(const struct lacp_peerinfo *, 178168561Sthompsa const struct lacp_peerinfo *, char *, size_t); 179168561Sthompsastatic const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 180168561Sthompsa char *, size_t); 181168561Sthompsastatic const char *lacp_format_state(uint8_t, char *, size_t); 182168561Sthompsastatic const char *lacp_format_mac(const uint8_t *, char *, size_t); 183168561Sthompsastatic const char *lacp_format_systemid(const struct lacp_systemid *, char *, 184168561Sthompsa size_t); 185168561Sthompsastatic const char *lacp_format_portid(const struct lacp_portid *, char *, 186168561Sthompsa size_t); 187168561Sthompsastatic void lacp_dprintf(const struct lacp_port *, const char *, ...) 188168561Sthompsa __attribute__((__format__(__printf__, 2, 3))); 189168561Sthompsa 190236062Sthompsastatic int lacp_debug = 0; 191253314SadrianSYSCTL_NODE(_net_link_lagg, OID_AUTO, lacp, CTLFLAG_RD, 0, "ieee802.3ad"); 192253314SadrianSYSCTL_INT(_net_link_lagg_lacp, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, 193236062Sthompsa &lacp_debug, 0, "Enable LACP debug logging (1=debug, 2=trace)"); 194253314SadrianTUNABLE_INT("net.link.lagg.lacp.debug", &lacp_debug); 195236062Sthompsa 196253314Sadrian#define LACP_DPRINTF(a) if (lacp_debug & 0x01) { lacp_dprintf a ; } 197253314Sadrian#define LACP_TRACE(a) if (lacp_debug & 0x02) { lacp_dprintf(a,"%s\n",__func__); } 198253314Sadrian#define LACP_TPRINTF(a) if (lacp_debug & 0x04) { lacp_dprintf a ; } 199253314Sadrian 200168561Sthompsa/* 201168561Sthompsa * partner administration variables. 202168561Sthompsa * XXX should be configurable. 203168561Sthompsa */ 204168561Sthompsa 205253314Sadrianstatic const struct lacp_peerinfo lacp_partner_admin_optimistic = { 206168561Sthompsa .lip_systemid = { .lsi_prio = 0xffff }, 207168561Sthompsa .lip_portid = { .lpi_prio = 0xffff }, 208168561Sthompsa .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 209168561Sthompsa LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 210253314Sadrian}; 211253314Sadrian 212253314Sadrianstatic const struct lacp_peerinfo lacp_partner_admin_strict = { 213253314Sadrian .lip_systemid = { .lsi_prio = 0xffff }, 214253314Sadrian .lip_portid = { .lpi_prio = 0xffff }, 215168561Sthompsa .lip_state = 0, 216168561Sthompsa}; 217168561Sthompsa 218168561Sthompsastatic const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 219168561Sthompsa [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 220168561Sthompsa [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 221168561Sthompsa [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 222168561Sthompsa}; 223168561Sthompsa 224175005Sthompsastruct mbuf * 225169569Sthompsalacp_input(struct lagg_port *lgp, struct mbuf *m) 226169569Sthompsa{ 227177274Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 228169569Sthompsa uint8_t subtype; 229169569Sthompsa 230169569Sthompsa if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 231169569Sthompsa m_freem(m); 232175005Sthompsa return (NULL); 233169569Sthompsa } 234169569Sthompsa 235169569Sthompsa m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 236169569Sthompsa switch (subtype) { 237169569Sthompsa case SLOWPROTOCOLS_SUBTYPE_LACP: 238177274Sthompsa lacp_pdu_input(lp, m); 239177274Sthompsa return (NULL); 240169569Sthompsa 241169569Sthompsa case SLOWPROTOCOLS_SUBTYPE_MARKER: 242177274Sthompsa lacp_marker_input(lp, m); 243177274Sthompsa return (NULL); 244169569Sthompsa } 245169569Sthompsa 246177274Sthompsa /* Not a subtype we are interested in */ 247177274Sthompsa return (m); 248169569Sthompsa} 249169569Sthompsa 250168561Sthompsa/* 251169569Sthompsa * lacp_pdu_input: process lacpdu 252168561Sthompsa */ 253169569Sthompsastatic int 254177274Sthompsalacp_pdu_input(struct lacp_port *lp, struct mbuf *m) 255168561Sthompsa{ 256177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 257168561Sthompsa struct lacpdu *du; 258168561Sthompsa int error = 0; 259168561Sthompsa 260168561Sthompsa if (m->m_pkthdr.len != sizeof(*du)) { 261168561Sthompsa goto bad; 262168561Sthompsa } 263168561Sthompsa 264168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 265168561Sthompsa goto bad; 266168561Sthompsa } 267168561Sthompsa 268168561Sthompsa if (m->m_len < sizeof(*du)) { 269168561Sthompsa m = m_pullup(m, sizeof(*du)); 270168561Sthompsa if (m == NULL) { 271168561Sthompsa return (ENOMEM); 272168561Sthompsa } 273168561Sthompsa } 274168561Sthompsa 275168561Sthompsa du = mtod(m, struct lacpdu *); 276168561Sthompsa 277168561Sthompsa if (memcmp(&du->ldu_eh.ether_dhost, 278168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 279168561Sthompsa goto bad; 280168561Sthompsa } 281168561Sthompsa 282168561Sthompsa /* 283168561Sthompsa * ignore the version for compatibility with 284168561Sthompsa * the future protocol revisions. 285168561Sthompsa */ 286168561Sthompsa#if 0 287168561Sthompsa if (du->ldu_sph.sph_version != 1) { 288168561Sthompsa goto bad; 289168561Sthompsa } 290168561Sthompsa#endif 291168561Sthompsa 292168561Sthompsa /* 293168561Sthompsa * ignore tlv types for compatibility with 294168561Sthompsa * the future protocol revisions. 295168561Sthompsa */ 296168561Sthompsa if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 297168561Sthompsa lacp_info_tlv_template, FALSE)) { 298168561Sthompsa goto bad; 299168561Sthompsa } 300168561Sthompsa 301236062Sthompsa if (lacp_debug > 0) { 302236062Sthompsa lacp_dprintf(lp, "lacpdu receive\n"); 303236062Sthompsa lacp_dump_lacpdu(du); 304236062Sthompsa } 305177274Sthompsa 306253687Sadrian if ((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_rx_test) { 307253314Sadrian LACP_TPRINTF((lp, "Dropping RX PDU\n")); 308253314Sadrian goto bad; 309253314Sadrian } 310253314Sadrian 311177274Sthompsa LACP_LOCK(lsc); 312168561Sthompsa lacp_sm_rx(lp, du); 313177274Sthompsa LACP_UNLOCK(lsc); 314168561Sthompsa 315168561Sthompsa m_freem(m); 316168561Sthompsa return (error); 317168561Sthompsa 318168561Sthompsabad: 319168561Sthompsa m_freem(m); 320168561Sthompsa return (EINVAL); 321168561Sthompsa} 322168561Sthompsa 323168561Sthompsastatic void 324168561Sthompsalacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 325168561Sthompsa{ 326168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 327170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 328168561Sthompsa 329168561Sthompsa info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 330168561Sthompsa memcpy(&info->lip_systemid.lsi_mac, 331170599Sthompsa IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 332168561Sthompsa info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 333168561Sthompsa info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 334168561Sthompsa info->lip_state = lp->lp_state; 335168561Sthompsa} 336168561Sthompsa 337169739Sthompsastatic void 338169739Sthompsalacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 339169739Sthompsa{ 340169739Sthompsa struct ifnet *ifp = lp->lp_ifp; 341169739Sthompsa 342169739Sthompsa /* Fill in the port index and system id (encoded as the MAC) */ 343169739Sthompsa info->mi_rq_port = htons(ifp->if_index); 344169739Sthompsa memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 345169739Sthompsa info->mi_rq_xid = htonl(0); 346169739Sthompsa} 347169739Sthompsa 348168561Sthompsastatic int 349168561Sthompsalacp_xmit_lacpdu(struct lacp_port *lp) 350168561Sthompsa{ 351168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 352168561Sthompsa struct mbuf *m; 353168561Sthompsa struct lacpdu *du; 354168561Sthompsa int error; 355168561Sthompsa 356177274Sthompsa LACP_LOCK_ASSERT(lp->lp_lsc); 357168561Sthompsa 358243882Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 359168561Sthompsa if (m == NULL) { 360168561Sthompsa return (ENOMEM); 361168561Sthompsa } 362168561Sthompsa m->m_len = m->m_pkthdr.len = sizeof(*du); 363168561Sthompsa 364168561Sthompsa du = mtod(m, struct lacpdu *); 365168561Sthompsa memset(du, 0, sizeof(*du)); 366168561Sthompsa 367168561Sthompsa memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 368168561Sthompsa ETHER_ADDR_LEN); 369168793Sthompsa memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 370168561Sthompsa du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 371168561Sthompsa 372168561Sthompsa du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 373168561Sthompsa du->ldu_sph.sph_version = 1; 374168561Sthompsa 375168561Sthompsa TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 376168561Sthompsa du->ldu_actor = lp->lp_actor; 377168561Sthompsa 378168561Sthompsa TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 379168561Sthompsa sizeof(du->ldu_partner)); 380168561Sthompsa du->ldu_partner = lp->lp_partner; 381168561Sthompsa 382168561Sthompsa TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 383168561Sthompsa sizeof(du->ldu_collector)); 384168561Sthompsa du->ldu_collector.lci_maxdelay = 0; 385168561Sthompsa 386236062Sthompsa if (lacp_debug > 0) { 387236062Sthompsa lacp_dprintf(lp, "lacpdu transmit\n"); 388236062Sthompsa lacp_dump_lacpdu(du); 389236062Sthompsa } 390168561Sthompsa 391168561Sthompsa m->m_flags |= M_MCAST; 392168561Sthompsa 393168561Sthompsa /* 394168561Sthompsa * XXX should use higher priority queue. 395168561Sthompsa * otherwise network congestion can break aggregation. 396168561Sthompsa */ 397168561Sthompsa 398168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 399168561Sthompsa return (error); 400168561Sthompsa} 401168561Sthompsa 402169739Sthompsastatic int 403169739Sthompsalacp_xmit_marker(struct lacp_port *lp) 404169739Sthompsa{ 405169739Sthompsa struct lagg_port *lgp = lp->lp_lagg; 406169739Sthompsa struct mbuf *m; 407169739Sthompsa struct markerdu *mdu; 408169739Sthompsa int error; 409169739Sthompsa 410177274Sthompsa LACP_LOCK_ASSERT(lp->lp_lsc); 411169739Sthompsa 412243882Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 413169739Sthompsa if (m == NULL) { 414169739Sthompsa return (ENOMEM); 415169739Sthompsa } 416169739Sthompsa m->m_len = m->m_pkthdr.len = sizeof(*mdu); 417169739Sthompsa 418169739Sthompsa mdu = mtod(m, struct markerdu *); 419169739Sthompsa memset(mdu, 0, sizeof(*mdu)); 420169739Sthompsa 421169739Sthompsa memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 422169739Sthompsa ETHER_ADDR_LEN); 423169739Sthompsa memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 424169739Sthompsa mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 425169739Sthompsa 426169739Sthompsa mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 427169739Sthompsa mdu->mdu_sph.sph_version = 1; 428169739Sthompsa 429169739Sthompsa /* Bump the transaction id and copy over the marker info */ 430169739Sthompsa lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 431169739Sthompsa TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 432169739Sthompsa mdu->mdu_info = lp->lp_marker; 433169739Sthompsa 434169739Sthompsa LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 435169739Sthompsa ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 436169739Sthompsa ntohl(mdu->mdu_info.mi_rq_xid))); 437169739Sthompsa 438169739Sthompsa m->m_flags |= M_MCAST; 439169739Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 440169739Sthompsa return (error); 441169739Sthompsa} 442177274Sthompsa 443168561Sthompsavoid 444168793Sthompsalacp_linkstate(struct lagg_port *lgp) 445168561Sthompsa{ 446168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 447177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 448168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 449168561Sthompsa struct ifmediareq ifmr; 450168561Sthompsa int error = 0; 451168561Sthompsa u_int media; 452168561Sthompsa uint8_t old_state; 453168561Sthompsa uint16_t old_key; 454168561Sthompsa 455168561Sthompsa bzero((char *)&ifmr, sizeof(ifmr)); 456168561Sthompsa error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 457168561Sthompsa if (error != 0) 458168561Sthompsa return; 459168561Sthompsa 460177274Sthompsa LACP_LOCK(lsc); 461168561Sthompsa media = ifmr.ifm_active; 462169227Sthompsa LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 463169227Sthompsa "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 464169227Sthompsa (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 465168561Sthompsa old_state = lp->lp_state; 466168561Sthompsa old_key = lp->lp_key; 467168561Sthompsa 468168561Sthompsa lp->lp_media = media; 469170599Sthompsa /* 470169227Sthompsa * If the port is not an active full duplex Ethernet link then it can 471169227Sthompsa * not be aggregated. 472169227Sthompsa */ 473169227Sthompsa if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 474169227Sthompsa ifp->if_link_state != LINK_STATE_UP) { 475168561Sthompsa lacp_port_disable(lp); 476168561Sthompsa } else { 477168561Sthompsa lacp_port_enable(lp); 478168561Sthompsa } 479168561Sthompsa lp->lp_key = lacp_compose_key(lp); 480168561Sthompsa 481168561Sthompsa if (old_state != lp->lp_state || old_key != lp->lp_key) { 482168561Sthompsa LACP_DPRINTF((lp, "-> UNSELECTED\n")); 483168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 484168561Sthompsa } 485177274Sthompsa LACP_UNLOCK(lsc); 486168561Sthompsa} 487168561Sthompsa 488168561Sthompsastatic void 489168561Sthompsalacp_tick(void *arg) 490168561Sthompsa{ 491168561Sthompsa struct lacp_softc *lsc = arg; 492168561Sthompsa struct lacp_port *lp; 493168561Sthompsa 494168561Sthompsa LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 495168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 496168561Sthompsa continue; 497168561Sthompsa 498168561Sthompsa lacp_run_timers(lp); 499168561Sthompsa 500168561Sthompsa lacp_select(lp); 501168561Sthompsa lacp_sm_mux(lp); 502168561Sthompsa lacp_sm_tx(lp); 503168561Sthompsa lacp_sm_ptx_tx_schedule(lp); 504168561Sthompsa } 505168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 506168561Sthompsa} 507168561Sthompsa 508168561Sthompsaint 509168793Sthompsalacp_port_create(struct lagg_port *lgp) 510168561Sthompsa{ 511170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 512170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 513168561Sthompsa struct lacp_port *lp; 514168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 515168561Sthompsa struct sockaddr_dl sdl; 516168561Sthompsa struct ifmultiaddr *rifma = NULL; 517168561Sthompsa int error; 518168561Sthompsa 519168561Sthompsa boolean_t active = TRUE; /* XXX should be configurable */ 520168561Sthompsa boolean_t fast = FALSE; /* XXX should be configurable */ 521168561Sthompsa 522168561Sthompsa bzero((char *)&sdl, sizeof(sdl)); 523168561Sthompsa sdl.sdl_len = sizeof(sdl); 524168561Sthompsa sdl.sdl_family = AF_LINK; 525168561Sthompsa sdl.sdl_index = ifp->if_index; 526168561Sthompsa sdl.sdl_type = IFT_ETHER; 527168561Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 528168561Sthompsa 529168561Sthompsa bcopy(ðermulticastaddr_slowprotocols, 530168561Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 531168561Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 532168561Sthompsa if (error) { 533168793Sthompsa printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname); 534168561Sthompsa return (error); 535168561Sthompsa } 536168561Sthompsa 537168561Sthompsa lp = malloc(sizeof(struct lacp_port), 538168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 539168561Sthompsa if (lp == NULL) 540168561Sthompsa return (ENOMEM); 541168561Sthompsa 542177274Sthompsa LACP_LOCK(lsc); 543168793Sthompsa lgp->lp_psc = (caddr_t)lp; 544168561Sthompsa lp->lp_ifp = ifp; 545168793Sthompsa lp->lp_lagg = lgp; 546168561Sthompsa lp->lp_lsc = lsc; 547169327Sthompsa lp->lp_ifma = rifma; 548168561Sthompsa 549168561Sthompsa LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 550168561Sthompsa 551168561Sthompsa lacp_fill_actorinfo(lp, &lp->lp_actor); 552169739Sthompsa lacp_fill_markerinfo(lp, &lp->lp_marker); 553168561Sthompsa lp->lp_state = 554168561Sthompsa (active ? LACP_STATE_ACTIVITY : 0) | 555168561Sthompsa (fast ? LACP_STATE_TIMEOUT : 0); 556168561Sthompsa lp->lp_aggregator = NULL; 557177274Sthompsa lacp_sm_rx_set_expired(lp); 558177274Sthompsa LACP_UNLOCK(lsc); 559168793Sthompsa lacp_linkstate(lgp); 560168561Sthompsa 561168561Sthompsa return (0); 562168561Sthompsa} 563168561Sthompsa 564168561Sthompsavoid 565168793Sthompsalacp_port_destroy(struct lagg_port *lgp) 566168561Sthompsa{ 567168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 568177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 569169327Sthompsa int i; 570168561Sthompsa 571177274Sthompsa LACP_LOCK(lsc); 572168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 573168561Sthompsa LACP_TIMER_DISARM(lp, i); 574168561Sthompsa } 575168561Sthompsa 576168561Sthompsa lacp_disable_collecting(lp); 577168561Sthompsa lacp_disable_distributing(lp); 578168561Sthompsa lacp_unselect(lp); 579168561Sthompsa 580169328Sthompsa /* The address may have already been removed by if_purgemaddrs() */ 581169328Sthompsa if (!lgp->lp_detaching) 582169328Sthompsa if_delmulti_ifma(lp->lp_ifma); 583168561Sthompsa 584168561Sthompsa LIST_REMOVE(lp, lp_next); 585177274Sthompsa LACP_UNLOCK(lsc); 586168561Sthompsa free(lp, M_DEVBUF); 587168561Sthompsa} 588168561Sthompsa 589171247Sthompsavoid 590171247Sthompsalacp_req(struct lagg_softc *sc, caddr_t data) 591171247Sthompsa{ 592171247Sthompsa struct lacp_opreq *req = (struct lacp_opreq *)data; 593171247Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 594171247Sthompsa struct lacp_aggregator *la = lsc->lsc_active_aggregator; 595171247Sthompsa 596177274Sthompsa LACP_LOCK(lsc); 597171247Sthompsa bzero(req, sizeof(struct lacp_opreq)); 598171247Sthompsa if (la != NULL) { 599171247Sthompsa req->actor_prio = ntohs(la->la_actor.lip_systemid.lsi_prio); 600171247Sthompsa memcpy(&req->actor_mac, &la->la_actor.lip_systemid.lsi_mac, 601171247Sthompsa ETHER_ADDR_LEN); 602171247Sthompsa req->actor_key = ntohs(la->la_actor.lip_key); 603171247Sthompsa req->actor_portprio = ntohs(la->la_actor.lip_portid.lpi_prio); 604171247Sthompsa req->actor_portno = ntohs(la->la_actor.lip_portid.lpi_portno); 605171247Sthompsa req->actor_state = la->la_actor.lip_state; 606171247Sthompsa 607171247Sthompsa req->partner_prio = ntohs(la->la_partner.lip_systemid.lsi_prio); 608171247Sthompsa memcpy(&req->partner_mac, &la->la_partner.lip_systemid.lsi_mac, 609171247Sthompsa ETHER_ADDR_LEN); 610171247Sthompsa req->partner_key = ntohs(la->la_partner.lip_key); 611171247Sthompsa req->partner_portprio = ntohs(la->la_partner.lip_portid.lpi_prio); 612171247Sthompsa req->partner_portno = ntohs(la->la_partner.lip_portid.lpi_portno); 613171247Sthompsa req->partner_state = la->la_partner.lip_state; 614171247Sthompsa } 615177274Sthompsa LACP_UNLOCK(lsc); 616171247Sthompsa} 617171247Sthompsa 618171247Sthompsavoid 619171247Sthompsalacp_portreq(struct lagg_port *lgp, caddr_t data) 620171247Sthompsa{ 621171247Sthompsa struct lacp_opreq *req = (struct lacp_opreq *)data; 622171247Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 623177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 624171247Sthompsa 625177274Sthompsa LACP_LOCK(lsc); 626171247Sthompsa req->actor_prio = ntohs(lp->lp_actor.lip_systemid.lsi_prio); 627171247Sthompsa memcpy(&req->actor_mac, &lp->lp_actor.lip_systemid.lsi_mac, 628171247Sthompsa ETHER_ADDR_LEN); 629171247Sthompsa req->actor_key = ntohs(lp->lp_actor.lip_key); 630171247Sthompsa req->actor_portprio = ntohs(lp->lp_actor.lip_portid.lpi_prio); 631171247Sthompsa req->actor_portno = ntohs(lp->lp_actor.lip_portid.lpi_portno); 632171247Sthompsa req->actor_state = lp->lp_actor.lip_state; 633171247Sthompsa 634171247Sthompsa req->partner_prio = ntohs(lp->lp_partner.lip_systemid.lsi_prio); 635171247Sthompsa memcpy(&req->partner_mac, &lp->lp_partner.lip_systemid.lsi_mac, 636171247Sthompsa ETHER_ADDR_LEN); 637171247Sthompsa req->partner_key = ntohs(lp->lp_partner.lip_key); 638171247Sthompsa req->partner_portprio = ntohs(lp->lp_partner.lip_portid.lpi_prio); 639171247Sthompsa req->partner_portno = ntohs(lp->lp_partner.lip_portid.lpi_portno); 640171247Sthompsa req->partner_state = lp->lp_partner.lip_state; 641177274Sthompsa LACP_UNLOCK(lsc); 642171247Sthompsa} 643171247Sthompsa 644168561Sthompsastatic void 645168561Sthompsalacp_disable_collecting(struct lacp_port *lp) 646168561Sthompsa{ 647168561Sthompsa LACP_DPRINTF((lp, "collecting disabled\n")); 648168561Sthompsa lp->lp_state &= ~LACP_STATE_COLLECTING; 649168561Sthompsa} 650168561Sthompsa 651168561Sthompsastatic void 652168561Sthompsalacp_enable_collecting(struct lacp_port *lp) 653168561Sthompsa{ 654168561Sthompsa LACP_DPRINTF((lp, "collecting enabled\n")); 655168561Sthompsa lp->lp_state |= LACP_STATE_COLLECTING; 656168561Sthompsa} 657168561Sthompsa 658168561Sthompsastatic void 659168561Sthompsalacp_disable_distributing(struct lacp_port *lp) 660168561Sthompsa{ 661168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 662168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 663253314Sadrian struct lagg_softc *sc = lsc->lsc_softc; 664168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 665168561Sthompsa 666177274Sthompsa LACP_LOCK_ASSERT(lsc); 667168561Sthompsa 668168561Sthompsa if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 669168561Sthompsa return; 670168561Sthompsa } 671168561Sthompsa 672168561Sthompsa KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 673168561Sthompsa KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 674168561Sthompsa KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 675168561Sthompsa 676168561Sthompsa LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 677168561Sthompsa "nports %d -> %d\n", 678168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 679168561Sthompsa la->la_nports, la->la_nports - 1)); 680168561Sthompsa 681168561Sthompsa TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 682168561Sthompsa la->la_nports--; 683253314Sadrian sc->sc_active = la->la_nports; 684168561Sthompsa 685168561Sthompsa if (lsc->lsc_active_aggregator == la) { 686177274Sthompsa lacp_suppress_distributing(lsc, la); 687168561Sthompsa lacp_select_active_aggregator(lsc); 688177274Sthompsa /* regenerate the port map, the active aggregator has changed */ 689177274Sthompsa lacp_update_portmap(lsc); 690168561Sthompsa } 691177274Sthompsa 692177274Sthompsa lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 693168561Sthompsa} 694168561Sthompsa 695168561Sthompsastatic void 696168561Sthompsalacp_enable_distributing(struct lacp_port *lp) 697168561Sthompsa{ 698168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 699168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 700253314Sadrian struct lagg_softc *sc = lsc->lsc_softc; 701168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 702168561Sthompsa 703177274Sthompsa LACP_LOCK_ASSERT(lsc); 704168561Sthompsa 705168561Sthompsa if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 706168561Sthompsa return; 707168561Sthompsa } 708168561Sthompsa 709168561Sthompsa LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 710168561Sthompsa "nports %d -> %d\n", 711168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 712168561Sthompsa la->la_nports, la->la_nports + 1)); 713168561Sthompsa 714168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 715168561Sthompsa TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 716168561Sthompsa la->la_nports++; 717253314Sadrian sc->sc_active = la->la_nports; 718168561Sthompsa 719168561Sthompsa lp->lp_state |= LACP_STATE_DISTRIBUTING; 720168561Sthompsa 721177274Sthompsa if (lsc->lsc_active_aggregator == la) { 722177274Sthompsa lacp_suppress_distributing(lsc, la); 723177274Sthompsa lacp_update_portmap(lsc); 724177274Sthompsa } else 725177274Sthompsa /* try to become the active aggregator */ 726168561Sthompsa lacp_select_active_aggregator(lsc); 727168561Sthompsa} 728168561Sthompsa 729168561Sthompsastatic void 730168561Sthompsalacp_transit_expire(void *vp) 731168561Sthompsa{ 732168561Sthompsa struct lacp_softc *lsc = vp; 733168561Sthompsa 734177274Sthompsa LACP_LOCK_ASSERT(lsc); 735177274Sthompsa 736236062Sthompsa LACP_TRACE(NULL); 737236062Sthompsa 738168561Sthompsa lsc->lsc_suppress_distributing = FALSE; 739168561Sthompsa} 740168561Sthompsa 741253687Sadrianstatic void 742253687Sadrianlacp_attach_sysctl(struct lacp_softc *lsc, struct sysctl_oid *p_oid) 743253687Sadrian{ 744253687Sadrian struct lagg_softc *sc = lsc->lsc_softc; 745253687Sadrian 746253687Sadrian SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(p_oid), OID_AUTO, 747253687Sadrian "lacp_strict_mode", 748253687Sadrian CTLFLAG_RW, 749253687Sadrian &lsc->lsc_strict_mode, 750253687Sadrian lsc->lsc_strict_mode, 751253687Sadrian "Enable LACP strict mode"); 752253687Sadrian} 753253687Sadrian 754253687Sadrianstatic void 755253687Sadrianlacp_attach_sysctl_debug(struct lacp_softc *lsc, struct sysctl_oid *p_oid) 756253687Sadrian{ 757253687Sadrian struct lagg_softc *sc = lsc->lsc_softc; 758253687Sadrian struct sysctl_oid *oid; 759253687Sadrian 760253687Sadrian /* Create a child of the parent lagg interface */ 761253687Sadrian oid = SYSCTL_ADD_NODE(&sc->ctx, SYSCTL_CHILDREN(p_oid), 762253687Sadrian OID_AUTO, "debug", CTLFLAG_RD, NULL, "DEBUG"); 763253687Sadrian 764253687Sadrian SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 765253687Sadrian "rx_test", 766253687Sadrian CTLFLAG_RW, 767253687Sadrian &lsc->lsc_debug.lsc_rx_test, 768253687Sadrian lsc->lsc_debug.lsc_rx_test, 769253687Sadrian "Bitmap of if_dunit entries to drop RX frames for"); 770253687Sadrian SYSCTL_ADD_UINT(&sc->ctx, SYSCTL_CHILDREN(oid), OID_AUTO, 771253687Sadrian "tx_test", 772253687Sadrian CTLFLAG_RW, 773253687Sadrian &lsc->lsc_debug.lsc_tx_test, 774253687Sadrian lsc->lsc_debug.lsc_tx_test, 775253687Sadrian "Bitmap of if_dunit entries to drop TX frames for"); 776253687Sadrian} 777253687Sadrian 778168561Sthompsaint 779170599Sthompsalacp_attach(struct lagg_softc *sc) 780168561Sthompsa{ 781168561Sthompsa struct lacp_softc *lsc; 782253687Sadrian struct sysctl_oid *oid; 783168561Sthompsa 784168561Sthompsa lsc = malloc(sizeof(struct lacp_softc), 785168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 786168561Sthompsa if (lsc == NULL) 787168561Sthompsa return (ENOMEM); 788168561Sthompsa 789170599Sthompsa sc->sc_psc = (caddr_t)lsc; 790170599Sthompsa lsc->lsc_softc = sc; 791168561Sthompsa 792168561Sthompsa lsc->lsc_hashkey = arc4random(); 793168561Sthompsa lsc->lsc_active_aggregator = NULL; 794253687Sadrian lsc->lsc_strict_mode = 1; 795177274Sthompsa LACP_LOCK_INIT(lsc); 796168561Sthompsa TAILQ_INIT(&lsc->lsc_aggregators); 797168561Sthompsa LIST_INIT(&lsc->lsc_ports); 798168561Sthompsa 799253687Sadrian /* Create a child of the parent lagg interface */ 800253687Sadrian oid = SYSCTL_ADD_NODE(&sc->ctx, SYSCTL_CHILDREN(sc->sc_oid), 801253687Sadrian OID_AUTO, "lacp", CTLFLAG_RD, NULL, "LACP"); 802253687Sadrian 803253687Sadrian /* Attach sysctl nodes */ 804253687Sadrian lacp_attach_sysctl(lsc, oid); 805253687Sadrian lacp_attach_sysctl_debug(lsc, oid); 806253687Sadrian 807177274Sthompsa callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0); 808177274Sthompsa callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0); 809168561Sthompsa 810168793Sthompsa /* if the lagg is already up then do the same */ 811170599Sthompsa if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 812170599Sthompsa lacp_init(sc); 813168561Sthompsa 814168561Sthompsa return (0); 815168561Sthompsa} 816168561Sthompsa 817168561Sthompsaint 818170599Sthompsalacp_detach(struct lagg_softc *sc) 819168561Sthompsa{ 820170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 821168561Sthompsa 822168561Sthompsa KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 823168561Sthompsa ("aggregators still active")); 824168561Sthompsa KASSERT(lsc->lsc_active_aggregator == NULL, 825168561Sthompsa ("aggregator still attached")); 826168561Sthompsa 827170599Sthompsa sc->sc_psc = NULL; 828168561Sthompsa callout_drain(&lsc->lsc_transit_callout); 829168561Sthompsa callout_drain(&lsc->lsc_callout); 830168561Sthompsa 831177274Sthompsa LACP_LOCK_DESTROY(lsc); 832168561Sthompsa free(lsc, M_DEVBUF); 833168561Sthompsa return (0); 834168561Sthompsa} 835168561Sthompsa 836168561Sthompsavoid 837170599Sthompsalacp_init(struct lagg_softc *sc) 838168561Sthompsa{ 839170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 840168561Sthompsa 841177274Sthompsa LACP_LOCK(lsc); 842168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 843177274Sthompsa LACP_UNLOCK(lsc); 844168561Sthompsa} 845168561Sthompsa 846168561Sthompsavoid 847170599Sthompsalacp_stop(struct lagg_softc *sc) 848168561Sthompsa{ 849170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 850168561Sthompsa 851177274Sthompsa LACP_LOCK(lsc); 852168561Sthompsa callout_stop(&lsc->lsc_transit_callout); 853168561Sthompsa callout_stop(&lsc->lsc_callout); 854177274Sthompsa LACP_UNLOCK(lsc); 855168561Sthompsa} 856168561Sthompsa 857168793Sthompsastruct lagg_port * 858170599Sthompsalacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m) 859168561Sthompsa{ 860170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 861177274Sthompsa struct lacp_portmap *pm; 862168561Sthompsa struct lacp_port *lp; 863168561Sthompsa uint32_t hash; 864168561Sthompsa 865168561Sthompsa if (__predict_false(lsc->lsc_suppress_distributing)) { 866168561Sthompsa LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 867168561Sthompsa return (NULL); 868168561Sthompsa } 869168561Sthompsa 870177274Sthompsa pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 871177274Sthompsa if (pm->pm_count == 0) { 872168561Sthompsa LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 873168561Sthompsa return (NULL); 874168561Sthompsa } 875168561Sthompsa 876232008Sthompsa if (sc->use_flowid && (m->m_flags & M_FLOWID)) 877260179Sscottl hash = m->m_pkthdr.flowid >> sc->flowid_shift; 878191692Sthompsa else 879232629Sthompsa hash = lagg_hashmbuf(sc, m, lsc->lsc_hashkey); 880177274Sthompsa hash %= pm->pm_count; 881177274Sthompsa lp = pm->pm_map[hash]; 882168561Sthompsa 883168561Sthompsa KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 884168561Sthompsa ("aggregated port is not distributing")); 885168561Sthompsa 886168793Sthompsa return (lp->lp_lagg); 887168561Sthompsa} 888168561Sthompsa/* 889168561Sthompsa * lacp_suppress_distributing: drop transmit packets for a while 890168561Sthompsa * to preserve packet ordering. 891168561Sthompsa */ 892168561Sthompsa 893168561Sthompsastatic void 894168561Sthompsalacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 895168561Sthompsa{ 896169739Sthompsa struct lacp_port *lp; 897169739Sthompsa 898168561Sthompsa if (lsc->lsc_active_aggregator != la) { 899168561Sthompsa return; 900168561Sthompsa } 901168561Sthompsa 902236062Sthompsa LACP_TRACE(NULL); 903236062Sthompsa 904168561Sthompsa lsc->lsc_suppress_distributing = TRUE; 905169739Sthompsa 906169739Sthompsa /* send a marker frame down each port to verify the queues are empty */ 907169739Sthompsa LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 908169739Sthompsa lp->lp_flags |= LACP_PORT_MARK; 909169739Sthompsa lacp_xmit_marker(lp); 910169739Sthompsa } 911169739Sthompsa 912169739Sthompsa /* set a timeout for the marker frames */ 913168561Sthompsa callout_reset(&lsc->lsc_transit_callout, 914168561Sthompsa LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 915168561Sthompsa} 916168561Sthompsa 917168561Sthompsastatic int 918168561Sthompsalacp_compare_peerinfo(const struct lacp_peerinfo *a, 919168561Sthompsa const struct lacp_peerinfo *b) 920168561Sthompsa{ 921168561Sthompsa return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 922168561Sthompsa} 923168561Sthompsa 924168561Sthompsastatic int 925168561Sthompsalacp_compare_systemid(const struct lacp_systemid *a, 926168561Sthompsa const struct lacp_systemid *b) 927168561Sthompsa{ 928168561Sthompsa return (memcmp(a, b, sizeof(*a))); 929168561Sthompsa} 930168561Sthompsa 931168561Sthompsa#if 0 /* unused */ 932168561Sthompsastatic int 933168561Sthompsalacp_compare_portid(const struct lacp_portid *a, 934168561Sthompsa const struct lacp_portid *b) 935168561Sthompsa{ 936168561Sthompsa return (memcmp(a, b, sizeof(*a))); 937168561Sthompsa} 938168561Sthompsa#endif 939168561Sthompsa 940168561Sthompsastatic uint64_t 941168561Sthompsalacp_aggregator_bandwidth(struct lacp_aggregator *la) 942168561Sthompsa{ 943168561Sthompsa struct lacp_port *lp; 944168561Sthompsa uint64_t speed; 945168561Sthompsa 946168561Sthompsa lp = TAILQ_FIRST(&la->la_ports); 947168561Sthompsa if (lp == NULL) { 948168561Sthompsa return (0); 949168561Sthompsa } 950168561Sthompsa 951168561Sthompsa speed = ifmedia_baudrate(lp->lp_media); 952168561Sthompsa speed *= la->la_nports; 953168561Sthompsa if (speed == 0) { 954168561Sthompsa LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 955168561Sthompsa lp->lp_media, la->la_nports)); 956168561Sthompsa } 957168561Sthompsa 958168561Sthompsa return (speed); 959168561Sthompsa} 960168561Sthompsa 961168561Sthompsa/* 962168561Sthompsa * lacp_select_active_aggregator: select an aggregator to be used to transmit 963168793Sthompsa * packets from lagg(4) interface. 964168561Sthompsa */ 965168561Sthompsa 966168561Sthompsastatic void 967168561Sthompsalacp_select_active_aggregator(struct lacp_softc *lsc) 968168561Sthompsa{ 969168561Sthompsa struct lacp_aggregator *la; 970168561Sthompsa struct lacp_aggregator *best_la = NULL; 971168561Sthompsa uint64_t best_speed = 0; 972168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 973168561Sthompsa 974236062Sthompsa LACP_TRACE(NULL); 975168561Sthompsa 976168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 977168561Sthompsa uint64_t speed; 978168561Sthompsa 979168561Sthompsa if (la->la_nports == 0) { 980168561Sthompsa continue; 981168561Sthompsa } 982168561Sthompsa 983168561Sthompsa speed = lacp_aggregator_bandwidth(la); 984168561Sthompsa LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 985168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 986168561Sthompsa speed, la->la_nports)); 987169741Sthompsa 988169741Sthompsa /* This aggregator is chosen if 989169741Sthompsa * the partner has a better system priority 990169741Sthompsa * or, the total aggregated speed is higher 991169741Sthompsa * or, it is already the chosen aggregator 992169741Sthompsa */ 993169741Sthompsa if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 994169741Sthompsa LACP_SYS_PRI(best_la->la_partner)) || 995169741Sthompsa speed > best_speed || 996168561Sthompsa (speed == best_speed && 997168561Sthompsa la == lsc->lsc_active_aggregator)) { 998168561Sthompsa best_la = la; 999168561Sthompsa best_speed = speed; 1000168561Sthompsa } 1001168561Sthompsa } 1002168561Sthompsa 1003168561Sthompsa KASSERT(best_la == NULL || best_la->la_nports > 0, 1004168561Sthompsa ("invalid aggregator refcnt")); 1005168561Sthompsa KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 1006168561Sthompsa ("invalid aggregator list")); 1007168561Sthompsa 1008168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 1009168561Sthompsa LACP_DPRINTF((NULL, "active aggregator changed\n")); 1010168561Sthompsa LACP_DPRINTF((NULL, "old %s\n", 1011168561Sthompsa lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 1012168561Sthompsa buf, sizeof(buf)))); 1013168561Sthompsa } else { 1014168561Sthompsa LACP_DPRINTF((NULL, "active aggregator not changed\n")); 1015168561Sthompsa } 1016168561Sthompsa LACP_DPRINTF((NULL, "new %s\n", 1017168561Sthompsa lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 1018168561Sthompsa 1019168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 1020168561Sthompsa lsc->lsc_active_aggregator = best_la; 1021177274Sthompsa lacp_update_portmap(lsc); 1022168561Sthompsa if (best_la) { 1023168561Sthompsa lacp_suppress_distributing(lsc, best_la); 1024168561Sthompsa } 1025168561Sthompsa } 1026168561Sthompsa} 1027168561Sthompsa 1028177274Sthompsa/* 1029177274Sthompsa * Updated the inactive portmap array with the new list of ports and 1030177274Sthompsa * make it live. 1031177274Sthompsa */ 1032177274Sthompsastatic void 1033177274Sthompsalacp_update_portmap(struct lacp_softc *lsc) 1034177274Sthompsa{ 1035253314Sadrian struct lagg_softc *sc = lsc->lsc_softc; 1036177274Sthompsa struct lacp_aggregator *la; 1037177274Sthompsa struct lacp_portmap *p; 1038177274Sthompsa struct lacp_port *lp; 1039253314Sadrian uint64_t speed; 1040177274Sthompsa u_int newmap; 1041177274Sthompsa int i; 1042177274Sthompsa 1043177274Sthompsa newmap = lsc->lsc_activemap == 0 ? 1 : 0; 1044177274Sthompsa p = &lsc->lsc_pmap[newmap]; 1045177274Sthompsa la = lsc->lsc_active_aggregator; 1046253314Sadrian speed = 0; 1047177274Sthompsa bzero(p, sizeof(struct lacp_portmap)); 1048177274Sthompsa 1049177274Sthompsa if (la != NULL && la->la_nports > 0) { 1050177274Sthompsa p->pm_count = la->la_nports; 1051177274Sthompsa i = 0; 1052177274Sthompsa TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q) 1053177274Sthompsa p->pm_map[i++] = lp; 1054177274Sthompsa KASSERT(i == p->pm_count, ("Invalid port count")); 1055253314Sadrian speed = lacp_aggregator_bandwidth(la); 1056177274Sthompsa } 1057253314Sadrian sc->sc_ifp->if_baudrate = speed; 1058177274Sthompsa 1059177274Sthompsa /* switch the active portmap over */ 1060177274Sthompsa atomic_store_rel_int(&lsc->lsc_activemap, newmap); 1061177274Sthompsa LACP_DPRINTF((NULL, "Set table %d with %d ports\n", 1062177274Sthompsa lsc->lsc_activemap, 1063177274Sthompsa lsc->lsc_pmap[lsc->lsc_activemap].pm_count)); 1064177274Sthompsa} 1065177274Sthompsa 1066168561Sthompsastatic uint16_t 1067168561Sthompsalacp_compose_key(struct lacp_port *lp) 1068168561Sthompsa{ 1069168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 1070170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 1071168561Sthompsa u_int media = lp->lp_media; 1072168561Sthompsa uint16_t key; 1073168561Sthompsa 1074168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 1075168561Sthompsa 1076168561Sthompsa /* 1077168561Sthompsa * non-aggregatable links should have unique keys. 1078168561Sthompsa * 1079168561Sthompsa * XXX this isn't really unique as if_index is 16 bit. 1080168561Sthompsa */ 1081168561Sthompsa 1082168561Sthompsa /* bit 0..14: (some bits of) if_index of this port */ 1083168561Sthompsa key = lp->lp_ifp->if_index; 1084168561Sthompsa /* bit 15: 1 */ 1085168561Sthompsa key |= 0x8000; 1086168561Sthompsa } else { 1087168561Sthompsa u_int subtype = IFM_SUBTYPE(media); 1088168561Sthompsa 1089169227Sthompsa KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1090169227Sthompsa KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1091168561Sthompsa 1092257956Sae /* bit 0..4: IFM_SUBTYPE modulo speed */ 1093257956Sae switch (subtype) { 1094257956Sae case IFM_10_T: 1095257956Sae case IFM_10_2: 1096257956Sae case IFM_10_5: 1097257956Sae case IFM_10_STP: 1098257956Sae case IFM_10_FL: 1099257956Sae key = IFM_10_T; 1100257956Sae break; 1101257956Sae case IFM_100_TX: 1102257956Sae case IFM_100_FX: 1103257956Sae case IFM_100_T4: 1104257956Sae case IFM_100_VG: 1105257956Sae case IFM_100_T2: 1106257956Sae key = IFM_100_TX; 1107257956Sae break; 1108257956Sae case IFM_1000_SX: 1109257956Sae case IFM_1000_LX: 1110257956Sae case IFM_1000_CX: 1111257956Sae case IFM_1000_T: 1112257956Sae key = IFM_1000_SX; 1113257956Sae break; 1114257956Sae case IFM_10G_LR: 1115257956Sae case IFM_10G_SR: 1116257956Sae case IFM_10G_CX4: 1117257956Sae case IFM_10G_TWINAX: 1118257956Sae case IFM_10G_TWINAX_LONG: 1119257956Sae case IFM_10G_LRM: 1120257956Sae case IFM_10G_T: 1121257956Sae key = IFM_10G_LR; 1122257956Sae break; 1123257956Sae case IFM_40G_CR4: 1124257956Sae case IFM_40G_SR4: 1125257956Sae case IFM_40G_LR4: 1126257956Sae key = IFM_40G_CR4; 1127257956Sae break; 1128257956Sae default: 1129257956Sae key = subtype; 1130257956Sae } 1131168793Sthompsa /* bit 5..14: (some bits of) if_index of lagg device */ 1132170599Sthompsa key |= 0x7fe0 & ((sc->sc_ifp->if_index) << 5); 1133168561Sthompsa /* bit 15: 0 */ 1134168561Sthompsa } 1135168561Sthompsa return (htons(key)); 1136168561Sthompsa} 1137168561Sthompsa 1138168561Sthompsastatic void 1139168561Sthompsalacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1140168561Sthompsa{ 1141168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1142168561Sthompsa 1143168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1144168561Sthompsa __func__, 1145168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1146168561Sthompsa buf, sizeof(buf)), 1147168561Sthompsa la->la_refcnt, la->la_refcnt + 1)); 1148168561Sthompsa 1149168561Sthompsa KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1150168561Sthompsa la->la_refcnt++; 1151168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1152168561Sthompsa} 1153168561Sthompsa 1154168561Sthompsastatic void 1155168561Sthompsalacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1156168561Sthompsa{ 1157168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1158168561Sthompsa 1159168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1160168561Sthompsa __func__, 1161168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1162168561Sthompsa buf, sizeof(buf)), 1163168561Sthompsa la->la_refcnt, la->la_refcnt - 1)); 1164168561Sthompsa 1165168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1166168561Sthompsa la->la_refcnt--; 1167168561Sthompsa if (la->la_refcnt > 0) { 1168168561Sthompsa return; 1169168561Sthompsa } 1170168561Sthompsa 1171168561Sthompsa KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1172168561Sthompsa KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1173168561Sthompsa 1174168561Sthompsa TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1175168561Sthompsa 1176168561Sthompsa free(la, M_DEVBUF); 1177168561Sthompsa} 1178168561Sthompsa 1179168561Sthompsa/* 1180168561Sthompsa * lacp_aggregator_get: allocate an aggregator. 1181168561Sthompsa */ 1182168561Sthompsa 1183168561Sthompsastatic struct lacp_aggregator * 1184168561Sthompsalacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1185168561Sthompsa{ 1186168561Sthompsa struct lacp_aggregator *la; 1187168561Sthompsa 1188168561Sthompsa la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1189168561Sthompsa if (la) { 1190168561Sthompsa la->la_refcnt = 1; 1191168561Sthompsa la->la_nports = 0; 1192168561Sthompsa TAILQ_INIT(&la->la_ports); 1193168561Sthompsa la->la_pending = 0; 1194168561Sthompsa TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1195168561Sthompsa } 1196168561Sthompsa 1197168561Sthompsa return (la); 1198168561Sthompsa} 1199168561Sthompsa 1200168561Sthompsa/* 1201168561Sthompsa * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1202168561Sthompsa */ 1203168561Sthompsa 1204168561Sthompsastatic void 1205168561Sthompsalacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1206168561Sthompsa{ 1207168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1208168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1209168561Sthompsa 1210168561Sthompsa la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1211168561Sthompsa} 1212168561Sthompsa 1213168561Sthompsastatic void 1214168561Sthompsalacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1215168561Sthompsa const struct lacp_peerinfo *lpi_port) 1216168561Sthompsa{ 1217168561Sthompsa memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1218168561Sthompsa lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1219168561Sthompsa lpi_aggr->lip_key = lpi_port->lip_key; 1220168561Sthompsa} 1221168561Sthompsa 1222168561Sthompsa/* 1223168561Sthompsa * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1224168561Sthompsa */ 1225168561Sthompsa 1226168561Sthompsastatic int 1227168561Sthompsalacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1228168561Sthompsa const struct lacp_port *lp) 1229168561Sthompsa{ 1230168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1231168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1232168561Sthompsa return (0); 1233168561Sthompsa } 1234168561Sthompsa 1235168561Sthompsa if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1236168561Sthompsa return (0); 1237168561Sthompsa } 1238168561Sthompsa 1239168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1240168561Sthompsa return (0); 1241168561Sthompsa } 1242168561Sthompsa 1243168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1244168561Sthompsa return (0); 1245168561Sthompsa } 1246168561Sthompsa 1247168561Sthompsa return (1); 1248168561Sthompsa} 1249168561Sthompsa 1250168561Sthompsastatic int 1251168561Sthompsalacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1252168561Sthompsa const struct lacp_peerinfo *b) 1253168561Sthompsa{ 1254168561Sthompsa if (memcmp(&a->lip_systemid, &b->lip_systemid, 1255168561Sthompsa sizeof(a->lip_systemid))) { 1256168561Sthompsa return (0); 1257168561Sthompsa } 1258168561Sthompsa 1259168561Sthompsa if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1260168561Sthompsa return (0); 1261168561Sthompsa } 1262168561Sthompsa 1263168561Sthompsa return (1); 1264168561Sthompsa} 1265168561Sthompsa 1266168561Sthompsastatic void 1267168561Sthompsalacp_port_enable(struct lacp_port *lp) 1268168561Sthompsa{ 1269168561Sthompsa lp->lp_state |= LACP_STATE_AGGREGATION; 1270168561Sthompsa} 1271168561Sthompsa 1272168561Sthompsastatic void 1273168561Sthompsalacp_port_disable(struct lacp_port *lp) 1274168561Sthompsa{ 1275168561Sthompsa lacp_set_mux(lp, LACP_MUX_DETACHED); 1276168561Sthompsa 1277168561Sthompsa lp->lp_state &= ~LACP_STATE_AGGREGATION; 1278168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1279168561Sthompsa lacp_sm_rx_record_default(lp); 1280168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1281168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1282168561Sthompsa} 1283168561Sthompsa 1284168561Sthompsa/* 1285168561Sthompsa * lacp_select: select an aggregator. create one if necessary. 1286168561Sthompsa */ 1287168561Sthompsastatic void 1288168561Sthompsalacp_select(struct lacp_port *lp) 1289168561Sthompsa{ 1290168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1291168561Sthompsa struct lacp_aggregator *la; 1292168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1293168561Sthompsa 1294168561Sthompsa if (lp->lp_aggregator) { 1295168561Sthompsa return; 1296168561Sthompsa } 1297168561Sthompsa 1298168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1299168561Sthompsa ("timer_wait_while still active")); 1300168561Sthompsa 1301168561Sthompsa LACP_DPRINTF((lp, "port lagid=%s\n", 1302168561Sthompsa lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1303168561Sthompsa buf, sizeof(buf)))); 1304168561Sthompsa 1305168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1306168561Sthompsa if (lacp_aggregator_is_compatible(la, lp)) { 1307168561Sthompsa break; 1308168561Sthompsa } 1309168561Sthompsa } 1310168561Sthompsa 1311168561Sthompsa if (la == NULL) { 1312168561Sthompsa la = lacp_aggregator_get(lsc, lp); 1313168561Sthompsa if (la == NULL) { 1314168561Sthompsa LACP_DPRINTF((lp, "aggregator creation failed\n")); 1315168561Sthompsa 1316168561Sthompsa /* 1317168561Sthompsa * will retry on the next tick. 1318168561Sthompsa */ 1319168561Sthompsa 1320168561Sthompsa return; 1321168561Sthompsa } 1322168561Sthompsa lacp_fill_aggregator_id(la, lp); 1323168561Sthompsa LACP_DPRINTF((lp, "aggregator created\n")); 1324168561Sthompsa } else { 1325168561Sthompsa LACP_DPRINTF((lp, "compatible aggregator found\n")); 1326177274Sthompsa if (la->la_refcnt == LACP_MAX_PORTS) 1327177274Sthompsa return; 1328168561Sthompsa lacp_aggregator_addref(lsc, la); 1329168561Sthompsa } 1330168561Sthompsa 1331168561Sthompsa LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1332168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1333168561Sthompsa buf, sizeof(buf)))); 1334168561Sthompsa 1335168561Sthompsa lp->lp_aggregator = la; 1336168561Sthompsa lp->lp_selected = LACP_SELECTED; 1337168561Sthompsa} 1338168561Sthompsa 1339168561Sthompsa/* 1340168561Sthompsa * lacp_unselect: finish unselect/detach process. 1341168561Sthompsa */ 1342168561Sthompsa 1343168561Sthompsastatic void 1344168561Sthompsalacp_unselect(struct lacp_port *lp) 1345168561Sthompsa{ 1346168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1347168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1348168561Sthompsa 1349168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1350168561Sthompsa ("timer_wait_while still active")); 1351168561Sthompsa 1352168561Sthompsa if (la == NULL) { 1353168561Sthompsa return; 1354168561Sthompsa } 1355168561Sthompsa 1356168561Sthompsa lp->lp_aggregator = NULL; 1357168561Sthompsa lacp_aggregator_delref(lsc, la); 1358168561Sthompsa} 1359168561Sthompsa 1360168561Sthompsa/* mux machine */ 1361168561Sthompsa 1362168561Sthompsastatic void 1363168561Sthompsalacp_sm_mux(struct lacp_port *lp) 1364168561Sthompsa{ 1365253314Sadrian struct lagg_port *lgp = lp->lp_lagg; 1366253314Sadrian struct lagg_softc *sc = lgp->lp_softc; 1367168561Sthompsa enum lacp_mux_state new_state; 1368168561Sthompsa boolean_t p_sync = 1369168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1370168561Sthompsa boolean_t p_collecting = 1371168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1372168561Sthompsa enum lacp_selected selected = lp->lp_selected; 1373168561Sthompsa struct lacp_aggregator *la; 1374168561Sthompsa 1375236062Sthompsa if (lacp_debug > 1) 1376253314Sadrian lacp_dprintf(lp, "%s: state= 0x%x, selected= 0x%x, " 1377253314Sadrian "p_sync= 0x%x, p_collecting= 0x%x\n", __func__, 1378253314Sadrian lp->lp_mux_state, selected, p_sync, p_collecting); 1379168561Sthompsa 1380168561Sthompsare_eval: 1381168561Sthompsa la = lp->lp_aggregator; 1382168561Sthompsa KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1383168561Sthompsa ("MUX not detached")); 1384168561Sthompsa new_state = lp->lp_mux_state; 1385168561Sthompsa switch (lp->lp_mux_state) { 1386168561Sthompsa case LACP_MUX_DETACHED: 1387168561Sthompsa if (selected != LACP_UNSELECTED) { 1388168561Sthompsa new_state = LACP_MUX_WAITING; 1389168561Sthompsa } 1390168561Sthompsa break; 1391168561Sthompsa case LACP_MUX_WAITING: 1392168561Sthompsa KASSERT(la->la_pending > 0 || 1393168561Sthompsa !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1394168561Sthompsa ("timer_wait_while still active")); 1395168561Sthompsa if (selected == LACP_SELECTED && la->la_pending == 0) { 1396168561Sthompsa new_state = LACP_MUX_ATTACHED; 1397168561Sthompsa } else if (selected == LACP_UNSELECTED) { 1398168561Sthompsa new_state = LACP_MUX_DETACHED; 1399168561Sthompsa } 1400168561Sthompsa break; 1401168561Sthompsa case LACP_MUX_ATTACHED: 1402168561Sthompsa if (selected == LACP_SELECTED && p_sync) { 1403168561Sthompsa new_state = LACP_MUX_COLLECTING; 1404168561Sthompsa } else if (selected != LACP_SELECTED) { 1405168561Sthompsa new_state = LACP_MUX_DETACHED; 1406168561Sthompsa } 1407168561Sthompsa break; 1408168561Sthompsa case LACP_MUX_COLLECTING: 1409168561Sthompsa if (selected == LACP_SELECTED && p_sync && p_collecting) { 1410168561Sthompsa new_state = LACP_MUX_DISTRIBUTING; 1411168561Sthompsa } else if (selected != LACP_SELECTED || !p_sync) { 1412168561Sthompsa new_state = LACP_MUX_ATTACHED; 1413168561Sthompsa } 1414168561Sthompsa break; 1415168561Sthompsa case LACP_MUX_DISTRIBUTING: 1416168561Sthompsa if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1417168561Sthompsa new_state = LACP_MUX_COLLECTING; 1418253655Sadrian lacp_dprintf(lp, "Interface stopped DISTRIBUTING, possible flapping\n"); 1419253314Sadrian sc->sc_flapping++; 1420168561Sthompsa } 1421168561Sthompsa break; 1422168561Sthompsa default: 1423168561Sthompsa panic("%s: unknown state", __func__); 1424168561Sthompsa } 1425168561Sthompsa 1426168561Sthompsa if (lp->lp_mux_state == new_state) { 1427168561Sthompsa return; 1428168561Sthompsa } 1429168561Sthompsa 1430168561Sthompsa lacp_set_mux(lp, new_state); 1431168561Sthompsa goto re_eval; 1432168561Sthompsa} 1433168561Sthompsa 1434168561Sthompsastatic void 1435168561Sthompsalacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1436168561Sthompsa{ 1437168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1438168561Sthompsa 1439168561Sthompsa if (lp->lp_mux_state == new_state) { 1440168561Sthompsa return; 1441168561Sthompsa } 1442168561Sthompsa 1443168561Sthompsa switch (new_state) { 1444168561Sthompsa case LACP_MUX_DETACHED: 1445168561Sthompsa lp->lp_state &= ~LACP_STATE_SYNC; 1446168561Sthompsa lacp_disable_distributing(lp); 1447168561Sthompsa lacp_disable_collecting(lp); 1448168561Sthompsa lacp_sm_assert_ntt(lp); 1449168561Sthompsa /* cancel timer */ 1450168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1451168561Sthompsa KASSERT(la->la_pending > 0, 1452168561Sthompsa ("timer_wait_while not active")); 1453168561Sthompsa la->la_pending--; 1454168561Sthompsa } 1455168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1456168561Sthompsa lacp_unselect(lp); 1457168561Sthompsa break; 1458168561Sthompsa case LACP_MUX_WAITING: 1459168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1460168561Sthompsa LACP_AGGREGATE_WAIT_TIME); 1461168561Sthompsa la->la_pending++; 1462168561Sthompsa break; 1463168561Sthompsa case LACP_MUX_ATTACHED: 1464168561Sthompsa lp->lp_state |= LACP_STATE_SYNC; 1465168561Sthompsa lacp_disable_collecting(lp); 1466168561Sthompsa lacp_sm_assert_ntt(lp); 1467168561Sthompsa break; 1468168561Sthompsa case LACP_MUX_COLLECTING: 1469168561Sthompsa lacp_enable_collecting(lp); 1470168561Sthompsa lacp_disable_distributing(lp); 1471168561Sthompsa lacp_sm_assert_ntt(lp); 1472168561Sthompsa break; 1473168561Sthompsa case LACP_MUX_DISTRIBUTING: 1474168561Sthompsa lacp_enable_distributing(lp); 1475168561Sthompsa break; 1476168561Sthompsa default: 1477168561Sthompsa panic("%s: unknown state", __func__); 1478168561Sthompsa } 1479168561Sthompsa 1480168561Sthompsa LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1481168561Sthompsa 1482168561Sthompsa lp->lp_mux_state = new_state; 1483168561Sthompsa} 1484168561Sthompsa 1485168561Sthompsastatic void 1486168561Sthompsalacp_sm_mux_timer(struct lacp_port *lp) 1487168561Sthompsa{ 1488168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1489168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1490168561Sthompsa 1491168561Sthompsa KASSERT(la->la_pending > 0, ("no pending event")); 1492168561Sthompsa 1493168561Sthompsa LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1494168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1495168561Sthompsa buf, sizeof(buf)), 1496168561Sthompsa la->la_pending, la->la_pending - 1)); 1497168561Sthompsa 1498168561Sthompsa la->la_pending--; 1499168561Sthompsa} 1500168561Sthompsa 1501168561Sthompsa/* periodic transmit machine */ 1502168561Sthompsa 1503168561Sthompsastatic void 1504168561Sthompsalacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1505168561Sthompsa{ 1506168561Sthompsa if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1507168561Sthompsa LACP_STATE_TIMEOUT)) { 1508168561Sthompsa return; 1509168561Sthompsa } 1510168561Sthompsa 1511168561Sthompsa LACP_DPRINTF((lp, "partner timeout changed\n")); 1512168561Sthompsa 1513168561Sthompsa /* 1514168561Sthompsa * FAST_PERIODIC -> SLOW_PERIODIC 1515168561Sthompsa * or 1516168561Sthompsa * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1517168561Sthompsa * 1518168561Sthompsa * let lacp_sm_ptx_tx_schedule to update timeout. 1519168561Sthompsa */ 1520168561Sthompsa 1521168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1522168561Sthompsa 1523168561Sthompsa /* 1524168561Sthompsa * if timeout has been shortened, assert NTT. 1525168561Sthompsa */ 1526168561Sthompsa 1527168561Sthompsa if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1528168561Sthompsa lacp_sm_assert_ntt(lp); 1529168561Sthompsa } 1530168561Sthompsa} 1531168561Sthompsa 1532168561Sthompsastatic void 1533168561Sthompsalacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1534168561Sthompsa{ 1535168561Sthompsa int timeout; 1536168561Sthompsa 1537168561Sthompsa if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1538168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1539168561Sthompsa 1540168561Sthompsa /* 1541168561Sthompsa * NO_PERIODIC 1542168561Sthompsa */ 1543168561Sthompsa 1544168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1545168561Sthompsa return; 1546168561Sthompsa } 1547168561Sthompsa 1548168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1549168561Sthompsa return; 1550168561Sthompsa } 1551168561Sthompsa 1552168561Sthompsa timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1553168561Sthompsa LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1554168561Sthompsa 1555168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1556168561Sthompsa} 1557168561Sthompsa 1558168561Sthompsastatic void 1559168561Sthompsalacp_sm_ptx_timer(struct lacp_port *lp) 1560168561Sthompsa{ 1561168561Sthompsa lacp_sm_assert_ntt(lp); 1562168561Sthompsa} 1563168561Sthompsa 1564168561Sthompsastatic void 1565168561Sthompsalacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1566168561Sthompsa{ 1567168561Sthompsa int timeout; 1568168561Sthompsa 1569168561Sthompsa /* 1570168561Sthompsa * check LACP_DISABLED first 1571168561Sthompsa */ 1572168561Sthompsa 1573168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1574168561Sthompsa return; 1575168561Sthompsa } 1576168561Sthompsa 1577168561Sthompsa /* 1578168561Sthompsa * check loopback condition. 1579168561Sthompsa */ 1580168561Sthompsa 1581168561Sthompsa if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1582168561Sthompsa &lp->lp_actor.lip_systemid)) { 1583168561Sthompsa return; 1584168561Sthompsa } 1585168561Sthompsa 1586168561Sthompsa /* 1587168561Sthompsa * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1588168561Sthompsa */ 1589168561Sthompsa 1590168561Sthompsa lacp_sm_rx_update_selected(lp, du); 1591168561Sthompsa lacp_sm_rx_update_ntt(lp, du); 1592168561Sthompsa lacp_sm_rx_record_pdu(lp, du); 1593168561Sthompsa 1594168561Sthompsa timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1595168561Sthompsa LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1596168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1597168561Sthompsa 1598168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1599168561Sthompsa 1600168561Sthompsa /* 1601168561Sthompsa * kick transmit machine without waiting the next tick. 1602168561Sthompsa */ 1603168561Sthompsa 1604168561Sthompsa lacp_sm_tx(lp); 1605168561Sthompsa} 1606168561Sthompsa 1607168561Sthompsastatic void 1608168561Sthompsalacp_sm_rx_set_expired(struct lacp_port *lp) 1609168561Sthompsa{ 1610168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1611168561Sthompsa lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1612168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1613168561Sthompsa lp->lp_state |= LACP_STATE_EXPIRED; 1614168561Sthompsa} 1615168561Sthompsa 1616168561Sthompsastatic void 1617168561Sthompsalacp_sm_rx_timer(struct lacp_port *lp) 1618168561Sthompsa{ 1619168561Sthompsa if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1620168561Sthompsa /* CURRENT -> EXPIRED */ 1621168561Sthompsa LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1622168561Sthompsa lacp_sm_rx_set_expired(lp); 1623168561Sthompsa } else { 1624168561Sthompsa /* EXPIRED -> DEFAULTED */ 1625168561Sthompsa LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1626168561Sthompsa lacp_sm_rx_update_default_selected(lp); 1627168561Sthompsa lacp_sm_rx_record_default(lp); 1628168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1629168561Sthompsa } 1630168561Sthompsa} 1631168561Sthompsa 1632168561Sthompsastatic void 1633168561Sthompsalacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1634168561Sthompsa{ 1635168561Sthompsa boolean_t active; 1636168561Sthompsa uint8_t oldpstate; 1637168561Sthompsa char buf[LACP_STATESTR_MAX+1]; 1638168561Sthompsa 1639236062Sthompsa LACP_TRACE(lp); 1640168561Sthompsa 1641168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1642168561Sthompsa 1643168561Sthompsa active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1644168561Sthompsa || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1645168561Sthompsa (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1646168561Sthompsa 1647168561Sthompsa lp->lp_partner = du->ldu_actor; 1648168561Sthompsa if (active && 1649168561Sthompsa ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1650168561Sthompsa LACP_STATE_AGGREGATION) && 1651168561Sthompsa !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1652168561Sthompsa || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1653168561Sthompsa /* XXX nothing? */ 1654168561Sthompsa } else { 1655168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1656168561Sthompsa } 1657168561Sthompsa 1658168561Sthompsa lp->lp_state &= ~LACP_STATE_DEFAULTED; 1659168561Sthompsa 1660168561Sthompsa if (oldpstate != lp->lp_partner.lip_state) { 1661168561Sthompsa LACP_DPRINTF((lp, "old pstate %s\n", 1662168561Sthompsa lacp_format_state(oldpstate, buf, sizeof(buf)))); 1663168561Sthompsa LACP_DPRINTF((lp, "new pstate %s\n", 1664168561Sthompsa lacp_format_state(lp->lp_partner.lip_state, buf, 1665168561Sthompsa sizeof(buf)))); 1666168561Sthompsa } 1667168561Sthompsa 1668253314Sadrian /* XXX Hack, still need to implement 5.4.9 para 2,3,4 */ 1669253687Sadrian if (lp->lp_lsc->lsc_strict_mode) 1670253314Sadrian lp->lp_partner.lip_state |= LACP_STATE_SYNC; 1671253314Sadrian 1672168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1673168561Sthompsa} 1674168561Sthompsa 1675168561Sthompsastatic void 1676168561Sthompsalacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1677168561Sthompsa{ 1678168561Sthompsa 1679236062Sthompsa LACP_TRACE(lp); 1680236062Sthompsa 1681168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1682168561Sthompsa !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1683168561Sthompsa LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1684168561Sthompsa LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1685168561Sthompsa lacp_sm_assert_ntt(lp); 1686168561Sthompsa } 1687168561Sthompsa} 1688168561Sthompsa 1689168561Sthompsastatic void 1690168561Sthompsalacp_sm_rx_record_default(struct lacp_port *lp) 1691168561Sthompsa{ 1692168561Sthompsa uint8_t oldpstate; 1693168561Sthompsa 1694236062Sthompsa LACP_TRACE(lp); 1695168561Sthompsa 1696168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1697253687Sadrian if (lp->lp_lsc->lsc_strict_mode) 1698253314Sadrian lp->lp_partner = lacp_partner_admin_strict; 1699253314Sadrian else 1700253314Sadrian lp->lp_partner = lacp_partner_admin_optimistic;; 1701168561Sthompsa lp->lp_state |= LACP_STATE_DEFAULTED; 1702168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1703168561Sthompsa} 1704168561Sthompsa 1705168561Sthompsastatic void 1706168561Sthompsalacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1707168561Sthompsa const struct lacp_peerinfo *info) 1708168561Sthompsa{ 1709168561Sthompsa 1710236062Sthompsa LACP_TRACE(lp); 1711236062Sthompsa 1712168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1713168561Sthompsa !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1714168561Sthompsa LACP_STATE_AGGREGATION)) { 1715168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1716168561Sthompsa /* mux machine will clean up lp->lp_aggregator */ 1717168561Sthompsa } 1718168561Sthompsa} 1719168561Sthompsa 1720168561Sthompsastatic void 1721168561Sthompsalacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1722168561Sthompsa{ 1723168561Sthompsa 1724236062Sthompsa LACP_TRACE(lp); 1725236062Sthompsa 1726168561Sthompsa lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1727168561Sthompsa} 1728168561Sthompsa 1729168561Sthompsastatic void 1730168561Sthompsalacp_sm_rx_update_default_selected(struct lacp_port *lp) 1731168561Sthompsa{ 1732168561Sthompsa 1733236062Sthompsa LACP_TRACE(lp); 1734236062Sthompsa 1735253687Sadrian if (lp->lp_lsc->lsc_strict_mode) 1736253314Sadrian lacp_sm_rx_update_selected_from_peerinfo(lp, 1737253314Sadrian &lacp_partner_admin_strict); 1738253314Sadrian else 1739253314Sadrian lacp_sm_rx_update_selected_from_peerinfo(lp, 1740253314Sadrian &lacp_partner_admin_optimistic); 1741168561Sthompsa} 1742168561Sthompsa 1743168561Sthompsa/* transmit machine */ 1744168561Sthompsa 1745168561Sthompsastatic void 1746168561Sthompsalacp_sm_tx(struct lacp_port *lp) 1747168561Sthompsa{ 1748253314Sadrian int error = 0; 1749168561Sthompsa 1750168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1751168561Sthompsa#if 1 1752168561Sthompsa || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1753168561Sthompsa && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1754168561Sthompsa#endif 1755168561Sthompsa ) { 1756168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1757168561Sthompsa } 1758168561Sthompsa 1759168561Sthompsa if (!(lp->lp_flags & LACP_PORT_NTT)) { 1760168561Sthompsa return; 1761168561Sthompsa } 1762168561Sthompsa 1763168561Sthompsa /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1764168561Sthompsa if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1765168561Sthompsa (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1766168561Sthompsa LACP_DPRINTF((lp, "rate limited pdu\n")); 1767168561Sthompsa return; 1768168561Sthompsa } 1769168561Sthompsa 1770253687Sadrian if (((1 << lp->lp_ifp->if_dunit) & lp->lp_lsc->lsc_debug.lsc_tx_test) == 0) { 1771253314Sadrian error = lacp_xmit_lacpdu(lp); 1772253687Sadrian } else { 1773253314Sadrian LACP_TPRINTF((lp, "Dropping TX PDU\n")); 1774253687Sadrian } 1775168561Sthompsa 1776168561Sthompsa if (error == 0) { 1777168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1778168561Sthompsa } else { 1779168561Sthompsa LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1780168561Sthompsa error)); 1781168561Sthompsa } 1782168561Sthompsa} 1783168561Sthompsa 1784168561Sthompsastatic void 1785168561Sthompsalacp_sm_assert_ntt(struct lacp_port *lp) 1786168561Sthompsa{ 1787168561Sthompsa 1788168561Sthompsa lp->lp_flags |= LACP_PORT_NTT; 1789168561Sthompsa} 1790168561Sthompsa 1791168561Sthompsastatic void 1792168561Sthompsalacp_run_timers(struct lacp_port *lp) 1793168561Sthompsa{ 1794168561Sthompsa int i; 1795168561Sthompsa 1796168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 1797168561Sthompsa KASSERT(lp->lp_timer[i] >= 0, 1798168561Sthompsa ("invalid timer value %d", lp->lp_timer[i])); 1799168561Sthompsa if (lp->lp_timer[i] == 0) { 1800168561Sthompsa continue; 1801168561Sthompsa } else if (--lp->lp_timer[i] <= 0) { 1802168561Sthompsa if (lacp_timer_funcs[i]) { 1803168561Sthompsa (*lacp_timer_funcs[i])(lp); 1804168561Sthompsa } 1805168561Sthompsa } 1806168561Sthompsa } 1807168561Sthompsa} 1808168561Sthompsa 1809168561Sthompsaint 1810177274Sthompsalacp_marker_input(struct lacp_port *lp, struct mbuf *m) 1811168561Sthompsa{ 1812177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1813177274Sthompsa struct lagg_port *lgp = lp->lp_lagg; 1814169739Sthompsa struct lacp_port *lp2; 1815168561Sthompsa struct markerdu *mdu; 1816168561Sthompsa int error = 0; 1817169739Sthompsa int pending = 0; 1818168561Sthompsa 1819168561Sthompsa if (m->m_pkthdr.len != sizeof(*mdu)) { 1820168561Sthompsa goto bad; 1821168561Sthompsa } 1822168561Sthompsa 1823168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 1824168561Sthompsa goto bad; 1825168561Sthompsa } 1826168561Sthompsa 1827168561Sthompsa if (m->m_len < sizeof(*mdu)) { 1828168561Sthompsa m = m_pullup(m, sizeof(*mdu)); 1829168561Sthompsa if (m == NULL) { 1830168561Sthompsa return (ENOMEM); 1831168561Sthompsa } 1832168561Sthompsa } 1833168561Sthompsa 1834168561Sthompsa mdu = mtod(m, struct markerdu *); 1835168561Sthompsa 1836168561Sthompsa if (memcmp(&mdu->mdu_eh.ether_dhost, 1837168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1838168561Sthompsa goto bad; 1839168561Sthompsa } 1840168561Sthompsa 1841168561Sthompsa if (mdu->mdu_sph.sph_version != 1) { 1842168561Sthompsa goto bad; 1843168561Sthompsa } 1844168561Sthompsa 1845168561Sthompsa switch (mdu->mdu_tlv.tlv_type) { 1846168561Sthompsa case MARKER_TYPE_INFO: 1847168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1848168561Sthompsa marker_info_tlv_template, TRUE)) { 1849168561Sthompsa goto bad; 1850168561Sthompsa } 1851168561Sthompsa mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1852168561Sthompsa memcpy(&mdu->mdu_eh.ether_dhost, 1853168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1854168561Sthompsa memcpy(&mdu->mdu_eh.ether_shost, 1855168793Sthompsa lgp->lp_lladdr, ETHER_ADDR_LEN); 1856168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 1857168561Sthompsa break; 1858168561Sthompsa 1859168561Sthompsa case MARKER_TYPE_RESPONSE: 1860168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1861168561Sthompsa marker_response_tlv_template, TRUE)) { 1862168561Sthompsa goto bad; 1863168561Sthompsa } 1864169739Sthompsa LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 1865169739Sthompsa ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 1866169739Sthompsa ":", ntohl(mdu->mdu_info.mi_rq_xid))); 1867169739Sthompsa 1868169739Sthompsa /* Verify that it is the last marker we sent out */ 1869169739Sthompsa if (memcmp(&mdu->mdu_info, &lp->lp_marker, 1870169739Sthompsa sizeof(struct lacp_markerinfo))) 1871169739Sthompsa goto bad; 1872169739Sthompsa 1873177274Sthompsa LACP_LOCK(lsc); 1874169739Sthompsa lp->lp_flags &= ~LACP_PORT_MARK; 1875169739Sthompsa 1876169739Sthompsa if (lsc->lsc_suppress_distributing) { 1877169739Sthompsa /* Check if any ports are waiting for a response */ 1878169739Sthompsa LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 1879169739Sthompsa if (lp2->lp_flags & LACP_PORT_MARK) { 1880169739Sthompsa pending = 1; 1881169739Sthompsa break; 1882169739Sthompsa } 1883169739Sthompsa } 1884169739Sthompsa 1885169739Sthompsa if (pending == 0) { 1886169739Sthompsa /* All interface queues are clear */ 1887169739Sthompsa LACP_DPRINTF((NULL, "queue flush complete\n")); 1888169739Sthompsa lsc->lsc_suppress_distributing = FALSE; 1889169739Sthompsa } 1890169739Sthompsa } 1891177274Sthompsa LACP_UNLOCK(lsc); 1892169739Sthompsa m_freem(m); 1893169739Sthompsa break; 1894169739Sthompsa 1895168561Sthompsa default: 1896168561Sthompsa goto bad; 1897168561Sthompsa } 1898168561Sthompsa 1899168561Sthompsa return (error); 1900168561Sthompsa 1901168561Sthompsabad: 1902169739Sthompsa LACP_DPRINTF((lp, "bad marker frame\n")); 1903168561Sthompsa m_freem(m); 1904168561Sthompsa return (EINVAL); 1905168561Sthompsa} 1906168561Sthompsa 1907168561Sthompsastatic int 1908168561Sthompsatlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1909168561Sthompsa const struct tlv_template *tmpl, boolean_t check_type) 1910168561Sthompsa{ 1911168561Sthompsa while (/* CONSTCOND */ 1) { 1912168561Sthompsa if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1913168561Sthompsa return (EINVAL); 1914168561Sthompsa } 1915168561Sthompsa if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1916168561Sthompsa tlv->tlv_length != tmpl->tmpl_length) { 1917168561Sthompsa return (EINVAL); 1918168561Sthompsa } 1919168561Sthompsa if (tmpl->tmpl_type == 0) { 1920168561Sthompsa break; 1921168561Sthompsa } 1922168561Sthompsa tlv = (const struct tlvhdr *) 1923168561Sthompsa ((const char *)tlv + tlv->tlv_length); 1924168561Sthompsa tmpl++; 1925168561Sthompsa } 1926168561Sthompsa 1927168561Sthompsa return (0); 1928168561Sthompsa} 1929168561Sthompsa 1930236062Sthompsa/* Debugging */ 1931168561Sthompsaconst char * 1932168561Sthompsalacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1933168561Sthompsa{ 1934168561Sthompsa snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1935168561Sthompsa (int)mac[0], 1936168561Sthompsa (int)mac[1], 1937168561Sthompsa (int)mac[2], 1938168561Sthompsa (int)mac[3], 1939168561Sthompsa (int)mac[4], 1940168561Sthompsa (int)mac[5]); 1941168561Sthompsa 1942168561Sthompsa return (buf); 1943168561Sthompsa} 1944168561Sthompsa 1945168561Sthompsaconst char * 1946168561Sthompsalacp_format_systemid(const struct lacp_systemid *sysid, 1947168561Sthompsa char *buf, size_t buflen) 1948168561Sthompsa{ 1949168561Sthompsa char macbuf[LACP_MACSTR_MAX+1]; 1950168561Sthompsa 1951168561Sthompsa snprintf(buf, buflen, "%04X,%s", 1952168561Sthompsa ntohs(sysid->lsi_prio), 1953168561Sthompsa lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1954168561Sthompsa 1955168561Sthompsa return (buf); 1956168561Sthompsa} 1957168561Sthompsa 1958168561Sthompsaconst char * 1959168561Sthompsalacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1960168561Sthompsa{ 1961168561Sthompsa snprintf(buf, buflen, "%04X,%04X", 1962168561Sthompsa ntohs(portid->lpi_prio), 1963168561Sthompsa ntohs(portid->lpi_portno)); 1964168561Sthompsa 1965168561Sthompsa return (buf); 1966168561Sthompsa} 1967168561Sthompsa 1968168561Sthompsaconst char * 1969168561Sthompsalacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1970168561Sthompsa{ 1971168561Sthompsa char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1972168561Sthompsa char portid[LACP_PORTIDSTR_MAX+1]; 1973168561Sthompsa 1974168561Sthompsa snprintf(buf, buflen, "(%s,%04X,%s)", 1975168561Sthompsa lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1976168561Sthompsa ntohs(peer->lip_key), 1977168561Sthompsa lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1978168561Sthompsa 1979168561Sthompsa return (buf); 1980168561Sthompsa} 1981168561Sthompsa 1982168561Sthompsaconst char * 1983168561Sthompsalacp_format_lagid(const struct lacp_peerinfo *a, 1984168561Sthompsa const struct lacp_peerinfo *b, char *buf, size_t buflen) 1985168561Sthompsa{ 1986168561Sthompsa char astr[LACP_PARTNERSTR_MAX+1]; 1987168561Sthompsa char bstr[LACP_PARTNERSTR_MAX+1]; 1988168561Sthompsa 1989168561Sthompsa#if 0 1990168561Sthompsa /* 1991168561Sthompsa * there's a convention to display small numbered peer 1992168561Sthompsa * in the left. 1993168561Sthompsa */ 1994168561Sthompsa 1995168561Sthompsa if (lacp_compare_peerinfo(a, b) > 0) { 1996168561Sthompsa const struct lacp_peerinfo *t; 1997168561Sthompsa 1998168561Sthompsa t = a; 1999168561Sthompsa a = b; 2000168561Sthompsa b = t; 2001168561Sthompsa } 2002168561Sthompsa#endif 2003168561Sthompsa 2004168561Sthompsa snprintf(buf, buflen, "[%s,%s]", 2005168561Sthompsa lacp_format_partner(a, astr, sizeof(astr)), 2006168561Sthompsa lacp_format_partner(b, bstr, sizeof(bstr))); 2007168561Sthompsa 2008168561Sthompsa return (buf); 2009168561Sthompsa} 2010168561Sthompsa 2011168561Sthompsaconst char * 2012168561Sthompsalacp_format_lagid_aggregator(const struct lacp_aggregator *la, 2013168561Sthompsa char *buf, size_t buflen) 2014168561Sthompsa{ 2015168561Sthompsa if (la == NULL) { 2016168561Sthompsa return ("(none)"); 2017168561Sthompsa } 2018168561Sthompsa 2019168561Sthompsa return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 2020168561Sthompsa} 2021168561Sthompsa 2022168561Sthompsaconst char * 2023168561Sthompsalacp_format_state(uint8_t state, char *buf, size_t buflen) 2024168561Sthompsa{ 2025168561Sthompsa snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 2026168561Sthompsa return (buf); 2027168561Sthompsa} 2028168561Sthompsa 2029168561Sthompsastatic void 2030168561Sthompsalacp_dump_lacpdu(const struct lacpdu *du) 2031168561Sthompsa{ 2032168561Sthompsa char buf[LACP_PARTNERSTR_MAX+1]; 2033168561Sthompsa char buf2[LACP_STATESTR_MAX+1]; 2034168561Sthompsa 2035168561Sthompsa printf("actor=%s\n", 2036168561Sthompsa lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 2037168561Sthompsa printf("actor.state=%s\n", 2038168561Sthompsa lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 2039168561Sthompsa printf("partner=%s\n", 2040168561Sthompsa lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 2041168561Sthompsa printf("partner.state=%s\n", 2042168561Sthompsa lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 2043168561Sthompsa 2044168561Sthompsa printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 2045168561Sthompsa} 2046168561Sthompsa 2047168561Sthompsastatic void 2048168561Sthompsalacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 2049168561Sthompsa{ 2050168561Sthompsa va_list va; 2051168561Sthompsa 2052168561Sthompsa if (lp) { 2053168561Sthompsa printf("%s: ", lp->lp_ifp->if_xname); 2054168561Sthompsa } 2055168561Sthompsa 2056168561Sthompsa va_start(va, fmt); 2057168561Sthompsa vprintf(fmt, va); 2058168561Sthompsa va_end(va); 2059168561Sthompsa} 2060