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> 41b47888ceSAndrew Thompson 42b47888ceSAndrew Thompson #include <net/if.h> 43b47888ceSAndrew Thompson #include <net/if_dl.h> 44b47888ceSAndrew Thompson #include <net/ethernet.h> 45b47888ceSAndrew Thompson #include <net/if_media.h> 46b47888ceSAndrew Thompson #include <net/if_types.h> 47b47888ceSAndrew Thompson 48b47888ceSAndrew Thompson #include <net/if_trunk.h> 49b47888ceSAndrew Thompson #include <net/ieee8023ad_lacp.h> 50b47888ceSAndrew Thompson 51b47888ceSAndrew Thompson /* 52b47888ceSAndrew Thompson * actor system priority and port priority. 53b47888ceSAndrew Thompson * XXX should be configurable. 54b47888ceSAndrew Thompson */ 55b47888ceSAndrew Thompson 56b47888ceSAndrew Thompson #define LACP_SYSTEM_PRIO 0x8000 57b47888ceSAndrew Thompson #define LACP_PORT_PRIO 0x8000 58b47888ceSAndrew Thompson 59b47888ceSAndrew Thompson const uint8_t ethermulticastaddr_slowprotocols[ETHER_ADDR_LEN] = 60b47888ceSAndrew Thompson { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 }; 61b47888ceSAndrew Thompson 62b47888ceSAndrew Thompson static const struct tlv_template lacp_info_tlv_template[] = { 63b47888ceSAndrew Thompson { LACP_TYPE_ACTORINFO, 64b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 65b47888ceSAndrew Thompson { LACP_TYPE_PARTNERINFO, 66b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_peerinfo) }, 67b47888ceSAndrew Thompson { LACP_TYPE_COLLECTORINFO, 68b47888ceSAndrew Thompson sizeof(struct tlvhdr) + sizeof(struct lacp_collectorinfo) }, 69b47888ceSAndrew Thompson { 0, 0 }, 70b47888ceSAndrew Thompson }; 71b47888ceSAndrew Thompson 72b47888ceSAndrew Thompson typedef void (*lacp_timer_func_t)(struct lacp_port *); 73b47888ceSAndrew Thompson 74b47888ceSAndrew Thompson static const struct tlv_template marker_info_tlv_template[] = { 75b47888ceSAndrew Thompson { MARKER_TYPE_INFO, 16 }, 76b47888ceSAndrew Thompson { 0, 0 }, 77b47888ceSAndrew Thompson }; 78b47888ceSAndrew Thompson 79b47888ceSAndrew Thompson static const struct tlv_template marker_response_tlv_template[] = { 80b47888ceSAndrew Thompson { MARKER_TYPE_RESPONSE, 16 }, 81b47888ceSAndrew Thompson { 0, 0 }, 82b47888ceSAndrew Thompson }; 83b47888ceSAndrew Thompson 84b47888ceSAndrew Thompson static void lacp_fill_actorinfo(struct lacp_port *, struct lacp_peerinfo *); 85b47888ceSAndrew Thompson 86b47888ceSAndrew Thompson static uint64_t lacp_aggregator_bandwidth(struct lacp_aggregator *); 87b47888ceSAndrew Thompson static void lacp_suppress_distributing(struct lacp_softc *, 88b47888ceSAndrew Thompson struct lacp_aggregator *); 89b47888ceSAndrew Thompson static void lacp_transit_expire(void *); 90b47888ceSAndrew Thompson static void lacp_select_active_aggregator(struct lacp_softc *); 91b47888ceSAndrew Thompson static uint16_t lacp_compose_key(struct lacp_port *); 92b47888ceSAndrew Thompson static int tlv_check(const void *, size_t, const struct tlvhdr *, 93b47888ceSAndrew Thompson const struct tlv_template *, boolean_t); 94b47888ceSAndrew Thompson static void lacp_tick(void *); 95b47888ceSAndrew Thompson 96b47888ceSAndrew Thompson static void lacp_fill_aggregator_id(struct lacp_aggregator *, 97b47888ceSAndrew Thompson const struct lacp_port *); 98b47888ceSAndrew Thompson static void lacp_fill_aggregator_id_peer(struct lacp_peerinfo *, 99b47888ceSAndrew Thompson const struct lacp_peerinfo *); 100b47888ceSAndrew Thompson static int lacp_aggregator_is_compatible(const struct lacp_aggregator *, 101b47888ceSAndrew Thompson const struct lacp_port *); 102b47888ceSAndrew Thompson static int lacp_peerinfo_is_compatible(const struct lacp_peerinfo *, 103b47888ceSAndrew Thompson const struct lacp_peerinfo *); 104b47888ceSAndrew Thompson 105b47888ceSAndrew Thompson static struct lacp_aggregator *lacp_aggregator_get(struct lacp_softc *, 106b47888ceSAndrew Thompson struct lacp_port *); 107b47888ceSAndrew Thompson static void lacp_aggregator_addref(struct lacp_softc *, 108b47888ceSAndrew Thompson struct lacp_aggregator *); 109b47888ceSAndrew Thompson static void lacp_aggregator_delref(struct lacp_softc *, 110b47888ceSAndrew Thompson struct lacp_aggregator *); 111b47888ceSAndrew Thompson 112b47888ceSAndrew Thompson /* receive machine */ 113b47888ceSAndrew Thompson 114b47888ceSAndrew Thompson static void lacp_sm_rx(struct lacp_port *, const struct lacpdu *); 115b47888ceSAndrew Thompson static void lacp_sm_rx_timer(struct lacp_port *); 116b47888ceSAndrew Thompson static void lacp_sm_rx_set_expired(struct lacp_port *); 117b47888ceSAndrew Thompson static void lacp_sm_rx_update_ntt(struct lacp_port *, 118b47888ceSAndrew Thompson const struct lacpdu *); 119b47888ceSAndrew Thompson static void lacp_sm_rx_record_pdu(struct lacp_port *, 120b47888ceSAndrew Thompson const struct lacpdu *); 121b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected(struct lacp_port *, 122b47888ceSAndrew Thompson const struct lacpdu *); 123b47888ceSAndrew Thompson static void lacp_sm_rx_record_default(struct lacp_port *); 124b47888ceSAndrew Thompson static void lacp_sm_rx_update_default_selected(struct lacp_port *); 125b47888ceSAndrew Thompson static void lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *, 126b47888ceSAndrew Thompson const struct lacp_peerinfo *); 127b47888ceSAndrew Thompson 128b47888ceSAndrew Thompson /* mux machine */ 129b47888ceSAndrew Thompson 130b47888ceSAndrew Thompson static void lacp_sm_mux(struct lacp_port *); 131b47888ceSAndrew Thompson static void lacp_set_mux(struct lacp_port *, enum lacp_mux_state); 132b47888ceSAndrew Thompson static void lacp_sm_mux_timer(struct lacp_port *); 133b47888ceSAndrew Thompson 134b47888ceSAndrew Thompson /* periodic transmit machine */ 135b47888ceSAndrew Thompson 136b47888ceSAndrew Thompson static void lacp_sm_ptx_update_timeout(struct lacp_port *, uint8_t); 137b47888ceSAndrew Thompson static void lacp_sm_ptx_tx_schedule(struct lacp_port *); 138b47888ceSAndrew Thompson static void lacp_sm_ptx_timer(struct lacp_port *); 139b47888ceSAndrew Thompson 140b47888ceSAndrew Thompson /* transmit machine */ 141b47888ceSAndrew Thompson 142b47888ceSAndrew Thompson static void lacp_sm_tx(struct lacp_port *); 143b47888ceSAndrew Thompson static void lacp_sm_assert_ntt(struct lacp_port *); 144b47888ceSAndrew Thompson 145b47888ceSAndrew Thompson static void lacp_run_timers(struct lacp_port *); 146b47888ceSAndrew Thompson static int lacp_compare_peerinfo(const struct lacp_peerinfo *, 147b47888ceSAndrew Thompson const struct lacp_peerinfo *); 148b47888ceSAndrew Thompson static int lacp_compare_systemid(const struct lacp_systemid *, 149b47888ceSAndrew Thompson const struct lacp_systemid *); 150b47888ceSAndrew Thompson static void lacp_port_enable(struct lacp_port *); 151b47888ceSAndrew Thompson static void lacp_port_disable(struct lacp_port *); 152b47888ceSAndrew Thompson static void lacp_select(struct lacp_port *); 153b47888ceSAndrew Thompson static void lacp_unselect(struct lacp_port *); 154b47888ceSAndrew Thompson static void lacp_disable_collecting(struct lacp_port *); 155b47888ceSAndrew Thompson static void lacp_enable_collecting(struct lacp_port *); 156b47888ceSAndrew Thompson static void lacp_disable_distributing(struct lacp_port *); 157b47888ceSAndrew Thompson static void lacp_enable_distributing(struct lacp_port *); 158b47888ceSAndrew Thompson static int lacp_xmit_lacpdu(struct lacp_port *); 159b47888ceSAndrew Thompson 160b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 161b47888ceSAndrew Thompson static void lacp_dump_lacpdu(const struct lacpdu *); 162b47888ceSAndrew Thompson static const char *lacp_format_partner(const struct lacp_peerinfo *, char *, 163b47888ceSAndrew Thompson size_t); 164b47888ceSAndrew Thompson static const char *lacp_format_lagid(const struct lacp_peerinfo *, 165b47888ceSAndrew Thompson const struct lacp_peerinfo *, char *, size_t); 166b47888ceSAndrew Thompson static const char *lacp_format_lagid_aggregator(const struct lacp_aggregator *, 167b47888ceSAndrew Thompson char *, size_t); 168b47888ceSAndrew Thompson static const char *lacp_format_state(uint8_t, char *, size_t); 169b47888ceSAndrew Thompson static const char *lacp_format_mac(const uint8_t *, char *, size_t); 170b47888ceSAndrew Thompson static const char *lacp_format_systemid(const struct lacp_systemid *, char *, 171b47888ceSAndrew Thompson size_t); 172b47888ceSAndrew Thompson static const char *lacp_format_portid(const struct lacp_portid *, char *, 173b47888ceSAndrew Thompson size_t); 174b47888ceSAndrew Thompson static void lacp_dprintf(const struct lacp_port *, const char *, ...) 175b47888ceSAndrew Thompson __attribute__((__format__(__printf__, 2, 3))); 176b47888ceSAndrew Thompson #define LACP_DPRINTF(a) lacp_dprintf a 177b47888ceSAndrew Thompson #else 178b47888ceSAndrew Thompson #define LACP_DPRINTF(a) /* nothing */ 179b47888ceSAndrew Thompson #endif 180b47888ceSAndrew Thompson 181b47888ceSAndrew Thompson /* 182b47888ceSAndrew Thompson * partner administration variables. 183b47888ceSAndrew Thompson * XXX should be configurable. 184b47888ceSAndrew Thompson */ 185b47888ceSAndrew Thompson 186b47888ceSAndrew Thompson static const struct lacp_peerinfo lacp_partner_admin = { 187b47888ceSAndrew Thompson .lip_systemid = { .lsi_prio = 0xffff }, 188b47888ceSAndrew Thompson .lip_portid = { .lpi_prio = 0xffff }, 189b47888ceSAndrew Thompson #if 1 190b47888ceSAndrew Thompson /* optimistic */ 191b47888ceSAndrew Thompson .lip_state = LACP_STATE_SYNC | LACP_STATE_AGGREGATION | 192b47888ceSAndrew Thompson LACP_STATE_COLLECTING | LACP_STATE_DISTRIBUTING, 193b47888ceSAndrew Thompson #else 194b47888ceSAndrew Thompson /* pessimistic */ 195b47888ceSAndrew Thompson .lip_state = 0, 196b47888ceSAndrew Thompson #endif 197b47888ceSAndrew Thompson }; 198b47888ceSAndrew Thompson 199b47888ceSAndrew Thompson static const lacp_timer_func_t lacp_timer_funcs[LACP_NTIMER] = { 200b47888ceSAndrew Thompson [LACP_TIMER_CURRENT_WHILE] = lacp_sm_rx_timer, 201b47888ceSAndrew Thompson [LACP_TIMER_PERIODIC] = lacp_sm_ptx_timer, 202b47888ceSAndrew Thompson [LACP_TIMER_WAIT_WHILE] = lacp_sm_mux_timer, 203b47888ceSAndrew Thompson }; 204b47888ceSAndrew Thompson 205b47888ceSAndrew Thompson /* 206b47888ceSAndrew Thompson * lacp_input: process lacpdu 207b47888ceSAndrew Thompson */ 208b47888ceSAndrew Thompson int 209b47888ceSAndrew Thompson lacp_input(struct trunk_port *tp, struct mbuf *m) 210b47888ceSAndrew Thompson { 211b47888ceSAndrew Thompson struct lacp_port *lp = LACP_PORT(tp); 212b47888ceSAndrew Thompson struct lacpdu *du; 213b47888ceSAndrew Thompson int error = 0; 214b47888ceSAndrew Thompson 215b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 216b47888ceSAndrew Thompson 217b47888ceSAndrew Thompson if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 218b47888ceSAndrew Thompson goto bad; 219b47888ceSAndrew Thompson } 220b47888ceSAndrew Thompson 221b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*du)) { 222b47888ceSAndrew Thompson goto bad; 223b47888ceSAndrew Thompson } 224b47888ceSAndrew Thompson 225b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 226b47888ceSAndrew Thompson goto bad; 227b47888ceSAndrew Thompson } 228b47888ceSAndrew Thompson 229b47888ceSAndrew Thompson if (m->m_len < sizeof(*du)) { 230b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*du)); 231b47888ceSAndrew Thompson if (m == NULL) { 232b47888ceSAndrew Thompson return (ENOMEM); 233b47888ceSAndrew Thompson } 234b47888ceSAndrew Thompson } 235b47888ceSAndrew Thompson 236b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 237b47888ceSAndrew Thompson 238b47888ceSAndrew Thompson if (memcmp(&du->ldu_eh.ether_dhost, 239b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 240b47888ceSAndrew Thompson goto bad; 241b47888ceSAndrew Thompson } 242b47888ceSAndrew Thompson 243b47888ceSAndrew Thompson /* XXX 244b47888ceSAndrew Thompson KASSERT(du->ldu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_LACP, 245b47888ceSAndrew Thompson ("a very bad kassert!")); 246b47888ceSAndrew Thompson */ 247b47888ceSAndrew Thompson 248b47888ceSAndrew Thompson /* 249b47888ceSAndrew Thompson * ignore the version for compatibility with 250b47888ceSAndrew Thompson * the future protocol revisions. 251b47888ceSAndrew Thompson */ 252b47888ceSAndrew Thompson 253b47888ceSAndrew Thompson #if 0 254b47888ceSAndrew Thompson if (du->ldu_sph.sph_version != 1) { 255b47888ceSAndrew Thompson goto bad; 256b47888ceSAndrew Thompson } 257b47888ceSAndrew Thompson #endif 258b47888ceSAndrew Thompson 259b47888ceSAndrew Thompson /* 260b47888ceSAndrew Thompson * ignore tlv types for compatibility with 261b47888ceSAndrew Thompson * the future protocol revisions. 262b47888ceSAndrew Thompson */ 263b47888ceSAndrew Thompson 264b47888ceSAndrew Thompson if (tlv_check(du, sizeof(*du), &du->ldu_tlv_actor, 265b47888ceSAndrew Thompson lacp_info_tlv_template, FALSE)) { 266b47888ceSAndrew Thompson goto bad; 267b47888ceSAndrew Thompson } 268b47888ceSAndrew Thompson 269b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 270b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu receive\n")); 271b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 272b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 273b47888ceSAndrew Thompson lacp_sm_rx(lp, du); 274b47888ceSAndrew Thompson 275b47888ceSAndrew Thompson m_freem(m); 276b47888ceSAndrew Thompson 277b47888ceSAndrew Thompson return (error); 278b47888ceSAndrew Thompson 279b47888ceSAndrew Thompson bad: 280b47888ceSAndrew Thompson m_freem(m); 281b47888ceSAndrew Thompson return (EINVAL); 282b47888ceSAndrew Thompson } 283b47888ceSAndrew Thompson 284b47888ceSAndrew Thompson static void 285b47888ceSAndrew Thompson lacp_fill_actorinfo(struct lacp_port *lp, struct lacp_peerinfo *info) 286b47888ceSAndrew Thompson { 287b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 288b47888ceSAndrew Thompson struct trunk_softc *tr = tp->tp_trunk; 289b47888ceSAndrew Thompson 290b47888ceSAndrew Thompson info->lip_systemid.lsi_prio = htons(LACP_SYSTEM_PRIO); 291b47888ceSAndrew Thompson memcpy(&info->lip_systemid.lsi_mac, 292b47888ceSAndrew Thompson IF_LLADDR(tr->tr_ifp), ETHER_ADDR_LEN); 293b47888ceSAndrew Thompson info->lip_portid.lpi_prio = htons(LACP_PORT_PRIO); 294b47888ceSAndrew Thompson info->lip_portid.lpi_portno = htons(lp->lp_ifp->if_index); 295b47888ceSAndrew Thompson info->lip_state = lp->lp_state; 296b47888ceSAndrew Thompson } 297b47888ceSAndrew Thompson 298b47888ceSAndrew Thompson static int 299b47888ceSAndrew Thompson lacp_xmit_lacpdu(struct lacp_port *lp) 300b47888ceSAndrew Thompson { 301b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 302b47888ceSAndrew Thompson struct mbuf *m; 303b47888ceSAndrew Thompson struct lacpdu *du; 304b47888ceSAndrew Thompson int error; 305b47888ceSAndrew Thompson 306b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 307b47888ceSAndrew Thompson 308b47888ceSAndrew Thompson m = m_gethdr(M_DONTWAIT, MT_DATA); 309b47888ceSAndrew Thompson if (m == NULL) { 310b47888ceSAndrew Thompson return (ENOMEM); 311b47888ceSAndrew Thompson } 312b47888ceSAndrew Thompson m->m_len = m->m_pkthdr.len = sizeof(*du); 313b47888ceSAndrew Thompson 314b47888ceSAndrew Thompson du = mtod(m, struct lacpdu *); 315b47888ceSAndrew Thompson memset(du, 0, sizeof(*du)); 316b47888ceSAndrew Thompson 317b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_dhost, ethermulticastaddr_slowprotocols, 318b47888ceSAndrew Thompson ETHER_ADDR_LEN); 319b47888ceSAndrew Thompson memcpy(&du->ldu_eh.ether_shost, tp->tp_lladdr, ETHER_ADDR_LEN); 320b47888ceSAndrew Thompson du->ldu_eh.ether_type = htons(ETHERTYPE_SLOW); 321b47888ceSAndrew Thompson 322b47888ceSAndrew Thompson du->ldu_sph.sph_subtype = SLOWPROTOCOLS_SUBTYPE_LACP; 323b47888ceSAndrew Thompson du->ldu_sph.sph_version = 1; 324b47888ceSAndrew Thompson 325b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_actor, LACP_TYPE_ACTORINFO, sizeof(du->ldu_actor)); 326b47888ceSAndrew Thompson du->ldu_actor = lp->lp_actor; 327b47888ceSAndrew Thompson 328b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_partner, LACP_TYPE_PARTNERINFO, 329b47888ceSAndrew Thompson sizeof(du->ldu_partner)); 330b47888ceSAndrew Thompson du->ldu_partner = lp->lp_partner; 331b47888ceSAndrew Thompson 332b47888ceSAndrew Thompson TLV_SET(&du->ldu_tlv_collector, LACP_TYPE_COLLECTORINFO, 333b47888ceSAndrew Thompson sizeof(du->ldu_collector)); 334b47888ceSAndrew Thompson du->ldu_collector.lci_maxdelay = 0; 335b47888ceSAndrew Thompson 336b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 337b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit\n")); 338b47888ceSAndrew Thompson lacp_dump_lacpdu(du); 339b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 340b47888ceSAndrew Thompson 341b47888ceSAndrew Thompson m->m_flags |= M_MCAST; 342b47888ceSAndrew Thompson 343b47888ceSAndrew Thompson /* 344b47888ceSAndrew Thompson * XXX should use higher priority queue. 345b47888ceSAndrew Thompson * otherwise network congestion can break aggregation. 346b47888ceSAndrew Thompson */ 347b47888ceSAndrew Thompson 348b47888ceSAndrew Thompson error = trunk_enqueue(lp->lp_ifp, m); 349b47888ceSAndrew Thompson return (error); 350b47888ceSAndrew Thompson } 351b47888ceSAndrew Thompson 352b47888ceSAndrew Thompson void 353b47888ceSAndrew Thompson lacp_linkstate(struct trunk_port *tp) 354b47888ceSAndrew Thompson { 355b47888ceSAndrew Thompson struct lacp_port *lp = LACP_PORT(tp); 356b47888ceSAndrew Thompson struct ifnet *ifp = tp->tp_ifp; 357b47888ceSAndrew Thompson struct ifmediareq ifmr; 358b47888ceSAndrew Thompson int error = 0; 359b47888ceSAndrew Thompson u_int media; 360b47888ceSAndrew Thompson uint8_t old_state; 361b47888ceSAndrew Thompson uint16_t old_key; 362b47888ceSAndrew Thompson 363b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 364b47888ceSAndrew Thompson 365b47888ceSAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr)); 366b47888ceSAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 367b47888ceSAndrew Thompson if (error != 0) 368b47888ceSAndrew Thompson return; 369b47888ceSAndrew Thompson 370b47888ceSAndrew Thompson media = ifmr.ifm_active; 371b47888ceSAndrew Thompson LACP_DPRINTF((lp, "media changed 0x%x -> 0x%x\n", lp->lp_media, media)); 372b47888ceSAndrew Thompson old_state = lp->lp_state; 373b47888ceSAndrew Thompson old_key = lp->lp_key; 374b47888ceSAndrew Thompson 375b47888ceSAndrew Thompson lp->lp_media = media; 376b47888ceSAndrew Thompson if ((media & IFM_HDX) != 0 || ifp->if_link_state == LINK_STATE_DOWN) { 377b47888ceSAndrew Thompson lacp_port_disable(lp); 378b47888ceSAndrew Thompson } else { 379b47888ceSAndrew Thompson lacp_port_enable(lp); 380b47888ceSAndrew Thompson } 381b47888ceSAndrew Thompson lp->lp_key = lacp_compose_key(lp); 382b47888ceSAndrew Thompson 383b47888ceSAndrew Thompson if (old_state != lp->lp_state || old_key != lp->lp_key) { 384b47888ceSAndrew Thompson LACP_DPRINTF((lp, "-> UNSELECTED\n")); 385b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 386b47888ceSAndrew Thompson } 387b47888ceSAndrew Thompson } 388b47888ceSAndrew Thompson 389b47888ceSAndrew Thompson static void 390b47888ceSAndrew Thompson lacp_tick(void *arg) 391b47888ceSAndrew Thompson { 392b47888ceSAndrew Thompson struct lacp_softc *lsc = arg; 393b47888ceSAndrew Thompson struct lacp_port *lp; 394b47888ceSAndrew Thompson 395b47888ceSAndrew Thompson LIST_FOREACH(lp, &lsc->lsc_ports, lp_next) { 396b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) 397b47888ceSAndrew Thompson continue; 398b47888ceSAndrew Thompson 399b47888ceSAndrew Thompson lacp_run_timers(lp); 400b47888ceSAndrew Thompson 401b47888ceSAndrew Thompson lacp_select(lp); 402b47888ceSAndrew Thompson lacp_sm_mux(lp); 403b47888ceSAndrew Thompson lacp_sm_tx(lp); 404b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(lp); 405b47888ceSAndrew Thompson } 406b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 407b47888ceSAndrew Thompson } 408b47888ceSAndrew Thompson 409b47888ceSAndrew Thompson int 410b47888ceSAndrew Thompson lacp_port_create(struct trunk_port *tp) 411b47888ceSAndrew Thompson { 412b47888ceSAndrew Thompson struct trunk_softc *tr = tp->tp_trunk; 413b47888ceSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(tr); 414b47888ceSAndrew Thompson struct lacp_port *lp; 415b47888ceSAndrew Thompson struct ifnet *ifp = tp->tp_ifp; 416b47888ceSAndrew Thompson struct sockaddr_dl sdl; 417b47888ceSAndrew Thompson struct ifmultiaddr *rifma = NULL; 418b47888ceSAndrew Thompson int error; 419b47888ceSAndrew Thompson 420b47888ceSAndrew Thompson boolean_t active = TRUE; /* XXX should be configurable */ 421b47888ceSAndrew Thompson boolean_t fast = FALSE; /* XXX should be configurable */ 422b47888ceSAndrew Thompson 423b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tr); 424b47888ceSAndrew Thompson 425b47888ceSAndrew Thompson bzero((char *)&sdl, sizeof(sdl)); 426b47888ceSAndrew Thompson sdl.sdl_len = sizeof(sdl); 427b47888ceSAndrew Thompson sdl.sdl_family = AF_LINK; 428b47888ceSAndrew Thompson sdl.sdl_index = ifp->if_index; 429b47888ceSAndrew Thompson sdl.sdl_type = IFT_ETHER; 430b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 431b47888ceSAndrew Thompson 432b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 433b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 434b47888ceSAndrew Thompson error = if_addmulti(ifp, (struct sockaddr *)&sdl, &rifma); 435b47888ceSAndrew Thompson if (error) { 436b47888ceSAndrew Thompson printf("%s: ADDMULTI failed on %s\n", __func__, tp->tp_ifname); 437b47888ceSAndrew Thompson return (error); 438b47888ceSAndrew Thompson } 439b47888ceSAndrew Thompson 440b47888ceSAndrew Thompson lp = malloc(sizeof(struct lacp_port), 441b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 442b47888ceSAndrew Thompson if (lp == NULL) 443b47888ceSAndrew Thompson return (ENOMEM); 444b47888ceSAndrew Thompson 445b47888ceSAndrew Thompson tp->tp_psc = (caddr_t)lp; 446b47888ceSAndrew Thompson lp->lp_ifp = ifp; 447b47888ceSAndrew Thompson lp->lp_trunk = tp; 448b47888ceSAndrew Thompson lp->lp_lsc = lsc; 449b47888ceSAndrew Thompson 450b47888ceSAndrew Thompson LIST_INSERT_HEAD(&lsc->lsc_ports, lp, lp_next); 451b47888ceSAndrew Thompson 452b47888ceSAndrew Thompson lacp_fill_actorinfo(lp, &lp->lp_actor); 453b47888ceSAndrew Thompson lp->lp_state = 454b47888ceSAndrew Thompson (active ? LACP_STATE_ACTIVITY : 0) | 455b47888ceSAndrew Thompson (fast ? LACP_STATE_TIMEOUT : 0); 456b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 457b47888ceSAndrew Thompson lacp_linkstate(tp); 458b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 459b47888ceSAndrew Thompson 460b47888ceSAndrew Thompson return (0); 461b47888ceSAndrew Thompson } 462b47888ceSAndrew Thompson 463b47888ceSAndrew Thompson void 464b47888ceSAndrew Thompson lacp_port_destroy(struct trunk_port *tp) 465b47888ceSAndrew Thompson { 466b47888ceSAndrew Thompson struct lacp_port *lp = LACP_PORT(tp); 467b47888ceSAndrew Thompson struct ifnet *ifp = tp->tp_ifp; 468b47888ceSAndrew Thompson struct sockaddr_dl sdl; 469b47888ceSAndrew Thompson int i, error; 470b47888ceSAndrew Thompson 471b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 472b47888ceSAndrew Thompson 473b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 474b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, i); 475b47888ceSAndrew Thompson } 476b47888ceSAndrew Thompson 477b47888ceSAndrew Thompson lacp_disable_collecting(lp); 478b47888ceSAndrew Thompson lacp_disable_distributing(lp); 479b47888ceSAndrew Thompson lacp_unselect(lp); 480b47888ceSAndrew Thompson 481b47888ceSAndrew Thompson bzero((char *)&sdl, sizeof(sdl)); 482b47888ceSAndrew Thompson sdl.sdl_len = sizeof(sdl); 483b47888ceSAndrew Thompson sdl.sdl_family = AF_LINK; 484b47888ceSAndrew Thompson sdl.sdl_index = ifp->if_index; 485b47888ceSAndrew Thompson sdl.sdl_type = IFT_ETHER; 486b47888ceSAndrew Thompson sdl.sdl_alen = ETHER_ADDR_LEN; 487b47888ceSAndrew Thompson 488b47888ceSAndrew Thompson bcopy(ðermulticastaddr_slowprotocols, 489b47888ceSAndrew Thompson LLADDR(&sdl), ETHER_ADDR_LEN); 490b47888ceSAndrew Thompson error = if_delmulti(ifp, (struct sockaddr *)&sdl); 491b47888ceSAndrew Thompson if (error) 492b47888ceSAndrew Thompson printf("%s: DELMULTI failed on %s\n", __func__, tp->tp_ifname); 493b47888ceSAndrew Thompson 494b47888ceSAndrew Thompson LIST_REMOVE(lp, lp_next); 495b47888ceSAndrew Thompson free(lp, M_DEVBUF); 496b47888ceSAndrew Thompson } 497b47888ceSAndrew Thompson 498b47888ceSAndrew Thompson int 499b47888ceSAndrew Thompson lacp_port_isactive(struct trunk_port *tp) 500b47888ceSAndrew Thompson { 501b47888ceSAndrew Thompson struct lacp_port *lp = LACP_PORT(tp); 502b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 503b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 504b47888ceSAndrew Thompson 505b47888ceSAndrew Thompson /* This port is joined to the active aggregator */ 506b47888ceSAndrew Thompson if (la != NULL && la == lsc->lsc_active_aggregator) 507b47888ceSAndrew Thompson return (1); 508b47888ceSAndrew Thompson 509b47888ceSAndrew Thompson return (0); 510b47888ceSAndrew Thompson } 511b47888ceSAndrew Thompson 512b47888ceSAndrew Thompson static void 513b47888ceSAndrew Thompson lacp_disable_collecting(struct lacp_port *lp) 514b47888ceSAndrew Thompson { 515b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 516b47888ceSAndrew Thompson 517b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting disabled\n")); 518b47888ceSAndrew Thompson 519b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_COLLECTING; 520b47888ceSAndrew Thompson tp->tp_flags &= ~TRUNK_PORT_COLLECTING; 521b47888ceSAndrew Thompson } 522b47888ceSAndrew Thompson 523b47888ceSAndrew Thompson static void 524b47888ceSAndrew Thompson lacp_enable_collecting(struct lacp_port *lp) 525b47888ceSAndrew Thompson { 526b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 527b47888ceSAndrew Thompson 528b47888ceSAndrew Thompson LACP_DPRINTF((lp, "collecting enabled\n")); 529b47888ceSAndrew Thompson 530b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_COLLECTING; 531b47888ceSAndrew Thompson tp->tp_flags |= TRUNK_PORT_COLLECTING; 532b47888ceSAndrew Thompson } 533b47888ceSAndrew Thompson 534b47888ceSAndrew Thompson static void 535b47888ceSAndrew Thompson lacp_disable_distributing(struct lacp_port *lp) 536b47888ceSAndrew Thompson { 537b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 538b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 539b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 540b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 541b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 542b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 543b47888ceSAndrew Thompson 544b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 545b47888ceSAndrew Thompson 546b47888ceSAndrew Thompson if (la == NULL || (lp->lp_state & LACP_STATE_DISTRIBUTING) == 0) { 547b47888ceSAndrew Thompson return; 548b47888ceSAndrew Thompson } 549b47888ceSAndrew Thompson 550b47888ceSAndrew Thompson KASSERT(!TAILQ_EMPTY(&la->la_ports), ("no aggregator ports")); 551b47888ceSAndrew Thompson KASSERT(la->la_nports > 0, ("nports invalid (%d)", la->la_nports)); 552b47888ceSAndrew Thompson KASSERT(la->la_refcnt >= la->la_nports, ("aggregator refcnt invalid")); 553b47888ceSAndrew Thompson 554b47888ceSAndrew Thompson LACP_DPRINTF((lp, "disable distributing on aggregator %s, " 555b47888ceSAndrew Thompson "nports %d -> %d\n", 556b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 557b47888ceSAndrew Thompson la->la_nports, la->la_nports - 1)); 558b47888ceSAndrew Thompson 559b47888ceSAndrew Thompson TAILQ_REMOVE(&la->la_ports, lp, lp_dist_q); 560b47888ceSAndrew Thompson la->la_nports--; 561b47888ceSAndrew Thompson 562b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 563b47888ceSAndrew Thompson 564b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DISTRIBUTING; 565b47888ceSAndrew Thompson tp->tp_flags &= ~TRUNK_PORT_DISTRIBUTING; 566b47888ceSAndrew Thompson 567b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator == la) { 568b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 569b47888ceSAndrew Thompson } 570b47888ceSAndrew Thompson } 571b47888ceSAndrew Thompson 572b47888ceSAndrew Thompson static void 573b47888ceSAndrew Thompson lacp_enable_distributing(struct lacp_port *lp) 574b47888ceSAndrew Thompson { 575b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 576b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 577b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 578b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 579b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 580b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 581b47888ceSAndrew Thompson 582b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 583b47888ceSAndrew Thompson 584b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0) { 585b47888ceSAndrew Thompson return; 586b47888ceSAndrew Thompson } 587b47888ceSAndrew Thompson 588b47888ceSAndrew Thompson LACP_DPRINTF((lp, "enable distributing on aggregator %s, " 589b47888ceSAndrew Thompson "nports %d -> %d\n", 590b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 591b47888ceSAndrew Thompson la->la_nports, la->la_nports + 1)); 592b47888ceSAndrew Thompson 593b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("aggregator refcnt invalid")); 594b47888ceSAndrew Thompson TAILQ_INSERT_HEAD(&la->la_ports, lp, lp_dist_q); 595b47888ceSAndrew Thompson la->la_nports++; 596b47888ceSAndrew Thompson 597b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, la); 598b47888ceSAndrew Thompson 599b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DISTRIBUTING; 600b47888ceSAndrew Thompson tp->tp_flags |= TRUNK_PORT_DISTRIBUTING; 601b47888ceSAndrew Thompson 602b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 603b47888ceSAndrew Thompson lacp_select_active_aggregator(lsc); 604b47888ceSAndrew Thompson } 605b47888ceSAndrew Thompson } 606b47888ceSAndrew Thompson 607b47888ceSAndrew Thompson static void 608b47888ceSAndrew Thompson lacp_transit_expire(void *vp) 609b47888ceSAndrew Thompson { 610b47888ceSAndrew Thompson struct lacp_softc *lsc = vp; 611b47888ceSAndrew Thompson 612b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s\n", __func__)); 613b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = FALSE; 614b47888ceSAndrew Thompson } 615b47888ceSAndrew Thompson 616b47888ceSAndrew Thompson int 617b47888ceSAndrew Thompson lacp_attach(struct trunk_softc *tr) 618b47888ceSAndrew Thompson { 619b47888ceSAndrew Thompson struct lacp_softc *lsc; 620b47888ceSAndrew Thompson 621b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tr); 622b47888ceSAndrew Thompson 623b47888ceSAndrew Thompson lsc = malloc(sizeof(struct lacp_softc), 624b47888ceSAndrew Thompson M_DEVBUF, M_NOWAIT|M_ZERO); 625b47888ceSAndrew Thompson if (lsc == NULL) 626b47888ceSAndrew Thompson return (ENOMEM); 627b47888ceSAndrew Thompson 628b47888ceSAndrew Thompson tr->tr_psc = (caddr_t)lsc; 629b47888ceSAndrew Thompson lsc->lsc_trunk = tr; 630b47888ceSAndrew Thompson 631b47888ceSAndrew Thompson lsc->lsc_hashkey = arc4random(); 632b47888ceSAndrew Thompson lsc->lsc_active_aggregator = NULL; 633b47888ceSAndrew Thompson TAILQ_INIT(&lsc->lsc_aggregators); 634b47888ceSAndrew Thompson LIST_INIT(&lsc->lsc_ports); 635b47888ceSAndrew Thompson 636b47888ceSAndrew Thompson callout_init_mtx(&lsc->lsc_transit_callout, &tr->tr_mtx, 0); 637b47888ceSAndrew Thompson callout_init_mtx(&lsc->lsc_callout, &tr->tr_mtx, 0); 638b47888ceSAndrew Thompson 639b47888ceSAndrew Thompson /* if the trunk is already up then do the same */ 640b47888ceSAndrew Thompson if (tr->tr_ifp->if_drv_flags & IFF_DRV_RUNNING) 641b47888ceSAndrew Thompson lacp_init(tr); 642b47888ceSAndrew Thompson 643b47888ceSAndrew Thompson return (0); 644b47888ceSAndrew Thompson } 645b47888ceSAndrew Thompson 646b47888ceSAndrew Thompson int 647b47888ceSAndrew Thompson lacp_detach(struct trunk_softc *tr) 648b47888ceSAndrew Thompson { 649b47888ceSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(tr); 650b47888ceSAndrew Thompson 651b47888ceSAndrew Thompson KASSERT(TAILQ_EMPTY(&lsc->lsc_aggregators), 652b47888ceSAndrew Thompson ("aggregators still active")); 653b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator == NULL, 654b47888ceSAndrew Thompson ("aggregator still attached")); 655b47888ceSAndrew Thompson 656b47888ceSAndrew Thompson tr->tr_psc = NULL; 657b47888ceSAndrew Thompson callout_drain(&lsc->lsc_transit_callout); 658b47888ceSAndrew Thompson callout_drain(&lsc->lsc_callout); 659b47888ceSAndrew Thompson 660b47888ceSAndrew Thompson free(lsc, M_DEVBUF); 661b47888ceSAndrew Thompson return (0); 662b47888ceSAndrew Thompson } 663b47888ceSAndrew Thompson 664b47888ceSAndrew Thompson void 665b47888ceSAndrew Thompson lacp_init(struct trunk_softc *tr) 666b47888ceSAndrew Thompson { 667b47888ceSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(tr); 668b47888ceSAndrew Thompson 669b47888ceSAndrew Thompson callout_reset(&lsc->lsc_callout, hz, lacp_tick, lsc); 670b47888ceSAndrew Thompson } 671b47888ceSAndrew Thompson 672b47888ceSAndrew Thompson void 673b47888ceSAndrew Thompson lacp_stop(struct trunk_softc *tr) 674b47888ceSAndrew Thompson { 675b47888ceSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(tr); 676b47888ceSAndrew Thompson 677b47888ceSAndrew Thompson callout_stop(&lsc->lsc_transit_callout); 678b47888ceSAndrew Thompson callout_stop(&lsc->lsc_callout); 679b47888ceSAndrew Thompson } 680b47888ceSAndrew Thompson 681b47888ceSAndrew Thompson struct trunk_port * 682b47888ceSAndrew Thompson lacp_select_tx_port(struct trunk_softc *tr, struct mbuf *m) 683b47888ceSAndrew Thompson { 684b47888ceSAndrew Thompson struct lacp_softc *lsc = LACP_SOFTC(tr); 685b47888ceSAndrew Thompson struct lacp_aggregator *la; 686b47888ceSAndrew Thompson struct lacp_port *lp; 687b47888ceSAndrew Thompson uint32_t hash; 688b47888ceSAndrew Thompson int nports; 689b47888ceSAndrew Thompson 690b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tr); 691b47888ceSAndrew Thompson 692b47888ceSAndrew Thompson if (__predict_false(lsc->lsc_suppress_distributing)) { 693b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: waiting transit\n", __func__)); 694b47888ceSAndrew Thompson return (NULL); 695b47888ceSAndrew Thompson } 696b47888ceSAndrew Thompson 697b47888ceSAndrew Thompson la = lsc->lsc_active_aggregator; 698b47888ceSAndrew Thompson if (__predict_false(la == NULL)) { 699b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: no active aggregator\n", __func__)); 700b47888ceSAndrew Thompson return (NULL); 701b47888ceSAndrew Thompson } 702b47888ceSAndrew Thompson 703b47888ceSAndrew Thompson nports = la->la_nports; 704b47888ceSAndrew Thompson KASSERT(nports > 0, ("no ports available")); 705b47888ceSAndrew Thompson 706b47888ceSAndrew Thompson hash = trunk_hashmbuf(m, lsc->lsc_hashkey); 707b47888ceSAndrew Thompson hash %= nports; 708b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 709b47888ceSAndrew Thompson while (hash--) { 710b47888ceSAndrew Thompson lp = TAILQ_NEXT(lp, lp_dist_q); 711b47888ceSAndrew Thompson } 712b47888ceSAndrew Thompson 713b47888ceSAndrew Thompson KASSERT((lp->lp_state & LACP_STATE_DISTRIBUTING) != 0, 714b47888ceSAndrew Thompson ("aggregated port is not distributing")); 715b47888ceSAndrew Thompson 716b47888ceSAndrew Thompson return (lp->lp_trunk); 717b47888ceSAndrew Thompson } 718b47888ceSAndrew Thompson /* 719b47888ceSAndrew Thompson * lacp_suppress_distributing: drop transmit packets for a while 720b47888ceSAndrew Thompson * to preserve packet ordering. 721b47888ceSAndrew Thompson */ 722b47888ceSAndrew Thompson 723b47888ceSAndrew Thompson static void 724b47888ceSAndrew Thompson lacp_suppress_distributing(struct lacp_softc *lsc, struct lacp_aggregator *la) 725b47888ceSAndrew Thompson { 726b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != la) { 727b47888ceSAndrew Thompson return; 728b47888ceSAndrew Thompson } 729b47888ceSAndrew Thompson 730b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s\n", __func__)); 731b47888ceSAndrew Thompson lsc->lsc_suppress_distributing = TRUE; 732b47888ceSAndrew Thompson /* XXX should consider collector max delay */ 733b47888ceSAndrew Thompson callout_reset(&lsc->lsc_transit_callout, 734b47888ceSAndrew Thompson LACP_TRANSIT_DELAY * hz / 1000, lacp_transit_expire, lsc); 735b47888ceSAndrew Thompson } 736b47888ceSAndrew Thompson 737b47888ceSAndrew Thompson static int 738b47888ceSAndrew Thompson lacp_compare_peerinfo(const struct lacp_peerinfo *a, 739b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 740b47888ceSAndrew Thompson { 741b47888ceSAndrew Thompson return (memcmp(a, b, offsetof(struct lacp_peerinfo, lip_state))); 742b47888ceSAndrew Thompson } 743b47888ceSAndrew Thompson 744b47888ceSAndrew Thompson static int 745b47888ceSAndrew Thompson lacp_compare_systemid(const struct lacp_systemid *a, 746b47888ceSAndrew Thompson const struct lacp_systemid *b) 747b47888ceSAndrew Thompson { 748b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 749b47888ceSAndrew Thompson } 750b47888ceSAndrew Thompson 751b47888ceSAndrew Thompson #if 0 /* unused */ 752b47888ceSAndrew Thompson static int 753b47888ceSAndrew Thompson lacp_compare_portid(const struct lacp_portid *a, 754b47888ceSAndrew Thompson const struct lacp_portid *b) 755b47888ceSAndrew Thompson { 756b47888ceSAndrew Thompson return (memcmp(a, b, sizeof(*a))); 757b47888ceSAndrew Thompson } 758b47888ceSAndrew Thompson #endif 759b47888ceSAndrew Thompson 760b47888ceSAndrew Thompson static uint64_t 761b47888ceSAndrew Thompson lacp_aggregator_bandwidth(struct lacp_aggregator *la) 762b47888ceSAndrew Thompson { 763b47888ceSAndrew Thompson struct lacp_port *lp; 764b47888ceSAndrew Thompson uint64_t speed; 765b47888ceSAndrew Thompson 766b47888ceSAndrew Thompson lp = TAILQ_FIRST(&la->la_ports); 767b47888ceSAndrew Thompson if (lp == NULL) { 768b47888ceSAndrew Thompson return (0); 769b47888ceSAndrew Thompson } 770b47888ceSAndrew Thompson 771b47888ceSAndrew Thompson speed = ifmedia_baudrate(lp->lp_media); 772b47888ceSAndrew Thompson speed *= la->la_nports; 773b47888ceSAndrew Thompson if (speed == 0) { 774b47888ceSAndrew Thompson LACP_DPRINTF((lp, "speed 0? media=0x%x nports=%d\n", 775b47888ceSAndrew Thompson lp->lp_media, la->la_nports)); 776b47888ceSAndrew Thompson } 777b47888ceSAndrew Thompson 778b47888ceSAndrew Thompson return (speed); 779b47888ceSAndrew Thompson } 780b47888ceSAndrew Thompson 781b47888ceSAndrew Thompson /* 782b47888ceSAndrew Thompson * lacp_select_active_aggregator: select an aggregator to be used to transmit 783b47888ceSAndrew Thompson * packets from trunk(4) interface. 784b47888ceSAndrew Thompson */ 785b47888ceSAndrew Thompson 786b47888ceSAndrew Thompson static void 787b47888ceSAndrew Thompson lacp_select_active_aggregator(struct lacp_softc *lsc) 788b47888ceSAndrew Thompson { 789b47888ceSAndrew Thompson struct lacp_aggregator *la; 790b47888ceSAndrew Thompson struct lacp_aggregator *best_la = NULL; 791b47888ceSAndrew Thompson uint64_t best_speed = 0; 792b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 793b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 794b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 795b47888ceSAndrew Thompson 796b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s:\n", __func__)); 797b47888ceSAndrew Thompson 798b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 799b47888ceSAndrew Thompson uint64_t speed; 800b47888ceSAndrew Thompson 801b47888ceSAndrew Thompson if (la->la_nports == 0) { 802b47888ceSAndrew Thompson continue; 803b47888ceSAndrew Thompson } 804b47888ceSAndrew Thompson 805b47888ceSAndrew Thompson speed = lacp_aggregator_bandwidth(la); 806b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s, speed=%jd, nports=%d\n", 807b47888ceSAndrew Thompson lacp_format_lagid_aggregator(la, buf, sizeof(buf)), 808b47888ceSAndrew Thompson speed, la->la_nports)); 809b47888ceSAndrew Thompson if (speed > best_speed || 810b47888ceSAndrew Thompson (speed == best_speed && 811b47888ceSAndrew Thompson la == lsc->lsc_active_aggregator)) { 812b47888ceSAndrew Thompson best_la = la; 813b47888ceSAndrew Thompson best_speed = speed; 814b47888ceSAndrew Thompson } 815b47888ceSAndrew Thompson } 816b47888ceSAndrew Thompson 817b47888ceSAndrew Thompson KASSERT(best_la == NULL || best_la->la_nports > 0, 818b47888ceSAndrew Thompson ("invalid aggregator refcnt")); 819b47888ceSAndrew Thompson KASSERT(best_la == NULL || !TAILQ_EMPTY(&best_la->la_ports), 820b47888ceSAndrew Thompson ("invalid aggregator list")); 821b47888ceSAndrew Thompson 822b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 823b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 824b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator changed\n")); 825b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "old %s\n", 826b47888ceSAndrew Thompson lacp_format_lagid_aggregator(lsc->lsc_active_aggregator, 827b47888ceSAndrew Thompson buf, sizeof(buf)))); 828b47888ceSAndrew Thompson } else { 829b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "active aggregator not changed\n")); 830b47888ceSAndrew Thompson } 831b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "new %s\n", 832b47888ceSAndrew Thompson lacp_format_lagid_aggregator(best_la, buf, sizeof(buf)))); 833b47888ceSAndrew Thompson #endif /* defined(LACP_DEBUG) */ 834b47888ceSAndrew Thompson 835b47888ceSAndrew Thompson if (lsc->lsc_active_aggregator != best_la) { 836b47888ceSAndrew Thompson lsc->lsc_active_aggregator = best_la; 837b47888ceSAndrew Thompson if (best_la) { 838b47888ceSAndrew Thompson lacp_suppress_distributing(lsc, best_la); 839b47888ceSAndrew Thompson } 840b47888ceSAndrew Thompson } 841b47888ceSAndrew Thompson } 842b47888ceSAndrew Thompson 843b47888ceSAndrew Thompson static uint16_t 844b47888ceSAndrew Thompson lacp_compose_key(struct lacp_port *lp) 845b47888ceSAndrew Thompson { 846b47888ceSAndrew Thompson struct trunk_port *tp = lp->lp_trunk; 847b47888ceSAndrew Thompson struct trunk_softc *tr = tp->tp_trunk; 848b47888ceSAndrew Thompson u_int media = lp->lp_media; 849b47888ceSAndrew Thompson uint16_t key; 850b47888ceSAndrew Thompson 851b47888ceSAndrew Thompson KASSERT(IFM_TYPE(media) == IFM_ETHER, ("invalid interface type")); 852b47888ceSAndrew Thompson 853b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_AGGREGATION) == 0) { 854b47888ceSAndrew Thompson 855b47888ceSAndrew Thompson /* 856b47888ceSAndrew Thompson * non-aggregatable links should have unique keys. 857b47888ceSAndrew Thompson * 858b47888ceSAndrew Thompson * XXX this isn't really unique as if_index is 16 bit. 859b47888ceSAndrew Thompson */ 860b47888ceSAndrew Thompson 861b47888ceSAndrew Thompson /* bit 0..14: (some bits of) if_index of this port */ 862b47888ceSAndrew Thompson key = lp->lp_ifp->if_index; 863b47888ceSAndrew Thompson /* bit 15: 1 */ 864b47888ceSAndrew Thompson key |= 0x8000; 865b47888ceSAndrew Thompson } else { 866b47888ceSAndrew Thompson u_int subtype = IFM_SUBTYPE(media); 867b47888ceSAndrew Thompson 868b47888ceSAndrew Thompson KASSERT((media & IFM_HDX) == 0, ("aggregating HDX interface")); 869b47888ceSAndrew Thompson 870b47888ceSAndrew Thompson /* bit 0..4: IFM_SUBTYPE */ 871b47888ceSAndrew Thompson key = subtype; 872b47888ceSAndrew Thompson /* bit 5..14: (some bits of) if_index of trunk device */ 873b47888ceSAndrew Thompson key |= 0x7fe0 & ((tr->tr_ifp->if_index) << 5); 874b47888ceSAndrew Thompson /* bit 15: 0 */ 875b47888ceSAndrew Thompson } 876b47888ceSAndrew Thompson return (htons(key)); 877b47888ceSAndrew Thompson } 878b47888ceSAndrew Thompson 879b47888ceSAndrew Thompson static void 880b47888ceSAndrew Thompson lacp_aggregator_addref(struct lacp_softc *lsc, struct lacp_aggregator *la) 881b47888ceSAndrew Thompson { 882b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 883b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 884b47888ceSAndrew Thompson #endif 885b47888ceSAndrew Thompson 886b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 887b47888ceSAndrew Thompson __func__, 888b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 889b47888ceSAndrew Thompson buf, sizeof(buf)), 890b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt + 1)); 891b47888ceSAndrew Thompson 892b47888ceSAndrew Thompson KASSERT(la->la_refcnt > 0, ("refcount <= 0")); 893b47888ceSAndrew Thompson la->la_refcnt++; 894b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcount")); 895b47888ceSAndrew Thompson } 896b47888ceSAndrew Thompson 897b47888ceSAndrew Thompson static void 898b47888ceSAndrew Thompson lacp_aggregator_delref(struct lacp_softc *lsc, struct lacp_aggregator *la) 899b47888ceSAndrew Thompson { 900b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 901b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 902b47888ceSAndrew Thompson #endif 903b47888ceSAndrew Thompson 904b47888ceSAndrew Thompson LACP_DPRINTF((NULL, "%s: lagid=%s, refcnt %d -> %d\n", 905b47888ceSAndrew Thompson __func__, 906b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 907b47888ceSAndrew Thompson buf, sizeof(buf)), 908b47888ceSAndrew Thompson la->la_refcnt, la->la_refcnt - 1)); 909b47888ceSAndrew Thompson 910b47888ceSAndrew Thompson KASSERT(la->la_refcnt > la->la_nports, ("invalid refcnt")); 911b47888ceSAndrew Thompson la->la_refcnt--; 912b47888ceSAndrew Thompson if (la->la_refcnt > 0) { 913b47888ceSAndrew Thompson return; 914b47888ceSAndrew Thompson } 915b47888ceSAndrew Thompson 916b47888ceSAndrew Thompson KASSERT(la->la_refcnt == 0, ("refcount not zero")); 917b47888ceSAndrew Thompson KASSERT(lsc->lsc_active_aggregator != la, ("aggregator active")); 918b47888ceSAndrew Thompson 919b47888ceSAndrew Thompson TAILQ_REMOVE(&lsc->lsc_aggregators, la, la_q); 920b47888ceSAndrew Thompson 921b47888ceSAndrew Thompson free(la, M_DEVBUF); 922b47888ceSAndrew Thompson } 923b47888ceSAndrew Thompson 924b47888ceSAndrew Thompson /* 925b47888ceSAndrew Thompson * lacp_aggregator_get: allocate an aggregator. 926b47888ceSAndrew Thompson */ 927b47888ceSAndrew Thompson 928b47888ceSAndrew Thompson static struct lacp_aggregator * 929b47888ceSAndrew Thompson lacp_aggregator_get(struct lacp_softc *lsc, struct lacp_port *lp) 930b47888ceSAndrew Thompson { 931b47888ceSAndrew Thompson struct lacp_aggregator *la; 932b47888ceSAndrew Thompson 933b47888ceSAndrew Thompson la = malloc(sizeof(*la), M_DEVBUF, M_NOWAIT); 934b47888ceSAndrew Thompson if (la) { 935b47888ceSAndrew Thompson la->la_refcnt = 1; 936b47888ceSAndrew Thompson la->la_nports = 0; 937b47888ceSAndrew Thompson TAILQ_INIT(&la->la_ports); 938b47888ceSAndrew Thompson la->la_pending = 0; 939b47888ceSAndrew Thompson TAILQ_INSERT_TAIL(&lsc->lsc_aggregators, la, la_q); 940b47888ceSAndrew Thompson } 941b47888ceSAndrew Thompson 942b47888ceSAndrew Thompson return (la); 943b47888ceSAndrew Thompson } 944b47888ceSAndrew Thompson 945b47888ceSAndrew Thompson /* 946b47888ceSAndrew Thompson * lacp_fill_aggregator_id: setup a newly allocated aggregator from a port. 947b47888ceSAndrew Thompson */ 948b47888ceSAndrew Thompson 949b47888ceSAndrew Thompson static void 950b47888ceSAndrew Thompson lacp_fill_aggregator_id(struct lacp_aggregator *la, const struct lacp_port *lp) 951b47888ceSAndrew Thompson { 952b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_partner, &lp->lp_partner); 953b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(&la->la_actor, &lp->lp_actor); 954b47888ceSAndrew Thompson 955b47888ceSAndrew Thompson la->la_actor.lip_state = lp->lp_state & LACP_STATE_AGGREGATION; 956b47888ceSAndrew Thompson } 957b47888ceSAndrew Thompson 958b47888ceSAndrew Thompson static void 959b47888ceSAndrew Thompson lacp_fill_aggregator_id_peer(struct lacp_peerinfo *lpi_aggr, 960b47888ceSAndrew Thompson const struct lacp_peerinfo *lpi_port) 961b47888ceSAndrew Thompson { 962b47888ceSAndrew Thompson memset(lpi_aggr, 0, sizeof(*lpi_aggr)); 963b47888ceSAndrew Thompson lpi_aggr->lip_systemid = lpi_port->lip_systemid; 964b47888ceSAndrew Thompson lpi_aggr->lip_key = lpi_port->lip_key; 965b47888ceSAndrew Thompson } 966b47888ceSAndrew Thompson 967b47888ceSAndrew Thompson /* 968b47888ceSAndrew Thompson * lacp_aggregator_is_compatible: check if a port can join to an aggregator. 969b47888ceSAndrew Thompson */ 970b47888ceSAndrew Thompson 971b47888ceSAndrew Thompson static int 972b47888ceSAndrew Thompson lacp_aggregator_is_compatible(const struct lacp_aggregator *la, 973b47888ceSAndrew Thompson const struct lacp_port *lp) 974b47888ceSAndrew Thompson { 975b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) || 976b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_AGGREGATION)) { 977b47888ceSAndrew Thompson return (0); 978b47888ceSAndrew Thompson } 979b47888ceSAndrew Thompson 980b47888ceSAndrew Thompson if (!(la->la_actor.lip_state & LACP_STATE_AGGREGATION)) { 981b47888ceSAndrew Thompson return (0); 982b47888ceSAndrew Thompson } 983b47888ceSAndrew Thompson 984b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_partner, &lp->lp_partner)) { 985b47888ceSAndrew Thompson return (0); 986b47888ceSAndrew Thompson } 987b47888ceSAndrew Thompson 988b47888ceSAndrew Thompson if (!lacp_peerinfo_is_compatible(&la->la_actor, &lp->lp_actor)) { 989b47888ceSAndrew Thompson return (0); 990b47888ceSAndrew Thompson } 991b47888ceSAndrew Thompson 992b47888ceSAndrew Thompson return (1); 993b47888ceSAndrew Thompson } 994b47888ceSAndrew Thompson 995b47888ceSAndrew Thompson static int 996b47888ceSAndrew Thompson lacp_peerinfo_is_compatible(const struct lacp_peerinfo *a, 997b47888ceSAndrew Thompson const struct lacp_peerinfo *b) 998b47888ceSAndrew Thompson { 999b47888ceSAndrew Thompson if (memcmp(&a->lip_systemid, &b->lip_systemid, 1000b47888ceSAndrew Thompson sizeof(a->lip_systemid))) { 1001b47888ceSAndrew Thompson return (0); 1002b47888ceSAndrew Thompson } 1003b47888ceSAndrew Thompson 1004b47888ceSAndrew Thompson if (memcmp(&a->lip_key, &b->lip_key, sizeof(a->lip_key))) { 1005b47888ceSAndrew Thompson return (0); 1006b47888ceSAndrew Thompson } 1007b47888ceSAndrew Thompson 1008b47888ceSAndrew Thompson return (1); 1009b47888ceSAndrew Thompson } 1010b47888ceSAndrew Thompson 1011b47888ceSAndrew Thompson static void 1012b47888ceSAndrew Thompson lacp_port_enable(struct lacp_port *lp) 1013b47888ceSAndrew Thompson { 1014b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_AGGREGATION; 1015b47888ceSAndrew Thompson } 1016b47888ceSAndrew Thompson 1017b47888ceSAndrew Thompson static void 1018b47888ceSAndrew Thompson lacp_port_disable(struct lacp_port *lp) 1019b47888ceSAndrew Thompson { 1020b47888ceSAndrew Thompson lacp_set_mux(lp, LACP_MUX_DETACHED); 1021b47888ceSAndrew Thompson 1022b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_AGGREGATION; 1023b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1024b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1025b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_AGGREGATION; 1026b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1027b47888ceSAndrew Thompson } 1028b47888ceSAndrew Thompson 1029b47888ceSAndrew Thompson /* 1030b47888ceSAndrew Thompson * lacp_select: select an aggregator. create one if necessary. 1031b47888ceSAndrew Thompson */ 1032b47888ceSAndrew Thompson static void 1033b47888ceSAndrew Thompson lacp_select(struct lacp_port *lp) 1034b47888ceSAndrew Thompson { 1035b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1036b47888ceSAndrew Thompson struct lacp_aggregator *la; 1037b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1038b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1039b47888ceSAndrew Thompson #endif 1040b47888ceSAndrew Thompson 1041b47888ceSAndrew Thompson if (lp->lp_aggregator) { 1042b47888ceSAndrew Thompson return; 1043b47888ceSAndrew Thompson } 1044b47888ceSAndrew Thompson 1045b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1046b47888ceSAndrew Thompson ("timer_wait_while still active")); 1047b47888ceSAndrew Thompson 1048b47888ceSAndrew Thompson LACP_DPRINTF((lp, "port lagid=%s\n", 1049b47888ceSAndrew Thompson lacp_format_lagid(&lp->lp_actor, &lp->lp_partner, 1050b47888ceSAndrew Thompson buf, sizeof(buf)))); 1051b47888ceSAndrew Thompson 1052b47888ceSAndrew Thompson TAILQ_FOREACH(la, &lsc->lsc_aggregators, la_q) { 1053b47888ceSAndrew Thompson if (lacp_aggregator_is_compatible(la, lp)) { 1054b47888ceSAndrew Thompson break; 1055b47888ceSAndrew Thompson } 1056b47888ceSAndrew Thompson } 1057b47888ceSAndrew Thompson 1058b47888ceSAndrew Thompson if (la == NULL) { 1059b47888ceSAndrew Thompson la = lacp_aggregator_get(lsc, lp); 1060b47888ceSAndrew Thompson if (la == NULL) { 1061b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator creation failed\n")); 1062b47888ceSAndrew Thompson 1063b47888ceSAndrew Thompson /* 1064b47888ceSAndrew Thompson * will retry on the next tick. 1065b47888ceSAndrew Thompson */ 1066b47888ceSAndrew Thompson 1067b47888ceSAndrew Thompson return; 1068b47888ceSAndrew Thompson } 1069b47888ceSAndrew Thompson lacp_fill_aggregator_id(la, lp); 1070b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator created\n")); 1071b47888ceSAndrew Thompson } else { 1072b47888ceSAndrew Thompson LACP_DPRINTF((lp, "compatible aggregator found\n")); 1073b47888ceSAndrew Thompson lacp_aggregator_addref(lsc, la); 1074b47888ceSAndrew Thompson } 1075b47888ceSAndrew Thompson 1076b47888ceSAndrew Thompson LACP_DPRINTF((lp, "aggregator lagid=%s\n", 1077b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1078b47888ceSAndrew Thompson buf, sizeof(buf)))); 1079b47888ceSAndrew Thompson 1080b47888ceSAndrew Thompson lp->lp_aggregator = la; 1081b47888ceSAndrew Thompson lp->lp_selected = LACP_SELECTED; 1082b47888ceSAndrew Thompson } 1083b47888ceSAndrew Thompson 1084b47888ceSAndrew Thompson /* 1085b47888ceSAndrew Thompson * lacp_unselect: finish unselect/detach process. 1086b47888ceSAndrew Thompson */ 1087b47888ceSAndrew Thompson 1088b47888ceSAndrew Thompson static void 1089b47888ceSAndrew Thompson lacp_unselect(struct lacp_port *lp) 1090b47888ceSAndrew Thompson { 1091b47888ceSAndrew Thompson struct lacp_softc *lsc = lp->lp_lsc; 1092b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1093b47888ceSAndrew Thompson 1094b47888ceSAndrew Thompson KASSERT(!LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1095b47888ceSAndrew Thompson ("timer_wait_while still active")); 1096b47888ceSAndrew Thompson 1097b47888ceSAndrew Thompson if (la == NULL) { 1098b47888ceSAndrew Thompson return; 1099b47888ceSAndrew Thompson } 1100b47888ceSAndrew Thompson 1101b47888ceSAndrew Thompson lp->lp_aggregator = NULL; 1102b47888ceSAndrew Thompson lacp_aggregator_delref(lsc, la); 1103b47888ceSAndrew Thompson } 1104b47888ceSAndrew Thompson 1105b47888ceSAndrew Thompson /* mux machine */ 1106b47888ceSAndrew Thompson 1107b47888ceSAndrew Thompson static void 1108b47888ceSAndrew Thompson lacp_sm_mux(struct lacp_port *lp) 1109b47888ceSAndrew Thompson { 1110b47888ceSAndrew Thompson enum lacp_mux_state new_state; 1111b47888ceSAndrew Thompson boolean_t p_sync = 1112b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_SYNC) != 0; 1113b47888ceSAndrew Thompson boolean_t p_collecting = 1114b47888ceSAndrew Thompson (lp->lp_partner.lip_state & LACP_STATE_COLLECTING) != 0; 1115b47888ceSAndrew Thompson enum lacp_selected selected = lp->lp_selected; 1116b47888ceSAndrew Thompson struct lacp_aggregator *la; 1117b47888ceSAndrew Thompson 1118b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s: state %d\n", __func__, lp->lp_mux_state)); */ 1119b47888ceSAndrew Thompson 1120b47888ceSAndrew Thompson re_eval: 1121b47888ceSAndrew Thompson la = lp->lp_aggregator; 1122b47888ceSAndrew Thompson KASSERT(lp->lp_mux_state == LACP_MUX_DETACHED || la != NULL, 1123b47888ceSAndrew Thompson ("MUX not detached")); 1124b47888ceSAndrew Thompson new_state = lp->lp_mux_state; 1125b47888ceSAndrew Thompson switch (lp->lp_mux_state) { 1126b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1127b47888ceSAndrew Thompson if (selected != LACP_UNSELECTED) { 1128b47888ceSAndrew Thompson new_state = LACP_MUX_WAITING; 1129b47888ceSAndrew Thompson } 1130b47888ceSAndrew Thompson break; 1131b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1132b47888ceSAndrew Thompson KASSERT(la->la_pending > 0 || 1133b47888ceSAndrew Thompson !LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE), 1134b47888ceSAndrew Thompson ("timer_wait_while still active")); 1135b47888ceSAndrew Thompson if (selected == LACP_SELECTED && la->la_pending == 0) { 1136b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1137b47888ceSAndrew Thompson } else if (selected == LACP_UNSELECTED) { 1138b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1139b47888ceSAndrew Thompson } 1140b47888ceSAndrew Thompson break; 1141b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1142b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync) { 1143b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1144b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED) { 1145b47888ceSAndrew Thompson new_state = LACP_MUX_DETACHED; 1146b47888ceSAndrew Thompson } 1147b47888ceSAndrew Thompson break; 1148b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1149b47888ceSAndrew Thompson if (selected == LACP_SELECTED && p_sync && p_collecting) { 1150b47888ceSAndrew Thompson new_state = LACP_MUX_DISTRIBUTING; 1151b47888ceSAndrew Thompson } else if (selected != LACP_SELECTED || !p_sync) { 1152b47888ceSAndrew Thompson new_state = LACP_MUX_ATTACHED; 1153b47888ceSAndrew Thompson } 1154b47888ceSAndrew Thompson break; 1155b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1156b47888ceSAndrew Thompson if (selected != LACP_SELECTED || !p_sync || !p_collecting) { 1157b47888ceSAndrew Thompson new_state = LACP_MUX_COLLECTING; 1158b47888ceSAndrew Thompson } 1159b47888ceSAndrew Thompson break; 1160b47888ceSAndrew Thompson default: 1161b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1162b47888ceSAndrew Thompson } 1163b47888ceSAndrew Thompson 1164b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1165b47888ceSAndrew Thompson return; 1166b47888ceSAndrew Thompson } 1167b47888ceSAndrew Thompson 1168b47888ceSAndrew Thompson lacp_set_mux(lp, new_state); 1169b47888ceSAndrew Thompson goto re_eval; 1170b47888ceSAndrew Thompson } 1171b47888ceSAndrew Thompson 1172b47888ceSAndrew Thompson static void 1173b47888ceSAndrew Thompson lacp_set_mux(struct lacp_port *lp, enum lacp_mux_state new_state) 1174b47888ceSAndrew Thompson { 1175b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1176b47888ceSAndrew Thompson 1177b47888ceSAndrew Thompson if (lp->lp_mux_state == new_state) { 1178b47888ceSAndrew Thompson return; 1179b47888ceSAndrew Thompson } 1180b47888ceSAndrew Thompson 1181b47888ceSAndrew Thompson switch (new_state) { 1182b47888ceSAndrew Thompson case LACP_MUX_DETACHED: 1183b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_SYNC; 1184b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1185b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1186b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1187b47888ceSAndrew Thompson /* cancel timer */ 1188b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_WAIT_WHILE)) { 1189b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, 1190b47888ceSAndrew Thompson ("timer_wait_while not active")); 1191b47888ceSAndrew Thompson la->la_pending--; 1192b47888ceSAndrew Thompson } 1193b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_WAIT_WHILE); 1194b47888ceSAndrew Thompson lacp_unselect(lp); 1195b47888ceSAndrew Thompson break; 1196b47888ceSAndrew Thompson case LACP_MUX_WAITING: 1197b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_WAIT_WHILE, 1198b47888ceSAndrew Thompson LACP_AGGREGATE_WAIT_TIME); 1199b47888ceSAndrew Thompson la->la_pending++; 1200b47888ceSAndrew Thompson break; 1201b47888ceSAndrew Thompson case LACP_MUX_ATTACHED: 1202b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_SYNC; 1203b47888ceSAndrew Thompson lacp_disable_collecting(lp); 1204b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1205b47888ceSAndrew Thompson break; 1206b47888ceSAndrew Thompson case LACP_MUX_COLLECTING: 1207b47888ceSAndrew Thompson lacp_enable_collecting(lp); 1208b47888ceSAndrew Thompson lacp_disable_distributing(lp); 1209b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1210b47888ceSAndrew Thompson break; 1211b47888ceSAndrew Thompson case LACP_MUX_DISTRIBUTING: 1212b47888ceSAndrew Thompson lacp_enable_distributing(lp); 1213b47888ceSAndrew Thompson break; 1214b47888ceSAndrew Thompson default: 1215b47888ceSAndrew Thompson panic("%s: unknown state", __func__); 1216b47888ceSAndrew Thompson } 1217b47888ceSAndrew Thompson 1218b47888ceSAndrew Thompson LACP_DPRINTF((lp, "mux_state %d -> %d\n", lp->lp_mux_state, new_state)); 1219b47888ceSAndrew Thompson 1220b47888ceSAndrew Thompson lp->lp_mux_state = new_state; 1221b47888ceSAndrew Thompson } 1222b47888ceSAndrew Thompson 1223b47888ceSAndrew Thompson static void 1224b47888ceSAndrew Thompson lacp_sm_mux_timer(struct lacp_port *lp) 1225b47888ceSAndrew Thompson { 1226b47888ceSAndrew Thompson struct lacp_aggregator *la = lp->lp_aggregator; 1227b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1228b47888ceSAndrew Thompson char buf[LACP_LAGIDSTR_MAX+1]; 1229b47888ceSAndrew Thompson #endif 1230b47888ceSAndrew Thompson 1231b47888ceSAndrew Thompson KASSERT(la->la_pending > 0, ("no pending event")); 1232b47888ceSAndrew Thompson 1233b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: aggregator %s, pending %d -> %d\n", __func__, 1234b47888ceSAndrew Thompson lacp_format_lagid(&la->la_actor, &la->la_partner, 1235b47888ceSAndrew Thompson buf, sizeof(buf)), 1236b47888ceSAndrew Thompson la->la_pending, la->la_pending - 1)); 1237b47888ceSAndrew Thompson 1238b47888ceSAndrew Thompson la->la_pending--; 1239b47888ceSAndrew Thompson } 1240b47888ceSAndrew Thompson 1241b47888ceSAndrew Thompson /* periodic transmit machine */ 1242b47888ceSAndrew Thompson 1243b47888ceSAndrew Thompson static void 1244b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(struct lacp_port *lp, uint8_t oldpstate) 1245b47888ceSAndrew Thompson { 1246b47888ceSAndrew Thompson if (LACP_STATE_EQ(oldpstate, lp->lp_partner.lip_state, 1247b47888ceSAndrew Thompson LACP_STATE_TIMEOUT)) { 1248b47888ceSAndrew Thompson return; 1249b47888ceSAndrew Thompson } 1250b47888ceSAndrew Thompson 1251b47888ceSAndrew Thompson LACP_DPRINTF((lp, "partner timeout changed\n")); 1252b47888ceSAndrew Thompson 1253b47888ceSAndrew Thompson /* 1254b47888ceSAndrew Thompson * FAST_PERIODIC -> SLOW_PERIODIC 1255b47888ceSAndrew Thompson * or 1256b47888ceSAndrew Thompson * SLOW_PERIODIC (-> PERIODIC_TX) -> FAST_PERIODIC 1257b47888ceSAndrew Thompson * 1258b47888ceSAndrew Thompson * let lacp_sm_ptx_tx_schedule to update timeout. 1259b47888ceSAndrew Thompson */ 1260b47888ceSAndrew Thompson 1261b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1262b47888ceSAndrew Thompson 1263b47888ceSAndrew Thompson /* 1264b47888ceSAndrew Thompson * if timeout has been shortened, assert NTT. 1265b47888ceSAndrew Thompson */ 1266b47888ceSAndrew Thompson 1267b47888ceSAndrew Thompson if ((lp->lp_partner.lip_state & LACP_STATE_TIMEOUT)) { 1268b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1269b47888ceSAndrew Thompson } 1270b47888ceSAndrew Thompson } 1271b47888ceSAndrew Thompson 1272b47888ceSAndrew Thompson static void 1273b47888ceSAndrew Thompson lacp_sm_ptx_tx_schedule(struct lacp_port *lp) 1274b47888ceSAndrew Thompson { 1275b47888ceSAndrew Thompson int timeout; 1276b47888ceSAndrew Thompson 1277b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_ACTIVITY) && 1278b47888ceSAndrew Thompson !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) { 1279b47888ceSAndrew Thompson 1280b47888ceSAndrew Thompson /* 1281b47888ceSAndrew Thompson * NO_PERIODIC 1282b47888ceSAndrew Thompson */ 1283b47888ceSAndrew Thompson 1284b47888ceSAndrew Thompson LACP_TIMER_DISARM(lp, LACP_TIMER_PERIODIC); 1285b47888ceSAndrew Thompson return; 1286b47888ceSAndrew Thompson } 1287b47888ceSAndrew Thompson 1288b47888ceSAndrew Thompson if (LACP_TIMER_ISARMED(lp, LACP_TIMER_PERIODIC)) { 1289b47888ceSAndrew Thompson return; 1290b47888ceSAndrew Thompson } 1291b47888ceSAndrew Thompson 1292b47888ceSAndrew Thompson timeout = (lp->lp_partner.lip_state & LACP_STATE_TIMEOUT) ? 1293b47888ceSAndrew Thompson LACP_FAST_PERIODIC_TIME : LACP_SLOW_PERIODIC_TIME; 1294b47888ceSAndrew Thompson 1295b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_PERIODIC, timeout); 1296b47888ceSAndrew Thompson } 1297b47888ceSAndrew Thompson 1298b47888ceSAndrew Thompson static void 1299b47888ceSAndrew Thompson lacp_sm_ptx_timer(struct lacp_port *lp) 1300b47888ceSAndrew Thompson { 1301b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1302b47888ceSAndrew Thompson } 1303b47888ceSAndrew Thompson 1304b47888ceSAndrew Thompson static void 1305b47888ceSAndrew Thompson lacp_sm_rx(struct lacp_port *lp, const struct lacpdu *du) 1306b47888ceSAndrew Thompson { 1307b47888ceSAndrew Thompson int timeout; 1308b47888ceSAndrew Thompson 1309b47888ceSAndrew Thompson /* 1310b47888ceSAndrew Thompson * check LACP_DISABLED first 1311b47888ceSAndrew Thompson */ 1312b47888ceSAndrew Thompson 1313b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION)) { 1314b47888ceSAndrew Thompson return; 1315b47888ceSAndrew Thompson } 1316b47888ceSAndrew Thompson 1317b47888ceSAndrew Thompson /* 1318b47888ceSAndrew Thompson * check loopback condition. 1319b47888ceSAndrew Thompson */ 1320b47888ceSAndrew Thompson 1321b47888ceSAndrew Thompson if (!lacp_compare_systemid(&du->ldu_actor.lip_systemid, 1322b47888ceSAndrew Thompson &lp->lp_actor.lip_systemid)) { 1323b47888ceSAndrew Thompson return; 1324b47888ceSAndrew Thompson } 1325b47888ceSAndrew Thompson 1326b47888ceSAndrew Thompson /* 1327b47888ceSAndrew Thompson * EXPIRED, DEFAULTED, CURRENT -> CURRENT 1328b47888ceSAndrew Thompson */ 1329b47888ceSAndrew Thompson 1330b47888ceSAndrew Thompson lacp_sm_rx_update_selected(lp, du); 1331b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(lp, du); 1332b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(lp, du); 1333b47888ceSAndrew Thompson 1334b47888ceSAndrew Thompson timeout = (lp->lp_state & LACP_STATE_TIMEOUT) ? 1335b47888ceSAndrew Thompson LACP_SHORT_TIMEOUT_TIME : LACP_LONG_TIMEOUT_TIME; 1336b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, timeout); 1337b47888ceSAndrew Thompson 1338b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1339b47888ceSAndrew Thompson 1340b47888ceSAndrew Thompson /* 1341b47888ceSAndrew Thompson * kick transmit machine without waiting the next tick. 1342b47888ceSAndrew Thompson */ 1343b47888ceSAndrew Thompson 1344b47888ceSAndrew Thompson lacp_sm_tx(lp); 1345b47888ceSAndrew Thompson } 1346b47888ceSAndrew Thompson 1347b47888ceSAndrew Thompson static void 1348b47888ceSAndrew Thompson lacp_sm_rx_set_expired(struct lacp_port *lp) 1349b47888ceSAndrew Thompson { 1350b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1351b47888ceSAndrew Thompson lp->lp_partner.lip_state |= LACP_STATE_TIMEOUT; 1352b47888ceSAndrew Thompson LACP_TIMER_ARM(lp, LACP_TIMER_CURRENT_WHILE, LACP_SHORT_TIMEOUT_TIME); 1353b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_EXPIRED; 1354b47888ceSAndrew Thompson } 1355b47888ceSAndrew Thompson 1356b47888ceSAndrew Thompson static void 1357b47888ceSAndrew Thompson lacp_sm_rx_timer(struct lacp_port *lp) 1358b47888ceSAndrew Thompson { 1359b47888ceSAndrew Thompson if ((lp->lp_state & LACP_STATE_EXPIRED) == 0) { 1360b47888ceSAndrew Thompson /* CURRENT -> EXPIRED */ 1361b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: CURRENT -> EXPIRED\n", __func__)); 1362b47888ceSAndrew Thompson lacp_sm_rx_set_expired(lp); 1363b47888ceSAndrew Thompson } else { 1364b47888ceSAndrew Thompson /* EXPIRED -> DEFAULTED */ 1365b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: EXPIRED -> DEFAULTED\n", __func__)); 1366b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(lp); 1367b47888ceSAndrew Thompson lacp_sm_rx_record_default(lp); 1368b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_EXPIRED; 1369b47888ceSAndrew Thompson } 1370b47888ceSAndrew Thompson } 1371b47888ceSAndrew Thompson 1372b47888ceSAndrew Thompson static void 1373b47888ceSAndrew Thompson lacp_sm_rx_record_pdu(struct lacp_port *lp, const struct lacpdu *du) 1374b47888ceSAndrew Thompson { 1375b47888ceSAndrew Thompson boolean_t active; 1376b47888ceSAndrew Thompson uint8_t oldpstate; 1377b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1378b47888ceSAndrew Thompson char buf[LACP_STATESTR_MAX+1]; 1379b47888ceSAndrew Thompson #endif 1380b47888ceSAndrew Thompson 1381b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1382b47888ceSAndrew Thompson 1383b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1384b47888ceSAndrew Thompson 1385b47888ceSAndrew Thompson active = (du->ldu_actor.lip_state & LACP_STATE_ACTIVITY) 1386b47888ceSAndrew Thompson || ((lp->lp_state & LACP_STATE_ACTIVITY) && 1387b47888ceSAndrew Thompson (du->ldu_partner.lip_state & LACP_STATE_ACTIVITY)); 1388b47888ceSAndrew Thompson 1389b47888ceSAndrew Thompson lp->lp_partner = du->ldu_actor; 1390b47888ceSAndrew Thompson if (active && 1391b47888ceSAndrew Thompson ((LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1392b47888ceSAndrew Thompson LACP_STATE_AGGREGATION) && 1393b47888ceSAndrew Thompson !lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner)) 1394b47888ceSAndrew Thompson || (du->ldu_partner.lip_state & LACP_STATE_AGGREGATION) == 0)) { 1395b47888ceSAndrew Thompson /* XXX nothing? */ 1396b47888ceSAndrew Thompson } else { 1397b47888ceSAndrew Thompson lp->lp_partner.lip_state &= ~LACP_STATE_SYNC; 1398b47888ceSAndrew Thompson } 1399b47888ceSAndrew Thompson 1400b47888ceSAndrew Thompson lp->lp_state &= ~LACP_STATE_DEFAULTED; 1401b47888ceSAndrew Thompson 1402b47888ceSAndrew Thompson if (oldpstate != lp->lp_partner.lip_state) { 1403b47888ceSAndrew Thompson LACP_DPRINTF((lp, "old pstate %s\n", 1404b47888ceSAndrew Thompson lacp_format_state(oldpstate, buf, sizeof(buf)))); 1405b47888ceSAndrew Thompson LACP_DPRINTF((lp, "new pstate %s\n", 1406b47888ceSAndrew Thompson lacp_format_state(lp->lp_partner.lip_state, buf, 1407b47888ceSAndrew Thompson sizeof(buf)))); 1408b47888ceSAndrew Thompson } 1409b47888ceSAndrew Thompson 1410b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1411b47888ceSAndrew Thompson } 1412b47888ceSAndrew Thompson 1413b47888ceSAndrew Thompson static void 1414b47888ceSAndrew Thompson lacp_sm_rx_update_ntt(struct lacp_port *lp, const struct lacpdu *du) 1415b47888ceSAndrew Thompson { 1416b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1417b47888ceSAndrew Thompson 1418b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_actor, &du->ldu_partner) || 1419b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_state, du->ldu_partner.lip_state, 1420b47888ceSAndrew Thompson LACP_STATE_ACTIVITY | LACP_STATE_SYNC | LACP_STATE_AGGREGATION)) { 1421b47888ceSAndrew Thompson LACP_DPRINTF((lp, "%s: assert ntt\n", __func__)); 1422b47888ceSAndrew Thompson lacp_sm_assert_ntt(lp); 1423b47888ceSAndrew Thompson } 1424b47888ceSAndrew Thompson } 1425b47888ceSAndrew Thompson 1426b47888ceSAndrew Thompson static void 1427b47888ceSAndrew Thompson lacp_sm_rx_record_default(struct lacp_port *lp) 1428b47888ceSAndrew Thompson { 1429b47888ceSAndrew Thompson uint8_t oldpstate; 1430b47888ceSAndrew Thompson 1431b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1432b47888ceSAndrew Thompson 1433b47888ceSAndrew Thompson oldpstate = lp->lp_partner.lip_state; 1434b47888ceSAndrew Thompson lp->lp_partner = lacp_partner_admin; 1435b47888ceSAndrew Thompson lp->lp_state |= LACP_STATE_DEFAULTED; 1436b47888ceSAndrew Thompson lacp_sm_ptx_update_timeout(lp, oldpstate); 1437b47888ceSAndrew Thompson } 1438b47888ceSAndrew Thompson 1439b47888ceSAndrew Thompson static void 1440b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(struct lacp_port *lp, 1441b47888ceSAndrew Thompson const struct lacp_peerinfo *info) 1442b47888ceSAndrew Thompson { 1443b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1444b47888ceSAndrew Thompson 1445b47888ceSAndrew Thompson if (lacp_compare_peerinfo(&lp->lp_partner, info) || 1446b47888ceSAndrew Thompson !LACP_STATE_EQ(lp->lp_partner.lip_state, info->lip_state, 1447b47888ceSAndrew Thompson LACP_STATE_AGGREGATION)) { 1448b47888ceSAndrew Thompson lp->lp_selected = LACP_UNSELECTED; 1449b47888ceSAndrew Thompson /* mux machine will clean up lp->lp_aggregator */ 1450b47888ceSAndrew Thompson } 1451b47888ceSAndrew Thompson } 1452b47888ceSAndrew Thompson 1453b47888ceSAndrew Thompson static void 1454b47888ceSAndrew Thompson lacp_sm_rx_update_selected(struct lacp_port *lp, const struct lacpdu *du) 1455b47888ceSAndrew Thompson { 1456b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1457b47888ceSAndrew Thompson 1458b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &du->ldu_actor); 1459b47888ceSAndrew Thompson } 1460b47888ceSAndrew Thompson 1461b47888ceSAndrew Thompson static void 1462b47888ceSAndrew Thompson lacp_sm_rx_update_default_selected(struct lacp_port *lp) 1463b47888ceSAndrew Thompson { 1464b47888ceSAndrew Thompson /* LACP_DPRINTF((lp, "%s\n", __func__)); */ 1465b47888ceSAndrew Thompson 1466b47888ceSAndrew Thompson lacp_sm_rx_update_selected_from_peerinfo(lp, &lacp_partner_admin); 1467b47888ceSAndrew Thompson } 1468b47888ceSAndrew Thompson 1469b47888ceSAndrew Thompson /* transmit machine */ 1470b47888ceSAndrew Thompson 1471b47888ceSAndrew Thompson static void 1472b47888ceSAndrew Thompson lacp_sm_tx(struct lacp_port *lp) 1473b47888ceSAndrew Thompson { 1474b47888ceSAndrew Thompson int error; 1475b47888ceSAndrew Thompson 1476b47888ceSAndrew Thompson if (!(lp->lp_state & LACP_STATE_AGGREGATION) 1477b47888ceSAndrew Thompson #if 1 1478b47888ceSAndrew Thompson || (!(lp->lp_state & LACP_STATE_ACTIVITY) 1479b47888ceSAndrew Thompson && !(lp->lp_partner.lip_state & LACP_STATE_ACTIVITY)) 1480b47888ceSAndrew Thompson #endif 1481b47888ceSAndrew Thompson ) { 1482b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1483b47888ceSAndrew Thompson } 1484b47888ceSAndrew Thompson 1485b47888ceSAndrew Thompson if (!(lp->lp_flags & LACP_PORT_NTT)) { 1486b47888ceSAndrew Thompson return; 1487b47888ceSAndrew Thompson } 1488b47888ceSAndrew Thompson 1489b47888ceSAndrew Thompson /* Rate limit to 3 PDUs per LACP_FAST_PERIODIC_TIME */ 1490b47888ceSAndrew Thompson if (ppsratecheck(&lp->lp_last_lacpdu, &lp->lp_lacpdu_sent, 1491b47888ceSAndrew Thompson (3 / LACP_FAST_PERIODIC_TIME)) == 0) { 1492b47888ceSAndrew Thompson LACP_DPRINTF((lp, "rate limited pdu\n")); 1493b47888ceSAndrew Thompson return; 1494b47888ceSAndrew Thompson } 1495b47888ceSAndrew Thompson 1496b47888ceSAndrew Thompson error = lacp_xmit_lacpdu(lp); 1497b47888ceSAndrew Thompson 1498b47888ceSAndrew Thompson if (error == 0) { 1499b47888ceSAndrew Thompson lp->lp_flags &= ~LACP_PORT_NTT; 1500b47888ceSAndrew Thompson } else { 1501b47888ceSAndrew Thompson LACP_DPRINTF((lp, "lacpdu transmit failure, error %d\n", 1502b47888ceSAndrew Thompson error)); 1503b47888ceSAndrew Thompson } 1504b47888ceSAndrew Thompson } 1505b47888ceSAndrew Thompson 1506b47888ceSAndrew Thompson static void 1507b47888ceSAndrew Thompson lacp_sm_assert_ntt(struct lacp_port *lp) 1508b47888ceSAndrew Thompson { 1509b47888ceSAndrew Thompson 1510b47888ceSAndrew Thompson lp->lp_flags |= LACP_PORT_NTT; 1511b47888ceSAndrew Thompson } 1512b47888ceSAndrew Thompson 1513b47888ceSAndrew Thompson static void 1514b47888ceSAndrew Thompson lacp_run_timers(struct lacp_port *lp) 1515b47888ceSAndrew Thompson { 1516b47888ceSAndrew Thompson int i; 1517b47888ceSAndrew Thompson 1518b47888ceSAndrew Thompson for (i = 0; i < LACP_NTIMER; i++) { 1519b47888ceSAndrew Thompson KASSERT(lp->lp_timer[i] >= 0, 1520b47888ceSAndrew Thompson ("invalid timer value %d", lp->lp_timer[i])); 1521b47888ceSAndrew Thompson if (lp->lp_timer[i] == 0) { 1522b47888ceSAndrew Thompson continue; 1523b47888ceSAndrew Thompson } else if (--lp->lp_timer[i] <= 0) { 1524b47888ceSAndrew Thompson if (lacp_timer_funcs[i]) { 1525b47888ceSAndrew Thompson (*lacp_timer_funcs[i])(lp); 1526b47888ceSAndrew Thompson } 1527b47888ceSAndrew Thompson } 1528b47888ceSAndrew Thompson } 1529b47888ceSAndrew Thompson } 1530b47888ceSAndrew Thompson 1531b47888ceSAndrew Thompson int 1532b47888ceSAndrew Thompson lacp_marker_input(struct trunk_port *tp, struct mbuf *m) 1533b47888ceSAndrew Thompson { 1534b47888ceSAndrew Thompson struct lacp_port *lp = LACP_PORT(tp); 1535b47888ceSAndrew Thompson struct markerdu *mdu; 1536b47888ceSAndrew Thompson int error = 0; 1537b47888ceSAndrew Thompson 1538b47888ceSAndrew Thompson TRUNK_LOCK_ASSERT(tp->tp_trunk); 1539b47888ceSAndrew Thompson 1540b47888ceSAndrew Thompson if (__predict_false(lp->lp_flags & LACP_PORT_DETACHING)) { 1541b47888ceSAndrew Thompson goto bad; 1542b47888ceSAndrew Thompson } 1543b47888ceSAndrew Thompson 1544b47888ceSAndrew Thompson if (m->m_pkthdr.len != sizeof(*mdu)) { 1545b47888ceSAndrew Thompson goto bad; 1546b47888ceSAndrew Thompson } 1547b47888ceSAndrew Thompson 1548b47888ceSAndrew Thompson if ((m->m_flags & M_MCAST) == 0) { 1549b47888ceSAndrew Thompson goto bad; 1550b47888ceSAndrew Thompson } 1551b47888ceSAndrew Thompson 1552b47888ceSAndrew Thompson if (m->m_len < sizeof(*mdu)) { 1553b47888ceSAndrew Thompson m = m_pullup(m, sizeof(*mdu)); 1554b47888ceSAndrew Thompson if (m == NULL) { 1555b47888ceSAndrew Thompson return (ENOMEM); 1556b47888ceSAndrew Thompson } 1557b47888ceSAndrew Thompson } 1558b47888ceSAndrew Thompson 1559b47888ceSAndrew Thompson mdu = mtod(m, struct markerdu *); 1560b47888ceSAndrew Thompson 1561b47888ceSAndrew Thompson if (memcmp(&mdu->mdu_eh.ether_dhost, 1562b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN)) { 1563b47888ceSAndrew Thompson goto bad; 1564b47888ceSAndrew Thompson } 1565b47888ceSAndrew Thompson 1566b47888ceSAndrew Thompson /* XXX 1567b47888ceSAndrew Thompson KASSERT(mdu->mdu_sph.sph_subtype == SLOWPROTOCOLS_SUBTYPE_MARKER, 1568b47888ceSAndrew Thompson ("a very bad kassert!")); 1569b47888ceSAndrew Thompson */ 1570b47888ceSAndrew Thompson 1571b47888ceSAndrew Thompson if (mdu->mdu_sph.sph_version != 1) { 1572b47888ceSAndrew Thompson goto bad; 1573b47888ceSAndrew Thompson } 1574b47888ceSAndrew Thompson 1575b47888ceSAndrew Thompson switch (mdu->mdu_tlv.tlv_type) { 1576b47888ceSAndrew Thompson case MARKER_TYPE_INFO: 1577b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1578b47888ceSAndrew Thompson marker_info_tlv_template, TRUE)) { 1579b47888ceSAndrew Thompson goto bad; 1580b47888ceSAndrew Thompson } 1581b47888ceSAndrew Thompson mdu->mdu_tlv.tlv_type = MARKER_TYPE_RESPONSE; 1582b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_dhost, 1583b47888ceSAndrew Thompson ðermulticastaddr_slowprotocols, ETHER_ADDR_LEN); 1584b47888ceSAndrew Thompson memcpy(&mdu->mdu_eh.ether_shost, 1585b47888ceSAndrew Thompson tp->tp_lladdr, ETHER_ADDR_LEN); 1586b47888ceSAndrew Thompson error = trunk_enqueue(lp->lp_ifp, m); 1587b47888ceSAndrew Thompson break; 1588b47888ceSAndrew Thompson 1589b47888ceSAndrew Thompson case MARKER_TYPE_RESPONSE: 1590b47888ceSAndrew Thompson if (tlv_check(mdu, sizeof(*mdu), &mdu->mdu_tlv, 1591b47888ceSAndrew Thompson marker_response_tlv_template, TRUE)) { 1592b47888ceSAndrew Thompson goto bad; 1593b47888ceSAndrew Thompson } 1594b47888ceSAndrew Thompson /* 1595b47888ceSAndrew Thompson * we are not interested in responses as 1596b47888ceSAndrew Thompson * we don't have a marker sender. 1597b47888ceSAndrew Thompson */ 1598b47888ceSAndrew Thompson /* FALLTHROUGH */ 1599b47888ceSAndrew Thompson default: 1600b47888ceSAndrew Thompson goto bad; 1601b47888ceSAndrew Thompson } 1602b47888ceSAndrew Thompson 1603b47888ceSAndrew Thompson return (error); 1604b47888ceSAndrew Thompson 1605b47888ceSAndrew Thompson bad: 1606b47888ceSAndrew Thompson m_freem(m); 1607b47888ceSAndrew Thompson return (EINVAL); 1608b47888ceSAndrew Thompson } 1609b47888ceSAndrew Thompson 1610b47888ceSAndrew Thompson static int 1611b47888ceSAndrew Thompson tlv_check(const void *p, size_t size, const struct tlvhdr *tlv, 1612b47888ceSAndrew Thompson const struct tlv_template *tmpl, boolean_t check_type) 1613b47888ceSAndrew Thompson { 1614b47888ceSAndrew Thompson while (/* CONSTCOND */ 1) { 1615b47888ceSAndrew Thompson if ((const char *)tlv - (const char *)p + sizeof(*tlv) > size) { 1616b47888ceSAndrew Thompson return (EINVAL); 1617b47888ceSAndrew Thompson } 1618b47888ceSAndrew Thompson if ((check_type && tlv->tlv_type != tmpl->tmpl_type) || 1619b47888ceSAndrew Thompson tlv->tlv_length != tmpl->tmpl_length) { 1620b47888ceSAndrew Thompson return (EINVAL); 1621b47888ceSAndrew Thompson } 1622b47888ceSAndrew Thompson if (tmpl->tmpl_type == 0) { 1623b47888ceSAndrew Thompson break; 1624b47888ceSAndrew Thompson } 1625b47888ceSAndrew Thompson tlv = (const struct tlvhdr *) 1626b47888ceSAndrew Thompson ((const char *)tlv + tlv->tlv_length); 1627b47888ceSAndrew Thompson tmpl++; 1628b47888ceSAndrew Thompson } 1629b47888ceSAndrew Thompson 1630b47888ceSAndrew Thompson return (0); 1631b47888ceSAndrew Thompson } 1632b47888ceSAndrew Thompson 1633b47888ceSAndrew Thompson #if defined(LACP_DEBUG) 1634b47888ceSAndrew Thompson const char * 1635b47888ceSAndrew Thompson lacp_format_mac(const uint8_t *mac, char *buf, size_t buflen) 1636b47888ceSAndrew Thompson { 1637b47888ceSAndrew Thompson snprintf(buf, buflen, "%02X-%02X-%02X-%02X-%02X-%02X", 1638b47888ceSAndrew Thompson (int)mac[0], 1639b47888ceSAndrew Thompson (int)mac[1], 1640b47888ceSAndrew Thompson (int)mac[2], 1641b47888ceSAndrew Thompson (int)mac[3], 1642b47888ceSAndrew Thompson (int)mac[4], 1643b47888ceSAndrew Thompson (int)mac[5]); 1644b47888ceSAndrew Thompson 1645b47888ceSAndrew Thompson return (buf); 1646b47888ceSAndrew Thompson } 1647b47888ceSAndrew Thompson 1648b47888ceSAndrew Thompson const char * 1649b47888ceSAndrew Thompson lacp_format_systemid(const struct lacp_systemid *sysid, 1650b47888ceSAndrew Thompson char *buf, size_t buflen) 1651b47888ceSAndrew Thompson { 1652b47888ceSAndrew Thompson char macbuf[LACP_MACSTR_MAX+1]; 1653b47888ceSAndrew Thompson 1654b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%s", 1655b47888ceSAndrew Thompson ntohs(sysid->lsi_prio), 1656b47888ceSAndrew Thompson lacp_format_mac(sysid->lsi_mac, macbuf, sizeof(macbuf))); 1657b47888ceSAndrew Thompson 1658b47888ceSAndrew Thompson return (buf); 1659b47888ceSAndrew Thompson } 1660b47888ceSAndrew Thompson 1661b47888ceSAndrew Thompson const char * 1662b47888ceSAndrew Thompson lacp_format_portid(const struct lacp_portid *portid, char *buf, size_t buflen) 1663b47888ceSAndrew Thompson { 1664b47888ceSAndrew Thompson snprintf(buf, buflen, "%04X,%04X", 1665b47888ceSAndrew Thompson ntohs(portid->lpi_prio), 1666b47888ceSAndrew Thompson ntohs(portid->lpi_portno)); 1667b47888ceSAndrew Thompson 1668b47888ceSAndrew Thompson return (buf); 1669b47888ceSAndrew Thompson } 1670b47888ceSAndrew Thompson 1671b47888ceSAndrew Thompson const char * 1672b47888ceSAndrew Thompson lacp_format_partner(const struct lacp_peerinfo *peer, char *buf, size_t buflen) 1673b47888ceSAndrew Thompson { 1674b47888ceSAndrew Thompson char sysid[LACP_SYSTEMIDSTR_MAX+1]; 1675b47888ceSAndrew Thompson char portid[LACP_PORTIDSTR_MAX+1]; 1676b47888ceSAndrew Thompson 1677b47888ceSAndrew Thompson snprintf(buf, buflen, "(%s,%04X,%s)", 1678b47888ceSAndrew Thompson lacp_format_systemid(&peer->lip_systemid, sysid, sizeof(sysid)), 1679b47888ceSAndrew Thompson ntohs(peer->lip_key), 1680b47888ceSAndrew Thompson lacp_format_portid(&peer->lip_portid, portid, sizeof(portid))); 1681b47888ceSAndrew Thompson 1682b47888ceSAndrew Thompson return (buf); 1683b47888ceSAndrew Thompson } 1684b47888ceSAndrew Thompson 1685b47888ceSAndrew Thompson const char * 1686b47888ceSAndrew Thompson lacp_format_lagid(const struct lacp_peerinfo *a, 1687b47888ceSAndrew Thompson const struct lacp_peerinfo *b, char *buf, size_t buflen) 1688b47888ceSAndrew Thompson { 1689b47888ceSAndrew Thompson char astr[LACP_PARTNERSTR_MAX+1]; 1690b47888ceSAndrew Thompson char bstr[LACP_PARTNERSTR_MAX+1]; 1691b47888ceSAndrew Thompson 1692b47888ceSAndrew Thompson #if 0 1693b47888ceSAndrew Thompson /* 1694b47888ceSAndrew Thompson * there's a convention to display small numbered peer 1695b47888ceSAndrew Thompson * in the left. 1696b47888ceSAndrew Thompson */ 1697b47888ceSAndrew Thompson 1698b47888ceSAndrew Thompson if (lacp_compare_peerinfo(a, b) > 0) { 1699b47888ceSAndrew Thompson const struct lacp_peerinfo *t; 1700b47888ceSAndrew Thompson 1701b47888ceSAndrew Thompson t = a; 1702b47888ceSAndrew Thompson a = b; 1703b47888ceSAndrew Thompson b = t; 1704b47888ceSAndrew Thompson } 1705b47888ceSAndrew Thompson #endif 1706b47888ceSAndrew Thompson 1707b47888ceSAndrew Thompson snprintf(buf, buflen, "[%s,%s]", 1708b47888ceSAndrew Thompson lacp_format_partner(a, astr, sizeof(astr)), 1709b47888ceSAndrew Thompson lacp_format_partner(b, bstr, sizeof(bstr))); 1710b47888ceSAndrew Thompson 1711b47888ceSAndrew Thompson return (buf); 1712b47888ceSAndrew Thompson } 1713b47888ceSAndrew Thompson 1714b47888ceSAndrew Thompson const char * 1715b47888ceSAndrew Thompson lacp_format_lagid_aggregator(const struct lacp_aggregator *la, 1716b47888ceSAndrew Thompson char *buf, size_t buflen) 1717b47888ceSAndrew Thompson { 1718b47888ceSAndrew Thompson if (la == NULL) { 1719b47888ceSAndrew Thompson return ("(none)"); 1720b47888ceSAndrew Thompson } 1721b47888ceSAndrew Thompson 1722b47888ceSAndrew Thompson return (lacp_format_lagid(&la->la_actor, &la->la_partner, buf, buflen)); 1723b47888ceSAndrew Thompson } 1724b47888ceSAndrew Thompson 1725b47888ceSAndrew Thompson const char * 1726b47888ceSAndrew Thompson lacp_format_state(uint8_t state, char *buf, size_t buflen) 1727b47888ceSAndrew Thompson { 1728b47888ceSAndrew Thompson snprintf(buf, buflen, "%b", state, LACP_STATE_BITS); 1729b47888ceSAndrew Thompson return (buf); 1730b47888ceSAndrew Thompson } 1731b47888ceSAndrew Thompson 1732b47888ceSAndrew Thompson static void 1733b47888ceSAndrew Thompson lacp_dump_lacpdu(const struct lacpdu *du) 1734b47888ceSAndrew Thompson { 1735b47888ceSAndrew Thompson char buf[LACP_PARTNERSTR_MAX+1]; 1736b47888ceSAndrew Thompson char buf2[LACP_STATESTR_MAX+1]; 1737b47888ceSAndrew Thompson 1738b47888ceSAndrew Thompson printf("actor=%s\n", 1739b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_actor, buf, sizeof(buf))); 1740b47888ceSAndrew Thompson printf("actor.state=%s\n", 1741b47888ceSAndrew Thompson lacp_format_state(du->ldu_actor.lip_state, buf2, sizeof(buf2))); 1742b47888ceSAndrew Thompson printf("partner=%s\n", 1743b47888ceSAndrew Thompson lacp_format_partner(&du->ldu_partner, buf, sizeof(buf))); 1744b47888ceSAndrew Thompson printf("partner.state=%s\n", 1745b47888ceSAndrew Thompson lacp_format_state(du->ldu_partner.lip_state, buf2, sizeof(buf2))); 1746b47888ceSAndrew Thompson 1747b47888ceSAndrew Thompson printf("maxdelay=%d\n", ntohs(du->ldu_collector.lci_maxdelay)); 1748b47888ceSAndrew Thompson } 1749b47888ceSAndrew Thompson 1750b47888ceSAndrew Thompson static void 1751b47888ceSAndrew Thompson lacp_dprintf(const struct lacp_port *lp, const char *fmt, ...) 1752b47888ceSAndrew Thompson { 1753b47888ceSAndrew Thompson va_list va; 1754b47888ceSAndrew Thompson 1755b47888ceSAndrew Thompson if (lp) { 1756b47888ceSAndrew Thompson printf("%s: ", lp->lp_ifp->if_xname); 1757b47888ceSAndrew Thompson } 1758b47888ceSAndrew Thompson 1759b47888ceSAndrew Thompson va_start(va, fmt); 1760b47888ceSAndrew Thompson vprintf(fmt, va); 1761b47888ceSAndrew Thompson va_end(va); 1762b47888ceSAndrew Thompson } 1763b47888ceSAndrew Thompson #endif 1764