ieee8023ad_lacp.c revision 168793
1168561Sthompsa/* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */ 2168561Sthompsa 3168561Sthompsa/*- 4168561Sthompsa * Copyright (c)2005 YAMAMOTO Takashi, 5168561Sthompsa * All rights reserved. 6168561Sthompsa * 7168561Sthompsa * Redistribution and use in source and binary forms, with or without 8168561Sthompsa * modification, are permitted provided that the following conditions 9168561Sthompsa * are met: 10168561Sthompsa * 1. Redistributions of source code must retain the above copyright 11168561Sthompsa * notice, this list of conditions and the following disclaimer. 12168561Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 13168561Sthompsa * notice, this list of conditions and the following disclaimer in the 14168561Sthompsa * documentation and/or other materials provided with the distribution. 15168561Sthompsa * 16168561Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17168561Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18168561Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19168561Sthompsa * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20168561Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21168561Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22168561Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23168561Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24168561Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25168561Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26168561Sthompsa * SUCH DAMAGE. 27168561Sthompsa */ 28168561Sthompsa 29168561Sthompsa#include <sys/cdefs.h> 30168561Sthompsa__FBSDID("$FreeBSD: head/sys/net/ieee8023ad_lacp.c 168793 2007-04-17 00:35:11Z thompsa $"); 31168561Sthompsa 32168561Sthompsa#include <sys/param.h> 33168561Sthompsa#include <sys/callout.h> 34168561Sthompsa#include <sys/mbuf.h> 35168561Sthompsa#include <sys/systm.h> 36168561Sthompsa#include <sys/malloc.h> 37168561Sthompsa#include <sys/kernel.h> /* hz */ 38168561Sthompsa#include <sys/socket.h> /* for net/if.h */ 39168561Sthompsa#include <sys/sockio.h> 40168561Sthompsa#include <machine/stdarg.h> 41168561Sthompsa 42168561Sthompsa#include <net/if.h> 43168561Sthompsa#include <net/if_dl.h> 44168561Sthompsa#include <net/ethernet.h> 45168561Sthompsa#include <net/if_media.h> 46168561Sthompsa#include <net/if_types.h> 47168561Sthompsa 48168793Sthompsa#include <net/if_lagg.h> 49168561Sthompsa#include <net/ieee8023ad_lacp.h> 50168561Sthompsa 51168561Sthompsa/* 52168561Sthompsa * actor system priority and port priority. 53168561Sthompsa * XXX should be configurable. 54168561Sthompsa */ 55168561Sthompsa 56168561Sthompsa#define LACP_SYSTEM_PRIO 0x8000 57168561Sthompsa#define LACP_PORT_PRIO 0x8000 58168561Sthompsa 59168561Sthompsaconst uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 60168561Sthompsa { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 61168561Sthompsa 62168561Sthompsastatic const struct tlv_template lacp_info_tlv_template[] = { 63168561Sthompsa { LACP_TYPE_ACTORINFO, 64168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 65168561Sthompsa { LACP_TYPE_PARTNERINFO, 66168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 67168561Sthompsa { LACP_TYPE_COLLECTORINFO, 68168561Sthompsa sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 69168561Sthompsa { 0, 0 }, 70168561Sthompsa}; 71168561Sthompsa 72168561Sthompsatypedef void (*lacp_timer_func_t)(struct lacp_port *); 73168561Sthompsa 74168561Sthompsastatic const struct tlv_template marker_info_tlv_template[] = { 75168561Sthompsa { MARKER_TYPE_INFO, 16 }, 76168561Sthompsa { 0, 0 }, 77168561Sthompsa}; 78168561Sthompsa 79168561Sthompsastatic const struct tlv_template marker_response_tlv_template[] = { 80168561Sthompsa { MARKER_TYPE_RESPONSE, 16 }, 81168561Sthompsa { 0, 0 }, 82168561Sthompsa}; 83168561Sthompsa 84168561Sthompsastatic void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 85168561Sthompsa 86168561Sthompsastatic uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 87168561Sthompsastatic void lacp_suppress_distributing(struct lacp_softc *, 88168561Sthompsa struct lacp_aggregator *); 89168561Sthompsastatic void lacp_transit_expire(void *); 90168561Sthompsastatic void lacp_select_active_aggregator(struct lacp_softc *); 91168561Sthompsastatic uint16_t lacp_compose_key(struct lacp_port *); 92168561Sthompsastatic int tlv_check(const void *, size_t, const struct tlvhdr *, 93168561Sthompsa const struct tlv_template *, boolean_t); 94168561Sthompsastatic void lacp_tick(void *); 95168561Sthompsa 96168561Sthompsastatic void lacp_fill_aggregator_id(struct lacp_aggregator *, 97168561Sthompsa const struct lacp_port *); 98168561Sthompsastatic void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 99168561Sthompsa const struct lacp_peerinfo *); 100168561Sthompsastatic int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 101168561Sthompsa const struct lacp_port *); 102168561Sthompsastatic int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 103168561Sthompsa const struct lacp_peerinfo *); 104168561Sthompsa 105168561Sthompsastatic struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 106168561Sthompsa struct lacp_port *); 107168561Sthompsastatic void lacp_aggregator_addref(struct lacp_softc *, 108168561Sthompsa struct lacp_aggregator *); 109168561Sthompsastatic void lacp_aggregator_delref(struct lacp_softc *, 110168561Sthompsa struct lacp_aggregator *); 111168561Sthompsa 112168561Sthompsa/* receive machine */ 113168561Sthompsa 114168561Sthompsastatic void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 115168561Sthompsastatic void lacp_sm_rx_timer(struct lacp_port *); 116168561Sthompsastatic void lacp_sm_rx_set_expired(struct lacp_port *); 117168561Sthompsastatic void lacp_sm_rx_update_ntt(struct lacp_port *, 118168561Sthompsa const struct lacpdu *); 119168561Sthompsastatic void lacp_sm_rx_record_pdu(struct lacp_port *, 120168561Sthompsa const struct lacpdu *); 121168561Sthompsastatic void lacp_sm_rx_update_selected(struct lacp_port *, 122168561Sthompsa const struct lacpdu *); 123168561Sthompsastatic void lacp_sm_rx_record_default(struct lacp_port *); 124168561Sthompsastatic void lacp_sm_rx_update_default_selected(struct lacp_port *); 125168561Sthompsastatic void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 126168561Sthompsa const struct lacp_peerinfo *); 127168561Sthompsa 128168561Sthompsa/* mux machine */ 129168561Sthompsa 130168561Sthompsastatic void lacp_sm_mux(struct lacp_port *); 131168561Sthompsastatic void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 132168561Sthompsastatic void lacp_sm_mux_timer(struct lacp_port *); 133168561Sthompsa 134168561Sthompsa/* periodic transmit machine */ 135168561Sthompsa 136168561Sthompsastatic void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 137168561Sthompsastatic void lacp_sm_ptx_tx_schedule(struct lacp_port *); 138168561Sthompsastatic void lacp_sm_ptx_timer(struct lacp_port *); 139168561Sthompsa 140168561Sthompsa/* transmit machine */ 141168561Sthompsa 142168561Sthompsastatic void lacp_sm_tx(struct lacp_port *); 143168561Sthompsastatic void lacp_sm_assert_ntt(struct lacp_port *); 144168561Sthompsa 145168561Sthompsastatic void lacp_run_timers(struct lacp_port *); 146168561Sthompsastatic int lacp_compare_peerinfo(const struct lacp_peerinfo *, 147168561Sthompsa const struct lacp_peerinfo *); 148168561Sthompsastatic int lacp_compare_systemid(const struct lacp_systemid *, 149168561Sthompsa const struct lacp_systemid *); 150168561Sthompsastatic void lacp_port_enable(struct lacp_port *); 151168561Sthompsastatic void lacp_port_disable(struct lacp_port *); 152168561Sthompsastatic void lacp_select(struct lacp_port *); 153168561Sthompsastatic void lacp_unselect(struct lacp_port *); 154168561Sthompsastatic void lacp_disable_collecting(struct lacp_port *); 155168561Sthompsastatic void lacp_enable_collecting(struct lacp_port *); 156168561Sthompsastatic void lacp_disable_distributing(struct lacp_port *); 157168561Sthompsastatic void lacp_enable_distributing(struct lacp_port *); 158168561Sthompsastatic int lacp_xmit_lacpdu(struct lacp_port *); 159168561Sthompsa 160168561Sthompsa#if defined(LACP_DEBUG) 161168561Sthompsastatic void lacp_dump_lacpdu(const struct lacpdu *); 162168561Sthompsastatic const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 163168561Sthompsa size_t); 164168561Sthompsastatic const char *lacp_format_lagid(const struct lacp_peerinfo *, 165168561Sthompsa const struct lacp_peerinfo *, char *, size_t); 166168561Sthompsastatic const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 167168561Sthompsa char *, size_t); 168168561Sthompsastatic const char *lacp_format_state(uint8_t, char *, size_t); 169168561Sthompsastatic const char *lacp_format_mac(const uint8_t *, char *, size_t); 170168561Sthompsastatic const char *lacp_format_systemid(const struct lacp_systemid *, char *, 171168561Sthompsa size_t); 172168561Sthompsastatic const char *lacp_format_portid(const struct lacp_portid *, char *, 173168561Sthompsa size_t); 174168561Sthompsastatic void lacp_dprintf(const struct lacp_port *, const char *, ...) 175168561Sthompsa __attribute__((__format__(__printf__, 2, 3))); 176168561Sthompsa#define LACP_DPRINTF(a) lacp_dprintf a 177168561Sthompsa#else 178168561Sthompsa#define LACP_DPRINTF(a) /* nothing */ 179168561Sthompsa#endif 180168561Sthompsa 181168561Sthompsa/* 182168561Sthompsa * partner administration variables. 183168561Sthompsa * XXX should be configurable. 184168561Sthompsa */ 185168561Sthompsa 186168561Sthompsastatic const struct lacp_peerinfo lacp_partner_admin = { 187168561Sthompsa .lip_systemid = { .lsi_prio = 0xffff }, 188168561Sthompsa .lip_portid = { .lpi_prio = 0xffff }, 189168561Sthompsa#if 1 190168561Sthompsa /* optimistic */ 191168561Sthompsa .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 192168561Sthompsa LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 193168561Sthompsa#else 194168561Sthompsa /* pessimistic */ 195168561Sthompsa .lip_state = 0, 196168561Sthompsa#endif 197168561Sthompsa}; 198168561Sthompsa 199168561Sthompsastatic const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 200168561Sthompsa [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 201168561Sthompsa [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 202168561Sthompsa [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 203168561Sthompsa}; 204168561Sthompsa 205168561Sthompsa/* 206168561Sthompsa * lacp_input: process lacpdu 207168561Sthompsa */ 208168561Sthompsaint 209168793Sthompsalacp_input(struct lagg_port *lgp, struct mbuf *m) 210168561Sthompsa{ 211168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 212168561Sthompsa struct lacpdu *du; 213168561Sthompsa int error = 0; 214168561Sthompsa 215168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 216168561Sthompsa 217168561Sthompsa if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 218168561Sthompsa goto bad; 219168561Sthompsa } 220168561Sthompsa 221168561Sthompsa if (m->m_pkthdr.len != sizeof(*du)) { 222168561Sthompsa goto bad; 223168561Sthompsa } 224168561Sthompsa 225168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 226168561Sthompsa goto bad; 227168561Sthompsa } 228168561Sthompsa 229168561Sthompsa if (m->m_len < sizeof(*du)) { 230168561Sthompsa m = m_pullup(m, sizeof(*du)); 231168561Sthompsa if (m == NULL) { 232168561Sthompsa return (ENOMEM); 233168561Sthompsa } 234168561Sthompsa } 235168561Sthompsa 236168561Sthompsa du = mtod(m, struct lacpdu *); 237168561Sthompsa 238168561Sthompsa if (memcmp(&du->ldu_eh.ether_dhost, 239168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 240168561Sthompsa goto bad; 241168561Sthompsa } 242168561Sthompsa 243168561Sthompsa /* XXX 244168561Sthompsa KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP, 245168561Sthompsa ("a very bad kassert!")); 246168561Sthompsa */ 247168561Sthompsa 248168561Sthompsa /* 249168561Sthompsa * ignore the version for compatibility with 250168561Sthompsa * the future protocol revisions. 251168561Sthompsa */ 252168561Sthompsa 253168561Sthompsa#if 0 254168561Sthompsa if (du->ldu_sph.sph_version != 1) { 255168561Sthompsa goto bad; 256168561Sthompsa } 257168561Sthompsa#endif 258168561Sthompsa 259168561Sthompsa /* 260168561Sthompsa * ignore tlv types for compatibility with 261168561Sthompsa * the future protocol revisions. 262168561Sthompsa */ 263168561Sthompsa 264168561Sthompsa if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 265168561Sthompsa lacp_info_tlv_template, FALSE)) { 266168561Sthompsa goto bad; 267168561Sthompsa } 268168561Sthompsa 269168561Sthompsa#if defined(LACP_DEBUG) 270168561Sthompsa LACP_DPRINTF((lp, "lacpdu receive\n")); 271168561Sthompsa lacp_dump_lacpdu(du); 272168561Sthompsa#endif /* defined(LACP_DEBUG) */ 273168561Sthompsa lacp_sm_rx(lp, du); 274168561Sthompsa 275168561Sthompsa m_freem(m); 276168561Sthompsa 277168561Sthompsa return (error); 278168561Sthompsa 279168561Sthompsabad: 280168561Sthompsa m_freem(m); 281168561Sthompsa return (EINVAL); 282168561Sthompsa} 283168561Sthompsa 284168561Sthompsastatic void 285168561Sthompsalacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 286168561Sthompsa{ 287168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 288168793Sthompsa struct lagg_softc *lgs = lgp->lp_lagg; 289168561Sthompsa 290168561Sthompsa info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 291168561Sthompsa memcpy(&info->lip_systemid.lsi_mac, 292168793Sthompsa IF_LLADDR(lgs->sc_ifp), ETHER_ADDR_LEN); 293168561Sthompsa info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 294168561Sthompsa info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 295168561Sthompsa info->lip_state = lp->lp_state; 296168561Sthompsa} 297168561Sthompsa 298168561Sthompsastatic int 299168561Sthompsalacp_xmit_lacpdu(struct lacp_port *lp) 300168561Sthompsa{ 301168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 302168561Sthompsa struct mbuf *m; 303168561Sthompsa struct lacpdu *du; 304168561Sthompsa int error; 305168561Sthompsa 306168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 307168561Sthompsa 308168561Sthompsa m = m_gethdr(M_DONTWAIT, MT_DATA); 309168561Sthompsa if (m == NULL) { 310168561Sthompsa return (ENOMEM); 311168561Sthompsa } 312168561Sthompsa m->m_len = m->m_pkthdr.len = sizeof(*du); 313168561Sthompsa 314168561Sthompsa du = mtod(m, struct lacpdu *); 315168561Sthompsa memset(du, 0, sizeof(*du)); 316168561Sthompsa 317168561Sthompsa memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 318168561Sthompsa ETHER_ADDR_LEN); 319168793Sthompsa memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 320168561Sthompsa du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 321168561Sthompsa 322168561Sthompsa du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 323168561Sthompsa du->ldu_sph.sph_version = 1; 324168561Sthompsa 325168561Sthompsa TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 326168561Sthompsa du->ldu_actor = lp->lp_actor; 327168561Sthompsa 328168561Sthompsa TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 329168561Sthompsa sizeof(du->ldu_partner)); 330168561Sthompsa du->ldu_partner = lp->lp_partner; 331168561Sthompsa 332168561Sthompsa TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 333168561Sthompsa sizeof(du->ldu_collector)); 334168561Sthompsa du->ldu_collector.lci_maxdelay = 0; 335168561Sthompsa 336168561Sthompsa#if defined(LACP_DEBUG) 337168561Sthompsa LACP_DPRINTF((lp, "lacpdu transmit\n")); 338168561Sthompsa lacp_dump_lacpdu(du); 339168561Sthompsa#endif /* defined(LACP_DEBUG) */ 340168561Sthompsa 341168561Sthompsa m->m_flags |= M_MCAST; 342168561Sthompsa 343168561Sthompsa /* 344168561Sthompsa * XXX should use higher priority queue. 345168561Sthompsa * otherwise network congestion can break aggregation. 346168561Sthompsa */ 347168561Sthompsa 348168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 349168561Sthompsa return (error); 350168561Sthompsa} 351168561Sthompsa 352168561Sthompsavoid 353168793Sthompsalacp_linkstate(struct lagg_port *lgp) 354168561Sthompsa{ 355168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 356168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 357168561Sthompsa struct ifmediareq ifmr; 358168561Sthompsa int error = 0; 359168561Sthompsa u_int media; 360168561Sthompsa uint8_t old_state; 361168561Sthompsa uint16_t old_key; 362168561Sthompsa 363168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 364168561Sthompsa 365168561Sthompsa bzero((char *)&ifmr, sizeof(ifmr)); 366168561Sthompsa error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 367168561Sthompsa if (error != 0) 368168561Sthompsa return; 369168561Sthompsa 370168561Sthompsa media = ifmr.ifm_active; 371168561Sthompsa LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x\n", lp->lp_media, media)); 372168561Sthompsa old_state = lp->lp_state; 373168561Sthompsa old_key = lp->lp_key; 374168561Sthompsa 375168561Sthompsa lp->lp_media = media; 376168561Sthompsa if ((media & IFM_HDX) != 0 || ifp->if_link_state == LINK_STATE_DOWN) { 377168561Sthompsa lacp_port_disable(lp); 378168561Sthompsa } else { 379168561Sthompsa lacp_port_enable(lp); 380168561Sthompsa } 381168561Sthompsa lp->lp_key = lacp_compose_key(lp); 382168561Sthompsa 383168561Sthompsa if (old_state != lp->lp_state || old_key != lp->lp_key) { 384168561Sthompsa LACP_DPRINTF((lp, "-> UNSELECTED\n")); 385168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 386168561Sthompsa } 387168561Sthompsa} 388168561Sthompsa 389168561Sthompsastatic void 390168561Sthompsalacp_tick(void *arg) 391168561Sthompsa{ 392168561Sthompsa struct lacp_softc *lsc = arg; 393168561Sthompsa struct lacp_port *lp; 394168561Sthompsa 395168561Sthompsa LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 396168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 397168561Sthompsa continue; 398168561Sthompsa 399168561Sthompsa lacp_run_timers(lp); 400168561Sthompsa 401168561Sthompsa lacp_select(lp); 402168561Sthompsa lacp_sm_mux(lp); 403168561Sthompsa lacp_sm_tx(lp); 404168561Sthompsa lacp_sm_ptx_tx_schedule(lp); 405168561Sthompsa } 406168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 407168561Sthompsa} 408168561Sthompsa 409168561Sthompsaint 410168793Sthompsalacp_port_create(struct lagg_port *lgp) 411168561Sthompsa{ 412168793Sthompsa struct lagg_softc *lgs = lgp->lp_lagg; 413168793Sthompsa struct lacp_softc *lsc = LACP_SOFTC(lgs); 414168561Sthompsa struct lacp_port *lp; 415168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 416168561Sthompsa struct sockaddr_dl sdl; 417168561Sthompsa struct ifmultiaddr *rifma = NULL; 418168561Sthompsa int error; 419168561Sthompsa 420168561Sthompsa boolean_t active = TRUE; /* XXX should be configurable */ 421168561Sthompsa boolean_t fast = FALSE; /* XXX should be configurable */ 422168561Sthompsa 423168793Sthompsa LAGG_LOCK_ASSERT(lgs); 424168561Sthompsa 425168561Sthompsa bzero((char *)&sdl, sizeof(sdl)); 426168561Sthompsa sdl.sdl_len = sizeof(sdl); 427168561Sthompsa sdl.sdl_family = AF_LINK; 428168561Sthompsa sdl.sdl_index = ifp->if_index; 429168561Sthompsa sdl.sdl_type = IFT_ETHER; 430168561Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 431168561Sthompsa 432168561Sthompsa bcopy(ðermulticastaddr_slowprotocols, 433168561Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 434168561Sthompsa error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 435168561Sthompsa if (error) { 436168793Sthompsa printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname); 437168561Sthompsa return (error); 438168561Sthompsa } 439168561Sthompsa 440168561Sthompsa lp = malloc(sizeof(struct lacp_port), 441168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 442168561Sthompsa if (lp == NULL) 443168561Sthompsa return (ENOMEM); 444168561Sthompsa 445168793Sthompsa lgp->lp_psc = (caddr_t)lp; 446168561Sthompsa lp->lp_ifp = ifp; 447168793Sthompsa lp->lp_lagg = lgp; 448168561Sthompsa lp->lp_lsc = lsc; 449168561Sthompsa 450168561Sthompsa LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 451168561Sthompsa 452168561Sthompsa lacp_fill_actorinfo(lp, &lp->lp_actor); 453168561Sthompsa lp->lp_state = 454168561Sthompsa (active ? LACP_STATE_ACTIVITY : 0) | 455168561Sthompsa (fast ? LACP_STATE_TIMEOUT : 0); 456168561Sthompsa lp->lp_aggregator = NULL; 457168793Sthompsa lacp_linkstate(lgp); 458168561Sthompsa lacp_sm_rx_set_expired(lp); 459168561Sthompsa 460168561Sthompsa return (0); 461168561Sthompsa} 462168561Sthompsa 463168561Sthompsavoid 464168793Sthompsalacp_port_destroy(struct lagg_port *lgp) 465168561Sthompsa{ 466168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 467168793Sthompsa struct ifnet *ifp = lgp->lp_ifp; 468168561Sthompsa struct sockaddr_dl sdl; 469168561Sthompsa int i, error; 470168561Sthompsa 471168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 472168561Sthompsa 473168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 474168561Sthompsa LACP_TIMER_DISARM(lp, i); 475168561Sthompsa } 476168561Sthompsa 477168561Sthompsa lacp_disable_collecting(lp); 478168561Sthompsa lacp_disable_distributing(lp); 479168561Sthompsa lacp_unselect(lp); 480168561Sthompsa 481168561Sthompsa bzero((char *)&sdl, sizeof(sdl)); 482168561Sthompsa sdl.sdl_len = sizeof(sdl); 483168561Sthompsa sdl.sdl_family = AF_LINK; 484168561Sthompsa sdl.sdl_index = ifp->if_index; 485168561Sthompsa sdl.sdl_type = IFT_ETHER; 486168561Sthompsa sdl.sdl_alen = ETHER_ADDR_LEN; 487168561Sthompsa 488168561Sthompsa bcopy(ðermulticastaddr_slowprotocols, 489168561Sthompsa LLADDR(&sdl), ETHER_ADDR_LEN); 490168561Sthompsa error = if_delmulti(ifp, (struct sockaddr *)&sdl); 491168561Sthompsa if (error) 492168793Sthompsa printf("%s: DELMULTI failed on %s\n", __func__, lgp->lp_ifname); 493168561Sthompsa 494168561Sthompsa LIST_REMOVE(lp, lp_next); 495168561Sthompsa free(lp, M_DEVBUF); 496168561Sthompsa} 497168561Sthompsa 498168561Sthompsaint 499168793Sthompsalacp_port_isactive(struct lagg_port *lgp) 500168561Sthompsa{ 501168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 502168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 503168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 504168561Sthompsa 505168561Sthompsa /* This port is joined to the active aggregator */ 506168561Sthompsa if (la != NULL && la == lsc->lsc_active_aggregator) 507168561Sthompsa return (1); 508168561Sthompsa 509168561Sthompsa return (0); 510168561Sthompsa} 511168561Sthompsa 512168561Sthompsastatic void 513168561Sthompsalacp_disable_collecting(struct lacp_port *lp) 514168561Sthompsa{ 515168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 516168561Sthompsa 517168561Sthompsa LACP_DPRINTF((lp, "collecting disabled\n")); 518168561Sthompsa 519168561Sthompsa lp->lp_state &= ~LACP_STATE_COLLECTING; 520168793Sthompsa lgp->lp_flags &= ~LAGG_PORT_COLLECTING; 521168561Sthompsa} 522168561Sthompsa 523168561Sthompsastatic void 524168561Sthompsalacp_enable_collecting(struct lacp_port *lp) 525168561Sthompsa{ 526168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 527168561Sthompsa 528168561Sthompsa LACP_DPRINTF((lp, "collecting enabled\n")); 529168561Sthompsa 530168561Sthompsa lp->lp_state |= LACP_STATE_COLLECTING; 531168793Sthompsa lgp->lp_flags |= LAGG_PORT_COLLECTING; 532168561Sthompsa} 533168561Sthompsa 534168561Sthompsastatic void 535168561Sthompsalacp_disable_distributing(struct lacp_port *lp) 536168561Sthompsa{ 537168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 538168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 539168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 540168561Sthompsa#if defined(LACP_DEBUG) 541168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 542168561Sthompsa#endif /* defined(LACP_DEBUG) */ 543168561Sthompsa 544168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 545168561Sthompsa 546168561Sthompsa if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 547168561Sthompsa return; 548168561Sthompsa } 549168561Sthompsa 550168561Sthompsa KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 551168561Sthompsa KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 552168561Sthompsa KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 553168561Sthompsa 554168561Sthompsa LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 555168561Sthompsa "nports %d -> %d\n", 556168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 557168561Sthompsa la->la_nports, la->la_nports - 1)); 558168561Sthompsa 559168561Sthompsa TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 560168561Sthompsa la->la_nports--; 561168561Sthompsa 562168561Sthompsa lacp_suppress_distributing(lsc, la); 563168561Sthompsa 564168561Sthompsa lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 565168793Sthompsa lgp->lp_flags &= ~LAGG_PORT_DISTRIBUTING; 566168561Sthompsa 567168561Sthompsa if (lsc->lsc_active_aggregator == la) { 568168561Sthompsa lacp_select_active_aggregator(lsc); 569168561Sthompsa } 570168561Sthompsa} 571168561Sthompsa 572168561Sthompsastatic void 573168561Sthompsalacp_enable_distributing(struct lacp_port *lp) 574168561Sthompsa{ 575168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 576168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 577168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 578168561Sthompsa#if defined(LACP_DEBUG) 579168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 580168561Sthompsa#endif /* defined(LACP_DEBUG) */ 581168561Sthompsa 582168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 583168561Sthompsa 584168561Sthompsa if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 585168561Sthompsa return; 586168561Sthompsa } 587168561Sthompsa 588168561Sthompsa LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 589168561Sthompsa "nports %d -> %d\n", 590168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 591168561Sthompsa la->la_nports, la->la_nports + 1)); 592168561Sthompsa 593168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 594168561Sthompsa TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 595168561Sthompsa la->la_nports++; 596168561Sthompsa 597168561Sthompsa lacp_suppress_distributing(lsc, la); 598168561Sthompsa 599168561Sthompsa lp->lp_state |= LACP_STATE_DISTRIBUTING; 600168793Sthompsa lgp->lp_flags |= LAGG_PORT_DISTRIBUTING; 601168561Sthompsa 602168561Sthompsa if (lsc->lsc_active_aggregator != la) { 603168561Sthompsa lacp_select_active_aggregator(lsc); 604168561Sthompsa } 605168561Sthompsa} 606168561Sthompsa 607168561Sthompsastatic void 608168561Sthompsalacp_transit_expire(void *vp) 609168561Sthompsa{ 610168561Sthompsa struct lacp_softc *lsc = vp; 611168561Sthompsa 612168561Sthompsa LACP_DPRINTF((NULL, "%s\n", __func__)); 613168561Sthompsa lsc->lsc_suppress_distributing = FALSE; 614168561Sthompsa} 615168561Sthompsa 616168561Sthompsaint 617168793Sthompsalacp_attach(struct lagg_softc *lgs) 618168561Sthompsa{ 619168561Sthompsa struct lacp_softc *lsc; 620168561Sthompsa 621168793Sthompsa LAGG_LOCK_ASSERT(lgs); 622168561Sthompsa 623168561Sthompsa lsc = malloc(sizeof(struct lacp_softc), 624168561Sthompsa M_DEVBUF, M_NOWAIT|M_ZERO); 625168561Sthompsa if (lsc == NULL) 626168561Sthompsa return (ENOMEM); 627168561Sthompsa 628168793Sthompsa lgs->sc_psc = (caddr_t)lsc; 629168793Sthompsa lsc->lsc_lagg = lgs; 630168561Sthompsa 631168561Sthompsa lsc->lsc_hashkey = arc4random(); 632168561Sthompsa lsc->lsc_active_aggregator = NULL; 633168561Sthompsa TAILQ_INIT(&lsc->lsc_aggregators); 634168561Sthompsa LIST_INIT(&lsc->lsc_ports); 635168561Sthompsa 636168793Sthompsa callout_init_mtx(&lsc->lsc_transit_callout, &lgs->sc_mtx, 0); 637168793Sthompsa callout_init_mtx(&lsc->lsc_callout, &lgs->sc_mtx, 0); 638168561Sthompsa 639168793Sthompsa /* if the lagg is already up then do the same */ 640168793Sthompsa if (lgs->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 641168793Sthompsa lacp_init(lgs); 642168561Sthompsa 643168561Sthompsa return (0); 644168561Sthompsa} 645168561Sthompsa 646168561Sthompsaint 647168793Sthompsalacp_detach(struct lagg_softc *lgs) 648168561Sthompsa{ 649168793Sthompsa struct lacp_softc *lsc = LACP_SOFTC(lgs); 650168561Sthompsa 651168561Sthompsa KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 652168561Sthompsa ("aggregators still active")); 653168561Sthompsa KASSERT(lsc->lsc_active_aggregator == NULL, 654168561Sthompsa ("aggregator still attached")); 655168561Sthompsa 656168793Sthompsa lgs->sc_psc = NULL; 657168561Sthompsa callout_drain(&lsc->lsc_transit_callout); 658168561Sthompsa callout_drain(&lsc->lsc_callout); 659168561Sthompsa 660168561Sthompsa free(lsc, M_DEVBUF); 661168561Sthompsa return (0); 662168561Sthompsa} 663168561Sthompsa 664168561Sthompsavoid 665168793Sthompsalacp_init(struct lagg_softc *lgs) 666168561Sthompsa{ 667168793Sthompsa struct lacp_softc *lsc = LACP_SOFTC(lgs); 668168561Sthompsa 669168561Sthompsa callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 670168561Sthompsa} 671168561Sthompsa 672168561Sthompsavoid 673168793Sthompsalacp_stop(struct lagg_softc *lgs) 674168561Sthompsa{ 675168793Sthompsa struct lacp_softc *lsc = LACP_SOFTC(lgs); 676168561Sthompsa 677168561Sthompsa callout_stop(&lsc->lsc_transit_callout); 678168561Sthompsa callout_stop(&lsc->lsc_callout); 679168561Sthompsa} 680168561Sthompsa 681168793Sthompsastruct lagg_port * 682168793Sthompsalacp_select_tx_port(struct lagg_softc *lgs, struct mbuf *m) 683168561Sthompsa{ 684168793Sthompsa struct lacp_softc *lsc = LACP_SOFTC(lgs); 685168561Sthompsa struct lacp_aggregator *la; 686168561Sthompsa struct lacp_port *lp; 687168561Sthompsa uint32_t hash; 688168561Sthompsa int nports; 689168561Sthompsa 690168793Sthompsa LAGG_LOCK_ASSERT(lgs); 691168561Sthompsa 692168561Sthompsa if (__predict_false(lsc->lsc_suppress_distributing)) { 693168561Sthompsa LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 694168561Sthompsa return (NULL); 695168561Sthompsa } 696168561Sthompsa 697168561Sthompsa la = lsc->lsc_active_aggregator; 698168561Sthompsa if (__predict_false(la == NULL)) { 699168561Sthompsa LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 700168561Sthompsa return (NULL); 701168561Sthompsa } 702168561Sthompsa 703168561Sthompsa nports = la->la_nports; 704168561Sthompsa KASSERT(nports > 0, ("no ports available")); 705168561Sthompsa 706168793Sthompsa hash = lagg_hashmbuf(m, lsc->lsc_hashkey); 707168561Sthompsa hash %= nports; 708168561Sthompsa lp = TAILQ_FIRST(&la->la_ports); 709168561Sthompsa while (hash--) { 710168561Sthompsa lp = TAILQ_NEXT(lp, lp_dist_q); 711168561Sthompsa } 712168561Sthompsa 713168561Sthompsa KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 714168561Sthompsa ("aggregated port is not distributing")); 715168561Sthompsa 716168793Sthompsa return (lp->lp_lagg); 717168561Sthompsa} 718168561Sthompsa/* 719168561Sthompsa * lacp_suppress_distributing: drop transmit packets for a while 720168561Sthompsa * to preserve packet ordering. 721168561Sthompsa */ 722168561Sthompsa 723168561Sthompsastatic void 724168561Sthompsalacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 725168561Sthompsa{ 726168561Sthompsa if (lsc->lsc_active_aggregator != la) { 727168561Sthompsa return; 728168561Sthompsa } 729168561Sthompsa 730168561Sthompsa LACP_DPRINTF((NULL, "%s\n", __func__)); 731168561Sthompsa lsc->lsc_suppress_distributing = TRUE; 732168561Sthompsa /* XXX should consider collector max delay */ 733168561Sthompsa callout_reset(&lsc->lsc_transit_callout, 734168561Sthompsa LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 735168561Sthompsa} 736168561Sthompsa 737168561Sthompsastatic int 738168561Sthompsalacp_compare_peerinfo(const struct lacp_peerinfo *a, 739168561Sthompsa const struct lacp_peerinfo *b) 740168561Sthompsa{ 741168561Sthompsa return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 742168561Sthompsa} 743168561Sthompsa 744168561Sthompsastatic int 745168561Sthompsalacp_compare_systemid(const struct lacp_systemid *a, 746168561Sthompsa const struct lacp_systemid *b) 747168561Sthompsa{ 748168561Sthompsa return (memcmp(a, b, sizeof(*a))); 749168561Sthompsa} 750168561Sthompsa 751168561Sthompsa#if 0 /* unused */ 752168561Sthompsastatic int 753168561Sthompsalacp_compare_portid(const struct lacp_portid *a, 754168561Sthompsa const struct lacp_portid *b) 755168561Sthompsa{ 756168561Sthompsa return (memcmp(a, b, sizeof(*a))); 757168561Sthompsa} 758168561Sthompsa#endif 759168561Sthompsa 760168561Sthompsastatic uint64_t 761168561Sthompsalacp_aggregator_bandwidth(struct lacp_aggregator *la) 762168561Sthompsa{ 763168561Sthompsa struct lacp_port *lp; 764168561Sthompsa uint64_t speed; 765168561Sthompsa 766168561Sthompsa lp = TAILQ_FIRST(&la->la_ports); 767168561Sthompsa if (lp == NULL) { 768168561Sthompsa return (0); 769168561Sthompsa } 770168561Sthompsa 771168561Sthompsa speed = ifmedia_baudrate(lp->lp_media); 772168561Sthompsa speed *= la->la_nports; 773168561Sthompsa if (speed == 0) { 774168561Sthompsa LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 775168561Sthompsa lp->lp_media, la->la_nports)); 776168561Sthompsa } 777168561Sthompsa 778168561Sthompsa return (speed); 779168561Sthompsa} 780168561Sthompsa 781168561Sthompsa/* 782168561Sthompsa * lacp_select_active_aggregator: select an aggregator to be used to transmit 783168793Sthompsa * packets from lagg(4) interface. 784168561Sthompsa */ 785168561Sthompsa 786168561Sthompsastatic void 787168561Sthompsalacp_select_active_aggregator(struct lacp_softc *lsc) 788168561Sthompsa{ 789168561Sthompsa struct lacp_aggregator *la; 790168561Sthompsa struct lacp_aggregator *best_la = NULL; 791168561Sthompsa uint64_t best_speed = 0; 792168561Sthompsa#if defined(LACP_DEBUG) 793168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 794168561Sthompsa#endif /* defined(LACP_DEBUG) */ 795168561Sthompsa 796168561Sthompsa LACP_DPRINTF((NULL, "%s:\n", __func__)); 797168561Sthompsa 798168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 799168561Sthompsa uint64_t speed; 800168561Sthompsa 801168561Sthompsa if (la->la_nports == 0) { 802168561Sthompsa continue; 803168561Sthompsa } 804168561Sthompsa 805168561Sthompsa speed = lacp_aggregator_bandwidth(la); 806168561Sthompsa LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 807168561Sthompsa lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 808168561Sthompsa speed, la->la_nports)); 809168561Sthompsa if (speed > best_speed || 810168561Sthompsa (speed == best_speed && 811168561Sthompsa la == lsc->lsc_active_aggregator)) { 812168561Sthompsa best_la = la; 813168561Sthompsa best_speed = speed; 814168561Sthompsa } 815168561Sthompsa } 816168561Sthompsa 817168561Sthompsa KASSERT(best_la == NULL || best_la->la_nports > 0, 818168561Sthompsa ("invalid aggregator refcnt")); 819168561Sthompsa KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 820168561Sthompsa ("invalid aggregator list")); 821168561Sthompsa 822168561Sthompsa#if defined(LACP_DEBUG) 823168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 824168561Sthompsa LACP_DPRINTF((NULL, "active aggregator changed\n")); 825168561Sthompsa LACP_DPRINTF((NULL, "old %s\n", 826168561Sthompsa lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 827168561Sthompsa buf, sizeof(buf)))); 828168561Sthompsa } else { 829168561Sthompsa LACP_DPRINTF((NULL, "active aggregator not changed\n")); 830168561Sthompsa } 831168561Sthompsa LACP_DPRINTF((NULL, "new %s\n", 832168561Sthompsa lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 833168561Sthompsa#endif /* defined(LACP_DEBUG) */ 834168561Sthompsa 835168561Sthompsa if (lsc->lsc_active_aggregator != best_la) { 836168561Sthompsa lsc->lsc_active_aggregator = best_la; 837168561Sthompsa if (best_la) { 838168561Sthompsa lacp_suppress_distributing(lsc, best_la); 839168561Sthompsa } 840168561Sthompsa } 841168561Sthompsa} 842168561Sthompsa 843168561Sthompsastatic uint16_t 844168561Sthompsalacp_compose_key(struct lacp_port *lp) 845168561Sthompsa{ 846168793Sthompsa struct lagg_port *lgp = lp->lp_lagg; 847168793Sthompsa struct lagg_softc *lgs = lgp->lp_lagg; 848168561Sthompsa u_int media = lp->lp_media; 849168561Sthompsa uint16_t key; 850168561Sthompsa 851168561Sthompsa KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid interface type")); 852168561Sthompsa 853168561Sthompsa if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 854168561Sthompsa 855168561Sthompsa /* 856168561Sthompsa * non-aggregatable links should have unique keys. 857168561Sthompsa * 858168561Sthompsa * XXX this isn't really unique as if_index is 16 bit. 859168561Sthompsa */ 860168561Sthompsa 861168561Sthompsa /* bit 0..14: (some bits of) if_index of this port */ 862168561Sthompsa key = lp->lp_ifp->if_index; 863168561Sthompsa /* bit 15: 1 */ 864168561Sthompsa key |= 0x8000; 865168561Sthompsa } else { 866168561Sthompsa u_int subtype = IFM_SUBTYPE(media); 867168561Sthompsa 868168561Sthompsa KASSERT((media & IFM_HDX) == 0, ("aggregating HDX interface")); 869168561Sthompsa 870168561Sthompsa /* bit 0..4: IFM_SUBTYPE */ 871168561Sthompsa key = subtype; 872168793Sthompsa /* bit 5..14: (some bits of) if_index of lagg device */ 873168793Sthompsa key |= 0x7fe0 & ((lgs->sc_ifp->if_index) << 5); 874168561Sthompsa /* bit 15: 0 */ 875168561Sthompsa } 876168561Sthompsa return (htons(key)); 877168561Sthompsa} 878168561Sthompsa 879168561Sthompsastatic void 880168561Sthompsalacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 881168561Sthompsa{ 882168561Sthompsa#if defined(LACP_DEBUG) 883168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 884168561Sthompsa#endif 885168561Sthompsa 886168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 887168561Sthompsa __func__, 888168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 889168561Sthompsa buf, sizeof(buf)), 890168561Sthompsa la->la_refcnt, la->la_refcnt + 1)); 891168561Sthompsa 892168561Sthompsa KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 893168561Sthompsa la->la_refcnt++; 894168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 895168561Sthompsa} 896168561Sthompsa 897168561Sthompsastatic void 898168561Sthompsalacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 899168561Sthompsa{ 900168561Sthompsa#if defined(LACP_DEBUG) 901168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 902168561Sthompsa#endif 903168561Sthompsa 904168561Sthompsa LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 905168561Sthompsa __func__, 906168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 907168561Sthompsa buf, sizeof(buf)), 908168561Sthompsa la->la_refcnt, la->la_refcnt - 1)); 909168561Sthompsa 910168561Sthompsa KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 911168561Sthompsa la->la_refcnt--; 912168561Sthompsa if (la->la_refcnt > 0) { 913168561Sthompsa return; 914168561Sthompsa } 915168561Sthompsa 916168561Sthompsa KASSERT(la->la_refcnt == 0, ("refcount not zero")); 917168561Sthompsa KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 918168561Sthompsa 919168561Sthompsa TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 920168561Sthompsa 921168561Sthompsa free(la, M_DEVBUF); 922168561Sthompsa} 923168561Sthompsa 924168561Sthompsa/* 925168561Sthompsa * lacp_aggregator_get: allocate an aggregator. 926168561Sthompsa */ 927168561Sthompsa 928168561Sthompsastatic struct lacp_aggregator * 929168561Sthompsalacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 930168561Sthompsa{ 931168561Sthompsa struct lacp_aggregator *la; 932168561Sthompsa 933168561Sthompsa la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 934168561Sthompsa if (la) { 935168561Sthompsa la->la_refcnt = 1; 936168561Sthompsa la->la_nports = 0; 937168561Sthompsa TAILQ_INIT(&la->la_ports); 938168561Sthompsa la->la_pending = 0; 939168561Sthompsa TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 940168561Sthompsa } 941168561Sthompsa 942168561Sthompsa return (la); 943168561Sthompsa} 944168561Sthompsa 945168561Sthompsa/* 946168561Sthompsa * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 947168561Sthompsa */ 948168561Sthompsa 949168561Sthompsastatic void 950168561Sthompsalacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 951168561Sthompsa{ 952168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 953168561Sthompsa lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 954168561Sthompsa 955168561Sthompsa la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 956168561Sthompsa} 957168561Sthompsa 958168561Sthompsastatic void 959168561Sthompsalacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 960168561Sthompsa const struct lacp_peerinfo *lpi_port) 961168561Sthompsa{ 962168561Sthompsa memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 963168561Sthompsa lpi_aggr->lip_systemid = lpi_port->lip_systemid; 964168561Sthompsa lpi_aggr->lip_key = lpi_port->lip_key; 965168561Sthompsa} 966168561Sthompsa 967168561Sthompsa/* 968168561Sthompsa * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 969168561Sthompsa */ 970168561Sthompsa 971168561Sthompsastatic int 972168561Sthompsalacp_aggregator_is_compatible(const struct lacp_aggregator *la, 973168561Sthompsa const struct lacp_port *lp) 974168561Sthompsa{ 975168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 976168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 977168561Sthompsa return (0); 978168561Sthompsa } 979168561Sthompsa 980168561Sthompsa if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 981168561Sthompsa return (0); 982168561Sthompsa } 983168561Sthompsa 984168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 985168561Sthompsa return (0); 986168561Sthompsa } 987168561Sthompsa 988168561Sthompsa if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 989168561Sthompsa return (0); 990168561Sthompsa } 991168561Sthompsa 992168561Sthompsa return (1); 993168561Sthompsa} 994168561Sthompsa 995168561Sthompsastatic int 996168561Sthompsalacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 997168561Sthompsa const struct lacp_peerinfo *b) 998168561Sthompsa{ 999168561Sthompsa if (memcmp(&a->lip_systemid, &b->lip_systemid, 1000168561Sthompsa sizeof(a->lip_systemid))) { 1001168561Sthompsa return (0); 1002168561Sthompsa } 1003168561Sthompsa 1004168561Sthompsa if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1005168561Sthompsa return (0); 1006168561Sthompsa } 1007168561Sthompsa 1008168561Sthompsa return (1); 1009168561Sthompsa} 1010168561Sthompsa 1011168561Sthompsastatic void 1012168561Sthompsalacp_port_enable(struct lacp_port *lp) 1013168561Sthompsa{ 1014168561Sthompsa lp->lp_state |= LACP_STATE_AGGREGATION; 1015168561Sthompsa} 1016168561Sthompsa 1017168561Sthompsastatic void 1018168561Sthompsalacp_port_disable(struct lacp_port *lp) 1019168561Sthompsa{ 1020168561Sthompsa lacp_set_mux(lp, LACP_MUX_DETACHED); 1021168561Sthompsa 1022168561Sthompsa lp->lp_state &= ~LACP_STATE_AGGREGATION; 1023168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1024168561Sthompsa lacp_sm_rx_record_default(lp); 1025168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1026168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1027168561Sthompsa} 1028168561Sthompsa 1029168561Sthompsa/* 1030168561Sthompsa * lacp_select: select an aggregator. create one if necessary. 1031168561Sthompsa */ 1032168561Sthompsastatic void 1033168561Sthompsalacp_select(struct lacp_port *lp) 1034168561Sthompsa{ 1035168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1036168561Sthompsa struct lacp_aggregator *la; 1037168561Sthompsa#if defined(LACP_DEBUG) 1038168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1039168561Sthompsa#endif 1040168561Sthompsa 1041168561Sthompsa if (lp->lp_aggregator) { 1042168561Sthompsa return; 1043168561Sthompsa } 1044168561Sthompsa 1045168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1046168561Sthompsa ("timer_wait_while still active")); 1047168561Sthompsa 1048168561Sthompsa LACP_DPRINTF((lp, "port lagid=%s\n", 1049168561Sthompsa lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1050168561Sthompsa buf, sizeof(buf)))); 1051168561Sthompsa 1052168561Sthompsa TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1053168561Sthompsa if (lacp_aggregator_is_compatible(la, lp)) { 1054168561Sthompsa break; 1055168561Sthompsa } 1056168561Sthompsa } 1057168561Sthompsa 1058168561Sthompsa if (la == NULL) { 1059168561Sthompsa la = lacp_aggregator_get(lsc, lp); 1060168561Sthompsa if (la == NULL) { 1061168561Sthompsa LACP_DPRINTF((lp, "aggregator creation failed\n")); 1062168561Sthompsa 1063168561Sthompsa /* 1064168561Sthompsa * will retry on the next tick. 1065168561Sthompsa */ 1066168561Sthompsa 1067168561Sthompsa return; 1068168561Sthompsa } 1069168561Sthompsa lacp_fill_aggregator_id(la, lp); 1070168561Sthompsa LACP_DPRINTF((lp, "aggregator created\n")); 1071168561Sthompsa } else { 1072168561Sthompsa LACP_DPRINTF((lp, "compatible aggregator found\n")); 1073168561Sthompsa lacp_aggregator_addref(lsc, la); 1074168561Sthompsa } 1075168561Sthompsa 1076168561Sthompsa LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1077168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1078168561Sthompsa buf, sizeof(buf)))); 1079168561Sthompsa 1080168561Sthompsa lp->lp_aggregator = la; 1081168561Sthompsa lp->lp_selected = LACP_SELECTED; 1082168561Sthompsa} 1083168561Sthompsa 1084168561Sthompsa/* 1085168561Sthompsa * lacp_unselect: finish unselect/detach process. 1086168561Sthompsa */ 1087168561Sthompsa 1088168561Sthompsastatic void 1089168561Sthompsalacp_unselect(struct lacp_port *lp) 1090168561Sthompsa{ 1091168561Sthompsa struct lacp_softc *lsc = lp->lp_lsc; 1092168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1093168561Sthompsa 1094168561Sthompsa KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1095168561Sthompsa ("timer_wait_while still active")); 1096168561Sthompsa 1097168561Sthompsa if (la == NULL) { 1098168561Sthompsa return; 1099168561Sthompsa } 1100168561Sthompsa 1101168561Sthompsa lp->lp_aggregator = NULL; 1102168561Sthompsa lacp_aggregator_delref(lsc, la); 1103168561Sthompsa} 1104168561Sthompsa 1105168561Sthompsa/* mux machine */ 1106168561Sthompsa 1107168561Sthompsastatic void 1108168561Sthompsalacp_sm_mux(struct lacp_port *lp) 1109168561Sthompsa{ 1110168561Sthompsa enum lacp_mux_state new_state; 1111168561Sthompsa boolean_t p_sync = 1112168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1113168561Sthompsa boolean_t p_collecting = 1114168561Sthompsa (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1115168561Sthompsa enum lacp_selected selected = lp->lp_selected; 1116168561Sthompsa struct lacp_aggregator *la; 1117168561Sthompsa 1118168561Sthompsa /* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */ 1119168561Sthompsa 1120168561Sthompsare_eval: 1121168561Sthompsa la = lp->lp_aggregator; 1122168561Sthompsa KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1123168561Sthompsa ("MUX not detached")); 1124168561Sthompsa new_state = lp->lp_mux_state; 1125168561Sthompsa switch (lp->lp_mux_state) { 1126168561Sthompsa case LACP_MUX_DETACHED: 1127168561Sthompsa if (selected != LACP_UNSELECTED) { 1128168561Sthompsa new_state = LACP_MUX_WAITING; 1129168561Sthompsa } 1130168561Sthompsa break; 1131168561Sthompsa case LACP_MUX_WAITING: 1132168561Sthompsa KASSERT(la->la_pending > 0 || 1133168561Sthompsa !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1134168561Sthompsa ("timer_wait_while still active")); 1135168561Sthompsa if (selected == LACP_SELECTED && la->la_pending == 0) { 1136168561Sthompsa new_state = LACP_MUX_ATTACHED; 1137168561Sthompsa } else if (selected == LACP_UNSELECTED) { 1138168561Sthompsa new_state = LACP_MUX_DETACHED; 1139168561Sthompsa } 1140168561Sthompsa break; 1141168561Sthompsa case LACP_MUX_ATTACHED: 1142168561Sthompsa if (selected == LACP_SELECTED && p_sync) { 1143168561Sthompsa new_state = LACP_MUX_COLLECTING; 1144168561Sthompsa } else if (selected != LACP_SELECTED) { 1145168561Sthompsa new_state = LACP_MUX_DETACHED; 1146168561Sthompsa } 1147168561Sthompsa break; 1148168561Sthompsa case LACP_MUX_COLLECTING: 1149168561Sthompsa if (selected == LACP_SELECTED && p_sync && p_collecting) { 1150168561Sthompsa new_state = LACP_MUX_DISTRIBUTING; 1151168561Sthompsa } else if (selected != LACP_SELECTED || !p_sync) { 1152168561Sthompsa new_state = LACP_MUX_ATTACHED; 1153168561Sthompsa } 1154168561Sthompsa break; 1155168561Sthompsa case LACP_MUX_DISTRIBUTING: 1156168561Sthompsa if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1157168561Sthompsa new_state = LACP_MUX_COLLECTING; 1158168561Sthompsa } 1159168561Sthompsa break; 1160168561Sthompsa default: 1161168561Sthompsa panic("%s: unknown state", __func__); 1162168561Sthompsa } 1163168561Sthompsa 1164168561Sthompsa if (lp->lp_mux_state == new_state) { 1165168561Sthompsa return; 1166168561Sthompsa } 1167168561Sthompsa 1168168561Sthompsa lacp_set_mux(lp, new_state); 1169168561Sthompsa goto re_eval; 1170168561Sthompsa} 1171168561Sthompsa 1172168561Sthompsastatic void 1173168561Sthompsalacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1174168561Sthompsa{ 1175168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1176168561Sthompsa 1177168561Sthompsa if (lp->lp_mux_state == new_state) { 1178168561Sthompsa return; 1179168561Sthompsa } 1180168561Sthompsa 1181168561Sthompsa switch (new_state) { 1182168561Sthompsa case LACP_MUX_DETACHED: 1183168561Sthompsa lp->lp_state &= ~LACP_STATE_SYNC; 1184168561Sthompsa lacp_disable_distributing(lp); 1185168561Sthompsa lacp_disable_collecting(lp); 1186168561Sthompsa lacp_sm_assert_ntt(lp); 1187168561Sthompsa /* cancel timer */ 1188168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1189168561Sthompsa KASSERT(la->la_pending > 0, 1190168561Sthompsa ("timer_wait_while not active")); 1191168561Sthompsa la->la_pending--; 1192168561Sthompsa } 1193168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1194168561Sthompsa lacp_unselect(lp); 1195168561Sthompsa break; 1196168561Sthompsa case LACP_MUX_WAITING: 1197168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1198168561Sthompsa LACP_AGGREGATE_WAIT_TIME); 1199168561Sthompsa la->la_pending++; 1200168561Sthompsa break; 1201168561Sthompsa case LACP_MUX_ATTACHED: 1202168561Sthompsa lp->lp_state |= LACP_STATE_SYNC; 1203168561Sthompsa lacp_disable_collecting(lp); 1204168561Sthompsa lacp_sm_assert_ntt(lp); 1205168561Sthompsa break; 1206168561Sthompsa case LACP_MUX_COLLECTING: 1207168561Sthompsa lacp_enable_collecting(lp); 1208168561Sthompsa lacp_disable_distributing(lp); 1209168561Sthompsa lacp_sm_assert_ntt(lp); 1210168561Sthompsa break; 1211168561Sthompsa case LACP_MUX_DISTRIBUTING: 1212168561Sthompsa lacp_enable_distributing(lp); 1213168561Sthompsa break; 1214168561Sthompsa default: 1215168561Sthompsa panic("%s: unknown state", __func__); 1216168561Sthompsa } 1217168561Sthompsa 1218168561Sthompsa LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1219168561Sthompsa 1220168561Sthompsa lp->lp_mux_state = new_state; 1221168561Sthompsa} 1222168561Sthompsa 1223168561Sthompsastatic void 1224168561Sthompsalacp_sm_mux_timer(struct lacp_port *lp) 1225168561Sthompsa{ 1226168561Sthompsa struct lacp_aggregator *la = lp->lp_aggregator; 1227168561Sthompsa#if defined(LACP_DEBUG) 1228168561Sthompsa char buf[LACP_LAGIDSTR_MAX+1]; 1229168561Sthompsa#endif 1230168561Sthompsa 1231168561Sthompsa KASSERT(la->la_pending > 0, ("no pending event")); 1232168561Sthompsa 1233168561Sthompsa LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1234168561Sthompsa lacp_format_lagid(&la->la_actor, &la->la_partner, 1235168561Sthompsa buf, sizeof(buf)), 1236168561Sthompsa la->la_pending, la->la_pending - 1)); 1237168561Sthompsa 1238168561Sthompsa la->la_pending--; 1239168561Sthompsa} 1240168561Sthompsa 1241168561Sthompsa/* periodic transmit machine */ 1242168561Sthompsa 1243168561Sthompsastatic void 1244168561Sthompsalacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1245168561Sthompsa{ 1246168561Sthompsa if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1247168561Sthompsa LACP_STATE_TIMEOUT)) { 1248168561Sthompsa return; 1249168561Sthompsa } 1250168561Sthompsa 1251168561Sthompsa LACP_DPRINTF((lp, "partner timeout changed\n")); 1252168561Sthompsa 1253168561Sthompsa /* 1254168561Sthompsa * FAST_PERIODIC -> SLOW_PERIODIC 1255168561Sthompsa * or 1256168561Sthompsa * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1257168561Sthompsa * 1258168561Sthompsa * let lacp_sm_ptx_tx_schedule to update timeout. 1259168561Sthompsa */ 1260168561Sthompsa 1261168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1262168561Sthompsa 1263168561Sthompsa /* 1264168561Sthompsa * if timeout has been shortened, assert NTT. 1265168561Sthompsa */ 1266168561Sthompsa 1267168561Sthompsa if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1268168561Sthompsa lacp_sm_assert_ntt(lp); 1269168561Sthompsa } 1270168561Sthompsa} 1271168561Sthompsa 1272168561Sthompsastatic void 1273168561Sthompsalacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1274168561Sthompsa{ 1275168561Sthompsa int timeout; 1276168561Sthompsa 1277168561Sthompsa if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1278168561Sthompsa !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1279168561Sthompsa 1280168561Sthompsa /* 1281168561Sthompsa * NO_PERIODIC 1282168561Sthompsa */ 1283168561Sthompsa 1284168561Sthompsa LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1285168561Sthompsa return; 1286168561Sthompsa } 1287168561Sthompsa 1288168561Sthompsa if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1289168561Sthompsa return; 1290168561Sthompsa } 1291168561Sthompsa 1292168561Sthompsa timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1293168561Sthompsa LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1294168561Sthompsa 1295168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1296168561Sthompsa} 1297168561Sthompsa 1298168561Sthompsastatic void 1299168561Sthompsalacp_sm_ptx_timer(struct lacp_port *lp) 1300168561Sthompsa{ 1301168561Sthompsa lacp_sm_assert_ntt(lp); 1302168561Sthompsa} 1303168561Sthompsa 1304168561Sthompsastatic void 1305168561Sthompsalacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1306168561Sthompsa{ 1307168561Sthompsa int timeout; 1308168561Sthompsa 1309168561Sthompsa /* 1310168561Sthompsa * check LACP_DISABLED first 1311168561Sthompsa */ 1312168561Sthompsa 1313168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1314168561Sthompsa return; 1315168561Sthompsa } 1316168561Sthompsa 1317168561Sthompsa /* 1318168561Sthompsa * check loopback condition. 1319168561Sthompsa */ 1320168561Sthompsa 1321168561Sthompsa if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1322168561Sthompsa &lp->lp_actor.lip_systemid)) { 1323168561Sthompsa return; 1324168561Sthompsa } 1325168561Sthompsa 1326168561Sthompsa /* 1327168561Sthompsa * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1328168561Sthompsa */ 1329168561Sthompsa 1330168561Sthompsa lacp_sm_rx_update_selected(lp, du); 1331168561Sthompsa lacp_sm_rx_update_ntt(lp, du); 1332168561Sthompsa lacp_sm_rx_record_pdu(lp, du); 1333168561Sthompsa 1334168561Sthompsa timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1335168561Sthompsa LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1336168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1337168561Sthompsa 1338168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1339168561Sthompsa 1340168561Sthompsa /* 1341168561Sthompsa * kick transmit machine without waiting the next tick. 1342168561Sthompsa */ 1343168561Sthompsa 1344168561Sthompsa lacp_sm_tx(lp); 1345168561Sthompsa} 1346168561Sthompsa 1347168561Sthompsastatic void 1348168561Sthompsalacp_sm_rx_set_expired(struct lacp_port *lp) 1349168561Sthompsa{ 1350168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1351168561Sthompsa lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1352168561Sthompsa LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1353168561Sthompsa lp->lp_state |= LACP_STATE_EXPIRED; 1354168561Sthompsa} 1355168561Sthompsa 1356168561Sthompsastatic void 1357168561Sthompsalacp_sm_rx_timer(struct lacp_port *lp) 1358168561Sthompsa{ 1359168561Sthompsa if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1360168561Sthompsa /* CURRENT -> EXPIRED */ 1361168561Sthompsa LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1362168561Sthompsa lacp_sm_rx_set_expired(lp); 1363168561Sthompsa } else { 1364168561Sthompsa /* EXPIRED -> DEFAULTED */ 1365168561Sthompsa LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1366168561Sthompsa lacp_sm_rx_update_default_selected(lp); 1367168561Sthompsa lacp_sm_rx_record_default(lp); 1368168561Sthompsa lp->lp_state &= ~LACP_STATE_EXPIRED; 1369168561Sthompsa } 1370168561Sthompsa} 1371168561Sthompsa 1372168561Sthompsastatic void 1373168561Sthompsalacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1374168561Sthompsa{ 1375168561Sthompsa boolean_t active; 1376168561Sthompsa uint8_t oldpstate; 1377168561Sthompsa#if defined(LACP_DEBUG) 1378168561Sthompsa char buf[LACP_STATESTR_MAX+1]; 1379168561Sthompsa#endif 1380168561Sthompsa 1381168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1382168561Sthompsa 1383168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1384168561Sthompsa 1385168561Sthompsa active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1386168561Sthompsa || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1387168561Sthompsa (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1388168561Sthompsa 1389168561Sthompsa lp->lp_partner = du->ldu_actor; 1390168561Sthompsa if (active && 1391168561Sthompsa ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1392168561Sthompsa LACP_STATE_AGGREGATION) && 1393168561Sthompsa !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1394168561Sthompsa || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1395168561Sthompsa /* XXX nothing? */ 1396168561Sthompsa } else { 1397168561Sthompsa lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1398168561Sthompsa } 1399168561Sthompsa 1400168561Sthompsa lp->lp_state &= ~LACP_STATE_DEFAULTED; 1401168561Sthompsa 1402168561Sthompsa if (oldpstate != lp->lp_partner.lip_state) { 1403168561Sthompsa LACP_DPRINTF((lp, "old pstate %s\n", 1404168561Sthompsa lacp_format_state(oldpstate, buf, sizeof(buf)))); 1405168561Sthompsa LACP_DPRINTF((lp, "new pstate %s\n", 1406168561Sthompsa lacp_format_state(lp->lp_partner.lip_state, buf, 1407168561Sthompsa sizeof(buf)))); 1408168561Sthompsa } 1409168561Sthompsa 1410168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1411168561Sthompsa} 1412168561Sthompsa 1413168561Sthompsastatic void 1414168561Sthompsalacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1415168561Sthompsa{ 1416168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1417168561Sthompsa 1418168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1419168561Sthompsa !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1420168561Sthompsa LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1421168561Sthompsa LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1422168561Sthompsa lacp_sm_assert_ntt(lp); 1423168561Sthompsa } 1424168561Sthompsa} 1425168561Sthompsa 1426168561Sthompsastatic void 1427168561Sthompsalacp_sm_rx_record_default(struct lacp_port *lp) 1428168561Sthompsa{ 1429168561Sthompsa uint8_t oldpstate; 1430168561Sthompsa 1431168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1432168561Sthompsa 1433168561Sthompsa oldpstate = lp->lp_partner.lip_state; 1434168561Sthompsa lp->lp_partner = lacp_partner_admin; 1435168561Sthompsa lp->lp_state |= LACP_STATE_DEFAULTED; 1436168561Sthompsa lacp_sm_ptx_update_timeout(lp, oldpstate); 1437168561Sthompsa} 1438168561Sthompsa 1439168561Sthompsastatic void 1440168561Sthompsalacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1441168561Sthompsa const struct lacp_peerinfo *info) 1442168561Sthompsa{ 1443168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1444168561Sthompsa 1445168561Sthompsa if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1446168561Sthompsa !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1447168561Sthompsa LACP_STATE_AGGREGATION)) { 1448168561Sthompsa lp->lp_selected = LACP_UNSELECTED; 1449168561Sthompsa /* mux machine will clean up lp->lp_aggregator */ 1450168561Sthompsa } 1451168561Sthompsa} 1452168561Sthompsa 1453168561Sthompsastatic void 1454168561Sthompsalacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1455168561Sthompsa{ 1456168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1457168561Sthompsa 1458168561Sthompsa lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1459168561Sthompsa} 1460168561Sthompsa 1461168561Sthompsastatic void 1462168561Sthompsalacp_sm_rx_update_default_selected(struct lacp_port *lp) 1463168561Sthompsa{ 1464168561Sthompsa /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1465168561Sthompsa 1466168561Sthompsa lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin); 1467168561Sthompsa} 1468168561Sthompsa 1469168561Sthompsa/* transmit machine */ 1470168561Sthompsa 1471168561Sthompsastatic void 1472168561Sthompsalacp_sm_tx(struct lacp_port *lp) 1473168561Sthompsa{ 1474168561Sthompsa int error; 1475168561Sthompsa 1476168561Sthompsa if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1477168561Sthompsa#if 1 1478168561Sthompsa || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1479168561Sthompsa && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1480168561Sthompsa#endif 1481168561Sthompsa ) { 1482168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1483168561Sthompsa } 1484168561Sthompsa 1485168561Sthompsa if (!(lp->lp_flags & LACP_PORT_NTT)) { 1486168561Sthompsa return; 1487168561Sthompsa } 1488168561Sthompsa 1489168561Sthompsa /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1490168561Sthompsa if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1491168561Sthompsa (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1492168561Sthompsa LACP_DPRINTF((lp, "rate limited pdu\n")); 1493168561Sthompsa return; 1494168561Sthompsa } 1495168561Sthompsa 1496168561Sthompsa error = lacp_xmit_lacpdu(lp); 1497168561Sthompsa 1498168561Sthompsa if (error == 0) { 1499168561Sthompsa lp->lp_flags &= ~LACP_PORT_NTT; 1500168561Sthompsa } else { 1501168561Sthompsa LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1502168561Sthompsa error)); 1503168561Sthompsa } 1504168561Sthompsa} 1505168561Sthompsa 1506168561Sthompsastatic void 1507168561Sthompsalacp_sm_assert_ntt(struct lacp_port *lp) 1508168561Sthompsa{ 1509168561Sthompsa 1510168561Sthompsa lp->lp_flags |= LACP_PORT_NTT; 1511168561Sthompsa} 1512168561Sthompsa 1513168561Sthompsastatic void 1514168561Sthompsalacp_run_timers(struct lacp_port *lp) 1515168561Sthompsa{ 1516168561Sthompsa int i; 1517168561Sthompsa 1518168561Sthompsa for (i = 0; i < LACP_NTIMER; i++) { 1519168561Sthompsa KASSERT(lp->lp_timer[i] >= 0, 1520168561Sthompsa ("invalid timer value %d", lp->lp_timer[i])); 1521168561Sthompsa if (lp->lp_timer[i] == 0) { 1522168561Sthompsa continue; 1523168561Sthompsa } else if (--lp->lp_timer[i] <= 0) { 1524168561Sthompsa if (lacp_timer_funcs[i]) { 1525168561Sthompsa (*lacp_timer_funcs[i])(lp); 1526168561Sthompsa } 1527168561Sthompsa } 1528168561Sthompsa } 1529168561Sthompsa} 1530168561Sthompsa 1531168561Sthompsaint 1532168793Sthompsalacp_marker_input(struct lagg_port *lgp, struct mbuf *m) 1533168561Sthompsa{ 1534168793Sthompsa struct lacp_port *lp = LACP_PORT(lgp); 1535168561Sthompsa struct markerdu *mdu; 1536168561Sthompsa int error = 0; 1537168561Sthompsa 1538168793Sthompsa LAGG_LOCK_ASSERT(lgp->lp_lagg); 1539168561Sthompsa 1540168561Sthompsa if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 1541168561Sthompsa goto bad; 1542168561Sthompsa } 1543168561Sthompsa 1544168561Sthompsa if (m->m_pkthdr.len != sizeof(*mdu)) { 1545168561Sthompsa goto bad; 1546168561Sthompsa } 1547168561Sthompsa 1548168561Sthompsa if ((m->m_flags & M_MCAST) == 0) { 1549168561Sthompsa goto bad; 1550168561Sthompsa } 1551168561Sthompsa 1552168561Sthompsa if (m->m_len < sizeof(*mdu)) { 1553168561Sthompsa m = m_pullup(m, sizeof(*mdu)); 1554168561Sthompsa if (m == NULL) { 1555168561Sthompsa return (ENOMEM); 1556168561Sthompsa } 1557168561Sthompsa } 1558168561Sthompsa 1559168561Sthompsa mdu = mtod(m, struct markerdu *); 1560168561Sthompsa 1561168561Sthompsa if (memcmp(&mdu->mdu_eh.ether_dhost, 1562168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1563168561Sthompsa goto bad; 1564168561Sthompsa } 1565168561Sthompsa 1566168561Sthompsa /* XXX 1567168561Sthompsa KASSERT(mdu->mdu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_MARKER, 1568168561Sthompsa ("a very bad kassert!")); 1569168561Sthompsa */ 1570168561Sthompsa 1571168561Sthompsa if (mdu->mdu_sph.sph_version != 1) { 1572168561Sthompsa goto bad; 1573168561Sthompsa } 1574168561Sthompsa 1575168561Sthompsa switch (mdu->mdu_tlv.tlv_type) { 1576168561Sthompsa case MARKER_TYPE_INFO: 1577168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1578168561Sthompsa marker_info_tlv_template, TRUE)) { 1579168561Sthompsa goto bad; 1580168561Sthompsa } 1581168561Sthompsa mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1582168561Sthompsa memcpy(&mdu->mdu_eh.ether_dhost, 1583168561Sthompsa ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1584168561Sthompsa memcpy(&mdu->mdu_eh.ether_shost, 1585168793Sthompsa lgp->lp_lladdr, ETHER_ADDR_LEN); 1586168793Sthompsa error = lagg_enqueue(lp->lp_ifp, m); 1587168561Sthompsa break; 1588168561Sthompsa 1589168561Sthompsa case MARKER_TYPE_RESPONSE: 1590168561Sthompsa if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1591168561Sthompsa marker_response_tlv_template, TRUE)) { 1592168561Sthompsa goto bad; 1593168561Sthompsa } 1594168561Sthompsa /* 1595168561Sthompsa * we are not interested in responses as 1596168561Sthompsa * we don't have a marker sender. 1597168561Sthompsa */ 1598168561Sthompsa /* FALLTHROUGH */ 1599168561Sthompsa default: 1600168561Sthompsa goto bad; 1601168561Sthompsa } 1602168561Sthompsa 1603168561Sthompsa return (error); 1604168561Sthompsa 1605168561Sthompsabad: 1606168561Sthompsa m_freem(m); 1607168561Sthompsa return (EINVAL); 1608168561Sthompsa} 1609168561Sthompsa 1610168561Sthompsastatic int 1611168561Sthompsatlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1612168561Sthompsa const struct tlv_template *tmpl, boolean_t check_type) 1613168561Sthompsa{ 1614168561Sthompsa while (/* CONSTCOND */ 1) { 1615168561Sthompsa if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1616168561Sthompsa return (EINVAL); 1617168561Sthompsa } 1618168561Sthompsa if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1619168561Sthompsa tlv->tlv_length != tmpl->tmpl_length) { 1620168561Sthompsa return (EINVAL); 1621168561Sthompsa } 1622168561Sthompsa if (tmpl->tmpl_type == 0) { 1623168561Sthompsa break; 1624168561Sthompsa } 1625168561Sthompsa tlv = (const struct tlvhdr *) 1626168561Sthompsa ((const char *)tlv + tlv->tlv_length); 1627168561Sthompsa tmpl++; 1628168561Sthompsa } 1629168561Sthompsa 1630168561Sthompsa return (0); 1631168561Sthompsa} 1632168561Sthompsa 1633168561Sthompsa#if defined(LACP_DEBUG) 1634168561Sthompsaconst char * 1635168561Sthompsalacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1636168561Sthompsa{ 1637168561Sthompsa snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1638168561Sthompsa (int)mac[0], 1639168561Sthompsa (int)mac[1], 1640168561Sthompsa (int)mac[2], 1641168561Sthompsa (int)mac[3], 1642168561Sthompsa (int)mac[4], 1643168561Sthompsa (int)mac[5]); 1644168561Sthompsa 1645168561Sthompsa return (buf); 1646168561Sthompsa} 1647168561Sthompsa 1648168561Sthompsaconst char * 1649168561Sthompsalacp_format_systemid(const struct lacp_systemid *sysid, 1650168561Sthompsa char *buf, size_t buflen) 1651168561Sthompsa{ 1652168561Sthompsa char macbuf[LACP_MACSTR_MAX+1]; 1653168561Sthompsa 1654168561Sthompsa snprintf(buf, buflen, "%04X,%s", 1655168561Sthompsa ntohs(sysid->lsi_prio), 1656168561Sthompsa lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1657168561Sthompsa 1658168561Sthompsa return (buf); 1659168561Sthompsa} 1660168561Sthompsa 1661168561Sthompsaconst char * 1662168561Sthompsalacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1663168561Sthompsa{ 1664168561Sthompsa snprintf(buf, buflen, "%04X,%04X", 1665168561Sthompsa ntohs(portid->lpi_prio), 1666168561Sthompsa ntohs(portid->lpi_portno)); 1667168561Sthompsa 1668168561Sthompsa return (buf); 1669168561Sthompsa} 1670168561Sthompsa 1671168561Sthompsaconst char * 1672168561Sthompsalacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1673168561Sthompsa{ 1674168561Sthompsa char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1675168561Sthompsa char portid[LACP_PORTIDSTR_MAX+1]; 1676168561Sthompsa 1677168561Sthompsa snprintf(buf, buflen, "(%s,%04X,%s)", 1678168561Sthompsa lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1679168561Sthompsa ntohs(peer->lip_key), 1680168561Sthompsa lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1681168561Sthompsa 1682168561Sthompsa return (buf); 1683168561Sthompsa} 1684168561Sthompsa 1685168561Sthompsaconst char * 1686168561Sthompsalacp_format_lagid(const struct lacp_peerinfo *a, 1687168561Sthompsa const struct lacp_peerinfo *b, char *buf, size_t buflen) 1688168561Sthompsa{ 1689168561Sthompsa char astr[LACP_PARTNERSTR_MAX+1]; 1690168561Sthompsa char bstr[LACP_PARTNERSTR_MAX+1]; 1691168561Sthompsa 1692168561Sthompsa#if 0 1693168561Sthompsa /* 1694168561Sthompsa * there's a convention to display small numbered peer 1695168561Sthompsa * in the left. 1696168561Sthompsa */ 1697168561Sthompsa 1698168561Sthompsa if (lacp_compare_peerinfo(a, b) > 0) { 1699168561Sthompsa const struct lacp_peerinfo *t; 1700168561Sthompsa 1701168561Sthompsa t = a; 1702168561Sthompsa a = b; 1703168561Sthompsa b = t; 1704168561Sthompsa } 1705168561Sthompsa#endif 1706168561Sthompsa 1707168561Sthompsa snprintf(buf, buflen, "[%s,%s]", 1708168561Sthompsa lacp_format_partner(a, astr, sizeof(astr)), 1709168561Sthompsa lacp_format_partner(b, bstr, sizeof(bstr))); 1710168561Sthompsa 1711168561Sthompsa return (buf); 1712168561Sthompsa} 1713168561Sthompsa 1714168561Sthompsaconst char * 1715168561Sthompsalacp_format_lagid_aggregator(const struct lacp_aggregator *la, 1716168561Sthompsa char *buf, size_t buflen) 1717168561Sthompsa{ 1718168561Sthompsa if (la == NULL) { 1719168561Sthompsa return ("(none)"); 1720168561Sthompsa } 1721168561Sthompsa 1722168561Sthompsa return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 1723168561Sthompsa} 1724168561Sthompsa 1725168561Sthompsaconst char * 1726168561Sthompsalacp_format_state(uint8_t state, char *buf, size_t buflen) 1727168561Sthompsa{ 1728168561Sthompsa snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 1729168561Sthompsa return (buf); 1730168561Sthompsa} 1731168561Sthompsa 1732168561Sthompsastatic void 1733168561Sthompsalacp_dump_lacpdu(const struct lacpdu *du) 1734168561Sthompsa{ 1735168561Sthompsa char buf[LACP_PARTNERSTR_MAX+1]; 1736168561Sthompsa char buf2[LACP_STATESTR_MAX+1]; 1737168561Sthompsa 1738168561Sthompsa printf("actor=%s\n", 1739168561Sthompsa lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 1740168561Sthompsa printf("actor.state=%s\n", 1741168561Sthompsa lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 1742168561Sthompsa printf("partner=%s\n", 1743168561Sthompsa lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 1744168561Sthompsa printf("partner.state=%s\n", 1745168561Sthompsa lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 1746168561Sthompsa 1747168561Sthompsa printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 1748168561Sthompsa} 1749168561Sthompsa 1750168561Sthompsastatic void 1751168561Sthompsalacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 1752168561Sthompsa{ 1753168561Sthompsa va_list va; 1754168561Sthompsa 1755168561Sthompsa if (lp) { 1756168561Sthompsa printf("%s: ", lp->lp_ifp->if_xname); 1757168561Sthompsa } 1758168561Sthompsa 1759168561Sthompsa va_start(va, fmt); 1760168561Sthompsa vprintf(fmt, va); 1761168561Sthompsa va_end(va); 1762168561Sthompsa} 1763168561Sthompsa#endif 1764