131997bf2SAndrew Thompson /* $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $ */
231997bf2SAndrew Thompson
3fe267a55SPedro F. Giffuni /*-
4b61a5730SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
5fe267a55SPedro F. Giffuni *
631997bf2SAndrew Thompson * Copyright (c) 2000 Jason L. Wright (jason@thought.net)
73fab7669SAndrew Thompson * Copyright (c) 2006 Andrew Thompson (thompsa@FreeBSD.org)
831997bf2SAndrew Thompson * All rights reserved.
931997bf2SAndrew Thompson *
1031997bf2SAndrew Thompson * Redistribution and use in source and binary forms, with or without
1131997bf2SAndrew Thompson * modification, are permitted provided that the following conditions
1231997bf2SAndrew Thompson * are met:
1331997bf2SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
1431997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer.
1531997bf2SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
1631997bf2SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
1731997bf2SAndrew Thompson * documentation and/or other materials provided with the distribution.
1831997bf2SAndrew Thompson *
1931997bf2SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2031997bf2SAndrew Thompson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2131997bf2SAndrew Thompson * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2231997bf2SAndrew Thompson * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2331997bf2SAndrew Thompson * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2431997bf2SAndrew Thompson * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2531997bf2SAndrew Thompson * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2631997bf2SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2731997bf2SAndrew Thompson * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2831997bf2SAndrew Thompson * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2931997bf2SAndrew Thompson * POSSIBILITY OF SUCH DAMAGE.
3031997bf2SAndrew Thompson *
3131997bf2SAndrew Thompson * OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp
3231997bf2SAndrew Thompson */
3331997bf2SAndrew Thompson
3431997bf2SAndrew Thompson /*
3531997bf2SAndrew Thompson * Implementation of the spanning tree protocol as defined in
363fab7669SAndrew Thompson * ISO/IEC 802.1D-2004, June 9, 2004.
3731997bf2SAndrew Thompson */
3831997bf2SAndrew Thompson
3931997bf2SAndrew Thompson #include <sys/param.h>
4031997bf2SAndrew Thompson #include <sys/systm.h>
4131997bf2SAndrew Thompson #include <sys/mbuf.h>
4231997bf2SAndrew Thompson #include <sys/socket.h>
4331997bf2SAndrew Thompson #include <sys/sockio.h>
4431997bf2SAndrew Thompson #include <sys/kernel.h>
458ec07310SGleb Smirnoff #include <sys/malloc.h>
4631997bf2SAndrew Thompson #include <sys/callout.h>
4796e47153SAndrew Thompson #include <sys/module.h>
4831997bf2SAndrew Thompson #include <sys/proc.h>
4931997bf2SAndrew Thompson #include <sys/lock.h>
5031997bf2SAndrew Thompson #include <sys/mutex.h>
516f2abce0SAndrew Thompson #include <sys/taskqueue.h>
5231997bf2SAndrew Thompson
5331997bf2SAndrew Thompson #include <net/if.h>
5476039bc8SGleb Smirnoff #include <net/if_var.h>
552c2b37adSJustin Hibbits #include <net/if_private.h>
5631997bf2SAndrew Thompson #include <net/if_dl.h>
5731997bf2SAndrew Thompson #include <net/if_types.h>
5831997bf2SAndrew Thompson #include <net/if_llc.h>
5931997bf2SAndrew Thompson #include <net/if_media.h>
60*85967694SLexi Winter #include <net/if_bridgevar.h>
614b79449eSBjoern A. Zeeb #include <net/vnet.h>
6231997bf2SAndrew Thompson
6331997bf2SAndrew Thompson #include <netinet/in.h>
6431997bf2SAndrew Thompson #include <netinet/in_systm.h>
6531997bf2SAndrew Thompson #include <netinet/in_var.h>
6631997bf2SAndrew Thompson #include <netinet/if_ether.h>
6796e47153SAndrew Thompson #include <net/bridgestp.h>
6831997bf2SAndrew Thompson
693fab7669SAndrew Thompson #ifdef BRIDGESTP_DEBUG
703fab7669SAndrew Thompson #define DPRINTF(fmt, arg...) printf("bstp: " fmt, ##arg)
713fab7669SAndrew Thompson #else
722b7d10c2SRoman Divacky #define DPRINTF(fmt, arg...) (void)0
733fab7669SAndrew Thompson #endif
743fab7669SAndrew Thompson
753fab7669SAndrew Thompson #define PV2ADDR(pv, eaddr) do { \
763fab7669SAndrew Thompson eaddr[0] = pv >> 40; \
773fab7669SAndrew Thompson eaddr[1] = pv >> 32; \
783fab7669SAndrew Thompson eaddr[2] = pv >> 24; \
793fab7669SAndrew Thompson eaddr[3] = pv >> 16; \
803fab7669SAndrew Thompson eaddr[4] = pv >> 8; \
813fab7669SAndrew Thompson eaddr[5] = pv >> 0; \
823fab7669SAndrew Thompson } while (0)
833fab7669SAndrew Thompson
843fab7669SAndrew Thompson #define INFO_BETTER 1
853fab7669SAndrew Thompson #define INFO_SAME 0
863fab7669SAndrew Thompson #define INFO_WORSE -1
873fab7669SAndrew Thompson
8831997bf2SAndrew Thompson const uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
8931997bf2SAndrew Thompson
90fd0020f8SZhenlei Huang LIST_HEAD(, bstp_state) bstp_list = LIST_HEAD_INITIALIZER(bstp_list);
9196e47153SAndrew Thompson static struct mtx bstp_list_mtx;
9296e47153SAndrew Thompson
933fab7669SAndrew Thompson static void bstp_transmit(struct bstp_state *, struct bstp_port *);
943fab7669SAndrew Thompson static void bstp_transmit_bpdu(struct bstp_state *, struct bstp_port *);
953fab7669SAndrew Thompson static void bstp_transmit_tcn(struct bstp_state *, struct bstp_port *);
963fab7669SAndrew Thompson static void bstp_decode_bpdu(struct bstp_port *, struct bstp_cbpdu *,
973fab7669SAndrew Thompson struct bstp_config_unit *);
983fab7669SAndrew Thompson static void bstp_send_bpdu(struct bstp_state *, struct bstp_port *,
993fab7669SAndrew Thompson struct bstp_cbpdu *);
1003fab7669SAndrew Thompson static int bstp_pdu_flags(struct bstp_port *);
1013fab7669SAndrew Thompson static void bstp_received_stp(struct bstp_state *, struct bstp_port *,
102cd281f6dSAndrew Thompson struct mbuf **, struct bstp_tbpdu *);
1033fab7669SAndrew Thompson static void bstp_received_rstp(struct bstp_state *, struct bstp_port *,
104cd281f6dSAndrew Thompson struct mbuf **, struct bstp_tbpdu *);
1053fab7669SAndrew Thompson static void bstp_received_tcn(struct bstp_state *, struct bstp_port *,
1063fab7669SAndrew Thompson struct bstp_tcn_unit *);
1073fab7669SAndrew Thompson static void bstp_received_bpdu(struct bstp_state *, struct bstp_port *,
1083fab7669SAndrew Thompson struct bstp_config_unit *);
1093fab7669SAndrew Thompson static int bstp_pdu_rcvtype(struct bstp_port *, struct bstp_config_unit *);
1103fab7669SAndrew Thompson static int bstp_pdu_bettersame(struct bstp_port *, int);
1113fab7669SAndrew Thompson static int bstp_info_cmp(struct bstp_pri_vector *,
1123fab7669SAndrew Thompson struct bstp_pri_vector *);
1133fab7669SAndrew Thompson static int bstp_info_superior(struct bstp_pri_vector *,
1143fab7669SAndrew Thompson struct bstp_pri_vector *);
1153fab7669SAndrew Thompson static void bstp_assign_roles(struct bstp_state *);
1163fab7669SAndrew Thompson static void bstp_update_roles(struct bstp_state *, struct bstp_port *);
1173fab7669SAndrew Thompson static void bstp_update_state(struct bstp_state *, struct bstp_port *);
1183fab7669SAndrew Thompson static void bstp_update_tc(struct bstp_port *);
1193fab7669SAndrew Thompson static void bstp_update_info(struct bstp_port *);
1203fab7669SAndrew Thompson static void bstp_set_other_tcprop(struct bstp_port *);
1213fab7669SAndrew Thompson static void bstp_set_all_reroot(struct bstp_state *);
1223fab7669SAndrew Thompson static void bstp_set_all_sync(struct bstp_state *);
1233fab7669SAndrew Thompson static void bstp_set_port_state(struct bstp_port *, int);
1243fab7669SAndrew Thompson static void bstp_set_port_role(struct bstp_port *, int);
1253fab7669SAndrew Thompson static void bstp_set_port_proto(struct bstp_port *, int);
1263fab7669SAndrew Thompson static void bstp_set_port_tc(struct bstp_port *, int);
1273fab7669SAndrew Thompson static void bstp_set_timer_tc(struct bstp_port *);
1283fab7669SAndrew Thompson static void bstp_set_timer_msgage(struct bstp_port *);
1293fab7669SAndrew Thompson static int bstp_rerooted(struct bstp_state *, struct bstp_port *);
1303fab7669SAndrew Thompson static uint32_t bstp_calc_path_cost(struct bstp_port *);
1313fab7669SAndrew Thompson static void bstp_notify_state(void *, int);
1323fab7669SAndrew Thompson static void bstp_notify_rtage(void *, int);
1332885c19eSAndrew Thompson static void bstp_ifupdstatus(void *, int);
13496e47153SAndrew Thompson static void bstp_enable_port(struct bstp_state *, struct bstp_port *);
1353fab7669SAndrew Thompson static void bstp_disable_port(struct bstp_state *, struct bstp_port *);
1366b32f3d3SAndrew Thompson static void bstp_tick(void *);
13796e47153SAndrew Thompson static void bstp_timer_start(struct bstp_timer *, uint16_t);
13896e47153SAndrew Thompson static void bstp_timer_stop(struct bstp_timer *);
1393fab7669SAndrew Thompson static void bstp_timer_latch(struct bstp_timer *);
14047190ea6SAndrew Thompson static int bstp_timer_dectest(struct bstp_timer *);
1413fab7669SAndrew Thompson static void bstp_hello_timer_expiry(struct bstp_state *,
14296e47153SAndrew Thompson struct bstp_port *);
1433fab7669SAndrew Thompson static void bstp_message_age_expiry(struct bstp_state *,
14496e47153SAndrew Thompson struct bstp_port *);
1453fab7669SAndrew Thompson static void bstp_migrate_delay_expiry(struct bstp_state *,
14696e47153SAndrew Thompson struct bstp_port *);
1473fab7669SAndrew Thompson static void bstp_edge_delay_expiry(struct bstp_state *,
1483fab7669SAndrew Thompson struct bstp_port *);
14943dc0e8cSAndrew Thompson static int bstp_addr_cmp(const uint8_t *, const uint8_t *);
1503fab7669SAndrew Thompson static int bstp_same_bridgeid(uint64_t, uint64_t);
1513fab7669SAndrew Thompson static void bstp_reinit(struct bstp_state *);
15231997bf2SAndrew Thompson
1536b32f3d3SAndrew Thompson static void
bstp_transmit(struct bstp_state * bs,struct bstp_port * bp)1543fab7669SAndrew Thompson bstp_transmit(struct bstp_state *bs, struct bstp_port *bp)
15531997bf2SAndrew Thompson {
15689fa9c34SKristof Provost NET_EPOCH_ASSERT();
15789fa9c34SKristof Provost
158c25789ccSAndrew Thompson if (bs->bs_running == 0)
159c25789ccSAndrew Thompson return;
160c25789ccSAndrew Thompson
1613fab7669SAndrew Thompson /*
1623fab7669SAndrew Thompson * a PDU can only be sent if we have tx quota left and the
1633fab7669SAndrew Thompson * hello timer is running.
1643fab7669SAndrew Thompson */
1653fab7669SAndrew Thompson if (bp->bp_hello_timer.active == 0) {
1663fab7669SAndrew Thompson /* Test if it needs to be reset */
1673fab7669SAndrew Thompson bstp_hello_timer_expiry(bs, bp);
16831997bf2SAndrew Thompson return;
16931997bf2SAndrew Thompson }
1703fab7669SAndrew Thompson if (bp->bp_txcount > bs->bs_txholdcount)
1713fab7669SAndrew Thompson /* Ran out of karma */
1723fab7669SAndrew Thompson return;
17331997bf2SAndrew Thompson
1743fab7669SAndrew Thompson if (bp->bp_protover == BSTP_PROTO_RSTP) {
1753fab7669SAndrew Thompson bstp_transmit_bpdu(bs, bp);
1763fab7669SAndrew Thompson bp->bp_tc_ack = 0;
1773fab7669SAndrew Thompson } else { /* STP */
1783fab7669SAndrew Thompson switch (bp->bp_role) {
1793fab7669SAndrew Thompson case BSTP_ROLE_DESIGNATED:
1803fab7669SAndrew Thompson bstp_transmit_bpdu(bs, bp);
1813fab7669SAndrew Thompson bp->bp_tc_ack = 0;
1823fab7669SAndrew Thompson break;
18331997bf2SAndrew Thompson
1843fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
1853fab7669SAndrew Thompson bstp_transmit_tcn(bs, bp);
1863fab7669SAndrew Thompson break;
18731997bf2SAndrew Thompson }
18831997bf2SAndrew Thompson }
1893fab7669SAndrew Thompson bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
1903fab7669SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_NEWINFO;
1913fab7669SAndrew Thompson }
19231997bf2SAndrew Thompson
1936b32f3d3SAndrew Thompson static void
bstp_transmit_bpdu(struct bstp_state * bs,struct bstp_port * bp)1943fab7669SAndrew Thompson bstp_transmit_bpdu(struct bstp_state *bs, struct bstp_port *bp)
19531997bf2SAndrew Thompson {
19631997bf2SAndrew Thompson struct bstp_cbpdu bpdu;
19731997bf2SAndrew Thompson
19896e47153SAndrew Thompson BSTP_LOCK_ASSERT(bs);
19931997bf2SAndrew Thompson
2003fab7669SAndrew Thompson bpdu.cbu_rootpri = htons(bp->bp_desg_pv.pv_root_id >> 48);
2013fab7669SAndrew Thompson PV2ADDR(bp->bp_desg_pv.pv_root_id, bpdu.cbu_rootaddr);
20231997bf2SAndrew Thompson
2033fab7669SAndrew Thompson bpdu.cbu_rootpathcost = htonl(bp->bp_desg_pv.pv_cost);
20431997bf2SAndrew Thompson
2053fab7669SAndrew Thompson bpdu.cbu_bridgepri = htons(bp->bp_desg_pv.pv_dbridge_id >> 48);
2063fab7669SAndrew Thompson PV2ADDR(bp->bp_desg_pv.pv_dbridge_id, bpdu.cbu_bridgeaddr);
20731997bf2SAndrew Thompson
2083fab7669SAndrew Thompson bpdu.cbu_portid = htons(bp->bp_port_id);
2093fab7669SAndrew Thompson bpdu.cbu_messageage = htons(bp->bp_desg_msg_age);
2103fab7669SAndrew Thompson bpdu.cbu_maxage = htons(bp->bp_desg_max_age);
2113fab7669SAndrew Thompson bpdu.cbu_hellotime = htons(bp->bp_desg_htime);
2123fab7669SAndrew Thompson bpdu.cbu_forwarddelay = htons(bp->bp_desg_fdelay);
21331997bf2SAndrew Thompson
2143fab7669SAndrew Thompson bpdu.cbu_flags = bstp_pdu_flags(bp);
21531997bf2SAndrew Thompson
2163fab7669SAndrew Thompson switch (bp->bp_protover) {
2173fab7669SAndrew Thompson case BSTP_PROTO_STP:
2183fab7669SAndrew Thompson bpdu.cbu_bpdutype = BSTP_MSGTYPE_CFG;
2193fab7669SAndrew Thompson break;
22031997bf2SAndrew Thompson
2213fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
2223fab7669SAndrew Thompson bpdu.cbu_bpdutype = BSTP_MSGTYPE_RSTP;
2233fab7669SAndrew Thompson break;
22431997bf2SAndrew Thompson }
22531997bf2SAndrew Thompson
2263fab7669SAndrew Thompson bstp_send_bpdu(bs, bp, &bpdu);
22731997bf2SAndrew Thompson }
22831997bf2SAndrew Thompson
2296b32f3d3SAndrew Thompson static void
bstp_transmit_tcn(struct bstp_state * bs,struct bstp_port * bp)2303fab7669SAndrew Thompson bstp_transmit_tcn(struct bstp_state *bs, struct bstp_port *bp)
23131997bf2SAndrew Thompson {
23231997bf2SAndrew Thompson struct bstp_tbpdu bpdu;
23396e47153SAndrew Thompson struct ifnet *ifp = bp->bp_ifp;
23431997bf2SAndrew Thompson struct ether_header *eh;
23531997bf2SAndrew Thompson struct mbuf *m;
23631997bf2SAndrew Thompson
2373fab7669SAndrew Thompson KASSERT(bp == bs->bs_root_port, ("%s: bad root port\n", __func__));
23831997bf2SAndrew Thompson
23913f4c340SRobert Watson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
24031997bf2SAndrew Thompson return;
24131997bf2SAndrew Thompson
242dc4ad05eSGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA);
24331997bf2SAndrew Thompson if (m == NULL)
24431997bf2SAndrew Thompson return;
24531997bf2SAndrew Thompson
24631997bf2SAndrew Thompson m->m_pkthdr.rcvif = ifp;
24731997bf2SAndrew Thompson m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
24831997bf2SAndrew Thompson m->m_len = m->m_pkthdr.len;
24931997bf2SAndrew Thompson
25031997bf2SAndrew Thompson eh = mtod(m, struct ether_header *);
25131997bf2SAndrew Thompson
25231997bf2SAndrew Thompson memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
25331997bf2SAndrew Thompson memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
25431997bf2SAndrew Thompson eh->ether_type = htons(sizeof(bpdu));
25531997bf2SAndrew Thompson
25631997bf2SAndrew Thompson bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP;
25731997bf2SAndrew Thompson bpdu.tbu_ctl = LLC_UI;
25831997bf2SAndrew Thompson bpdu.tbu_protoid = 0;
25931997bf2SAndrew Thompson bpdu.tbu_protover = 0;
26031997bf2SAndrew Thompson bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN;
26131997bf2SAndrew Thompson
26231997bf2SAndrew Thompson memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
26331997bf2SAndrew Thompson
2643fab7669SAndrew Thompson bp->bp_txcount++;
2656e3513f5SKip Macy ifp->if_transmit(ifp, m);
26631997bf2SAndrew Thompson }
26731997bf2SAndrew Thompson
2686b32f3d3SAndrew Thompson static void
bstp_decode_bpdu(struct bstp_port * bp,struct bstp_cbpdu * cpdu,struct bstp_config_unit * cu)2693fab7669SAndrew Thompson bstp_decode_bpdu(struct bstp_port *bp, struct bstp_cbpdu *cpdu,
2703fab7669SAndrew Thompson struct bstp_config_unit *cu)
27131997bf2SAndrew Thompson {
2723fab7669SAndrew Thompson int flags;
27331997bf2SAndrew Thompson
2743fab7669SAndrew Thompson cu->cu_pv.pv_root_id =
2753fab7669SAndrew Thompson (((uint64_t)ntohs(cpdu->cbu_rootpri)) << 48) |
2763fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[0]) << 40) |
2773fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[1]) << 32) |
2783fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[2]) << 24) |
2793fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[3]) << 16) |
2803fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[4]) << 8) |
2813fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_rootaddr[5]) << 0);
2823fab7669SAndrew Thompson
2833fab7669SAndrew Thompson cu->cu_pv.pv_dbridge_id =
2843fab7669SAndrew Thompson (((uint64_t)ntohs(cpdu->cbu_bridgepri)) << 48) |
2853fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[0]) << 40) |
2863fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[1]) << 32) |
2873fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[2]) << 24) |
2883fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[3]) << 16) |
2893fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[4]) << 8) |
2903fab7669SAndrew Thompson (((uint64_t)cpdu->cbu_bridgeaddr[5]) << 0);
2913fab7669SAndrew Thompson
2923fab7669SAndrew Thompson cu->cu_pv.pv_cost = ntohl(cpdu->cbu_rootpathcost);
2933fab7669SAndrew Thompson cu->cu_message_age = ntohs(cpdu->cbu_messageage);
2943fab7669SAndrew Thompson cu->cu_max_age = ntohs(cpdu->cbu_maxage);
2953fab7669SAndrew Thompson cu->cu_hello_time = ntohs(cpdu->cbu_hellotime);
2963fab7669SAndrew Thompson cu->cu_forward_delay = ntohs(cpdu->cbu_forwarddelay);
2973fab7669SAndrew Thompson cu->cu_pv.pv_dport_id = ntohs(cpdu->cbu_portid);
2983fab7669SAndrew Thompson cu->cu_pv.pv_port_id = bp->bp_port_id;
2993fab7669SAndrew Thompson cu->cu_message_type = cpdu->cbu_bpdutype;
3003fab7669SAndrew Thompson
3013fab7669SAndrew Thompson /* Strip off unused flags in STP mode */
3023fab7669SAndrew Thompson flags = cpdu->cbu_flags;
3033fab7669SAndrew Thompson switch (cpdu->cbu_protover) {
3043fab7669SAndrew Thompson case BSTP_PROTO_STP:
3053fab7669SAndrew Thompson flags &= BSTP_PDU_STPMASK;
3063fab7669SAndrew Thompson /* A STP BPDU explicitly conveys a Designated Port */
3073fab7669SAndrew Thompson cu->cu_role = BSTP_ROLE_DESIGNATED;
3083fab7669SAndrew Thompson break;
3093fab7669SAndrew Thompson
3103fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
3113fab7669SAndrew Thompson flags &= BSTP_PDU_RSTPMASK;
3123fab7669SAndrew Thompson break;
3133fab7669SAndrew Thompson }
3143fab7669SAndrew Thompson
3153fab7669SAndrew Thompson cu->cu_topology_change_ack =
3163fab7669SAndrew Thompson (flags & BSTP_PDU_F_TCA) ? 1 : 0;
3173fab7669SAndrew Thompson cu->cu_proposal =
3183fab7669SAndrew Thompson (flags & BSTP_PDU_F_P) ? 1 : 0;
3193fab7669SAndrew Thompson cu->cu_agree =
3203fab7669SAndrew Thompson (flags & BSTP_PDU_F_A) ? 1 : 0;
3213fab7669SAndrew Thompson cu->cu_learning =
3223fab7669SAndrew Thompson (flags & BSTP_PDU_F_L) ? 1 : 0;
3233fab7669SAndrew Thompson cu->cu_forwarding =
3243fab7669SAndrew Thompson (flags & BSTP_PDU_F_F) ? 1 : 0;
3253fab7669SAndrew Thompson cu->cu_topology_change =
3263fab7669SAndrew Thompson (flags & BSTP_PDU_F_TC) ? 1 : 0;
3273fab7669SAndrew Thompson
3283fab7669SAndrew Thompson switch ((flags & BSTP_PDU_PRMASK) >> BSTP_PDU_PRSHIFT) {
3293fab7669SAndrew Thompson case BSTP_PDU_F_ROOT:
3303fab7669SAndrew Thompson cu->cu_role = BSTP_ROLE_ROOT;
3313fab7669SAndrew Thompson break;
3323fab7669SAndrew Thompson case BSTP_PDU_F_ALT:
3333fab7669SAndrew Thompson cu->cu_role = BSTP_ROLE_ALTERNATE;
3343fab7669SAndrew Thompson break;
3353fab7669SAndrew Thompson case BSTP_PDU_F_DESG:
3363fab7669SAndrew Thompson cu->cu_role = BSTP_ROLE_DESIGNATED;
3373fab7669SAndrew Thompson break;
3383fab7669SAndrew Thompson }
33931997bf2SAndrew Thompson }
34031997bf2SAndrew Thompson
3416b32f3d3SAndrew Thompson static void
bstp_send_bpdu(struct bstp_state * bs,struct bstp_port * bp,struct bstp_cbpdu * bpdu)3423fab7669SAndrew Thompson bstp_send_bpdu(struct bstp_state *bs, struct bstp_port *bp,
3433fab7669SAndrew Thompson struct bstp_cbpdu *bpdu)
34431997bf2SAndrew Thompson {
3453fab7669SAndrew Thompson struct ifnet *ifp;
3463fab7669SAndrew Thompson struct mbuf *m;
3473fab7669SAndrew Thompson struct ether_header *eh;
34831997bf2SAndrew Thompson
34996e47153SAndrew Thompson BSTP_LOCK_ASSERT(bs);
35089fa9c34SKristof Provost NET_EPOCH_ASSERT();
35131997bf2SAndrew Thompson
3523fab7669SAndrew Thompson ifp = bp->bp_ifp;
35331997bf2SAndrew Thompson
3543fab7669SAndrew Thompson if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
3553fab7669SAndrew Thompson return;
35631997bf2SAndrew Thompson
357dc4ad05eSGleb Smirnoff m = m_gethdr(M_NOWAIT, MT_DATA);
3583fab7669SAndrew Thompson if (m == NULL)
3593fab7669SAndrew Thompson return;
36031997bf2SAndrew Thompson
3613fab7669SAndrew Thompson eh = mtod(m, struct ether_header *);
36231997bf2SAndrew Thompson
3633fab7669SAndrew Thompson bpdu->cbu_ssap = bpdu->cbu_dsap = LLC_8021D_LSAP;
3643fab7669SAndrew Thompson bpdu->cbu_ctl = LLC_UI;
3653fab7669SAndrew Thompson bpdu->cbu_protoid = htons(BSTP_PROTO_ID);
36631997bf2SAndrew Thompson
3673fab7669SAndrew Thompson memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
3683fab7669SAndrew Thompson memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
3693fab7669SAndrew Thompson
3703fab7669SAndrew Thompson switch (bpdu->cbu_bpdutype) {
3713fab7669SAndrew Thompson case BSTP_MSGTYPE_CFG:
3723fab7669SAndrew Thompson bpdu->cbu_protover = BSTP_PROTO_STP;
3733fab7669SAndrew Thompson m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_STP_LEN;
3743fab7669SAndrew Thompson eh->ether_type = htons(BSTP_BPDU_STP_LEN);
3753fab7669SAndrew Thompson memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
3763fab7669SAndrew Thompson BSTP_BPDU_STP_LEN);
3773fab7669SAndrew Thompson break;
3783fab7669SAndrew Thompson
3793fab7669SAndrew Thompson case BSTP_MSGTYPE_RSTP:
3803fab7669SAndrew Thompson bpdu->cbu_protover = BSTP_PROTO_RSTP;
3813fab7669SAndrew Thompson bpdu->cbu_versionlen = htons(0);
3823fab7669SAndrew Thompson m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_RSTP_LEN;
3833fab7669SAndrew Thompson eh->ether_type = htons(BSTP_BPDU_RSTP_LEN);
3843fab7669SAndrew Thompson memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
3853fab7669SAndrew Thompson BSTP_BPDU_RSTP_LEN);
3863fab7669SAndrew Thompson break;
3873fab7669SAndrew Thompson
3883fab7669SAndrew Thompson default:
3893fab7669SAndrew Thompson panic("not implemented");
39031997bf2SAndrew Thompson }
3913fab7669SAndrew Thompson m->m_pkthdr.rcvif = ifp;
3923fab7669SAndrew Thompson m->m_len = m->m_pkthdr.len;
39331997bf2SAndrew Thompson
3943fab7669SAndrew Thompson bp->bp_txcount++;
3956e3513f5SKip Macy ifp->if_transmit(ifp, m);
39631997bf2SAndrew Thompson }
39731997bf2SAndrew Thompson
3983fab7669SAndrew Thompson static int
bstp_pdu_flags(struct bstp_port * bp)3993fab7669SAndrew Thompson bstp_pdu_flags(struct bstp_port *bp)
40031997bf2SAndrew Thompson {
4013fab7669SAndrew Thompson int flags = 0;
40296e47153SAndrew Thompson
4033fab7669SAndrew Thompson if (bp->bp_proposing && bp->bp_state != BSTP_IFSTATE_FORWARDING)
4043fab7669SAndrew Thompson flags |= BSTP_PDU_F_P;
4053fab7669SAndrew Thompson
4063fab7669SAndrew Thompson if (bp->bp_agree)
4073fab7669SAndrew Thompson flags |= BSTP_PDU_F_A;
4083fab7669SAndrew Thompson
4093fab7669SAndrew Thompson if (bp->bp_tc_timer.active)
4103fab7669SAndrew Thompson flags |= BSTP_PDU_F_TC;
4113fab7669SAndrew Thompson
4123fab7669SAndrew Thompson if (bp->bp_tc_ack)
4133fab7669SAndrew Thompson flags |= BSTP_PDU_F_TCA;
4143fab7669SAndrew Thompson
4153fab7669SAndrew Thompson switch (bp->bp_state) {
4163fab7669SAndrew Thompson case BSTP_IFSTATE_LEARNING:
4173fab7669SAndrew Thompson flags |= BSTP_PDU_F_L;
4183fab7669SAndrew Thompson break;
4193fab7669SAndrew Thompson
4203fab7669SAndrew Thompson case BSTP_IFSTATE_FORWARDING:
4213fab7669SAndrew Thompson flags |= (BSTP_PDU_F_L | BSTP_PDU_F_F);
4223fab7669SAndrew Thompson break;
42331997bf2SAndrew Thompson }
42431997bf2SAndrew Thompson
4253fab7669SAndrew Thompson switch (bp->bp_role) {
4263fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
4273fab7669SAndrew Thompson flags |=
4283fab7669SAndrew Thompson (BSTP_PDU_F_ROOT << BSTP_PDU_PRSHIFT);
4293fab7669SAndrew Thompson break;
43031997bf2SAndrew Thompson
4313fab7669SAndrew Thompson case BSTP_ROLE_ALTERNATE:
4323fab7669SAndrew Thompson case BSTP_ROLE_BACKUP: /* fall through */
4333fab7669SAndrew Thompson flags |=
4343fab7669SAndrew Thompson (BSTP_PDU_F_ALT << BSTP_PDU_PRSHIFT);
4353fab7669SAndrew Thompson break;
43696e47153SAndrew Thompson
4373fab7669SAndrew Thompson case BSTP_ROLE_DESIGNATED:
4383fab7669SAndrew Thompson flags |=
4393fab7669SAndrew Thompson (BSTP_PDU_F_DESG << BSTP_PDU_PRSHIFT);
4403fab7669SAndrew Thompson break;
44131997bf2SAndrew Thompson }
44231997bf2SAndrew Thompson
4433fab7669SAndrew Thompson /* Strip off unused flags in either mode */
4443fab7669SAndrew Thompson switch (bp->bp_protover) {
4453fab7669SAndrew Thompson case BSTP_PROTO_STP:
4463fab7669SAndrew Thompson flags &= BSTP_PDU_STPMASK;
4473fab7669SAndrew Thompson break;
4483fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
4493fab7669SAndrew Thompson flags &= BSTP_PDU_RSTPMASK;
4503fab7669SAndrew Thompson break;
45131997bf2SAndrew Thompson }
4523fab7669SAndrew Thompson return (flags);
45331997bf2SAndrew Thompson }
45431997bf2SAndrew Thompson
4554661f862SAndrew Thompson void
bstp_input(struct bstp_port * bp,struct ifnet * ifp,struct mbuf * m)45696e47153SAndrew Thompson bstp_input(struct bstp_port *bp, struct ifnet *ifp, struct mbuf *m)
45731997bf2SAndrew Thompson {
45896e47153SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
45931997bf2SAndrew Thompson struct ether_header *eh;
46031997bf2SAndrew Thompson struct bstp_tbpdu tpdu;
46131997bf2SAndrew Thompson uint16_t len;
46231997bf2SAndrew Thompson
46396e47153SAndrew Thompson if (bp->bp_active == 0) {
46496e47153SAndrew Thompson m_freem(m);
4654661f862SAndrew Thompson return;
46696e47153SAndrew Thompson }
46796e47153SAndrew Thompson
46896e47153SAndrew Thompson BSTP_LOCK(bs);
46931997bf2SAndrew Thompson
47031997bf2SAndrew Thompson eh = mtod(m, struct ether_header *);
47131997bf2SAndrew Thompson
47231997bf2SAndrew Thompson len = ntohs(eh->ether_type);
47331997bf2SAndrew Thompson if (len < sizeof(tpdu))
47431997bf2SAndrew Thompson goto out;
47531997bf2SAndrew Thompson
47631997bf2SAndrew Thompson m_adj(m, ETHER_HDR_LEN);
47731997bf2SAndrew Thompson
47831997bf2SAndrew Thompson if (m->m_pkthdr.len > len)
47931997bf2SAndrew Thompson m_adj(m, len - m->m_pkthdr.len);
48031997bf2SAndrew Thompson if (m->m_len < sizeof(tpdu) &&
48131997bf2SAndrew Thompson (m = m_pullup(m, sizeof(tpdu))) == NULL)
48231997bf2SAndrew Thompson goto out;
48331997bf2SAndrew Thompson
48431997bf2SAndrew Thompson memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu));
48531997bf2SAndrew Thompson
4863fab7669SAndrew Thompson /* basic packet checks */
48731997bf2SAndrew Thompson if (tpdu.tbu_dsap != LLC_8021D_LSAP ||
48831997bf2SAndrew Thompson tpdu.tbu_ssap != LLC_8021D_LSAP ||
48931997bf2SAndrew Thompson tpdu.tbu_ctl != LLC_UI)
49031997bf2SAndrew Thompson goto out;
4913fab7669SAndrew Thompson if (tpdu.tbu_protoid != BSTP_PROTO_ID)
49231997bf2SAndrew Thompson goto out;
49331997bf2SAndrew Thompson
4943fab7669SAndrew Thompson /*
4953fab7669SAndrew Thompson * We can treat later versions of the PDU as the same as the maximum
4963fab7669SAndrew Thompson * version we implement. All additional parameters/flags are ignored.
4973fab7669SAndrew Thompson */
4983fab7669SAndrew Thompson if (tpdu.tbu_protover > BSTP_PROTO_MAX)
4993fab7669SAndrew Thompson tpdu.tbu_protover = BSTP_PROTO_MAX;
50031997bf2SAndrew Thompson
5013fab7669SAndrew Thompson if (tpdu.tbu_protover != bp->bp_protover) {
5023fab7669SAndrew Thompson /*
5033fab7669SAndrew Thompson * Wait for the migration delay timer to expire before changing
5043fab7669SAndrew Thompson * protocol version to avoid flip-flops.
5053fab7669SAndrew Thompson */
5063fab7669SAndrew Thompson if (bp->bp_flags & BSTP_PORT_CANMIGRATE)
5073fab7669SAndrew Thompson bstp_set_port_proto(bp, tpdu.tbu_protover);
5083fab7669SAndrew Thompson else
50931997bf2SAndrew Thompson goto out;
51031997bf2SAndrew Thompson }
51131997bf2SAndrew Thompson
5123fab7669SAndrew Thompson /* Clear operedge upon receiving a PDU on the port */
5133fab7669SAndrew Thompson bp->bp_operedge = 0;
5143fab7669SAndrew Thompson bstp_timer_start(&bp->bp_edge_delay_timer,
5153fab7669SAndrew Thompson BSTP_DEFAULT_MIGRATE_DELAY);
5163fab7669SAndrew Thompson
5173fab7669SAndrew Thompson switch (tpdu.tbu_protover) {
5183fab7669SAndrew Thompson case BSTP_PROTO_STP:
519cd281f6dSAndrew Thompson bstp_received_stp(bs, bp, &m, &tpdu);
5203fab7669SAndrew Thompson break;
5213fab7669SAndrew Thompson
5223fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
523cd281f6dSAndrew Thompson bstp_received_rstp(bs, bp, &m, &tpdu);
5243fab7669SAndrew Thompson break;
5253fab7669SAndrew Thompson }
52631997bf2SAndrew Thompson out:
52796e47153SAndrew Thompson BSTP_UNLOCK(bs);
52831997bf2SAndrew Thompson if (m)
52931997bf2SAndrew Thompson m_freem(m);
53031997bf2SAndrew Thompson }
53131997bf2SAndrew Thompson
5326b32f3d3SAndrew Thompson static void
bstp_received_stp(struct bstp_state * bs,struct bstp_port * bp,struct mbuf ** mp,struct bstp_tbpdu * tpdu)5333fab7669SAndrew Thompson bstp_received_stp(struct bstp_state *bs, struct bstp_port *bp,
534cd281f6dSAndrew Thompson struct mbuf **mp, struct bstp_tbpdu *tpdu)
53531997bf2SAndrew Thompson {
5363fab7669SAndrew Thompson struct bstp_cbpdu cpdu;
5373fab7669SAndrew Thompson struct bstp_config_unit *cu = &bp->bp_msg_cu;
5383fab7669SAndrew Thompson struct bstp_tcn_unit tu;
53931997bf2SAndrew Thompson
5403fab7669SAndrew Thompson switch (tpdu->tbu_bpdutype) {
5413fab7669SAndrew Thompson case BSTP_MSGTYPE_TCN:
5423fab7669SAndrew Thompson tu.tu_message_type = tpdu->tbu_bpdutype;
5433fab7669SAndrew Thompson bstp_received_tcn(bs, bp, &tu);
5443fab7669SAndrew Thompson break;
5453fab7669SAndrew Thompson case BSTP_MSGTYPE_CFG:
546cd281f6dSAndrew Thompson if ((*mp)->m_len < BSTP_BPDU_STP_LEN &&
547cd281f6dSAndrew Thompson (*mp = m_pullup(*mp, BSTP_BPDU_STP_LEN)) == NULL)
5483fab7669SAndrew Thompson return;
549cd281f6dSAndrew Thompson memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_STP_LEN);
55031997bf2SAndrew Thompson
5513fab7669SAndrew Thompson bstp_decode_bpdu(bp, &cpdu, cu);
5523fab7669SAndrew Thompson bstp_received_bpdu(bs, bp, cu);
5533fab7669SAndrew Thompson break;
55431997bf2SAndrew Thompson }
55531997bf2SAndrew Thompson }
55631997bf2SAndrew Thompson
5576b32f3d3SAndrew Thompson static void
bstp_received_rstp(struct bstp_state * bs,struct bstp_port * bp,struct mbuf ** mp,struct bstp_tbpdu * tpdu)5583fab7669SAndrew Thompson bstp_received_rstp(struct bstp_state *bs, struct bstp_port *bp,
559cd281f6dSAndrew Thompson struct mbuf **mp, struct bstp_tbpdu *tpdu)
56031997bf2SAndrew Thompson {
5613fab7669SAndrew Thompson struct bstp_cbpdu cpdu;
5623fab7669SAndrew Thompson struct bstp_config_unit *cu = &bp->bp_msg_cu;
56331997bf2SAndrew Thompson
564cd281f6dSAndrew Thompson if (tpdu->tbu_bpdutype != BSTP_MSGTYPE_RSTP)
56531997bf2SAndrew Thompson return;
56631997bf2SAndrew Thompson
567cd281f6dSAndrew Thompson if ((*mp)->m_len < BSTP_BPDU_RSTP_LEN &&
568cd281f6dSAndrew Thompson (*mp = m_pullup(*mp, BSTP_BPDU_RSTP_LEN)) == NULL)
5693fab7669SAndrew Thompson return;
570cd281f6dSAndrew Thompson memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_RSTP_LEN);
57131997bf2SAndrew Thompson
5723fab7669SAndrew Thompson bstp_decode_bpdu(bp, &cpdu, cu);
5733fab7669SAndrew Thompson bstp_received_bpdu(bs, bp, cu);
5743fab7669SAndrew Thompson }
57531997bf2SAndrew Thompson
5763fab7669SAndrew Thompson static void
bstp_received_tcn(struct bstp_state * bs,struct bstp_port * bp,struct bstp_tcn_unit * tcn)5773fab7669SAndrew Thompson bstp_received_tcn(struct bstp_state *bs, struct bstp_port *bp,
5783fab7669SAndrew Thompson struct bstp_tcn_unit *tcn)
5793fab7669SAndrew Thompson {
5803fab7669SAndrew Thompson bp->bp_rcvdtcn = 1;
5813fab7669SAndrew Thompson bstp_update_tc(bp);
5823fab7669SAndrew Thompson }
58331997bf2SAndrew Thompson
5843fab7669SAndrew Thompson static void
bstp_received_bpdu(struct bstp_state * bs,struct bstp_port * bp,struct bstp_config_unit * cu)5853fab7669SAndrew Thompson bstp_received_bpdu(struct bstp_state *bs, struct bstp_port *bp,
5863fab7669SAndrew Thompson struct bstp_config_unit *cu)
5873fab7669SAndrew Thompson {
5883fab7669SAndrew Thompson int type;
58931997bf2SAndrew Thompson
5903fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
59196e47153SAndrew Thompson
5923fab7669SAndrew Thompson /* We need to have transitioned to INFO_MINE before proceeding */
5933fab7669SAndrew Thompson switch (bp->bp_infois) {
5943fab7669SAndrew Thompson case BSTP_INFO_DISABLED:
5953fab7669SAndrew Thompson case BSTP_INFO_AGED:
5963fab7669SAndrew Thompson return;
5973fab7669SAndrew Thompson }
5983fab7669SAndrew Thompson
5990e4025bfSJonah Caplan /* range checks */
6000e4025bfSJonah Caplan if (cu->cu_message_age >= cu->cu_max_age) {
6010e4025bfSJonah Caplan return;
6020e4025bfSJonah Caplan }
6030e4025bfSJonah Caplan if (cu->cu_max_age < BSTP_MIN_MAX_AGE ||
6040e4025bfSJonah Caplan cu->cu_max_age > BSTP_MAX_MAX_AGE) {
6050e4025bfSJonah Caplan return;
6060e4025bfSJonah Caplan }
6070e4025bfSJonah Caplan if (cu->cu_forward_delay < BSTP_MIN_FORWARD_DELAY ||
6080e4025bfSJonah Caplan cu->cu_forward_delay > BSTP_MAX_FORWARD_DELAY) {
6090e4025bfSJonah Caplan return;
6100e4025bfSJonah Caplan }
6110e4025bfSJonah Caplan if (cu->cu_hello_time < BSTP_MIN_HELLO_TIME ||
6120e4025bfSJonah Caplan cu->cu_hello_time > BSTP_MAX_HELLO_TIME) {
6130e4025bfSJonah Caplan return;
6140e4025bfSJonah Caplan }
6150e4025bfSJonah Caplan
6163fab7669SAndrew Thompson type = bstp_pdu_rcvtype(bp, cu);
6173fab7669SAndrew Thompson
6183fab7669SAndrew Thompson switch (type) {
6193fab7669SAndrew Thompson case BSTP_PDU_SUPERIOR:
6203fab7669SAndrew Thompson bs->bs_allsynced = 0;
6213fab7669SAndrew Thompson bp->bp_agreed = 0;
6223fab7669SAndrew Thompson bp->bp_proposing = 0;
6233fab7669SAndrew Thompson
6243fab7669SAndrew Thompson if (cu->cu_proposal && cu->cu_forwarding == 0)
6253fab7669SAndrew Thompson bp->bp_proposed = 1;
6263fab7669SAndrew Thompson if (cu->cu_topology_change)
6273fab7669SAndrew Thompson bp->bp_rcvdtc = 1;
6283fab7669SAndrew Thompson if (cu->cu_topology_change_ack)
6293fab7669SAndrew Thompson bp->bp_rcvdtca = 1;
6303fab7669SAndrew Thompson
6313fab7669SAndrew Thompson if (bp->bp_agree &&
6329ddd3624SAndrew Thompson !bstp_pdu_bettersame(bp, BSTP_INFO_RECEIVED))
6333fab7669SAndrew Thompson bp->bp_agree = 0;
6343fab7669SAndrew Thompson
6353fab7669SAndrew Thompson /* copy the received priority and timers to the port */
6363fab7669SAndrew Thompson bp->bp_port_pv = cu->cu_pv;
6373fab7669SAndrew Thompson bp->bp_port_msg_age = cu->cu_message_age;
6383fab7669SAndrew Thompson bp->bp_port_max_age = cu->cu_max_age;
6393fab7669SAndrew Thompson bp->bp_port_fdelay = cu->cu_forward_delay;
6403fab7669SAndrew Thompson bp->bp_port_htime =
6413fab7669SAndrew Thompson (cu->cu_hello_time > BSTP_MIN_HELLO_TIME ?
6423fab7669SAndrew Thompson cu->cu_hello_time : BSTP_MIN_HELLO_TIME);
6433fab7669SAndrew Thompson
6443fab7669SAndrew Thompson /* set expiry for the new info */
6453fab7669SAndrew Thompson bstp_set_timer_msgage(bp);
6463fab7669SAndrew Thompson
6479ddd3624SAndrew Thompson bp->bp_infois = BSTP_INFO_RECEIVED;
6483fab7669SAndrew Thompson bstp_assign_roles(bs);
6493fab7669SAndrew Thompson break;
6503fab7669SAndrew Thompson
6513fab7669SAndrew Thompson case BSTP_PDU_REPEATED:
6523fab7669SAndrew Thompson if (cu->cu_proposal && cu->cu_forwarding == 0)
6533fab7669SAndrew Thompson bp->bp_proposed = 1;
6543fab7669SAndrew Thompson if (cu->cu_topology_change)
6553fab7669SAndrew Thompson bp->bp_rcvdtc = 1;
6563fab7669SAndrew Thompson if (cu->cu_topology_change_ack)
6573fab7669SAndrew Thompson bp->bp_rcvdtca = 1;
6583fab7669SAndrew Thompson
6593fab7669SAndrew Thompson /* rearm the age timer */
6603fab7669SAndrew Thompson bstp_set_timer_msgage(bp);
6613fab7669SAndrew Thompson break;
6623fab7669SAndrew Thompson
6633fab7669SAndrew Thompson case BSTP_PDU_INFERIOR:
6643fab7669SAndrew Thompson if (cu->cu_learning) {
6653fab7669SAndrew Thompson bp->bp_agreed = 1;
6663fab7669SAndrew Thompson bp->bp_proposing = 0;
6673fab7669SAndrew Thompson }
6683fab7669SAndrew Thompson break;
6693fab7669SAndrew Thompson
6703fab7669SAndrew Thompson case BSTP_PDU_INFERIORALT:
6713fab7669SAndrew Thompson /*
6723fab7669SAndrew Thompson * only point to point links are allowed fast
6733fab7669SAndrew Thompson * transitions to forwarding.
6743fab7669SAndrew Thompson */
67578709605SAndrew Thompson if (cu->cu_agree && bp->bp_ptp_link) {
6763fab7669SAndrew Thompson bp->bp_agreed = 1;
6773fab7669SAndrew Thompson bp->bp_proposing = 0;
6783fab7669SAndrew Thompson } else
6793fab7669SAndrew Thompson bp->bp_agreed = 0;
6803fab7669SAndrew Thompson
6813fab7669SAndrew Thompson if (cu->cu_topology_change)
6823fab7669SAndrew Thompson bp->bp_rcvdtc = 1;
6833fab7669SAndrew Thompson if (cu->cu_topology_change_ack)
6843fab7669SAndrew Thompson bp->bp_rcvdtca = 1;
6853fab7669SAndrew Thompson break;
6863fab7669SAndrew Thompson
6873fab7669SAndrew Thompson case BSTP_PDU_OTHER:
6883fab7669SAndrew Thompson return; /* do nothing */
6893fab7669SAndrew Thompson }
6903fab7669SAndrew Thompson /* update the state machines with the new data */
6913fab7669SAndrew Thompson bstp_update_state(bs, bp);
69231997bf2SAndrew Thompson }
69331997bf2SAndrew Thompson
69496e47153SAndrew Thompson static int
bstp_pdu_rcvtype(struct bstp_port * bp,struct bstp_config_unit * cu)6953fab7669SAndrew Thompson bstp_pdu_rcvtype(struct bstp_port *bp, struct bstp_config_unit *cu)
69696e47153SAndrew Thompson {
6973fab7669SAndrew Thompson int type;
69896e47153SAndrew Thompson
6993fab7669SAndrew Thompson /* default return type */
7003fab7669SAndrew Thompson type = BSTP_PDU_OTHER;
7013fab7669SAndrew Thompson
7023fab7669SAndrew Thompson switch (cu->cu_role) {
7033fab7669SAndrew Thompson case BSTP_ROLE_DESIGNATED:
7043fab7669SAndrew Thompson if (bstp_info_superior(&bp->bp_port_pv, &cu->cu_pv))
7053fab7669SAndrew Thompson /* bpdu priority is superior */
7063fab7669SAndrew Thompson type = BSTP_PDU_SUPERIOR;
7073fab7669SAndrew Thompson else if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) ==
7083fab7669SAndrew Thompson INFO_SAME) {
7093fab7669SAndrew Thompson if (bp->bp_port_msg_age != cu->cu_message_age ||
7103fab7669SAndrew Thompson bp->bp_port_max_age != cu->cu_max_age ||
7113fab7669SAndrew Thompson bp->bp_port_fdelay != cu->cu_forward_delay ||
7123fab7669SAndrew Thompson bp->bp_port_htime != cu->cu_hello_time)
7133fab7669SAndrew Thompson /* bpdu priority is equal and timers differ */
7143fab7669SAndrew Thompson type = BSTP_PDU_SUPERIOR;
7153fab7669SAndrew Thompson else
7163fab7669SAndrew Thompson /* bpdu is equal */
7173fab7669SAndrew Thompson type = BSTP_PDU_REPEATED;
7183fab7669SAndrew Thompson } else
7193fab7669SAndrew Thompson /* bpdu priority is worse */
7203fab7669SAndrew Thompson type = BSTP_PDU_INFERIOR;
7213fab7669SAndrew Thompson
72296e47153SAndrew Thompson break;
7233fab7669SAndrew Thompson
7243fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
7253fab7669SAndrew Thompson case BSTP_ROLE_ALTERNATE:
7263fab7669SAndrew Thompson case BSTP_ROLE_BACKUP:
7273fab7669SAndrew Thompson if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) <= INFO_SAME)
7283fab7669SAndrew Thompson /*
7293fab7669SAndrew Thompson * not a designated port and priority is the same or
7303fab7669SAndrew Thompson * worse
7313fab7669SAndrew Thompson */
7323fab7669SAndrew Thompson type = BSTP_PDU_INFERIORALT;
73396e47153SAndrew Thompson break;
73496e47153SAndrew Thompson }
7353fab7669SAndrew Thompson
7363fab7669SAndrew Thompson return (type);
7373fab7669SAndrew Thompson }
7383fab7669SAndrew Thompson
7393fab7669SAndrew Thompson static int
bstp_pdu_bettersame(struct bstp_port * bp,int newinfo)7403fab7669SAndrew Thompson bstp_pdu_bettersame(struct bstp_port *bp, int newinfo)
7413fab7669SAndrew Thompson {
7429ddd3624SAndrew Thompson if (newinfo == BSTP_INFO_RECEIVED &&
7439ddd3624SAndrew Thompson bp->bp_infois == BSTP_INFO_RECEIVED &&
7443fab7669SAndrew Thompson bstp_info_cmp(&bp->bp_port_pv, &bp->bp_msg_cu.cu_pv) >= INFO_SAME)
7453fab7669SAndrew Thompson return (1);
7463fab7669SAndrew Thompson
7473fab7669SAndrew Thompson if (newinfo == BSTP_INFO_MINE &&
7483fab7669SAndrew Thompson bp->bp_infois == BSTP_INFO_MINE &&
7493fab7669SAndrew Thompson bstp_info_cmp(&bp->bp_port_pv, &bp->bp_desg_pv) >= INFO_SAME)
7503fab7669SAndrew Thompson return (1);
7513fab7669SAndrew Thompson
75296e47153SAndrew Thompson return (0);
75396e47153SAndrew Thompson }
75496e47153SAndrew Thompson
7553fab7669SAndrew Thompson static int
bstp_info_cmp(struct bstp_pri_vector * pv,struct bstp_pri_vector * cpv)7563fab7669SAndrew Thompson bstp_info_cmp(struct bstp_pri_vector *pv,
7573fab7669SAndrew Thompson struct bstp_pri_vector *cpv)
75896e47153SAndrew Thompson {
7593fab7669SAndrew Thompson if (cpv->pv_root_id < pv->pv_root_id)
7603fab7669SAndrew Thompson return (INFO_BETTER);
7613fab7669SAndrew Thompson if (cpv->pv_root_id > pv->pv_root_id)
7623fab7669SAndrew Thompson return (INFO_WORSE);
76396e47153SAndrew Thompson
7643fab7669SAndrew Thompson if (cpv->pv_cost < pv->pv_cost)
7653fab7669SAndrew Thompson return (INFO_BETTER);
7663fab7669SAndrew Thompson if (cpv->pv_cost > pv->pv_cost)
7673fab7669SAndrew Thompson return (INFO_WORSE);
76896e47153SAndrew Thompson
7693fab7669SAndrew Thompson if (cpv->pv_dbridge_id < pv->pv_dbridge_id)
7703fab7669SAndrew Thompson return (INFO_BETTER);
7713fab7669SAndrew Thompson if (cpv->pv_dbridge_id > pv->pv_dbridge_id)
7723fab7669SAndrew Thompson return (INFO_WORSE);
7733fab7669SAndrew Thompson
7743fab7669SAndrew Thompson if (cpv->pv_dport_id < pv->pv_dport_id)
7753fab7669SAndrew Thompson return (INFO_BETTER);
7763fab7669SAndrew Thompson if (cpv->pv_dport_id > pv->pv_dport_id)
7773fab7669SAndrew Thompson return (INFO_WORSE);
7783fab7669SAndrew Thompson
7793fab7669SAndrew Thompson return (INFO_SAME);
78031997bf2SAndrew Thompson }
78131997bf2SAndrew Thompson
7823fab7669SAndrew Thompson /*
7833fab7669SAndrew Thompson * This message priority vector is superior to the port priority vector and
7843fab7669SAndrew Thompson * will replace it if, and only if, the message priority vector is better than
7853fab7669SAndrew Thompson * the port priority vector, or the message has been transmitted from the same
7863fab7669SAndrew Thompson * designated bridge and designated port as the port priority vector.
7873fab7669SAndrew Thompson */
7883fab7669SAndrew Thompson static int
bstp_info_superior(struct bstp_pri_vector * pv,struct bstp_pri_vector * cpv)7893fab7669SAndrew Thompson bstp_info_superior(struct bstp_pri_vector *pv,
7903fab7669SAndrew Thompson struct bstp_pri_vector *cpv)
79131997bf2SAndrew Thompson {
7923fab7669SAndrew Thompson if (bstp_info_cmp(pv, cpv) == INFO_BETTER ||
7933fab7669SAndrew Thompson (bstp_same_bridgeid(pv->pv_dbridge_id, cpv->pv_dbridge_id) &&
7943fab7669SAndrew Thompson (cpv->pv_dport_id & 0xfff) == (pv->pv_dport_id & 0xfff)))
7953fab7669SAndrew Thompson return (1);
7963fab7669SAndrew Thompson return (0);
79731997bf2SAndrew Thompson }
79831997bf2SAndrew Thompson
7993fab7669SAndrew Thompson static void
bstp_assign_roles(struct bstp_state * bs)8003fab7669SAndrew Thompson bstp_assign_roles(struct bstp_state *bs)
80196e47153SAndrew Thompson {
8023fab7669SAndrew Thompson struct bstp_port *bp, *rbp = NULL;
8033fab7669SAndrew Thompson struct bstp_pri_vector pv;
8043fab7669SAndrew Thompson
8053fab7669SAndrew Thompson /* default to our priority vector */
8063fab7669SAndrew Thompson bs->bs_root_pv = bs->bs_bridge_pv;
8073fab7669SAndrew Thompson bs->bs_root_msg_age = 0;
8083fab7669SAndrew Thompson bs->bs_root_max_age = bs->bs_bridge_max_age;
8093fab7669SAndrew Thompson bs->bs_root_fdelay = bs->bs_bridge_fdelay;
8103fab7669SAndrew Thompson bs->bs_root_htime = bs->bs_bridge_htime;
8113fab7669SAndrew Thompson bs->bs_root_port = NULL;
8123fab7669SAndrew Thompson
813a4641f4eSPedro F. Giffuni /* check if any received info supersedes us */
8143fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
8159ddd3624SAndrew Thompson if (bp->bp_infois != BSTP_INFO_RECEIVED)
8163fab7669SAndrew Thompson continue;
8173fab7669SAndrew Thompson
8183fab7669SAndrew Thompson pv = bp->bp_port_pv;
8193fab7669SAndrew Thompson pv.pv_cost += bp->bp_path_cost;
8203fab7669SAndrew Thompson
8213fab7669SAndrew Thompson /*
8223fab7669SAndrew Thompson * The root priority vector is the best of the set comprising
8233fab7669SAndrew Thompson * the bridge priority vector plus all root path priority
8243fab7669SAndrew Thompson * vectors whose bridge address is not equal to us.
8253fab7669SAndrew Thompson */
8263fab7669SAndrew Thompson if (bstp_same_bridgeid(pv.pv_dbridge_id,
8273fab7669SAndrew Thompson bs->bs_bridge_pv.pv_dbridge_id) == 0 &&
8283fab7669SAndrew Thompson bstp_info_cmp(&bs->bs_root_pv, &pv) == INFO_BETTER) {
8293fab7669SAndrew Thompson /* the port vector replaces the root */
8303fab7669SAndrew Thompson bs->bs_root_pv = pv;
8313fab7669SAndrew Thompson bs->bs_root_msg_age = bp->bp_port_msg_age +
8323fab7669SAndrew Thompson BSTP_MESSAGE_AGE_INCR;
8333fab7669SAndrew Thompson bs->bs_root_max_age = bp->bp_port_max_age;
8343fab7669SAndrew Thompson bs->bs_root_fdelay = bp->bp_port_fdelay;
8353fab7669SAndrew Thompson bs->bs_root_htime = bp->bp_port_htime;
8363fab7669SAndrew Thompson rbp = bp;
8373fab7669SAndrew Thompson }
83896e47153SAndrew Thompson }
83931997bf2SAndrew Thompson
8403fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
8413fab7669SAndrew Thompson /* calculate the port designated vector */
8423fab7669SAndrew Thompson bp->bp_desg_pv.pv_root_id = bs->bs_root_pv.pv_root_id;
8433fab7669SAndrew Thompson bp->bp_desg_pv.pv_cost = bs->bs_root_pv.pv_cost;
8443fab7669SAndrew Thompson bp->bp_desg_pv.pv_dbridge_id = bs->bs_bridge_pv.pv_dbridge_id;
8453fab7669SAndrew Thompson bp->bp_desg_pv.pv_dport_id = bp->bp_port_id;
8463fab7669SAndrew Thompson bp->bp_desg_pv.pv_port_id = bp->bp_port_id;
8473fab7669SAndrew Thompson
8483fab7669SAndrew Thompson /* calculate designated times */
8493fab7669SAndrew Thompson bp->bp_desg_msg_age = bs->bs_root_msg_age;
8503fab7669SAndrew Thompson bp->bp_desg_max_age = bs->bs_root_max_age;
8513fab7669SAndrew Thompson bp->bp_desg_fdelay = bs->bs_root_fdelay;
8523fab7669SAndrew Thompson bp->bp_desg_htime = bs->bs_bridge_htime;
8533fab7669SAndrew Thompson
8543fab7669SAndrew Thompson switch (bp->bp_infois) {
8553fab7669SAndrew Thompson case BSTP_INFO_DISABLED:
8563fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
8573fab7669SAndrew Thompson break;
8583fab7669SAndrew Thompson
8593fab7669SAndrew Thompson case BSTP_INFO_AGED:
8603fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
8613fab7669SAndrew Thompson bstp_update_info(bp);
8623fab7669SAndrew Thompson break;
8633fab7669SAndrew Thompson
8643fab7669SAndrew Thompson case BSTP_INFO_MINE:
8653fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
8663fab7669SAndrew Thompson /* update the port info if stale */
8673fab7669SAndrew Thompson if (bstp_info_cmp(&bp->bp_port_pv,
8683fab7669SAndrew Thompson &bp->bp_desg_pv) != INFO_SAME ||
8693fab7669SAndrew Thompson (rbp != NULL &&
8703fab7669SAndrew Thompson (bp->bp_port_msg_age != rbp->bp_port_msg_age ||
8713fab7669SAndrew Thompson bp->bp_port_max_age != rbp->bp_port_max_age ||
8723fab7669SAndrew Thompson bp->bp_port_fdelay != rbp->bp_port_fdelay ||
8733fab7669SAndrew Thompson bp->bp_port_htime != rbp->bp_port_htime)))
8743fab7669SAndrew Thompson bstp_update_info(bp);
8753fab7669SAndrew Thompson break;
8763fab7669SAndrew Thompson
8779ddd3624SAndrew Thompson case BSTP_INFO_RECEIVED:
8783fab7669SAndrew Thompson if (bp == rbp) {
8793fab7669SAndrew Thompson /*
8803fab7669SAndrew Thompson * root priority is derived from this
8813fab7669SAndrew Thompson * port, make it the root port.
8823fab7669SAndrew Thompson */
8833fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_ROOT);
8843fab7669SAndrew Thompson bs->bs_root_port = bp;
8853fab7669SAndrew Thompson } else if (bstp_info_cmp(&bp->bp_port_pv,
8863fab7669SAndrew Thompson &bp->bp_desg_pv) == INFO_BETTER) {
8873fab7669SAndrew Thompson /*
8883fab7669SAndrew Thompson * the port priority is lower than the root
8893fab7669SAndrew Thompson * port.
8903fab7669SAndrew Thompson */
8913fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
8923fab7669SAndrew Thompson bstp_update_info(bp);
8933fab7669SAndrew Thompson } else {
8943fab7669SAndrew Thompson if (bstp_same_bridgeid(
8953fab7669SAndrew Thompson bp->bp_port_pv.pv_dbridge_id,
8963fab7669SAndrew Thompson bs->bs_bridge_pv.pv_dbridge_id)) {
8973fab7669SAndrew Thompson /*
8983fab7669SAndrew Thompson * the designated bridge refers to
8993fab7669SAndrew Thompson * another port on this bridge.
9003fab7669SAndrew Thompson */
9013fab7669SAndrew Thompson bstp_set_port_role(bp,
9023fab7669SAndrew Thompson BSTP_ROLE_BACKUP);
9033fab7669SAndrew Thompson } else {
9043fab7669SAndrew Thompson /*
9053fab7669SAndrew Thompson * the port is an inferior path to the
9063fab7669SAndrew Thompson * root bridge.
9073fab7669SAndrew Thompson */
9083fab7669SAndrew Thompson bstp_set_port_role(bp,
9093fab7669SAndrew Thompson BSTP_ROLE_ALTERNATE);
9103fab7669SAndrew Thompson }
9113fab7669SAndrew Thompson }
9123fab7669SAndrew Thompson break;
9133fab7669SAndrew Thompson }
9143fab7669SAndrew Thompson }
9153fab7669SAndrew Thompson }
9163fab7669SAndrew Thompson
9173fab7669SAndrew Thompson static void
bstp_update_state(struct bstp_state * bs,struct bstp_port * bp)9183fab7669SAndrew Thompson bstp_update_state(struct bstp_state *bs, struct bstp_port *bp)
9193fab7669SAndrew Thompson {
9203fab7669SAndrew Thompson struct bstp_port *bp2;
9213fab7669SAndrew Thompson int synced;
9223fab7669SAndrew Thompson
9233fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
9243fab7669SAndrew Thompson
9253fab7669SAndrew Thompson /* check if all the ports have syncronised again */
9263fab7669SAndrew Thompson if (!bs->bs_allsynced) {
9273fab7669SAndrew Thompson synced = 1;
9283fab7669SAndrew Thompson LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
929e67b204aSAndrew Thompson if (!(bp2->bp_synced ||
930e67b204aSAndrew Thompson bp2->bp_role == BSTP_ROLE_ROOT)) {
9313fab7669SAndrew Thompson synced = 0;
9323fab7669SAndrew Thompson break;
9333fab7669SAndrew Thompson }
9343fab7669SAndrew Thompson }
9353fab7669SAndrew Thompson bs->bs_allsynced = synced;
9363fab7669SAndrew Thompson }
9373fab7669SAndrew Thompson
9383fab7669SAndrew Thompson bstp_update_roles(bs, bp);
9393fab7669SAndrew Thompson bstp_update_tc(bp);
9403fab7669SAndrew Thompson }
9413fab7669SAndrew Thompson
9423fab7669SAndrew Thompson static void
bstp_update_roles(struct bstp_state * bs,struct bstp_port * bp)9433fab7669SAndrew Thompson bstp_update_roles(struct bstp_state *bs, struct bstp_port *bp)
9443fab7669SAndrew Thompson {
94589fa9c34SKristof Provost NET_EPOCH_ASSERT();
94689fa9c34SKristof Provost
9473fab7669SAndrew Thompson switch (bp->bp_role) {
9483fab7669SAndrew Thompson case BSTP_ROLE_DISABLED:
9493fab7669SAndrew Thompson /* Clear any flags if set */
9503fab7669SAndrew Thompson if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
9513fab7669SAndrew Thompson bp->bp_sync = 0;
9523fab7669SAndrew Thompson bp->bp_synced = 1;
9533fab7669SAndrew Thompson bp->bp_reroot = 0;
9543fab7669SAndrew Thompson }
9553fab7669SAndrew Thompson break;
9563fab7669SAndrew Thompson
9573fab7669SAndrew Thompson case BSTP_ROLE_ALTERNATE:
9583fab7669SAndrew Thompson case BSTP_ROLE_BACKUP:
9593fab7669SAndrew Thompson if ((bs->bs_allsynced && !bp->bp_agree) ||
9603fab7669SAndrew Thompson (bp->bp_proposed && bp->bp_agree)) {
9613fab7669SAndrew Thompson bp->bp_proposed = 0;
9623fab7669SAndrew Thompson bp->bp_agree = 1;
9633fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
9643fab7669SAndrew Thompson DPRINTF("%s -> ALTERNATE_AGREED\n",
9653fab7669SAndrew Thompson bp->bp_ifp->if_xname);
9663fab7669SAndrew Thompson }
9673fab7669SAndrew Thompson
9683fab7669SAndrew Thompson if (bp->bp_proposed && !bp->bp_agree) {
9693fab7669SAndrew Thompson bstp_set_all_sync(bs);
9703fab7669SAndrew Thompson bp->bp_proposed = 0;
9713fab7669SAndrew Thompson DPRINTF("%s -> ALTERNATE_PROPOSED\n",
9723fab7669SAndrew Thompson bp->bp_ifp->if_xname);
9733fab7669SAndrew Thompson }
9743fab7669SAndrew Thompson
9753fab7669SAndrew Thompson /* Clear any flags if set */
9763fab7669SAndrew Thompson if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
9773fab7669SAndrew Thompson bp->bp_sync = 0;
9783fab7669SAndrew Thompson bp->bp_synced = 1;
9793fab7669SAndrew Thompson bp->bp_reroot = 0;
9803fab7669SAndrew Thompson DPRINTF("%s -> ALTERNATE_PORT\n", bp->bp_ifp->if_xname);
9813fab7669SAndrew Thompson }
9823fab7669SAndrew Thompson break;
9833fab7669SAndrew Thompson
9843fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
9853fab7669SAndrew Thompson if (bp->bp_state != BSTP_IFSTATE_FORWARDING && !bp->bp_reroot) {
9863fab7669SAndrew Thompson bstp_set_all_reroot(bs);
9873fab7669SAndrew Thompson DPRINTF("%s -> ROOT_REROOT\n", bp->bp_ifp->if_xname);
9883fab7669SAndrew Thompson }
9893fab7669SAndrew Thompson
9903fab7669SAndrew Thompson if ((bs->bs_allsynced && !bp->bp_agree) ||
9913fab7669SAndrew Thompson (bp->bp_proposed && bp->bp_agree)) {
9923fab7669SAndrew Thompson bp->bp_proposed = 0;
9933fab7669SAndrew Thompson bp->bp_sync = 0;
9943fab7669SAndrew Thompson bp->bp_agree = 1;
9953fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
9963fab7669SAndrew Thompson DPRINTF("%s -> ROOT_AGREED\n", bp->bp_ifp->if_xname);
9973fab7669SAndrew Thompson }
9983fab7669SAndrew Thompson
9993fab7669SAndrew Thompson if (bp->bp_proposed && !bp->bp_agree) {
10003fab7669SAndrew Thompson bstp_set_all_sync(bs);
10013fab7669SAndrew Thompson bp->bp_proposed = 0;
10023fab7669SAndrew Thompson DPRINTF("%s -> ROOT_PROPOSED\n", bp->bp_ifp->if_xname);
10033fab7669SAndrew Thompson }
10043fab7669SAndrew Thompson
10052efdffeeSAndrew Thompson if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
10062efdffeeSAndrew Thompson (bp->bp_forward_delay_timer.active == 0 ||
10073fab7669SAndrew Thompson (bstp_rerooted(bs, bp) &&
10083fab7669SAndrew Thompson bp->bp_recent_backup_timer.active == 0 &&
10092efdffeeSAndrew Thompson bp->bp_protover == BSTP_PROTO_RSTP))) {
10103fab7669SAndrew Thompson switch (bp->bp_state) {
10113fab7669SAndrew Thompson case BSTP_IFSTATE_DISCARDING:
10123fab7669SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
10133fab7669SAndrew Thompson break;
10143fab7669SAndrew Thompson case BSTP_IFSTATE_LEARNING:
10153fab7669SAndrew Thompson bstp_set_port_state(bp,
10163fab7669SAndrew Thompson BSTP_IFSTATE_FORWARDING);
10173fab7669SAndrew Thompson break;
10183fab7669SAndrew Thompson }
10193fab7669SAndrew Thompson }
10203fab7669SAndrew Thompson
10213fab7669SAndrew Thompson if (bp->bp_state == BSTP_IFSTATE_FORWARDING && bp->bp_reroot) {
10223fab7669SAndrew Thompson bp->bp_reroot = 0;
10233fab7669SAndrew Thompson DPRINTF("%s -> ROOT_REROOTED\n", bp->bp_ifp->if_xname);
10243fab7669SAndrew Thompson }
10253fab7669SAndrew Thompson break;
10263fab7669SAndrew Thompson
10273fab7669SAndrew Thompson case BSTP_ROLE_DESIGNATED:
10283fab7669SAndrew Thompson if (bp->bp_recent_root_timer.active == 0 && bp->bp_reroot) {
10293fab7669SAndrew Thompson bp->bp_reroot = 0;
10303fab7669SAndrew Thompson DPRINTF("%s -> DESIGNATED_RETIRED\n",
10313fab7669SAndrew Thompson bp->bp_ifp->if_xname);
10323fab7669SAndrew Thompson }
10333fab7669SAndrew Thompson
10343fab7669SAndrew Thompson if ((bp->bp_state == BSTP_IFSTATE_DISCARDING &&
10353fab7669SAndrew Thompson !bp->bp_synced) || (bp->bp_agreed && !bp->bp_synced) ||
10363fab7669SAndrew Thompson (bp->bp_operedge && !bp->bp_synced) ||
10373fab7669SAndrew Thompson (bp->bp_sync && bp->bp_synced)) {
10383fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_recent_root_timer);
10393fab7669SAndrew Thompson bp->bp_synced = 1;
10403fab7669SAndrew Thompson bp->bp_sync = 0;
10413fab7669SAndrew Thompson DPRINTF("%s -> DESIGNATED_SYNCED\n",
10423fab7669SAndrew Thompson bp->bp_ifp->if_xname);
10433fab7669SAndrew Thompson }
10443fab7669SAndrew Thompson
10453fab7669SAndrew Thompson if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
10463fab7669SAndrew Thompson !bp->bp_agreed && !bp->bp_proposing &&
10473fab7669SAndrew Thompson !bp->bp_operedge) {
10483fab7669SAndrew Thompson bp->bp_proposing = 1;
10493fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
10503fab7669SAndrew Thompson bstp_timer_start(&bp->bp_edge_delay_timer,
105178709605SAndrew Thompson (bp->bp_ptp_link ? BSTP_DEFAULT_MIGRATE_DELAY :
10523fab7669SAndrew Thompson bp->bp_desg_max_age));
10533fab7669SAndrew Thompson DPRINTF("%s -> DESIGNATED_PROPOSE\n",
10543fab7669SAndrew Thompson bp->bp_ifp->if_xname);
10553fab7669SAndrew Thompson }
10563fab7669SAndrew Thompson
10572efdffeeSAndrew Thompson if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
10582efdffeeSAndrew Thompson (bp->bp_forward_delay_timer.active == 0 || bp->bp_agreed ||
10593fab7669SAndrew Thompson bp->bp_operedge) &&
10603fab7669SAndrew Thompson (bp->bp_recent_root_timer.active == 0 || !bp->bp_reroot) &&
10613fab7669SAndrew Thompson !bp->bp_sync) {
10622efdffeeSAndrew Thompson if (bp->bp_agreed)
10632efdffeeSAndrew Thompson DPRINTF("%s -> AGREED\n", bp->bp_ifp->if_xname);
1064edc9f4aeSAndrew Thompson /*
1065edc9f4aeSAndrew Thompson * If agreed|operedge then go straight to forwarding,
1066edc9f4aeSAndrew Thompson * otherwise follow discard -> learn -> forward.
1067edc9f4aeSAndrew Thompson */
1068edc9f4aeSAndrew Thompson if (bp->bp_agreed || bp->bp_operedge ||
1069edc9f4aeSAndrew Thompson bp->bp_state == BSTP_IFSTATE_LEARNING) {
10703fab7669SAndrew Thompson bstp_set_port_state(bp,
10713fab7669SAndrew Thompson BSTP_IFSTATE_FORWARDING);
10723fab7669SAndrew Thompson bp->bp_agreed = bp->bp_protover;
1073edc9f4aeSAndrew Thompson } else if (bp->bp_state == BSTP_IFSTATE_DISCARDING)
1074edc9f4aeSAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
10753fab7669SAndrew Thompson }
10763fab7669SAndrew Thompson
10773fab7669SAndrew Thompson if (((bp->bp_sync && !bp->bp_synced) ||
10783fab7669SAndrew Thompson (bp->bp_reroot && bp->bp_recent_root_timer.active) ||
10793fab7669SAndrew Thompson (bp->bp_flags & BSTP_PORT_DISPUTED)) && !bp->bp_operedge &&
10803fab7669SAndrew Thompson bp->bp_state != BSTP_IFSTATE_DISCARDING) {
10813fab7669SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
10823fab7669SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_DISPUTED;
10833fab7669SAndrew Thompson bstp_timer_start(&bp->bp_forward_delay_timer,
10843fab7669SAndrew Thompson bp->bp_protover == BSTP_PROTO_RSTP ?
10853fab7669SAndrew Thompson bp->bp_desg_htime : bp->bp_desg_fdelay);
10863fab7669SAndrew Thompson DPRINTF("%s -> DESIGNATED_DISCARD\n",
10873fab7669SAndrew Thompson bp->bp_ifp->if_xname);
10883fab7669SAndrew Thompson }
10893fab7669SAndrew Thompson break;
10903fab7669SAndrew Thompson }
10913fab7669SAndrew Thompson
10923fab7669SAndrew Thompson if (bp->bp_flags & BSTP_PORT_NEWINFO)
10933fab7669SAndrew Thompson bstp_transmit(bs, bp);
10943fab7669SAndrew Thompson }
10953fab7669SAndrew Thompson
10963fab7669SAndrew Thompson static void
bstp_update_tc(struct bstp_port * bp)10973fab7669SAndrew Thompson bstp_update_tc(struct bstp_port *bp)
10983fab7669SAndrew Thompson {
10993fab7669SAndrew Thompson switch (bp->bp_tcstate) {
11003fab7669SAndrew Thompson case BSTP_TCSTATE_ACTIVE:
11013fab7669SAndrew Thompson if ((bp->bp_role != BSTP_ROLE_DESIGNATED &&
11023fab7669SAndrew Thompson bp->bp_role != BSTP_ROLE_ROOT) || bp->bp_operedge)
11033fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
11043fab7669SAndrew Thompson
11053fab7669SAndrew Thompson if (bp->bp_rcvdtcn)
11063fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_TCN);
11073fab7669SAndrew Thompson if (bp->bp_rcvdtc)
11083fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_TC);
11093fab7669SAndrew Thompson
11103fab7669SAndrew Thompson if (bp->bp_tc_prop && !bp->bp_operedge)
11113fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_PROPAG);
11123fab7669SAndrew Thompson
11133fab7669SAndrew Thompson if (bp->bp_rcvdtca)
11143fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_ACK);
11153fab7669SAndrew Thompson break;
11163fab7669SAndrew Thompson
11173fab7669SAndrew Thompson case BSTP_TCSTATE_INACTIVE:
11183fab7669SAndrew Thompson if ((bp->bp_state == BSTP_IFSTATE_LEARNING ||
11193fab7669SAndrew Thompson bp->bp_state == BSTP_IFSTATE_FORWARDING) &&
11203fab7669SAndrew Thompson bp->bp_fdbflush == 0)
11213fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
11223fab7669SAndrew Thompson break;
11233fab7669SAndrew Thompson
11243fab7669SAndrew Thompson case BSTP_TCSTATE_LEARNING:
11253fab7669SAndrew Thompson if (bp->bp_rcvdtc || bp->bp_rcvdtcn || bp->bp_rcvdtca ||
11263fab7669SAndrew Thompson bp->bp_tc_prop)
11273fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
11283fab7669SAndrew Thompson else if (bp->bp_role != BSTP_ROLE_DESIGNATED &&
11293fab7669SAndrew Thompson bp->bp_role != BSTP_ROLE_ROOT &&
11303fab7669SAndrew Thompson bp->bp_state == BSTP_IFSTATE_DISCARDING)
11313fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
11323fab7669SAndrew Thompson
11333fab7669SAndrew Thompson if ((bp->bp_role == BSTP_ROLE_DESIGNATED ||
11343fab7669SAndrew Thompson bp->bp_role == BSTP_ROLE_ROOT) &&
11353fab7669SAndrew Thompson bp->bp_state == BSTP_IFSTATE_FORWARDING &&
11363fab7669SAndrew Thompson !bp->bp_operedge)
11373fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_DETECTED);
11383fab7669SAndrew Thompson break;
11393fab7669SAndrew Thompson
11403fab7669SAndrew Thompson /* these are transient states and go straight back to ACTIVE */
11413fab7669SAndrew Thompson case BSTP_TCSTATE_DETECTED:
11423fab7669SAndrew Thompson case BSTP_TCSTATE_TCN:
11433fab7669SAndrew Thompson case BSTP_TCSTATE_TC:
11443fab7669SAndrew Thompson case BSTP_TCSTATE_PROPAG:
11453fab7669SAndrew Thompson case BSTP_TCSTATE_ACK:
11463fab7669SAndrew Thompson DPRINTF("Invalid TC state for %s\n",
11473fab7669SAndrew Thompson bp->bp_ifp->if_xname);
11483fab7669SAndrew Thompson break;
11493fab7669SAndrew Thompson }
11503fab7669SAndrew Thompson
11513fab7669SAndrew Thompson }
11523fab7669SAndrew Thompson
11533fab7669SAndrew Thompson static void
bstp_update_info(struct bstp_port * bp)11543fab7669SAndrew Thompson bstp_update_info(struct bstp_port *bp)
11553fab7669SAndrew Thompson {
11563fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
11573fab7669SAndrew Thompson
11583fab7669SAndrew Thompson bp->bp_proposing = 0;
11593fab7669SAndrew Thompson bp->bp_proposed = 0;
11603fab7669SAndrew Thompson
11613fab7669SAndrew Thompson if (bp->bp_agreed && !bstp_pdu_bettersame(bp, BSTP_INFO_MINE))
11623fab7669SAndrew Thompson bp->bp_agreed = 0;
11633fab7669SAndrew Thompson
11643fab7669SAndrew Thompson if (bp->bp_synced && !bp->bp_agreed) {
11653fab7669SAndrew Thompson bp->bp_synced = 0;
11663fab7669SAndrew Thompson bs->bs_allsynced = 0;
11673fab7669SAndrew Thompson }
11683fab7669SAndrew Thompson
11693fab7669SAndrew Thompson /* copy the designated pv to the port */
11703fab7669SAndrew Thompson bp->bp_port_pv = bp->bp_desg_pv;
11713fab7669SAndrew Thompson bp->bp_port_msg_age = bp->bp_desg_msg_age;
11723fab7669SAndrew Thompson bp->bp_port_max_age = bp->bp_desg_max_age;
11733fab7669SAndrew Thompson bp->bp_port_fdelay = bp->bp_desg_fdelay;
11743fab7669SAndrew Thompson bp->bp_port_htime = bp->bp_desg_htime;
11753fab7669SAndrew Thompson bp->bp_infois = BSTP_INFO_MINE;
11763fab7669SAndrew Thompson
1177f935a26dSAndrew Thompson /* Set transmit flag but do not immediately send */
11783fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
11793fab7669SAndrew Thompson }
11803fab7669SAndrew Thompson
11813fab7669SAndrew Thompson /* set tcprop on every port other than the caller */
11823fab7669SAndrew Thompson static void
bstp_set_other_tcprop(struct bstp_port * bp)11833fab7669SAndrew Thompson bstp_set_other_tcprop(struct bstp_port *bp)
11843fab7669SAndrew Thompson {
11853fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
11863fab7669SAndrew Thompson struct bstp_port *bp2;
11873fab7669SAndrew Thompson
11883fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
11893fab7669SAndrew Thompson
11903fab7669SAndrew Thompson LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
11913fab7669SAndrew Thompson if (bp2 == bp)
11923fab7669SAndrew Thompson continue;
119398b81793SAndrew Thompson bp2->bp_tc_prop = 1;
11943fab7669SAndrew Thompson }
11953fab7669SAndrew Thompson }
11963fab7669SAndrew Thompson
11973fab7669SAndrew Thompson static void
bstp_set_all_reroot(struct bstp_state * bs)11983fab7669SAndrew Thompson bstp_set_all_reroot(struct bstp_state *bs)
119996e47153SAndrew Thompson {
120096e47153SAndrew Thompson struct bstp_port *bp;
120131997bf2SAndrew Thompson
12023fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
12033fab7669SAndrew Thompson
12043fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
12053fab7669SAndrew Thompson bp->bp_reroot = 1;
12063fab7669SAndrew Thompson }
12073fab7669SAndrew Thompson
12083fab7669SAndrew Thompson static void
bstp_set_all_sync(struct bstp_state * bs)12093fab7669SAndrew Thompson bstp_set_all_sync(struct bstp_state *bs)
12103fab7669SAndrew Thompson {
12113fab7669SAndrew Thompson struct bstp_port *bp;
12123fab7669SAndrew Thompson
12133fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
12143fab7669SAndrew Thompson
12153fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
12163fab7669SAndrew Thompson bp->bp_sync = 1;
12173fab7669SAndrew Thompson bp->bp_synced = 0; /* Not explicit in spec */
12183fab7669SAndrew Thompson }
12193fab7669SAndrew Thompson
12203fab7669SAndrew Thompson bs->bs_allsynced = 0;
12213fab7669SAndrew Thompson }
12223fab7669SAndrew Thompson
12233fab7669SAndrew Thompson static void
bstp_set_port_state(struct bstp_port * bp,int state)12243fab7669SAndrew Thompson bstp_set_port_state(struct bstp_port *bp, int state)
12253fab7669SAndrew Thompson {
12263fab7669SAndrew Thompson if (bp->bp_state == state)
12273fab7669SAndrew Thompson return;
12283fab7669SAndrew Thompson
12293fab7669SAndrew Thompson bp->bp_state = state;
12303fab7669SAndrew Thompson
12313fab7669SAndrew Thompson switch (bp->bp_state) {
12323fab7669SAndrew Thompson case BSTP_IFSTATE_DISCARDING:
12333fab7669SAndrew Thompson DPRINTF("state changed to DISCARDING on %s\n",
12343fab7669SAndrew Thompson bp->bp_ifp->if_xname);
12353fab7669SAndrew Thompson break;
12363fab7669SAndrew Thompson
12373fab7669SAndrew Thompson case BSTP_IFSTATE_LEARNING:
12383fab7669SAndrew Thompson DPRINTF("state changed to LEARNING on %s\n",
12393fab7669SAndrew Thompson bp->bp_ifp->if_xname);
12403fab7669SAndrew Thompson
12413fab7669SAndrew Thompson bstp_timer_start(&bp->bp_forward_delay_timer,
12423fab7669SAndrew Thompson bp->bp_protover == BSTP_PROTO_RSTP ?
12433fab7669SAndrew Thompson bp->bp_desg_htime : bp->bp_desg_fdelay);
12443fab7669SAndrew Thompson break;
12453fab7669SAndrew Thompson
12463fab7669SAndrew Thompson case BSTP_IFSTATE_FORWARDING:
12473fab7669SAndrew Thompson DPRINTF("state changed to FORWARDING on %s\n",
12483fab7669SAndrew Thompson bp->bp_ifp->if_xname);
12493fab7669SAndrew Thompson
12503fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_forward_delay_timer);
12513fab7669SAndrew Thompson /* Record that we enabled forwarding */
12523fab7669SAndrew Thompson bp->bp_forward_transitions++;
12533fab7669SAndrew Thompson break;
12543fab7669SAndrew Thompson }
12553fab7669SAndrew Thompson
12563fab7669SAndrew Thompson /* notify the parent bridge */
12573fab7669SAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_statetask);
12583fab7669SAndrew Thompson }
12593fab7669SAndrew Thompson
12603fab7669SAndrew Thompson static void
bstp_set_port_role(struct bstp_port * bp,int role)12613fab7669SAndrew Thompson bstp_set_port_role(struct bstp_port *bp, int role)
12623fab7669SAndrew Thompson {
12633fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
12643fab7669SAndrew Thompson
12653fab7669SAndrew Thompson if (bp->bp_role == role)
12663fab7669SAndrew Thompson return;
12673fab7669SAndrew Thompson
12683fab7669SAndrew Thompson /* perform pre-change tasks */
12693fab7669SAndrew Thompson switch (bp->bp_role) {
12703fab7669SAndrew Thompson case BSTP_ROLE_DISABLED:
12713fab7669SAndrew Thompson bstp_timer_start(&bp->bp_forward_delay_timer,
12723fab7669SAndrew Thompson bp->bp_desg_max_age);
12733fab7669SAndrew Thompson break;
12743fab7669SAndrew Thompson
12753fab7669SAndrew Thompson case BSTP_ROLE_BACKUP:
12763fab7669SAndrew Thompson bstp_timer_start(&bp->bp_recent_backup_timer,
12773fab7669SAndrew Thompson bp->bp_desg_htime * 2);
12783fab7669SAndrew Thompson /* fall through */
12793fab7669SAndrew Thompson case BSTP_ROLE_ALTERNATE:
12803fab7669SAndrew Thompson bstp_timer_start(&bp->bp_forward_delay_timer,
12813fab7669SAndrew Thompson bp->bp_desg_fdelay);
12823fab7669SAndrew Thompson bp->bp_sync = 0;
12833fab7669SAndrew Thompson bp->bp_synced = 1;
12843fab7669SAndrew Thompson bp->bp_reroot = 0;
12853fab7669SAndrew Thompson break;
12863fab7669SAndrew Thompson
12873fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
12883fab7669SAndrew Thompson bstp_timer_start(&bp->bp_recent_root_timer,
12893fab7669SAndrew Thompson BSTP_DEFAULT_FORWARD_DELAY);
12903fab7669SAndrew Thompson break;
12913fab7669SAndrew Thompson }
12923fab7669SAndrew Thompson
12933fab7669SAndrew Thompson bp->bp_role = role;
12943fab7669SAndrew Thompson /* clear values not carried between roles */
12953fab7669SAndrew Thompson bp->bp_proposing = 0;
12963fab7669SAndrew Thompson bs->bs_allsynced = 0;
12973fab7669SAndrew Thompson
12983fab7669SAndrew Thompson /* initialise the new role */
12993fab7669SAndrew Thompson switch (bp->bp_role) {
13003fab7669SAndrew Thompson case BSTP_ROLE_DISABLED:
13013fab7669SAndrew Thompson case BSTP_ROLE_ALTERNATE:
13023fab7669SAndrew Thompson case BSTP_ROLE_BACKUP:
13033fab7669SAndrew Thompson DPRINTF("%s role -> ALT/BACK/DISABLED\n",
13043fab7669SAndrew Thompson bp->bp_ifp->if_xname);
13053fab7669SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
13063fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_recent_root_timer);
13073fab7669SAndrew Thompson bstp_timer_latch(&bp->bp_forward_delay_timer);
13083fab7669SAndrew Thompson bp->bp_sync = 0;
13093fab7669SAndrew Thompson bp->bp_synced = 1;
13103fab7669SAndrew Thompson bp->bp_reroot = 0;
13113fab7669SAndrew Thompson break;
13123fab7669SAndrew Thompson
13133fab7669SAndrew Thompson case BSTP_ROLE_ROOT:
13143fab7669SAndrew Thompson DPRINTF("%s role -> ROOT\n",
13153fab7669SAndrew Thompson bp->bp_ifp->if_xname);
13163fab7669SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
13173fab7669SAndrew Thompson bstp_timer_latch(&bp->bp_recent_root_timer);
13183fab7669SAndrew Thompson bp->bp_proposing = 0;
13193fab7669SAndrew Thompson break;
13203fab7669SAndrew Thompson
13213fab7669SAndrew Thompson case BSTP_ROLE_DESIGNATED:
13223fab7669SAndrew Thompson DPRINTF("%s role -> DESIGNATED\n",
13233fab7669SAndrew Thompson bp->bp_ifp->if_xname);
13243fab7669SAndrew Thompson bstp_timer_start(&bp->bp_hello_timer,
13253fab7669SAndrew Thompson bp->bp_desg_htime);
13263fab7669SAndrew Thompson bp->bp_agree = 0;
13273fab7669SAndrew Thompson break;
13283fab7669SAndrew Thompson }
13293fab7669SAndrew Thompson
13303fab7669SAndrew Thompson /* let the TC state know that the role changed */
13313fab7669SAndrew Thompson bstp_update_tc(bp);
13323fab7669SAndrew Thompson }
13333fab7669SAndrew Thompson
13343fab7669SAndrew Thompson static void
bstp_set_port_proto(struct bstp_port * bp,int proto)13353fab7669SAndrew Thompson bstp_set_port_proto(struct bstp_port *bp, int proto)
13363fab7669SAndrew Thompson {
13373fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
13383fab7669SAndrew Thompson
13393fab7669SAndrew Thompson /* supported protocol versions */
13403fab7669SAndrew Thompson switch (proto) {
13413fab7669SAndrew Thompson case BSTP_PROTO_STP:
13423fab7669SAndrew Thompson /* we can downgrade protocols only */
13433fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_migrate_delay_timer);
13443fab7669SAndrew Thompson /* clear unsupported features */
13453fab7669SAndrew Thompson bp->bp_operedge = 0;
1346dd040130SAndrew Thompson /* STP compat mode only uses 16 bits of the 32 */
1347dd040130SAndrew Thompson if (bp->bp_path_cost > 65535)
1348dd040130SAndrew Thompson bp->bp_path_cost = 65535;
13493fab7669SAndrew Thompson break;
13503fab7669SAndrew Thompson
13513fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
13523fab7669SAndrew Thompson bstp_timer_start(&bp->bp_migrate_delay_timer,
13533fab7669SAndrew Thompson bs->bs_migration_delay);
13543fab7669SAndrew Thompson break;
13553fab7669SAndrew Thompson
13563fab7669SAndrew Thompson default:
13573fab7669SAndrew Thompson DPRINTF("Unsupported STP version %d\n", proto);
13583fab7669SAndrew Thompson return;
13593fab7669SAndrew Thompson }
13603fab7669SAndrew Thompson
13613fab7669SAndrew Thompson bp->bp_protover = proto;
13623fab7669SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_CANMIGRATE;
13633fab7669SAndrew Thompson }
13643fab7669SAndrew Thompson
13653fab7669SAndrew Thompson static void
bstp_set_port_tc(struct bstp_port * bp,int state)13663fab7669SAndrew Thompson bstp_set_port_tc(struct bstp_port *bp, int state)
13673fab7669SAndrew Thompson {
13683fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
13693fab7669SAndrew Thompson
13703fab7669SAndrew Thompson bp->bp_tcstate = state;
13713fab7669SAndrew Thompson
13723fab7669SAndrew Thompson /* initialise the new state */
13733fab7669SAndrew Thompson switch (bp->bp_tcstate) {
13743fab7669SAndrew Thompson case BSTP_TCSTATE_ACTIVE:
13753fab7669SAndrew Thompson DPRINTF("%s -> TC_ACTIVE\n", bp->bp_ifp->if_xname);
13763fab7669SAndrew Thompson /* nothing to do */
13773fab7669SAndrew Thompson break;
13783fab7669SAndrew Thompson
13793fab7669SAndrew Thompson case BSTP_TCSTATE_INACTIVE:
13803fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_tc_timer);
13813fab7669SAndrew Thompson /* flush routes on the parent bridge */
13823fab7669SAndrew Thompson bp->bp_fdbflush = 1;
13833fab7669SAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_rtagetask);
13843fab7669SAndrew Thompson bp->bp_tc_ack = 0;
13853fab7669SAndrew Thompson DPRINTF("%s -> TC_INACTIVE\n", bp->bp_ifp->if_xname);
13863fab7669SAndrew Thompson break;
13873fab7669SAndrew Thompson
13883fab7669SAndrew Thompson case BSTP_TCSTATE_LEARNING:
13893fab7669SAndrew Thompson bp->bp_rcvdtc = 0;
13903fab7669SAndrew Thompson bp->bp_rcvdtcn = 0;
13913fab7669SAndrew Thompson bp->bp_rcvdtca = 0;
13923fab7669SAndrew Thompson bp->bp_tc_prop = 0;
13933fab7669SAndrew Thompson DPRINTF("%s -> TC_LEARNING\n", bp->bp_ifp->if_xname);
13943fab7669SAndrew Thompson break;
13953fab7669SAndrew Thompson
13963fab7669SAndrew Thompson case BSTP_TCSTATE_DETECTED:
13973fab7669SAndrew Thompson bstp_set_timer_tc(bp);
13983fab7669SAndrew Thompson bstp_set_other_tcprop(bp);
13993fab7669SAndrew Thompson /* send out notification */
14003fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
14013fab7669SAndrew Thompson bstp_transmit(bs, bp);
14023fab7669SAndrew Thompson getmicrotime(&bs->bs_last_tc_time);
14033fab7669SAndrew Thompson DPRINTF("%s -> TC_DETECTED\n", bp->bp_ifp->if_xname);
14043fab7669SAndrew Thompson bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
14053fab7669SAndrew Thompson break;
14063fab7669SAndrew Thompson
14073fab7669SAndrew Thompson case BSTP_TCSTATE_TCN:
14083fab7669SAndrew Thompson bstp_set_timer_tc(bp);
14093fab7669SAndrew Thompson DPRINTF("%s -> TC_TCN\n", bp->bp_ifp->if_xname);
14103fab7669SAndrew Thompson /* fall through */
14113fab7669SAndrew Thompson case BSTP_TCSTATE_TC:
14123fab7669SAndrew Thompson bp->bp_rcvdtc = 0;
14133fab7669SAndrew Thompson bp->bp_rcvdtcn = 0;
14143fab7669SAndrew Thompson if (bp->bp_role == BSTP_ROLE_DESIGNATED)
14153fab7669SAndrew Thompson bp->bp_tc_ack = 1;
14163fab7669SAndrew Thompson
14173fab7669SAndrew Thompson bstp_set_other_tcprop(bp);
14183fab7669SAndrew Thompson DPRINTF("%s -> TC_TC\n", bp->bp_ifp->if_xname);
14193fab7669SAndrew Thompson bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
14203fab7669SAndrew Thompson break;
14213fab7669SAndrew Thompson
14223fab7669SAndrew Thompson case BSTP_TCSTATE_PROPAG:
14233fab7669SAndrew Thompson /* flush routes on the parent bridge */
14243fab7669SAndrew Thompson bp->bp_fdbflush = 1;
14253fab7669SAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_rtagetask);
14263fab7669SAndrew Thompson bp->bp_tc_prop = 0;
14273fab7669SAndrew Thompson bstp_set_timer_tc(bp);
14283fab7669SAndrew Thompson DPRINTF("%s -> TC_PROPAG\n", bp->bp_ifp->if_xname);
14293fab7669SAndrew Thompson bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
14303fab7669SAndrew Thompson break;
14313fab7669SAndrew Thompson
14323fab7669SAndrew Thompson case BSTP_TCSTATE_ACK:
14333fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_tc_timer);
14343fab7669SAndrew Thompson bp->bp_rcvdtca = 0;
14353fab7669SAndrew Thompson DPRINTF("%s -> TC_ACK\n", bp->bp_ifp->if_xname);
14363fab7669SAndrew Thompson bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
14373fab7669SAndrew Thompson break;
14383fab7669SAndrew Thompson }
14393fab7669SAndrew Thompson }
14403fab7669SAndrew Thompson
14413fab7669SAndrew Thompson static void
bstp_set_timer_tc(struct bstp_port * bp)14423fab7669SAndrew Thompson bstp_set_timer_tc(struct bstp_port *bp)
14433fab7669SAndrew Thompson {
14443fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
14453fab7669SAndrew Thompson
14463fab7669SAndrew Thompson if (bp->bp_tc_timer.active)
14473fab7669SAndrew Thompson return;
14483fab7669SAndrew Thompson
14493fab7669SAndrew Thompson switch (bp->bp_protover) {
14503fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
14513fab7669SAndrew Thompson bstp_timer_start(&bp->bp_tc_timer,
14523fab7669SAndrew Thompson bp->bp_desg_htime + BSTP_TICK_VAL);
14533fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
14543fab7669SAndrew Thompson break;
14553fab7669SAndrew Thompson
14563fab7669SAndrew Thompson case BSTP_PROTO_STP:
14573fab7669SAndrew Thompson bstp_timer_start(&bp->bp_tc_timer,
14583fab7669SAndrew Thompson bs->bs_root_max_age + bs->bs_root_fdelay);
14593fab7669SAndrew Thompson break;
14603fab7669SAndrew Thompson }
14613fab7669SAndrew Thompson }
14623fab7669SAndrew Thompson
14633fab7669SAndrew Thompson static void
bstp_set_timer_msgage(struct bstp_port * bp)14643fab7669SAndrew Thompson bstp_set_timer_msgage(struct bstp_port *bp)
14653fab7669SAndrew Thompson {
14663fab7669SAndrew Thompson if (bp->bp_port_msg_age + BSTP_MESSAGE_AGE_INCR <=
14673fab7669SAndrew Thompson bp->bp_port_max_age) {
14683fab7669SAndrew Thompson bstp_timer_start(&bp->bp_message_age_timer,
14693fab7669SAndrew Thompson bp->bp_port_htime * 3);
14703fab7669SAndrew Thompson } else
14713fab7669SAndrew Thompson /* expires immediately */
14723fab7669SAndrew Thompson bstp_timer_start(&bp->bp_message_age_timer, 0);
14733fab7669SAndrew Thompson }
14743fab7669SAndrew Thompson
14753fab7669SAndrew Thompson static int
bstp_rerooted(struct bstp_state * bs,struct bstp_port * bp)14763fab7669SAndrew Thompson bstp_rerooted(struct bstp_state *bs, struct bstp_port *bp)
14773fab7669SAndrew Thompson {
14783fab7669SAndrew Thompson struct bstp_port *bp2;
14793fab7669SAndrew Thompson int rr_set = 0;
14803fab7669SAndrew Thompson
14813fab7669SAndrew Thompson LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
14823fab7669SAndrew Thompson if (bp2 == bp)
14833fab7669SAndrew Thompson continue;
14843fab7669SAndrew Thompson if (bp2->bp_recent_root_timer.active) {
14853fab7669SAndrew Thompson rr_set = 1;
14863fab7669SAndrew Thompson break;
14873fab7669SAndrew Thompson }
14883fab7669SAndrew Thompson }
14893fab7669SAndrew Thompson return (!rr_set);
14903fab7669SAndrew Thompson }
14913fab7669SAndrew Thompson
14923fab7669SAndrew Thompson int
bstp_set_htime(struct bstp_state * bs,int t)14933fab7669SAndrew Thompson bstp_set_htime(struct bstp_state *bs, int t)
14943fab7669SAndrew Thompson {
14953fab7669SAndrew Thompson /* convert seconds to ticks */
14963fab7669SAndrew Thompson t *= BSTP_TICK_VAL;
14973fab7669SAndrew Thompson
14983fab7669SAndrew Thompson /* value can only be changed in leagacy stp mode */
14993fab7669SAndrew Thompson if (bs->bs_protover != BSTP_PROTO_STP)
15003fab7669SAndrew Thompson return (EPERM);
15013fab7669SAndrew Thompson
15023fab7669SAndrew Thompson if (t < BSTP_MIN_HELLO_TIME || t > BSTP_MAX_HELLO_TIME)
15033fab7669SAndrew Thompson return (EINVAL);
15043fab7669SAndrew Thompson
15053fab7669SAndrew Thompson BSTP_LOCK(bs);
15063fab7669SAndrew Thompson bs->bs_bridge_htime = t;
15073fab7669SAndrew Thompson bstp_reinit(bs);
15083fab7669SAndrew Thompson BSTP_UNLOCK(bs);
15093fab7669SAndrew Thompson return (0);
15103fab7669SAndrew Thompson }
15113fab7669SAndrew Thompson
15123fab7669SAndrew Thompson int
bstp_set_fdelay(struct bstp_state * bs,int t)15133fab7669SAndrew Thompson bstp_set_fdelay(struct bstp_state *bs, int t)
15143fab7669SAndrew Thompson {
15153fab7669SAndrew Thompson /* convert seconds to ticks */
15163fab7669SAndrew Thompson t *= BSTP_TICK_VAL;
15173fab7669SAndrew Thompson
15183fab7669SAndrew Thompson if (t < BSTP_MIN_FORWARD_DELAY || t > BSTP_MAX_FORWARD_DELAY)
15193fab7669SAndrew Thompson return (EINVAL);
15203fab7669SAndrew Thompson
15213fab7669SAndrew Thompson BSTP_LOCK(bs);
15223fab7669SAndrew Thompson bs->bs_bridge_fdelay = t;
15233fab7669SAndrew Thompson bstp_reinit(bs);
15243fab7669SAndrew Thompson BSTP_UNLOCK(bs);
15253fab7669SAndrew Thompson return (0);
15263fab7669SAndrew Thompson }
15273fab7669SAndrew Thompson
15283fab7669SAndrew Thompson int
bstp_set_maxage(struct bstp_state * bs,int t)15293fab7669SAndrew Thompson bstp_set_maxage(struct bstp_state *bs, int t)
15303fab7669SAndrew Thompson {
15313fab7669SAndrew Thompson /* convert seconds to ticks */
15323fab7669SAndrew Thompson t *= BSTP_TICK_VAL;
15333fab7669SAndrew Thompson
15343fab7669SAndrew Thompson if (t < BSTP_MIN_MAX_AGE || t > BSTP_MAX_MAX_AGE)
15353fab7669SAndrew Thompson return (EINVAL);
15363fab7669SAndrew Thompson
15373fab7669SAndrew Thompson BSTP_LOCK(bs);
15383fab7669SAndrew Thompson bs->bs_bridge_max_age = t;
15393fab7669SAndrew Thompson bstp_reinit(bs);
15403fab7669SAndrew Thompson BSTP_UNLOCK(bs);
15413fab7669SAndrew Thompson return (0);
15423fab7669SAndrew Thompson }
15433fab7669SAndrew Thompson
15443fab7669SAndrew Thompson int
bstp_set_holdcount(struct bstp_state * bs,int count)15453fab7669SAndrew Thompson bstp_set_holdcount(struct bstp_state *bs, int count)
15463fab7669SAndrew Thompson {
15473fab7669SAndrew Thompson struct bstp_port *bp;
15483fab7669SAndrew Thompson
15493fab7669SAndrew Thompson if (count < BSTP_MIN_HOLD_COUNT ||
15503fab7669SAndrew Thompson count > BSTP_MAX_HOLD_COUNT)
15513fab7669SAndrew Thompson return (EINVAL);
15523fab7669SAndrew Thompson
15533fab7669SAndrew Thompson BSTP_LOCK(bs);
15543fab7669SAndrew Thompson bs->bs_txholdcount = count;
15553fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
15563fab7669SAndrew Thompson bp->bp_txcount = 0;
15573fab7669SAndrew Thompson BSTP_UNLOCK(bs);
15583fab7669SAndrew Thompson return (0);
15593fab7669SAndrew Thompson }
15603fab7669SAndrew Thompson
15613fab7669SAndrew Thompson int
bstp_set_protocol(struct bstp_state * bs,int proto)15623fab7669SAndrew Thompson bstp_set_protocol(struct bstp_state *bs, int proto)
15633fab7669SAndrew Thompson {
15643fab7669SAndrew Thompson struct bstp_port *bp;
15653fab7669SAndrew Thompson
15663fab7669SAndrew Thompson switch (proto) {
15673fab7669SAndrew Thompson /* Supported protocol versions */
15683fab7669SAndrew Thompson case BSTP_PROTO_STP:
15693fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
15703fab7669SAndrew Thompson break;
15713fab7669SAndrew Thompson
15723fab7669SAndrew Thompson default:
15733fab7669SAndrew Thompson return (EINVAL);
15743fab7669SAndrew Thompson }
15753fab7669SAndrew Thompson
15763fab7669SAndrew Thompson BSTP_LOCK(bs);
15773fab7669SAndrew Thompson bs->bs_protover = proto;
15783fab7669SAndrew Thompson bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
15793fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
15803fab7669SAndrew Thompson /* reinit state */
15813fab7669SAndrew Thompson bp->bp_infois = BSTP_INFO_DISABLED;
15823fab7669SAndrew Thompson bp->bp_txcount = 0;
15833fab7669SAndrew Thompson bstp_set_port_proto(bp, bs->bs_protover);
15843fab7669SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
15853fab7669SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
15863fab7669SAndrew Thompson bstp_timer_stop(&bp->bp_recent_backup_timer);
15873fab7669SAndrew Thompson }
15883fab7669SAndrew Thompson bstp_reinit(bs);
15893fab7669SAndrew Thompson BSTP_UNLOCK(bs);
15903fab7669SAndrew Thompson return (0);
15913fab7669SAndrew Thompson }
15923fab7669SAndrew Thompson
15933fab7669SAndrew Thompson int
bstp_set_priority(struct bstp_state * bs,int pri)15943fab7669SAndrew Thompson bstp_set_priority(struct bstp_state *bs, int pri)
15953fab7669SAndrew Thompson {
15963fab7669SAndrew Thompson if (pri < 0 || pri > BSTP_MAX_PRIORITY)
15973fab7669SAndrew Thompson return (EINVAL);
15983fab7669SAndrew Thompson
15993fab7669SAndrew Thompson /* Limit to steps of 4096 */
16003fab7669SAndrew Thompson pri -= pri % 4096;
16013fab7669SAndrew Thompson
16023fab7669SAndrew Thompson BSTP_LOCK(bs);
16033fab7669SAndrew Thompson bs->bs_bridge_priority = pri;
16043fab7669SAndrew Thompson bstp_reinit(bs);
16053fab7669SAndrew Thompson BSTP_UNLOCK(bs);
16063fab7669SAndrew Thompson return (0);
16073fab7669SAndrew Thompson }
16083fab7669SAndrew Thompson
16093fab7669SAndrew Thompson int
bstp_set_port_priority(struct bstp_port * bp,int pri)16103fab7669SAndrew Thompson bstp_set_port_priority(struct bstp_port *bp, int pri)
16113fab7669SAndrew Thompson {
16123fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16133fab7669SAndrew Thompson
16143fab7669SAndrew Thompson if (pri < 0 || pri > BSTP_MAX_PORT_PRIORITY)
16153fab7669SAndrew Thompson return (EINVAL);
16163fab7669SAndrew Thompson
16173fab7669SAndrew Thompson /* Limit to steps of 16 */
16183fab7669SAndrew Thompson pri -= pri % 16;
16193fab7669SAndrew Thompson
16203fab7669SAndrew Thompson BSTP_LOCK(bs);
16213fab7669SAndrew Thompson bp->bp_priority = pri;
16223fab7669SAndrew Thompson bstp_reinit(bs);
16233fab7669SAndrew Thompson BSTP_UNLOCK(bs);
16243fab7669SAndrew Thompson return (0);
16253fab7669SAndrew Thompson }
16263fab7669SAndrew Thompson
16273fab7669SAndrew Thompson int
bstp_set_path_cost(struct bstp_port * bp,uint32_t path_cost)16283fab7669SAndrew Thompson bstp_set_path_cost(struct bstp_port *bp, uint32_t path_cost)
16293fab7669SAndrew Thompson {
16303fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16313fab7669SAndrew Thompson
16323fab7669SAndrew Thompson if (path_cost > BSTP_MAX_PATH_COST)
16333fab7669SAndrew Thompson return (EINVAL);
16343fab7669SAndrew Thompson
1635dd040130SAndrew Thompson /* STP compat mode only uses 16 bits of the 32 */
1636dd040130SAndrew Thompson if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535)
1637dd040130SAndrew Thompson path_cost = 65535;
1638dd040130SAndrew Thompson
163996e47153SAndrew Thompson BSTP_LOCK(bs);
164096e47153SAndrew Thompson
16413fab7669SAndrew Thompson if (path_cost == 0) { /* use auto */
16423fab7669SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_ADMCOST;
16433fab7669SAndrew Thompson bp->bp_path_cost = bstp_calc_path_cost(bp);
16443fab7669SAndrew Thompson } else {
164596e47153SAndrew Thompson bp->bp_path_cost = path_cost;
16463fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_ADMCOST;
16473fab7669SAndrew Thompson }
16483fab7669SAndrew Thompson bstp_reinit(bs);
16493fab7669SAndrew Thompson BSTP_UNLOCK(bs);
16503fab7669SAndrew Thompson return (0);
165131997bf2SAndrew Thompson }
165231997bf2SAndrew Thompson
16533fab7669SAndrew Thompson int
bstp_set_edge(struct bstp_port * bp,int set)16543fab7669SAndrew Thompson bstp_set_edge(struct bstp_port *bp, int set)
165531997bf2SAndrew Thompson {
16563fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16573fab7669SAndrew Thompson
16583fab7669SAndrew Thompson BSTP_LOCK(bs);
1659daacddcaSShteryana Shopova if ((bp->bp_operedge = set) == 0)
1660daacddcaSShteryana Shopova bp->bp_flags &= ~BSTP_PORT_ADMEDGE;
1661daacddcaSShteryana Shopova else
1662daacddcaSShteryana Shopova bp->bp_flags |= BSTP_PORT_ADMEDGE;
16633fab7669SAndrew Thompson BSTP_UNLOCK(bs);
16643fab7669SAndrew Thompson return (0);
166531997bf2SAndrew Thompson }
166631997bf2SAndrew Thompson
16673fab7669SAndrew Thompson int
bstp_set_autoedge(struct bstp_port * bp,int set)16683fab7669SAndrew Thompson bstp_set_autoedge(struct bstp_port *bp, int set)
166931997bf2SAndrew Thompson {
16703fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16713fab7669SAndrew Thompson
16723fab7669SAndrew Thompson BSTP_LOCK(bs);
16733fab7669SAndrew Thompson if (set) {
16743fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_AUTOEDGE;
16753fab7669SAndrew Thompson /* we may be able to transition straight to edge */
16763fab7669SAndrew Thompson if (bp->bp_edge_delay_timer.active == 0)
16773fab7669SAndrew Thompson bstp_edge_delay_expiry(bs, bp);
16783fab7669SAndrew Thompson } else
16793fab7669SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_AUTOEDGE;
16803fab7669SAndrew Thompson BSTP_UNLOCK(bs);
16813fab7669SAndrew Thompson return (0);
168231997bf2SAndrew Thompson }
16836c32e05cSAndrew Thompson
16846c32e05cSAndrew Thompson int
bstp_set_ptp(struct bstp_port * bp,int set)168578709605SAndrew Thompson bstp_set_ptp(struct bstp_port *bp, int set)
16866c32e05cSAndrew Thompson {
16876c32e05cSAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16886c32e05cSAndrew Thompson
16896c32e05cSAndrew Thompson BSTP_LOCK(bs);
169078709605SAndrew Thompson bp->bp_ptp_link = set;
16916c32e05cSAndrew Thompson BSTP_UNLOCK(bs);
16926c32e05cSAndrew Thompson return (0);
16936c32e05cSAndrew Thompson }
16946c32e05cSAndrew Thompson
16956c32e05cSAndrew Thompson int
bstp_set_autoptp(struct bstp_port * bp,int set)169678709605SAndrew Thompson bstp_set_autoptp(struct bstp_port *bp, int set)
16976c32e05cSAndrew Thompson {
16986c32e05cSAndrew Thompson struct bstp_state *bs = bp->bp_bs;
16996c32e05cSAndrew Thompson
17006c32e05cSAndrew Thompson BSTP_LOCK(bs);
17016c32e05cSAndrew Thompson if (set) {
170278709605SAndrew Thompson bp->bp_flags |= BSTP_PORT_AUTOPTP;
170311eeea5eSAndrew Thompson if (bp->bp_role != BSTP_ROLE_DISABLED)
17042885c19eSAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask);
17056c32e05cSAndrew Thompson } else
170678709605SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_AUTOPTP;
17076c32e05cSAndrew Thompson BSTP_UNLOCK(bs);
17086c32e05cSAndrew Thompson return (0);
17096c32e05cSAndrew Thompson }
17106c32e05cSAndrew Thompson
17113fab7669SAndrew Thompson /*
17123fab7669SAndrew Thompson * Calculate the path cost according to the link speed.
17133fab7669SAndrew Thompson */
17143fab7669SAndrew Thompson static uint32_t
bstp_calc_path_cost(struct bstp_port * bp)17153fab7669SAndrew Thompson bstp_calc_path_cost(struct bstp_port *bp)
171696e47153SAndrew Thompson {
17173fab7669SAndrew Thompson struct ifnet *ifp = bp->bp_ifp;
17183fab7669SAndrew Thompson uint32_t path_cost;
171996e47153SAndrew Thompson
17203fab7669SAndrew Thompson /* If the priority has been manually set then retain the value */
17213fab7669SAndrew Thompson if (bp->bp_flags & BSTP_PORT_ADMCOST)
17223fab7669SAndrew Thompson return bp->bp_path_cost;
172396e47153SAndrew Thompson
1724dd040130SAndrew Thompson if (ifp->if_link_state == LINK_STATE_DOWN) {
1725dd040130SAndrew Thompson /* Recalc when the link comes up again */
1726dd040130SAndrew Thompson bp->bp_flags |= BSTP_PORT_PNDCOST;
1727dd040130SAndrew Thompson return (BSTP_DEFAULT_PATH_COST);
1728dd040130SAndrew Thompson }
1729dd040130SAndrew Thompson
17303fab7669SAndrew Thompson if (ifp->if_baudrate < 1000)
17313fab7669SAndrew Thompson return (BSTP_DEFAULT_PATH_COST);
17323fab7669SAndrew Thompson
17333fab7669SAndrew Thompson /* formula from section 17.14, IEEE Std 802.1D-2004 */
17346c32e05cSAndrew Thompson path_cost = 20000000000ULL / (ifp->if_baudrate / 1000);
17353fab7669SAndrew Thompson
17363fab7669SAndrew Thompson if (path_cost > BSTP_MAX_PATH_COST)
17373fab7669SAndrew Thompson path_cost = BSTP_MAX_PATH_COST;
17383fab7669SAndrew Thompson
17393fab7669SAndrew Thompson /* STP compat mode only uses 16 bits of the 32 */
17403fab7669SAndrew Thompson if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535)
17413fab7669SAndrew Thompson path_cost = 65535;
17423fab7669SAndrew Thompson
17433fab7669SAndrew Thompson return (path_cost);
17443fab7669SAndrew Thompson }
17453fab7669SAndrew Thompson
17463fab7669SAndrew Thompson /*
17473fab7669SAndrew Thompson * Notify the bridge that a port state has changed, we need to do this from a
17483fab7669SAndrew Thompson * taskqueue to avoid a LOR.
17493fab7669SAndrew Thompson */
17503fab7669SAndrew Thompson static void
bstp_notify_state(void * arg,int pending)17513fab7669SAndrew Thompson bstp_notify_state(void *arg, int pending)
17523fab7669SAndrew Thompson {
17533fab7669SAndrew Thompson struct bstp_port *bp = (struct bstp_port *)arg;
17543fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
17553fab7669SAndrew Thompson
17563fab7669SAndrew Thompson if (bp->bp_active == 1 && bs->bs_state_cb != NULL)
17573fab7669SAndrew Thompson (*bs->bs_state_cb)(bp->bp_ifp, bp->bp_state);
17583fab7669SAndrew Thompson }
17593fab7669SAndrew Thompson
17603fab7669SAndrew Thompson /*
17613fab7669SAndrew Thompson * Flush the routes on the bridge port, we need to do this from a
17623fab7669SAndrew Thompson * taskqueue to avoid a LOR.
17633fab7669SAndrew Thompson */
17643fab7669SAndrew Thompson static void
bstp_notify_rtage(void * arg,int pending)17653fab7669SAndrew Thompson bstp_notify_rtage(void *arg, int pending)
17663fab7669SAndrew Thompson {
17673fab7669SAndrew Thompson struct bstp_port *bp = (struct bstp_port *)arg;
17683fab7669SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
17693fab7669SAndrew Thompson int age = 0;
17703fab7669SAndrew Thompson
17713fab7669SAndrew Thompson BSTP_LOCK(bs);
17723fab7669SAndrew Thompson switch (bp->bp_protover) {
17733fab7669SAndrew Thompson case BSTP_PROTO_STP:
17743fab7669SAndrew Thompson /* convert to seconds */
17753fab7669SAndrew Thompson age = bp->bp_desg_fdelay / BSTP_TICK_VAL;
17763fab7669SAndrew Thompson break;
17773fab7669SAndrew Thompson
17783fab7669SAndrew Thompson case BSTP_PROTO_RSTP:
17793fab7669SAndrew Thompson age = 0;
17803fab7669SAndrew Thompson break;
17813fab7669SAndrew Thompson }
17823fab7669SAndrew Thompson BSTP_UNLOCK(bs);
17833fab7669SAndrew Thompson
17843fab7669SAndrew Thompson if (bp->bp_active == 1 && bs->bs_rtage_cb != NULL)
17853fab7669SAndrew Thompson (*bs->bs_rtage_cb)(bp->bp_ifp, age);
17863fab7669SAndrew Thompson
17873fab7669SAndrew Thompson /* flush is complete */
17883fab7669SAndrew Thompson BSTP_LOCK(bs);
17893fab7669SAndrew Thompson bp->bp_fdbflush = 0;
17903fab7669SAndrew Thompson BSTP_UNLOCK(bs);
179196e47153SAndrew Thompson }
179296e47153SAndrew Thompson
179331997bf2SAndrew Thompson void
bstp_linkstate(struct bstp_port * bp)17947702d401SAndrew Thompson bstp_linkstate(struct bstp_port *bp)
179531997bf2SAndrew Thompson {
17967702d401SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
179731997bf2SAndrew Thompson
17982885c19eSAndrew Thompson if (!bp->bp_active)
17992885c19eSAndrew Thompson return;
18002885c19eSAndrew Thompson
18012885c19eSAndrew Thompson bstp_ifupdstatus(bp, 0);
180296e47153SAndrew Thompson BSTP_LOCK(bs);
1803c25789ccSAndrew Thompson bstp_update_state(bs, bp);
180496e47153SAndrew Thompson BSTP_UNLOCK(bs);
180596e47153SAndrew Thompson }
180631997bf2SAndrew Thompson
18076b32f3d3SAndrew Thompson static void
bstp_ifupdstatus(void * arg,int pending)18082885c19eSAndrew Thompson bstp_ifupdstatus(void *arg, int pending)
180931997bf2SAndrew Thompson {
18102885c19eSAndrew Thompson struct bstp_port *bp = (struct bstp_port *)arg;
18112885c19eSAndrew Thompson struct bstp_state *bs = bp->bp_bs;
181296e47153SAndrew Thompson struct ifnet *ifp = bp->bp_ifp;
181331997bf2SAndrew Thompson struct ifmediareq ifmr;
18142885c19eSAndrew Thompson int error, changed;
181531997bf2SAndrew Thompson
18162885c19eSAndrew Thompson if (!bp->bp_active)
18172885c19eSAndrew Thompson return;
181831997bf2SAndrew Thompson
181931997bf2SAndrew Thompson bzero((char *)&ifmr, sizeof(ifmr));
182031997bf2SAndrew Thompson error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
182131997bf2SAndrew Thompson
18222885c19eSAndrew Thompson BSTP_LOCK(bs);
18232885c19eSAndrew Thompson changed = 0;
182431997bf2SAndrew Thompson if ((error == 0) && (ifp->if_flags & IFF_UP)) {
182531997bf2SAndrew Thompson if (ifmr.ifm_status & IFM_ACTIVE) {
18263fab7669SAndrew Thompson /* A full-duplex link is assumed to be point to point */
182778709605SAndrew Thompson if (bp->bp_flags & BSTP_PORT_AUTOPTP) {
18282885c19eSAndrew Thompson int fdx;
18292885c19eSAndrew Thompson
18302885c19eSAndrew Thompson fdx = ifmr.ifm_active & IFM_FDX ? 1 : 0;
18312885c19eSAndrew Thompson if (bp->bp_ptp_link ^ fdx) {
18322885c19eSAndrew Thompson bp->bp_ptp_link = fdx;
18332885c19eSAndrew Thompson changed = 1;
18342885c19eSAndrew Thompson }
18356c32e05cSAndrew Thompson }
183631997bf2SAndrew Thompson
1837dd040130SAndrew Thompson /* Calc the cost if the link was down previously */
1838dd040130SAndrew Thompson if (bp->bp_flags & BSTP_PORT_PNDCOST) {
18392885c19eSAndrew Thompson uint32_t cost;
18402885c19eSAndrew Thompson
18412885c19eSAndrew Thompson cost = bstp_calc_path_cost(bp);
18422885c19eSAndrew Thompson if (bp->bp_path_cost != cost) {
18432885c19eSAndrew Thompson bp->bp_path_cost = cost;
18442885c19eSAndrew Thompson changed = 1;
18452885c19eSAndrew Thompson }
1846dd040130SAndrew Thompson bp->bp_flags &= ~BSTP_PORT_PNDCOST;
1847dd040130SAndrew Thompson }
1848dd040130SAndrew Thompson
18492885c19eSAndrew Thompson if (bp->bp_role == BSTP_ROLE_DISABLED) {
18503fab7669SAndrew Thompson bstp_enable_port(bs, bp);
18512885c19eSAndrew Thompson changed = 1;
18522885c19eSAndrew Thompson }
185331997bf2SAndrew Thompson } else {
1854daacddcaSShteryana Shopova if (bp->bp_role != BSTP_ROLE_DISABLED) {
185596e47153SAndrew Thompson bstp_disable_port(bs, bp);
18562885c19eSAndrew Thompson changed = 1;
1857daacddcaSShteryana Shopova if ((bp->bp_flags & BSTP_PORT_ADMEDGE) &&
1858daacddcaSShteryana Shopova bp->bp_protover == BSTP_PROTO_RSTP)
1859daacddcaSShteryana Shopova bp->bp_operedge = 1;
1860daacddcaSShteryana Shopova }
186131997bf2SAndrew Thompson }
18622885c19eSAndrew Thompson } else if (bp->bp_infois != BSTP_INFO_DISABLED) {
186396e47153SAndrew Thompson bstp_disable_port(bs, bp);
18642885c19eSAndrew Thompson changed = 1;
18652885c19eSAndrew Thompson }
18662885c19eSAndrew Thompson if (changed)
18672885c19eSAndrew Thompson bstp_assign_roles(bs);
18682885c19eSAndrew Thompson BSTP_UNLOCK(bs);
186931997bf2SAndrew Thompson }
187031997bf2SAndrew Thompson
18716b32f3d3SAndrew Thompson static void
bstp_enable_port(struct bstp_state * bs,struct bstp_port * bp)18723fab7669SAndrew Thompson bstp_enable_port(struct bstp_state *bs, struct bstp_port *bp)
18733fab7669SAndrew Thompson {
18743fab7669SAndrew Thompson bp->bp_infois = BSTP_INFO_AGED;
18753fab7669SAndrew Thompson }
18763fab7669SAndrew Thompson
18773fab7669SAndrew Thompson static void
bstp_disable_port(struct bstp_state * bs,struct bstp_port * bp)18783fab7669SAndrew Thompson bstp_disable_port(struct bstp_state *bs, struct bstp_port *bp)
18793fab7669SAndrew Thompson {
18803fab7669SAndrew Thompson bp->bp_infois = BSTP_INFO_DISABLED;
18813fab7669SAndrew Thompson }
18823fab7669SAndrew Thompson
18833fab7669SAndrew Thompson static void
bstp_tick(void * arg)188431997bf2SAndrew Thompson bstp_tick(void *arg)
188531997bf2SAndrew Thompson {
188689fa9c34SKristof Provost struct epoch_tracker et;
188796e47153SAndrew Thompson struct bstp_state *bs = arg;
188896e47153SAndrew Thompson struct bstp_port *bp;
188931997bf2SAndrew Thompson
189096e47153SAndrew Thompson BSTP_LOCK_ASSERT(bs);
189131997bf2SAndrew Thompson
1892c25789ccSAndrew Thompson if (bs->bs_running == 0)
1893c25789ccSAndrew Thompson return;
1894c25789ccSAndrew Thompson
189589fa9c34SKristof Provost NET_EPOCH_ENTER(et);
18962fe7ca2cSMarko Zec CURVNET_SET(bs->bs_vnet);
18972fe7ca2cSMarko Zec
18982ad65e31SAndrew Thompson /* poll link events on interfaces that do not support linkstate */
189947190ea6SAndrew Thompson if (bstp_timer_dectest(&bs->bs_link_timer)) {
19002ad65e31SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
19012ad65e31SAndrew Thompson if (!(bp->bp_ifp->if_capabilities & IFCAP_LINKSTATE))
19022885c19eSAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask);
19032ad65e31SAndrew Thompson }
19043fab7669SAndrew Thompson bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
190531997bf2SAndrew Thompson }
190631997bf2SAndrew Thompson
190796e47153SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
19083fab7669SAndrew Thompson /* no events need to happen for these */
190947190ea6SAndrew Thompson bstp_timer_dectest(&bp->bp_tc_timer);
191047190ea6SAndrew Thompson bstp_timer_dectest(&bp->bp_recent_root_timer);
191147190ea6SAndrew Thompson bstp_timer_dectest(&bp->bp_forward_delay_timer);
191247190ea6SAndrew Thompson bstp_timer_dectest(&bp->bp_recent_backup_timer);
191331997bf2SAndrew Thompson
191447190ea6SAndrew Thompson if (bstp_timer_dectest(&bp->bp_hello_timer))
19153fab7669SAndrew Thompson bstp_hello_timer_expiry(bs, bp);
19163fab7669SAndrew Thompson
191747190ea6SAndrew Thompson if (bstp_timer_dectest(&bp->bp_message_age_timer))
19183fab7669SAndrew Thompson bstp_message_age_expiry(bs, bp);
19193fab7669SAndrew Thompson
192047190ea6SAndrew Thompson if (bstp_timer_dectest(&bp->bp_migrate_delay_timer))
19213fab7669SAndrew Thompson bstp_migrate_delay_expiry(bs, bp);
19223fab7669SAndrew Thompson
192347190ea6SAndrew Thompson if (bstp_timer_dectest(&bp->bp_edge_delay_timer))
19243fab7669SAndrew Thompson bstp_edge_delay_expiry(bs, bp);
19253fab7669SAndrew Thompson
19263fab7669SAndrew Thompson /* update the various state machines for the port */
19273fab7669SAndrew Thompson bstp_update_state(bs, bp);
19283fab7669SAndrew Thompson
19293fab7669SAndrew Thompson if (bp->bp_txcount > 0)
19303fab7669SAndrew Thompson bp->bp_txcount--;
193131997bf2SAndrew Thompson }
193231997bf2SAndrew Thompson
19332fe7ca2cSMarko Zec CURVNET_RESTORE();
193489fa9c34SKristof Provost NET_EPOCH_EXIT(et);
19352fe7ca2cSMarko Zec
193696e47153SAndrew Thompson callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs);
193731997bf2SAndrew Thompson }
193831997bf2SAndrew Thompson
19396b32f3d3SAndrew Thompson static void
bstp_timer_start(struct bstp_timer * t,uint16_t v)194096e47153SAndrew Thompson bstp_timer_start(struct bstp_timer *t, uint16_t v)
194131997bf2SAndrew Thompson {
194231997bf2SAndrew Thompson t->value = v;
194331997bf2SAndrew Thompson t->active = 1;
19443fab7669SAndrew Thompson t->latched = 0;
194531997bf2SAndrew Thompson }
194631997bf2SAndrew Thompson
19476b32f3d3SAndrew Thompson static void
bstp_timer_stop(struct bstp_timer * t)194896e47153SAndrew Thompson bstp_timer_stop(struct bstp_timer *t)
194931997bf2SAndrew Thompson {
195031997bf2SAndrew Thompson t->value = 0;
195131997bf2SAndrew Thompson t->active = 0;
19523fab7669SAndrew Thompson t->latched = 0;
19533fab7669SAndrew Thompson }
19543fab7669SAndrew Thompson
19553fab7669SAndrew Thompson static void
bstp_timer_latch(struct bstp_timer * t)19563fab7669SAndrew Thompson bstp_timer_latch(struct bstp_timer *t)
19573fab7669SAndrew Thompson {
19583fab7669SAndrew Thompson t->latched = 1;
19593fab7669SAndrew Thompson t->active = 1;
196031997bf2SAndrew Thompson }
196131997bf2SAndrew Thompson
19626b32f3d3SAndrew Thompson static int
bstp_timer_dectest(struct bstp_timer * t)196347190ea6SAndrew Thompson bstp_timer_dectest(struct bstp_timer *t)
196431997bf2SAndrew Thompson {
19653fab7669SAndrew Thompson if (t->active == 0 || t->latched)
196631997bf2SAndrew Thompson return (0);
19673fab7669SAndrew Thompson t->value -= BSTP_TICK_VAL;
19683fab7669SAndrew Thompson if (t->value <= 0) {
196931997bf2SAndrew Thompson bstp_timer_stop(t);
197031997bf2SAndrew Thompson return (1);
197131997bf2SAndrew Thompson }
197231997bf2SAndrew Thompson return (0);
19733fab7669SAndrew Thompson }
197431997bf2SAndrew Thompson
19753fab7669SAndrew Thompson static void
bstp_hello_timer_expiry(struct bstp_state * bs,struct bstp_port * bp)19763fab7669SAndrew Thompson bstp_hello_timer_expiry(struct bstp_state *bs, struct bstp_port *bp)
19773fab7669SAndrew Thompson {
19783fab7669SAndrew Thompson if ((bp->bp_flags & BSTP_PORT_NEWINFO) ||
19793fab7669SAndrew Thompson bp->bp_role == BSTP_ROLE_DESIGNATED ||
19803fab7669SAndrew Thompson (bp->bp_role == BSTP_ROLE_ROOT &&
19813fab7669SAndrew Thompson bp->bp_tc_timer.active == 1)) {
19823fab7669SAndrew Thompson bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
19833fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
19843fab7669SAndrew Thompson bstp_transmit(bs, bp);
19853fab7669SAndrew Thompson }
19863fab7669SAndrew Thompson }
19873fab7669SAndrew Thompson
19883fab7669SAndrew Thompson static void
bstp_message_age_expiry(struct bstp_state * bs,struct bstp_port * bp)19893fab7669SAndrew Thompson bstp_message_age_expiry(struct bstp_state *bs, struct bstp_port *bp)
19903fab7669SAndrew Thompson {
19919ddd3624SAndrew Thompson if (bp->bp_infois == BSTP_INFO_RECEIVED) {
19923fab7669SAndrew Thompson bp->bp_infois = BSTP_INFO_AGED;
19933fab7669SAndrew Thompson bstp_assign_roles(bs);
19943fab7669SAndrew Thompson DPRINTF("aged info on %s\n", bp->bp_ifp->if_xname);
19953fab7669SAndrew Thompson }
19963fab7669SAndrew Thompson }
19973fab7669SAndrew Thompson
19983fab7669SAndrew Thompson static void
bstp_migrate_delay_expiry(struct bstp_state * bs,struct bstp_port * bp)19993fab7669SAndrew Thompson bstp_migrate_delay_expiry(struct bstp_state *bs, struct bstp_port *bp)
20003fab7669SAndrew Thompson {
20013fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_CANMIGRATE;
20023fab7669SAndrew Thompson }
20033fab7669SAndrew Thompson
20043fab7669SAndrew Thompson static void
bstp_edge_delay_expiry(struct bstp_state * bs,struct bstp_port * bp)20053fab7669SAndrew Thompson bstp_edge_delay_expiry(struct bstp_state *bs, struct bstp_port *bp)
20063fab7669SAndrew Thompson {
20073fab7669SAndrew Thompson if ((bp->bp_flags & BSTP_PORT_AUTOEDGE) &&
20083fab7669SAndrew Thompson bp->bp_protover == BSTP_PROTO_RSTP && bp->bp_proposing &&
2009c25789ccSAndrew Thompson bp->bp_role == BSTP_ROLE_DESIGNATED) {
20103fab7669SAndrew Thompson bp->bp_operedge = 1;
2011c25789ccSAndrew Thompson DPRINTF("%s -> edge port\n", bp->bp_ifp->if_xname);
2012c25789ccSAndrew Thompson }
20133fab7669SAndrew Thompson }
20143fab7669SAndrew Thompson
20153fab7669SAndrew Thompson static int
bstp_addr_cmp(const uint8_t * a,const uint8_t * b)20163fab7669SAndrew Thompson bstp_addr_cmp(const uint8_t *a, const uint8_t *b)
20173fab7669SAndrew Thompson {
20183fab7669SAndrew Thompson int i, d;
20193fab7669SAndrew Thompson
20203fab7669SAndrew Thompson for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) {
20213fab7669SAndrew Thompson d = ((int)a[i]) - ((int)b[i]);
20223fab7669SAndrew Thompson }
20233fab7669SAndrew Thompson
20243fab7669SAndrew Thompson return (d);
20253fab7669SAndrew Thompson }
20263fab7669SAndrew Thompson
20273fab7669SAndrew Thompson /*
20283fab7669SAndrew Thompson * compare the bridge address component of the bridgeid
20293fab7669SAndrew Thompson */
20303fab7669SAndrew Thompson static int
bstp_same_bridgeid(uint64_t id1,uint64_t id2)20313fab7669SAndrew Thompson bstp_same_bridgeid(uint64_t id1, uint64_t id2)
20323fab7669SAndrew Thompson {
20333fab7669SAndrew Thompson u_char addr1[ETHER_ADDR_LEN];
20343fab7669SAndrew Thompson u_char addr2[ETHER_ADDR_LEN];
20353fab7669SAndrew Thompson
20363fab7669SAndrew Thompson PV2ADDR(id1, addr1);
20373fab7669SAndrew Thompson PV2ADDR(id2, addr2);
20383fab7669SAndrew Thompson
20393fab7669SAndrew Thompson if (bstp_addr_cmp(addr1, addr2) == 0)
20403fab7669SAndrew Thompson return (1);
20413fab7669SAndrew Thompson
20423fab7669SAndrew Thompson return (0);
20433fab7669SAndrew Thompson }
20443fab7669SAndrew Thompson
20453fab7669SAndrew Thompson void
bstp_reinit(struct bstp_state * bs)20463fab7669SAndrew Thompson bstp_reinit(struct bstp_state *bs)
20473fab7669SAndrew Thompson {
2048a68cc388SGleb Smirnoff struct epoch_tracker et;
2049c25789ccSAndrew Thompson struct bstp_port *bp;
2050c25789ccSAndrew Thompson struct ifnet *ifp, *mif;
20513fab7669SAndrew Thompson u_char *e_addr;
20528d45bd6eSAndrew Thompson void *bridgeptr;
2053dd040130SAndrew Thompson static const u_char llzero[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
20543fab7669SAndrew Thompson
20553fab7669SAndrew Thompson BSTP_LOCK_ASSERT(bs);
20563fab7669SAndrew Thompson
20578d45bd6eSAndrew Thompson if (LIST_EMPTY(&bs->bs_bplist))
20588d45bd6eSAndrew Thompson goto disablestp;
20598d45bd6eSAndrew Thompson
2060c25789ccSAndrew Thompson mif = NULL;
20618d45bd6eSAndrew Thompson bridgeptr = LIST_FIRST(&bs->bs_bplist)->bp_ifp->if_bridge;
20628d45bd6eSAndrew Thompson KASSERT(bridgeptr != NULL, ("Invalid bridge pointer"));
2063*85967694SLexi Winter KASSERT(bridge_same_p != NULL, ("if_bridge not loaded"));
2064c25789ccSAndrew Thompson /*
2065c25789ccSAndrew Thompson * Search through the Ethernet adapters and find the one with the
20668d45bd6eSAndrew Thompson * lowest value. Make sure the adapter which we take the MAC address
20678d45bd6eSAndrew Thompson * from is part of this bridge, so we can have more than one independent
20688d45bd6eSAndrew Thompson * bridges in the same STP domain.
2069c25789ccSAndrew Thompson */
2070a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et);
20714f6c66ccSMatt Macy CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
2072f5537cd0SKristof Provost if (ifp->if_type != IFT_ETHER && ifp->if_type != IFT_L2VLAN)
20738d45bd6eSAndrew Thompson continue; /* Not Ethernet */
20748d45bd6eSAndrew Thompson
2075*85967694SLexi Winter if (!bridge_same_p(ifp->if_bridge, bridgeptr))
20768d45bd6eSAndrew Thompson continue; /* Not part of our bridge */
2077c25789ccSAndrew Thompson
2078dd040130SAndrew Thompson if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0)
20798d45bd6eSAndrew Thompson continue; /* No mac address set */
2080dd040130SAndrew Thompson
2081c25789ccSAndrew Thompson if (mif == NULL) {
2082c25789ccSAndrew Thompson mif = ifp;
2083c25789ccSAndrew Thompson continue;
2084c25789ccSAndrew Thompson }
2085c25789ccSAndrew Thompson if (bstp_addr_cmp(IF_LLADDR(ifp), IF_LLADDR(mif)) < 0) {
2086c25789ccSAndrew Thompson mif = ifp;
2087c25789ccSAndrew Thompson continue;
2088c25789ccSAndrew Thompson }
2089c25789ccSAndrew Thompson }
2090a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et);
20918d45bd6eSAndrew Thompson if (mif == NULL)
20928d45bd6eSAndrew Thompson goto disablestp;
2093dd040130SAndrew Thompson
2094c25789ccSAndrew Thompson e_addr = IF_LLADDR(mif);
20953fab7669SAndrew Thompson bs->bs_bridge_pv.pv_dbridge_id =
20963fab7669SAndrew Thompson (((uint64_t)bs->bs_bridge_priority) << 48) |
20973fab7669SAndrew Thompson (((uint64_t)e_addr[0]) << 40) |
20983fab7669SAndrew Thompson (((uint64_t)e_addr[1]) << 32) |
20993fab7669SAndrew Thompson (((uint64_t)e_addr[2]) << 24) |
21003fab7669SAndrew Thompson (((uint64_t)e_addr[3]) << 16) |
21013fab7669SAndrew Thompson (((uint64_t)e_addr[4]) << 8) |
21023fab7669SAndrew Thompson (((uint64_t)e_addr[5]));
21033fab7669SAndrew Thompson
21043fab7669SAndrew Thompson bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
21053fab7669SAndrew Thompson bs->bs_bridge_pv.pv_cost = 0;
21063fab7669SAndrew Thompson bs->bs_bridge_pv.pv_dport_id = 0;
21073fab7669SAndrew Thompson bs->bs_bridge_pv.pv_port_id = 0;
21083fab7669SAndrew Thompson
2109c25789ccSAndrew Thompson if (bs->bs_running && callout_pending(&bs->bs_bstpcallout) == 0)
21103fab7669SAndrew Thompson callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs);
21113fab7669SAndrew Thompson
2112c25789ccSAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
2113c25789ccSAndrew Thompson bp->bp_port_id = (bp->bp_priority << 8) |
2114c25789ccSAndrew Thompson (bp->bp_ifp->if_index & 0xfff);
21152885c19eSAndrew Thompson taskqueue_enqueue(taskqueue_swi, &bp->bp_mediatask);
2116c25789ccSAndrew Thompson }
21173fab7669SAndrew Thompson
21183fab7669SAndrew Thompson bstp_assign_roles(bs);
21193fab7669SAndrew Thompson bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
21208d45bd6eSAndrew Thompson return;
21218d45bd6eSAndrew Thompson
21228d45bd6eSAndrew Thompson disablestp:
21238d45bd6eSAndrew Thompson /* Set the bridge and root id (lower bits) to zero */
21248d45bd6eSAndrew Thompson bs->bs_bridge_pv.pv_dbridge_id =
21258d45bd6eSAndrew Thompson ((uint64_t)bs->bs_bridge_priority) << 48;
21268d45bd6eSAndrew Thompson bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
21278d45bd6eSAndrew Thompson bs->bs_root_pv = bs->bs_bridge_pv;
21288d45bd6eSAndrew Thompson /* Disable any remaining ports, they will have no MAC address */
21298d45bd6eSAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
21308d45bd6eSAndrew Thompson bp->bp_infois = BSTP_INFO_DISABLED;
21318d45bd6eSAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
21328d45bd6eSAndrew Thompson }
21338d45bd6eSAndrew Thompson callout_stop(&bs->bs_bstpcallout);
21343fab7669SAndrew Thompson }
21353fab7669SAndrew Thompson
21363fab7669SAndrew Thompson static int
bstp_modevent(module_t mod,int type,void * data)21373fab7669SAndrew Thompson bstp_modevent(module_t mod, int type, void *data)
21383fab7669SAndrew Thompson {
21393fab7669SAndrew Thompson switch (type) {
21403fab7669SAndrew Thompson case MOD_LOAD:
21413fab7669SAndrew Thompson mtx_init(&bstp_list_mtx, "bridgestp list", NULL, MTX_DEF);
21423fab7669SAndrew Thompson break;
21433fab7669SAndrew Thompson case MOD_UNLOAD:
21443fab7669SAndrew Thompson mtx_destroy(&bstp_list_mtx);
21453fab7669SAndrew Thompson break;
21463fab7669SAndrew Thompson default:
21473fab7669SAndrew Thompson return (EOPNOTSUPP);
21483fab7669SAndrew Thompson }
21493fab7669SAndrew Thompson return (0);
21503fab7669SAndrew Thompson }
21513fab7669SAndrew Thompson
21523fab7669SAndrew Thompson static moduledata_t bstp_mod = {
21533fab7669SAndrew Thompson "bridgestp",
21543fab7669SAndrew Thompson bstp_modevent,
21559823d527SKevin Lo 0
21563fab7669SAndrew Thompson };
21573fab7669SAndrew Thompson
21583fab7669SAndrew Thompson DECLARE_MODULE(bridgestp, bstp_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
21593fab7669SAndrew Thompson MODULE_VERSION(bridgestp, 1);
21603fab7669SAndrew Thompson
21613fab7669SAndrew Thompson void
bstp_attach(struct bstp_state * bs,struct bstp_cb_ops * cb)2162e5bda9fbSAndrew Thompson bstp_attach(struct bstp_state *bs, struct bstp_cb_ops *cb)
21633fab7669SAndrew Thompson {
21643fab7669SAndrew Thompson BSTP_LOCK_INIT(bs);
21653fab7669SAndrew Thompson callout_init_mtx(&bs->bs_bstpcallout, &bs->bs_mtx, 0);
21663fab7669SAndrew Thompson LIST_INIT(&bs->bs_bplist);
21673fab7669SAndrew Thompson
21683fab7669SAndrew Thompson bs->bs_bridge_max_age = BSTP_DEFAULT_MAX_AGE;
21693fab7669SAndrew Thompson bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
21703fab7669SAndrew Thompson bs->bs_bridge_fdelay = BSTP_DEFAULT_FORWARD_DELAY;
21713fab7669SAndrew Thompson bs->bs_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY;
21723fab7669SAndrew Thompson bs->bs_hold_time = BSTP_DEFAULT_HOLD_TIME;
21733fab7669SAndrew Thompson bs->bs_migration_delay = BSTP_DEFAULT_MIGRATE_DELAY;
21743fab7669SAndrew Thompson bs->bs_txholdcount = BSTP_DEFAULT_HOLD_COUNT;
21753fab7669SAndrew Thompson bs->bs_protover = BSTP_PROTO_RSTP;
2176e5bda9fbSAndrew Thompson bs->bs_state_cb = cb->bcb_state;
2177e5bda9fbSAndrew Thompson bs->bs_rtage_cb = cb->bcb_rtage;
21782fe7ca2cSMarko Zec bs->bs_vnet = curvnet;
21793fab7669SAndrew Thompson
21803fab7669SAndrew Thompson getmicrotime(&bs->bs_last_tc_time);
21813fab7669SAndrew Thompson
21823fab7669SAndrew Thompson mtx_lock(&bstp_list_mtx);
21833fab7669SAndrew Thompson LIST_INSERT_HEAD(&bstp_list, bs, bs_list);
21843fab7669SAndrew Thompson mtx_unlock(&bstp_list_mtx);
21853fab7669SAndrew Thompson }
21863fab7669SAndrew Thompson
21873fab7669SAndrew Thompson void
bstp_detach(struct bstp_state * bs)21883fab7669SAndrew Thompson bstp_detach(struct bstp_state *bs)
21893fab7669SAndrew Thompson {
21903fab7669SAndrew Thompson KASSERT(LIST_EMPTY(&bs->bs_bplist), ("bstp still active"));
21913fab7669SAndrew Thompson
21923fab7669SAndrew Thompson mtx_lock(&bstp_list_mtx);
21933fab7669SAndrew Thompson LIST_REMOVE(bs, bs_list);
21943fab7669SAndrew Thompson mtx_unlock(&bstp_list_mtx);
2195c25789ccSAndrew Thompson callout_drain(&bs->bs_bstpcallout);
21963fab7669SAndrew Thompson BSTP_LOCK_DESTROY(bs);
21973fab7669SAndrew Thompson }
21983fab7669SAndrew Thompson
21993fab7669SAndrew Thompson void
bstp_init(struct bstp_state * bs)22003fab7669SAndrew Thompson bstp_init(struct bstp_state *bs)
22013fab7669SAndrew Thompson {
22023fab7669SAndrew Thompson BSTP_LOCK(bs);
22033fab7669SAndrew Thompson callout_reset(&bs->bs_bstpcallout, hz, bstp_tick, bs);
2204c25789ccSAndrew Thompson bs->bs_running = 1;
22053fab7669SAndrew Thompson bstp_reinit(bs);
22063fab7669SAndrew Thompson BSTP_UNLOCK(bs);
22073fab7669SAndrew Thompson }
22083fab7669SAndrew Thompson
22093fab7669SAndrew Thompson void
bstp_stop(struct bstp_state * bs)22103fab7669SAndrew Thompson bstp_stop(struct bstp_state *bs)
22113fab7669SAndrew Thompson {
22123fab7669SAndrew Thompson struct bstp_port *bp;
22133fab7669SAndrew Thompson
221436dac7e2SAndrew Thompson BSTP_LOCK(bs);
22153fab7669SAndrew Thompson
22163fab7669SAndrew Thompson LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
22173fab7669SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
22183fab7669SAndrew Thompson
2219c25789ccSAndrew Thompson bs->bs_running = 0;
22203fab7669SAndrew Thompson callout_stop(&bs->bs_bstpcallout);
222136dac7e2SAndrew Thompson BSTP_UNLOCK(bs);
222231997bf2SAndrew Thompson }
222396e47153SAndrew Thompson
222496e47153SAndrew Thompson int
bstp_create(struct bstp_state * bs,struct bstp_port * bp,struct ifnet * ifp)2225071fff62SAndrew Thompson bstp_create(struct bstp_state *bs, struct bstp_port *bp, struct ifnet *ifp)
222696e47153SAndrew Thompson {
2227071fff62SAndrew Thompson bzero(bp, sizeof(struct bstp_port));
2228071fff62SAndrew Thompson
2229071fff62SAndrew Thompson BSTP_LOCK(bs);
2230071fff62SAndrew Thompson bp->bp_ifp = ifp;
2231071fff62SAndrew Thompson bp->bp_bs = bs;
2232071fff62SAndrew Thompson bp->bp_priority = BSTP_DEFAULT_PORT_PRIORITY;
2233071fff62SAndrew Thompson TASK_INIT(&bp->bp_statetask, 0, bstp_notify_state, bp);
2234071fff62SAndrew Thompson TASK_INIT(&bp->bp_rtagetask, 0, bstp_notify_rtage, bp);
22352885c19eSAndrew Thompson TASK_INIT(&bp->bp_mediatask, 0, bstp_ifupdstatus, bp);
2236071fff62SAndrew Thompson
2237071fff62SAndrew Thompson /* Init state */
2238ebd4a4baSAndrew Thompson bp->bp_infois = BSTP_INFO_DISABLED;
223978709605SAndrew Thompson bp->bp_flags = BSTP_PORT_AUTOEDGE|BSTP_PORT_AUTOPTP;
2240071fff62SAndrew Thompson bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
2241071fff62SAndrew Thompson bstp_set_port_proto(bp, bs->bs_protover);
2242071fff62SAndrew Thompson bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
2243071fff62SAndrew Thompson bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
2244071fff62SAndrew Thompson bp->bp_path_cost = bstp_calc_path_cost(bp);
2245071fff62SAndrew Thompson BSTP_UNLOCK(bs);
2246071fff62SAndrew Thompson return (0);
2247071fff62SAndrew Thompson }
2248071fff62SAndrew Thompson
2249071fff62SAndrew Thompson int
bstp_enable(struct bstp_port * bp)2250071fff62SAndrew Thompson bstp_enable(struct bstp_port *bp)
2251071fff62SAndrew Thompson {
2252071fff62SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
2253071fff62SAndrew Thompson struct ifnet *ifp = bp->bp_ifp;
2254071fff62SAndrew Thompson
225596e47153SAndrew Thompson KASSERT(bp->bp_active == 0, ("already a bstp member"));
225689fa9c34SKristof Provost NET_EPOCH_ASSERT(); /* Because bstp_update_roles() causes traffic. */
225796e47153SAndrew Thompson
225896e47153SAndrew Thompson switch (ifp->if_type) {
225996e47153SAndrew Thompson case IFT_ETHER: /* These can do spanning tree. */
2260711ed156SKristof Provost case IFT_L2VLAN:
226196e47153SAndrew Thompson break;
226296e47153SAndrew Thompson default:
226396e47153SAndrew Thompson /* Nothing else can. */
226496e47153SAndrew Thompson return (EINVAL);
226596e47153SAndrew Thompson }
226696e47153SAndrew Thompson
226796e47153SAndrew Thompson BSTP_LOCK(bs);
226896e47153SAndrew Thompson LIST_INSERT_HEAD(&bs->bs_bplist, bp, bp_next);
22693fab7669SAndrew Thompson bp->bp_active = 1;
22703fab7669SAndrew Thompson bp->bp_flags |= BSTP_PORT_NEWINFO;
22713fab7669SAndrew Thompson bstp_reinit(bs);
22723fab7669SAndrew Thompson bstp_update_roles(bs, bp);
22733fab7669SAndrew Thompson BSTP_UNLOCK(bs);
227496e47153SAndrew Thompson return (0);
227596e47153SAndrew Thompson }
227696e47153SAndrew Thompson
227796e47153SAndrew Thompson void
bstp_disable(struct bstp_port * bp)2278071fff62SAndrew Thompson bstp_disable(struct bstp_port *bp)
227996e47153SAndrew Thompson {
228096e47153SAndrew Thompson struct bstp_state *bs = bp->bp_bs;
228196e47153SAndrew Thompson
228296e47153SAndrew Thompson KASSERT(bp->bp_active == 1, ("not a bstp member"));
228396e47153SAndrew Thompson
228496e47153SAndrew Thompson BSTP_LOCK(bs);
2285071fff62SAndrew Thompson bstp_disable_port(bs, bp);
228696e47153SAndrew Thompson LIST_REMOVE(bp, bp_next);
228796e47153SAndrew Thompson bp->bp_active = 0;
228896e47153SAndrew Thompson bstp_reinit(bs);
22893fab7669SAndrew Thompson BSTP_UNLOCK(bs);
229096e47153SAndrew Thompson }
22916f2abce0SAndrew Thompson
22926f2abce0SAndrew Thompson /*
22936f2abce0SAndrew Thompson * The bstp_port structure is about to be freed by the parent bridge.
22946f2abce0SAndrew Thompson */
22956f2abce0SAndrew Thompson void
bstp_destroy(struct bstp_port * bp)2296071fff62SAndrew Thompson bstp_destroy(struct bstp_port *bp)
22976f2abce0SAndrew Thompson {
22986f2abce0SAndrew Thompson KASSERT(bp->bp_active == 0, ("port is still attached"));
22996f2abce0SAndrew Thompson taskqueue_drain(taskqueue_swi, &bp->bp_statetask);
23003fab7669SAndrew Thompson taskqueue_drain(taskqueue_swi, &bp->bp_rtagetask);
23012885c19eSAndrew Thompson taskqueue_drain(taskqueue_swi, &bp->bp_mediatask);
2302d6747eafSKristof Provost
2303d6747eafSKristof Provost if (bp->bp_bs->bs_root_port == bp)
2304d6747eafSKristof Provost bstp_assign_roles(bp->bp_bs);
23056f2abce0SAndrew Thompson }
2306