1b47888ceSAndrew Thompson /* $NetBSD: ieee8023ad_lacp.c,v 1.3 2005/12/11 12:24:54 christos Exp $ */ 2b47888ceSAndrew Thompson 3b47888ceSAndrew Thompson /*- 4b47888ceSAndrew Thompson * Copyright (c)2005 YAMAMOTO Takashi, 5b47888ceSAndrew Thompson * All rights reserved. 6b47888ceSAndrew Thompson * 7b47888ceSAndrew Thompson * Redistribution and use in source and binary forms, with or without 8b47888ceSAndrew Thompson * modification, are permitted provided that the following conditions 9b47888ceSAndrew Thompson * are met: 10b47888ceSAndrew Thompson * 1. Redistributions of source code must retain the above copyright 11b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer. 12b47888ceSAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 13b47888ceSAndrew Thompson * notice, this list of conditions and the following disclaimer in the 14b47888ceSAndrew Thompson * documentation and/or other materials provided with the distribution. 15b47888ceSAndrew Thompson * 16b47888ceSAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17b47888ceSAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18b47888ceSAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19b47888ceSAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20b47888ceSAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21b47888ceSAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22b47888ceSAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23b47888ceSAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24b47888ceSAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25b47888ceSAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26b47888ceSAndrew Thompson * SUCH DAMAGE. 27b47888ceSAndrew Thompson */ 28b47888ceSAndrew Thompson 29b47888ceSAndrew Thompson #include <sys/cdefs.h> 30b47888ceSAndrew Thompson __FBSDID("$FreeBSD$"); 31b47888ceSAndrew Thompson 32b47888ceSAndrew Thompson #include <sys/param.h> 33b47888ceSAndrew Thompson #include <sys/callout.h> 34b47888ceSAndrew Thompson #include <sys/mbuf.h> 35b47888ceSAndrew Thompson #include <sys/systm.h> 36b47888ceSAndrew Thompson #include <sys/malloc.h> 37b47888ceSAndrew Thompson #include <sys/kernel.h> /* hz */ 38b47888ceSAndrew Thompson #include <sys/socket.h> /* for net/if.h */ 39b47888ceSAndrew Thompson #include <sys/sockio.h> 40b47888ceSAndrew Thompson #include <machine/stdarg.h> 413bf517e3SAndrew Thompson #include <sys/lock.h> 423bf517e3SAndrew Thompson #include <sys/rwlock.h> 433bf517e3SAndrew Thompson #include <sys/taskqueue.h> 44b47888ceSAndrew Thompson 45b47888ceSAndrew Thompson #include <net/if.h> 46b47888ceSAndrew Thompson #include <net/if_dl.h> 47b47888ceSAndrew Thompson #include <net/ethernet.h> 48b47888ceSAndrew Thompson #include <net/if_media.h> 49b47888ceSAndrew Thompson #include <net/if_types.h> 50b47888ceSAndrew Thompson 5118242d3bSAndrew Thompson #include <net/if_lagg.h> 52b47888ceSAndrew Thompson #include <net/ieee8023ad_lacp.h> 53b47888ceSAndrew Thompson 54b47888ceSAndrew Thompson /* 55b47888ceSAndrew Thompson * actor system priority and port priority. 56b47888ceSAndrew Thompson * XXX should be configurable. 57b47888ceSAndrew Thompson */ 58b47888ceSAndrew Thompson 59b47888ceSAndrew Thompson #define LACP_SYSTEM_PRIO 0x8000 60b47888ceSAndrew Thompson #define LACP_PORT_PRIO 0x8000 61b47888ceSAndrew Thompson 62b47888ceSAndrew Thompson const uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 63b47888ceSAndrew Thompson { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 64b47888ceSAndrew Thompson 65b47888ceSAndrew Thompson static const struct tlv_template lacp_info_tlv_template[] = { 66b47888ceSAndrew Thompson { LACP_TYPE_ACTORINFO, 67b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 68b47888ceSAndrew Thompson { LACP_TYPE_PARTNERINFO, 69b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 70b47888ceSAndrew Thompson { LACP_TYPE_COLLECTORINFO, 71b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 72b47888ceSAndrew Thompson { 0, 0 }, 73b47888ceSAndrew Thompson }; 74b47888ceSAndrew Thompson 75b47888ceSAndrew Thompson typedef void (*lacp_timer_func_t)(struct lacp_port *); 76b47888ceSAndrew Thompson 77b47888ceSAndrew Thompson static const struct tlv_template marker_info_tlv_template[] = { 78998971a7SAndrew Thompson { MARKER_TYPE_INFO, 79998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 80b47888ceSAndrew Thompson { 0, 0 }, 81b47888ceSAndrew Thompson }; 82b47888ceSAndrew Thompson 83b47888ceSAndrew Thompson static const struct tlv_template marker_response_tlv_template[] = { 84998971a7SAndrew Thompson { MARKER_TYPE_RESPONSE, 85998971a7SAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_markerinfo) }, 86b47888ceSAndrew Thompson { 0, 0 }, 87b47888ceSAndrew Thompson }; 88b47888ceSAndrew Thompson 89b47888ceSAndrew Thompson static void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 90998971a7SAndrew Thompson static void lacp_fill_markerinfo(struct lacp_port *, 91998971a7SAndrew Thompson struct lacp_markerinfo *); 92b47888ceSAndrew Thompson 93b47888ceSAndrew Thompson static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 94b47888ceSAndrew Thompson static void lacp_suppress_distributing(struct lacp_softc *, 95b47888ceSAndrew Thompson struct lacp_aggregator *); 96b47888ceSAndrew Thompson static void lacp_transit_expire(void *); 97b47888ceSAndrew Thompson static void lacp_select_active_aggregator(struct lacp_softc *); 98b47888ceSAndrew Thompson static uint16_t lacp_compose_key(struct lacp_port *); 99b47888ceSAndrew Thompson static int tlv_check(const void *, size_t, const struct tlvhdr *, 100b47888ceSAndrew Thompson const struct tlv_template *, boolean_t); 101b47888ceSAndrew Thompson static void lacp_tick(void *); 102b47888ceSAndrew Thompson 103b47888ceSAndrew Thompson static void lacp_fill_aggregator_id(struct lacp_aggregator *, 104b47888ceSAndrew Thompson const struct lacp_port *); 105b47888ceSAndrew Thompson static void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 106b47888ceSAndrew Thompson const struct lacp_peerinfo *); 107b47888ceSAndrew Thompson static int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 108b47888ceSAndrew Thompson const struct lacp_port *); 109b47888ceSAndrew Thompson static int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 110b47888ceSAndrew Thompson const struct lacp_peerinfo *); 111b47888ceSAndrew Thompson 112b47888ceSAndrew Thompson static struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 113b47888ceSAndrew Thompson struct lacp_port *); 114b47888ceSAndrew Thompson static void lacp_aggregator_addref(struct lacp_softc *, 115b47888ceSAndrew Thompson struct lacp_aggregator *); 116b47888ceSAndrew Thompson static void lacp_aggregator_delref(struct lacp_softc *, 117b47888ceSAndrew Thompson struct lacp_aggregator *); 118b47888ceSAndrew Thompson 119b47888ceSAndrew Thompson /* receive machine */ 120b47888ceSAndrew Thompson 1213bf517e3SAndrew Thompson static void lacp_dequeue(void *, int); 1223bf517e3SAndrew Thompson static int lacp_pdu_input(struct lagg_port *, struct mbuf *); 1233bf517e3SAndrew Thompson static int lacp_marker_input(struct lagg_port *, struct mbuf *); 124b47888ceSAndrew Thompson static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 125b47888ceSAndrew Thompson static void lacp_sm_rx_timer(struct lacp_port *); 126b47888ceSAndrew Thompson static void lacp_sm_rx_set_expired(struct lacp_port *); 127b47888ceSAndrew Thompson static void lacp_sm_rx_update_ntt(struct lacp_port *, 128b47888ceSAndrew Thompson const struct lacpdu *); 129b47888ceSAndrew Thompson static void lacp_sm_rx_record_pdu(struct lacp_port *, 130b47888ceSAndrew Thompson const struct lacpdu *); 131b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected(struct lacp_port *, 132b47888ceSAndrew Thompson const struct lacpdu *); 133b47888ceSAndrew Thompson static void lacp_sm_rx_record_default(struct lacp_port *); 134b47888ceSAndrew Thompson static void lacp_sm_rx_update_default_selected(struct lacp_port *); 135b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 136b47888ceSAndrew Thompson const struct lacp_peerinfo *); 137b47888ceSAndrew Thompson 138b47888ceSAndrew Thompson /* mux machine */ 139b47888ceSAndrew Thompson 140b47888ceSAndrew Thompson static void lacp_sm_mux(struct lacp_port *); 141b47888ceSAndrew Thompson static void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 142b47888ceSAndrew Thompson static void lacp_sm_mux_timer(struct lacp_port *); 143b47888ceSAndrew Thompson 144b47888ceSAndrew Thompson /* periodic transmit machine */ 145b47888ceSAndrew Thompson 146b47888ceSAndrew Thompson static void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 147b47888ceSAndrew Thompson static void lacp_sm_ptx_tx_schedule(struct lacp_port *); 148b47888ceSAndrew Thompson static void lacp_sm_ptx_timer(struct lacp_port *); 149b47888ceSAndrew Thompson 150b47888ceSAndrew Thompson /* transmit machine */ 151b47888ceSAndrew Thompson 152b47888ceSAndrew Thompson static void lacp_sm_tx(struct lacp_port *); 153b47888ceSAndrew Thompson static void lacp_sm_assert_ntt(struct lacp_port *); 154b47888ceSAndrew Thompson 155b47888ceSAndrew Thompson static void lacp_run_timers(struct lacp_port *); 156b47888ceSAndrew Thompson static int lacp_compare_peerinfo(const struct lacp_peerinfo *, 157b47888ceSAndrew Thompson const struct lacp_peerinfo *); 158b47888ceSAndrew Thompson static int lacp_compare_systemid(const struct lacp_systemid *, 159b47888ceSAndrew Thompson const struct lacp_systemid *); 160b47888ceSAndrew Thompson static void lacp_port_enable(struct lacp_port *); 161b47888ceSAndrew Thompson static void lacp_port_disable(struct lacp_port *); 162b47888ceSAndrew Thompson static void lacp_select(struct lacp_port *); 163b47888ceSAndrew Thompson static void lacp_unselect(struct lacp_port *); 164b47888ceSAndrew Thompson static void lacp_disable_collecting(struct lacp_port *); 165b47888ceSAndrew Thompson static void lacp_enable_collecting(struct lacp_port *); 166b47888ceSAndrew Thompson static void lacp_disable_distributing(struct lacp_port *); 167b47888ceSAndrew Thompson static void lacp_enable_distributing(struct lacp_port *); 168b47888ceSAndrew Thompson static int lacp_xmit_lacpdu(struct lacp_port *); 169998971a7SAndrew Thompson static int lacp_xmit_marker(struct lacp_port *); 170b47888ceSAndrew Thompson 171b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 172b47888ceSAndrew Thompson static void lacp_dump_lacpdu(const struct lacpdu *); 173b47888ceSAndrew Thompson static const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 174b47888ceSAndrew Thompson size_t); 175b47888ceSAndrew Thompson static const char *lacp_format_lagid(const struct lacp_peerinfo *, 176b47888ceSAndrew Thompson const struct lacp_peerinfo *, char *, size_t); 177b47888ceSAndrew Thompson static const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 178b47888ceSAndrew Thompson char *, size_t); 179b47888ceSAndrew Thompson static const char *lacp_format_state(uint8_t, char *, size_t); 180b47888ceSAndrew Thompson static const char *lacp_format_mac(const uint8_t *, char *, size_t); 181b47888ceSAndrew Thompson static const char *lacp_format_systemid(const struct lacp_systemid *, char *, 182b47888ceSAndrew Thompson size_t); 183b47888ceSAndrew Thompson static const char *lacp_format_portid(const struct lacp_portid *, char *, 184b47888ceSAndrew Thompson size_t); 185b47888ceSAndrew Thompson static void lacp_dprintf(const struct lacp_port *, const char *, ...) 186b47888ceSAndrew Thompson __attribute__((__format__(__printf__, 2, 3))); 187b47888ceSAndrew Thompson #define LACP_DPRINTF(a) lacp_dprintf a 188b47888ceSAndrew Thompson #else 189b47888ceSAndrew Thompson #define LACP_DPRINTF(a) /* nothing */ 190b47888ceSAndrew Thompson #endif 191b47888ceSAndrew Thompson 192b47888ceSAndrew Thompson /* 193b47888ceSAndrew Thompson * partner administration variables. 194b47888ceSAndrew Thompson * XXX should be configurable. 195b47888ceSAndrew Thompson */ 196b47888ceSAndrew Thompson 197b47888ceSAndrew Thompson static const struct lacp_peerinfo lacp_partner_admin = { 198b47888ceSAndrew Thompson .lip_systemid = { .lsi_prio = 0xffff }, 199b47888ceSAndrew Thompson .lip_portid = { .lpi_prio = 0xffff }, 200b47888ceSAndrew Thompson #if 1 201b47888ceSAndrew Thompson /* optimistic */ 202b47888ceSAndrew Thompson .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 203b47888ceSAndrew Thompson LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 204b47888ceSAndrew Thompson #else 205b47888ceSAndrew Thompson /* pessimistic */ 206b47888ceSAndrew Thompson .lip_state = 0, 207b47888ceSAndrew Thompson #endif 208b47888ceSAndrew Thompson }; 209b47888ceSAndrew Thompson 210b47888ceSAndrew Thompson static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 211b47888ceSAndrew Thompson [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 212b47888ceSAndrew Thompson [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 213b47888ceSAndrew Thompson [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 214b47888ceSAndrew Thompson }; 215b47888ceSAndrew Thompson 2163bf517e3SAndrew Thompson void 21718242d3bSAndrew Thompson lacp_input(struct lagg_port *lgp, struct mbuf *m) 218b47888ceSAndrew Thompson { 2193bf517e3SAndrew Thompson struct lagg_softc *lgs = lgp->lp_lagg; 2203bf517e3SAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 2213bf517e3SAndrew Thompson uint8_t subtype; 2223bf517e3SAndrew Thompson 2233bf517e3SAndrew Thompson if (m->m_pkthdr.len < sizeof(struct ether_header) + sizeof(subtype)) { 2243bf517e3SAndrew Thompson m_freem(m); 2253bf517e3SAndrew Thompson return; 2263bf517e3SAndrew Thompson } 2273bf517e3SAndrew Thompson 2283bf517e3SAndrew Thompson m_copydata(m, sizeof(struct ether_header), sizeof(subtype), &subtype); 2293bf517e3SAndrew Thompson switch (subtype) { 2303bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_LACP: 2313bf517e3SAndrew Thompson IF_HANDOFF(&lsc->lsc_queue, m, NULL); 2323bf517e3SAndrew Thompson taskqueue_enqueue(taskqueue_swi, &lsc->lsc_qtask); 2333bf517e3SAndrew Thompson break; 2343bf517e3SAndrew Thompson 2353bf517e3SAndrew Thompson case SLOWPROTOCOLS_SUBTYPE_MARKER: 2363bf517e3SAndrew Thompson lacp_marker_input(lgp, m); 2373bf517e3SAndrew Thompson break; 2383bf517e3SAndrew Thompson 2393bf517e3SAndrew Thompson default: 2403bf517e3SAndrew Thompson /* Unknown LACP packet type */ 2413bf517e3SAndrew Thompson m_freem(m); 2423bf517e3SAndrew Thompson break; 2433bf517e3SAndrew Thompson } 2443bf517e3SAndrew Thompson } 2453bf517e3SAndrew Thompson 2463bf517e3SAndrew Thompson static void 2473bf517e3SAndrew Thompson lacp_dequeue(void *arg, int pending) 2483bf517e3SAndrew Thompson { 2493bf517e3SAndrew Thompson struct lacp_softc *lsc = (struct lacp_softc *)arg; 2503bf517e3SAndrew Thompson struct lagg_softc *sc = lsc->lsc_lagg; 2513bf517e3SAndrew Thompson struct lagg_port *lgp; 2523bf517e3SAndrew Thompson struct mbuf *m; 2533bf517e3SAndrew Thompson 2543bf517e3SAndrew Thompson LAGG_WLOCK(sc); 2553bf517e3SAndrew Thompson for (;;) { 2563bf517e3SAndrew Thompson IF_DEQUEUE(&lsc->lsc_queue, m); 2573bf517e3SAndrew Thompson if (m == NULL) 2583bf517e3SAndrew Thompson break; 2593bf517e3SAndrew Thompson lgp = m->m_pkthdr.rcvif->if_lagg; 2603bf517e3SAndrew Thompson lacp_pdu_input(lgp, m); 2613bf517e3SAndrew Thompson } 2623bf517e3SAndrew Thompson LAGG_WUNLOCK(sc); 2633bf517e3SAndrew Thompson } 2643bf517e3SAndrew Thompson 2653bf517e3SAndrew Thompson /* 2663bf517e3SAndrew Thompson * lacp_pdu_input: process lacpdu 2673bf517e3SAndrew Thompson */ 2683bf517e3SAndrew Thompson static int 2693bf517e3SAndrew Thompson lacp_pdu_input(struct lagg_port *lgp, struct mbuf *m) 2703bf517e3SAndrew Thompson { 27118242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 272b47888ceSAndrew Thompson struct lacpdu *du; 273b47888ceSAndrew Thompson int error = 0; 274b47888ceSAndrew Thompson 2753bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 276b47888ceSAndrew Thompson 277b47888ceSAndrew Thompson if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 278b47888ceSAndrew Thompson goto bad; 279b47888ceSAndrew Thompson } 280b47888ceSAndrew Thompson 281b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*du)) { 282b47888ceSAndrew Thompson goto bad; 283b47888ceSAndrew Thompson } 284b47888ceSAndrew Thompson 285b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 286b47888ceSAndrew Thompson goto bad; 287b47888ceSAndrew Thompson } 288b47888ceSAndrew Thompson 289b47888ceSAndrew Thompson if (m->m_len < sizeof(*du)) { 290b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*du)); 291b47888ceSAndrew Thompson if (m == NULL) { 292b47888ceSAndrew Thompson return (ENOMEM); 293b47888ceSAndrew Thompson } 294b47888ceSAndrew Thompson } 295b47888ceSAndrew Thompson 296b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 297b47888ceSAndrew Thompson 298b47888ceSAndrew Thompson if (memcmp(&du->ldu_eh.ether_dhost, 299b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 300b47888ceSAndrew Thompson goto bad; 301b47888ceSAndrew Thompson } 302b47888ceSAndrew Thompson 303b47888ceSAndrew Thompson /* XXX 304b47888ceSAndrew Thompson KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP, 305b47888ceSAndrew Thompson ("a very bad kassert!")); 306b47888ceSAndrew Thompson */ 307b47888ceSAndrew Thompson 308b47888ceSAndrew Thompson /* 309b47888ceSAndrew Thompson * ignore the version for compatibility with 310b47888ceSAndrew Thompson * the future protocol revisions. 311b47888ceSAndrew Thompson */ 312b47888ceSAndrew Thompson 313b47888ceSAndrew Thompson #if 0 314b47888ceSAndrew Thompson if (du->ldu_sph.sph_version != 1) { 315b47888ceSAndrew Thompson goto bad; 316b47888ceSAndrew Thompson } 317b47888ceSAndrew Thompson #endif 318b47888ceSAndrew Thompson 319b47888ceSAndrew Thompson /* 320b47888ceSAndrew Thompson * ignore tlv types for compatibility with 321b47888ceSAndrew Thompson * the future protocol revisions. 322b47888ceSAndrew Thompson */ 323b47888ceSAndrew Thompson 324b47888ceSAndrew Thompson if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 325b47888ceSAndrew Thompson lacp_info_tlv_template, FALSE)) { 326b47888ceSAndrew Thompson goto bad; 327b47888ceSAndrew Thompson } 328b47888ceSAndrew Thompson 329b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 330b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu receive\n")); 331b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 332b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 333b47888ceSAndrew Thompson lacp_sm_rx(lp, du); 334b47888ceSAndrew Thompson 335b47888ceSAndrew Thompson m_freem(m); 336b47888ceSAndrew Thompson 337b47888ceSAndrew Thompson return (error); 338b47888ceSAndrew Thompson 339b47888ceSAndrew Thompson bad: 340b47888ceSAndrew Thompson m_freem(m); 341b47888ceSAndrew Thompson return (EINVAL); 342b47888ceSAndrew Thompson } 343b47888ceSAndrew Thompson 344b47888ceSAndrew Thompson static void 345b47888ceSAndrew Thompson lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 346b47888ceSAndrew Thompson { 34718242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 34818242d3bSAndrew Thompson struct lagg_softc *lgs = lgp->lp_lagg; 349b47888ceSAndrew Thompson 350b47888ceSAndrew Thompson info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 351b47888ceSAndrew Thompson memcpy(&info->lip_systemid.lsi_mac, 35218242d3bSAndrew Thompson IF_LLADDR(lgs->sc_ifp), ETHER_ADDR_LEN); 353b47888ceSAndrew Thompson info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 354b47888ceSAndrew Thompson info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 355b47888ceSAndrew Thompson info->lip_state = lp->lp_state; 356b47888ceSAndrew Thompson } 357b47888ceSAndrew Thompson 358998971a7SAndrew Thompson static void 359998971a7SAndrew Thompson lacp_fill_markerinfo(struct lacp_port *lp, struct lacp_markerinfo *info) 360998971a7SAndrew Thompson { 361998971a7SAndrew Thompson struct ifnet *ifp = lp->lp_ifp; 362998971a7SAndrew Thompson 363998971a7SAndrew Thompson /* Fill in the port index and system id (encoded as the MAC) */ 364998971a7SAndrew Thompson info->mi_rq_port = htons(ifp->if_index); 365998971a7SAndrew Thompson memcpy(&info->mi_rq_system, lp->lp_systemid.lsi_mac, ETHER_ADDR_LEN); 366998971a7SAndrew Thompson info->mi_rq_xid = htonl(0); 367998971a7SAndrew Thompson } 368998971a7SAndrew Thompson 369b47888ceSAndrew Thompson static int 370b47888ceSAndrew Thompson lacp_xmit_lacpdu(struct lacp_port *lp) 371b47888ceSAndrew Thompson { 37218242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 373b47888ceSAndrew Thompson struct mbuf *m; 374b47888ceSAndrew Thompson struct lacpdu *du; 375b47888ceSAndrew Thompson int error; 376b47888ceSAndrew Thompson 3773bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 378b47888ceSAndrew Thompson 379b47888ceSAndrew Thompson m = m_gethdr(M_DONTWAIT, MT_DATA); 380b47888ceSAndrew Thompson if (m == NULL) { 381b47888ceSAndrew Thompson return (ENOMEM); 382b47888ceSAndrew Thompson } 383b47888ceSAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*du); 384b47888ceSAndrew Thompson 385b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 386b47888ceSAndrew Thompson memset(du, 0, sizeof(*du)); 387b47888ceSAndrew Thompson 388b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 389b47888ceSAndrew Thompson ETHER_ADDR_LEN); 39018242d3bSAndrew Thompson memcpy(&du->ldu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 391b47888ceSAndrew Thompson du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 392b47888ceSAndrew Thompson 393b47888ceSAndrew Thompson du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 394b47888ceSAndrew Thompson du->ldu_sph.sph_version = 1; 395b47888ceSAndrew Thompson 396b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 397b47888ceSAndrew Thompson du->ldu_actor = lp->lp_actor; 398b47888ceSAndrew Thompson 399b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 400b47888ceSAndrew Thompson sizeof(du->ldu_partner)); 401b47888ceSAndrew Thompson du->ldu_partner = lp->lp_partner; 402b47888ceSAndrew Thompson 403b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 404b47888ceSAndrew Thompson sizeof(du->ldu_collector)); 405b47888ceSAndrew Thompson du->ldu_collector.lci_maxdelay = 0; 406b47888ceSAndrew Thompson 407b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 408b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit\n")); 409b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 410b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 411b47888ceSAndrew Thompson 412b47888ceSAndrew Thompson m->m_flags |= M_MCAST; 413b47888ceSAndrew Thompson 414b47888ceSAndrew Thompson /* 415b47888ceSAndrew Thompson * XXX should use higher priority queue. 416b47888ceSAndrew Thompson * otherwise network congestion can break aggregation. 417b47888ceSAndrew Thompson */ 418b47888ceSAndrew Thompson 41918242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 420b47888ceSAndrew Thompson return (error); 421b47888ceSAndrew Thompson } 422b47888ceSAndrew Thompson 423998971a7SAndrew Thompson static int 424998971a7SAndrew Thompson lacp_xmit_marker(struct lacp_port *lp) 425998971a7SAndrew Thompson { 426998971a7SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 427998971a7SAndrew Thompson struct mbuf *m; 428998971a7SAndrew Thompson struct markerdu *mdu; 429998971a7SAndrew Thompson int error; 430998971a7SAndrew Thompson 431998971a7SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 432998971a7SAndrew Thompson 433998971a7SAndrew Thompson m = m_gethdr(M_DONTWAIT, MT_DATA); 434998971a7SAndrew Thompson if (m == NULL) { 435998971a7SAndrew Thompson return (ENOMEM); 436998971a7SAndrew Thompson } 437998971a7SAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*mdu); 438998971a7SAndrew Thompson 439998971a7SAndrew Thompson mdu = mtod(m, struct markerdu *); 440998971a7SAndrew Thompson memset(mdu, 0, sizeof(*mdu)); 441998971a7SAndrew Thompson 442998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 443998971a7SAndrew Thompson ETHER_ADDR_LEN); 444998971a7SAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, lgp->lp_lladdr, ETHER_ADDR_LEN); 445998971a7SAndrew Thompson mdu->mdu_eh.ether_type = htons(ETHERTYPE_SLOW); 446998971a7SAndrew Thompson 447998971a7SAndrew Thompson mdu->mdu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_MARKER; 448998971a7SAndrew Thompson mdu->mdu_sph.sph_version = 1; 449998971a7SAndrew Thompson 450998971a7SAndrew Thompson /* Bump the transaction id and copy over the marker info */ 451998971a7SAndrew Thompson lp->lp_marker.mi_rq_xid = htonl(ntohl(lp->lp_marker.mi_rq_xid) + 1); 452998971a7SAndrew Thompson TLV_SET(&mdu->mdu_tlv, MARKER_TYPE_INFO, sizeof(mdu->mdu_info)); 453998971a7SAndrew Thompson mdu->mdu_info = lp->lp_marker; 454998971a7SAndrew Thompson 455998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker transmit, port=%u, sys=%6D, id=%u\n", 456998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, ":", 457998971a7SAndrew Thompson ntohl(mdu->mdu_info.mi_rq_xid))); 458998971a7SAndrew Thompson 459998971a7SAndrew Thompson m->m_flags |= M_MCAST; 460998971a7SAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 461998971a7SAndrew Thompson return (error); 462998971a7SAndrew Thompson } 463b47888ceSAndrew Thompson void 46418242d3bSAndrew Thompson lacp_linkstate(struct lagg_port *lgp) 465b47888ceSAndrew Thompson { 46618242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 46718242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 468b47888ceSAndrew Thompson struct ifmediareq ifmr; 469b47888ceSAndrew Thompson int error = 0; 470b47888ceSAndrew Thompson u_int media; 471b47888ceSAndrew Thompson uint8_t old_state; 472b47888ceSAndrew Thompson uint16_t old_key; 473b47888ceSAndrew Thompson 4743bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 475b47888ceSAndrew Thompson 476b47888ceSAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr)); 477b47888ceSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 478b47888ceSAndrew Thompson if (error != 0) 479b47888ceSAndrew Thompson return; 480b47888ceSAndrew Thompson 481b47888ceSAndrew Thompson media = ifmr.ifm_active; 482e3163ef6SAndrew Thompson LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x, ether = %d, fdx = %d, " 483e3163ef6SAndrew Thompson "link = %d\n", lp->lp_media, media, IFM_TYPE(media) == IFM_ETHER, 484e3163ef6SAndrew Thompson (media & IFM_FDX) != 0, ifp->if_link_state == LINK_STATE_UP)); 485b47888ceSAndrew Thompson old_state = lp->lp_state; 486b47888ceSAndrew Thompson old_key = lp->lp_key; 487b47888ceSAndrew Thompson 488b47888ceSAndrew Thompson lp->lp_media = media; 489e3163ef6SAndrew Thompson /* 490e3163ef6SAndrew Thompson * If the port is not an active full duplex Ethernet link then it can 491e3163ef6SAndrew Thompson * not be aggregated. 492e3163ef6SAndrew Thompson */ 493e3163ef6SAndrew Thompson if (IFM_TYPE(media) != IFM_ETHER || (media & IFM_FDX) == 0 || 494e3163ef6SAndrew Thompson ifp->if_link_state != LINK_STATE_UP) { 495b47888ceSAndrew Thompson lacp_port_disable(lp); 496b47888ceSAndrew Thompson } else { 497b47888ceSAndrew Thompson lacp_port_enable(lp); 498b47888ceSAndrew Thompson } 499b47888ceSAndrew Thompson lp->lp_key = lacp_compose_key(lp); 500b47888ceSAndrew Thompson 501b47888ceSAndrew Thompson if (old_state != lp->lp_state || old_key != lp->lp_key) { 502b47888ceSAndrew Thompson LACP_DPRINTF((lp, "-> UNSELECTED\n")); 503b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 504b47888ceSAndrew Thompson } 505b47888ceSAndrew Thompson } 506b47888ceSAndrew Thompson 507b47888ceSAndrew Thompson static void 508b47888ceSAndrew Thompson lacp_tick(void *arg) 509b47888ceSAndrew Thompson { 510b47888ceSAndrew Thompson struct lacp_softc *lsc = arg; 5113bf517e3SAndrew Thompson struct lagg_softc *sc = lsc->lsc_lagg; 512b47888ceSAndrew Thompson struct lacp_port *lp; 513b47888ceSAndrew Thompson 5143bf517e3SAndrew Thompson LAGG_WLOCK(sc); 515b47888ceSAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 516b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 517b47888ceSAndrew Thompson continue; 518b47888ceSAndrew Thompson 519b47888ceSAndrew Thompson lacp_run_timers(lp); 520b47888ceSAndrew Thompson 521b47888ceSAndrew Thompson lacp_select(lp); 522b47888ceSAndrew Thompson lacp_sm_mux(lp); 523b47888ceSAndrew Thompson lacp_sm_tx(lp); 524b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(lp); 525b47888ceSAndrew Thompson } 5263bf517e3SAndrew Thompson LAGG_WUNLOCK(sc); 527b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 528b47888ceSAndrew Thompson } 529b47888ceSAndrew Thompson 530b47888ceSAndrew Thompson int 53118242d3bSAndrew Thompson lacp_port_create(struct lagg_port *lgp) 532b47888ceSAndrew Thompson { 53318242d3bSAndrew Thompson struct lagg_softc *lgs = lgp->lp_lagg; 53418242d3bSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 535b47888ceSAndrew Thompson struct lacp_port *lp; 53618242d3bSAndrew Thompson struct ifnet *ifp = lgp->lp_ifp; 537b47888ceSAndrew Thompson struct sockaddr_dl sdl; 538b47888ceSAndrew Thompson struct ifmultiaddr *rifma = NULL; 539b47888ceSAndrew Thompson int error; 540b47888ceSAndrew Thompson 541b47888ceSAndrew Thompson boolean_t active = TRUE; /* XXX should be configurable */ 542b47888ceSAndrew Thompson boolean_t fast = FALSE; /* XXX should be configurable */ 543b47888ceSAndrew Thompson 5443bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgs); 545b47888ceSAndrew Thompson 546b47888ceSAndrew Thompson bzero((char *)&sdl, sizeof(sdl)); 547b47888ceSAndrew Thompson sdl.sdl_len = sizeof(sdl); 548b47888ceSAndrew Thompson sdl.sdl_family = AF_LINK; 549b47888ceSAndrew Thompson sdl.sdl_index = ifp->if_index; 550b47888ceSAndrew Thompson sdl.sdl_type = IFT_ETHER; 551b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 552b47888ceSAndrew Thompson 553b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 554b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 555b47888ceSAndrew Thompson error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 556b47888ceSAndrew Thompson if (error) { 55718242d3bSAndrew Thompson printf("%s: ADDMULTI failed on %s\n", __func__, lgp->lp_ifname); 558b47888ceSAndrew Thompson return (error); 559b47888ceSAndrew Thompson } 560b47888ceSAndrew Thompson 561b47888ceSAndrew Thompson lp = malloc(sizeof(struct lacp_port), 562b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 563b47888ceSAndrew Thompson if (lp == NULL) 564b47888ceSAndrew Thompson return (ENOMEM); 565b47888ceSAndrew Thompson 56618242d3bSAndrew Thompson lgp->lp_psc = (caddr_t)lp; 567b47888ceSAndrew Thompson lp->lp_ifp = ifp; 56818242d3bSAndrew Thompson lp->lp_lagg = lgp; 569b47888ceSAndrew Thompson lp->lp_lsc = lsc; 570d74fd345SAndrew Thompson lp->lp_ifma = rifma; 571b47888ceSAndrew Thompson 572b47888ceSAndrew Thompson LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 573b47888ceSAndrew Thompson 574b47888ceSAndrew Thompson lacp_fill_actorinfo(lp, &lp->lp_actor); 575998971a7SAndrew Thompson lacp_fill_markerinfo(lp, &lp->lp_marker); 576b47888ceSAndrew Thompson lp->lp_state = 577b47888ceSAndrew Thompson (active ? LACP_STATE_ACTIVITY : 0) | 578b47888ceSAndrew Thompson (fast ? LACP_STATE_TIMEOUT : 0); 579b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 58018242d3bSAndrew Thompson lacp_linkstate(lgp); 581b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 582b47888ceSAndrew Thompson 583b47888ceSAndrew Thompson return (0); 584b47888ceSAndrew Thompson } 585b47888ceSAndrew Thompson 586b47888ceSAndrew Thompson void 58718242d3bSAndrew Thompson lacp_port_destroy(struct lagg_port *lgp) 588b47888ceSAndrew Thompson { 58918242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 590d74fd345SAndrew Thompson int i; 591b47888ceSAndrew Thompson 5923bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 593b47888ceSAndrew Thompson 594b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 595b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, i); 596b47888ceSAndrew Thompson } 597b47888ceSAndrew Thompson 598b47888ceSAndrew Thompson lacp_disable_collecting(lp); 599b47888ceSAndrew Thompson lacp_disable_distributing(lp); 600b47888ceSAndrew Thompson lacp_unselect(lp); 601e3163ef6SAndrew Thompson lgp->lp_flags &= ~LAGG_PORT_DISABLED; 602b47888ceSAndrew Thompson 603108fe96aSAndrew Thompson /* The address may have already been removed by if_purgemaddrs() */ 604108fe96aSAndrew Thompson if (!lgp->lp_detaching) 605d74fd345SAndrew Thompson if_delmulti_ifma(lp->lp_ifma); 606b47888ceSAndrew Thompson 607b47888ceSAndrew Thompson LIST_REMOVE(lp, lp_next); 608b47888ceSAndrew Thompson free(lp, M_DEVBUF); 609b47888ceSAndrew Thompson } 610b47888ceSAndrew Thompson 611b47888ceSAndrew Thompson int 61218242d3bSAndrew Thompson lacp_port_isactive(struct lagg_port *lgp) 613b47888ceSAndrew Thompson { 61418242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 615b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 616b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 617b47888ceSAndrew Thompson 618b47888ceSAndrew Thompson /* This port is joined to the active aggregator */ 619b47888ceSAndrew Thompson if (la != NULL && la == lsc->lsc_active_aggregator) 620b47888ceSAndrew Thompson return (1); 621b47888ceSAndrew Thompson 622b47888ceSAndrew Thompson return (0); 623b47888ceSAndrew Thompson } 624b47888ceSAndrew Thompson 625b47888ceSAndrew Thompson static void 626b47888ceSAndrew Thompson lacp_disable_collecting(struct lacp_port *lp) 627b47888ceSAndrew Thompson { 62818242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 629b47888ceSAndrew Thompson 630b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting disabled\n")); 631b47888ceSAndrew Thompson 632b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_COLLECTING; 63318242d3bSAndrew Thompson lgp->lp_flags &= ~LAGG_PORT_COLLECTING; 634b47888ceSAndrew Thompson } 635b47888ceSAndrew Thompson 636b47888ceSAndrew Thompson static void 637b47888ceSAndrew Thompson lacp_enable_collecting(struct lacp_port *lp) 638b47888ceSAndrew Thompson { 63918242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 640b47888ceSAndrew Thompson 641b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting enabled\n")); 642b47888ceSAndrew Thompson 643b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_COLLECTING; 64418242d3bSAndrew Thompson lgp->lp_flags |= LAGG_PORT_COLLECTING; 645b47888ceSAndrew Thompson } 646b47888ceSAndrew Thompson 647b47888ceSAndrew Thompson static void 648b47888ceSAndrew Thompson lacp_disable_distributing(struct lacp_port *lp) 649b47888ceSAndrew Thompson { 650b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 651b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 65218242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 653b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 654b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 655b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 656b47888ceSAndrew Thompson 6573bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 658b47888ceSAndrew Thompson 659b47888ceSAndrew Thompson if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 660b47888ceSAndrew Thompson return; 661b47888ceSAndrew Thompson } 662b47888ceSAndrew Thompson 663b47888ceSAndrew Thompson KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 664b47888ceSAndrew Thompson KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 665b47888ceSAndrew Thompson KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 666b47888ceSAndrew Thompson 667b47888ceSAndrew Thompson LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 668b47888ceSAndrew Thompson "nports %d -> %d\n", 669b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 670b47888ceSAndrew Thompson la->la_nports, la->la_nports - 1)); 671b47888ceSAndrew Thompson 672b47888ceSAndrew Thompson TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 673b47888ceSAndrew Thompson la->la_nports--; 674b47888ceSAndrew Thompson 675b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 676b47888ceSAndrew Thompson 677b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 67818242d3bSAndrew Thompson lgp->lp_flags &= ~LAGG_PORT_DISTRIBUTING; 679b47888ceSAndrew Thompson 680b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator == la) { 681b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 682b47888ceSAndrew Thompson } 683b47888ceSAndrew Thompson } 684b47888ceSAndrew Thompson 685b47888ceSAndrew Thompson static void 686b47888ceSAndrew Thompson lacp_enable_distributing(struct lacp_port *lp) 687b47888ceSAndrew Thompson { 688b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 689b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 69018242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 691b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 692b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 693b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 694b47888ceSAndrew Thompson 6953bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgp->lp_lagg); 696b47888ceSAndrew Thompson 697b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 698b47888ceSAndrew Thompson return; 699b47888ceSAndrew Thompson } 700b47888ceSAndrew Thompson 701b47888ceSAndrew Thompson LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 702b47888ceSAndrew Thompson "nports %d -> %d\n", 703b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 704b47888ceSAndrew Thompson la->la_nports, la->la_nports + 1)); 705b47888ceSAndrew Thompson 706b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 707b47888ceSAndrew Thompson TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 708b47888ceSAndrew Thompson la->la_nports++; 709b47888ceSAndrew Thompson 710b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 711b47888ceSAndrew Thompson 712b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DISTRIBUTING; 71318242d3bSAndrew Thompson lgp->lp_flags |= LAGG_PORT_DISTRIBUTING; 714b47888ceSAndrew Thompson 715b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 716b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 717b47888ceSAndrew Thompson } 718b47888ceSAndrew Thompson } 719b47888ceSAndrew Thompson 720b47888ceSAndrew Thompson static void 721b47888ceSAndrew Thompson lacp_transit_expire(void *vp) 722b47888ceSAndrew Thompson { 723b47888ceSAndrew Thompson struct lacp_softc *lsc = vp; 724b47888ceSAndrew Thompson 725b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s\n", __func__)); 726b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 727b47888ceSAndrew Thompson } 728b47888ceSAndrew Thompson 729b47888ceSAndrew Thompson int 73018242d3bSAndrew Thompson lacp_attach(struct lagg_softc *lgs) 731b47888ceSAndrew Thompson { 732b47888ceSAndrew Thompson struct lacp_softc *lsc; 733b47888ceSAndrew Thompson 7343bf517e3SAndrew Thompson LAGG_WLOCK_ASSERT(lgs); 735b47888ceSAndrew Thompson 736b47888ceSAndrew Thompson lsc = malloc(sizeof(struct lacp_softc), 737b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 738b47888ceSAndrew Thompson if (lsc == NULL) 739b47888ceSAndrew Thompson return (ENOMEM); 740b47888ceSAndrew Thompson 74118242d3bSAndrew Thompson lgs->sc_psc = (caddr_t)lsc; 74218242d3bSAndrew Thompson lsc->lsc_lagg = lgs; 743b47888ceSAndrew Thompson 744b47888ceSAndrew Thompson lsc->lsc_hashkey = arc4random(); 745b47888ceSAndrew Thompson lsc->lsc_active_aggregator = NULL; 746b47888ceSAndrew Thompson TAILQ_INIT(&lsc->lsc_aggregators); 747b47888ceSAndrew Thompson LIST_INIT(&lsc->lsc_ports); 748b47888ceSAndrew Thompson 7493bf517e3SAndrew Thompson TASK_INIT(&lsc->lsc_qtask, 0, lacp_dequeue, lsc); 7503bf517e3SAndrew Thompson mtx_init(&lsc->lsc_queue.ifq_mtx, "lacp queue", NULL, MTX_DEF); 7513bf517e3SAndrew Thompson lsc->lsc_queue.ifq_maxlen = ifqmaxlen; 7523bf517e3SAndrew Thompson 7533bf517e3SAndrew Thompson callout_init(&lsc->lsc_transit_callout, CALLOUT_MPSAFE); 7543bf517e3SAndrew Thompson callout_init(&lsc->lsc_callout, CALLOUT_MPSAFE); 755b47888ceSAndrew Thompson 75618242d3bSAndrew Thompson /* if the lagg is already up then do the same */ 75718242d3bSAndrew Thompson if (lgs->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) 75818242d3bSAndrew Thompson lacp_init(lgs); 759b47888ceSAndrew Thompson 760b47888ceSAndrew Thompson return (0); 761b47888ceSAndrew Thompson } 762b47888ceSAndrew Thompson 763b47888ceSAndrew Thompson int 76418242d3bSAndrew Thompson lacp_detach(struct lagg_softc *lgs) 765b47888ceSAndrew Thompson { 76618242d3bSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 767b47888ceSAndrew Thompson 768b47888ceSAndrew Thompson KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 769b47888ceSAndrew Thompson ("aggregators still active")); 770b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator == NULL, 771b47888ceSAndrew Thompson ("aggregator still attached")); 772b47888ceSAndrew Thompson 77318242d3bSAndrew Thompson lgs->sc_psc = NULL; 774b47888ceSAndrew Thompson callout_drain(&lsc->lsc_transit_callout); 775b47888ceSAndrew Thompson callout_drain(&lsc->lsc_callout); 7763bf517e3SAndrew Thompson taskqueue_drain(taskqueue_swi, &lsc->lsc_qtask); 7773bf517e3SAndrew Thompson IF_DRAIN(&lsc->lsc_queue); 7783bf517e3SAndrew Thompson mtx_destroy(&lsc->lsc_queue.ifq_mtx); 779b47888ceSAndrew Thompson 780b47888ceSAndrew Thompson free(lsc, M_DEVBUF); 781b47888ceSAndrew Thompson return (0); 782b47888ceSAndrew Thompson } 783b47888ceSAndrew Thompson 784b47888ceSAndrew Thompson void 78518242d3bSAndrew Thompson lacp_init(struct lagg_softc *lgs) 786b47888ceSAndrew Thompson { 78718242d3bSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 788b47888ceSAndrew Thompson 789b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 790b47888ceSAndrew Thompson } 791b47888ceSAndrew Thompson 792b47888ceSAndrew Thompson void 79318242d3bSAndrew Thompson lacp_stop(struct lagg_softc *lgs) 794b47888ceSAndrew Thompson { 79518242d3bSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 796b47888ceSAndrew Thompson 797b47888ceSAndrew Thompson callout_stop(&lsc->lsc_transit_callout); 798b47888ceSAndrew Thompson callout_stop(&lsc->lsc_callout); 799b47888ceSAndrew Thompson } 800b47888ceSAndrew Thompson 80118242d3bSAndrew Thompson struct lagg_port * 80218242d3bSAndrew Thompson lacp_select_tx_port(struct lagg_softc *lgs, struct mbuf *m) 803b47888ceSAndrew Thompson { 80418242d3bSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(lgs); 805b47888ceSAndrew Thompson struct lacp_aggregator *la; 806b47888ceSAndrew Thompson struct lacp_port *lp; 807b47888ceSAndrew Thompson uint32_t hash; 808b47888ceSAndrew Thompson int nports; 809b47888ceSAndrew Thompson 8103362a474SAndrew Thompson LAGG_RLOCK_ASSERT(lgs); 811b47888ceSAndrew Thompson 812b47888ceSAndrew Thompson if (__predict_false(lsc->lsc_suppress_distributing)) { 813b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 814b47888ceSAndrew Thompson return (NULL); 815b47888ceSAndrew Thompson } 816b47888ceSAndrew Thompson 817b47888ceSAndrew Thompson la = lsc->lsc_active_aggregator; 818b47888ceSAndrew Thompson if (__predict_false(la == NULL)) { 819b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 820b47888ceSAndrew Thompson return (NULL); 821b47888ceSAndrew Thompson } 822b47888ceSAndrew Thompson 823b47888ceSAndrew Thompson nports = la->la_nports; 824b47888ceSAndrew Thompson KASSERT(nports > 0, ("no ports available")); 825b47888ceSAndrew Thompson 82618242d3bSAndrew Thompson hash = lagg_hashmbuf(m, lsc->lsc_hashkey); 827b47888ceSAndrew Thompson hash %= nports; 828b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 829b47888ceSAndrew Thompson while (hash--) { 830b47888ceSAndrew Thompson lp = TAILQ_NEXT(lp, lp_dist_q); 831b47888ceSAndrew Thompson } 832b47888ceSAndrew Thompson 833b47888ceSAndrew Thompson KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 834b47888ceSAndrew Thompson ("aggregated port is not distributing")); 835b47888ceSAndrew Thompson 83618242d3bSAndrew Thompson return (lp->lp_lagg); 837b47888ceSAndrew Thompson } 838b47888ceSAndrew Thompson /* 839b47888ceSAndrew Thompson * lacp_suppress_distributing: drop transmit packets for a while 840b47888ceSAndrew Thompson * to preserve packet ordering. 841b47888ceSAndrew Thompson */ 842b47888ceSAndrew Thompson 843b47888ceSAndrew Thompson static void 844b47888ceSAndrew Thompson lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 845b47888ceSAndrew Thompson { 846998971a7SAndrew Thompson struct lacp_port *lp; 847998971a7SAndrew Thompson 848b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 849b47888ceSAndrew Thompson return; 850b47888ceSAndrew Thompson } 851b47888ceSAndrew Thompson 852b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s\n", __func__)); 853b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = TRUE; 854998971a7SAndrew Thompson 855998971a7SAndrew Thompson /* send a marker frame down each port to verify the queues are empty */ 856998971a7SAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 857998971a7SAndrew Thompson lp->lp_flags |= LACP_PORT_MARK; 858998971a7SAndrew Thompson lacp_xmit_marker(lp); 859998971a7SAndrew Thompson } 860998971a7SAndrew Thompson 861998971a7SAndrew Thompson /* set a timeout for the marker frames */ 862b47888ceSAndrew Thompson callout_reset(&lsc->lsc_transit_callout, 863b47888ceSAndrew Thompson LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 864b47888ceSAndrew Thompson } 865b47888ceSAndrew Thompson 866b47888ceSAndrew Thompson static int 867b47888ceSAndrew Thompson lacp_compare_peerinfo(const struct lacp_peerinfo *a, 868b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 869b47888ceSAndrew Thompson { 870b47888ceSAndrew Thompson return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 871b47888ceSAndrew Thompson } 872b47888ceSAndrew Thompson 873b47888ceSAndrew Thompson static int 874b47888ceSAndrew Thompson lacp_compare_systemid(const struct lacp_systemid *a, 875b47888ceSAndrew Thompson const struct lacp_systemid *b) 876b47888ceSAndrew Thompson { 877b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 878b47888ceSAndrew Thompson } 879b47888ceSAndrew Thompson 880b47888ceSAndrew Thompson #if 0 /* unused */ 881b47888ceSAndrew Thompson static int 882b47888ceSAndrew Thompson lacp_compare_portid(const struct lacp_portid *a, 883b47888ceSAndrew Thompson const struct lacp_portid *b) 884b47888ceSAndrew Thompson { 885b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 886b47888ceSAndrew Thompson } 887b47888ceSAndrew Thompson #endif 888b47888ceSAndrew Thompson 889b47888ceSAndrew Thompson static uint64_t 890b47888ceSAndrew Thompson lacp_aggregator_bandwidth(struct lacp_aggregator *la) 891b47888ceSAndrew Thompson { 892b47888ceSAndrew Thompson struct lacp_port *lp; 893b47888ceSAndrew Thompson uint64_t speed; 894b47888ceSAndrew Thompson 895b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 896b47888ceSAndrew Thompson if (lp == NULL) { 897b47888ceSAndrew Thompson return (0); 898b47888ceSAndrew Thompson } 899b47888ceSAndrew Thompson 900b47888ceSAndrew Thompson speed = ifmedia_baudrate(lp->lp_media); 901b47888ceSAndrew Thompson speed *= la->la_nports; 902b47888ceSAndrew Thompson if (speed == 0) { 903b47888ceSAndrew Thompson LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 904b47888ceSAndrew Thompson lp->lp_media, la->la_nports)); 905b47888ceSAndrew Thompson } 906b47888ceSAndrew Thompson 907b47888ceSAndrew Thompson return (speed); 908b47888ceSAndrew Thompson } 909b47888ceSAndrew Thompson 910b47888ceSAndrew Thompson /* 911b47888ceSAndrew Thompson * lacp_select_active_aggregator: select an aggregator to be used to transmit 91218242d3bSAndrew Thompson * packets from lagg(4) interface. 913b47888ceSAndrew Thompson */ 914b47888ceSAndrew Thompson 915b47888ceSAndrew Thompson static void 916b47888ceSAndrew Thompson lacp_select_active_aggregator(struct lacp_softc *lsc) 917b47888ceSAndrew Thompson { 918b47888ceSAndrew Thompson struct lacp_aggregator *la; 919b47888ceSAndrew Thompson struct lacp_aggregator *best_la = NULL; 920b47888ceSAndrew Thompson uint64_t best_speed = 0; 921b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 922b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 923b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 924b47888ceSAndrew Thompson 925b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s:\n", __func__)); 926b47888ceSAndrew Thompson 927b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 928b47888ceSAndrew Thompson uint64_t speed; 929b47888ceSAndrew Thompson 930b47888ceSAndrew Thompson if (la->la_nports == 0) { 931b47888ceSAndrew Thompson continue; 932b47888ceSAndrew Thompson } 933b47888ceSAndrew Thompson 934b47888ceSAndrew Thompson speed = lacp_aggregator_bandwidth(la); 935b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 936b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 937b47888ceSAndrew Thompson speed, la->la_nports)); 938fe45e65fSAndrew Thompson 939fe45e65fSAndrew Thompson /* This aggregator is chosen if 940fe45e65fSAndrew Thompson * the partner has a better system priority 941fe45e65fSAndrew Thompson * or, the total aggregated speed is higher 942fe45e65fSAndrew Thompson * or, it is already the chosen aggregator 943fe45e65fSAndrew Thompson */ 944fe45e65fSAndrew Thompson if ((best_la != NULL && LACP_SYS_PRI(la->la_partner) < 945fe45e65fSAndrew Thompson LACP_SYS_PRI(best_la->la_partner)) || 946fe45e65fSAndrew Thompson speed > best_speed || 947b47888ceSAndrew Thompson (speed == best_speed && 948b47888ceSAndrew Thompson la == lsc->lsc_active_aggregator)) { 949b47888ceSAndrew Thompson best_la = la; 950b47888ceSAndrew Thompson best_speed = speed; 951b47888ceSAndrew Thompson } 952b47888ceSAndrew Thompson } 953b47888ceSAndrew Thompson 954b47888ceSAndrew Thompson KASSERT(best_la == NULL || best_la->la_nports > 0, 955b47888ceSAndrew Thompson ("invalid aggregator refcnt")); 956b47888ceSAndrew Thompson KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 957b47888ceSAndrew Thompson ("invalid aggregator list")); 958b47888ceSAndrew Thompson 959b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 960b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 961b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator changed\n")); 962b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "old %s\n", 963b47888ceSAndrew Thompson lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 964b47888ceSAndrew Thompson buf, sizeof(buf)))); 965b47888ceSAndrew Thompson } else { 966b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator not changed\n")); 967b47888ceSAndrew Thompson } 968b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "new %s\n", 969b47888ceSAndrew Thompson lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 970b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 971b47888ceSAndrew Thompson 972b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 973b47888ceSAndrew Thompson lsc->lsc_active_aggregator = best_la; 974b47888ceSAndrew Thompson if (best_la) { 975b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, best_la); 976b47888ceSAndrew Thompson } 977b47888ceSAndrew Thompson } 978b47888ceSAndrew Thompson } 979b47888ceSAndrew Thompson 980b47888ceSAndrew Thompson static uint16_t 981b47888ceSAndrew Thompson lacp_compose_key(struct lacp_port *lp) 982b47888ceSAndrew Thompson { 98318242d3bSAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 98418242d3bSAndrew Thompson struct lagg_softc *lgs = lgp->lp_lagg; 985b47888ceSAndrew Thompson u_int media = lp->lp_media; 986b47888ceSAndrew Thompson uint16_t key; 987b47888ceSAndrew Thompson 988b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 989b47888ceSAndrew Thompson 990b47888ceSAndrew Thompson /* 991b47888ceSAndrew Thompson * non-aggregatable links should have unique keys. 992b47888ceSAndrew Thompson * 993b47888ceSAndrew Thompson * XXX this isn't really unique as if_index is 16 bit. 994b47888ceSAndrew Thompson */ 995b47888ceSAndrew Thompson 996b47888ceSAndrew Thompson /* bit 0..14: (some bits of) if_index of this port */ 997b47888ceSAndrew Thompson key = lp->lp_ifp->if_index; 998b47888ceSAndrew Thompson /* bit 15: 1 */ 999b47888ceSAndrew Thompson key |= 0x8000; 1000b47888ceSAndrew Thompson } else { 1001b47888ceSAndrew Thompson u_int subtype = IFM_SUBTYPE(media); 1002b47888ceSAndrew Thompson 1003e3163ef6SAndrew Thompson KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid media type")); 1004e3163ef6SAndrew Thompson KASSERT((media & IFM_FDX) != 0, ("aggregating HDX interface")); 1005b47888ceSAndrew Thompson 1006b47888ceSAndrew Thompson /* bit 0..4: IFM_SUBTYPE */ 1007b47888ceSAndrew Thompson key = subtype; 100818242d3bSAndrew Thompson /* bit 5..14: (some bits of) if_index of lagg device */ 100918242d3bSAndrew Thompson key |= 0x7fe0 & ((lgs->sc_ifp->if_index) << 5); 1010b47888ceSAndrew Thompson /* bit 15: 0 */ 1011b47888ceSAndrew Thompson } 1012b47888ceSAndrew Thompson return (htons(key)); 1013b47888ceSAndrew Thompson } 1014b47888ceSAndrew Thompson 1015b47888ceSAndrew Thompson static void 1016b47888ceSAndrew Thompson lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1017b47888ceSAndrew Thompson { 1018b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1019b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1020b47888ceSAndrew Thompson #endif 1021b47888ceSAndrew Thompson 1022b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1023b47888ceSAndrew Thompson __func__, 1024b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1025b47888ceSAndrew Thompson buf, sizeof(buf)), 1026b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt + 1)); 1027b47888ceSAndrew Thompson 1028b47888ceSAndrew Thompson KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 1029b47888ceSAndrew Thompson la->la_refcnt++; 1030b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 1031b47888ceSAndrew Thompson } 1032b47888ceSAndrew Thompson 1033b47888ceSAndrew Thompson static void 1034b47888ceSAndrew Thompson lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 1035b47888ceSAndrew Thompson { 1036b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1037b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1038b47888ceSAndrew Thompson #endif 1039b47888ceSAndrew Thompson 1040b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 1041b47888ceSAndrew Thompson __func__, 1042b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1043b47888ceSAndrew Thompson buf, sizeof(buf)), 1044b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt - 1)); 1045b47888ceSAndrew Thompson 1046b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 1047b47888ceSAndrew Thompson la->la_refcnt--; 1048b47888ceSAndrew Thompson if (la->la_refcnt > 0) { 1049b47888ceSAndrew Thompson return; 1050b47888ceSAndrew Thompson } 1051b47888ceSAndrew Thompson 1052b47888ceSAndrew Thompson KASSERT(la->la_refcnt == 0, ("refcount not zero")); 1053b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 1054b47888ceSAndrew Thompson 1055b47888ceSAndrew Thompson TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 1056b47888ceSAndrew Thompson 1057b47888ceSAndrew Thompson free(la, M_DEVBUF); 1058b47888ceSAndrew Thompson } 1059b47888ceSAndrew Thompson 1060b47888ceSAndrew Thompson /* 1061b47888ceSAndrew Thompson * lacp_aggregator_get: allocate an aggregator. 1062b47888ceSAndrew Thompson */ 1063b47888ceSAndrew Thompson 1064b47888ceSAndrew Thompson static struct lacp_aggregator * 1065b47888ceSAndrew Thompson lacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 1066b47888ceSAndrew Thompson { 1067b47888ceSAndrew Thompson struct lacp_aggregator *la; 1068b47888ceSAndrew Thompson 1069b47888ceSAndrew Thompson la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 1070b47888ceSAndrew Thompson if (la) { 1071b47888ceSAndrew Thompson la->la_refcnt = 1; 1072b47888ceSAndrew Thompson la->la_nports = 0; 1073b47888ceSAndrew Thompson TAILQ_INIT(&la->la_ports); 1074b47888ceSAndrew Thompson la->la_pending = 0; 1075b47888ceSAndrew Thompson TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 1076b47888ceSAndrew Thompson } 1077b47888ceSAndrew Thompson 1078b47888ceSAndrew Thompson return (la); 1079b47888ceSAndrew Thompson } 1080b47888ceSAndrew Thompson 1081b47888ceSAndrew Thompson /* 1082b47888ceSAndrew Thompson * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 1083b47888ceSAndrew Thompson */ 1084b47888ceSAndrew Thompson 1085b47888ceSAndrew Thompson static void 1086b47888ceSAndrew Thompson lacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 1087b47888ceSAndrew Thompson { 1088b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 1089b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 1090b47888ceSAndrew Thompson 1091b47888ceSAndrew Thompson la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 1092b47888ceSAndrew Thompson } 1093b47888ceSAndrew Thompson 1094b47888ceSAndrew Thompson static void 1095b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 1096b47888ceSAndrew Thompson const struct lacp_peerinfo *lpi_port) 1097b47888ceSAndrew Thompson { 1098b47888ceSAndrew Thompson memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 1099b47888ceSAndrew Thompson lpi_aggr->lip_systemid = lpi_port->lip_systemid; 1100b47888ceSAndrew Thompson lpi_aggr->lip_key = lpi_port->lip_key; 1101b47888ceSAndrew Thompson } 1102b47888ceSAndrew Thompson 1103b47888ceSAndrew Thompson /* 1104b47888ceSAndrew Thompson * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 1105b47888ceSAndrew Thompson */ 1106b47888ceSAndrew Thompson 1107b47888ceSAndrew Thompson static int 1108b47888ceSAndrew Thompson lacp_aggregator_is_compatible(const struct lacp_aggregator *la, 1109b47888ceSAndrew Thompson const struct lacp_port *lp) 1110b47888ceSAndrew Thompson { 1111b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 1112b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 1113b47888ceSAndrew Thompson return (0); 1114b47888ceSAndrew Thompson } 1115b47888ceSAndrew Thompson 1116b47888ceSAndrew Thompson if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 1117b47888ceSAndrew Thompson return (0); 1118b47888ceSAndrew Thompson } 1119b47888ceSAndrew Thompson 1120b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 1121b47888ceSAndrew Thompson return (0); 1122b47888ceSAndrew Thompson } 1123b47888ceSAndrew Thompson 1124b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 1125b47888ceSAndrew Thompson return (0); 1126b47888ceSAndrew Thompson } 1127b47888ceSAndrew Thompson 1128b47888ceSAndrew Thompson return (1); 1129b47888ceSAndrew Thompson } 1130b47888ceSAndrew Thompson 1131b47888ceSAndrew Thompson static int 1132b47888ceSAndrew Thompson lacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 1133b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 1134b47888ceSAndrew Thompson { 1135b47888ceSAndrew Thompson if (memcmp(&a->lip_systemid, &b->lip_systemid, 1136b47888ceSAndrew Thompson sizeof(a->lip_systemid))) { 1137b47888ceSAndrew Thompson return (0); 1138b47888ceSAndrew Thompson } 1139b47888ceSAndrew Thompson 1140b47888ceSAndrew Thompson if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1141b47888ceSAndrew Thompson return (0); 1142b47888ceSAndrew Thompson } 1143b47888ceSAndrew Thompson 1144b47888ceSAndrew Thompson return (1); 1145b47888ceSAndrew Thompson } 1146b47888ceSAndrew Thompson 1147b47888ceSAndrew Thompson static void 1148b47888ceSAndrew Thompson lacp_port_enable(struct lacp_port *lp) 1149b47888ceSAndrew Thompson { 1150e3163ef6SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 1151e3163ef6SAndrew Thompson 1152b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_AGGREGATION; 1153e3163ef6SAndrew Thompson lgp->lp_flags &= ~LAGG_PORT_DISABLED; 1154b47888ceSAndrew Thompson } 1155b47888ceSAndrew Thompson 1156b47888ceSAndrew Thompson static void 1157b47888ceSAndrew Thompson lacp_port_disable(struct lacp_port *lp) 1158b47888ceSAndrew Thompson { 1159e3163ef6SAndrew Thompson struct lagg_port *lgp = lp->lp_lagg; 1160e3163ef6SAndrew Thompson 1161b47888ceSAndrew Thompson lacp_set_mux(lp, LACP_MUX_DETACHED); 1162b47888ceSAndrew Thompson 1163b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_AGGREGATION; 1164b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1165b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1166b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1167b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1168e3163ef6SAndrew Thompson lgp->lp_flags |= LAGG_PORT_DISABLED; 1169b47888ceSAndrew Thompson } 1170b47888ceSAndrew Thompson 1171b47888ceSAndrew Thompson /* 1172b47888ceSAndrew Thompson * lacp_select: select an aggregator. create one if necessary. 1173b47888ceSAndrew Thompson */ 1174b47888ceSAndrew Thompson static void 1175b47888ceSAndrew Thompson lacp_select(struct lacp_port *lp) 1176b47888ceSAndrew Thompson { 1177b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1178b47888ceSAndrew Thompson struct lacp_aggregator *la; 1179b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1180b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1181b47888ceSAndrew Thompson #endif 1182b47888ceSAndrew Thompson 1183b47888ceSAndrew Thompson if (lp->lp_aggregator) { 1184b47888ceSAndrew Thompson return; 1185b47888ceSAndrew Thompson } 1186b47888ceSAndrew Thompson 1187b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1188b47888ceSAndrew Thompson ("timer_wait_while still active")); 1189b47888ceSAndrew Thompson 1190b47888ceSAndrew Thompson LACP_DPRINTF((lp, "port lagid=%s\n", 1191b47888ceSAndrew Thompson lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1192b47888ceSAndrew Thompson buf, sizeof(buf)))); 1193b47888ceSAndrew Thompson 1194b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1195b47888ceSAndrew Thompson if (lacp_aggregator_is_compatible(la, lp)) { 1196b47888ceSAndrew Thompson break; 1197b47888ceSAndrew Thompson } 1198b47888ceSAndrew Thompson } 1199b47888ceSAndrew Thompson 1200b47888ceSAndrew Thompson if (la == NULL) { 1201b47888ceSAndrew Thompson la = lacp_aggregator_get(lsc, lp); 1202b47888ceSAndrew Thompson if (la == NULL) { 1203b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator creation failed\n")); 1204b47888ceSAndrew Thompson 1205b47888ceSAndrew Thompson /* 1206b47888ceSAndrew Thompson * will retry on the next tick. 1207b47888ceSAndrew Thompson */ 1208b47888ceSAndrew Thompson 1209b47888ceSAndrew Thompson return; 1210b47888ceSAndrew Thompson } 1211b47888ceSAndrew Thompson lacp_fill_aggregator_id(la, lp); 1212b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator created\n")); 1213b47888ceSAndrew Thompson } else { 1214b47888ceSAndrew Thompson LACP_DPRINTF((lp, "compatible aggregator found\n")); 1215b47888ceSAndrew Thompson lacp_aggregator_addref(lsc, la); 1216b47888ceSAndrew Thompson } 1217b47888ceSAndrew Thompson 1218b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1219b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1220b47888ceSAndrew Thompson buf, sizeof(buf)))); 1221b47888ceSAndrew Thompson 1222b47888ceSAndrew Thompson lp->lp_aggregator = la; 1223b47888ceSAndrew Thompson lp->lp_selected = LACP_SELECTED; 1224b47888ceSAndrew Thompson } 1225b47888ceSAndrew Thompson 1226b47888ceSAndrew Thompson /* 1227b47888ceSAndrew Thompson * lacp_unselect: finish unselect/detach process. 1228b47888ceSAndrew Thompson */ 1229b47888ceSAndrew Thompson 1230b47888ceSAndrew Thompson static void 1231b47888ceSAndrew Thompson lacp_unselect(struct lacp_port *lp) 1232b47888ceSAndrew Thompson { 1233b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1234b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1235b47888ceSAndrew Thompson 1236b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1237b47888ceSAndrew Thompson ("timer_wait_while still active")); 1238b47888ceSAndrew Thompson 1239b47888ceSAndrew Thompson if (la == NULL) { 1240b47888ceSAndrew Thompson return; 1241b47888ceSAndrew Thompson } 1242b47888ceSAndrew Thompson 1243b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 1244b47888ceSAndrew Thompson lacp_aggregator_delref(lsc, la); 1245b47888ceSAndrew Thompson } 1246b47888ceSAndrew Thompson 1247b47888ceSAndrew Thompson /* mux machine */ 1248b47888ceSAndrew Thompson 1249b47888ceSAndrew Thompson static void 1250b47888ceSAndrew Thompson lacp_sm_mux(struct lacp_port *lp) 1251b47888ceSAndrew Thompson { 1252b47888ceSAndrew Thompson enum lacp_mux_state new_state; 1253b47888ceSAndrew Thompson boolean_t p_sync = 1254b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1255b47888ceSAndrew Thompson boolean_t p_collecting = 1256b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1257b47888ceSAndrew Thompson enum lacp_selected selected = lp->lp_selected; 1258b47888ceSAndrew Thompson struct lacp_aggregator *la; 1259b47888ceSAndrew Thompson 1260b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */ 1261b47888ceSAndrew Thompson 1262b47888ceSAndrew Thompson re_eval: 1263b47888ceSAndrew Thompson la = lp->lp_aggregator; 1264b47888ceSAndrew Thompson KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1265b47888ceSAndrew Thompson ("MUX not detached")); 1266b47888ceSAndrew Thompson new_state = lp->lp_mux_state; 1267b47888ceSAndrew Thompson switch (lp->lp_mux_state) { 1268b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1269b47888ceSAndrew Thompson if (selected != LACP_UNSELECTED) { 1270b47888ceSAndrew Thompson new_state = LACP_MUX_WAITING; 1271b47888ceSAndrew Thompson } 1272b47888ceSAndrew Thompson break; 1273b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1274b47888ceSAndrew Thompson KASSERT(la->la_pending > 0 || 1275b47888ceSAndrew Thompson !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1276b47888ceSAndrew Thompson ("timer_wait_while still active")); 1277b47888ceSAndrew Thompson if (selected == LACP_SELECTED && la->la_pending == 0) { 1278b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1279b47888ceSAndrew Thompson } else if (selected == LACP_UNSELECTED) { 1280b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1281b47888ceSAndrew Thompson } 1282b47888ceSAndrew Thompson break; 1283b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1284b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync) { 1285b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1286b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED) { 1287b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1288b47888ceSAndrew Thompson } 1289b47888ceSAndrew Thompson break; 1290b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1291b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync && p_collecting) { 1292b47888ceSAndrew Thompson new_state = LACP_MUX_DISTRIBUTING; 1293b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED || !p_sync) { 1294b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1295b47888ceSAndrew Thompson } 1296b47888ceSAndrew Thompson break; 1297b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1298b47888ceSAndrew Thompson if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1299b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1300b47888ceSAndrew Thompson } 1301b47888ceSAndrew Thompson break; 1302b47888ceSAndrew Thompson default: 1303b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1304b47888ceSAndrew Thompson } 1305b47888ceSAndrew Thompson 1306b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1307b47888ceSAndrew Thompson return; 1308b47888ceSAndrew Thompson } 1309b47888ceSAndrew Thompson 1310b47888ceSAndrew Thompson lacp_set_mux(lp, new_state); 1311b47888ceSAndrew Thompson goto re_eval; 1312b47888ceSAndrew Thompson } 1313b47888ceSAndrew Thompson 1314b47888ceSAndrew Thompson static void 1315b47888ceSAndrew Thompson lacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1316b47888ceSAndrew Thompson { 1317b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1318b47888ceSAndrew Thompson 1319b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1320b47888ceSAndrew Thompson return; 1321b47888ceSAndrew Thompson } 1322b47888ceSAndrew Thompson 1323b47888ceSAndrew Thompson switch (new_state) { 1324b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1325b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_SYNC; 1326b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1327b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1328b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1329b47888ceSAndrew Thompson /* cancel timer */ 1330b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1331b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, 1332b47888ceSAndrew Thompson ("timer_wait_while not active")); 1333b47888ceSAndrew Thompson la->la_pending--; 1334b47888ceSAndrew Thompson } 1335b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1336b47888ceSAndrew Thompson lacp_unselect(lp); 1337b47888ceSAndrew Thompson break; 1338b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1339b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1340b47888ceSAndrew Thompson LACP_AGGREGATE_WAIT_TIME); 1341b47888ceSAndrew Thompson la->la_pending++; 1342b47888ceSAndrew Thompson break; 1343b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1344b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_SYNC; 1345b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1346b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1347b47888ceSAndrew Thompson break; 1348b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1349b47888ceSAndrew Thompson lacp_enable_collecting(lp); 1350b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1351b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1352b47888ceSAndrew Thompson break; 1353b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1354b47888ceSAndrew Thompson lacp_enable_distributing(lp); 1355b47888ceSAndrew Thompson break; 1356b47888ceSAndrew Thompson default: 1357b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1358b47888ceSAndrew Thompson } 1359b47888ceSAndrew Thompson 1360b47888ceSAndrew Thompson LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1361b47888ceSAndrew Thompson 1362b47888ceSAndrew Thompson lp->lp_mux_state = new_state; 1363b47888ceSAndrew Thompson } 1364b47888ceSAndrew Thompson 1365b47888ceSAndrew Thompson static void 1366b47888ceSAndrew Thompson lacp_sm_mux_timer(struct lacp_port *lp) 1367b47888ceSAndrew Thompson { 1368b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1369b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1370b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1371b47888ceSAndrew Thompson #endif 1372b47888ceSAndrew Thompson 1373b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, ("no pending event")); 1374b47888ceSAndrew Thompson 1375b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1376b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1377b47888ceSAndrew Thompson buf, sizeof(buf)), 1378b47888ceSAndrew Thompson la->la_pending, la->la_pending - 1)); 1379b47888ceSAndrew Thompson 1380b47888ceSAndrew Thompson la->la_pending--; 1381b47888ceSAndrew Thompson } 1382b47888ceSAndrew Thompson 1383b47888ceSAndrew Thompson /* periodic transmit machine */ 1384b47888ceSAndrew Thompson 1385b47888ceSAndrew Thompson static void 1386b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1387b47888ceSAndrew Thompson { 1388b47888ceSAndrew Thompson if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1389b47888ceSAndrew Thompson LACP_STATE_TIMEOUT)) { 1390b47888ceSAndrew Thompson return; 1391b47888ceSAndrew Thompson } 1392b47888ceSAndrew Thompson 1393b47888ceSAndrew Thompson LACP_DPRINTF((lp, "partner timeout changed\n")); 1394b47888ceSAndrew Thompson 1395b47888ceSAndrew Thompson /* 1396b47888ceSAndrew Thompson * FAST_PERIODIC -> SLOW_PERIODIC 1397b47888ceSAndrew Thompson * or 1398b47888ceSAndrew Thompson * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1399b47888ceSAndrew Thompson * 1400b47888ceSAndrew Thompson * let lacp_sm_ptx_tx_schedule to update timeout. 1401b47888ceSAndrew Thompson */ 1402b47888ceSAndrew Thompson 1403b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1404b47888ceSAndrew Thompson 1405b47888ceSAndrew Thompson /* 1406b47888ceSAndrew Thompson * if timeout has been shortened, assert NTT. 1407b47888ceSAndrew Thompson */ 1408b47888ceSAndrew Thompson 1409b47888ceSAndrew Thompson if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1410b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1411b47888ceSAndrew Thompson } 1412b47888ceSAndrew Thompson } 1413b47888ceSAndrew Thompson 1414b47888ceSAndrew Thompson static void 1415b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1416b47888ceSAndrew Thompson { 1417b47888ceSAndrew Thompson int timeout; 1418b47888ceSAndrew Thompson 1419b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1420b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1421b47888ceSAndrew Thompson 1422b47888ceSAndrew Thompson /* 1423b47888ceSAndrew Thompson * NO_PERIODIC 1424b47888ceSAndrew Thompson */ 1425b47888ceSAndrew Thompson 1426b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1427b47888ceSAndrew Thompson return; 1428b47888ceSAndrew Thompson } 1429b47888ceSAndrew Thompson 1430b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1431b47888ceSAndrew Thompson return; 1432b47888ceSAndrew Thompson } 1433b47888ceSAndrew Thompson 1434b47888ceSAndrew Thompson timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1435b47888ceSAndrew Thompson LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1436b47888ceSAndrew Thompson 1437b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1438b47888ceSAndrew Thompson } 1439b47888ceSAndrew Thompson 1440b47888ceSAndrew Thompson static void 1441b47888ceSAndrew Thompson lacp_sm_ptx_timer(struct lacp_port *lp) 1442b47888ceSAndrew Thompson { 1443b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1444b47888ceSAndrew Thompson } 1445b47888ceSAndrew Thompson 1446b47888ceSAndrew Thompson static void 1447b47888ceSAndrew Thompson lacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1448b47888ceSAndrew Thompson { 1449b47888ceSAndrew Thompson int timeout; 1450b47888ceSAndrew Thompson 1451b47888ceSAndrew Thompson /* 1452b47888ceSAndrew Thompson * check LACP_DISABLED first 1453b47888ceSAndrew Thompson */ 1454b47888ceSAndrew Thompson 1455b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1456b47888ceSAndrew Thompson return; 1457b47888ceSAndrew Thompson } 1458b47888ceSAndrew Thompson 1459b47888ceSAndrew Thompson /* 1460b47888ceSAndrew Thompson * check loopback condition. 1461b47888ceSAndrew Thompson */ 1462b47888ceSAndrew Thompson 1463b47888ceSAndrew Thompson if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1464b47888ceSAndrew Thompson &lp->lp_actor.lip_systemid)) { 1465b47888ceSAndrew Thompson return; 1466b47888ceSAndrew Thompson } 1467b47888ceSAndrew Thompson 1468b47888ceSAndrew Thompson /* 1469b47888ceSAndrew Thompson * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1470b47888ceSAndrew Thompson */ 1471b47888ceSAndrew Thompson 1472b47888ceSAndrew Thompson lacp_sm_rx_update_selected(lp, du); 1473b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(lp, du); 1474b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(lp, du); 1475b47888ceSAndrew Thompson 1476b47888ceSAndrew Thompson timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1477b47888ceSAndrew Thompson LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1478b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1479b47888ceSAndrew Thompson 1480b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1481b47888ceSAndrew Thompson 1482b47888ceSAndrew Thompson /* 1483b47888ceSAndrew Thompson * kick transmit machine without waiting the next tick. 1484b47888ceSAndrew Thompson */ 1485b47888ceSAndrew Thompson 1486b47888ceSAndrew Thompson lacp_sm_tx(lp); 1487b47888ceSAndrew Thompson } 1488b47888ceSAndrew Thompson 1489b47888ceSAndrew Thompson static void 1490b47888ceSAndrew Thompson lacp_sm_rx_set_expired(struct lacp_port *lp) 1491b47888ceSAndrew Thompson { 1492b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1493b47888ceSAndrew Thompson lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1494b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1495b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_EXPIRED; 1496b47888ceSAndrew Thompson } 1497b47888ceSAndrew Thompson 1498b47888ceSAndrew Thompson static void 1499b47888ceSAndrew Thompson lacp_sm_rx_timer(struct lacp_port *lp) 1500b47888ceSAndrew Thompson { 1501b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1502b47888ceSAndrew Thompson /* CURRENT -> EXPIRED */ 1503b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1504b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 1505b47888ceSAndrew Thompson } else { 1506b47888ceSAndrew Thompson /* EXPIRED -> DEFAULTED */ 1507b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1508b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(lp); 1509b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1510b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1511b47888ceSAndrew Thompson } 1512b47888ceSAndrew Thompson } 1513b47888ceSAndrew Thompson 1514b47888ceSAndrew Thompson static void 1515b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1516b47888ceSAndrew Thompson { 1517b47888ceSAndrew Thompson boolean_t active; 1518b47888ceSAndrew Thompson uint8_t oldpstate; 1519b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1520b47888ceSAndrew Thompson char buf[LACP_STATESTR_MAX+1]; 1521b47888ceSAndrew Thompson #endif 1522b47888ceSAndrew Thompson 1523b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1524b47888ceSAndrew Thompson 1525b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1526b47888ceSAndrew Thompson 1527b47888ceSAndrew Thompson active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1528b47888ceSAndrew Thompson || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1529b47888ceSAndrew Thompson (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1530b47888ceSAndrew Thompson 1531b47888ceSAndrew Thompson lp->lp_partner = du->ldu_actor; 1532b47888ceSAndrew Thompson if (active && 1533b47888ceSAndrew Thompson ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1534b47888ceSAndrew Thompson LACP_STATE_AGGREGATION) && 1535b47888ceSAndrew Thompson !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1536b47888ceSAndrew Thompson || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1537b47888ceSAndrew Thompson /* XXX nothing? */ 1538b47888ceSAndrew Thompson } else { 1539b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1540b47888ceSAndrew Thompson } 1541b47888ceSAndrew Thompson 1542b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DEFAULTED; 1543b47888ceSAndrew Thompson 1544b47888ceSAndrew Thompson if (oldpstate != lp->lp_partner.lip_state) { 1545b47888ceSAndrew Thompson LACP_DPRINTF((lp, "old pstate %s\n", 1546b47888ceSAndrew Thompson lacp_format_state(oldpstate, buf, sizeof(buf)))); 1547b47888ceSAndrew Thompson LACP_DPRINTF((lp, "new pstate %s\n", 1548b47888ceSAndrew Thompson lacp_format_state(lp->lp_partner.lip_state, buf, 1549b47888ceSAndrew Thompson sizeof(buf)))); 1550b47888ceSAndrew Thompson } 1551b47888ceSAndrew Thompson 1552b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1553b47888ceSAndrew Thompson } 1554b47888ceSAndrew Thompson 1555b47888ceSAndrew Thompson static void 1556b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1557b47888ceSAndrew Thompson { 1558b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1559b47888ceSAndrew Thompson 1560b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1561b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1562b47888ceSAndrew Thompson LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1563b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1564b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1565b47888ceSAndrew Thompson } 1566b47888ceSAndrew Thompson } 1567b47888ceSAndrew Thompson 1568b47888ceSAndrew Thompson static void 1569b47888ceSAndrew Thompson lacp_sm_rx_record_default(struct lacp_port *lp) 1570b47888ceSAndrew Thompson { 1571b47888ceSAndrew Thompson uint8_t oldpstate; 1572b47888ceSAndrew Thompson 1573b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1574b47888ceSAndrew Thompson 1575b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1576b47888ceSAndrew Thompson lp->lp_partner = lacp_partner_admin; 1577b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DEFAULTED; 1578b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1579b47888ceSAndrew Thompson } 1580b47888ceSAndrew Thompson 1581b47888ceSAndrew Thompson static void 1582b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1583b47888ceSAndrew Thompson const struct lacp_peerinfo *info) 1584b47888ceSAndrew Thompson { 1585b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1586b47888ceSAndrew Thompson 1587b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1588b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1589b47888ceSAndrew Thompson LACP_STATE_AGGREGATION)) { 1590b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1591b47888ceSAndrew Thompson /* mux machine will clean up lp->lp_aggregator */ 1592b47888ceSAndrew Thompson } 1593b47888ceSAndrew Thompson } 1594b47888ceSAndrew Thompson 1595b47888ceSAndrew Thompson static void 1596b47888ceSAndrew Thompson lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1597b47888ceSAndrew Thompson { 1598b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1599b47888ceSAndrew Thompson 1600b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1601b47888ceSAndrew Thompson } 1602b47888ceSAndrew Thompson 1603b47888ceSAndrew Thompson static void 1604b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(struct lacp_port *lp) 1605b47888ceSAndrew Thompson { 1606b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1607b47888ceSAndrew Thompson 1608b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin); 1609b47888ceSAndrew Thompson } 1610b47888ceSAndrew Thompson 1611b47888ceSAndrew Thompson /* transmit machine */ 1612b47888ceSAndrew Thompson 1613b47888ceSAndrew Thompson static void 1614b47888ceSAndrew Thompson lacp_sm_tx(struct lacp_port *lp) 1615b47888ceSAndrew Thompson { 1616b47888ceSAndrew Thompson int error; 1617b47888ceSAndrew Thompson 1618b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1619b47888ceSAndrew Thompson #if 1 1620b47888ceSAndrew Thompson || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1621b47888ceSAndrew Thompson && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1622b47888ceSAndrew Thompson #endif 1623b47888ceSAndrew Thompson ) { 1624b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1625b47888ceSAndrew Thompson } 1626b47888ceSAndrew Thompson 1627b47888ceSAndrew Thompson if (!(lp->lp_flags & LACP_PORT_NTT)) { 1628b47888ceSAndrew Thompson return; 1629b47888ceSAndrew Thompson } 1630b47888ceSAndrew Thompson 1631b47888ceSAndrew Thompson /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1632b47888ceSAndrew Thompson if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1633b47888ceSAndrew Thompson (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1634b47888ceSAndrew Thompson LACP_DPRINTF((lp, "rate limited pdu\n")); 1635b47888ceSAndrew Thompson return; 1636b47888ceSAndrew Thompson } 1637b47888ceSAndrew Thompson 1638b47888ceSAndrew Thompson error = lacp_xmit_lacpdu(lp); 1639b47888ceSAndrew Thompson 1640b47888ceSAndrew Thompson if (error == 0) { 1641b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1642b47888ceSAndrew Thompson } else { 1643b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1644b47888ceSAndrew Thompson error)); 1645b47888ceSAndrew Thompson } 1646b47888ceSAndrew Thompson } 1647b47888ceSAndrew Thompson 1648b47888ceSAndrew Thompson static void 1649b47888ceSAndrew Thompson lacp_sm_assert_ntt(struct lacp_port *lp) 1650b47888ceSAndrew Thompson { 1651b47888ceSAndrew Thompson 1652b47888ceSAndrew Thompson lp->lp_flags |= LACP_PORT_NTT; 1653b47888ceSAndrew Thompson } 1654b47888ceSAndrew Thompson 1655b47888ceSAndrew Thompson static void 1656b47888ceSAndrew Thompson lacp_run_timers(struct lacp_port *lp) 1657b47888ceSAndrew Thompson { 1658b47888ceSAndrew Thompson int i; 1659b47888ceSAndrew Thompson 1660b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 1661b47888ceSAndrew Thompson KASSERT(lp->lp_timer[i] >= 0, 1662b47888ceSAndrew Thompson ("invalid timer value %d", lp->lp_timer[i])); 1663b47888ceSAndrew Thompson if (lp->lp_timer[i] == 0) { 1664b47888ceSAndrew Thompson continue; 1665b47888ceSAndrew Thompson } else if (--lp->lp_timer[i] <= 0) { 1666b47888ceSAndrew Thompson if (lacp_timer_funcs[i]) { 1667b47888ceSAndrew Thompson (*lacp_timer_funcs[i])(lp); 1668b47888ceSAndrew Thompson } 1669b47888ceSAndrew Thompson } 1670b47888ceSAndrew Thompson } 1671b47888ceSAndrew Thompson } 1672b47888ceSAndrew Thompson 1673b47888ceSAndrew Thompson int 167418242d3bSAndrew Thompson lacp_marker_input(struct lagg_port *lgp, struct mbuf *m) 1675b47888ceSAndrew Thompson { 167618242d3bSAndrew Thompson struct lacp_port *lp = LACP_PORT(lgp); 1677998971a7SAndrew Thompson struct lacp_port *lp2; 1678998971a7SAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1679b47888ceSAndrew Thompson struct markerdu *mdu; 1680b47888ceSAndrew Thompson int error = 0; 1681998971a7SAndrew Thompson int pending = 0; 1682b47888ceSAndrew Thompson 16833bf517e3SAndrew Thompson LAGG_RLOCK_ASSERT(lgp->lp_lagg); 1684b47888ceSAndrew Thompson 1685b47888ceSAndrew Thompson if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 1686b47888ceSAndrew Thompson goto bad; 1687b47888ceSAndrew Thompson } 1688b47888ceSAndrew Thompson 1689b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*mdu)) { 1690b47888ceSAndrew Thompson goto bad; 1691b47888ceSAndrew Thompson } 1692b47888ceSAndrew Thompson 1693b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 1694b47888ceSAndrew Thompson goto bad; 1695b47888ceSAndrew Thompson } 1696b47888ceSAndrew Thompson 1697b47888ceSAndrew Thompson if (m->m_len < sizeof(*mdu)) { 1698b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*mdu)); 1699b47888ceSAndrew Thompson if (m == NULL) { 1700b47888ceSAndrew Thompson return (ENOMEM); 1701b47888ceSAndrew Thompson } 1702b47888ceSAndrew Thompson } 1703b47888ceSAndrew Thompson 1704b47888ceSAndrew Thompson mdu = mtod(m, struct markerdu *); 1705b47888ceSAndrew Thompson 1706b47888ceSAndrew Thompson if (memcmp(&mdu->mdu_eh.ether_dhost, 1707b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1708b47888ceSAndrew Thompson goto bad; 1709b47888ceSAndrew Thompson } 1710b47888ceSAndrew Thompson 1711b47888ceSAndrew Thompson /* XXX 1712b47888ceSAndrew Thompson KASSERT(mdu->mdu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_MARKER, 1713b47888ceSAndrew Thompson ("a very bad kassert!")); 1714b47888ceSAndrew Thompson */ 1715b47888ceSAndrew Thompson 1716b47888ceSAndrew Thompson if (mdu->mdu_sph.sph_version != 1) { 1717b47888ceSAndrew Thompson goto bad; 1718b47888ceSAndrew Thompson } 1719b47888ceSAndrew Thompson 1720b47888ceSAndrew Thompson switch (mdu->mdu_tlv.tlv_type) { 1721b47888ceSAndrew Thompson case MARKER_TYPE_INFO: 1722b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1723b47888ceSAndrew Thompson marker_info_tlv_template, TRUE)) { 1724b47888ceSAndrew Thompson goto bad; 1725b47888ceSAndrew Thompson } 1726b47888ceSAndrew Thompson mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1727b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, 1728b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1729b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, 173018242d3bSAndrew Thompson lgp->lp_lladdr, ETHER_ADDR_LEN); 173118242d3bSAndrew Thompson error = lagg_enqueue(lp->lp_ifp, m); 1732b47888ceSAndrew Thompson break; 1733b47888ceSAndrew Thompson 1734b47888ceSAndrew Thompson case MARKER_TYPE_RESPONSE: 1735b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1736b47888ceSAndrew Thompson marker_response_tlv_template, TRUE)) { 1737b47888ceSAndrew Thompson goto bad; 1738b47888ceSAndrew Thompson } 1739998971a7SAndrew Thompson LACP_DPRINTF((lp, "marker response, port=%u, sys=%6D, id=%u\n", 1740998971a7SAndrew Thompson ntohs(mdu->mdu_info.mi_rq_port), mdu->mdu_info.mi_rq_system, 1741998971a7SAndrew Thompson ":", ntohl(mdu->mdu_info.mi_rq_xid))); 1742998971a7SAndrew Thompson 1743998971a7SAndrew Thompson /* Verify that it is the last marker we sent out */ 1744998971a7SAndrew Thompson if (memcmp(&mdu->mdu_info, &lp->lp_marker, 1745998971a7SAndrew Thompson sizeof(struct lacp_markerinfo))) 1746998971a7SAndrew Thompson goto bad; 1747998971a7SAndrew Thompson 1748998971a7SAndrew Thompson lp->lp_flags &= ~LACP_PORT_MARK; 1749998971a7SAndrew Thompson 1750998971a7SAndrew Thompson if (lsc->lsc_suppress_distributing) { 1751998971a7SAndrew Thompson /* Check if any ports are waiting for a response */ 1752998971a7SAndrew Thompson LIST_FOREACH(lp2, &lsc->lsc_ports, lp_next) { 1753998971a7SAndrew Thompson if (lp2->lp_flags & LACP_PORT_MARK) { 1754998971a7SAndrew Thompson pending = 1; 1755998971a7SAndrew Thompson break; 1756998971a7SAndrew Thompson } 1757998971a7SAndrew Thompson } 1758998971a7SAndrew Thompson 1759998971a7SAndrew Thompson if (pending == 0) { 1760998971a7SAndrew Thompson /* All interface queues are clear */ 1761998971a7SAndrew Thompson LACP_DPRINTF((NULL, "queue flush complete\n")); 1762998971a7SAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 1763998971a7SAndrew Thompson } 1764998971a7SAndrew Thompson } 1765998971a7SAndrew Thompson 1766998971a7SAndrew Thompson m_freem(m); 1767998971a7SAndrew Thompson break; 1768998971a7SAndrew Thompson 1769b47888ceSAndrew Thompson default: 1770b47888ceSAndrew Thompson goto bad; 1771b47888ceSAndrew Thompson } 1772b47888ceSAndrew Thompson 1773b47888ceSAndrew Thompson return (error); 1774b47888ceSAndrew Thompson 1775b47888ceSAndrew Thompson bad: 1776998971a7SAndrew Thompson LACP_DPRINTF((lp, "bad marker frame\n")); 1777b47888ceSAndrew Thompson m_freem(m); 1778b47888ceSAndrew Thompson return (EINVAL); 1779b47888ceSAndrew Thompson } 1780b47888ceSAndrew Thompson 1781b47888ceSAndrew Thompson static int 1782b47888ceSAndrew Thompson tlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1783b47888ceSAndrew Thompson const struct tlv_template *tmpl, boolean_t check_type) 1784b47888ceSAndrew Thompson { 1785b47888ceSAndrew Thompson while (/* CONSTCOND */ 1) { 1786b47888ceSAndrew Thompson if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1787b47888ceSAndrew Thompson return (EINVAL); 1788b47888ceSAndrew Thompson } 1789b47888ceSAndrew Thompson if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1790b47888ceSAndrew Thompson tlv->tlv_length != tmpl->tmpl_length) { 1791b47888ceSAndrew Thompson return (EINVAL); 1792b47888ceSAndrew Thompson } 1793b47888ceSAndrew Thompson if (tmpl->tmpl_type == 0) { 1794b47888ceSAndrew Thompson break; 1795b47888ceSAndrew Thompson } 1796b47888ceSAndrew Thompson tlv = (const struct tlvhdr *) 1797b47888ceSAndrew Thompson ((const char *)tlv + tlv->tlv_length); 1798b47888ceSAndrew Thompson tmpl++; 1799b47888ceSAndrew Thompson } 1800b47888ceSAndrew Thompson 1801b47888ceSAndrew Thompson return (0); 1802b47888ceSAndrew Thompson } 1803b47888ceSAndrew Thompson 1804b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1805b47888ceSAndrew Thompson const char * 1806b47888ceSAndrew Thompson lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1807b47888ceSAndrew Thompson { 1808b47888ceSAndrew Thompson snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1809b47888ceSAndrew Thompson (int)mac[0], 1810b47888ceSAndrew Thompson (int)mac[1], 1811b47888ceSAndrew Thompson (int)mac[2], 1812b47888ceSAndrew Thompson (int)mac[3], 1813b47888ceSAndrew Thompson (int)mac[4], 1814b47888ceSAndrew Thompson (int)mac[5]); 1815b47888ceSAndrew Thompson 1816b47888ceSAndrew Thompson return (buf); 1817b47888ceSAndrew Thompson } 1818b47888ceSAndrew Thompson 1819b47888ceSAndrew Thompson const char * 1820b47888ceSAndrew Thompson lacp_format_systemid(const struct lacp_systemid *sysid, 1821b47888ceSAndrew Thompson char *buf, size_t buflen) 1822b47888ceSAndrew Thompson { 1823b47888ceSAndrew Thompson char macbuf[LACP_MACSTR_MAX+1]; 1824b47888ceSAndrew Thompson 1825b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%s", 1826b47888ceSAndrew Thompson ntohs(sysid->lsi_prio), 1827b47888ceSAndrew Thompson lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1828b47888ceSAndrew Thompson 1829b47888ceSAndrew Thompson return (buf); 1830b47888ceSAndrew Thompson } 1831b47888ceSAndrew Thompson 1832b47888ceSAndrew Thompson const char * 1833b47888ceSAndrew Thompson lacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1834b47888ceSAndrew Thompson { 1835b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%04X", 1836b47888ceSAndrew Thompson ntohs(portid->lpi_prio), 1837b47888ceSAndrew Thompson ntohs(portid->lpi_portno)); 1838b47888ceSAndrew Thompson 1839b47888ceSAndrew Thompson return (buf); 1840b47888ceSAndrew Thompson } 1841b47888ceSAndrew Thompson 1842b47888ceSAndrew Thompson const char * 1843b47888ceSAndrew Thompson lacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1844b47888ceSAndrew Thompson { 1845b47888ceSAndrew Thompson char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1846b47888ceSAndrew Thompson char portid[LACP_PORTIDSTR_MAX+1]; 1847b47888ceSAndrew Thompson 1848b47888ceSAndrew Thompson snprintf(buf, buflen, "(%s,%04X,%s)", 1849b47888ceSAndrew Thompson lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1850b47888ceSAndrew Thompson ntohs(peer->lip_key), 1851b47888ceSAndrew Thompson lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1852b47888ceSAndrew Thompson 1853b47888ceSAndrew Thompson return (buf); 1854b47888ceSAndrew Thompson } 1855b47888ceSAndrew Thompson 1856b47888ceSAndrew Thompson const char * 1857b47888ceSAndrew Thompson lacp_format_lagid(const struct lacp_peerinfo *a, 1858b47888ceSAndrew Thompson const struct lacp_peerinfo *b, char *buf, size_t buflen) 1859b47888ceSAndrew Thompson { 1860b47888ceSAndrew Thompson char astr[LACP_PARTNERSTR_MAX+1]; 1861b47888ceSAndrew Thompson char bstr[LACP_PARTNERSTR_MAX+1]; 1862b47888ceSAndrew Thompson 1863b47888ceSAndrew Thompson #if 0 1864b47888ceSAndrew Thompson /* 1865b47888ceSAndrew Thompson * there's a convention to display small numbered peer 1866b47888ceSAndrew Thompson * in the left. 1867b47888ceSAndrew Thompson */ 1868b47888ceSAndrew Thompson 1869b47888ceSAndrew Thompson if (lacp_compare_peerinfo(a, b) > 0) { 1870b47888ceSAndrew Thompson const struct lacp_peerinfo *t; 1871b47888ceSAndrew Thompson 1872b47888ceSAndrew Thompson t = a; 1873b47888ceSAndrew Thompson a = b; 1874b47888ceSAndrew Thompson b = t; 1875b47888ceSAndrew Thompson } 1876b47888ceSAndrew Thompson #endif 1877b47888ceSAndrew Thompson 1878b47888ceSAndrew Thompson snprintf(buf, buflen, "[%s,%s]", 1879b47888ceSAndrew Thompson lacp_format_partner(a, astr, sizeof(astr)), 1880b47888ceSAndrew Thompson lacp_format_partner(b, bstr, sizeof(bstr))); 1881b47888ceSAndrew Thompson 1882b47888ceSAndrew Thompson return (buf); 1883b47888ceSAndrew Thompson } 1884b47888ceSAndrew Thompson 1885b47888ceSAndrew Thompson const char * 1886b47888ceSAndrew Thompson lacp_format_lagid_aggregator(const struct lacp_aggregator *la, 1887b47888ceSAndrew Thompson char *buf, size_t buflen) 1888b47888ceSAndrew Thompson { 1889b47888ceSAndrew Thompson if (la == NULL) { 1890b47888ceSAndrew Thompson return ("(none)"); 1891b47888ceSAndrew Thompson } 1892b47888ceSAndrew Thompson 1893b47888ceSAndrew Thompson return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 1894b47888ceSAndrew Thompson } 1895b47888ceSAndrew Thompson 1896b47888ceSAndrew Thompson const char * 1897b47888ceSAndrew Thompson lacp_format_state(uint8_t state, char *buf, size_t buflen) 1898b47888ceSAndrew Thompson { 1899b47888ceSAndrew Thompson snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 1900b47888ceSAndrew Thompson return (buf); 1901b47888ceSAndrew Thompson } 1902b47888ceSAndrew Thompson 1903b47888ceSAndrew Thompson static void 1904b47888ceSAndrew Thompson lacp_dump_lacpdu(const struct lacpdu *du) 1905b47888ceSAndrew Thompson { 1906b47888ceSAndrew Thompson char buf[LACP_PARTNERSTR_MAX+1]; 1907b47888ceSAndrew Thompson char buf2[LACP_STATESTR_MAX+1]; 1908b47888ceSAndrew Thompson 1909b47888ceSAndrew Thompson printf("actor=%s\n", 1910b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 1911b47888ceSAndrew Thompson printf("actor.state=%s\n", 1912b47888ceSAndrew Thompson lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 1913b47888ceSAndrew Thompson printf("partner=%s\n", 1914b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 1915b47888ceSAndrew Thompson printf("partner.state=%s\n", 1916b47888ceSAndrew Thompson lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 1917b47888ceSAndrew Thompson 1918b47888ceSAndrew Thompson printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 1919b47888ceSAndrew Thompson } 1920b47888ceSAndrew Thompson 1921b47888ceSAndrew Thompson static void 1922b47888ceSAndrew Thompson lacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 1923b47888ceSAndrew Thompson { 1924b47888ceSAndrew Thompson va_list va; 1925b47888ceSAndrew Thompson 1926b47888ceSAndrew Thompson if (lp) { 1927b47888ceSAndrew Thompson printf("%s: ", lp->lp_ifp->if_xname); 1928b47888ceSAndrew Thompson } 1929b47888ceSAndrew Thompson 1930b47888ceSAndrew Thompson va_start(va, fmt); 1931b47888ceSAndrew Thompson vprintf(fmt, va); 1932b47888ceSAndrew Thompson va_end(va); 1933b47888ceSAndrew Thompson } 1934b47888ceSAndrew Thompson #endif 1935