1146985Sthompsa/* $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $ */ 2146985Sthompsa 3146985Sthompsa/* 4146985Sthompsa * Copyright (c) 2000 Jason L. Wright (jason@thought.net) 5163863Sthompsa * Copyright (c) 2006 Andrew Thompson (thompsa@FreeBSD.org) 6146985Sthompsa * All rights reserved. 7146985Sthompsa * 8146985Sthompsa * Redistribution and use in source and binary forms, with or without 9146985Sthompsa * modification, are permitted provided that the following conditions 10146985Sthompsa * are met: 11146985Sthompsa * 1. Redistributions of source code must retain the above copyright 12146985Sthompsa * notice, this list of conditions and the following disclaimer. 13146985Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 14146985Sthompsa * notice, this list of conditions and the following disclaimer in the 15146985Sthompsa * documentation and/or other materials provided with the distribution. 16146985Sthompsa * 17146985Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18146985Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19146985Sthompsa * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20146985Sthompsa * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 21146985Sthompsa * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22146985Sthompsa * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23146985Sthompsa * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24146985Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 25146985Sthompsa * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 26146985Sthompsa * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27146985Sthompsa * POSSIBILITY OF SUCH DAMAGE. 28146985Sthompsa * 29146985Sthompsa * OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp 30146985Sthompsa */ 31146985Sthompsa 32146985Sthompsa/* 33146985Sthompsa * Implementation of the spanning tree protocol as defined in 34163863Sthompsa * ISO/IEC 802.1D-2004, June 9, 2004. 35146985Sthompsa */ 36146985Sthompsa 37146985Sthompsa#include <sys/cdefs.h> 38146985Sthompsa__FBSDID("$FreeBSD$"); 39146985Sthompsa 40146985Sthompsa#include <sys/param.h> 41146985Sthompsa#include <sys/systm.h> 42146985Sthompsa#include <sys/mbuf.h> 43146985Sthompsa#include <sys/socket.h> 44146985Sthompsa#include <sys/sockio.h> 45146985Sthompsa#include <sys/kernel.h> 46146985Sthompsa#include <sys/callout.h> 47160703Sthompsa#include <sys/module.h> 48146985Sthompsa#include <sys/proc.h> 49146985Sthompsa#include <sys/lock.h> 50146985Sthompsa#include <sys/mutex.h> 51160899Sthompsa#include <sys/taskqueue.h> 52146985Sthompsa 53146985Sthompsa#include <net/if.h> 54146985Sthompsa#include <net/if_dl.h> 55146985Sthompsa#include <net/if_types.h> 56146985Sthompsa#include <net/if_llc.h> 57146985Sthompsa#include <net/if_media.h> 58185571Sbz#include <net/vnet.h> 59146985Sthompsa 60146985Sthompsa#include <netinet/in.h> 61146985Sthompsa#include <netinet/in_systm.h> 62146985Sthompsa#include <netinet/in_var.h> 63146985Sthompsa#include <netinet/if_ether.h> 64160703Sthompsa#include <net/bridgestp.h> 65146985Sthompsa 66163863Sthompsa#ifdef BRIDGESTP_DEBUG 67163863Sthompsa#define DPRINTF(fmt, arg...) printf("bstp: " fmt, ##arg) 68163863Sthompsa#else 69194577Srdivacky#define DPRINTF(fmt, arg...) (void)0 70163863Sthompsa#endif 71163863Sthompsa 72163863Sthompsa#define PV2ADDR(pv, eaddr) do { \ 73163863Sthompsa eaddr[0] = pv >> 40; \ 74163863Sthompsa eaddr[1] = pv >> 32; \ 75163863Sthompsa eaddr[2] = pv >> 24; \ 76163863Sthompsa eaddr[3] = pv >> 16; \ 77163863Sthompsa eaddr[4] = pv >> 8; \ 78163863Sthompsa eaddr[5] = pv >> 0; \ 79163863Sthompsa} while (0) 80163863Sthompsa 81163863Sthompsa#define INFO_BETTER 1 82163863Sthompsa#define INFO_SAME 0 83163863Sthompsa#define INFO_WORSE -1 84163863Sthompsa 85160703Sthompsaconst uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; 86146985Sthompsa 87160703SthompsaLIST_HEAD(, bstp_state) bstp_list; 88160703Sthompsastatic struct mtx bstp_list_mtx; 89146985Sthompsa 90163863Sthompsastatic void bstp_transmit(struct bstp_state *, struct bstp_port *); 91163863Sthompsastatic void bstp_transmit_bpdu(struct bstp_state *, struct bstp_port *); 92163863Sthompsastatic void bstp_transmit_tcn(struct bstp_state *, struct bstp_port *); 93163863Sthompsastatic void bstp_decode_bpdu(struct bstp_port *, struct bstp_cbpdu *, 94163863Sthompsa struct bstp_config_unit *); 95163863Sthompsastatic void bstp_send_bpdu(struct bstp_state *, struct bstp_port *, 96163863Sthompsa struct bstp_cbpdu *); 97163863Sthompsastatic int bstp_pdu_flags(struct bstp_port *); 98163863Sthompsastatic void bstp_received_stp(struct bstp_state *, struct bstp_port *, 99163903Sthompsa struct mbuf **, struct bstp_tbpdu *); 100163863Sthompsastatic void bstp_received_rstp(struct bstp_state *, struct bstp_port *, 101163903Sthompsa struct mbuf **, struct bstp_tbpdu *); 102163863Sthompsastatic void bstp_received_tcn(struct bstp_state *, struct bstp_port *, 103163863Sthompsa struct bstp_tcn_unit *); 104163863Sthompsastatic void bstp_received_bpdu(struct bstp_state *, struct bstp_port *, 105163863Sthompsa struct bstp_config_unit *); 106163863Sthompsastatic int bstp_pdu_rcvtype(struct bstp_port *, struct bstp_config_unit *); 107163863Sthompsastatic int bstp_pdu_bettersame(struct bstp_port *, int); 108163863Sthompsastatic int bstp_info_cmp(struct bstp_pri_vector *, 109163863Sthompsa struct bstp_pri_vector *); 110163863Sthompsastatic int bstp_info_superior(struct bstp_pri_vector *, 111163863Sthompsa struct bstp_pri_vector *); 112163863Sthompsastatic void bstp_assign_roles(struct bstp_state *); 113163863Sthompsastatic void bstp_update_roles(struct bstp_state *, struct bstp_port *); 114163863Sthompsastatic void bstp_update_state(struct bstp_state *, struct bstp_port *); 115163863Sthompsastatic void bstp_update_tc(struct bstp_port *); 116163863Sthompsastatic void bstp_update_info(struct bstp_port *); 117163863Sthompsastatic void bstp_set_other_tcprop(struct bstp_port *); 118163863Sthompsastatic void bstp_set_all_reroot(struct bstp_state *); 119163863Sthompsastatic void bstp_set_all_sync(struct bstp_state *); 120163863Sthompsastatic void bstp_set_port_state(struct bstp_port *, int); 121163863Sthompsastatic void bstp_set_port_role(struct bstp_port *, int); 122163863Sthompsastatic void bstp_set_port_proto(struct bstp_port *, int); 123163863Sthompsastatic void bstp_set_port_tc(struct bstp_port *, int); 124163863Sthompsastatic void bstp_set_timer_tc(struct bstp_port *); 125163863Sthompsastatic void bstp_set_timer_msgage(struct bstp_port *); 126163863Sthompsastatic int bstp_rerooted(struct bstp_state *, struct bstp_port *); 127163863Sthompsastatic uint32_t bstp_calc_path_cost(struct bstp_port *); 128163863Sthompsastatic void bstp_notify_state(void *, int); 129163863Sthompsastatic void bstp_notify_rtage(void *, int); 130234488Sthompsastatic void bstp_ifupdstatus(void *, int); 131160703Sthompsastatic void bstp_enable_port(struct bstp_state *, struct bstp_port *); 132163863Sthompsastatic void bstp_disable_port(struct bstp_state *, struct bstp_port *); 133151313Sthompsastatic void bstp_tick(void *); 134160703Sthompsastatic void bstp_timer_start(struct bstp_timer *, uint16_t); 135160703Sthompsastatic void bstp_timer_stop(struct bstp_timer *); 136163863Sthompsastatic void bstp_timer_latch(struct bstp_timer *); 137232070Sthompsastatic int bstp_timer_dectest(struct bstp_timer *); 138163863Sthompsastatic void bstp_hello_timer_expiry(struct bstp_state *, 139160703Sthompsa struct bstp_port *); 140163863Sthompsastatic void bstp_message_age_expiry(struct bstp_state *, 141160703Sthompsa struct bstp_port *); 142163863Sthompsastatic void bstp_migrate_delay_expiry(struct bstp_state *, 143160703Sthompsa struct bstp_port *); 144163863Sthompsastatic void bstp_edge_delay_expiry(struct bstp_state *, 145163863Sthompsa struct bstp_port *); 146156096Sthompsastatic int bstp_addr_cmp(const uint8_t *, const uint8_t *); 147163863Sthompsastatic int bstp_same_bridgeid(uint64_t, uint64_t); 148163863Sthompsastatic void bstp_reinit(struct bstp_state *); 149146985Sthompsa 150151313Sthompsastatic void 151163863Sthompsabstp_transmit(struct bstp_state *bs, struct bstp_port *bp) 152146985Sthompsa{ 153164141Sthompsa if (bs->bs_running == 0) 154164141Sthompsa return; 155164141Sthompsa 156163863Sthompsa /* 157163863Sthompsa * a PDU can only be sent if we have tx quota left and the 158163863Sthompsa * hello timer is running. 159163863Sthompsa */ 160163863Sthompsa if (bp->bp_hello_timer.active == 0) { 161163863Sthompsa /* Test if it needs to be reset */ 162163863Sthompsa bstp_hello_timer_expiry(bs, bp); 163146985Sthompsa return; 164146985Sthompsa } 165163863Sthompsa if (bp->bp_txcount > bs->bs_txholdcount) 166163863Sthompsa /* Ran out of karma */ 167163863Sthompsa return; 168146985Sthompsa 169163863Sthompsa if (bp->bp_protover == BSTP_PROTO_RSTP) { 170163863Sthompsa bstp_transmit_bpdu(bs, bp); 171163863Sthompsa bp->bp_tc_ack = 0; 172163863Sthompsa } else { /* STP */ 173163863Sthompsa switch (bp->bp_role) { 174163863Sthompsa case BSTP_ROLE_DESIGNATED: 175163863Sthompsa bstp_transmit_bpdu(bs, bp); 176163863Sthompsa bp->bp_tc_ack = 0; 177163863Sthompsa break; 178146985Sthompsa 179163863Sthompsa case BSTP_ROLE_ROOT: 180163863Sthompsa bstp_transmit_tcn(bs, bp); 181163863Sthompsa break; 182163863Sthompsa } 183146985Sthompsa } 184163863Sthompsa bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime); 185163863Sthompsa bp->bp_flags &= ~BSTP_PORT_NEWINFO; 186146985Sthompsa} 187146985Sthompsa 188151313Sthompsastatic void 189163863Sthompsabstp_transmit_bpdu(struct bstp_state *bs, struct bstp_port *bp) 190146985Sthompsa{ 191146985Sthompsa struct bstp_cbpdu bpdu; 192146985Sthompsa 193160703Sthompsa BSTP_LOCK_ASSERT(bs); 194146985Sthompsa 195163863Sthompsa bpdu.cbu_rootpri = htons(bp->bp_desg_pv.pv_root_id >> 48); 196163863Sthompsa PV2ADDR(bp->bp_desg_pv.pv_root_id, bpdu.cbu_rootaddr); 197146985Sthompsa 198163863Sthompsa bpdu.cbu_rootpathcost = htonl(bp->bp_desg_pv.pv_cost); 199146985Sthompsa 200163863Sthompsa bpdu.cbu_bridgepri = htons(bp->bp_desg_pv.pv_dbridge_id >> 48); 201163863Sthompsa PV2ADDR(bp->bp_desg_pv.pv_dbridge_id, bpdu.cbu_bridgeaddr); 202146985Sthompsa 203163863Sthompsa bpdu.cbu_portid = htons(bp->bp_port_id); 204163863Sthompsa bpdu.cbu_messageage = htons(bp->bp_desg_msg_age); 205163863Sthompsa bpdu.cbu_maxage = htons(bp->bp_desg_max_age); 206163863Sthompsa bpdu.cbu_hellotime = htons(bp->bp_desg_htime); 207163863Sthompsa bpdu.cbu_forwarddelay = htons(bp->bp_desg_fdelay); 208146985Sthompsa 209163863Sthompsa bpdu.cbu_flags = bstp_pdu_flags(bp); 210146985Sthompsa 211163863Sthompsa switch (bp->bp_protover) { 212163863Sthompsa case BSTP_PROTO_STP: 213163863Sthompsa bpdu.cbu_bpdutype = BSTP_MSGTYPE_CFG; 214163863Sthompsa break; 215146985Sthompsa 216163863Sthompsa case BSTP_PROTO_RSTP: 217163863Sthompsa bpdu.cbu_bpdutype = BSTP_MSGTYPE_RSTP; 218163863Sthompsa break; 219146985Sthompsa } 220146985Sthompsa 221163863Sthompsa bstp_send_bpdu(bs, bp, &bpdu); 222146985Sthompsa} 223146985Sthompsa 224151313Sthompsastatic void 225163863Sthompsabstp_transmit_tcn(struct bstp_state *bs, struct bstp_port *bp) 226146985Sthompsa{ 227146985Sthompsa struct bstp_tbpdu bpdu; 228160703Sthompsa struct ifnet *ifp = bp->bp_ifp; 229146985Sthompsa struct ether_header *eh; 230146985Sthompsa struct mbuf *m; 231146985Sthompsa 232163863Sthompsa KASSERT(bp == bs->bs_root_port, ("%s: bad root port\n", __func__)); 233146985Sthompsa 234148887Srwatson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 235146985Sthompsa return; 236146985Sthompsa 237248324Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 238146985Sthompsa if (m == NULL) 239146985Sthompsa return; 240146985Sthompsa 241146985Sthompsa m->m_pkthdr.rcvif = ifp; 242146985Sthompsa m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu); 243146985Sthompsa m->m_len = m->m_pkthdr.len; 244146985Sthompsa 245146985Sthompsa eh = mtod(m, struct ether_header *); 246146985Sthompsa 247146985Sthompsa memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); 248146985Sthompsa memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); 249146985Sthompsa eh->ether_type = htons(sizeof(bpdu)); 250146985Sthompsa 251146985Sthompsa bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP; 252146985Sthompsa bpdu.tbu_ctl = LLC_UI; 253146985Sthompsa bpdu.tbu_protoid = 0; 254146985Sthompsa bpdu.tbu_protover = 0; 255146985Sthompsa bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN; 256146985Sthompsa 257146985Sthompsa memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu)); 258146985Sthompsa 259163863Sthompsa bp->bp_txcount++; 260191608Skmacy ifp->if_transmit(ifp, m); 261146985Sthompsa} 262146985Sthompsa 263151313Sthompsastatic void 264163863Sthompsabstp_decode_bpdu(struct bstp_port *bp, struct bstp_cbpdu *cpdu, 265163863Sthompsa struct bstp_config_unit *cu) 266146985Sthompsa{ 267163863Sthompsa int flags; 268146985Sthompsa 269163863Sthompsa cu->cu_pv.pv_root_id = 270163863Sthompsa (((uint64_t)ntohs(cpdu->cbu_rootpri)) << 48) | 271163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[0]) << 40) | 272163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[1]) << 32) | 273163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[2]) << 24) | 274163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[3]) << 16) | 275163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[4]) << 8) | 276163863Sthompsa (((uint64_t)cpdu->cbu_rootaddr[5]) << 0); 277146985Sthompsa 278163863Sthompsa cu->cu_pv.pv_dbridge_id = 279163863Sthompsa (((uint64_t)ntohs(cpdu->cbu_bridgepri)) << 48) | 280163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[0]) << 40) | 281163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[1]) << 32) | 282163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[2]) << 24) | 283163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[3]) << 16) | 284163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[4]) << 8) | 285163863Sthompsa (((uint64_t)cpdu->cbu_bridgeaddr[5]) << 0); 286146985Sthompsa 287163863Sthompsa cu->cu_pv.pv_cost = ntohl(cpdu->cbu_rootpathcost); 288163863Sthompsa cu->cu_message_age = ntohs(cpdu->cbu_messageage); 289163863Sthompsa cu->cu_max_age = ntohs(cpdu->cbu_maxage); 290163863Sthompsa cu->cu_hello_time = ntohs(cpdu->cbu_hellotime); 291163863Sthompsa cu->cu_forward_delay = ntohs(cpdu->cbu_forwarddelay); 292163863Sthompsa cu->cu_pv.pv_dport_id = ntohs(cpdu->cbu_portid); 293163863Sthompsa cu->cu_pv.pv_port_id = bp->bp_port_id; 294163863Sthompsa cu->cu_message_type = cpdu->cbu_bpdutype; 295146985Sthompsa 296163863Sthompsa /* Strip off unused flags in STP mode */ 297163863Sthompsa flags = cpdu->cbu_flags; 298163863Sthompsa switch (cpdu->cbu_protover) { 299163863Sthompsa case BSTP_PROTO_STP: 300163863Sthompsa flags &= BSTP_PDU_STPMASK; 301163863Sthompsa /* A STP BPDU explicitly conveys a Designated Port */ 302163863Sthompsa cu->cu_role = BSTP_ROLE_DESIGNATED; 303163863Sthompsa break; 304146985Sthompsa 305163863Sthompsa case BSTP_PROTO_RSTP: 306163863Sthompsa flags &= BSTP_PDU_RSTPMASK; 307163863Sthompsa break; 308163863Sthompsa } 309146985Sthompsa 310163863Sthompsa cu->cu_topology_change_ack = 311163863Sthompsa (flags & BSTP_PDU_F_TCA) ? 1 : 0; 312163863Sthompsa cu->cu_proposal = 313163863Sthompsa (flags & BSTP_PDU_F_P) ? 1 : 0; 314163863Sthompsa cu->cu_agree = 315163863Sthompsa (flags & BSTP_PDU_F_A) ? 1 : 0; 316163863Sthompsa cu->cu_learning = 317163863Sthompsa (flags & BSTP_PDU_F_L) ? 1 : 0; 318163863Sthompsa cu->cu_forwarding = 319163863Sthompsa (flags & BSTP_PDU_F_F) ? 1 : 0; 320163863Sthompsa cu->cu_topology_change = 321163863Sthompsa (flags & BSTP_PDU_F_TC) ? 1 : 0; 322146985Sthompsa 323163863Sthompsa switch ((flags & BSTP_PDU_PRMASK) >> BSTP_PDU_PRSHIFT) { 324163863Sthompsa case BSTP_PDU_F_ROOT: 325163863Sthompsa cu->cu_role = BSTP_ROLE_ROOT; 326163863Sthompsa break; 327163863Sthompsa case BSTP_PDU_F_ALT: 328163863Sthompsa cu->cu_role = BSTP_ROLE_ALTERNATE; 329163863Sthompsa break; 330163863Sthompsa case BSTP_PDU_F_DESG: 331163863Sthompsa cu->cu_role = BSTP_ROLE_DESIGNATED; 332163863Sthompsa break; 333146985Sthompsa } 334146985Sthompsa} 335146985Sthompsa 336151313Sthompsastatic void 337163863Sthompsabstp_send_bpdu(struct bstp_state *bs, struct bstp_port *bp, 338163863Sthompsa struct bstp_cbpdu *bpdu) 339146985Sthompsa{ 340163863Sthompsa struct ifnet *ifp; 341163863Sthompsa struct mbuf *m; 342163863Sthompsa struct ether_header *eh; 343146985Sthompsa 344160703Sthompsa BSTP_LOCK_ASSERT(bs); 345146985Sthompsa 346163863Sthompsa ifp = bp->bp_ifp; 347146985Sthompsa 348163863Sthompsa if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 349163863Sthompsa return; 350146985Sthompsa 351248324Sglebius m = m_gethdr(M_NOWAIT, MT_DATA); 352163863Sthompsa if (m == NULL) 353163863Sthompsa return; 354146985Sthompsa 355163863Sthompsa eh = mtod(m, struct ether_header *); 356146985Sthompsa 357163863Sthompsa bpdu->cbu_ssap = bpdu->cbu_dsap = LLC_8021D_LSAP; 358163863Sthompsa bpdu->cbu_ctl = LLC_UI; 359163863Sthompsa bpdu->cbu_protoid = htons(BSTP_PROTO_ID); 360160703Sthompsa 361163863Sthompsa memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN); 362163863Sthompsa memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); 363146985Sthompsa 364163863Sthompsa switch (bpdu->cbu_bpdutype) { 365163863Sthompsa case BSTP_MSGTYPE_CFG: 366163863Sthompsa bpdu->cbu_protover = BSTP_PROTO_STP; 367163863Sthompsa m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_STP_LEN; 368163863Sthompsa eh->ether_type = htons(BSTP_BPDU_STP_LEN); 369163863Sthompsa memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu, 370163863Sthompsa BSTP_BPDU_STP_LEN); 371163863Sthompsa break; 372146985Sthompsa 373163863Sthompsa case BSTP_MSGTYPE_RSTP: 374163863Sthompsa bpdu->cbu_protover = BSTP_PROTO_RSTP; 375163863Sthompsa bpdu->cbu_versionlen = htons(0); 376163863Sthompsa m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_RSTP_LEN; 377163863Sthompsa eh->ether_type = htons(BSTP_BPDU_RSTP_LEN); 378163863Sthompsa memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu, 379163863Sthompsa BSTP_BPDU_RSTP_LEN); 380163863Sthompsa break; 381160703Sthompsa 382163863Sthompsa default: 383163863Sthompsa panic("not implemented"); 384146985Sthompsa } 385163863Sthompsa m->m_pkthdr.rcvif = ifp; 386163863Sthompsa m->m_len = m->m_pkthdr.len; 387163863Sthompsa 388163863Sthompsa bp->bp_txcount++; 389191608Skmacy ifp->if_transmit(ifp, m); 390146985Sthompsa} 391146985Sthompsa 392163863Sthompsastatic int 393163863Sthompsabstp_pdu_flags(struct bstp_port *bp) 394146985Sthompsa{ 395163863Sthompsa int flags = 0; 396160899Sthompsa 397163863Sthompsa if (bp->bp_proposing && bp->bp_state != BSTP_IFSTATE_FORWARDING) 398163863Sthompsa flags |= BSTP_PDU_F_P; 399160900Sthompsa 400163863Sthompsa if (bp->bp_agree) 401163863Sthompsa flags |= BSTP_PDU_F_A; 402146985Sthompsa 403163863Sthompsa if (bp->bp_tc_timer.active) 404163863Sthompsa flags |= BSTP_PDU_F_TC; 405160899Sthompsa 406163863Sthompsa if (bp->bp_tc_ack) 407163863Sthompsa flags |= BSTP_PDU_F_TCA; 408160899Sthompsa 409163863Sthompsa switch (bp->bp_state) { 410163863Sthompsa case BSTP_IFSTATE_LEARNING: 411163863Sthompsa flags |= BSTP_PDU_F_L; 412163863Sthompsa break; 413160867Sthompsa 414163863Sthompsa case BSTP_IFSTATE_FORWARDING: 415163863Sthompsa flags |= (BSTP_PDU_F_L | BSTP_PDU_F_F); 416163863Sthompsa break; 417146985Sthompsa } 418146985Sthompsa 419163863Sthompsa switch (bp->bp_role) { 420163863Sthompsa case BSTP_ROLE_ROOT: 421163863Sthompsa flags |= 422163863Sthompsa (BSTP_PDU_F_ROOT << BSTP_PDU_PRSHIFT); 423163863Sthompsa break; 424160703Sthompsa 425163863Sthompsa case BSTP_ROLE_ALTERNATE: 426163863Sthompsa case BSTP_ROLE_BACKUP: /* fall through */ 427163863Sthompsa flags |= 428163863Sthompsa (BSTP_PDU_F_ALT << BSTP_PDU_PRSHIFT); 429163863Sthompsa break; 430146985Sthompsa 431163863Sthompsa case BSTP_ROLE_DESIGNATED: 432163863Sthompsa flags |= 433163863Sthompsa (BSTP_PDU_F_DESG << BSTP_PDU_PRSHIFT); 434163863Sthompsa break; 435163863Sthompsa } 436160703Sthompsa 437163863Sthompsa /* Strip off unused flags in either mode */ 438163863Sthompsa switch (bp->bp_protover) { 439163863Sthompsa case BSTP_PROTO_STP: 440163863Sthompsa flags &= BSTP_PDU_STPMASK; 441163863Sthompsa break; 442163863Sthompsa case BSTP_PROTO_RSTP: 443163863Sthompsa flags &= BSTP_PDU_RSTPMASK; 444163863Sthompsa break; 445163863Sthompsa } 446163863Sthompsa return (flags); 447146985Sthompsa} 448146985Sthompsa 449232014Sthompsavoid 450160703Sthompsabstp_input(struct bstp_port *bp, struct ifnet *ifp, struct mbuf *m) 451146985Sthompsa{ 452160703Sthompsa struct bstp_state *bs = bp->bp_bs; 453146985Sthompsa struct ether_header *eh; 454146985Sthompsa struct bstp_tbpdu tpdu; 455146985Sthompsa uint16_t len; 456146985Sthompsa 457160703Sthompsa if (bp->bp_active == 0) { 458160703Sthompsa m_freem(m); 459232014Sthompsa return; 460160703Sthompsa } 461146985Sthompsa 462160703Sthompsa BSTP_LOCK(bs); 463160703Sthompsa 464146985Sthompsa eh = mtod(m, struct ether_header *); 465146985Sthompsa 466146985Sthompsa len = ntohs(eh->ether_type); 467146985Sthompsa if (len < sizeof(tpdu)) 468146985Sthompsa goto out; 469146985Sthompsa 470146985Sthompsa m_adj(m, ETHER_HDR_LEN); 471146985Sthompsa 472146985Sthompsa if (m->m_pkthdr.len > len) 473146985Sthompsa m_adj(m, len - m->m_pkthdr.len); 474146985Sthompsa if (m->m_len < sizeof(tpdu) && 475146985Sthompsa (m = m_pullup(m, sizeof(tpdu))) == NULL) 476146985Sthompsa goto out; 477146985Sthompsa 478146985Sthompsa memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu)); 479146985Sthompsa 480163863Sthompsa /* basic packet checks */ 481146985Sthompsa if (tpdu.tbu_dsap != LLC_8021D_LSAP || 482146985Sthompsa tpdu.tbu_ssap != LLC_8021D_LSAP || 483146985Sthompsa tpdu.tbu_ctl != LLC_UI) 484146985Sthompsa goto out; 485163863Sthompsa if (tpdu.tbu_protoid != BSTP_PROTO_ID) 486146985Sthompsa goto out; 487146985Sthompsa 488163863Sthompsa /* 489163863Sthompsa * We can treat later versions of the PDU as the same as the maximum 490163863Sthompsa * version we implement. All additional parameters/flags are ignored. 491163863Sthompsa */ 492163863Sthompsa if (tpdu.tbu_protover > BSTP_PROTO_MAX) 493163863Sthompsa tpdu.tbu_protover = BSTP_PROTO_MAX; 494163863Sthompsa 495163863Sthompsa if (tpdu.tbu_protover != bp->bp_protover) { 496163863Sthompsa /* 497163863Sthompsa * Wait for the migration delay timer to expire before changing 498163863Sthompsa * protocol version to avoid flip-flops. 499163863Sthompsa */ 500163863Sthompsa if (bp->bp_flags & BSTP_PORT_CANMIGRATE) 501163863Sthompsa bstp_set_port_proto(bp, tpdu.tbu_protover); 502163863Sthompsa else 503146985Sthompsa goto out; 504163863Sthompsa } 505146985Sthompsa 506163863Sthompsa /* Clear operedge upon receiving a PDU on the port */ 507163863Sthompsa bp->bp_operedge = 0; 508163863Sthompsa bstp_timer_start(&bp->bp_edge_delay_timer, 509163863Sthompsa BSTP_DEFAULT_MIGRATE_DELAY); 510146985Sthompsa 511163863Sthompsa switch (tpdu.tbu_protover) { 512163863Sthompsa case BSTP_PROTO_STP: 513163903Sthompsa bstp_received_stp(bs, bp, &m, &tpdu); 514163863Sthompsa break; 515146985Sthompsa 516163863Sthompsa case BSTP_PROTO_RSTP: 517163903Sthompsa bstp_received_rstp(bs, bp, &m, &tpdu); 518163863Sthompsa break; 519146985Sthompsa } 520153497Sthompsaout: 521160703Sthompsa BSTP_UNLOCK(bs); 522146985Sthompsa if (m) 523146985Sthompsa m_freem(m); 524146985Sthompsa} 525146985Sthompsa 526151313Sthompsastatic void 527163863Sthompsabstp_received_stp(struct bstp_state *bs, struct bstp_port *bp, 528163903Sthompsa struct mbuf **mp, struct bstp_tbpdu *tpdu) 529163863Sthompsa{ 530163863Sthompsa struct bstp_cbpdu cpdu; 531163863Sthompsa struct bstp_config_unit *cu = &bp->bp_msg_cu; 532163863Sthompsa struct bstp_tcn_unit tu; 533163863Sthompsa 534163863Sthompsa switch (tpdu->tbu_bpdutype) { 535163863Sthompsa case BSTP_MSGTYPE_TCN: 536163863Sthompsa tu.tu_message_type = tpdu->tbu_bpdutype; 537163863Sthompsa bstp_received_tcn(bs, bp, &tu); 538163863Sthompsa break; 539163863Sthompsa case BSTP_MSGTYPE_CFG: 540163903Sthompsa if ((*mp)->m_len < BSTP_BPDU_STP_LEN && 541163903Sthompsa (*mp = m_pullup(*mp, BSTP_BPDU_STP_LEN)) == NULL) 542163863Sthompsa return; 543163903Sthompsa memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_STP_LEN); 544163863Sthompsa 545163863Sthompsa bstp_decode_bpdu(bp, &cpdu, cu); 546163863Sthompsa bstp_received_bpdu(bs, bp, cu); 547163863Sthompsa break; 548163863Sthompsa } 549163863Sthompsa} 550163863Sthompsa 551163863Sthompsastatic void 552163863Sthompsabstp_received_rstp(struct bstp_state *bs, struct bstp_port *bp, 553163903Sthompsa struct mbuf **mp, struct bstp_tbpdu *tpdu) 554163863Sthompsa{ 555163863Sthompsa struct bstp_cbpdu cpdu; 556163863Sthompsa struct bstp_config_unit *cu = &bp->bp_msg_cu; 557163863Sthompsa 558163903Sthompsa if (tpdu->tbu_bpdutype != BSTP_MSGTYPE_RSTP) 559163863Sthompsa return; 560163863Sthompsa 561163903Sthompsa if ((*mp)->m_len < BSTP_BPDU_RSTP_LEN && 562163903Sthompsa (*mp = m_pullup(*mp, BSTP_BPDU_RSTP_LEN)) == NULL) 563163863Sthompsa return; 564163903Sthompsa memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_RSTP_LEN); 565163863Sthompsa 566163863Sthompsa bstp_decode_bpdu(bp, &cpdu, cu); 567163863Sthompsa bstp_received_bpdu(bs, bp, cu); 568163863Sthompsa} 569163863Sthompsa 570163863Sthompsastatic void 571163863Sthompsabstp_received_tcn(struct bstp_state *bs, struct bstp_port *bp, 572163863Sthompsa struct bstp_tcn_unit *tcn) 573163863Sthompsa{ 574163863Sthompsa bp->bp_rcvdtcn = 1; 575163863Sthompsa bstp_update_tc(bp); 576163863Sthompsa} 577163863Sthompsa 578163863Sthompsastatic void 579163863Sthompsabstp_received_bpdu(struct bstp_state *bs, struct bstp_port *bp, 580146985Sthompsa struct bstp_config_unit *cu) 581146985Sthompsa{ 582163863Sthompsa int type; 583146985Sthompsa 584160703Sthompsa BSTP_LOCK_ASSERT(bs); 585146985Sthompsa 586163863Sthompsa /* We need to have transitioned to INFO_MINE before proceeding */ 587163863Sthompsa switch (bp->bp_infois) { 588163863Sthompsa case BSTP_INFO_DISABLED: 589163863Sthompsa case BSTP_INFO_AGED: 590163863Sthompsa return; 591163863Sthompsa } 592146985Sthompsa 593163863Sthompsa type = bstp_pdu_rcvtype(bp, cu); 594146985Sthompsa 595163863Sthompsa switch (type) { 596163863Sthompsa case BSTP_PDU_SUPERIOR: 597163863Sthompsa bs->bs_allsynced = 0; 598163863Sthompsa bp->bp_agreed = 0; 599163863Sthompsa bp->bp_proposing = 0; 600146985Sthompsa 601163863Sthompsa if (cu->cu_proposal && cu->cu_forwarding == 0) 602163863Sthompsa bp->bp_proposed = 1; 603163863Sthompsa if (cu->cu_topology_change) 604163863Sthompsa bp->bp_rcvdtc = 1; 605163863Sthompsa if (cu->cu_topology_change_ack) 606163863Sthompsa bp->bp_rcvdtca = 1; 607163863Sthompsa 608163863Sthompsa if (bp->bp_agree && 609174493Sthompsa !bstp_pdu_bettersame(bp, BSTP_INFO_RECEIVED)) 610163863Sthompsa bp->bp_agree = 0; 611163863Sthompsa 612163863Sthompsa /* copy the received priority and timers to the port */ 613163863Sthompsa bp->bp_port_pv = cu->cu_pv; 614163863Sthompsa bp->bp_port_msg_age = cu->cu_message_age; 615163863Sthompsa bp->bp_port_max_age = cu->cu_max_age; 616163863Sthompsa bp->bp_port_fdelay = cu->cu_forward_delay; 617163863Sthompsa bp->bp_port_htime = 618163863Sthompsa (cu->cu_hello_time > BSTP_MIN_HELLO_TIME ? 619163863Sthompsa cu->cu_hello_time : BSTP_MIN_HELLO_TIME); 620163863Sthompsa 621163863Sthompsa /* set expiry for the new info */ 622163863Sthompsa bstp_set_timer_msgage(bp); 623163863Sthompsa 624174493Sthompsa bp->bp_infois = BSTP_INFO_RECEIVED; 625163863Sthompsa bstp_assign_roles(bs); 626163863Sthompsa break; 627163863Sthompsa 628163863Sthompsa case BSTP_PDU_REPEATED: 629163863Sthompsa if (cu->cu_proposal && cu->cu_forwarding == 0) 630163863Sthompsa bp->bp_proposed = 1; 631163863Sthompsa if (cu->cu_topology_change) 632163863Sthompsa bp->bp_rcvdtc = 1; 633163863Sthompsa if (cu->cu_topology_change_ack) 634163863Sthompsa bp->bp_rcvdtca = 1; 635163863Sthompsa 636163863Sthompsa /* rearm the age timer */ 637163863Sthompsa bstp_set_timer_msgage(bp); 638163863Sthompsa break; 639163863Sthompsa 640163863Sthompsa case BSTP_PDU_INFERIOR: 641163863Sthompsa if (cu->cu_learning) { 642163863Sthompsa bp->bp_agreed = 1; 643163863Sthompsa bp->bp_proposing = 0; 644146985Sthompsa } 645163863Sthompsa break; 646146985Sthompsa 647163863Sthompsa case BSTP_PDU_INFERIORALT: 648163863Sthompsa /* 649163863Sthompsa * only point to point links are allowed fast 650163863Sthompsa * transitions to forwarding. 651163863Sthompsa */ 652165105Sthompsa if (cu->cu_agree && bp->bp_ptp_link) { 653163863Sthompsa bp->bp_agreed = 1; 654163863Sthompsa bp->bp_proposing = 0; 655163863Sthompsa } else 656163863Sthompsa bp->bp_agreed = 0; 657146985Sthompsa 658163863Sthompsa if (cu->cu_topology_change) 659163863Sthompsa bp->bp_rcvdtc = 1; 660163863Sthompsa if (cu->cu_topology_change_ack) 661163863Sthompsa bp->bp_rcvdtca = 1; 662163863Sthompsa break; 663163863Sthompsa 664163863Sthompsa case BSTP_PDU_OTHER: 665163863Sthompsa return; /* do nothing */ 666146985Sthompsa } 667163863Sthompsa /* update the state machines with the new data */ 668163863Sthompsa bstp_update_state(bs, bp); 669146985Sthompsa} 670146985Sthompsa 671163863Sthompsastatic int 672163863Sthompsabstp_pdu_rcvtype(struct bstp_port *bp, struct bstp_config_unit *cu) 673146985Sthompsa{ 674163863Sthompsa int type; 675163863Sthompsa 676163863Sthompsa /* default return type */ 677163863Sthompsa type = BSTP_PDU_OTHER; 678163863Sthompsa 679163863Sthompsa switch (cu->cu_role) { 680163863Sthompsa case BSTP_ROLE_DESIGNATED: 681163863Sthompsa if (bstp_info_superior(&bp->bp_port_pv, &cu->cu_pv)) 682163863Sthompsa /* bpdu priority is superior */ 683163863Sthompsa type = BSTP_PDU_SUPERIOR; 684163863Sthompsa else if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) == 685163863Sthompsa INFO_SAME) { 686163863Sthompsa if (bp->bp_port_msg_age != cu->cu_message_age || 687163863Sthompsa bp->bp_port_max_age != cu->cu_max_age || 688163863Sthompsa bp->bp_port_fdelay != cu->cu_forward_delay || 689163863Sthompsa bp->bp_port_htime != cu->cu_hello_time) 690163863Sthompsa /* bpdu priority is equal and timers differ */ 691163863Sthompsa type = BSTP_PDU_SUPERIOR; 692163863Sthompsa else 693163863Sthompsa /* bpdu is equal */ 694163863Sthompsa type = BSTP_PDU_REPEATED; 695163863Sthompsa } else 696163863Sthompsa /* bpdu priority is worse */ 697163863Sthompsa type = BSTP_PDU_INFERIOR; 698163863Sthompsa 699163863Sthompsa break; 700163863Sthompsa 701163863Sthompsa case BSTP_ROLE_ROOT: 702163863Sthompsa case BSTP_ROLE_ALTERNATE: 703163863Sthompsa case BSTP_ROLE_BACKUP: 704163863Sthompsa if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) <= INFO_SAME) 705163863Sthompsa /* 706163863Sthompsa * not a designated port and priority is the same or 707163863Sthompsa * worse 708163863Sthompsa */ 709163863Sthompsa type = BSTP_PDU_INFERIORALT; 710163863Sthompsa break; 711146985Sthompsa } 712163863Sthompsa 713163863Sthompsa return (type); 714146985Sthompsa} 715146985Sthompsa 716163863Sthompsastatic int 717163863Sthompsabstp_pdu_bettersame(struct bstp_port *bp, int newinfo) 718163863Sthompsa{ 719174493Sthompsa if (newinfo == BSTP_INFO_RECEIVED && 720174493Sthompsa bp->bp_infois == BSTP_INFO_RECEIVED && 721163863Sthompsa bstp_info_cmp(&bp->bp_port_pv, &bp->bp_msg_cu.cu_pv) >= INFO_SAME) 722163863Sthompsa return (1); 723163863Sthompsa 724163863Sthompsa if (newinfo == BSTP_INFO_MINE && 725163863Sthompsa bp->bp_infois == BSTP_INFO_MINE && 726163863Sthompsa bstp_info_cmp(&bp->bp_port_pv, &bp->bp_desg_pv) >= INFO_SAME) 727163863Sthompsa return (1); 728163863Sthompsa 729163863Sthompsa return (0); 730163863Sthompsa} 731163863Sthompsa 732163863Sthompsastatic int 733163863Sthompsabstp_info_cmp(struct bstp_pri_vector *pv, 734163863Sthompsa struct bstp_pri_vector *cpv) 735163863Sthompsa{ 736163863Sthompsa if (cpv->pv_root_id < pv->pv_root_id) 737163863Sthompsa return (INFO_BETTER); 738163863Sthompsa if (cpv->pv_root_id > pv->pv_root_id) 739163863Sthompsa return (INFO_WORSE); 740163863Sthompsa 741163863Sthompsa if (cpv->pv_cost < pv->pv_cost) 742163863Sthompsa return (INFO_BETTER); 743163863Sthompsa if (cpv->pv_cost > pv->pv_cost) 744163863Sthompsa return (INFO_WORSE); 745163863Sthompsa 746163863Sthompsa if (cpv->pv_dbridge_id < pv->pv_dbridge_id) 747163863Sthompsa return (INFO_BETTER); 748163863Sthompsa if (cpv->pv_dbridge_id > pv->pv_dbridge_id) 749163863Sthompsa return (INFO_WORSE); 750163863Sthompsa 751163863Sthompsa if (cpv->pv_dport_id < pv->pv_dport_id) 752163863Sthompsa return (INFO_BETTER); 753163863Sthompsa if (cpv->pv_dport_id > pv->pv_dport_id) 754163863Sthompsa return (INFO_WORSE); 755163863Sthompsa 756163863Sthompsa return (INFO_SAME); 757163863Sthompsa} 758163863Sthompsa 759163863Sthompsa/* 760163863Sthompsa * This message priority vector is superior to the port priority vector and 761163863Sthompsa * will replace it if, and only if, the message priority vector is better than 762163863Sthompsa * the port priority vector, or the message has been transmitted from the same 763163863Sthompsa * designated bridge and designated port as the port priority vector. 764163863Sthompsa */ 765163863Sthompsastatic int 766163863Sthompsabstp_info_superior(struct bstp_pri_vector *pv, 767163863Sthompsa struct bstp_pri_vector *cpv) 768163863Sthompsa{ 769163863Sthompsa if (bstp_info_cmp(pv, cpv) == INFO_BETTER || 770163863Sthompsa (bstp_same_bridgeid(pv->pv_dbridge_id, cpv->pv_dbridge_id) && 771163863Sthompsa (cpv->pv_dport_id & 0xfff) == (pv->pv_dport_id & 0xfff))) 772163863Sthompsa return (1); 773163863Sthompsa return (0); 774163863Sthompsa} 775163863Sthompsa 776151313Sthompsastatic void 777163863Sthompsabstp_assign_roles(struct bstp_state *bs) 778146985Sthompsa{ 779163863Sthompsa struct bstp_port *bp, *rbp = NULL; 780163863Sthompsa struct bstp_pri_vector pv; 781163863Sthompsa 782163863Sthompsa /* default to our priority vector */ 783163863Sthompsa bs->bs_root_pv = bs->bs_bridge_pv; 784163863Sthompsa bs->bs_root_msg_age = 0; 785163863Sthompsa bs->bs_root_max_age = bs->bs_bridge_max_age; 786163863Sthompsa bs->bs_root_fdelay = bs->bs_bridge_fdelay; 787163863Sthompsa bs->bs_root_htime = bs->bs_bridge_htime; 788163863Sthompsa bs->bs_root_port = NULL; 789163863Sthompsa 790163863Sthompsa /* check if any recieved info supersedes us */ 791163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 792174493Sthompsa if (bp->bp_infois != BSTP_INFO_RECEIVED) 793163863Sthompsa continue; 794163863Sthompsa 795163863Sthompsa pv = bp->bp_port_pv; 796163863Sthompsa pv.pv_cost += bp->bp_path_cost; 797163863Sthompsa 798163863Sthompsa /* 799163863Sthompsa * The root priority vector is the best of the set comprising 800163863Sthompsa * the bridge priority vector plus all root path priority 801163863Sthompsa * vectors whose bridge address is not equal to us. 802163863Sthompsa */ 803163863Sthompsa if (bstp_same_bridgeid(pv.pv_dbridge_id, 804163863Sthompsa bs->bs_bridge_pv.pv_dbridge_id) == 0 && 805163863Sthompsa bstp_info_cmp(&bs->bs_root_pv, &pv) == INFO_BETTER) { 806163863Sthompsa /* the port vector replaces the root */ 807163863Sthompsa bs->bs_root_pv = pv; 808163863Sthompsa bs->bs_root_msg_age = bp->bp_port_msg_age + 809163863Sthompsa BSTP_MESSAGE_AGE_INCR; 810163863Sthompsa bs->bs_root_max_age = bp->bp_port_max_age; 811163863Sthompsa bs->bs_root_fdelay = bp->bp_port_fdelay; 812163863Sthompsa bs->bs_root_htime = bp->bp_port_htime; 813163863Sthompsa rbp = bp; 814163863Sthompsa } 815163863Sthompsa } 816163863Sthompsa 817163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 818163863Sthompsa /* calculate the port designated vector */ 819163863Sthompsa bp->bp_desg_pv.pv_root_id = bs->bs_root_pv.pv_root_id; 820163863Sthompsa bp->bp_desg_pv.pv_cost = bs->bs_root_pv.pv_cost; 821163863Sthompsa bp->bp_desg_pv.pv_dbridge_id = bs->bs_bridge_pv.pv_dbridge_id; 822163863Sthompsa bp->bp_desg_pv.pv_dport_id = bp->bp_port_id; 823163863Sthompsa bp->bp_desg_pv.pv_port_id = bp->bp_port_id; 824163863Sthompsa 825163863Sthompsa /* calculate designated times */ 826163863Sthompsa bp->bp_desg_msg_age = bs->bs_root_msg_age; 827163863Sthompsa bp->bp_desg_max_age = bs->bs_root_max_age; 828163863Sthompsa bp->bp_desg_fdelay = bs->bs_root_fdelay; 829163863Sthompsa bp->bp_desg_htime = bs->bs_bridge_htime; 830163863Sthompsa 831163863Sthompsa 832163863Sthompsa switch (bp->bp_infois) { 833163863Sthompsa case BSTP_INFO_DISABLED: 834163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DISABLED); 835163863Sthompsa break; 836163863Sthompsa 837163863Sthompsa case BSTP_INFO_AGED: 838163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED); 839163863Sthompsa bstp_update_info(bp); 840163863Sthompsa break; 841163863Sthompsa 842163863Sthompsa case BSTP_INFO_MINE: 843163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED); 844163863Sthompsa /* update the port info if stale */ 845163863Sthompsa if (bstp_info_cmp(&bp->bp_port_pv, 846163863Sthompsa &bp->bp_desg_pv) != INFO_SAME || 847163863Sthompsa (rbp != NULL && 848163863Sthompsa (bp->bp_port_msg_age != rbp->bp_port_msg_age || 849163863Sthompsa bp->bp_port_max_age != rbp->bp_port_max_age || 850163863Sthompsa bp->bp_port_fdelay != rbp->bp_port_fdelay || 851163863Sthompsa bp->bp_port_htime != rbp->bp_port_htime))) 852163863Sthompsa bstp_update_info(bp); 853163863Sthompsa break; 854163863Sthompsa 855174493Sthompsa case BSTP_INFO_RECEIVED: 856163863Sthompsa if (bp == rbp) { 857163863Sthompsa /* 858163863Sthompsa * root priority is derived from this 859163863Sthompsa * port, make it the root port. 860163863Sthompsa */ 861163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_ROOT); 862163863Sthompsa bs->bs_root_port = bp; 863163863Sthompsa } else if (bstp_info_cmp(&bp->bp_port_pv, 864163863Sthompsa &bp->bp_desg_pv) == INFO_BETTER) { 865163863Sthompsa /* 866163863Sthompsa * the port priority is lower than the root 867163863Sthompsa * port. 868163863Sthompsa */ 869163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED); 870163863Sthompsa bstp_update_info(bp); 871163863Sthompsa } else { 872163863Sthompsa if (bstp_same_bridgeid( 873163863Sthompsa bp->bp_port_pv.pv_dbridge_id, 874163863Sthompsa bs->bs_bridge_pv.pv_dbridge_id)) { 875163863Sthompsa /* 876163863Sthompsa * the designated bridge refers to 877163863Sthompsa * another port on this bridge. 878163863Sthompsa */ 879163863Sthompsa bstp_set_port_role(bp, 880163863Sthompsa BSTP_ROLE_BACKUP); 881163863Sthompsa } else { 882163863Sthompsa /* 883163863Sthompsa * the port is an inferior path to the 884163863Sthompsa * root bridge. 885163863Sthompsa */ 886163863Sthompsa bstp_set_port_role(bp, 887163863Sthompsa BSTP_ROLE_ALTERNATE); 888163863Sthompsa } 889163863Sthompsa } 890163863Sthompsa break; 891163863Sthompsa } 892163863Sthompsa } 893146985Sthompsa} 894146985Sthompsa 895151313Sthompsastatic void 896163863Sthompsabstp_update_state(struct bstp_state *bs, struct bstp_port *bp) 897146985Sthompsa{ 898163863Sthompsa struct bstp_port *bp2; 899163863Sthompsa int synced; 900146985Sthompsa 901160703Sthompsa BSTP_LOCK_ASSERT(bs); 902146985Sthompsa 903163863Sthompsa /* check if all the ports have syncronised again */ 904163863Sthompsa if (!bs->bs_allsynced) { 905163863Sthompsa synced = 1; 906163863Sthompsa LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) { 907170995Sthompsa if (!(bp2->bp_synced || 908170995Sthompsa bp2->bp_role == BSTP_ROLE_ROOT)) { 909163863Sthompsa synced = 0; 910163863Sthompsa break; 911163863Sthompsa } 912163863Sthompsa } 913163863Sthompsa bs->bs_allsynced = synced; 914163863Sthompsa } 915146985Sthompsa 916163863Sthompsa bstp_update_roles(bs, bp); 917163863Sthompsa bstp_update_tc(bp); 918163863Sthompsa} 919160703Sthompsa 920163863Sthompsastatic void 921163863Sthompsabstp_update_roles(struct bstp_state *bs, struct bstp_port *bp) 922163863Sthompsa{ 923163863Sthompsa switch (bp->bp_role) { 924163863Sthompsa case BSTP_ROLE_DISABLED: 925163863Sthompsa /* Clear any flags if set */ 926163863Sthompsa if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) { 927163863Sthompsa bp->bp_sync = 0; 928163863Sthompsa bp->bp_synced = 1; 929163863Sthompsa bp->bp_reroot = 0; 930163863Sthompsa } 931163863Sthompsa break; 932163863Sthompsa 933163863Sthompsa case BSTP_ROLE_ALTERNATE: 934163863Sthompsa case BSTP_ROLE_BACKUP: 935163863Sthompsa if ((bs->bs_allsynced && !bp->bp_agree) || 936163863Sthompsa (bp->bp_proposed && bp->bp_agree)) { 937163863Sthompsa bp->bp_proposed = 0; 938163863Sthompsa bp->bp_agree = 1; 939163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 940163863Sthompsa DPRINTF("%s -> ALTERNATE_AGREED\n", 941163863Sthompsa bp->bp_ifp->if_xname); 942163863Sthompsa } 943163863Sthompsa 944163863Sthompsa if (bp->bp_proposed && !bp->bp_agree) { 945163863Sthompsa bstp_set_all_sync(bs); 946163863Sthompsa bp->bp_proposed = 0; 947163863Sthompsa DPRINTF("%s -> ALTERNATE_PROPOSED\n", 948163863Sthompsa bp->bp_ifp->if_xname); 949163863Sthompsa } 950163863Sthompsa 951163863Sthompsa /* Clear any flags if set */ 952163863Sthompsa if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) { 953163863Sthompsa bp->bp_sync = 0; 954163863Sthompsa bp->bp_synced = 1; 955163863Sthompsa bp->bp_reroot = 0; 956163863Sthompsa DPRINTF("%s -> ALTERNATE_PORT\n", bp->bp_ifp->if_xname); 957163863Sthompsa } 958163863Sthompsa break; 959163863Sthompsa 960163863Sthompsa case BSTP_ROLE_ROOT: 961163863Sthompsa if (bp->bp_state != BSTP_IFSTATE_FORWARDING && !bp->bp_reroot) { 962163863Sthompsa bstp_set_all_reroot(bs); 963163863Sthompsa DPRINTF("%s -> ROOT_REROOT\n", bp->bp_ifp->if_xname); 964163863Sthompsa } 965163863Sthompsa 966163863Sthompsa if ((bs->bs_allsynced && !bp->bp_agree) || 967163863Sthompsa (bp->bp_proposed && bp->bp_agree)) { 968163863Sthompsa bp->bp_proposed = 0; 969163863Sthompsa bp->bp_sync = 0; 970163863Sthompsa bp->bp_agree = 1; 971163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 972163863Sthompsa DPRINTF("%s -> ROOT_AGREED\n", bp->bp_ifp->if_xname); 973163863Sthompsa } 974163863Sthompsa 975163863Sthompsa if (bp->bp_proposed && !bp->bp_agree) { 976163863Sthompsa bstp_set_all_sync(bs); 977163863Sthompsa bp->bp_proposed = 0; 978163863Sthompsa DPRINTF("%s -> ROOT_PROPOSED\n", bp->bp_ifp->if_xname); 979163863Sthompsa } 980163863Sthompsa 981163904Sthompsa if (bp->bp_state != BSTP_IFSTATE_FORWARDING && 982163904Sthompsa (bp->bp_forward_delay_timer.active == 0 || 983163863Sthompsa (bstp_rerooted(bs, bp) && 984163863Sthompsa bp->bp_recent_backup_timer.active == 0 && 985163904Sthompsa bp->bp_protover == BSTP_PROTO_RSTP))) { 986163863Sthompsa switch (bp->bp_state) { 987163863Sthompsa case BSTP_IFSTATE_DISCARDING: 988163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING); 989163863Sthompsa break; 990163863Sthompsa case BSTP_IFSTATE_LEARNING: 991163863Sthompsa bstp_set_port_state(bp, 992163863Sthompsa BSTP_IFSTATE_FORWARDING); 993163863Sthompsa break; 994163863Sthompsa } 995163863Sthompsa } 996163863Sthompsa 997163863Sthompsa if (bp->bp_state == BSTP_IFSTATE_FORWARDING && bp->bp_reroot) { 998163863Sthompsa bp->bp_reroot = 0; 999163863Sthompsa DPRINTF("%s -> ROOT_REROOTED\n", bp->bp_ifp->if_xname); 1000163863Sthompsa } 1001163863Sthompsa break; 1002163863Sthompsa 1003163863Sthompsa case BSTP_ROLE_DESIGNATED: 1004163863Sthompsa if (bp->bp_recent_root_timer.active == 0 && bp->bp_reroot) { 1005163863Sthompsa bp->bp_reroot = 0; 1006163863Sthompsa DPRINTF("%s -> DESIGNATED_RETIRED\n", 1007163863Sthompsa bp->bp_ifp->if_xname); 1008163863Sthompsa } 1009163863Sthompsa 1010163863Sthompsa if ((bp->bp_state == BSTP_IFSTATE_DISCARDING && 1011163863Sthompsa !bp->bp_synced) || (bp->bp_agreed && !bp->bp_synced) || 1012163863Sthompsa (bp->bp_operedge && !bp->bp_synced) || 1013163863Sthompsa (bp->bp_sync && bp->bp_synced)) { 1014163863Sthompsa bstp_timer_stop(&bp->bp_recent_root_timer); 1015163863Sthompsa bp->bp_synced = 1; 1016163863Sthompsa bp->bp_sync = 0; 1017163863Sthompsa DPRINTF("%s -> DESIGNATED_SYNCED\n", 1018163863Sthompsa bp->bp_ifp->if_xname); 1019163863Sthompsa } 1020163863Sthompsa 1021163863Sthompsa if (bp->bp_state != BSTP_IFSTATE_FORWARDING && 1022163863Sthompsa !bp->bp_agreed && !bp->bp_proposing && 1023163863Sthompsa !bp->bp_operedge) { 1024163863Sthompsa bp->bp_proposing = 1; 1025163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 1026163863Sthompsa bstp_timer_start(&bp->bp_edge_delay_timer, 1027165105Sthompsa (bp->bp_ptp_link ? BSTP_DEFAULT_MIGRATE_DELAY : 1028163863Sthompsa bp->bp_desg_max_age)); 1029163863Sthompsa DPRINTF("%s -> DESIGNATED_PROPOSE\n", 1030163863Sthompsa bp->bp_ifp->if_xname); 1031163863Sthompsa } 1032163863Sthompsa 1033163904Sthompsa if (bp->bp_state != BSTP_IFSTATE_FORWARDING && 1034163904Sthompsa (bp->bp_forward_delay_timer.active == 0 || bp->bp_agreed || 1035163863Sthompsa bp->bp_operedge) && 1036163863Sthompsa (bp->bp_recent_root_timer.active == 0 || !bp->bp_reroot) && 1037163863Sthompsa !bp->bp_sync) { 1038163904Sthompsa if (bp->bp_agreed) 1039163904Sthompsa DPRINTF("%s -> AGREED\n", bp->bp_ifp->if_xname); 1040163895Sthompsa /* 1041163895Sthompsa * If agreed|operedge then go straight to forwarding, 1042163895Sthompsa * otherwise follow discard -> learn -> forward. 1043163895Sthompsa */ 1044163895Sthompsa if (bp->bp_agreed || bp->bp_operedge || 1045163895Sthompsa bp->bp_state == BSTP_IFSTATE_LEARNING) { 1046163863Sthompsa bstp_set_port_state(bp, 1047163863Sthompsa BSTP_IFSTATE_FORWARDING); 1048163863Sthompsa bp->bp_agreed = bp->bp_protover; 1049163895Sthompsa } else if (bp->bp_state == BSTP_IFSTATE_DISCARDING) 1050163895Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING); 1051163863Sthompsa } 1052163863Sthompsa 1053163863Sthompsa if (((bp->bp_sync && !bp->bp_synced) || 1054163863Sthompsa (bp->bp_reroot && bp->bp_recent_root_timer.active) || 1055163863Sthompsa (bp->bp_flags & BSTP_PORT_DISPUTED)) && !bp->bp_operedge && 1056163863Sthompsa bp->bp_state != BSTP_IFSTATE_DISCARDING) { 1057163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING); 1058163863Sthompsa bp->bp_flags &= ~BSTP_PORT_DISPUTED; 1059163863Sthompsa bstp_timer_start(&bp->bp_forward_delay_timer, 1060163863Sthompsa bp->bp_protover == BSTP_PROTO_RSTP ? 1061163863Sthompsa bp->bp_desg_htime : bp->bp_desg_fdelay); 1062163863Sthompsa DPRINTF("%s -> DESIGNATED_DISCARD\n", 1063163863Sthompsa bp->bp_ifp->if_xname); 1064163863Sthompsa } 1065163863Sthompsa break; 1066146985Sthompsa } 1067163863Sthompsa 1068163863Sthompsa if (bp->bp_flags & BSTP_PORT_NEWINFO) 1069163863Sthompsa bstp_transmit(bs, bp); 1070146985Sthompsa} 1071146985Sthompsa 1072151313Sthompsastatic void 1073163863Sthompsabstp_update_tc(struct bstp_port *bp) 1074146985Sthompsa{ 1075163863Sthompsa switch (bp->bp_tcstate) { 1076163863Sthompsa case BSTP_TCSTATE_ACTIVE: 1077163863Sthompsa if ((bp->bp_role != BSTP_ROLE_DESIGNATED && 1078163863Sthompsa bp->bp_role != BSTP_ROLE_ROOT) || bp->bp_operedge) 1079163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING); 1080163863Sthompsa 1081163863Sthompsa if (bp->bp_rcvdtcn) 1082163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_TCN); 1083163863Sthompsa if (bp->bp_rcvdtc) 1084163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_TC); 1085163863Sthompsa 1086163863Sthompsa if (bp->bp_tc_prop && !bp->bp_operedge) 1087163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_PROPAG); 1088163863Sthompsa 1089163863Sthompsa if (bp->bp_rcvdtca) 1090163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_ACK); 1091163863Sthompsa break; 1092163863Sthompsa 1093163863Sthompsa case BSTP_TCSTATE_INACTIVE: 1094163863Sthompsa if ((bp->bp_state == BSTP_IFSTATE_LEARNING || 1095163863Sthompsa bp->bp_state == BSTP_IFSTATE_FORWARDING) && 1096163863Sthompsa bp->bp_fdbflush == 0) 1097163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING); 1098163863Sthompsa break; 1099163863Sthompsa 1100163863Sthompsa case BSTP_TCSTATE_LEARNING: 1101163863Sthompsa if (bp->bp_rcvdtc || bp->bp_rcvdtcn || bp->bp_rcvdtca || 1102163863Sthompsa bp->bp_tc_prop) 1103163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING); 1104163863Sthompsa else if (bp->bp_role != BSTP_ROLE_DESIGNATED && 1105163863Sthompsa bp->bp_role != BSTP_ROLE_ROOT && 1106163863Sthompsa bp->bp_state == BSTP_IFSTATE_DISCARDING) 1107163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE); 1108163863Sthompsa 1109163863Sthompsa if ((bp->bp_role == BSTP_ROLE_DESIGNATED || 1110163863Sthompsa bp->bp_role == BSTP_ROLE_ROOT) && 1111163863Sthompsa bp->bp_state == BSTP_IFSTATE_FORWARDING && 1112163863Sthompsa !bp->bp_operedge) 1113163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_DETECTED); 1114163863Sthompsa break; 1115163863Sthompsa 1116163863Sthompsa /* these are transient states and go straight back to ACTIVE */ 1117163863Sthompsa case BSTP_TCSTATE_DETECTED: 1118163863Sthompsa case BSTP_TCSTATE_TCN: 1119163863Sthompsa case BSTP_TCSTATE_TC: 1120163863Sthompsa case BSTP_TCSTATE_PROPAG: 1121163863Sthompsa case BSTP_TCSTATE_ACK: 1122163863Sthompsa DPRINTF("Invalid TC state for %s\n", 1123163863Sthompsa bp->bp_ifp->if_xname); 1124163863Sthompsa break; 1125146985Sthompsa } 1126163863Sthompsa 1127146985Sthompsa} 1128146985Sthompsa 1129163863Sthompsastatic void 1130163863Sthompsabstp_update_info(struct bstp_port *bp) 1131146985Sthompsa{ 1132163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1133146985Sthompsa 1134163863Sthompsa bp->bp_proposing = 0; 1135163863Sthompsa bp->bp_proposed = 0; 1136146985Sthompsa 1137163863Sthompsa if (bp->bp_agreed && !bstp_pdu_bettersame(bp, BSTP_INFO_MINE)) 1138163863Sthompsa bp->bp_agreed = 0; 1139160703Sthompsa 1140163863Sthompsa if (bp->bp_synced && !bp->bp_agreed) { 1141163863Sthompsa bp->bp_synced = 0; 1142163863Sthompsa bs->bs_allsynced = 0; 1143146985Sthompsa } 1144163863Sthompsa 1145163863Sthompsa /* copy the designated pv to the port */ 1146163863Sthompsa bp->bp_port_pv = bp->bp_desg_pv; 1147163863Sthompsa bp->bp_port_msg_age = bp->bp_desg_msg_age; 1148163863Sthompsa bp->bp_port_max_age = bp->bp_desg_max_age; 1149163863Sthompsa bp->bp_port_fdelay = bp->bp_desg_fdelay; 1150163863Sthompsa bp->bp_port_htime = bp->bp_desg_htime; 1151163863Sthompsa bp->bp_infois = BSTP_INFO_MINE; 1152163863Sthompsa 1153163926Sthompsa /* Set transmit flag but do not immediately send */ 1154163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 1155146985Sthompsa} 1156146985Sthompsa 1157163863Sthompsa/* set tcprop on every port other than the caller */ 1158151313Sthompsastatic void 1159163863Sthompsabstp_set_other_tcprop(struct bstp_port *bp) 1160146985Sthompsa{ 1161163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1162163863Sthompsa struct bstp_port *bp2; 1163163863Sthompsa 1164160703Sthompsa BSTP_LOCK_ASSERT(bs); 1165160703Sthompsa 1166163863Sthompsa LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) { 1167163863Sthompsa if (bp2 == bp) 1168163863Sthompsa continue; 1169166083Sthompsa bp2->bp_tc_prop = 1; 1170163863Sthompsa } 1171146985Sthompsa} 1172146985Sthompsa 1173151313Sthompsastatic void 1174163863Sthompsabstp_set_all_reroot(struct bstp_state *bs) 1175146985Sthompsa{ 1176163863Sthompsa struct bstp_port *bp; 1177163863Sthompsa 1178160703Sthompsa BSTP_LOCK_ASSERT(bs); 1179160703Sthompsa 1180163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) 1181163863Sthompsa bp->bp_reroot = 1; 1182146985Sthompsa} 1183146985Sthompsa 1184151313Sthompsastatic void 1185163863Sthompsabstp_set_all_sync(struct bstp_state *bs) 1186146985Sthompsa{ 1187163863Sthompsa struct bstp_port *bp; 1188163863Sthompsa 1189163863Sthompsa BSTP_LOCK_ASSERT(bs); 1190163863Sthompsa 1191163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 1192163863Sthompsa bp->bp_sync = 1; 1193163863Sthompsa bp->bp_synced = 0; /* Not explicit in spec */ 1194163863Sthompsa } 1195163863Sthompsa 1196163863Sthompsa bs->bs_allsynced = 0; 1197146985Sthompsa} 1198146985Sthompsa 1199163863Sthompsastatic void 1200163863Sthompsabstp_set_port_state(struct bstp_port *bp, int state) 1201156096Sthompsa{ 1202163863Sthompsa if (bp->bp_state == state) 1203163863Sthompsa return; 1204156096Sthompsa 1205163863Sthompsa bp->bp_state = state; 1206163863Sthompsa 1207163863Sthompsa switch (bp->bp_state) { 1208163863Sthompsa case BSTP_IFSTATE_DISCARDING: 1209163863Sthompsa DPRINTF("state changed to DISCARDING on %s\n", 1210163863Sthompsa bp->bp_ifp->if_xname); 1211163863Sthompsa break; 1212163863Sthompsa 1213163863Sthompsa case BSTP_IFSTATE_LEARNING: 1214163863Sthompsa DPRINTF("state changed to LEARNING on %s\n", 1215163863Sthompsa bp->bp_ifp->if_xname); 1216163863Sthompsa 1217163863Sthompsa bstp_timer_start(&bp->bp_forward_delay_timer, 1218163863Sthompsa bp->bp_protover == BSTP_PROTO_RSTP ? 1219163863Sthompsa bp->bp_desg_htime : bp->bp_desg_fdelay); 1220163863Sthompsa break; 1221163863Sthompsa 1222163863Sthompsa case BSTP_IFSTATE_FORWARDING: 1223163863Sthompsa DPRINTF("state changed to FORWARDING on %s\n", 1224163863Sthompsa bp->bp_ifp->if_xname); 1225163863Sthompsa 1226163863Sthompsa bstp_timer_stop(&bp->bp_forward_delay_timer); 1227163863Sthompsa /* Record that we enabled forwarding */ 1228163863Sthompsa bp->bp_forward_transitions++; 1229163863Sthompsa break; 1230156096Sthompsa } 1231156096Sthompsa 1232163863Sthompsa /* notify the parent bridge */ 1233163863Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_statetask); 1234156096Sthompsa} 1235156096Sthompsa 1236163863Sthompsastatic void 1237163863Sthompsabstp_set_port_role(struct bstp_port *bp, int role) 1238146985Sthompsa{ 1239163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1240146985Sthompsa 1241163863Sthompsa if (bp->bp_role == role) 1242163863Sthompsa return; 1243149065Sthompsa 1244163863Sthompsa /* perform pre-change tasks */ 1245163863Sthompsa switch (bp->bp_role) { 1246163863Sthompsa case BSTP_ROLE_DISABLED: 1247163863Sthompsa bstp_timer_start(&bp->bp_forward_delay_timer, 1248163863Sthompsa bp->bp_desg_max_age); 1249163863Sthompsa break; 1250146985Sthompsa 1251163863Sthompsa case BSTP_ROLE_BACKUP: 1252163863Sthompsa bstp_timer_start(&bp->bp_recent_backup_timer, 1253163863Sthompsa bp->bp_desg_htime * 2); 1254163863Sthompsa /* fall through */ 1255163863Sthompsa case BSTP_ROLE_ALTERNATE: 1256163863Sthompsa bstp_timer_start(&bp->bp_forward_delay_timer, 1257163863Sthompsa bp->bp_desg_fdelay); 1258163863Sthompsa bp->bp_sync = 0; 1259163863Sthompsa bp->bp_synced = 1; 1260163863Sthompsa bp->bp_reroot = 0; 1261163863Sthompsa break; 1262163863Sthompsa 1263163863Sthompsa case BSTP_ROLE_ROOT: 1264163863Sthompsa bstp_timer_start(&bp->bp_recent_root_timer, 1265163863Sthompsa BSTP_DEFAULT_FORWARD_DELAY); 1266163863Sthompsa break; 1267146985Sthompsa } 1268146985Sthompsa 1269163863Sthompsa bp->bp_role = role; 1270163863Sthompsa /* clear values not carried between roles */ 1271163863Sthompsa bp->bp_proposing = 0; 1272163863Sthompsa bs->bs_allsynced = 0; 1273146985Sthompsa 1274163863Sthompsa /* initialise the new role */ 1275163863Sthompsa switch (bp->bp_role) { 1276163863Sthompsa case BSTP_ROLE_DISABLED: 1277163863Sthompsa case BSTP_ROLE_ALTERNATE: 1278163863Sthompsa case BSTP_ROLE_BACKUP: 1279163863Sthompsa DPRINTF("%s role -> ALT/BACK/DISABLED\n", 1280163863Sthompsa bp->bp_ifp->if_xname); 1281163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING); 1282163863Sthompsa bstp_timer_stop(&bp->bp_recent_root_timer); 1283163863Sthompsa bstp_timer_latch(&bp->bp_forward_delay_timer); 1284163863Sthompsa bp->bp_sync = 0; 1285163863Sthompsa bp->bp_synced = 1; 1286163863Sthompsa bp->bp_reroot = 0; 1287163863Sthompsa break; 1288146985Sthompsa 1289163863Sthompsa case BSTP_ROLE_ROOT: 1290163863Sthompsa DPRINTF("%s role -> ROOT\n", 1291163863Sthompsa bp->bp_ifp->if_xname); 1292163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING); 1293163863Sthompsa bstp_timer_latch(&bp->bp_recent_root_timer); 1294163863Sthompsa bp->bp_proposing = 0; 1295163863Sthompsa break; 1296146985Sthompsa 1297163863Sthompsa case BSTP_ROLE_DESIGNATED: 1298163863Sthompsa DPRINTF("%s role -> DESIGNATED\n", 1299163863Sthompsa bp->bp_ifp->if_xname); 1300163863Sthompsa bstp_timer_start(&bp->bp_hello_timer, 1301163863Sthompsa bp->bp_desg_htime); 1302163863Sthompsa bp->bp_agree = 0; 1303163863Sthompsa break; 1304163863Sthompsa } 1305146985Sthompsa 1306163863Sthompsa /* let the TC state know that the role changed */ 1307163863Sthompsa bstp_update_tc(bp); 1308160703Sthompsa} 1309160703Sthompsa 1310163863Sthompsastatic void 1311163863Sthompsabstp_set_port_proto(struct bstp_port *bp, int proto) 1312160703Sthompsa{ 1313163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1314160703Sthompsa 1315163863Sthompsa /* supported protocol versions */ 1316163863Sthompsa switch (proto) { 1317163863Sthompsa case BSTP_PROTO_STP: 1318163863Sthompsa /* we can downgrade protocols only */ 1319163863Sthompsa bstp_timer_stop(&bp->bp_migrate_delay_timer); 1320163863Sthompsa /* clear unsupported features */ 1321163863Sthompsa bp->bp_operedge = 0; 1322171724Sthompsa /* STP compat mode only uses 16 bits of the 32 */ 1323171724Sthompsa if (bp->bp_path_cost > 65535) 1324171724Sthompsa bp->bp_path_cost = 65535; 1325163863Sthompsa break; 1326163863Sthompsa 1327163863Sthompsa case BSTP_PROTO_RSTP: 1328163863Sthompsa bstp_timer_start(&bp->bp_migrate_delay_timer, 1329163863Sthompsa bs->bs_migration_delay); 1330163863Sthompsa break; 1331163863Sthompsa 1332163863Sthompsa default: 1333163863Sthompsa DPRINTF("Unsupported STP version %d\n", proto); 1334163863Sthompsa return; 1335146985Sthompsa } 1336163863Sthompsa 1337163863Sthompsa bp->bp_protover = proto; 1338163863Sthompsa bp->bp_flags &= ~BSTP_PORT_CANMIGRATE; 1339160703Sthompsa} 1340146985Sthompsa 1341163863Sthompsastatic void 1342163863Sthompsabstp_set_port_tc(struct bstp_port *bp, int state) 1343163863Sthompsa{ 1344163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1345160703Sthompsa 1346163863Sthompsa bp->bp_tcstate = state; 1347160703Sthompsa 1348163863Sthompsa /* initialise the new state */ 1349163863Sthompsa switch (bp->bp_tcstate) { 1350163863Sthompsa case BSTP_TCSTATE_ACTIVE: 1351163863Sthompsa DPRINTF("%s -> TC_ACTIVE\n", bp->bp_ifp->if_xname); 1352163863Sthompsa /* nothing to do */ 1353163863Sthompsa break; 1354160703Sthompsa 1355163863Sthompsa case BSTP_TCSTATE_INACTIVE: 1356163863Sthompsa bstp_timer_stop(&bp->bp_tc_timer); 1357163863Sthompsa /* flush routes on the parent bridge */ 1358163863Sthompsa bp->bp_fdbflush = 1; 1359163863Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_rtagetask); 1360163863Sthompsa bp->bp_tc_ack = 0; 1361163863Sthompsa DPRINTF("%s -> TC_INACTIVE\n", bp->bp_ifp->if_xname); 1362163863Sthompsa break; 1363160703Sthompsa 1364163863Sthompsa case BSTP_TCSTATE_LEARNING: 1365163863Sthompsa bp->bp_rcvdtc = 0; 1366163863Sthompsa bp->bp_rcvdtcn = 0; 1367163863Sthompsa bp->bp_rcvdtca = 0; 1368163863Sthompsa bp->bp_tc_prop = 0; 1369163863Sthompsa DPRINTF("%s -> TC_LEARNING\n", bp->bp_ifp->if_xname); 1370163863Sthompsa break; 1371163863Sthompsa 1372163863Sthompsa case BSTP_TCSTATE_DETECTED: 1373163863Sthompsa bstp_set_timer_tc(bp); 1374163863Sthompsa bstp_set_other_tcprop(bp); 1375163863Sthompsa /* send out notification */ 1376163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 1377163863Sthompsa bstp_transmit(bs, bp); 1378163863Sthompsa getmicrotime(&bs->bs_last_tc_time); 1379163863Sthompsa DPRINTF("%s -> TC_DETECTED\n", bp->bp_ifp->if_xname); 1380163863Sthompsa bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */ 1381163863Sthompsa break; 1382163863Sthompsa 1383163863Sthompsa case BSTP_TCSTATE_TCN: 1384163863Sthompsa bstp_set_timer_tc(bp); 1385163863Sthompsa DPRINTF("%s -> TC_TCN\n", bp->bp_ifp->if_xname); 1386163863Sthompsa /* fall through */ 1387163863Sthompsa case BSTP_TCSTATE_TC: 1388163863Sthompsa bp->bp_rcvdtc = 0; 1389163863Sthompsa bp->bp_rcvdtcn = 0; 1390163863Sthompsa if (bp->bp_role == BSTP_ROLE_DESIGNATED) 1391163863Sthompsa bp->bp_tc_ack = 1; 1392163863Sthompsa 1393163863Sthompsa bstp_set_other_tcprop(bp); 1394163863Sthompsa DPRINTF("%s -> TC_TC\n", bp->bp_ifp->if_xname); 1395163863Sthompsa bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */ 1396163863Sthompsa break; 1397163863Sthompsa 1398163863Sthompsa case BSTP_TCSTATE_PROPAG: 1399163863Sthompsa /* flush routes on the parent bridge */ 1400163863Sthompsa bp->bp_fdbflush = 1; 1401163863Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_rtagetask); 1402163863Sthompsa bp->bp_tc_prop = 0; 1403163863Sthompsa bstp_set_timer_tc(bp); 1404163863Sthompsa DPRINTF("%s -> TC_PROPAG\n", bp->bp_ifp->if_xname); 1405163863Sthompsa bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */ 1406163863Sthompsa break; 1407163863Sthompsa 1408163863Sthompsa case BSTP_TCSTATE_ACK: 1409163863Sthompsa bstp_timer_stop(&bp->bp_tc_timer); 1410163863Sthompsa bp->bp_rcvdtca = 0; 1411163863Sthompsa DPRINTF("%s -> TC_ACK\n", bp->bp_ifp->if_xname); 1412163863Sthompsa bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */ 1413163863Sthompsa break; 1414163863Sthompsa } 1415146985Sthompsa} 1416146985Sthompsa 1417163863Sthompsastatic void 1418163863Sthompsabstp_set_timer_tc(struct bstp_port *bp) 1419146985Sthompsa{ 1420163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1421146985Sthompsa 1422163863Sthompsa if (bp->bp_tc_timer.active) 1423163863Sthompsa return; 1424163863Sthompsa 1425163863Sthompsa switch (bp->bp_protover) { 1426163863Sthompsa case BSTP_PROTO_RSTP: 1427163863Sthompsa bstp_timer_start(&bp->bp_tc_timer, 1428163863Sthompsa bp->bp_desg_htime + BSTP_TICK_VAL); 1429163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 1430163863Sthompsa break; 1431163863Sthompsa 1432163863Sthompsa case BSTP_PROTO_STP: 1433163863Sthompsa bstp_timer_start(&bp->bp_tc_timer, 1434163863Sthompsa bs->bs_root_max_age + bs->bs_root_fdelay); 1435163863Sthompsa break; 1436163863Sthompsa } 1437160703Sthompsa} 1438146985Sthompsa 1439163863Sthompsastatic void 1440163863Sthompsabstp_set_timer_msgage(struct bstp_port *bp) 1441160703Sthompsa{ 1442163863Sthompsa if (bp->bp_port_msg_age + BSTP_MESSAGE_AGE_INCR <= 1443163863Sthompsa bp->bp_port_max_age) { 1444163863Sthompsa bstp_timer_start(&bp->bp_message_age_timer, 1445163863Sthompsa bp->bp_port_htime * 3); 1446163863Sthompsa } else 1447163863Sthompsa /* expires immediately */ 1448163863Sthompsa bstp_timer_start(&bp->bp_message_age_timer, 0); 1449160703Sthompsa} 1450160703Sthompsa 1451163863Sthompsastatic int 1452163863Sthompsabstp_rerooted(struct bstp_state *bs, struct bstp_port *bp) 1453160703Sthompsa{ 1454163863Sthompsa struct bstp_port *bp2; 1455163863Sthompsa int rr_set = 0; 1456160703Sthompsa 1457163863Sthompsa LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) { 1458163863Sthompsa if (bp2 == bp) 1459163863Sthompsa continue; 1460163863Sthompsa if (bp2->bp_recent_root_timer.active) { 1461163863Sthompsa rr_set = 1; 1462163863Sthompsa break; 1463163863Sthompsa } 1464146985Sthompsa } 1465163863Sthompsa return (!rr_set); 1466163863Sthompsa} 1467146985Sthompsa 1468163863Sthompsaint 1469163863Sthompsabstp_set_htime(struct bstp_state *bs, int t) 1470163863Sthompsa{ 1471163863Sthompsa /* convert seconds to ticks */ 1472163863Sthompsa t *= BSTP_TICK_VAL; 1473146985Sthompsa 1474163863Sthompsa /* value can only be changed in leagacy stp mode */ 1475163863Sthompsa if (bs->bs_protover != BSTP_PROTO_STP) 1476163863Sthompsa return (EPERM); 1477146985Sthompsa 1478163863Sthompsa if (t < BSTP_MIN_HELLO_TIME || t > BSTP_MAX_HELLO_TIME) 1479163863Sthompsa return (EINVAL); 1480163863Sthompsa 1481163863Sthompsa BSTP_LOCK(bs); 1482163863Sthompsa bs->bs_bridge_htime = t; 1483163863Sthompsa bstp_reinit(bs); 1484160703Sthompsa BSTP_UNLOCK(bs); 1485163863Sthompsa return (0); 1486146985Sthompsa} 1487146985Sthompsa 1488163863Sthompsaint 1489163863Sthompsabstp_set_fdelay(struct bstp_state *bs, int t) 1490146985Sthompsa{ 1491163863Sthompsa /* convert seconds to ticks */ 1492163863Sthompsa t *= BSTP_TICK_VAL; 1493160703Sthompsa 1494163863Sthompsa if (t < BSTP_MIN_FORWARD_DELAY || t > BSTP_MAX_FORWARD_DELAY) 1495163863Sthompsa return (EINVAL); 1496163863Sthompsa 1497163863Sthompsa BSTP_LOCK(bs); 1498163863Sthompsa bs->bs_bridge_fdelay = t; 1499163863Sthompsa bstp_reinit(bs); 1500163863Sthompsa BSTP_UNLOCK(bs); 1501163863Sthompsa return (0); 1502146985Sthompsa} 1503146985Sthompsa 1504163863Sthompsaint 1505163863Sthompsabstp_set_maxage(struct bstp_state *bs, int t) 1506146985Sthompsa{ 1507163863Sthompsa /* convert seconds to ticks */ 1508163863Sthompsa t *= BSTP_TICK_VAL; 1509163863Sthompsa 1510163863Sthompsa if (t < BSTP_MIN_MAX_AGE || t > BSTP_MAX_MAX_AGE) 1511163863Sthompsa return (EINVAL); 1512163863Sthompsa 1513163863Sthompsa BSTP_LOCK(bs); 1514163863Sthompsa bs->bs_bridge_max_age = t; 1515163863Sthompsa bstp_reinit(bs); 1516163863Sthompsa BSTP_UNLOCK(bs); 1517163863Sthompsa return (0); 1518146985Sthompsa} 1519146985Sthompsa 1520163863Sthompsaint 1521163863Sthompsabstp_set_holdcount(struct bstp_state *bs, int count) 1522146985Sthompsa{ 1523163863Sthompsa struct bstp_port *bp; 1524146985Sthompsa 1525163863Sthompsa if (count < BSTP_MIN_HOLD_COUNT || 1526163863Sthompsa count > BSTP_MAX_HOLD_COUNT) 1527163863Sthompsa return (EINVAL); 1528146985Sthompsa 1529163863Sthompsa BSTP_LOCK(bs); 1530163863Sthompsa bs->bs_txholdcount = count; 1531163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) 1532163863Sthompsa bp->bp_txcount = 0; 1533163863Sthompsa BSTP_UNLOCK(bs); 1534163863Sthompsa return (0); 1535146985Sthompsa} 1536146985Sthompsa 1537163863Sthompsaint 1538163863Sthompsabstp_set_protocol(struct bstp_state *bs, int proto) 1539146985Sthompsa{ 1540160703Sthompsa struct bstp_port *bp; 1541146985Sthompsa 1542163863Sthompsa switch (proto) { 1543163863Sthompsa /* Supported protocol versions */ 1544163863Sthompsa case BSTP_PROTO_STP: 1545163863Sthompsa case BSTP_PROTO_RSTP: 1546163863Sthompsa break; 1547146985Sthompsa 1548163863Sthompsa default: 1549163863Sthompsa return (EINVAL); 1550163863Sthompsa } 1551146985Sthompsa 1552163863Sthompsa BSTP_LOCK(bs); 1553163863Sthompsa bs->bs_protover = proto; 1554163863Sthompsa bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME; 1555160703Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 1556163863Sthompsa /* reinit state */ 1557163863Sthompsa bp->bp_infois = BSTP_INFO_DISABLED; 1558163863Sthompsa bp->bp_txcount = 0; 1559163863Sthompsa bstp_set_port_proto(bp, bs->bs_protover); 1560163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DISABLED); 1561163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE); 1562163863Sthompsa bstp_timer_stop(&bp->bp_recent_backup_timer); 1563146985Sthompsa } 1564163863Sthompsa bstp_reinit(bs); 1565163863Sthompsa BSTP_UNLOCK(bs); 1566163863Sthompsa return (0); 1567163863Sthompsa} 1568146985Sthompsa 1569163863Sthompsaint 1570163863Sthompsabstp_set_priority(struct bstp_state *bs, int pri) 1571163863Sthompsa{ 1572163863Sthompsa if (pri < 0 || pri > BSTP_MAX_PRIORITY) 1573163863Sthompsa return (EINVAL); 1574146985Sthompsa 1575163863Sthompsa /* Limit to steps of 4096 */ 1576163863Sthompsa pri -= pri % 4096; 1577146985Sthompsa 1578163863Sthompsa BSTP_LOCK(bs); 1579163863Sthompsa bs->bs_bridge_priority = pri; 1580163863Sthompsa bstp_reinit(bs); 1581163863Sthompsa BSTP_UNLOCK(bs); 1582163863Sthompsa return (0); 1583163863Sthompsa} 1584146985Sthompsa 1585163863Sthompsaint 1586163863Sthompsabstp_set_port_priority(struct bstp_port *bp, int pri) 1587163863Sthompsa{ 1588163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1589163863Sthompsa 1590163863Sthompsa if (pri < 0 || pri > BSTP_MAX_PORT_PRIORITY) 1591163863Sthompsa return (EINVAL); 1592163863Sthompsa 1593163863Sthompsa /* Limit to steps of 16 */ 1594163863Sthompsa pri -= pri % 16; 1595163863Sthompsa 1596163863Sthompsa BSTP_LOCK(bs); 1597163863Sthompsa bp->bp_priority = pri; 1598163863Sthompsa bstp_reinit(bs); 1599163863Sthompsa BSTP_UNLOCK(bs); 1600163863Sthompsa return (0); 1601146985Sthompsa} 1602146985Sthompsa 1603163863Sthompsaint 1604163863Sthompsabstp_set_path_cost(struct bstp_port *bp, uint32_t path_cost) 1605146985Sthompsa{ 1606163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1607146985Sthompsa 1608163863Sthompsa if (path_cost > BSTP_MAX_PATH_COST) 1609163863Sthompsa return (EINVAL); 1610146985Sthompsa 1611171724Sthompsa /* STP compat mode only uses 16 bits of the 32 */ 1612171724Sthompsa if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535) 1613171724Sthompsa path_cost = 65535; 1614171724Sthompsa 1615163863Sthompsa BSTP_LOCK(bs); 1616163863Sthompsa 1617163863Sthompsa if (path_cost == 0) { /* use auto */ 1618163863Sthompsa bp->bp_flags &= ~BSTP_PORT_ADMCOST; 1619163863Sthompsa bp->bp_path_cost = bstp_calc_path_cost(bp); 1620163863Sthompsa } else { 1621163863Sthompsa bp->bp_path_cost = path_cost; 1622163863Sthompsa bp->bp_flags |= BSTP_PORT_ADMCOST; 1623146985Sthompsa } 1624163863Sthompsa bstp_reinit(bs); 1625163863Sthompsa BSTP_UNLOCK(bs); 1626163863Sthompsa return (0); 1627146985Sthompsa} 1628146985Sthompsa 1629163863Sthompsaint 1630163863Sthompsabstp_set_edge(struct bstp_port *bp, int set) 1631146985Sthompsa{ 1632163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1633163863Sthompsa 1634163863Sthompsa BSTP_LOCK(bs); 1635164880Ssyrinx if ((bp->bp_operedge = set) == 0) 1636164880Ssyrinx bp->bp_flags &= ~BSTP_PORT_ADMEDGE; 1637164880Ssyrinx else 1638164880Ssyrinx bp->bp_flags |= BSTP_PORT_ADMEDGE; 1639163863Sthompsa BSTP_UNLOCK(bs); 1640163863Sthompsa return (0); 1641146985Sthompsa} 1642146985Sthompsa 1643163863Sthompsaint 1644163863Sthompsabstp_set_autoedge(struct bstp_port *bp, int set) 1645146985Sthompsa{ 1646163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1647163863Sthompsa 1648163863Sthompsa BSTP_LOCK(bs); 1649163863Sthompsa if (set) { 1650163863Sthompsa bp->bp_flags |= BSTP_PORT_AUTOEDGE; 1651163863Sthompsa /* we may be able to transition straight to edge */ 1652163863Sthompsa if (bp->bp_edge_delay_timer.active == 0) 1653163863Sthompsa bstp_edge_delay_expiry(bs, bp); 1654163863Sthompsa } else 1655163863Sthompsa bp->bp_flags &= ~BSTP_PORT_AUTOEDGE; 1656163863Sthompsa BSTP_UNLOCK(bs); 1657163863Sthompsa return (0); 1658146985Sthompsa} 1659164653Sthompsa 1660164653Sthompsaint 1661165105Sthompsabstp_set_ptp(struct bstp_port *bp, int set) 1662164653Sthompsa{ 1663164653Sthompsa struct bstp_state *bs = bp->bp_bs; 1664164653Sthompsa 1665164653Sthompsa BSTP_LOCK(bs); 1666165105Sthompsa bp->bp_ptp_link = set; 1667164653Sthompsa BSTP_UNLOCK(bs); 1668164653Sthompsa return (0); 1669164653Sthompsa} 1670164653Sthompsa 1671164653Sthompsaint 1672165105Sthompsabstp_set_autoptp(struct bstp_port *bp, int set) 1673164653Sthompsa{ 1674164653Sthompsa struct bstp_state *bs = bp->bp_bs; 1675164653Sthompsa 1676164653Sthompsa BSTP_LOCK(bs); 1677164653Sthompsa if (set) { 1678165105Sthompsa bp->bp_flags |= BSTP_PORT_AUTOPTP; 1679171886Sthompsa if (bp->bp_role != BSTP_ROLE_DISABLED) 1680234488Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask); 1681164653Sthompsa } else 1682165105Sthompsa bp->bp_flags &= ~BSTP_PORT_AUTOPTP; 1683164653Sthompsa BSTP_UNLOCK(bs); 1684164653Sthompsa return (0); 1685164653Sthompsa} 1686164653Sthompsa 1687163863Sthompsa/* 1688163863Sthompsa * Calculate the path cost according to the link speed. 1689163863Sthompsa */ 1690163863Sthompsastatic uint32_t 1691163863Sthompsabstp_calc_path_cost(struct bstp_port *bp) 1692163863Sthompsa{ 1693163863Sthompsa struct ifnet *ifp = bp->bp_ifp; 1694163863Sthompsa uint32_t path_cost; 1695146985Sthompsa 1696163863Sthompsa /* If the priority has been manually set then retain the value */ 1697163863Sthompsa if (bp->bp_flags & BSTP_PORT_ADMCOST) 1698163863Sthompsa return bp->bp_path_cost; 1699163863Sthompsa 1700171724Sthompsa if (ifp->if_link_state == LINK_STATE_DOWN) { 1701171724Sthompsa /* Recalc when the link comes up again */ 1702171724Sthompsa bp->bp_flags |= BSTP_PORT_PNDCOST; 1703171724Sthompsa return (BSTP_DEFAULT_PATH_COST); 1704171724Sthompsa } 1705171724Sthompsa 1706163863Sthompsa if (ifp->if_baudrate < 1000) 1707163863Sthompsa return (BSTP_DEFAULT_PATH_COST); 1708163863Sthompsa 1709163863Sthompsa /* formula from section 17.14, IEEE Std 802.1D-2004 */ 1710164653Sthompsa path_cost = 20000000000ULL / (ifp->if_baudrate / 1000); 1711163863Sthompsa 1712163863Sthompsa if (path_cost > BSTP_MAX_PATH_COST) 1713163863Sthompsa path_cost = BSTP_MAX_PATH_COST; 1714163863Sthompsa 1715163863Sthompsa /* STP compat mode only uses 16 bits of the 32 */ 1716163863Sthompsa if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535) 1717163863Sthompsa path_cost = 65535; 1718163863Sthompsa 1719163863Sthompsa return (path_cost); 1720163863Sthompsa} 1721163863Sthompsa 1722163863Sthompsa/* 1723163863Sthompsa * Notify the bridge that a port state has changed, we need to do this from a 1724163863Sthompsa * taskqueue to avoid a LOR. 1725163863Sthompsa */ 1726151313Sthompsastatic void 1727163863Sthompsabstp_notify_state(void *arg, int pending) 1728146985Sthompsa{ 1729163863Sthompsa struct bstp_port *bp = (struct bstp_port *)arg; 1730163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1731163863Sthompsa 1732163863Sthompsa if (bp->bp_active == 1 && bs->bs_state_cb != NULL) 1733163863Sthompsa (*bs->bs_state_cb)(bp->bp_ifp, bp->bp_state); 1734146985Sthompsa} 1735146985Sthompsa 1736163863Sthompsa/* 1737163863Sthompsa * Flush the routes on the bridge port, we need to do this from a 1738163863Sthompsa * taskqueue to avoid a LOR. 1739163863Sthompsa */ 1740160703Sthompsastatic void 1741163863Sthompsabstp_notify_rtage(void *arg, int pending) 1742160703Sthompsa{ 1743163863Sthompsa struct bstp_port *bp = (struct bstp_port *)arg; 1744163863Sthompsa struct bstp_state *bs = bp->bp_bs; 1745163863Sthompsa int age = 0; 1746160703Sthompsa 1747163863Sthompsa BSTP_LOCK(bs); 1748163863Sthompsa switch (bp->bp_protover) { 1749163863Sthompsa case BSTP_PROTO_STP: 1750163863Sthompsa /* convert to seconds */ 1751163863Sthompsa age = bp->bp_desg_fdelay / BSTP_TICK_VAL; 1752163863Sthompsa break; 1753160703Sthompsa 1754163863Sthompsa case BSTP_PROTO_RSTP: 1755163863Sthompsa age = 0; 1756163863Sthompsa break; 1757163863Sthompsa } 1758163863Sthompsa BSTP_UNLOCK(bs); 1759163863Sthompsa 1760163863Sthompsa if (bp->bp_active == 1 && bs->bs_rtage_cb != NULL) 1761163863Sthompsa (*bs->bs_rtage_cb)(bp->bp_ifp, age); 1762163863Sthompsa 1763163863Sthompsa /* flush is complete */ 1764163863Sthompsa BSTP_LOCK(bs); 1765163863Sthompsa bp->bp_fdbflush = 0; 1766163863Sthompsa BSTP_UNLOCK(bs); 1767160703Sthompsa} 1768160703Sthompsa 1769146985Sthompsavoid 1770234487Sthompsabstp_linkstate(struct bstp_port *bp) 1771146985Sthompsa{ 1772234487Sthompsa struct bstp_state *bs = bp->bp_bs; 1773146985Sthompsa 1774234488Sthompsa if (!bp->bp_active) 1775234488Sthompsa return; 1776234488Sthompsa 1777234488Sthompsa bstp_ifupdstatus(bp, 0); 1778234487Sthompsa BSTP_LOCK(bs); 1779234488Sthompsa bstp_update_state(bs, bp); 1780234487Sthompsa BSTP_UNLOCK(bs); 1781146985Sthompsa} 1782146985Sthompsa 1783151313Sthompsastatic void 1784234488Sthompsabstp_ifupdstatus(void *arg, int pending) 1785146985Sthompsa{ 1786234488Sthompsa struct bstp_port *bp = (struct bstp_port *)arg; 1787234488Sthompsa struct bstp_state *bs = bp->bp_bs; 1788160703Sthompsa struct ifnet *ifp = bp->bp_ifp; 1789146985Sthompsa struct ifmediareq ifmr; 1790234488Sthompsa int error, changed; 1791146985Sthompsa 1792234488Sthompsa if (!bp->bp_active) 1793234488Sthompsa return; 1794146985Sthompsa 1795146985Sthompsa bzero((char *)&ifmr, sizeof(ifmr)); 1796146985Sthompsa error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr); 1797146985Sthompsa 1798234488Sthompsa BSTP_LOCK(bs); 1799234488Sthompsa changed = 0; 1800146985Sthompsa if ((error == 0) && (ifp->if_flags & IFF_UP)) { 1801160703Sthompsa if (ifmr.ifm_status & IFM_ACTIVE) { 1802163863Sthompsa /* A full-duplex link is assumed to be point to point */ 1803165105Sthompsa if (bp->bp_flags & BSTP_PORT_AUTOPTP) { 1804234488Sthompsa int fdx; 1805234488Sthompsa 1806234488Sthompsa fdx = ifmr.ifm_active & IFM_FDX ? 1 : 0; 1807234488Sthompsa if (bp->bp_ptp_link ^ fdx) { 1808234488Sthompsa bp->bp_ptp_link = fdx; 1809234488Sthompsa changed = 1; 1810234488Sthompsa } 1811164653Sthompsa } 1812163863Sthompsa 1813171724Sthompsa /* Calc the cost if the link was down previously */ 1814171724Sthompsa if (bp->bp_flags & BSTP_PORT_PNDCOST) { 1815234488Sthompsa uint32_t cost; 1816234488Sthompsa 1817234488Sthompsa cost = bstp_calc_path_cost(bp); 1818234488Sthompsa if (bp->bp_path_cost != cost) { 1819234488Sthompsa bp->bp_path_cost = cost; 1820234488Sthompsa changed = 1; 1821234488Sthompsa } 1822171724Sthompsa bp->bp_flags &= ~BSTP_PORT_PNDCOST; 1823171724Sthompsa } 1824171724Sthompsa 1825234488Sthompsa if (bp->bp_role == BSTP_ROLE_DISABLED) { 1826160703Sthompsa bstp_enable_port(bs, bp); 1827234488Sthompsa changed = 1; 1828234488Sthompsa } 1829146985Sthompsa } else { 1830164880Ssyrinx if (bp->bp_role != BSTP_ROLE_DISABLED) { 1831160703Sthompsa bstp_disable_port(bs, bp); 1832234488Sthompsa changed = 1; 1833164880Ssyrinx if ((bp->bp_flags & BSTP_PORT_ADMEDGE) && 1834164880Ssyrinx bp->bp_protover == BSTP_PROTO_RSTP) 1835164880Ssyrinx bp->bp_operedge = 1; 1836164880Ssyrinx } 1837146985Sthompsa } 1838234488Sthompsa } else if (bp->bp_infois != BSTP_INFO_DISABLED) { 1839234488Sthompsa bstp_disable_port(bs, bp); 1840234488Sthompsa changed = 1; 1841146985Sthompsa } 1842234488Sthompsa if (changed) 1843234488Sthompsa bstp_assign_roles(bs); 1844234488Sthompsa BSTP_UNLOCK(bs); 1845146985Sthompsa} 1846146985Sthompsa 1847151313Sthompsastatic void 1848163863Sthompsabstp_enable_port(struct bstp_state *bs, struct bstp_port *bp) 1849163863Sthompsa{ 1850163863Sthompsa bp->bp_infois = BSTP_INFO_AGED; 1851163863Sthompsa} 1852163863Sthompsa 1853163863Sthompsastatic void 1854163863Sthompsabstp_disable_port(struct bstp_state *bs, struct bstp_port *bp) 1855163863Sthompsa{ 1856163863Sthompsa bp->bp_infois = BSTP_INFO_DISABLED; 1857163863Sthompsa} 1858163863Sthompsa 1859163863Sthompsastatic void 1860146985Sthompsabstp_tick(void *arg) 1861146985Sthompsa{ 1862160703Sthompsa struct bstp_state *bs = arg; 1863160703Sthompsa struct bstp_port *bp; 1864146985Sthompsa 1865160703Sthompsa BSTP_LOCK_ASSERT(bs); 1866146985Sthompsa 1867164141Sthompsa if (bs->bs_running == 0) 1868164141Sthompsa return; 1869164141Sthompsa 1870222834Szec CURVNET_SET(bs->bs_vnet); 1871222834Szec 1872232030Sthompsa /* poll link events on interfaces that do not support linkstate */ 1873232070Sthompsa if (bstp_timer_dectest(&bs->bs_link_timer)) { 1874232030Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 1875232030Sthompsa if (!(bp->bp_ifp->if_capabilities & IFCAP_LINKSTATE)) 1876234488Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask); 1877232030Sthompsa } 1878163863Sthompsa bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER); 1879146985Sthompsa } 1880146985Sthompsa 1881163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 1882163863Sthompsa /* no events need to happen for these */ 1883232070Sthompsa bstp_timer_dectest(&bp->bp_tc_timer); 1884232070Sthompsa bstp_timer_dectest(&bp->bp_recent_root_timer); 1885232070Sthompsa bstp_timer_dectest(&bp->bp_forward_delay_timer); 1886232070Sthompsa bstp_timer_dectest(&bp->bp_recent_backup_timer); 1887146985Sthompsa 1888232070Sthompsa if (bstp_timer_dectest(&bp->bp_hello_timer)) 1889163863Sthompsa bstp_hello_timer_expiry(bs, bp); 1890146985Sthompsa 1891232070Sthompsa if (bstp_timer_dectest(&bp->bp_message_age_timer)) 1892163863Sthompsa bstp_message_age_expiry(bs, bp); 1893146985Sthompsa 1894232070Sthompsa if (bstp_timer_dectest(&bp->bp_migrate_delay_timer)) 1895163863Sthompsa bstp_migrate_delay_expiry(bs, bp); 1896146985Sthompsa 1897232070Sthompsa if (bstp_timer_dectest(&bp->bp_edge_delay_timer)) 1898163863Sthompsa bstp_edge_delay_expiry(bs, bp); 1899146985Sthompsa 1900163863Sthompsa /* update the various state machines for the port */ 1901163863Sthompsa bstp_update_state(bs, bp); 1902163863Sthompsa 1903163863Sthompsa if (bp->bp_txcount > 0) 1904163863Sthompsa bp->bp_txcount--; 1905146985Sthompsa } 1906146985Sthompsa 1907222834Szec CURVNET_RESTORE(); 1908222834Szec 1909160703Sthompsa callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs); 1910146985Sthompsa} 1911146985Sthompsa 1912151313Sthompsastatic void 1913160703Sthompsabstp_timer_start(struct bstp_timer *t, uint16_t v) 1914146985Sthompsa{ 1915146985Sthompsa t->value = v; 1916146985Sthompsa t->active = 1; 1917163863Sthompsa t->latched = 0; 1918146985Sthompsa} 1919146985Sthompsa 1920151313Sthompsastatic void 1921160703Sthompsabstp_timer_stop(struct bstp_timer *t) 1922146985Sthompsa{ 1923146985Sthompsa t->value = 0; 1924146985Sthompsa t->active = 0; 1925163863Sthompsa t->latched = 0; 1926146985Sthompsa} 1927146985Sthompsa 1928163863Sthompsastatic void 1929163863Sthompsabstp_timer_latch(struct bstp_timer *t) 1930163863Sthompsa{ 1931163863Sthompsa t->latched = 1; 1932163863Sthompsa t->active = 1; 1933163863Sthompsa} 1934163863Sthompsa 1935151313Sthompsastatic int 1936232070Sthompsabstp_timer_dectest(struct bstp_timer *t) 1937146985Sthompsa{ 1938163863Sthompsa if (t->active == 0 || t->latched) 1939146985Sthompsa return (0); 1940163863Sthompsa t->value -= BSTP_TICK_VAL; 1941163863Sthompsa if (t->value <= 0) { 1942146985Sthompsa bstp_timer_stop(t); 1943146985Sthompsa return (1); 1944146985Sthompsa } 1945146985Sthompsa return (0); 1946163863Sthompsa} 1947146985Sthompsa 1948163863Sthompsastatic void 1949163863Sthompsabstp_hello_timer_expiry(struct bstp_state *bs, struct bstp_port *bp) 1950163863Sthompsa{ 1951163863Sthompsa if ((bp->bp_flags & BSTP_PORT_NEWINFO) || 1952163863Sthompsa bp->bp_role == BSTP_ROLE_DESIGNATED || 1953163863Sthompsa (bp->bp_role == BSTP_ROLE_ROOT && 1954163863Sthompsa bp->bp_tc_timer.active == 1)) { 1955163863Sthompsa bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime); 1956163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 1957163863Sthompsa bstp_transmit(bs, bp); 1958163863Sthompsa } 1959146985Sthompsa} 1960160703Sthompsa 1961163863Sthompsastatic void 1962163863Sthompsabstp_message_age_expiry(struct bstp_state *bs, struct bstp_port *bp) 1963163863Sthompsa{ 1964174493Sthompsa if (bp->bp_infois == BSTP_INFO_RECEIVED) { 1965163863Sthompsa bp->bp_infois = BSTP_INFO_AGED; 1966163863Sthompsa bstp_assign_roles(bs); 1967163863Sthompsa DPRINTF("aged info on %s\n", bp->bp_ifp->if_xname); 1968163863Sthompsa } 1969163863Sthompsa} 1970163863Sthompsa 1971163863Sthompsastatic void 1972163863Sthompsabstp_migrate_delay_expiry(struct bstp_state *bs, struct bstp_port *bp) 1973163863Sthompsa{ 1974163863Sthompsa bp->bp_flags |= BSTP_PORT_CANMIGRATE; 1975163863Sthompsa} 1976163863Sthompsa 1977163863Sthompsastatic void 1978163863Sthompsabstp_edge_delay_expiry(struct bstp_state *bs, struct bstp_port *bp) 1979163863Sthompsa{ 1980163863Sthompsa if ((bp->bp_flags & BSTP_PORT_AUTOEDGE) && 1981163863Sthompsa bp->bp_protover == BSTP_PROTO_RSTP && bp->bp_proposing && 1982164141Sthompsa bp->bp_role == BSTP_ROLE_DESIGNATED) { 1983163863Sthompsa bp->bp_operedge = 1; 1984164141Sthompsa DPRINTF("%s -> edge port\n", bp->bp_ifp->if_xname); 1985164141Sthompsa } 1986163863Sthompsa} 1987163863Sthompsa 1988163863Sthompsastatic int 1989163863Sthompsabstp_addr_cmp(const uint8_t *a, const uint8_t *b) 1990163863Sthompsa{ 1991163863Sthompsa int i, d; 1992163863Sthompsa 1993163863Sthompsa for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) { 1994163863Sthompsa d = ((int)a[i]) - ((int)b[i]); 1995163863Sthompsa } 1996163863Sthompsa 1997163863Sthompsa return (d); 1998163863Sthompsa} 1999163863Sthompsa 2000163863Sthompsa/* 2001163863Sthompsa * compare the bridge address component of the bridgeid 2002163863Sthompsa */ 2003163863Sthompsastatic int 2004163863Sthompsabstp_same_bridgeid(uint64_t id1, uint64_t id2) 2005163863Sthompsa{ 2006163863Sthompsa u_char addr1[ETHER_ADDR_LEN]; 2007163863Sthompsa u_char addr2[ETHER_ADDR_LEN]; 2008163863Sthompsa 2009163863Sthompsa PV2ADDR(id1, addr1); 2010163863Sthompsa PV2ADDR(id2, addr2); 2011163863Sthompsa 2012163863Sthompsa if (bstp_addr_cmp(addr1, addr2) == 0) 2013163863Sthompsa return (1); 2014163863Sthompsa 2015163863Sthompsa return (0); 2016163863Sthompsa} 2017163863Sthompsa 2018163863Sthompsavoid 2019163863Sthompsabstp_reinit(struct bstp_state *bs) 2020163863Sthompsa{ 2021164141Sthompsa struct bstp_port *bp; 2022164141Sthompsa struct ifnet *ifp, *mif; 2023163863Sthompsa u_char *e_addr; 2024232118Sthompsa void *bridgeptr; 2025171724Sthompsa static const u_char llzero[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */ 2026163863Sthompsa 2027163863Sthompsa BSTP_LOCK_ASSERT(bs); 2028163863Sthompsa 2029232118Sthompsa if (LIST_EMPTY(&bs->bs_bplist)) 2030232118Sthompsa goto disablestp; 2031232118Sthompsa 2032164141Sthompsa mif = NULL; 2033232118Sthompsa bridgeptr = LIST_FIRST(&bs->bs_bplist)->bp_ifp->if_bridge; 2034232118Sthompsa KASSERT(bridgeptr != NULL, ("Invalid bridge pointer")); 2035164141Sthompsa /* 2036164141Sthompsa * Search through the Ethernet adapters and find the one with the 2037232118Sthompsa * lowest value. Make sure the adapter which we take the MAC address 2038232118Sthompsa * from is part of this bridge, so we can have more than one independent 2039232118Sthompsa * bridges in the same STP domain. 2040164141Sthompsa */ 2041196481Srwatson IFNET_RLOCK_NOSLEEP(); 2042181803Sbz TAILQ_FOREACH(ifp, &V_ifnet, if_link) { 2043164141Sthompsa if (ifp->if_type != IFT_ETHER) 2044232118Sthompsa continue; /* Not Ethernet */ 2045164141Sthompsa 2046232118Sthompsa if (ifp->if_bridge != bridgeptr) 2047232118Sthompsa continue; /* Not part of our bridge */ 2048232118Sthompsa 2049171724Sthompsa if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0) 2050232118Sthompsa continue; /* No mac address set */ 2051171724Sthompsa 2052164141Sthompsa if (mif == NULL) { 2053164141Sthompsa mif = ifp; 2054164141Sthompsa continue; 2055163863Sthompsa } 2056164141Sthompsa if (bstp_addr_cmp(IF_LLADDR(ifp), IF_LLADDR(mif)) < 0) { 2057164141Sthompsa mif = ifp; 2058163863Sthompsa continue; 2059163863Sthompsa } 2060163863Sthompsa } 2061196481Srwatson IFNET_RUNLOCK_NOSLEEP(); 2062232118Sthompsa if (mif == NULL) 2063232118Sthompsa goto disablestp; 2064163863Sthompsa 2065164141Sthompsa e_addr = IF_LLADDR(mif); 2066163863Sthompsa bs->bs_bridge_pv.pv_dbridge_id = 2067163863Sthompsa (((uint64_t)bs->bs_bridge_priority) << 48) | 2068163863Sthompsa (((uint64_t)e_addr[0]) << 40) | 2069163863Sthompsa (((uint64_t)e_addr[1]) << 32) | 2070163863Sthompsa (((uint64_t)e_addr[2]) << 24) | 2071163863Sthompsa (((uint64_t)e_addr[3]) << 16) | 2072163863Sthompsa (((uint64_t)e_addr[4]) << 8) | 2073163863Sthompsa (((uint64_t)e_addr[5])); 2074163863Sthompsa 2075163863Sthompsa bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id; 2076163863Sthompsa bs->bs_bridge_pv.pv_cost = 0; 2077163863Sthompsa bs->bs_bridge_pv.pv_dport_id = 0; 2078163863Sthompsa bs->bs_bridge_pv.pv_port_id = 0; 2079163863Sthompsa 2080164141Sthompsa if (bs->bs_running && callout_pending(&bs->bs_bstpcallout) == 0) 2081163863Sthompsa callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs); 2082163863Sthompsa 2083164141Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 2084164141Sthompsa bp->bp_port_id = (bp->bp_priority << 8) | 2085164141Sthompsa (bp->bp_ifp->if_index & 0xfff); 2086234488Sthompsa taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask); 2087164141Sthompsa } 2088163863Sthompsa 2089163863Sthompsa bstp_assign_roles(bs); 2090163863Sthompsa bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER); 2091232118Sthompsa return; 2092232118Sthompsa 2093232118Sthompsadisablestp: 2094232118Sthompsa /* Set the bridge and root id (lower bits) to zero */ 2095232118Sthompsa bs->bs_bridge_pv.pv_dbridge_id = 2096232118Sthompsa ((uint64_t)bs->bs_bridge_priority) << 48; 2097232118Sthompsa bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id; 2098232118Sthompsa bs->bs_root_pv = bs->bs_bridge_pv; 2099232118Sthompsa /* Disable any remaining ports, they will have no MAC address */ 2100232118Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) { 2101232118Sthompsa bp->bp_infois = BSTP_INFO_DISABLED; 2102232118Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DISABLED); 2103232118Sthompsa } 2104232118Sthompsa callout_stop(&bs->bs_bstpcallout); 2105163863Sthompsa} 2106163863Sthompsa 2107163863Sthompsastatic int 2108163863Sthompsabstp_modevent(module_t mod, int type, void *data) 2109163863Sthompsa{ 2110163863Sthompsa switch (type) { 2111163863Sthompsa case MOD_LOAD: 2112163863Sthompsa mtx_init(&bstp_list_mtx, "bridgestp list", NULL, MTX_DEF); 2113163863Sthompsa LIST_INIT(&bstp_list); 2114163863Sthompsa break; 2115163863Sthompsa case MOD_UNLOAD: 2116163863Sthompsa mtx_destroy(&bstp_list_mtx); 2117163863Sthompsa break; 2118163863Sthompsa default: 2119163863Sthompsa return (EOPNOTSUPP); 2120163863Sthompsa } 2121163863Sthompsa return (0); 2122163863Sthompsa} 2123163863Sthompsa 2124163863Sthompsastatic moduledata_t bstp_mod = { 2125163863Sthompsa "bridgestp", 2126163863Sthompsa bstp_modevent, 2127241394Skevlo 0 2128163863Sthompsa}; 2129163863Sthompsa 2130163863SthompsaDECLARE_MODULE(bridgestp, bstp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 2131163863SthompsaMODULE_VERSION(bridgestp, 1); 2132163863Sthompsa 2133163863Sthompsavoid 2134167379Sthompsabstp_attach(struct bstp_state *bs, struct bstp_cb_ops *cb) 2135163863Sthompsa{ 2136163863Sthompsa BSTP_LOCK_INIT(bs); 2137163863Sthompsa callout_init_mtx(&bs->bs_bstpcallout, &bs->bs_mtx, 0); 2138163863Sthompsa LIST_INIT(&bs->bs_bplist); 2139163863Sthompsa 2140163863Sthompsa bs->bs_bridge_max_age = BSTP_DEFAULT_MAX_AGE; 2141163863Sthompsa bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME; 2142163863Sthompsa bs->bs_bridge_fdelay = BSTP_DEFAULT_FORWARD_DELAY; 2143163863Sthompsa bs->bs_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY; 2144163863Sthompsa bs->bs_hold_time = BSTP_DEFAULT_HOLD_TIME; 2145163863Sthompsa bs->bs_migration_delay = BSTP_DEFAULT_MIGRATE_DELAY; 2146163863Sthompsa bs->bs_txholdcount = BSTP_DEFAULT_HOLD_COUNT; 2147163863Sthompsa bs->bs_protover = BSTP_PROTO_RSTP; 2148167379Sthompsa bs->bs_state_cb = cb->bcb_state; 2149167379Sthompsa bs->bs_rtage_cb = cb->bcb_rtage; 2150222834Szec bs->bs_vnet = curvnet; 2151163863Sthompsa 2152163863Sthompsa getmicrotime(&bs->bs_last_tc_time); 2153163863Sthompsa 2154163863Sthompsa mtx_lock(&bstp_list_mtx); 2155163863Sthompsa LIST_INSERT_HEAD(&bstp_list, bs, bs_list); 2156163863Sthompsa mtx_unlock(&bstp_list_mtx); 2157163863Sthompsa} 2158163863Sthompsa 2159163863Sthompsavoid 2160163863Sthompsabstp_detach(struct bstp_state *bs) 2161163863Sthompsa{ 2162163863Sthompsa KASSERT(LIST_EMPTY(&bs->bs_bplist), ("bstp still active")); 2163163863Sthompsa 2164163863Sthompsa mtx_lock(&bstp_list_mtx); 2165163863Sthompsa LIST_REMOVE(bs, bs_list); 2166163863Sthompsa mtx_unlock(&bstp_list_mtx); 2167164141Sthompsa callout_drain(&bs->bs_bstpcallout); 2168163863Sthompsa BSTP_LOCK_DESTROY(bs); 2169163863Sthompsa} 2170163863Sthompsa 2171163863Sthompsavoid 2172163863Sthompsabstp_init(struct bstp_state *bs) 2173163863Sthompsa{ 2174163863Sthompsa BSTP_LOCK(bs); 2175163863Sthompsa callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs); 2176164141Sthompsa bs->bs_running = 1; 2177163863Sthompsa bstp_reinit(bs); 2178163863Sthompsa BSTP_UNLOCK(bs); 2179163863Sthompsa} 2180163863Sthompsa 2181163863Sthompsavoid 2182163863Sthompsabstp_stop(struct bstp_state *bs) 2183163863Sthompsa{ 2184163863Sthompsa struct bstp_port *bp; 2185163863Sthompsa 2186164414Sthompsa BSTP_LOCK(bs); 2187163863Sthompsa 2188163863Sthompsa LIST_FOREACH(bp, &bs->bs_bplist, bp_next) 2189163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING); 2190163863Sthompsa 2191164141Sthompsa bs->bs_running = 0; 2192163863Sthompsa callout_stop(&bs->bs_bstpcallout); 2193164414Sthompsa BSTP_UNLOCK(bs); 2194163863Sthompsa} 2195163863Sthompsa 2196160703Sthompsaint 2197164626Sthompsabstp_create(struct bstp_state *bs, struct bstp_port *bp, struct ifnet *ifp) 2198160703Sthompsa{ 2199163863Sthompsa bzero(bp, sizeof(struct bstp_port)); 2200163863Sthompsa 2201160703Sthompsa BSTP_LOCK(bs); 2202160703Sthompsa bp->bp_ifp = ifp; 2203160703Sthompsa bp->bp_bs = bs; 2204160703Sthompsa bp->bp_priority = BSTP_DEFAULT_PORT_PRIORITY; 2205163863Sthompsa TASK_INIT(&bp->bp_statetask, 0, bstp_notify_state, bp); 2206163863Sthompsa TASK_INIT(&bp->bp_rtagetask, 0, bstp_notify_rtage, bp); 2207234488Sthompsa TASK_INIT(&bp->bp_mediatask, 0, bstp_ifupdstatus, bp); 2208160703Sthompsa 2209163863Sthompsa /* Init state */ 2210164638Sthompsa bp->bp_infois = BSTP_INFO_DISABLED; 2211165105Sthompsa bp->bp_flags = BSTP_PORT_AUTOEDGE|BSTP_PORT_AUTOPTP; 2212163863Sthompsa bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING); 2213163863Sthompsa bstp_set_port_proto(bp, bs->bs_protover); 2214163863Sthompsa bstp_set_port_role(bp, BSTP_ROLE_DISABLED); 2215163863Sthompsa bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE); 2216163863Sthompsa bp->bp_path_cost = bstp_calc_path_cost(bp); 2217164626Sthompsa BSTP_UNLOCK(bs); 2218164626Sthompsa return (0); 2219164626Sthompsa} 2220163863Sthompsa 2221164626Sthompsaint 2222164626Sthompsabstp_enable(struct bstp_port *bp) 2223164626Sthompsa{ 2224164626Sthompsa struct bstp_state *bs = bp->bp_bs; 2225164626Sthompsa struct ifnet *ifp = bp->bp_ifp; 2226164626Sthompsa 2227164626Sthompsa KASSERT(bp->bp_active == 0, ("already a bstp member")); 2228164626Sthompsa 2229164626Sthompsa switch (ifp->if_type) { 2230164626Sthompsa case IFT_ETHER: /* These can do spanning tree. */ 2231164626Sthompsa break; 2232164626Sthompsa default: 2233164626Sthompsa /* Nothing else can. */ 2234164626Sthompsa return (EINVAL); 2235164626Sthompsa } 2236164626Sthompsa 2237164626Sthompsa BSTP_LOCK(bs); 2238160703Sthompsa LIST_INSERT_HEAD(&bs->bs_bplist, bp, bp_next); 2239163863Sthompsa bp->bp_active = 1; 2240163863Sthompsa bp->bp_flags |= BSTP_PORT_NEWINFO; 2241163863Sthompsa bstp_reinit(bs); 2242163863Sthompsa bstp_update_roles(bs, bp); 2243160703Sthompsa BSTP_UNLOCK(bs); 2244160703Sthompsa return (0); 2245160703Sthompsa} 2246160703Sthompsa 2247160703Sthompsavoid 2248164626Sthompsabstp_disable(struct bstp_port *bp) 2249160703Sthompsa{ 2250160703Sthompsa struct bstp_state *bs = bp->bp_bs; 2251160703Sthompsa 2252160703Sthompsa KASSERT(bp->bp_active == 1, ("not a bstp member")); 2253160703Sthompsa 2254160703Sthompsa BSTP_LOCK(bs); 2255164626Sthompsa bstp_disable_port(bs, bp); 2256160703Sthompsa LIST_REMOVE(bp, bp_next); 2257160703Sthompsa bp->bp_active = 0; 2258160703Sthompsa bstp_reinit(bs); 2259163863Sthompsa BSTP_UNLOCK(bs); 2260160703Sthompsa} 2261160899Sthompsa 2262160899Sthompsa/* 2263160899Sthompsa * The bstp_port structure is about to be freed by the parent bridge. 2264160899Sthompsa */ 2265160899Sthompsavoid 2266164626Sthompsabstp_destroy(struct bstp_port *bp) 2267160899Sthompsa{ 2268160899Sthompsa KASSERT(bp->bp_active == 0, ("port is still attached")); 2269160899Sthompsa taskqueue_drain(taskqueue_swi, &bp->bp_statetask); 2270163863Sthompsa taskqueue_drain(taskqueue_swi, &bp->bp_rtagetask); 2271234488Sthompsa taskqueue_drain(taskqueue_swi, &bp->bp_mediatask); 2272160899Sthompsa} 2273