ieee8023ad_lacp.c revision 232008
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: head/sys/net/ieee8023ad_lacp.c 232008 2012-02-22 22:01:30Z thompsa $"); 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> 41168561Sthompsa#include <machine/stdarg.h> 42169569Sthompsa#include <sys/lock.h> 43169569Sthompsa#include <sys/rwlock.h> 44168561Sthompsa 45168561Sthompsa#include <net/if.h> 46168561Sthompsa#include <net/if_dl.h> 47168561Sthompsa#include <net/ethernet.h> 48168561Sthompsa#include <net/if_media.h> 49168561Sthompsa#include <net/if_types.h> 50168561Sthompsa 51168793Sthompsa#include <net/if_lagg.h> 52168561Sthompsa#include <net/ieee8023ad_lacp.h> 53168561Sthompsa 54168561Sthompsa/* 55168561Sthompsa * actor system priority and port priority. 56168561Sthompsa * XXX should be configurable. 57168561Sthompsa */ 58168561Sthompsa 59168561Sthompsa#define LACP_SYSTEM_PRIO 0x8000 60168561Sthompsa#define LACP_PORT_PRIO 0x8000 61168561Sthompsa 62168561Sthompsaconst uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 63168561Sthompsa { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 64168561Sthompsa 65168561Sthompsastatic const struct tlv_template lacp_info_tlv_template[] = { 66168561Sthompsa { LACP_TYPE_ACTORINFO, 67168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 68168561Sthompsa { LACP_TYPE_PARTNERINFO, 69168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 70168561Sthompsa { LACP_TYPE_COLLECTORINFO, 71168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 72168561Sthompsa { 0, 0 }, 73168561Sthompsa}; 74168561Sthompsa 75168561Sthompsastatic const struct tlv_template marker_info_tlv_template[] = { 76169739Sthompsa { MARKER_TYPE_INFO, 77169739Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 78168561Sthompsa { 0, 0 }, 79168561Sthompsa}; 80168561Sthompsa 81168561Sthompsastatic const struct tlv_template marker_response_tlv_template[] = { 82169739Sthompsa { MARKER_TYPE_RESPONSE, 83169739Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 84168561Sthompsa { 0, 0 }, 85168561Sthompsa}; 86168561Sthompsa 87177274Sthompsatypedef void (*lacp_timer_func_t)(struct lacp_port *); 88177274Sthompsa 89168561Sthompsastatic void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 90169739Sthompsastatic void lacp_fill_markerinfo(struct lacp_port *, 91169739Sthompsa struct lacp_markerinfo *); 92168561Sthompsa 93168561Sthompsastatic uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 94168561Sthompsastatic void lacp_suppress_distributing(struct lacp_softc *, 95168561Sthompsa struct lacp_aggregator *); 96168561Sthompsastatic void lacp_transit_expire(void *); 97177274Sthompsastatic void lacp_update_portmap(struct lacp_softc *); 98168561Sthompsastatic void lacp_select_active_aggregator(struct lacp_softc *); 99168561Sthompsastatic uint16_t lacp_compose_key(struct lacp_port *); 100168561Sthompsastatic int tlv_check(const void *, size_t, const struct tlvhdr *, 101168561Sthompsa const struct tlv_template *, boolean_t); 102168561Sthompsastatic void lacp_tick(void *); 103168561Sthompsa 104168561Sthompsastatic void lacp_fill_aggregator_id(struct lacp_aggregator *, 105168561Sthompsa const struct lacp_port *); 106168561Sthompsastatic void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 107168561Sthompsa const struct lacp_peerinfo *); 108168561Sthompsastatic int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 109168561Sthompsa const struct lacp_port *); 110168561Sthompsastatic int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 111168561Sthompsa const struct lacp_peerinfo *); 112168561Sthompsa 113168561Sthompsastatic struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 114168561Sthompsa struct lacp_port *); 115168561Sthompsastatic void lacp_aggregator_addref(struct lacp_softc *, 116168561Sthompsa struct lacp_aggregator *); 117168561Sthompsastatic void lacp_aggregator_delref(struct lacp_softc *, 118168561Sthompsa struct lacp_aggregator *); 119168561Sthompsa 120168561Sthompsa/* receive machine */ 121168561Sthompsa 122177274Sthompsastatic int lacp_pdu_input(struct lacp_port *, struct mbuf *); 123177274Sthompsastatic int lacp_marker_input(struct lacp_port *, struct mbuf *); 124168561Sthompsastatic void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 125168561Sthompsastatic void lacp_sm_rx_timer(struct lacp_port *); 126168561Sthompsastatic void lacp_sm_rx_set_expired(struct lacp_port *); 127168561Sthompsastatic void lacp_sm_rx_update_ntt(struct lacp_port *, 128168561Sthompsa const struct lacpdu *); 129168561Sthompsastatic void lacp_sm_rx_record_pdu(struct lacp_port *, 130168561Sthompsa const struct lacpdu *); 131168561Sthompsastatic void lacp_sm_rx_update_selected(struct lacp_port *, 132168561Sthompsa const struct lacpdu *); 133168561Sthompsastatic void lacp_sm_rx_record_default(struct lacp_port *); 134168561Sthompsastatic void lacp_sm_rx_update_default_selected(struct lacp_port *); 135168561Sthompsastatic void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 136168561Sthompsa const struct lacp_peerinfo *); 137168561Sthompsa 138168561Sthompsa/* mux machine */ 139168561Sthompsa 140168561Sthompsastatic void lacp_sm_mux(struct lacp_port *); 141168561Sthompsastatic void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 142168561Sthompsastatic void lacp_sm_mux_timer(struct lacp_port *); 143168561Sthompsa 144168561Sthompsa/* periodic transmit machine */ 145168561Sthompsa 146168561Sthompsastatic void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 147168561Sthompsastatic void lacp_sm_ptx_tx_schedule(struct lacp_port *); 148168561Sthompsastatic void lacp_sm_ptx_timer(struct lacp_port *); 149168561Sthompsa 150168561Sthompsa/* transmit machine */ 151168561Sthompsa 152168561Sthompsastatic void lacp_sm_tx(struct lacp_port *); 153168561Sthompsastatic void lacp_sm_assert_ntt(struct lacp_port *); 154168561Sthompsa 155168561Sthompsastatic void lacp_run_timers(struct lacp_port *); 156168561Sthompsastatic int lacp_compare_peerinfo(const struct lacp_peerinfo *, 157168561Sthompsa const struct lacp_peerinfo *); 158168561Sthompsastatic int lacp_compare_systemid(const struct lacp_systemid *, 159168561Sthompsa const struct lacp_systemid *); 160168561Sthompsastatic void lacp_port_enable(struct lacp_port *); 161168561Sthompsastatic void lacp_port_disable(struct lacp_port *); 162168561Sthompsastatic void lacp_select(struct lacp_port *); 163168561Sthompsastatic void lacp_unselect(struct lacp_port *); 164168561Sthompsastatic void lacp_disable_collecting(struct lacp_port *); 165168561Sthompsastatic void lacp_enable_collecting(struct lacp_port *); 166168561Sthompsastatic void lacp_disable_distributing(struct lacp_port *); 167168561Sthompsastatic void lacp_enable_distributing(struct lacp_port *); 168168561Sthompsastatic int lacp_xmit_lacpdu(struct lacp_port *); 169169739Sthompsastatic int lacp_xmit_marker(struct lacp_port *); 170168561Sthompsa 171168561Sthompsa#if defined(LACP_DEBUG) 172168561Sthompsastatic void lacp_dump_lacpdu(const struct lacpdu *); 173168561Sthompsastatic const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 174168561Sthompsa size_t); 175168561Sthompsastatic const char *lacp_format_lagid(const struct lacp_peerinfo *, 176168561Sthompsa const struct lacp_peerinfo *, char *, size_t); 177168561Sthompsastatic const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 178168561Sthompsa char *, size_t); 179168561Sthompsastatic const char *lacp_format_state(uint8_t, char *, size_t); 180168561Sthompsastatic const char *lacp_format_mac(const uint8_t *, char *, size_t); 181168561Sthompsastatic const char *lacp_format_systemid(const struct lacp_systemid *, char *, 182168561Sthompsa size_t); 183168561Sthompsastatic const char *lacp_format_portid(const struct lacp_portid *, char *, 184168561Sthompsa size_t); 185168561Sthompsastatic void lacp_dprintf(const struct lacp_port *, const char *, ...) 186168561Sthompsa __attribute__((__format__(__printf__, 2, 3))); 187168561Sthompsa#define LACP_DPRINTF(a) lacp_dprintf a 188168561Sthompsa#else 189168561Sthompsa#define LACP_DPRINTF(a) /* nothing */ 190168561Sthompsa#endif 191168561Sthompsa 192168561Sthompsa/* 193168561Sthompsa * partner administration variables. 194168561Sthompsa * XXX should be configurable. 195168561Sthompsa */ 196168561Sthompsa 197168561Sthompsastatic const struct lacp_peerinfo lacp_partner_admin = { 198168561Sthompsa .lip_systemid = { .lsi_prio = 0xffff }, 199168561Sthompsa .lip_portid = { .lpi_prio = 0xffff }, 200168561Sthompsa#if 1 201168561Sthompsa /* optimistic */ 202168561Sthompsa .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 203168561Sthompsa LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 204168561Sthompsa#else 205168561Sthompsa /* pessimistic */ 206168561Sthompsa .lip_state = 0, 207168561Sthompsa#endif 208168561Sthompsa}; 209168561Sthompsa 210168561Sthompsastatic const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 211168561Sthompsa [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 212168561Sthompsa [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 213168561Sthompsa [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 214168561Sthompsa}; 215168561Sthompsa 216175005Sthompsastruct mbuf * 217169569Sthompsalacp_input(struct lagg_port *lgp, struct mbuf *m) 218169569Sthompsa{ 219177274Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 220169569Sthompsa uint8_t subtype; 221169569Sthompsa 222169569Sthompsa if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 223169569Sthompsa m_freem(m); 224175005Sthompsa return (NULL); 225169569Sthompsa } 226169569Sthompsa 227169569Sthompsa m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 228169569Sthompsa switch (subtype) { 229169569Sthompsa case SLOWPROTOCOLS_SUBTYPE_LACP: 230177274Sthompsa lacp_pdu_input(lp, m); 231177274Sthompsa return (NULL); 232169569Sthompsa 233169569Sthompsa case SLOWPROTOCOLS_SUBTYPE_MARKER: 234177274Sthompsa lacp_marker_input(lp, m); 235177274Sthompsa return (NULL); 236169569Sthompsa } 237169569Sthompsa 238177274Sthompsa /* Not a subtype we are interested in */ 239177274Sthompsa return (m); 240169569Sthompsa} 241169569Sthompsa 242168561Sthompsa/* 243169569Sthompsa * lacp_pdu_input: process lacpdu 244168561Sthompsa */ 245169569Sthompsastatic int 246177274Sthompsalacp_pdu_input(struct lacp_port *lp, struct mbuf *m) 247168561Sthompsa{ 248177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 249168561Sthompsa struct lacpdu *du; 250168561Sthompsa int error = 0; 251168561Sthompsa 252168561Sthompsa if (m->m_pkthdr.len != sizeof(*du)) { 253168561Sthompsa goto bad; 254168561Sthompsa } 255168561Sthompsa 256168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 257168561Sthompsa goto bad; 258168561Sthompsa } 259168561Sthompsa 260168561Sthompsa if (m->m_len < sizeof(*du)) { 261168561Sthompsa m = m_pullup(m, sizeof(*du)); 262168561Sthompsa if (m == NULL) { 263168561Sthompsa return (ENOMEM); 264168561Sthompsa } 265168561Sthompsa } 266168561Sthompsa 267168561Sthompsa du = mtod(m, struct lacpdu *); 268168561Sthompsa 269168561Sthompsa if (memcmp(&du->ldu_eh.ether_dhost, 270168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 271168561Sthompsa goto bad; 272168561Sthompsa } 273168561Sthompsa 274168561Sthompsa /* 275168561Sthompsa * ignore the version for compatibility with 276168561Sthompsa * the future protocol revisions. 277168561Sthompsa */ 278168561Sthompsa#if 0 279168561Sthompsa if (du->ldu_sph.sph_version != 1) { 280168561Sthompsa goto bad; 281168561Sthompsa } 282168561Sthompsa#endif 283168561Sthompsa 284168561Sthompsa /* 285168561Sthompsa * ignore tlv types for compatibility with 286168561Sthompsa * the future protocol revisions. 287168561Sthompsa */ 288168561Sthompsa if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 289168561Sthompsa lacp_info_tlv_template, FALSE)) { 290168561Sthompsa goto bad; 291168561Sthompsa } 292168561Sthompsa 293168561Sthompsa#if defined(LACP_DEBUG) 294168561Sthompsa LACP_DPRINTF((lp, "lacpdu receive\n")); 295168561Sthompsa lacp_dump_lacpdu(du); 296168561Sthompsa#endif /* defined(LACP_DEBUG) */ 297177274Sthompsa 298177274Sthompsa LACP_LOCK(lsc); 299168561Sthompsa lacp_sm_rx(lp, du); 300177274Sthompsa LACP_UNLOCK(lsc); 301168561Sthompsa 302168561Sthompsa m_freem(m); 303168561Sthompsa return (error); 304168561Sthompsa 305168561Sthompsabad: 306168561Sthompsa m_freem(m); 307168561Sthompsa return (EINVAL); 308168561Sthompsa} 309168561Sthompsa 310168561Sthompsastatic void 311168561Sthompsalacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 312168561Sthompsa{ 313168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 314170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 315168561Sthompsa 316168561Sthompsa info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 317168561Sthompsa memcpy(&info->lip_systemid.lsi_mac, 318170599Sthompsa IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); 319168561Sthompsa info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 320168561Sthompsa info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 321168561Sthompsa info->lip_state = lp->lp_state; 322168561Sthompsa} 323168561Sthompsa 324169739Sthompsastatic void 325169739Sthompsalacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 326169739Sthompsa{ 327169739Sthompsa struct ifnet *ifp = lp->lp_ifp; 328169739Sthompsa 329169739Sthompsa /* Fill in the port index and system id (encoded as the MAC) */ 330169739Sthompsa info->mi_rq_port = htons(ifp->if_index); 331169739Sthompsa memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 332169739Sthompsa info->mi_rq_xid = htonl(0); 333169739Sthompsa} 334169739Sthompsa 335168561Sthompsastatic int 336168561Sthompsalacp_xmit_lacpdu(struct lacp_port *lp) 337168561Sthompsa{ 338168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 339168561Sthompsa struct mbuf *m; 340168561Sthompsa struct lacpdu *du; 341168561Sthompsa int error; 342168561Sthompsa 343177274Sthompsa LACP_LOCK_ASSERT(lp->lp_lsc); 344168561Sthompsa 345168561Sthompsa m = m_gethdr(M_DONTWAIT, MT_DATA); 346168561Sthompsa if (m == NULL) { 347168561Sthompsa return (ENOMEM); 348168561Sthompsa } 349168561Sthompsa m->m_len = m->m_pkthdr.len = sizeof(*du); 350168561Sthompsa 351168561Sthompsa du = mtod(m, struct lacpdu *); 352168561Sthompsa memset(du, 0, sizeof(*du)); 353168561Sthompsa 354168561Sthompsa memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 355168561Sthompsa ETHER_ADDR_LEN); 356168793Sthompsa memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 357168561Sthompsa du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 358168561Sthompsa 359168561Sthompsa du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 360168561Sthompsa du->ldu_sph.sph_version = 1; 361168561Sthompsa 362168561Sthompsa TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 363168561Sthompsa du->ldu_actor = lp->lp_actor; 364168561Sthompsa 365168561Sthompsa TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 366168561Sthompsa sizeof(du->ldu_partner)); 367168561Sthompsa du->ldu_partner = lp->lp_partner; 368168561Sthompsa 369168561Sthompsa TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 370168561Sthompsa sizeof(du->ldu_collector)); 371168561Sthompsa du->ldu_collector.lci_maxdelay = 0; 372168561Sthompsa 373168561Sthompsa#if defined(LACP_DEBUG) 374168561Sthompsa LACP_DPRINTF((lp, "lacpdu transmit\n")); 375168561Sthompsa lacp_dump_lacpdu(du); 376168561Sthompsa#endif /* defined(LACP_DEBUG) */ 377168561Sthompsa 378168561Sthompsa m->m_flags |= M_MCAST; 379168561Sthompsa 380168561Sthompsa /* 381168561Sthompsa * XXX should use higher priority queue. 382168561Sthompsa * otherwise network congestion can break aggregation. 383168561Sthompsa */ 384168561Sthompsa 385168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 386168561Sthompsa return (error); 387168561Sthompsa} 388168561Sthompsa 389169739Sthompsastatic int 390169739Sthompsalacp_xmit_marker(struct lacp_port *lp) 391169739Sthompsa{ 392169739Sthompsa struct lagg_port *lgp = lp->lp_lagg; 393169739Sthompsa struct mbuf *m; 394169739Sthompsa struct markerdu *mdu; 395169739Sthompsa int error; 396169739Sthompsa 397177274Sthompsa LACP_LOCK_ASSERT(lp->lp_lsc); 398169739Sthompsa 399169739Sthompsa m = m_gethdr(M_DONTWAIT, MT_DATA); 400169739Sthompsa if (m == NULL) { 401169739Sthompsa return (ENOMEM); 402169739Sthompsa } 403169739Sthompsa m->m_len = m->m_pkthdr.len = sizeof(*mdu); 404169739Sthompsa 405169739Sthompsa mdu = mtod(m, struct markerdu *); 406169739Sthompsa memset(mdu, 0, sizeof(*mdu)); 407169739Sthompsa 408169739Sthompsa memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 409169739Sthompsa ETHER_ADDR_LEN); 410169739Sthompsa memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 411169739Sthompsa mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 412169739Sthompsa 413169739Sthompsa mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 414169739Sthompsa mdu->mdu_sph.sph_version = 1; 415169739Sthompsa 416169739Sthompsa /* Bump the transaction id and copy over the marker info */ 417169739Sthompsa lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 418169739Sthompsa TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 419169739Sthompsa mdu->mdu_info = lp->lp_marker; 420169739Sthompsa 421169739Sthompsa LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 422169739Sthompsa ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 423169739Sthompsa ntohl(mdu->mdu_info.mi_rq_xid))); 424169739Sthompsa 425169739Sthompsa m->m_flags |= M_MCAST; 426169739Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 427169739Sthompsa return (error); 428169739Sthompsa} 429177274Sthompsa 430168561Sthompsavoid 431168793Sthompsalacp_linkstate(struct lagg_port *lgp) 432168561Sthompsa{ 433168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 434177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 435168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 436168561Sthompsa struct ifmediareq ifmr; 437168561Sthompsa int error = 0; 438168561Sthompsa u_int media; 439168561Sthompsa uint8_t old_state; 440168561Sthompsa uint16_t old_key; 441168561Sthompsa 442168561Sthompsa bzero((char *)&ifmr, sizeof(ifmr)); 443168561Sthompsa error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 444168561Sthompsa if (error != 0) 445168561Sthompsa return; 446168561Sthompsa 447177274Sthompsa LACP_LOCK(lsc); 448168561Sthompsa media = ifmr.ifm_active; 449169227Sthompsa LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 450169227Sthompsa "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 451169227Sthompsa (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 452168561Sthompsa old_state = lp->lp_state; 453168561Sthompsa old_key = lp->lp_key; 454168561Sthompsa 455168561Sthompsa lp->lp_media = media; 456170599Sthompsa /* 457169227Sthompsa * If the port is not an active full duplex Ethernet link then it can 458169227Sthompsa * not be aggregated. 459169227Sthompsa */ 460169227Sthompsa if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 461169227Sthompsa ifp->if_link_state != LINK_STATE_UP) { 462168561Sthompsa lacp_port_disable(lp); 463168561Sthompsa } else { 464168561Sthompsa lacp_port_enable(lp); 465168561Sthompsa } 466168561Sthompsa lp->lp_key = lacp_compose_key(lp); 467168561Sthompsa 468168561Sthompsa if (old_state != lp->lp_state || old_key != lp->lp_key) { 469168561Sthompsa LACP_DPRINTF((lp, "-> UNSELECTED\n")); 470168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 471168561Sthompsa } 472177274Sthompsa LACP_UNLOCK(lsc); 473168561Sthompsa} 474168561Sthompsa 475168561Sthompsastatic void 476168561Sthompsalacp_tick(void *arg) 477168561Sthompsa{ 478168561Sthompsa struct lacp_softc *lsc = arg; 479168561Sthompsa struct lacp_port *lp; 480168561Sthompsa 481168561Sthompsa LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 482168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 483168561Sthompsa continue; 484168561Sthompsa 485168561Sthompsa lacp_run_timers(lp); 486168561Sthompsa 487168561Sthompsa lacp_select(lp); 488168561Sthompsa lacp_sm_mux(lp); 489168561Sthompsa lacp_sm_tx(lp); 490168561Sthompsa lacp_sm_ptx_tx_schedule(lp); 491168561Sthompsa } 492168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 493168561Sthompsa} 494168561Sthompsa 495168561Sthompsaint 496168793Sthompsalacp_port_create(struct lagg_port *lgp) 497168561Sthompsa{ 498170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 499170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 500168561Sthompsa struct lacp_port *lp; 501168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 502168561Sthompsa struct sockaddr_dl sdl; 503168561Sthompsa struct ifmultiaddr *rifma = NULL; 504168561Sthompsa int error; 505168561Sthompsa 506168561Sthompsa boolean_t active = TRUE; /* XXX should be configurable */ 507168561Sthompsa boolean_t fast = FALSE; /* XXX should be configurable */ 508168561Sthompsa 509168561Sthompsa bzero((char *)&sdl, sizeof(sdl)); 510168561Sthompsa sdl.sdl_len = sizeof(sdl); 511168561Sthompsa sdl.sdl_family = AF_LINK; 512168561Sthompsa sdl.sdl_index = ifp->if_index; 513168561Sthompsa sdl.sdl_type = IFT_ETHER; 514168561Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 515168561Sthompsa 516168561Sthompsa bcopy(ðermulticastaddr_slowprotocols, 517168561Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 518168561Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 519168561Sthompsa if (error) { 520168793Sthompsa printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname); 521168561Sthompsa return (error); 522168561Sthompsa } 523168561Sthompsa 524168561Sthompsa lp = malloc(sizeof(struct lacp_port), 525168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 526168561Sthompsa if (lp == NULL) 527168561Sthompsa return (ENOMEM); 528168561Sthompsa 529177274Sthompsa LACP_LOCK(lsc); 530168793Sthompsa lgp->lp_psc = (caddr_t)lp; 531168561Sthompsa lp->lp_ifp = ifp; 532168793Sthompsa lp->lp_lagg = lgp; 533168561Sthompsa lp->lp_lsc = lsc; 534169327Sthompsa lp->lp_ifma = rifma; 535168561Sthompsa 536168561Sthompsa LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 537168561Sthompsa 538168561Sthompsa lacp_fill_actorinfo(lp, &lp->lp_actor); 539169739Sthompsa lacp_fill_markerinfo(lp, &lp->lp_marker); 540168561Sthompsa lp->lp_state = 541168561Sthompsa (active ? LACP_STATE_ACTIVITY : 0) | 542168561Sthompsa (fast ? LACP_STATE_TIMEOUT : 0); 543168561Sthompsa lp->lp_aggregator = NULL; 544177274Sthompsa lacp_sm_rx_set_expired(lp); 545177274Sthompsa LACP_UNLOCK(lsc); 546168793Sthompsa lacp_linkstate(lgp); 547168561Sthompsa 548168561Sthompsa return (0); 549168561Sthompsa} 550168561Sthompsa 551168561Sthompsavoid 552168793Sthompsalacp_port_destroy(struct lagg_port *lgp) 553168561Sthompsa{ 554168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 555177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 556169327Sthompsa int i; 557168561Sthompsa 558177274Sthompsa LACP_LOCK(lsc); 559168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 560168561Sthompsa LACP_TIMER_DISARM(lp, i); 561168561Sthompsa } 562168561Sthompsa 563168561Sthompsa lacp_disable_collecting(lp); 564168561Sthompsa lacp_disable_distributing(lp); 565168561Sthompsa lacp_unselect(lp); 566168561Sthompsa 567169328Sthompsa /* The address may have already been removed by if_purgemaddrs() */ 568169328Sthompsa if (!lgp->lp_detaching) 569169328Sthompsa if_delmulti_ifma(lp->lp_ifma); 570168561Sthompsa 571168561Sthompsa LIST_REMOVE(lp, lp_next); 572177274Sthompsa LACP_UNLOCK(lsc); 573168561Sthompsa free(lp, M_DEVBUF); 574168561Sthompsa} 575168561Sthompsa 576171247Sthompsavoid 577171247Sthompsalacp_req(struct lagg_softc *sc, caddr_t data) 578171247Sthompsa{ 579171247Sthompsa struct lacp_opreq *req = (struct lacp_opreq *)data; 580171247Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 581171247Sthompsa struct lacp_aggregator *la = lsc->lsc_active_aggregator; 582171247Sthompsa 583177274Sthompsa LACP_LOCK(lsc); 584171247Sthompsa bzero(req, sizeof(struct lacp_opreq)); 585171247Sthompsa if (la != NULL) { 586171247Sthompsa req->actor_prio = ntohs(la->la_actor.lip_systemid.lsi_prio); 587171247Sthompsa memcpy(&req->actor_mac, &la->la_actor.lip_systemid.lsi_mac, 588171247Sthompsa ETHER_ADDR_LEN); 589171247Sthompsa req->actor_key = ntohs(la->la_actor.lip_key); 590171247Sthompsa req->actor_portprio = ntohs(la->la_actor.lip_portid.lpi_prio); 591171247Sthompsa req->actor_portno = ntohs(la->la_actor.lip_portid.lpi_portno); 592171247Sthompsa req->actor_state = la->la_actor.lip_state; 593171247Sthompsa 594171247Sthompsa req->partner_prio = ntohs(la->la_partner.lip_systemid.lsi_prio); 595171247Sthompsa memcpy(&req->partner_mac, &la->la_partner.lip_systemid.lsi_mac, 596171247Sthompsa ETHER_ADDR_LEN); 597171247Sthompsa req->partner_key = ntohs(la->la_partner.lip_key); 598171247Sthompsa req->partner_portprio = ntohs(la->la_partner.lip_portid.lpi_prio); 599171247Sthompsa req->partner_portno = ntohs(la->la_partner.lip_portid.lpi_portno); 600171247Sthompsa req->partner_state = la->la_partner.lip_state; 601171247Sthompsa } 602177274Sthompsa LACP_UNLOCK(lsc); 603171247Sthompsa} 604171247Sthompsa 605171247Sthompsavoid 606171247Sthompsalacp_portreq(struct lagg_port *lgp, caddr_t data) 607171247Sthompsa{ 608171247Sthompsa struct lacp_opreq *req = (struct lacp_opreq *)data; 609171247Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 610177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 611171247Sthompsa 612177274Sthompsa LACP_LOCK(lsc); 613171247Sthompsa req->actor_prio = ntohs(lp->lp_actor.lip_systemid.lsi_prio); 614171247Sthompsa memcpy(&req->actor_mac, &lp->lp_actor.lip_systemid.lsi_mac, 615171247Sthompsa ETHER_ADDR_LEN); 616171247Sthompsa req->actor_key = ntohs(lp->lp_actor.lip_key); 617171247Sthompsa req->actor_portprio = ntohs(lp->lp_actor.lip_portid.lpi_prio); 618171247Sthompsa req->actor_portno = ntohs(lp->lp_actor.lip_portid.lpi_portno); 619171247Sthompsa req->actor_state = lp->lp_actor.lip_state; 620171247Sthompsa 621171247Sthompsa req->partner_prio = ntohs(lp->lp_partner.lip_systemid.lsi_prio); 622171247Sthompsa memcpy(&req->partner_mac, &lp->lp_partner.lip_systemid.lsi_mac, 623171247Sthompsa ETHER_ADDR_LEN); 624171247Sthompsa req->partner_key = ntohs(lp->lp_partner.lip_key); 625171247Sthompsa req->partner_portprio = ntohs(lp->lp_partner.lip_portid.lpi_prio); 626171247Sthompsa req->partner_portno = ntohs(lp->lp_partner.lip_portid.lpi_portno); 627171247Sthompsa req->partner_state = lp->lp_partner.lip_state; 628177274Sthompsa LACP_UNLOCK(lsc); 629171247Sthompsa} 630171247Sthompsa 631168561Sthompsastatic void 632168561Sthompsalacp_disable_collecting(struct lacp_port *lp) 633168561Sthompsa{ 634168561Sthompsa LACP_DPRINTF((lp, "collecting disabled\n")); 635168561Sthompsa lp->lp_state &= ~LACP_STATE_COLLECTING; 636168561Sthompsa} 637168561Sthompsa 638168561Sthompsastatic void 639168561Sthompsalacp_enable_collecting(struct lacp_port *lp) 640168561Sthompsa{ 641168561Sthompsa LACP_DPRINTF((lp, "collecting enabled\n")); 642168561Sthompsa lp->lp_state |= LACP_STATE_COLLECTING; 643168561Sthompsa} 644168561Sthompsa 645168561Sthompsastatic void 646168561Sthompsalacp_disable_distributing(struct lacp_port *lp) 647168561Sthompsa{ 648168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 649168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 650168561Sthompsa#if defined(LACP_DEBUG) 651168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 652168561Sthompsa#endif /* defined(LACP_DEBUG) */ 653168561Sthompsa 654177274Sthompsa LACP_LOCK_ASSERT(lsc); 655168561Sthompsa 656168561Sthompsa if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 657168561Sthompsa return; 658168561Sthompsa } 659168561Sthompsa 660168561Sthompsa KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 661168561Sthompsa KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 662168561Sthompsa KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 663168561Sthompsa 664168561Sthompsa LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 665168561Sthompsa "nports %d -> %d\n", 666168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 667168561Sthompsa la->la_nports, la->la_nports - 1)); 668168561Sthompsa 669168561Sthompsa TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 670168561Sthompsa la->la_nports--; 671168561Sthompsa 672168561Sthompsa if (lsc->lsc_active_aggregator == la) { 673177274Sthompsa lacp_suppress_distributing(lsc, la); 674168561Sthompsa lacp_select_active_aggregator(lsc); 675177274Sthompsa /* regenerate the port map, the active aggregator has changed */ 676177274Sthompsa lacp_update_portmap(lsc); 677168561Sthompsa } 678177274Sthompsa 679177274Sthompsa lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 680168561Sthompsa} 681168561Sthompsa 682168561Sthompsastatic void 683168561Sthompsalacp_enable_distributing(struct lacp_port *lp) 684168561Sthompsa{ 685168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 686168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 687168561Sthompsa#if defined(LACP_DEBUG) 688168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 689168561Sthompsa#endif /* defined(LACP_DEBUG) */ 690168561Sthompsa 691177274Sthompsa LACP_LOCK_ASSERT(lsc); 692168561Sthompsa 693168561Sthompsa if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 694168561Sthompsa return; 695168561Sthompsa } 696168561Sthompsa 697168561Sthompsa LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 698168561Sthompsa "nports %d -> %d\n", 699168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 700168561Sthompsa la->la_nports, la->la_nports + 1)); 701168561Sthompsa 702168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 703168561Sthompsa TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 704168561Sthompsa la->la_nports++; 705168561Sthompsa 706168561Sthompsa lp->lp_state |= LACP_STATE_DISTRIBUTING; 707168561Sthompsa 708177274Sthompsa if (lsc->lsc_active_aggregator == la) { 709177274Sthompsa lacp_suppress_distributing(lsc, la); 710177274Sthompsa lacp_update_portmap(lsc); 711177274Sthompsa } else 712177274Sthompsa /* try to become the active aggregator */ 713168561Sthompsa lacp_select_active_aggregator(lsc); 714168561Sthompsa} 715168561Sthompsa 716168561Sthompsastatic void 717168561Sthompsalacp_transit_expire(void *vp) 718168561Sthompsa{ 719168561Sthompsa struct lacp_softc *lsc = vp; 720168561Sthompsa 721177274Sthompsa LACP_LOCK_ASSERT(lsc); 722177274Sthompsa 723168561Sthompsa LACP_DPRINTF((NULL, "%s\n", __func__)); 724168561Sthompsa lsc->lsc_suppress_distributing = FALSE; 725168561Sthompsa} 726168561Sthompsa 727168561Sthompsaint 728170599Sthompsalacp_attach(struct lagg_softc *sc) 729168561Sthompsa{ 730168561Sthompsa struct lacp_softc *lsc; 731168561Sthompsa 732168561Sthompsa lsc = malloc(sizeof(struct lacp_softc), 733168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 734168561Sthompsa if (lsc == NULL) 735168561Sthompsa return (ENOMEM); 736168561Sthompsa 737170599Sthompsa sc->sc_psc = (caddr_t)lsc; 738170599Sthompsa lsc->lsc_softc = sc; 739168561Sthompsa 740168561Sthompsa lsc->lsc_hashkey = arc4random(); 741168561Sthompsa lsc->lsc_active_aggregator = NULL; 742177274Sthompsa LACP_LOCK_INIT(lsc); 743168561Sthompsa TAILQ_INIT(&lsc->lsc_aggregators); 744168561Sthompsa LIST_INIT(&lsc->lsc_ports); 745168561Sthompsa 746177274Sthompsa callout_init_mtx(&lsc->lsc_transit_callout, &lsc->lsc_mtx, 0); 747177274Sthompsa callout_init_mtx(&lsc->lsc_callout, &lsc->lsc_mtx, 0); 748168561Sthompsa 749168793Sthompsa /* if the lagg is already up then do the same */ 750170599Sthompsa if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 751170599Sthompsa lacp_init(sc); 752168561Sthompsa 753168561Sthompsa return (0); 754168561Sthompsa} 755168561Sthompsa 756168561Sthompsaint 757170599Sthompsalacp_detach(struct lagg_softc *sc) 758168561Sthompsa{ 759170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 760168561Sthompsa 761168561Sthompsa KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 762168561Sthompsa ("aggregators still active")); 763168561Sthompsa KASSERT(lsc->lsc_active_aggregator == NULL, 764168561Sthompsa ("aggregator still attached")); 765168561Sthompsa 766170599Sthompsa sc->sc_psc = NULL; 767168561Sthompsa callout_drain(&lsc->lsc_transit_callout); 768168561Sthompsa callout_drain(&lsc->lsc_callout); 769168561Sthompsa 770177274Sthompsa LACP_LOCK_DESTROY(lsc); 771168561Sthompsa free(lsc, M_DEVBUF); 772168561Sthompsa return (0); 773168561Sthompsa} 774168561Sthompsa 775168561Sthompsavoid 776170599Sthompsalacp_init(struct lagg_softc *sc) 777168561Sthompsa{ 778170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 779168561Sthompsa 780177274Sthompsa LACP_LOCK(lsc); 781168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 782177274Sthompsa LACP_UNLOCK(lsc); 783168561Sthompsa} 784168561Sthompsa 785168561Sthompsavoid 786170599Sthompsalacp_stop(struct lagg_softc *sc) 787168561Sthompsa{ 788170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 789168561Sthompsa 790177274Sthompsa LACP_LOCK(lsc); 791168561Sthompsa callout_stop(&lsc->lsc_transit_callout); 792168561Sthompsa callout_stop(&lsc->lsc_callout); 793177274Sthompsa LACP_UNLOCK(lsc); 794168561Sthompsa} 795168561Sthompsa 796168793Sthompsastruct lagg_port * 797170599Sthompsalacp_select_tx_port(struct lagg_softc *sc, struct mbuf *m) 798168561Sthompsa{ 799170599Sthompsa struct lacp_softc *lsc = LACP_SOFTC(sc); 800177274Sthompsa struct lacp_portmap *pm; 801168561Sthompsa struct lacp_port *lp; 802168561Sthompsa uint32_t hash; 803168561Sthompsa 804168561Sthompsa if (__predict_false(lsc->lsc_suppress_distributing)) { 805168561Sthompsa LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 806168561Sthompsa return (NULL); 807168561Sthompsa } 808168561Sthompsa 809177274Sthompsa pm = &lsc->lsc_pmap[lsc->lsc_activemap]; 810177274Sthompsa if (pm->pm_count == 0) { 811168561Sthompsa LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 812168561Sthompsa return (NULL); 813168561Sthompsa } 814168561Sthompsa 815232008Sthompsa if (sc->use_flowid && (m->m_flags & M_FLOWID)) 816191692Sthompsa hash = m->m_pkthdr.flowid; 817191692Sthompsa else 818191692Sthompsa hash = lagg_hashmbuf(m, lsc->lsc_hashkey); 819177274Sthompsa hash %= pm->pm_count; 820177274Sthompsa lp = pm->pm_map[hash]; 821168561Sthompsa 822168561Sthompsa KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 823168561Sthompsa ("aggregated port is not distributing")); 824168561Sthompsa 825168793Sthompsa return (lp->lp_lagg); 826168561Sthompsa} 827168561Sthompsa/* 828168561Sthompsa * lacp_suppress_distributing: drop transmit packets for a while 829168561Sthompsa * to preserve packet ordering. 830168561Sthompsa */ 831168561Sthompsa 832168561Sthompsastatic void 833168561Sthompsalacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 834168561Sthompsa{ 835169739Sthompsa struct lacp_port *lp; 836169739Sthompsa 837168561Sthompsa if (lsc->lsc_active_aggregator != la) { 838168561Sthompsa return; 839168561Sthompsa } 840168561Sthompsa 841168561Sthompsa LACP_DPRINTF((NULL, "%s\n", __func__)); 842168561Sthompsa lsc->lsc_suppress_distributing = TRUE; 843169739Sthompsa 844169739Sthompsa /* send a marker frame down each port to verify the queues are empty */ 845169739Sthompsa LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 846169739Sthompsa lp->lp_flags |= LACP_PORT_MARK; 847169739Sthompsa lacp_xmit_marker(lp); 848169739Sthompsa } 849169739Sthompsa 850169739Sthompsa /* set a timeout for the marker frames */ 851168561Sthompsa callout_reset(&lsc->lsc_transit_callout, 852168561Sthompsa LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 853168561Sthompsa} 854168561Sthompsa 855168561Sthompsastatic int 856168561Sthompsalacp_compare_peerinfo(const struct lacp_peerinfo *a, 857168561Sthompsa const struct lacp_peerinfo *b) 858168561Sthompsa{ 859168561Sthompsa return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 860168561Sthompsa} 861168561Sthompsa 862168561Sthompsastatic int 863168561Sthompsalacp_compare_systemid(const struct lacp_systemid *a, 864168561Sthompsa const struct lacp_systemid *b) 865168561Sthompsa{ 866168561Sthompsa return (memcmp(a, b, sizeof(*a))); 867168561Sthompsa} 868168561Sthompsa 869168561Sthompsa#if 0 /* unused */ 870168561Sthompsastatic int 871168561Sthompsalacp_compare_portid(const struct lacp_portid *a, 872168561Sthompsa const struct lacp_portid *b) 873168561Sthompsa{ 874168561Sthompsa return (memcmp(a, b, sizeof(*a))); 875168561Sthompsa} 876168561Sthompsa#endif 877168561Sthompsa 878168561Sthompsastatic uint64_t 879168561Sthompsalacp_aggregator_bandwidth(struct lacp_aggregator *la) 880168561Sthompsa{ 881168561Sthompsa struct lacp_port *lp; 882168561Sthompsa uint64_t speed; 883168561Sthompsa 884168561Sthompsa lp = TAILQ_FIRST(&la->la_ports); 885168561Sthompsa if (lp == NULL) { 886168561Sthompsa return (0); 887168561Sthompsa } 888168561Sthompsa 889168561Sthompsa speed = ifmedia_baudrate(lp->lp_media); 890168561Sthompsa speed *= la->la_nports; 891168561Sthompsa if (speed == 0) { 892168561Sthompsa LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 893168561Sthompsa lp->lp_media, la->la_nports)); 894168561Sthompsa } 895168561Sthompsa 896168561Sthompsa return (speed); 897168561Sthompsa} 898168561Sthompsa 899168561Sthompsa/* 900168561Sthompsa * lacp_select_active_aggregator: select an aggregator to be used to transmit 901168793Sthompsa * packets from lagg(4) interface. 902168561Sthompsa */ 903168561Sthompsa 904168561Sthompsastatic void 905168561Sthompsalacp_select_active_aggregator(struct lacp_softc *lsc) 906168561Sthompsa{ 907186254Sthompsa struct lagg_softc *sc = lsc->lsc_softc; 908168561Sthompsa struct lacp_aggregator *la; 909168561Sthompsa struct lacp_aggregator *best_la = NULL; 910168561Sthompsa uint64_t best_speed = 0; 911168561Sthompsa#if defined(LACP_DEBUG) 912168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 913168561Sthompsa#endif /* defined(LACP_DEBUG) */ 914168561Sthompsa 915168561Sthompsa LACP_DPRINTF((NULL, "%s:\n", __func__)); 916168561Sthompsa 917168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 918168561Sthompsa uint64_t speed; 919168561Sthompsa 920168561Sthompsa if (la->la_nports == 0) { 921168561Sthompsa continue; 922168561Sthompsa } 923168561Sthompsa 924168561Sthompsa speed = lacp_aggregator_bandwidth(la); 925168561Sthompsa LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 926168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 927168561Sthompsa speed, la->la_nports)); 928169741Sthompsa 929169741Sthompsa /* This aggregator is chosen if 930169741Sthompsa * the partner has a better system priority 931169741Sthompsa * or, the total aggregated speed is higher 932169741Sthompsa * or, it is already the chosen aggregator 933169741Sthompsa */ 934169741Sthompsa if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 935169741Sthompsa LACP_SYS_PRI(best_la->la_partner)) || 936169741Sthompsa speed > best_speed || 937168561Sthompsa (speed == best_speed && 938168561Sthompsa la == lsc->lsc_active_aggregator)) { 939168561Sthompsa best_la = la; 940168561Sthompsa best_speed = speed; 941168561Sthompsa } 942168561Sthompsa } 943168561Sthompsa 944168561Sthompsa KASSERT(best_la == NULL || best_la->la_nports > 0, 945168561Sthompsa ("invalid aggregator refcnt")); 946168561Sthompsa KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 947168561Sthompsa ("invalid aggregator list")); 948168561Sthompsa 949168561Sthompsa#if defined(LACP_DEBUG) 950168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 951168561Sthompsa LACP_DPRINTF((NULL, "active aggregator changed\n")); 952168561Sthompsa LACP_DPRINTF((NULL, "old %s\n", 953168561Sthompsa lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 954168561Sthompsa buf, sizeof(buf)))); 955168561Sthompsa } else { 956168561Sthompsa LACP_DPRINTF((NULL, "active aggregator not changed\n")); 957168561Sthompsa } 958168561Sthompsa LACP_DPRINTF((NULL, "new %s\n", 959168561Sthompsa lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 960168561Sthompsa#endif /* defined(LACP_DEBUG) */ 961168561Sthompsa 962168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 963186254Sthompsa sc->sc_ifp->if_baudrate = best_speed; 964168561Sthompsa lsc->lsc_active_aggregator = best_la; 965177274Sthompsa lacp_update_portmap(lsc); 966168561Sthompsa if (best_la) { 967168561Sthompsa lacp_suppress_distributing(lsc, best_la); 968168561Sthompsa } 969168561Sthompsa } 970168561Sthompsa} 971168561Sthompsa 972177274Sthompsa/* 973177274Sthompsa * Updated the inactive portmap array with the new list of ports and 974177274Sthompsa * make it live. 975177274Sthompsa */ 976177274Sthompsastatic void 977177274Sthompsalacp_update_portmap(struct lacp_softc *lsc) 978177274Sthompsa{ 979177274Sthompsa struct lacp_aggregator *la; 980177274Sthompsa struct lacp_portmap *p; 981177274Sthompsa struct lacp_port *lp; 982177274Sthompsa u_int newmap; 983177274Sthompsa int i; 984177274Sthompsa 985177274Sthompsa newmap = lsc->lsc_activemap == 0 ? 1 : 0; 986177274Sthompsa p = &lsc->lsc_pmap[newmap]; 987177274Sthompsa la = lsc->lsc_active_aggregator; 988177274Sthompsa bzero(p, sizeof(struct lacp_portmap)); 989177274Sthompsa 990177274Sthompsa if (la != NULL && la->la_nports > 0) { 991177274Sthompsa p->pm_count = la->la_nports; 992177274Sthompsa i = 0; 993177274Sthompsa TAILQ_FOREACH(lp, &la->la_ports, lp_dist_q) 994177274Sthompsa p->pm_map[i++] = lp; 995177274Sthompsa KASSERT(i == p->pm_count, ("Invalid port count")); 996177274Sthompsa } 997177274Sthompsa 998177274Sthompsa /* switch the active portmap over */ 999177274Sthompsa atomic_store_rel_int(&lsc->lsc_activemap, newmap); 1000177274Sthompsa LACP_DPRINTF((NULL, "Set table %d with %d ports\n", 1001177274Sthompsa lsc->lsc_activemap, 1002177274Sthompsa lsc->lsc_pmap[lsc->lsc_activemap].pm_count)); 1003177274Sthompsa} 1004177274Sthompsa 1005168561Sthompsastatic uint16_t 1006168561Sthompsalacp_compose_key(struct lacp_port *lp) 1007168561Sthompsa{ 1008168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 1009170599Sthompsa struct lagg_softc *sc = lgp->lp_softc; 1010168561Sthompsa u_int media = lp->lp_media; 1011168561Sthompsa uint16_t key; 1012168561Sthompsa 1013168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 1014168561Sthompsa 1015168561Sthompsa /* 1016168561Sthompsa * non-aggregatable links should have unique keys. 1017168561Sthompsa * 1018168561Sthompsa * XXX this isn't really unique as if_index is 16 bit. 1019168561Sthompsa */ 1020168561Sthompsa 1021168561Sthompsa /* bit 0..14: (some bits of) if_index of this port */ 1022168561Sthompsa key = lp->lp_ifp->if_index; 1023168561Sthompsa /* bit 15: 1 */ 1024168561Sthompsa key |= 0x8000; 1025168561Sthompsa } else { 1026168561Sthompsa u_int subtype = IFM_SUBTYPE(media); 1027168561Sthompsa 1028169227Sthompsa KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1029169227Sthompsa KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1030168561Sthompsa 1031168561Sthompsa /* bit 0..4: IFM_SUBTYPE */ 1032168561Sthompsa key = subtype; 1033168793Sthompsa /* bit 5..14: (some bits of) if_index of lagg device */ 1034170599Sthompsa key |= 0x7fe0 & ((sc->sc_ifp->if_index) << 5); 1035168561Sthompsa /* bit 15: 0 */ 1036168561Sthompsa } 1037168561Sthompsa return (htons(key)); 1038168561Sthompsa} 1039168561Sthompsa 1040168561Sthompsastatic void 1041168561Sthompsalacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1042168561Sthompsa{ 1043168561Sthompsa#if defined(LACP_DEBUG) 1044168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1045168561Sthompsa#endif 1046168561Sthompsa 1047168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1048168561Sthompsa __func__, 1049168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1050168561Sthompsa buf, sizeof(buf)), 1051168561Sthompsa la->la_refcnt, la->la_refcnt + 1)); 1052168561Sthompsa 1053168561Sthompsa KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1054168561Sthompsa la->la_refcnt++; 1055168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1056168561Sthompsa} 1057168561Sthompsa 1058168561Sthompsastatic void 1059168561Sthompsalacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1060168561Sthompsa{ 1061168561Sthompsa#if defined(LACP_DEBUG) 1062168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1063168561Sthompsa#endif 1064168561Sthompsa 1065168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1066168561Sthompsa __func__, 1067168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1068168561Sthompsa buf, sizeof(buf)), 1069168561Sthompsa la->la_refcnt, la->la_refcnt - 1)); 1070168561Sthompsa 1071168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1072168561Sthompsa la->la_refcnt--; 1073168561Sthompsa if (la->la_refcnt > 0) { 1074168561Sthompsa return; 1075168561Sthompsa } 1076168561Sthompsa 1077168561Sthompsa KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1078168561Sthompsa KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1079168561Sthompsa 1080168561Sthompsa TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1081168561Sthompsa 1082168561Sthompsa free(la, M_DEVBUF); 1083168561Sthompsa} 1084168561Sthompsa 1085168561Sthompsa/* 1086168561Sthompsa * lacp_aggregator_get: allocate an aggregator. 1087168561Sthompsa */ 1088168561Sthompsa 1089168561Sthompsastatic struct lacp_aggregator * 1090168561Sthompsalacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1091168561Sthompsa{ 1092168561Sthompsa struct lacp_aggregator *la; 1093168561Sthompsa 1094168561Sthompsa la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1095168561Sthompsa if (la) { 1096168561Sthompsa la->la_refcnt = 1; 1097168561Sthompsa la->la_nports = 0; 1098168561Sthompsa TAILQ_INIT(&la->la_ports); 1099168561Sthompsa la->la_pending = 0; 1100168561Sthompsa TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1101168561Sthompsa } 1102168561Sthompsa 1103168561Sthompsa return (la); 1104168561Sthompsa} 1105168561Sthompsa 1106168561Sthompsa/* 1107168561Sthompsa * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1108168561Sthompsa */ 1109168561Sthompsa 1110168561Sthompsastatic void 1111168561Sthompsalacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1112168561Sthompsa{ 1113168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1114168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1115168561Sthompsa 1116168561Sthompsa la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1117168561Sthompsa} 1118168561Sthompsa 1119168561Sthompsastatic void 1120168561Sthompsalacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1121168561Sthompsa const struct lacp_peerinfo *lpi_port) 1122168561Sthompsa{ 1123168561Sthompsa memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1124168561Sthompsa lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1125168561Sthompsa lpi_aggr->lip_key = lpi_port->lip_key; 1126168561Sthompsa} 1127168561Sthompsa 1128168561Sthompsa/* 1129168561Sthompsa * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1130168561Sthompsa */ 1131168561Sthompsa 1132168561Sthompsastatic int 1133168561Sthompsalacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1134168561Sthompsa const struct lacp_port *lp) 1135168561Sthompsa{ 1136168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1137168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1138168561Sthompsa return (0); 1139168561Sthompsa } 1140168561Sthompsa 1141168561Sthompsa if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1142168561Sthompsa return (0); 1143168561Sthompsa } 1144168561Sthompsa 1145168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1146168561Sthompsa return (0); 1147168561Sthompsa } 1148168561Sthompsa 1149168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1150168561Sthompsa return (0); 1151168561Sthompsa } 1152168561Sthompsa 1153168561Sthompsa return (1); 1154168561Sthompsa} 1155168561Sthompsa 1156168561Sthompsastatic int 1157168561Sthompsalacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1158168561Sthompsa const struct lacp_peerinfo *b) 1159168561Sthompsa{ 1160168561Sthompsa if (memcmp(&a->lip_systemid, &b->lip_systemid, 1161168561Sthompsa sizeof(a->lip_systemid))) { 1162168561Sthompsa return (0); 1163168561Sthompsa } 1164168561Sthompsa 1165168561Sthompsa if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1166168561Sthompsa return (0); 1167168561Sthompsa } 1168168561Sthompsa 1169168561Sthompsa return (1); 1170168561Sthompsa} 1171168561Sthompsa 1172168561Sthompsastatic void 1173168561Sthompsalacp_port_enable(struct lacp_port *lp) 1174168561Sthompsa{ 1175168561Sthompsa lp->lp_state |= LACP_STATE_AGGREGATION; 1176168561Sthompsa} 1177168561Sthompsa 1178168561Sthompsastatic void 1179168561Sthompsalacp_port_disable(struct lacp_port *lp) 1180168561Sthompsa{ 1181168561Sthompsa lacp_set_mux(lp, LACP_MUX_DETACHED); 1182168561Sthompsa 1183168561Sthompsa lp->lp_state &= ~LACP_STATE_AGGREGATION; 1184168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1185168561Sthompsa lacp_sm_rx_record_default(lp); 1186168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1187168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1188168561Sthompsa} 1189168561Sthompsa 1190168561Sthompsa/* 1191168561Sthompsa * lacp_select: select an aggregator. create one if necessary. 1192168561Sthompsa */ 1193168561Sthompsastatic void 1194168561Sthompsalacp_select(struct lacp_port *lp) 1195168561Sthompsa{ 1196168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1197168561Sthompsa struct lacp_aggregator *la; 1198168561Sthompsa#if defined(LACP_DEBUG) 1199168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1200168561Sthompsa#endif 1201168561Sthompsa 1202168561Sthompsa if (lp->lp_aggregator) { 1203168561Sthompsa return; 1204168561Sthompsa } 1205168561Sthompsa 1206168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1207168561Sthompsa ("timer_wait_while still active")); 1208168561Sthompsa 1209168561Sthompsa LACP_DPRINTF((lp, "port lagid=%s\n", 1210168561Sthompsa lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1211168561Sthompsa buf, sizeof(buf)))); 1212168561Sthompsa 1213168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1214168561Sthompsa if (lacp_aggregator_is_compatible(la, lp)) { 1215168561Sthompsa break; 1216168561Sthompsa } 1217168561Sthompsa } 1218168561Sthompsa 1219168561Sthompsa if (la == NULL) { 1220168561Sthompsa la = lacp_aggregator_get(lsc, lp); 1221168561Sthompsa if (la == NULL) { 1222168561Sthompsa LACP_DPRINTF((lp, "aggregator creation failed\n")); 1223168561Sthompsa 1224168561Sthompsa /* 1225168561Sthompsa * will retry on the next tick. 1226168561Sthompsa */ 1227168561Sthompsa 1228168561Sthompsa return; 1229168561Sthompsa } 1230168561Sthompsa lacp_fill_aggregator_id(la, lp); 1231168561Sthompsa LACP_DPRINTF((lp, "aggregator created\n")); 1232168561Sthompsa } else { 1233168561Sthompsa LACP_DPRINTF((lp, "compatible aggregator found\n")); 1234177274Sthompsa if (la->la_refcnt == LACP_MAX_PORTS) 1235177274Sthompsa return; 1236168561Sthompsa lacp_aggregator_addref(lsc, la); 1237168561Sthompsa } 1238168561Sthompsa 1239168561Sthompsa LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1240168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1241168561Sthompsa buf, sizeof(buf)))); 1242168561Sthompsa 1243168561Sthompsa lp->lp_aggregator = la; 1244168561Sthompsa lp->lp_selected = LACP_SELECTED; 1245168561Sthompsa} 1246168561Sthompsa 1247168561Sthompsa/* 1248168561Sthompsa * lacp_unselect: finish unselect/detach process. 1249168561Sthompsa */ 1250168561Sthompsa 1251168561Sthompsastatic void 1252168561Sthompsalacp_unselect(struct lacp_port *lp) 1253168561Sthompsa{ 1254168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1255168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1256168561Sthompsa 1257168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1258168561Sthompsa ("timer_wait_while still active")); 1259168561Sthompsa 1260168561Sthompsa if (la == NULL) { 1261168561Sthompsa return; 1262168561Sthompsa } 1263168561Sthompsa 1264168561Sthompsa lp->lp_aggregator = NULL; 1265168561Sthompsa lacp_aggregator_delref(lsc, la); 1266168561Sthompsa} 1267168561Sthompsa 1268168561Sthompsa/* mux machine */ 1269168561Sthompsa 1270168561Sthompsastatic void 1271168561Sthompsalacp_sm_mux(struct lacp_port *lp) 1272168561Sthompsa{ 1273168561Sthompsa enum lacp_mux_state new_state; 1274168561Sthompsa boolean_t p_sync = 1275168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1276168561Sthompsa boolean_t p_collecting = 1277168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1278168561Sthompsa enum lacp_selected selected = lp->lp_selected; 1279168561Sthompsa struct lacp_aggregator *la; 1280168561Sthompsa 1281168561Sthompsa /* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */ 1282168561Sthompsa 1283168561Sthompsare_eval: 1284168561Sthompsa la = lp->lp_aggregator; 1285168561Sthompsa KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1286168561Sthompsa ("MUX not detached")); 1287168561Sthompsa new_state = lp->lp_mux_state; 1288168561Sthompsa switch (lp->lp_mux_state) { 1289168561Sthompsa case LACP_MUX_DETACHED: 1290168561Sthompsa if (selected != LACP_UNSELECTED) { 1291168561Sthompsa new_state = LACP_MUX_WAITING; 1292168561Sthompsa } 1293168561Sthompsa break; 1294168561Sthompsa case LACP_MUX_WAITING: 1295168561Sthompsa KASSERT(la->la_pending > 0 || 1296168561Sthompsa !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1297168561Sthompsa ("timer_wait_while still active")); 1298168561Sthompsa if (selected == LACP_SELECTED && la->la_pending == 0) { 1299168561Sthompsa new_state = LACP_MUX_ATTACHED; 1300168561Sthompsa } else if (selected == LACP_UNSELECTED) { 1301168561Sthompsa new_state = LACP_MUX_DETACHED; 1302168561Sthompsa } 1303168561Sthompsa break; 1304168561Sthompsa case LACP_MUX_ATTACHED: 1305168561Sthompsa if (selected == LACP_SELECTED && p_sync) { 1306168561Sthompsa new_state = LACP_MUX_COLLECTING; 1307168561Sthompsa } else if (selected != LACP_SELECTED) { 1308168561Sthompsa new_state = LACP_MUX_DETACHED; 1309168561Sthompsa } 1310168561Sthompsa break; 1311168561Sthompsa case LACP_MUX_COLLECTING: 1312168561Sthompsa if (selected == LACP_SELECTED && p_sync && p_collecting) { 1313168561Sthompsa new_state = LACP_MUX_DISTRIBUTING; 1314168561Sthompsa } else if (selected != LACP_SELECTED || !p_sync) { 1315168561Sthompsa new_state = LACP_MUX_ATTACHED; 1316168561Sthompsa } 1317168561Sthompsa break; 1318168561Sthompsa case LACP_MUX_DISTRIBUTING: 1319168561Sthompsa if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1320168561Sthompsa new_state = LACP_MUX_COLLECTING; 1321168561Sthompsa } 1322168561Sthompsa break; 1323168561Sthompsa default: 1324168561Sthompsa panic("%s: unknown state", __func__); 1325168561Sthompsa } 1326168561Sthompsa 1327168561Sthompsa if (lp->lp_mux_state == new_state) { 1328168561Sthompsa return; 1329168561Sthompsa } 1330168561Sthompsa 1331168561Sthompsa lacp_set_mux(lp, new_state); 1332168561Sthompsa goto re_eval; 1333168561Sthompsa} 1334168561Sthompsa 1335168561Sthompsastatic void 1336168561Sthompsalacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1337168561Sthompsa{ 1338168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1339168561Sthompsa 1340168561Sthompsa if (lp->lp_mux_state == new_state) { 1341168561Sthompsa return; 1342168561Sthompsa } 1343168561Sthompsa 1344168561Sthompsa switch (new_state) { 1345168561Sthompsa case LACP_MUX_DETACHED: 1346168561Sthompsa lp->lp_state &= ~LACP_STATE_SYNC; 1347168561Sthompsa lacp_disable_distributing(lp); 1348168561Sthompsa lacp_disable_collecting(lp); 1349168561Sthompsa lacp_sm_assert_ntt(lp); 1350168561Sthompsa /* cancel timer */ 1351168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1352168561Sthompsa KASSERT(la->la_pending > 0, 1353168561Sthompsa ("timer_wait_while not active")); 1354168561Sthompsa la->la_pending--; 1355168561Sthompsa } 1356168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1357168561Sthompsa lacp_unselect(lp); 1358168561Sthompsa break; 1359168561Sthompsa case LACP_MUX_WAITING: 1360168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1361168561Sthompsa LACP_AGGREGATE_WAIT_TIME); 1362168561Sthompsa la->la_pending++; 1363168561Sthompsa break; 1364168561Sthompsa case LACP_MUX_ATTACHED: 1365168561Sthompsa lp->lp_state |= LACP_STATE_SYNC; 1366168561Sthompsa lacp_disable_collecting(lp); 1367168561Sthompsa lacp_sm_assert_ntt(lp); 1368168561Sthompsa break; 1369168561Sthompsa case LACP_MUX_COLLECTING: 1370168561Sthompsa lacp_enable_collecting(lp); 1371168561Sthompsa lacp_disable_distributing(lp); 1372168561Sthompsa lacp_sm_assert_ntt(lp); 1373168561Sthompsa break; 1374168561Sthompsa case LACP_MUX_DISTRIBUTING: 1375168561Sthompsa lacp_enable_distributing(lp); 1376168561Sthompsa break; 1377168561Sthompsa default: 1378168561Sthompsa panic("%s: unknown state", __func__); 1379168561Sthompsa } 1380168561Sthompsa 1381168561Sthompsa LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1382168561Sthompsa 1383168561Sthompsa lp->lp_mux_state = new_state; 1384168561Sthompsa} 1385168561Sthompsa 1386168561Sthompsastatic void 1387168561Sthompsalacp_sm_mux_timer(struct lacp_port *lp) 1388168561Sthompsa{ 1389168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1390168561Sthompsa#if defined(LACP_DEBUG) 1391168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1392168561Sthompsa#endif 1393168561Sthompsa 1394168561Sthompsa KASSERT(la->la_pending > 0, ("no pending event")); 1395168561Sthompsa 1396168561Sthompsa LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1397168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1398168561Sthompsa buf, sizeof(buf)), 1399168561Sthompsa la->la_pending, la->la_pending - 1)); 1400168561Sthompsa 1401168561Sthompsa la->la_pending--; 1402168561Sthompsa} 1403168561Sthompsa 1404168561Sthompsa/* periodic transmit machine */ 1405168561Sthompsa 1406168561Sthompsastatic void 1407168561Sthompsalacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1408168561Sthompsa{ 1409168561Sthompsa if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1410168561Sthompsa LACP_STATE_TIMEOUT)) { 1411168561Sthompsa return; 1412168561Sthompsa } 1413168561Sthompsa 1414168561Sthompsa LACP_DPRINTF((lp, "partner timeout changed\n")); 1415168561Sthompsa 1416168561Sthompsa /* 1417168561Sthompsa * FAST_PERIODIC -> SLOW_PERIODIC 1418168561Sthompsa * or 1419168561Sthompsa * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1420168561Sthompsa * 1421168561Sthompsa * let lacp_sm_ptx_tx_schedule to update timeout. 1422168561Sthompsa */ 1423168561Sthompsa 1424168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1425168561Sthompsa 1426168561Sthompsa /* 1427168561Sthompsa * if timeout has been shortened, assert NTT. 1428168561Sthompsa */ 1429168561Sthompsa 1430168561Sthompsa if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1431168561Sthompsa lacp_sm_assert_ntt(lp); 1432168561Sthompsa } 1433168561Sthompsa} 1434168561Sthompsa 1435168561Sthompsastatic void 1436168561Sthompsalacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1437168561Sthompsa{ 1438168561Sthompsa int timeout; 1439168561Sthompsa 1440168561Sthompsa if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1441168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1442168561Sthompsa 1443168561Sthompsa /* 1444168561Sthompsa * NO_PERIODIC 1445168561Sthompsa */ 1446168561Sthompsa 1447168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1448168561Sthompsa return; 1449168561Sthompsa } 1450168561Sthompsa 1451168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1452168561Sthompsa return; 1453168561Sthompsa } 1454168561Sthompsa 1455168561Sthompsa timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1456168561Sthompsa LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1457168561Sthompsa 1458168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1459168561Sthompsa} 1460168561Sthompsa 1461168561Sthompsastatic void 1462168561Sthompsalacp_sm_ptx_timer(struct lacp_port *lp) 1463168561Sthompsa{ 1464168561Sthompsa lacp_sm_assert_ntt(lp); 1465168561Sthompsa} 1466168561Sthompsa 1467168561Sthompsastatic void 1468168561Sthompsalacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1469168561Sthompsa{ 1470168561Sthompsa int timeout; 1471168561Sthompsa 1472168561Sthompsa /* 1473168561Sthompsa * check LACP_DISABLED first 1474168561Sthompsa */ 1475168561Sthompsa 1476168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1477168561Sthompsa return; 1478168561Sthompsa } 1479168561Sthompsa 1480168561Sthompsa /* 1481168561Sthompsa * check loopback condition. 1482168561Sthompsa */ 1483168561Sthompsa 1484168561Sthompsa if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1485168561Sthompsa &lp->lp_actor.lip_systemid)) { 1486168561Sthompsa return; 1487168561Sthompsa } 1488168561Sthompsa 1489168561Sthompsa /* 1490168561Sthompsa * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1491168561Sthompsa */ 1492168561Sthompsa 1493168561Sthompsa lacp_sm_rx_update_selected(lp, du); 1494168561Sthompsa lacp_sm_rx_update_ntt(lp, du); 1495168561Sthompsa lacp_sm_rx_record_pdu(lp, du); 1496168561Sthompsa 1497168561Sthompsa timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1498168561Sthompsa LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1499168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1500168561Sthompsa 1501168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1502168561Sthompsa 1503168561Sthompsa /* 1504168561Sthompsa * kick transmit machine without waiting the next tick. 1505168561Sthompsa */ 1506168561Sthompsa 1507168561Sthompsa lacp_sm_tx(lp); 1508168561Sthompsa} 1509168561Sthompsa 1510168561Sthompsastatic void 1511168561Sthompsalacp_sm_rx_set_expired(struct lacp_port *lp) 1512168561Sthompsa{ 1513168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1514168561Sthompsa lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1515168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1516168561Sthompsa lp->lp_state |= LACP_STATE_EXPIRED; 1517168561Sthompsa} 1518168561Sthompsa 1519168561Sthompsastatic void 1520168561Sthompsalacp_sm_rx_timer(struct lacp_port *lp) 1521168561Sthompsa{ 1522168561Sthompsa if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1523168561Sthompsa /* CURRENT -> EXPIRED */ 1524168561Sthompsa LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1525168561Sthompsa lacp_sm_rx_set_expired(lp); 1526168561Sthompsa } else { 1527168561Sthompsa /* EXPIRED -> DEFAULTED */ 1528168561Sthompsa LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1529168561Sthompsa lacp_sm_rx_update_default_selected(lp); 1530168561Sthompsa lacp_sm_rx_record_default(lp); 1531168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1532168561Sthompsa } 1533168561Sthompsa} 1534168561Sthompsa 1535168561Sthompsastatic void 1536168561Sthompsalacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1537168561Sthompsa{ 1538168561Sthompsa boolean_t active; 1539168561Sthompsa uint8_t oldpstate; 1540168561Sthompsa#if defined(LACP_DEBUG) 1541168561Sthompsa char buf[LACP_STATESTR_MAX+1]; 1542168561Sthompsa#endif 1543168561Sthompsa 1544168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1545168561Sthompsa 1546168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1547168561Sthompsa 1548168561Sthompsa active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1549168561Sthompsa || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1550168561Sthompsa (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1551168561Sthompsa 1552168561Sthompsa lp->lp_partner = du->ldu_actor; 1553168561Sthompsa if (active && 1554168561Sthompsa ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1555168561Sthompsa LACP_STATE_AGGREGATION) && 1556168561Sthompsa !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1557168561Sthompsa || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1558168561Sthompsa /* XXX nothing? */ 1559168561Sthompsa } else { 1560168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1561168561Sthompsa } 1562168561Sthompsa 1563168561Sthompsa lp->lp_state &= ~LACP_STATE_DEFAULTED; 1564168561Sthompsa 1565168561Sthompsa if (oldpstate != lp->lp_partner.lip_state) { 1566168561Sthompsa LACP_DPRINTF((lp, "old pstate %s\n", 1567168561Sthompsa lacp_format_state(oldpstate, buf, sizeof(buf)))); 1568168561Sthompsa LACP_DPRINTF((lp, "new pstate %s\n", 1569168561Sthompsa lacp_format_state(lp->lp_partner.lip_state, buf, 1570168561Sthompsa sizeof(buf)))); 1571168561Sthompsa } 1572168561Sthompsa 1573168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1574168561Sthompsa} 1575168561Sthompsa 1576168561Sthompsastatic void 1577168561Sthompsalacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1578168561Sthompsa{ 1579168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1580168561Sthompsa 1581168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1582168561Sthompsa !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1583168561Sthompsa LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1584168561Sthompsa LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1585168561Sthompsa lacp_sm_assert_ntt(lp); 1586168561Sthompsa } 1587168561Sthompsa} 1588168561Sthompsa 1589168561Sthompsastatic void 1590168561Sthompsalacp_sm_rx_record_default(struct lacp_port *lp) 1591168561Sthompsa{ 1592168561Sthompsa uint8_t oldpstate; 1593168561Sthompsa 1594168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1595168561Sthompsa 1596168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1597168561Sthompsa lp->lp_partner = lacp_partner_admin; 1598168561Sthompsa lp->lp_state |= LACP_STATE_DEFAULTED; 1599168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1600168561Sthompsa} 1601168561Sthompsa 1602168561Sthompsastatic void 1603168561Sthompsalacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1604168561Sthompsa const struct lacp_peerinfo *info) 1605168561Sthompsa{ 1606168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1607168561Sthompsa 1608168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1609168561Sthompsa !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1610168561Sthompsa LACP_STATE_AGGREGATION)) { 1611168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1612168561Sthompsa /* mux machine will clean up lp->lp_aggregator */ 1613168561Sthompsa } 1614168561Sthompsa} 1615168561Sthompsa 1616168561Sthompsastatic void 1617168561Sthompsalacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1618168561Sthompsa{ 1619168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1620168561Sthompsa 1621168561Sthompsa lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1622168561Sthompsa} 1623168561Sthompsa 1624168561Sthompsastatic void 1625168561Sthompsalacp_sm_rx_update_default_selected(struct lacp_port *lp) 1626168561Sthompsa{ 1627168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1628168561Sthompsa 1629168561Sthompsa lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin); 1630168561Sthompsa} 1631168561Sthompsa 1632168561Sthompsa/* transmit machine */ 1633168561Sthompsa 1634168561Sthompsastatic void 1635168561Sthompsalacp_sm_tx(struct lacp_port *lp) 1636168561Sthompsa{ 1637168561Sthompsa int error; 1638168561Sthompsa 1639168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1640168561Sthompsa#if 1 1641168561Sthompsa || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1642168561Sthompsa && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1643168561Sthompsa#endif 1644168561Sthompsa ) { 1645168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1646168561Sthompsa } 1647168561Sthompsa 1648168561Sthompsa if (!(lp->lp_flags & LACP_PORT_NTT)) { 1649168561Sthompsa return; 1650168561Sthompsa } 1651168561Sthompsa 1652168561Sthompsa /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1653168561Sthompsa if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1654168561Sthompsa (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1655168561Sthompsa LACP_DPRINTF((lp, "rate limited pdu\n")); 1656168561Sthompsa return; 1657168561Sthompsa } 1658168561Sthompsa 1659168561Sthompsa error = lacp_xmit_lacpdu(lp); 1660168561Sthompsa 1661168561Sthompsa if (error == 0) { 1662168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1663168561Sthompsa } else { 1664168561Sthompsa LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1665168561Sthompsa error)); 1666168561Sthompsa } 1667168561Sthompsa} 1668168561Sthompsa 1669168561Sthompsastatic void 1670168561Sthompsalacp_sm_assert_ntt(struct lacp_port *lp) 1671168561Sthompsa{ 1672168561Sthompsa 1673168561Sthompsa lp->lp_flags |= LACP_PORT_NTT; 1674168561Sthompsa} 1675168561Sthompsa 1676168561Sthompsastatic void 1677168561Sthompsalacp_run_timers(struct lacp_port *lp) 1678168561Sthompsa{ 1679168561Sthompsa int i; 1680168561Sthompsa 1681168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 1682168561Sthompsa KASSERT(lp->lp_timer[i] >= 0, 1683168561Sthompsa ("invalid timer value %d", lp->lp_timer[i])); 1684168561Sthompsa if (lp->lp_timer[i] == 0) { 1685168561Sthompsa continue; 1686168561Sthompsa } else if (--lp->lp_timer[i] <= 0) { 1687168561Sthompsa if (lacp_timer_funcs[i]) { 1688168561Sthompsa (*lacp_timer_funcs[i])(lp); 1689168561Sthompsa } 1690168561Sthompsa } 1691168561Sthompsa } 1692168561Sthompsa} 1693168561Sthompsa 1694168561Sthompsaint 1695177274Sthompsalacp_marker_input(struct lacp_port *lp, struct mbuf *m) 1696168561Sthompsa{ 1697177274Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1698177274Sthompsa struct lagg_port *lgp = lp->lp_lagg; 1699169739Sthompsa struct lacp_port *lp2; 1700168561Sthompsa struct markerdu *mdu; 1701168561Sthompsa int error = 0; 1702169739Sthompsa int pending = 0; 1703168561Sthompsa 1704168561Sthompsa if (m->m_pkthdr.len != sizeof(*mdu)) { 1705168561Sthompsa goto bad; 1706168561Sthompsa } 1707168561Sthompsa 1708168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 1709168561Sthompsa goto bad; 1710168561Sthompsa } 1711168561Sthompsa 1712168561Sthompsa if (m->m_len < sizeof(*mdu)) { 1713168561Sthompsa m = m_pullup(m, sizeof(*mdu)); 1714168561Sthompsa if (m == NULL) { 1715168561Sthompsa return (ENOMEM); 1716168561Sthompsa } 1717168561Sthompsa } 1718168561Sthompsa 1719168561Sthompsa mdu = mtod(m, struct markerdu *); 1720168561Sthompsa 1721168561Sthompsa if (memcmp(&mdu->mdu_eh.ether_dhost, 1722168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1723168561Sthompsa goto bad; 1724168561Sthompsa } 1725168561Sthompsa 1726168561Sthompsa if (mdu->mdu_sph.sph_version != 1) { 1727168561Sthompsa goto bad; 1728168561Sthompsa } 1729168561Sthompsa 1730168561Sthompsa switch (mdu->mdu_tlv.tlv_type) { 1731168561Sthompsa case MARKER_TYPE_INFO: 1732168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1733168561Sthompsa marker_info_tlv_template, TRUE)) { 1734168561Sthompsa goto bad; 1735168561Sthompsa } 1736168561Sthompsa mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1737168561Sthompsa memcpy(&mdu->mdu_eh.ether_dhost, 1738168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1739168561Sthompsa memcpy(&mdu->mdu_eh.ether_shost, 1740168793Sthompsa lgp->lp_lladdr, ETHER_ADDR_LEN); 1741168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 1742168561Sthompsa break; 1743168561Sthompsa 1744168561Sthompsa case MARKER_TYPE_RESPONSE: 1745168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1746168561Sthompsa marker_response_tlv_template, TRUE)) { 1747168561Sthompsa goto bad; 1748168561Sthompsa } 1749169739Sthompsa LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 1750169739Sthompsa ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 1751169739Sthompsa ":", ntohl(mdu->mdu_info.mi_rq_xid))); 1752169739Sthompsa 1753169739Sthompsa /* Verify that it is the last marker we sent out */ 1754169739Sthompsa if (memcmp(&mdu->mdu_info, &lp->lp_marker, 1755169739Sthompsa sizeof(struct lacp_markerinfo))) 1756169739Sthompsa goto bad; 1757169739Sthompsa 1758177274Sthompsa LACP_LOCK(lsc); 1759169739Sthompsa lp->lp_flags &= ~LACP_PORT_MARK; 1760169739Sthompsa 1761169739Sthompsa if (lsc->lsc_suppress_distributing) { 1762169739Sthompsa /* Check if any ports are waiting for a response */ 1763169739Sthompsa LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 1764169739Sthompsa if (lp2->lp_flags & LACP_PORT_MARK) { 1765169739Sthompsa pending = 1; 1766169739Sthompsa break; 1767169739Sthompsa } 1768169739Sthompsa } 1769169739Sthompsa 1770169739Sthompsa if (pending == 0) { 1771169739Sthompsa /* All interface queues are clear */ 1772169739Sthompsa LACP_DPRINTF((NULL, "queue flush complete\n")); 1773169739Sthompsa lsc->lsc_suppress_distributing = FALSE; 1774169739Sthompsa } 1775169739Sthompsa } 1776177274Sthompsa LACP_UNLOCK(lsc); 1777169739Sthompsa m_freem(m); 1778169739Sthompsa break; 1779169739Sthompsa 1780168561Sthompsa default: 1781168561Sthompsa goto bad; 1782168561Sthompsa } 1783168561Sthompsa 1784168561Sthompsa return (error); 1785168561Sthompsa 1786168561Sthompsabad: 1787169739Sthompsa LACP_DPRINTF((lp, "bad marker frame\n")); 1788168561Sthompsa m_freem(m); 1789168561Sthompsa return (EINVAL); 1790168561Sthompsa} 1791168561Sthompsa 1792168561Sthompsastatic int 1793168561Sthompsatlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1794168561Sthompsa const struct tlv_template *tmpl, boolean_t check_type) 1795168561Sthompsa{ 1796168561Sthompsa while (/* CONSTCOND */ 1) { 1797168561Sthompsa if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1798168561Sthompsa return (EINVAL); 1799168561Sthompsa } 1800168561Sthompsa if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1801168561Sthompsa tlv->tlv_length != tmpl->tmpl_length) { 1802168561Sthompsa return (EINVAL); 1803168561Sthompsa } 1804168561Sthompsa if (tmpl->tmpl_type == 0) { 1805168561Sthompsa break; 1806168561Sthompsa } 1807168561Sthompsa tlv = (const struct tlvhdr *) 1808168561Sthompsa ((const char *)tlv + tlv->tlv_length); 1809168561Sthompsa tmpl++; 1810168561Sthompsa } 1811168561Sthompsa 1812168561Sthompsa return (0); 1813168561Sthompsa} 1814168561Sthompsa 1815168561Sthompsa#if defined(LACP_DEBUG) 1816168561Sthompsaconst char * 1817168561Sthompsalacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1818168561Sthompsa{ 1819168561Sthompsa snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1820168561Sthompsa (int)mac[0], 1821168561Sthompsa (int)mac[1], 1822168561Sthompsa (int)mac[2], 1823168561Sthompsa (int)mac[3], 1824168561Sthompsa (int)mac[4], 1825168561Sthompsa (int)mac[5]); 1826168561Sthompsa 1827168561Sthompsa return (buf); 1828168561Sthompsa} 1829168561Sthompsa 1830168561Sthompsaconst char * 1831168561Sthompsalacp_format_systemid(const struct lacp_systemid *sysid, 1832168561Sthompsa char *buf, size_t buflen) 1833168561Sthompsa{ 1834168561Sthompsa char macbuf[LACP_MACSTR_MAX+1]; 1835168561Sthompsa 1836168561Sthompsa snprintf(buf, buflen, "%04X,%s", 1837168561Sthompsa ntohs(sysid->lsi_prio), 1838168561Sthompsa lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1839168561Sthompsa 1840168561Sthompsa return (buf); 1841168561Sthompsa} 1842168561Sthompsa 1843168561Sthompsaconst char * 1844168561Sthompsalacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1845168561Sthompsa{ 1846168561Sthompsa snprintf(buf, buflen, "%04X,%04X", 1847168561Sthompsa ntohs(portid->lpi_prio), 1848168561Sthompsa ntohs(portid->lpi_portno)); 1849168561Sthompsa 1850168561Sthompsa return (buf); 1851168561Sthompsa} 1852168561Sthompsa 1853168561Sthompsaconst char * 1854168561Sthompsalacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1855168561Sthompsa{ 1856168561Sthompsa char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1857168561Sthompsa char portid[LACP_PORTIDSTR_MAX+1]; 1858168561Sthompsa 1859168561Sthompsa snprintf(buf, buflen, "(%s,%04X,%s)", 1860168561Sthompsa lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1861168561Sthompsa ntohs(peer->lip_key), 1862168561Sthompsa lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1863168561Sthompsa 1864168561Sthompsa return (buf); 1865168561Sthompsa} 1866168561Sthompsa 1867168561Sthompsaconst char * 1868168561Sthompsalacp_format_lagid(const struct lacp_peerinfo *a, 1869168561Sthompsa const struct lacp_peerinfo *b, char *buf, size_t buflen) 1870168561Sthompsa{ 1871168561Sthompsa char astr[LACP_PARTNERSTR_MAX+1]; 1872168561Sthompsa char bstr[LACP_PARTNERSTR_MAX+1]; 1873168561Sthompsa 1874168561Sthompsa#if 0 1875168561Sthompsa /* 1876168561Sthompsa * there's a convention to display small numbered peer 1877168561Sthompsa * in the left. 1878168561Sthompsa */ 1879168561Sthompsa 1880168561Sthompsa if (lacp_compare_peerinfo(a, b) > 0) { 1881168561Sthompsa const struct lacp_peerinfo *t; 1882168561Sthompsa 1883168561Sthompsa t = a; 1884168561Sthompsa a = b; 1885168561Sthompsa b = t; 1886168561Sthompsa } 1887168561Sthompsa#endif 1888168561Sthompsa 1889168561Sthompsa snprintf(buf, buflen, "[%s,%s]", 1890168561Sthompsa lacp_format_partner(a, astr, sizeof(astr)), 1891168561Sthompsa lacp_format_partner(b, bstr, sizeof(bstr))); 1892168561Sthompsa 1893168561Sthompsa return (buf); 1894168561Sthompsa} 1895168561Sthompsa 1896168561Sthompsaconst char * 1897168561Sthompsalacp_format_lagid_aggregator(const struct lacp_aggregator *la, 1898168561Sthompsa char *buf, size_t buflen) 1899168561Sthompsa{ 1900168561Sthompsa if (la == NULL) { 1901168561Sthompsa return ("(none)"); 1902168561Sthompsa } 1903168561Sthompsa 1904168561Sthompsa return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 1905168561Sthompsa} 1906168561Sthompsa 1907168561Sthompsaconst char * 1908168561Sthompsalacp_format_state(uint8_t state, char *buf, size_t buflen) 1909168561Sthompsa{ 1910168561Sthompsa snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 1911168561Sthompsa return (buf); 1912168561Sthompsa} 1913168561Sthompsa 1914168561Sthompsastatic void 1915168561Sthompsalacp_dump_lacpdu(const struct lacpdu *du) 1916168561Sthompsa{ 1917168561Sthompsa char buf[LACP_PARTNERSTR_MAX+1]; 1918168561Sthompsa char buf2[LACP_STATESTR_MAX+1]; 1919168561Sthompsa 1920168561Sthompsa printf("actor=%s\n", 1921168561Sthompsa lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 1922168561Sthompsa printf("actor.state=%s\n", 1923168561Sthompsa lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 1924168561Sthompsa printf("partner=%s\n", 1925168561Sthompsa lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 1926168561Sthompsa printf("partner.state=%s\n", 1927168561Sthompsa lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 1928168561Sthompsa 1929168561Sthompsa printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 1930168561Sthompsa} 1931168561Sthompsa 1932168561Sthompsastatic void 1933168561Sthompsalacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 1934168561Sthompsa{ 1935168561Sthompsa va_list va; 1936168561Sthompsa 1937168561Sthompsa if (lp) { 1938168561Sthompsa printf("%s: ", lp->lp_ifp->if_xname); 1939168561Sthompsa } 1940168561Sthompsa 1941168561Sthompsa va_start(va, fmt); 1942168561Sthompsa vprintf(fmt, va); 1943168561Sthompsa va_end(va); 1944168561Sthompsa} 1945168561Sthompsa#endif 1946