1*678831beSjsg /* $OpenBSD: bridgestp.c,v 1.77 2021/03/10 10:21:47 jsg Exp $ */
29fa0b7aaSjason
39fa0b7aaSjason /*
49fa0b7aaSjason * Copyright (c) 2000 Jason L. Wright (jason@thought.net)
5b1b9d014Sreyk * Copyright (c) 2006 Andrew Thompson (thompsa@FreeBSD.org)
69fa0b7aaSjason * All rights reserved.
79fa0b7aaSjason *
89fa0b7aaSjason * Redistribution and use in source and binary forms, with or without
99fa0b7aaSjason * modification, are permitted provided that the following conditions
109fa0b7aaSjason * are met:
119fa0b7aaSjason * 1. Redistributions of source code must retain the above copyright
129fa0b7aaSjason * notice, this list of conditions and the following disclaimer.
139fa0b7aaSjason * 2. Redistributions in binary form must reproduce the above copyright
149fa0b7aaSjason * notice, this list of conditions and the following disclaimer in the
159fa0b7aaSjason * documentation and/or other materials provided with the distribution.
169fa0b7aaSjason *
179fa0b7aaSjason * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
189fa0b7aaSjason * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
199fa0b7aaSjason * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
209fa0b7aaSjason * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
219fa0b7aaSjason * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
229fa0b7aaSjason * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
239fa0b7aaSjason * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
249fa0b7aaSjason * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
259fa0b7aaSjason * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
269fa0b7aaSjason * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
279fa0b7aaSjason * POSSIBILITY OF SUCH DAMAGE.
289fa0b7aaSjason */
299fa0b7aaSjason
309fa0b7aaSjason /*
319fa0b7aaSjason * Implementation of the spanning tree protocol as defined in
32b1b9d014Sreyk * ISO/IEC 802.1D-2004, June 9, 2004.
339fa0b7aaSjason */
349fa0b7aaSjason
359fa0b7aaSjason #include <sys/param.h>
369fa0b7aaSjason #include <sys/systm.h>
379fa0b7aaSjason #include <sys/mbuf.h>
389fa0b7aaSjason #include <sys/socket.h>
399fa0b7aaSjason #include <sys/ioctl.h>
409fa0b7aaSjason #include <sys/device.h>
419fa0b7aaSjason #include <sys/kernel.h>
420b2715c1Smickey #include <sys/timeout.h>
439fa0b7aaSjason
449fa0b7aaSjason #include <net/if.h>
459fa0b7aaSjason #include <net/if_types.h>
46b1b9d014Sreyk #include <net/if_dl.h>
479fa0b7aaSjason #include <net/if_llc.h>
489fa0b7aaSjason #include <net/netisr.h>
499fa0b7aaSjason
509fa0b7aaSjason #include <netinet/in.h>
519fa0b7aaSjason #include <netinet/ip.h>
529fa0b7aaSjason #include <netinet/if_ether.h>
539fa0b7aaSjason
549fa0b7aaSjason #include <net/if_bridge.h>
559fa0b7aaSjason
56b1b9d014Sreyk /* STP port states */
57b1b9d014Sreyk #define BSTP_IFSTATE_DISABLED 0
58b1b9d014Sreyk #define BSTP_IFSTATE_LISTENING 1
59b1b9d014Sreyk #define BSTP_IFSTATE_LEARNING 2
60b1b9d014Sreyk #define BSTP_IFSTATE_FORWARDING 3
61b1b9d014Sreyk #define BSTP_IFSTATE_BLOCKING 4
62b1b9d014Sreyk #define BSTP_IFSTATE_DISCARDING 5
63b1b9d014Sreyk
64b1b9d014Sreyk #define BSTP_TCSTATE_ACTIVE 1
65b1b9d014Sreyk #define BSTP_TCSTATE_DETECTED 2
66b1b9d014Sreyk #define BSTP_TCSTATE_INACTIVE 3
67b1b9d014Sreyk #define BSTP_TCSTATE_LEARNING 4
68b1b9d014Sreyk #define BSTP_TCSTATE_PROPAG 5
69b1b9d014Sreyk #define BSTP_TCSTATE_ACK 6
70b1b9d014Sreyk #define BSTP_TCSTATE_TC 7
71b1b9d014Sreyk #define BSTP_TCSTATE_TCN 8
72b1b9d014Sreyk
73b1b9d014Sreyk #define BSTP_ROLE_DISABLED 0
74b1b9d014Sreyk #define BSTP_ROLE_ROOT 1
75b1b9d014Sreyk #define BSTP_ROLE_DESIGNATED 2
76b1b9d014Sreyk #define BSTP_ROLE_ALTERNATE 3
77b1b9d014Sreyk #define BSTP_ROLE_BACKUP 4
78b1b9d014Sreyk
79b1b9d014Sreyk /* STP port flags */
80b1b9d014Sreyk #define BSTP_PORT_CANMIGRATE 0x0001
81b1b9d014Sreyk #define BSTP_PORT_NEWINFO 0x0002
82b1b9d014Sreyk #define BSTP_PORT_DISPUTED 0x0004
83b1b9d014Sreyk #define BSTP_PORT_ADMCOST 0x0008
84b1b9d014Sreyk #define BSTP_PORT_AUTOEDGE 0x0010
85b1b9d014Sreyk
86b1b9d014Sreyk /* BPDU priority */
87b1b9d014Sreyk #define BSTP_PDU_SUPERIOR 1
88b1b9d014Sreyk #define BSTP_PDU_REPEATED 2
89b1b9d014Sreyk #define BSTP_PDU_INFERIOR 3
90b1b9d014Sreyk #define BSTP_PDU_INFERIORALT 4
91b1b9d014Sreyk #define BSTP_PDU_OTHER 5
929fa0b7aaSjason
939fa0b7aaSjason /* BPDU flags */
94b1b9d014Sreyk #define BSTP_PDU_PRMASK 0x0c /* Port Role */
95b1b9d014Sreyk #define BSTP_PDU_PRSHIFT 2 /* Port Role offset */
96b1b9d014Sreyk #define BSTP_PDU_F_UNKN 0x00 /* Unknown port (00) */
97b1b9d014Sreyk #define BSTP_PDU_F_ALT 0x01 /* Alt/Backup port (01) */
98b1b9d014Sreyk #define BSTP_PDU_F_ROOT 0x02 /* Root port (10) */
99b1b9d014Sreyk #define BSTP_PDU_F_DESG 0x03 /* Designated port (11) */
100b1b9d014Sreyk
101b1b9d014Sreyk #define BSTP_PDU_STPMASK 0x81 /* strip unused STP flags */
102b1b9d014Sreyk #define BSTP_PDU_RSTPMASK 0x7f /* strip unused RSTP flags */
103b1b9d014Sreyk #define BSTP_PDU_F_TC 0x01 /* Topology change */
104b1b9d014Sreyk #define BSTP_PDU_F_P 0x02 /* Proposal flag */
105b1b9d014Sreyk #define BSTP_PDU_F_L 0x10 /* Learning flag */
106b1b9d014Sreyk #define BSTP_PDU_F_F 0x20 /* Forwarding flag */
107b1b9d014Sreyk #define BSTP_PDU_F_A 0x40 /* Agreement flag */
108b1b9d014Sreyk #define BSTP_PDU_F_TCA 0x80 /* Topology change ack */
109b1b9d014Sreyk
110b1b9d014Sreyk /*
111b1b9d014Sreyk * Spanning tree defaults.
112b1b9d014Sreyk */
113b1b9d014Sreyk #define BSTP_DEFAULT_MAX_AGE (20 * 256)
114b1b9d014Sreyk #define BSTP_DEFAULT_HELLO_TIME (2 * 256)
115b1b9d014Sreyk #define BSTP_DEFAULT_FORWARD_DELAY (15 * 256)
116b1b9d014Sreyk #define BSTP_DEFAULT_HOLD_TIME (1 * 256)
117b1b9d014Sreyk #define BSTP_DEFAULT_MIGRATE_DELAY (3 * 256)
118b1b9d014Sreyk #define BSTP_DEFAULT_HOLD_COUNT 6
119b1b9d014Sreyk #define BSTP_DEFAULT_BRIDGE_PRIORITY 0x8000
120b1b9d014Sreyk #define BSTP_DEFAULT_PORT_PRIORITY 0x80
121b1b9d014Sreyk #define BSTP_DEFAULT_PATH_COST 55
122b1b9d014Sreyk #define BSTP_MIN_HELLO_TIME (1 * 256)
123b1b9d014Sreyk #define BSTP_MIN_MAX_AGE (6 * 256)
124b1b9d014Sreyk #define BSTP_MIN_FORWARD_DELAY (4 * 256)
125b1b9d014Sreyk #define BSTP_MIN_HOLD_COUNT 1
126b1b9d014Sreyk #define BSTP_MAX_HELLO_TIME (2 * 256)
127b1b9d014Sreyk #define BSTP_MAX_MAX_AGE (40 * 256)
128b1b9d014Sreyk #define BSTP_MAX_FORWARD_DELAY (30 * 256)
129b1b9d014Sreyk #define BSTP_MAX_HOLD_COUNT 10
130b1b9d014Sreyk #define BSTP_MAX_PRIORITY 61440
131b1b9d014Sreyk #define BSTP_MAX_PORT_PRIORITY 240
132b1b9d014Sreyk #define BSTP_MAX_PATH_COST 200000000
133b1b9d014Sreyk
134b1b9d014Sreyk /* BPDU message types */
135b1b9d014Sreyk #define BSTP_MSGTYPE_CFG 0x00 /* Configuration */
136b1b9d014Sreyk #define BSTP_MSGTYPE_RSTP 0x02 /* Rapid STP */
137b1b9d014Sreyk #define BSTP_MSGTYPE_TCN 0x80 /* Topology chg notification */
138b1b9d014Sreyk
139af52927aSmartynas #define BSTP_INFO_RECEIVED 1
140b1b9d014Sreyk #define BSTP_INFO_MINE 2
141b1b9d014Sreyk #define BSTP_INFO_AGED 3
142b1b9d014Sreyk #define BSTP_INFO_DISABLED 4
1439fa0b7aaSjason
1449fa0b7aaSjason #define BSTP_MESSAGE_AGE_INCR (1 * 256) /* in 256ths of a second */
1459fa0b7aaSjason #define BSTP_TICK_VAL (1 * 256) /* in 256ths of a second */
146b1b9d014Sreyk #define BSTP_LINK_TIMER (BSTP_TICK_VAL * 15)
147b1b9d014Sreyk
148b1b9d014Sreyk #ifdef BRIDGESTP_DEBUG
149793c5642Smvs #define DPRINTF(bp, fmt, arg...) \
150793c5642Smvs do { \
151793c5642Smvs struct ifnet *__ifp = if_get((bp)->bp_ifindex); \
152793c5642Smvs printf("bstp: %s" fmt, __ifp? __ifp->if_xname : "Unknown", ##arg); \
153793c5642Smvs if_put(__ifp); \
154793c5642Smvs } while (0)
155b1b9d014Sreyk #else
156793c5642Smvs #define DPRINTF(bp, fmt, arg...)
157b1b9d014Sreyk #endif
158b1b9d014Sreyk
159b1b9d014Sreyk #define PV2ADDR(pv, eaddr) do { \
160b1b9d014Sreyk eaddr[0] = pv >> 40; \
161b1b9d014Sreyk eaddr[1] = pv >> 32; \
162b1b9d014Sreyk eaddr[2] = pv >> 24; \
163b1b9d014Sreyk eaddr[3] = pv >> 16; \
164b1b9d014Sreyk eaddr[4] = pv >> 8; \
165b1b9d014Sreyk eaddr[5] = pv >> 0; \
166b1b9d014Sreyk } while (0)
167b1b9d014Sreyk
168b1b9d014Sreyk #define INFO_BETTER 1
169b1b9d014Sreyk #define INFO_SAME 0
170b1b9d014Sreyk #define INFO_WORSE -1
1719fa0b7aaSjason
17233050f9eShenning #define BSTP_IFQ_PRIO 6
17333050f9eShenning
1749fa0b7aaSjason /*
1759fa0b7aaSjason * Because BPDU's do not make nicely aligned structures, two different
1769fa0b7aaSjason * declarations are used: bstp_?bpdu (wire representation, packed) and
1779fa0b7aaSjason * bstp_*_unit (internal, nicely aligned version).
1789fa0b7aaSjason */
1799fa0b7aaSjason
1809fa0b7aaSjason /* configuration bridge protocol data unit */
1819fa0b7aaSjason struct bstp_cbpdu {
1829fa0b7aaSjason u_int8_t cbu_dsap; /* LLC: destination sap */
1839fa0b7aaSjason u_int8_t cbu_ssap; /* LLC: source sap */
1849fa0b7aaSjason u_int8_t cbu_ctl; /* LLC: control */
1859fa0b7aaSjason u_int16_t cbu_protoid; /* protocol id */
1869fa0b7aaSjason u_int8_t cbu_protover; /* protocol version */
1879fa0b7aaSjason u_int8_t cbu_bpdutype; /* message type */
1889fa0b7aaSjason u_int8_t cbu_flags; /* flags (below) */
1899fa0b7aaSjason
1909fa0b7aaSjason /* root id */
1919fa0b7aaSjason u_int16_t cbu_rootpri; /* root priority */
1929fa0b7aaSjason u_int8_t cbu_rootaddr[6]; /* root address */
1939fa0b7aaSjason
1949fa0b7aaSjason u_int32_t cbu_rootpathcost; /* root path cost */
1959fa0b7aaSjason
1969fa0b7aaSjason /* bridge id */
1979fa0b7aaSjason u_int16_t cbu_bridgepri; /* bridge priority */
1989fa0b7aaSjason u_int8_t cbu_bridgeaddr[6]; /* bridge address */
1999fa0b7aaSjason
2009fa0b7aaSjason u_int16_t cbu_portid; /* port id */
2019fa0b7aaSjason u_int16_t cbu_messageage; /* current message age */
2029fa0b7aaSjason u_int16_t cbu_maxage; /* maximum age */
2039fa0b7aaSjason u_int16_t cbu_hellotime; /* hello time */
2049fa0b7aaSjason u_int16_t cbu_forwarddelay; /* forwarding delay */
205b1b9d014Sreyk u_int8_t cbu_versionlen; /* version 1 length */
206b8468dd4Savsm } __packed;
2079fa0b7aaSjason
208b1b9d014Sreyk #define BSTP_BPDU_STP_LEN (3 + 35) /* LLC + STP pdu */
209b1b9d014Sreyk #define BSTP_BPDU_RSTP_LEN (3 + 36) /* LLC + RSTP pdu */
210b1b9d014Sreyk
2119fa0b7aaSjason /* topology change notification bridge protocol data unit */
2129fa0b7aaSjason struct bstp_tbpdu {
2139fa0b7aaSjason u_int8_t tbu_dsap; /* LLC: destination sap */
2149fa0b7aaSjason u_int8_t tbu_ssap; /* LLC: source sap */
2159fa0b7aaSjason u_int8_t tbu_ctl; /* LLC: control */
2169fa0b7aaSjason u_int16_t tbu_protoid; /* protocol id */
2179fa0b7aaSjason u_int8_t tbu_protover; /* protocol version */
2189fa0b7aaSjason u_int8_t tbu_bpdutype; /* message type */
219b8468dd4Savsm } __packed;
2209fa0b7aaSjason
221b1b9d014Sreyk const u_int8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
2229fa0b7aaSjason
223b1b9d014Sreyk
224b1b9d014Sreyk void bstp_transmit(struct bstp_state *, struct bstp_port *);
225b1b9d014Sreyk void bstp_transmit_bpdu(struct bstp_state *, struct bstp_port *);
226b1b9d014Sreyk void bstp_transmit_tcn(struct bstp_state *, struct bstp_port *);
227b1b9d014Sreyk void bstp_decode_bpdu(struct bstp_port *, struct bstp_cbpdu *,
228023ba305Sderaadt struct bstp_config_unit *);
229b1b9d014Sreyk void bstp_send_bpdu(struct bstp_state *, struct bstp_port *,
230b1b9d014Sreyk struct bstp_cbpdu *);
231b1b9d014Sreyk int bstp_pdu_flags(struct bstp_port *);
232b1b9d014Sreyk void bstp_received_stp(struct bstp_state *, struct bstp_port *,
233b1b9d014Sreyk struct mbuf **, struct bstp_tbpdu *);
234b1b9d014Sreyk void bstp_received_rstp(struct bstp_state *, struct bstp_port *,
235b1b9d014Sreyk struct mbuf **, struct bstp_tbpdu *);
236b1b9d014Sreyk void bstp_received_tcn(struct bstp_state *, struct bstp_port *,
237023ba305Sderaadt struct bstp_tcn_unit *);
238b1b9d014Sreyk void bstp_received_bpdu(struct bstp_state *, struct bstp_port *,
239023ba305Sderaadt struct bstp_config_unit *);
240b1b9d014Sreyk int bstp_pdu_rcvtype(struct bstp_port *, struct bstp_config_unit *);
241b1b9d014Sreyk int bstp_pdu_bettersame(struct bstp_port *, int);
242b1b9d014Sreyk int bstp_info_cmp(struct bstp_pri_vector *,
243b1b9d014Sreyk struct bstp_pri_vector *);
244b1b9d014Sreyk int bstp_info_superior(struct bstp_pri_vector *,
245b1b9d014Sreyk struct bstp_pri_vector *);
246b1b9d014Sreyk void bstp_assign_roles(struct bstp_state *);
247b1b9d014Sreyk void bstp_update_roles(struct bstp_state *, struct bstp_port *);
248b1b9d014Sreyk void bstp_update_state(struct bstp_state *, struct bstp_port *);
249b1b9d014Sreyk void bstp_update_tc(struct bstp_port *);
250b1b9d014Sreyk void bstp_update_info(struct bstp_port *);
251b1b9d014Sreyk void bstp_set_other_tcprop(struct bstp_port *);
252b1b9d014Sreyk void bstp_set_all_reroot(struct bstp_state *);
253b1b9d014Sreyk void bstp_set_all_sync(struct bstp_state *);
254b1b9d014Sreyk void bstp_set_port_state(struct bstp_port *, int);
255b1b9d014Sreyk void bstp_set_port_role(struct bstp_port *, int);
256b1b9d014Sreyk void bstp_set_port_proto(struct bstp_port *, int);
257b1b9d014Sreyk void bstp_set_port_tc(struct bstp_port *, int);
258b1b9d014Sreyk void bstp_set_timer_tc(struct bstp_port *);
259b1b9d014Sreyk void bstp_set_timer_msgage(struct bstp_port *);
260e380692eSmpi void bstp_reset(struct bstp_state *);
261b1b9d014Sreyk int bstp_rerooted(struct bstp_state *, struct bstp_port *);
262b1b9d014Sreyk u_int32_t bstp_calc_path_cost(struct bstp_port *);
2637426de2dSmpi void bstp_notify_rtage(struct bstp_port *, int);
264b1b9d014Sreyk void bstp_ifupdstatus(struct bstp_state *, struct bstp_port *);
265b1b9d014Sreyk void bstp_enable_port(struct bstp_state *, struct bstp_port *);
266b1b9d014Sreyk void bstp_disable_port(struct bstp_state *, struct bstp_port *);
267c4071fd1Smillert void bstp_tick(void *);
268b1b9d014Sreyk void bstp_timer_start(struct bstp_timer *, u_int16_t);
269b1b9d014Sreyk void bstp_timer_stop(struct bstp_timer *);
270b1b9d014Sreyk void bstp_timer_latch(struct bstp_timer *);
271b1b9d014Sreyk int bstp_timer_expired(struct bstp_timer *);
272b1b9d014Sreyk void bstp_hello_timer_expiry(struct bstp_state *,
273b1b9d014Sreyk struct bstp_port *);
274b1b9d014Sreyk void bstp_message_age_expiry(struct bstp_state *,
275b1b9d014Sreyk struct bstp_port *);
276b1b9d014Sreyk void bstp_migrate_delay_expiry(struct bstp_state *,
277b1b9d014Sreyk struct bstp_port *);
278b1b9d014Sreyk void bstp_edge_delay_expiry(struct bstp_state *,
279b1b9d014Sreyk struct bstp_port *);
280b1b9d014Sreyk int bstp_addr_cmp(const u_int8_t *, const u_int8_t *);
281b1b9d014Sreyk int bstp_same_bridgeid(u_int64_t, u_int64_t);
2829fa0b7aaSjason
283b1b9d014Sreyk
284b1b9d014Sreyk void
bstp_transmit(struct bstp_state * bs,struct bstp_port * bp)285b1b9d014Sreyk bstp_transmit(struct bstp_state *bs, struct bstp_port *bp)
286b1b9d014Sreyk {
28713dba897Smvs struct ifnet *ifp;
28813dba897Smvs
28913dba897Smvs if ((ifp = if_get(bs->bs_ifindex)) == NULL)
290b1b9d014Sreyk return;
291b1b9d014Sreyk
29213dba897Smvs if ((ifp->if_flags & IFF_RUNNING) == 0 || bp == NULL) {
29313dba897Smvs if_put(ifp);
29413dba897Smvs return;
29513dba897Smvs }
29613dba897Smvs if_put(ifp);
29713dba897Smvs
298b1b9d014Sreyk /*
299b1b9d014Sreyk * a PDU can only be sent if we have tx quota left and the
300b1b9d014Sreyk * hello timer is running.
301b1b9d014Sreyk */
302b1b9d014Sreyk if (bp->bp_hello_timer.active == 0) {
303b1b9d014Sreyk /* Test if it needs to be reset */
304b1b9d014Sreyk bstp_hello_timer_expiry(bs, bp);
3059fa0b7aaSjason return;
3069fa0b7aaSjason }
307b1b9d014Sreyk if (bp->bp_txcount > bs->bs_txholdcount)
308b1b9d014Sreyk /* Ran out of karma */
309b1b9d014Sreyk return;
3109fa0b7aaSjason
311b1b9d014Sreyk if (bp->bp_protover == BSTP_PROTO_RSTP) {
312b1b9d014Sreyk bstp_transmit_bpdu(bs, bp);
313b1b9d014Sreyk bp->bp_tc_ack = 0;
314b1b9d014Sreyk } else { /* STP */
315b1b9d014Sreyk switch (bp->bp_role) {
316b1b9d014Sreyk case BSTP_ROLE_DESIGNATED:
317b1b9d014Sreyk bstp_transmit_bpdu(bs, bp);
318b1b9d014Sreyk bp->bp_tc_ack = 0;
319b1b9d014Sreyk break;
3209fa0b7aaSjason
321b1b9d014Sreyk case BSTP_ROLE_ROOT:
322b1b9d014Sreyk bstp_transmit_tcn(bs, bp);
323b1b9d014Sreyk break;
3249fa0b7aaSjason }
3259fa0b7aaSjason }
326b1b9d014Sreyk bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
327b1b9d014Sreyk bp->bp_flags &= ~BSTP_PORT_NEWINFO;
328b1b9d014Sreyk }
3299fa0b7aaSjason
3309fa0b7aaSjason void
bstp_transmit_bpdu(struct bstp_state * bs,struct bstp_port * bp)331b1b9d014Sreyk bstp_transmit_bpdu(struct bstp_state *bs, struct bstp_port *bp)
3329fa0b7aaSjason {
3339fa0b7aaSjason struct bstp_cbpdu bpdu;
3349fa0b7aaSjason
335b1b9d014Sreyk bpdu.cbu_rootpri = htons(bp->bp_desg_pv.pv_root_id >> 48);
336b1b9d014Sreyk PV2ADDR(bp->bp_desg_pv.pv_root_id, bpdu.cbu_rootaddr);
3379fa0b7aaSjason
338b1b9d014Sreyk bpdu.cbu_rootpathcost = htonl(bp->bp_desg_pv.pv_cost);
339b1b9d014Sreyk
340b1b9d014Sreyk bpdu.cbu_bridgepri = htons(bp->bp_desg_pv.pv_dbridge_id >> 48);
341b1b9d014Sreyk PV2ADDR(bp->bp_desg_pv.pv_dbridge_id, bpdu.cbu_bridgeaddr);
342b1b9d014Sreyk
343b1b9d014Sreyk bpdu.cbu_portid = htons(bp->bp_port_id);
344b1b9d014Sreyk bpdu.cbu_messageage = htons(bp->bp_desg_msg_age);
345b1b9d014Sreyk bpdu.cbu_maxage = htons(bp->bp_desg_max_age);
346b1b9d014Sreyk bpdu.cbu_hellotime = htons(bp->bp_desg_htime);
347b1b9d014Sreyk bpdu.cbu_forwarddelay = htons(bp->bp_desg_fdelay);
348b1b9d014Sreyk
349b1b9d014Sreyk bpdu.cbu_flags = bstp_pdu_flags(bp);
350b1b9d014Sreyk
351b1b9d014Sreyk switch (bp->bp_protover) {
352b1b9d014Sreyk case BSTP_PROTO_STP:
353b1b9d014Sreyk bpdu.cbu_bpdutype = BSTP_MSGTYPE_CFG;
354b1b9d014Sreyk break;
355b1b9d014Sreyk case BSTP_PROTO_RSTP:
356b1b9d014Sreyk bpdu.cbu_bpdutype = BSTP_MSGTYPE_RSTP;
357b1b9d014Sreyk break;
3589fa0b7aaSjason }
3599fa0b7aaSjason
360b1b9d014Sreyk bstp_send_bpdu(bs, bp, &bpdu);
3619fa0b7aaSjason }
3629fa0b7aaSjason
3639fa0b7aaSjason void
bstp_transmit_tcn(struct bstp_state * bs,struct bstp_port * bp)364b1b9d014Sreyk bstp_transmit_tcn(struct bstp_state *bs, struct bstp_port *bp)
3659fa0b7aaSjason {
3669fa0b7aaSjason struct bstp_tbpdu bpdu;
367793c5642Smvs struct ifnet *ifp;
368bfb65a4aSjason struct ether_header *eh;
369bfb65a4aSjason struct mbuf *m;
3709fa0b7aaSjason
371793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) == NULL)
372b40afe29Smarkus return;
373793c5642Smvs if ((ifp->if_flags & IFF_RUNNING) == 0)
374793c5642Smvs goto rele;
375b40afe29Smarkus
3769fa0b7aaSjason MGETHDR(m, M_DONTWAIT, MT_DATA);
377bfb65a4aSjason if (m == NULL)
378793c5642Smvs goto rele;
379fb492c37Smpi m->m_pkthdr.ph_ifidx = ifp->if_index;
380ac6b16b3Sjason m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
38133050f9eShenning m->m_pkthdr.pf.prio = BSTP_IFQ_PRIO;
3829fa0b7aaSjason m->m_len = m->m_pkthdr.len;
3839fa0b7aaSjason
384bfb65a4aSjason eh = mtod(m, struct ether_header *);
385b1b9d014Sreyk bcopy(LLADDR(ifp->if_sadl), eh->ether_shost, ETHER_ADDR_LEN);
386bfb65a4aSjason bcopy(bstp_etheraddr, eh->ether_dhost, ETHER_ADDR_LEN);
387bfb65a4aSjason eh->ether_type = htons(sizeof(bpdu));
388bfb65a4aSjason
3899fa0b7aaSjason bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP;
3909fa0b7aaSjason bpdu.tbu_ctl = LLC_UI;
3919fa0b7aaSjason bpdu.tbu_protoid = 0;
3929fa0b7aaSjason bpdu.tbu_protover = 0;
393bfb65a4aSjason bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN;
394b1b9d014Sreyk bcopy(&bpdu, mtod(m, caddr_t) + sizeof(*eh), sizeof(bpdu));
3959fa0b7aaSjason
396b1b9d014Sreyk bp->bp_txcount++;
397c38eb4ffSmpi if_enqueue(ifp, m);
398793c5642Smvs rele:
399793c5642Smvs if_put(ifp);
4009fa0b7aaSjason }
4019fa0b7aaSjason
4029fa0b7aaSjason void
bstp_decode_bpdu(struct bstp_port * bp,struct bstp_cbpdu * cpdu,struct bstp_config_unit * cu)403b1b9d014Sreyk bstp_decode_bpdu(struct bstp_port *bp, struct bstp_cbpdu *cpdu,
404b1b9d014Sreyk struct bstp_config_unit *cu)
4059fa0b7aaSjason {
406b1b9d014Sreyk int flags;
407b1b9d014Sreyk
408b1b9d014Sreyk cu->cu_pv.pv_root_id =
409b1b9d014Sreyk (((u_int64_t)ntohs(cpdu->cbu_rootpri)) << 48) |
410b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[0]) << 40) |
411b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[1]) << 32) |
412b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[2]) << 24) |
413b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[3]) << 16) |
414b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[4]) << 8) |
415b1b9d014Sreyk (((u_int64_t)cpdu->cbu_rootaddr[5]) << 0);
416b1b9d014Sreyk
417b1b9d014Sreyk cu->cu_pv.pv_dbridge_id =
418b1b9d014Sreyk (((u_int64_t)ntohs(cpdu->cbu_bridgepri)) << 48) |
419b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[0]) << 40) |
420b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[1]) << 32) |
421b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[2]) << 24) |
422b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[3]) << 16) |
423b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[4]) << 8) |
424b1b9d014Sreyk (((u_int64_t)cpdu->cbu_bridgeaddr[5]) << 0);
425b1b9d014Sreyk
426b1b9d014Sreyk cu->cu_pv.pv_cost = ntohl(cpdu->cbu_rootpathcost);
427b1b9d014Sreyk cu->cu_message_age = ntohs(cpdu->cbu_messageage);
428b1b9d014Sreyk cu->cu_max_age = ntohs(cpdu->cbu_maxage);
429b1b9d014Sreyk cu->cu_hello_time = ntohs(cpdu->cbu_hellotime);
430b1b9d014Sreyk cu->cu_forward_delay = ntohs(cpdu->cbu_forwarddelay);
431b1b9d014Sreyk cu->cu_pv.pv_dport_id = ntohs(cpdu->cbu_portid);
432b1b9d014Sreyk cu->cu_pv.pv_port_id = bp->bp_port_id;
433b1b9d014Sreyk cu->cu_message_type = cpdu->cbu_bpdutype;
434b1b9d014Sreyk
435b1b9d014Sreyk /* Strip off unused flags in STP mode */
436b1b9d014Sreyk flags = cpdu->cbu_flags;
437b1b9d014Sreyk switch (cpdu->cbu_protover) {
438b1b9d014Sreyk case BSTP_PROTO_STP:
439b1b9d014Sreyk flags &= BSTP_PDU_STPMASK;
440b1b9d014Sreyk /* A STP BPDU explicitly conveys a Designated Port */
441b1b9d014Sreyk cu->cu_role = BSTP_ROLE_DESIGNATED;
442b1b9d014Sreyk break;
443b1b9d014Sreyk case BSTP_PROTO_RSTP:
444b1b9d014Sreyk flags &= BSTP_PDU_RSTPMASK;
445b1b9d014Sreyk break;
4469fa0b7aaSjason }
4479fa0b7aaSjason
448b1b9d014Sreyk cu->cu_topology_change_ack =
449b1b9d014Sreyk (flags & BSTP_PDU_F_TCA) ? 1 : 0;
450b1b9d014Sreyk cu->cu_proposal =
451b1b9d014Sreyk (flags & BSTP_PDU_F_P) ? 1 : 0;
452b1b9d014Sreyk cu->cu_agree =
453b1b9d014Sreyk (flags & BSTP_PDU_F_A) ? 1 : 0;
454b1b9d014Sreyk cu->cu_learning =
455b1b9d014Sreyk (flags & BSTP_PDU_F_L) ? 1 : 0;
456b1b9d014Sreyk cu->cu_forwarding =
457b1b9d014Sreyk (flags & BSTP_PDU_F_F) ? 1 : 0;
458b1b9d014Sreyk cu->cu_topology_change =
459b1b9d014Sreyk (flags & BSTP_PDU_F_TC) ? 1 : 0;
4609fa0b7aaSjason
461b1b9d014Sreyk switch ((flags & BSTP_PDU_PRMASK) >> BSTP_PDU_PRSHIFT) {
462b1b9d014Sreyk case BSTP_PDU_F_ROOT:
463b1b9d014Sreyk cu->cu_role = BSTP_ROLE_ROOT;
464b1b9d014Sreyk break;
465b1b9d014Sreyk case BSTP_PDU_F_ALT:
466b1b9d014Sreyk cu->cu_role = BSTP_ROLE_ALTERNATE;
467b1b9d014Sreyk break;
468b1b9d014Sreyk case BSTP_PDU_F_DESG:
469b1b9d014Sreyk cu->cu_role = BSTP_ROLE_DESIGNATED;
470b1b9d014Sreyk break;
4719fa0b7aaSjason }
4729fa0b7aaSjason }
4739fa0b7aaSjason
4749fa0b7aaSjason void
bstp_send_bpdu(struct bstp_state * bs,struct bstp_port * bp,struct bstp_cbpdu * bpdu)475b1b9d014Sreyk bstp_send_bpdu(struct bstp_state *bs, struct bstp_port *bp,
476b1b9d014Sreyk struct bstp_cbpdu *bpdu)
4779fa0b7aaSjason {
478793c5642Smvs struct ifnet *ifp;
479b1b9d014Sreyk struct mbuf *m;
480b1b9d014Sreyk struct ether_header *eh;
4813bb4b50dSmpi int s;
4829fa0b7aaSjason
483b1b9d014Sreyk s = splnet();
484793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) == NULL)
485b1b9d014Sreyk goto done;
486793c5642Smvs if ((ifp->if_flags & IFF_RUNNING) == 0)
487793c5642Smvs goto rele;
4889fa0b7aaSjason
489b1b9d014Sreyk MGETHDR(m, M_DONTWAIT, MT_DATA);
490b1b9d014Sreyk if (m == NULL)
491793c5642Smvs goto rele;
4929fa0b7aaSjason
493b1b9d014Sreyk eh = mtod(m, struct ether_header *);
494b1b9d014Sreyk
495b1b9d014Sreyk bpdu->cbu_ssap = bpdu->cbu_dsap = LLC_8021D_LSAP;
496b1b9d014Sreyk bpdu->cbu_ctl = LLC_UI;
497b1b9d014Sreyk bpdu->cbu_protoid = htons(BSTP_PROTO_ID);
498b1b9d014Sreyk
499b1b9d014Sreyk memcpy(eh->ether_shost, LLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
500b1b9d014Sreyk memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
501b1b9d014Sreyk
502b1b9d014Sreyk switch (bpdu->cbu_bpdutype) {
503b1b9d014Sreyk case BSTP_MSGTYPE_CFG:
504b1b9d014Sreyk bpdu->cbu_protover = BSTP_PROTO_STP;
505b1b9d014Sreyk m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_STP_LEN;
506b1b9d014Sreyk eh->ether_type = htons(BSTP_BPDU_STP_LEN);
507b1b9d014Sreyk memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
508b1b9d014Sreyk BSTP_BPDU_STP_LEN);
509b1b9d014Sreyk break;
510b1b9d014Sreyk case BSTP_MSGTYPE_RSTP:
511b1b9d014Sreyk bpdu->cbu_protover = BSTP_PROTO_RSTP;
512b1b9d014Sreyk bpdu->cbu_versionlen = htons(0);
513b1b9d014Sreyk m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_RSTP_LEN;
514b1b9d014Sreyk eh->ether_type = htons(BSTP_BPDU_RSTP_LEN);
515b1b9d014Sreyk memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
516b1b9d014Sreyk BSTP_BPDU_RSTP_LEN);
517b1b9d014Sreyk break;
518b1b9d014Sreyk default:
519b1b9d014Sreyk panic("not implemented");
5209fa0b7aaSjason }
521fb492c37Smpi m->m_pkthdr.ph_ifidx = ifp->if_index;
522b1b9d014Sreyk m->m_len = m->m_pkthdr.len;
52333050f9eShenning m->m_pkthdr.pf.prio = BSTP_IFQ_PRIO;
524b1b9d014Sreyk
525b1b9d014Sreyk bp->bp_txcount++;
526c38eb4ffSmpi if_enqueue(ifp, m);
527793c5642Smvs rele:
528793c5642Smvs if_put(ifp);
529b1b9d014Sreyk done:
530b1b9d014Sreyk splx(s);
5319fa0b7aaSjason }
5329fa0b7aaSjason
533b1b9d014Sreyk int
bstp_pdu_flags(struct bstp_port * bp)534b1b9d014Sreyk bstp_pdu_flags(struct bstp_port *bp)
5359fa0b7aaSjason {
536b1b9d014Sreyk int flags = 0;
537b1b9d014Sreyk
538b1b9d014Sreyk if (bp->bp_proposing && bp->bp_state != BSTP_IFSTATE_FORWARDING)
539b1b9d014Sreyk flags |= BSTP_PDU_F_P;
540b1b9d014Sreyk
541b1b9d014Sreyk if (bp->bp_agree)
542b1b9d014Sreyk flags |= BSTP_PDU_F_A;
543b1b9d014Sreyk
544b1b9d014Sreyk if (bp->bp_tc_timer.active)
545b1b9d014Sreyk flags |= BSTP_PDU_F_TC;
546b1b9d014Sreyk
547b1b9d014Sreyk if (bp->bp_tc_ack)
548b1b9d014Sreyk flags |= BSTP_PDU_F_TCA;
549b1b9d014Sreyk
550b1b9d014Sreyk switch (bp->bp_state) {
551b1b9d014Sreyk case BSTP_IFSTATE_LEARNING:
552b1b9d014Sreyk flags |= BSTP_PDU_F_L;
553b1b9d014Sreyk break;
554b1b9d014Sreyk case BSTP_IFSTATE_FORWARDING:
555b1b9d014Sreyk flags |= (BSTP_PDU_F_L | BSTP_PDU_F_F);
556b1b9d014Sreyk break;
5579fa0b7aaSjason }
5589fa0b7aaSjason
559b1b9d014Sreyk switch (bp->bp_role) {
560b1b9d014Sreyk case BSTP_ROLE_ROOT:
561b1b9d014Sreyk flags |= (BSTP_PDU_F_ROOT << BSTP_PDU_PRSHIFT);
562b1b9d014Sreyk break;
563b1b9d014Sreyk case BSTP_ROLE_ALTERNATE:
564b1b9d014Sreyk case BSTP_ROLE_BACKUP:
565b1b9d014Sreyk flags |= (BSTP_PDU_F_ALT << BSTP_PDU_PRSHIFT);
566b1b9d014Sreyk break;
567b1b9d014Sreyk case BSTP_ROLE_DESIGNATED:
568b1b9d014Sreyk flags |= (BSTP_PDU_F_DESG << BSTP_PDU_PRSHIFT);
569b1b9d014Sreyk break;
5709fa0b7aaSjason }
5719fa0b7aaSjason
572b1b9d014Sreyk /* Strip off unused flags in either mode */
573b1b9d014Sreyk switch (bp->bp_protover) {
574b1b9d014Sreyk case BSTP_PROTO_STP:
575b1b9d014Sreyk flags &= BSTP_PDU_STPMASK;
576b1b9d014Sreyk break;
577b1b9d014Sreyk case BSTP_PROTO_RSTP:
578b1b9d014Sreyk flags &= BSTP_PDU_RSTPMASK;
579b1b9d014Sreyk break;
5809fa0b7aaSjason }
581b1b9d014Sreyk return (flags);
5829fa0b7aaSjason }
5839fa0b7aaSjason
584397a64b3Smpf struct mbuf *
bstp_input(struct bstp_state * bs,struct bstp_port * bp,struct ether_header * eh,struct mbuf * m)585b1b9d014Sreyk bstp_input(struct bstp_state *bs, struct bstp_port *bp,
586b1b9d014Sreyk struct ether_header *eh, struct mbuf *m)
5879fa0b7aaSjason {
5889fa0b7aaSjason struct bstp_tbpdu tpdu;
5899fa0b7aaSjason u_int16_t len;
5909fa0b7aaSjason
591b1b9d014Sreyk if (bs == NULL || bp == NULL || bp->bp_active == 0)
592397a64b3Smpf return (m);
5939fa0b7aaSjason
5949fa0b7aaSjason len = ntohs(eh->ether_type);
5959fa0b7aaSjason if (len < sizeof(tpdu))
5969fa0b7aaSjason goto out;
597765f8befSmpi
598765f8befSmpi m_adj(m, ETHER_HDR_LEN);
599765f8befSmpi
6009fa0b7aaSjason if (m->m_pkthdr.len > len)
6019fa0b7aaSjason m_adj(m, len - m->m_pkthdr.len);
6029fa0b7aaSjason if ((m = m_pullup(m, sizeof(tpdu))) == NULL)
6039fa0b7aaSjason goto out;
6049fa0b7aaSjason bcopy(mtod(m, struct tpdu *), &tpdu, sizeof(tpdu));
6059fa0b7aaSjason
6069fa0b7aaSjason if (tpdu.tbu_dsap != LLC_8021D_LSAP ||
6079fa0b7aaSjason tpdu.tbu_ssap != LLC_8021D_LSAP ||
6089fa0b7aaSjason tpdu.tbu_ctl != LLC_UI)
6099fa0b7aaSjason goto out;
610b1b9d014Sreyk if (tpdu.tbu_protoid != BSTP_PROTO_ID)
6119fa0b7aaSjason goto out;
6129fa0b7aaSjason
613b1b9d014Sreyk /*
614b1b9d014Sreyk * We can treat later versions of the PDU as the same as the maximum
615b1b9d014Sreyk * version we implement. All additional parameters/flags are ignored.
616b1b9d014Sreyk */
617b1b9d014Sreyk if (tpdu.tbu_protover > BSTP_PROTO_MAX)
618b1b9d014Sreyk tpdu.tbu_protover = BSTP_PROTO_MAX;
6199fa0b7aaSjason
620b1b9d014Sreyk if (tpdu.tbu_protover != bp->bp_protover) {
621b1b9d014Sreyk /*
622b1b9d014Sreyk * Wait for the migration delay timer to expire before changing
623b1b9d014Sreyk * protocol version to avoid flip-flops.
624b1b9d014Sreyk */
625b1b9d014Sreyk if (bp->bp_flags & BSTP_PORT_CANMIGRATE)
626b1b9d014Sreyk bstp_set_port_proto(bp, tpdu.tbu_protover);
627b1b9d014Sreyk else
6289fa0b7aaSjason goto out;
6299fa0b7aaSjason }
6309fa0b7aaSjason
631b1b9d014Sreyk /* Clear operedge upon receiving a PDU on the port */
632b1b9d014Sreyk bp->bp_operedge = 0;
633b1b9d014Sreyk bstp_timer_start(&bp->bp_edge_delay_timer,
634b1b9d014Sreyk BSTP_DEFAULT_MIGRATE_DELAY);
635b1b9d014Sreyk
636b1b9d014Sreyk switch (tpdu.tbu_protover) {
637b1b9d014Sreyk case BSTP_PROTO_STP:
638b1b9d014Sreyk bstp_received_stp(bs, bp, &m, &tpdu);
639b1b9d014Sreyk break;
640b1b9d014Sreyk case BSTP_PROTO_RSTP:
641b1b9d014Sreyk bstp_received_rstp(bs, bp, &m, &tpdu);
642b1b9d014Sreyk break;
643b1b9d014Sreyk }
6449fa0b7aaSjason out:
6459fa0b7aaSjason m_freem(m);
646397a64b3Smpf return (NULL);
6479fa0b7aaSjason }
6489fa0b7aaSjason
6499fa0b7aaSjason void
bstp_received_stp(struct bstp_state * bs,struct bstp_port * bp,struct mbuf ** mp,struct bstp_tbpdu * tpdu)650b1b9d014Sreyk bstp_received_stp(struct bstp_state *bs, struct bstp_port *bp,
651b1b9d014Sreyk struct mbuf **mp, struct bstp_tbpdu *tpdu)
6529fa0b7aaSjason {
653b1b9d014Sreyk struct bstp_cbpdu cpdu;
654b1b9d014Sreyk struct bstp_config_unit *cu = &bp->bp_msg_cu;
655b1b9d014Sreyk struct bstp_tcn_unit tu;
6569fa0b7aaSjason
657b1b9d014Sreyk switch (tpdu->tbu_bpdutype) {
658b1b9d014Sreyk case BSTP_MSGTYPE_TCN:
659b1b9d014Sreyk tu.tu_message_type = tpdu->tbu_bpdutype;
660b1b9d014Sreyk bstp_received_tcn(bs, bp, &tu);
661b1b9d014Sreyk break;
662b1b9d014Sreyk case BSTP_MSGTYPE_CFG:
663b1b9d014Sreyk if ((*mp)->m_len < BSTP_BPDU_STP_LEN &&
664b1b9d014Sreyk (*mp = m_pullup(*mp, BSTP_BPDU_STP_LEN)) == NULL)
665b1b9d014Sreyk return;
666b1b9d014Sreyk memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_STP_LEN);
6679fa0b7aaSjason
668b1b9d014Sreyk bstp_decode_bpdu(bp, &cpdu, cu);
669b1b9d014Sreyk bstp_received_bpdu(bs, bp, cu);
670b1b9d014Sreyk break;
6719fa0b7aaSjason }
6729fa0b7aaSjason }
6739fa0b7aaSjason
6749fa0b7aaSjason void
bstp_received_rstp(struct bstp_state * bs,struct bstp_port * bp,struct mbuf ** mp,struct bstp_tbpdu * tpdu)675b1b9d014Sreyk bstp_received_rstp(struct bstp_state *bs, struct bstp_port *bp,
676b1b9d014Sreyk struct mbuf **mp, struct bstp_tbpdu *tpdu)
6779fa0b7aaSjason {
678b1b9d014Sreyk struct bstp_cbpdu cpdu;
679b1b9d014Sreyk struct bstp_config_unit *cu = &bp->bp_msg_cu;
680b1b9d014Sreyk
681b1b9d014Sreyk if (tpdu->tbu_bpdutype != BSTP_MSGTYPE_RSTP)
682b1b9d014Sreyk return;
683b1b9d014Sreyk
684b1b9d014Sreyk if ((*mp)->m_len < BSTP_BPDU_RSTP_LEN &&
685b1b9d014Sreyk (*mp = m_pullup(*mp, BSTP_BPDU_RSTP_LEN)) == NULL)
686b1b9d014Sreyk return;
687b1b9d014Sreyk memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_RSTP_LEN);
688b1b9d014Sreyk
689b1b9d014Sreyk bstp_decode_bpdu(bp, &cpdu, cu);
690b1b9d014Sreyk bstp_received_bpdu(bs, bp, cu);
6919fa0b7aaSjason }
6929fa0b7aaSjason
6939fa0b7aaSjason void
bstp_received_tcn(struct bstp_state * bs,struct bstp_port * bp,struct bstp_tcn_unit * tcn)694b1b9d014Sreyk bstp_received_tcn(struct bstp_state *bs, struct bstp_port *bp,
695b1b9d014Sreyk struct bstp_tcn_unit *tcn)
6969fa0b7aaSjason {
697b1b9d014Sreyk bp->bp_rcvdtcn = 1;
698b1b9d014Sreyk bstp_update_tc(bp);
6999fa0b7aaSjason }
7009fa0b7aaSjason
7019fa0b7aaSjason void
bstp_received_bpdu(struct bstp_state * bs,struct bstp_port * bp,struct bstp_config_unit * cu)702b1b9d014Sreyk bstp_received_bpdu(struct bstp_state *bs, struct bstp_port *bp,
703b1b9d014Sreyk struct bstp_config_unit *cu)
7049fa0b7aaSjason {
705b1b9d014Sreyk int type;
7069fa0b7aaSjason
707b1b9d014Sreyk /* We need to have transitioned to INFO_MINE before proceeding */
708b1b9d014Sreyk switch (bp->bp_infois) {
709b1b9d014Sreyk case BSTP_INFO_DISABLED:
710b1b9d014Sreyk case BSTP_INFO_AGED:
711b1b9d014Sreyk return;
7129fa0b7aaSjason }
7139fa0b7aaSjason
714b1b9d014Sreyk type = bstp_pdu_rcvtype(bp, cu);
715b1b9d014Sreyk
716b1b9d014Sreyk switch (type) {
717b1b9d014Sreyk case BSTP_PDU_SUPERIOR:
718b1b9d014Sreyk bs->bs_allsynced = 0;
719b1b9d014Sreyk bp->bp_agreed = 0;
720b1b9d014Sreyk bp->bp_proposing = 0;
721b1b9d014Sreyk
722b1b9d014Sreyk if (cu->cu_proposal && cu->cu_forwarding == 0)
723b1b9d014Sreyk bp->bp_proposed = 1;
724b1b9d014Sreyk if (cu->cu_topology_change)
725b1b9d014Sreyk bp->bp_rcvdtc = 1;
726b1b9d014Sreyk if (cu->cu_topology_change_ack)
727b1b9d014Sreyk bp->bp_rcvdtca = 1;
728b1b9d014Sreyk
729b1b9d014Sreyk if (bp->bp_agree &&
730af52927aSmartynas !bstp_pdu_bettersame(bp, BSTP_INFO_RECEIVED))
731b1b9d014Sreyk bp->bp_agree = 0;
732b1b9d014Sreyk
733b1b9d014Sreyk /* copy the received priority and timers to the port */
734b1b9d014Sreyk bp->bp_port_pv = cu->cu_pv;
735b1b9d014Sreyk bp->bp_port_msg_age = cu->cu_message_age;
736b1b9d014Sreyk bp->bp_port_max_age = cu->cu_max_age;
737b1b9d014Sreyk bp->bp_port_fdelay = cu->cu_forward_delay;
738b1b9d014Sreyk bp->bp_port_htime =
739b1b9d014Sreyk (cu->cu_hello_time > BSTP_MIN_HELLO_TIME ?
740b1b9d014Sreyk cu->cu_hello_time : BSTP_MIN_HELLO_TIME);
741b1b9d014Sreyk
742b1b9d014Sreyk /* set expiry for the new info */
743b1b9d014Sreyk bstp_set_timer_msgage(bp);
744b1b9d014Sreyk
745af52927aSmartynas bp->bp_infois = BSTP_INFO_RECEIVED;
746b1b9d014Sreyk bstp_assign_roles(bs);
747b1b9d014Sreyk break;
748b1b9d014Sreyk
749b1b9d014Sreyk case BSTP_PDU_REPEATED:
750b1b9d014Sreyk if (cu->cu_proposal && cu->cu_forwarding == 0)
751b1b9d014Sreyk bp->bp_proposed = 1;
752b1b9d014Sreyk if (cu->cu_topology_change)
753b1b9d014Sreyk bp->bp_rcvdtc = 1;
754b1b9d014Sreyk if (cu->cu_topology_change_ack)
755b1b9d014Sreyk bp->bp_rcvdtca = 1;
756b1b9d014Sreyk
757b1b9d014Sreyk /* rearm the age timer */
758b1b9d014Sreyk bstp_set_timer_msgage(bp);
759b1b9d014Sreyk break;
760b1b9d014Sreyk
761b1b9d014Sreyk case BSTP_PDU_INFERIOR:
762b1b9d014Sreyk if (cu->cu_learning) {
763b1b9d014Sreyk bp->bp_agreed = 1;
764b1b9d014Sreyk bp->bp_proposing = 0;
7659fa0b7aaSjason }
766b1b9d014Sreyk break;
767b1b9d014Sreyk
768b1b9d014Sreyk case BSTP_PDU_INFERIORALT:
769b1b9d014Sreyk /*
770b1b9d014Sreyk * only point to point links are allowed fast
771b1b9d014Sreyk * transitions to forwarding.
772b1b9d014Sreyk */
773ff7746e3Sreyk if (cu->cu_agree && bp->bp_ptp_link) {
774b1b9d014Sreyk bp->bp_agreed = 1;
775b1b9d014Sreyk bp->bp_proposing = 0;
776b1b9d014Sreyk } else
777b1b9d014Sreyk bp->bp_agreed = 0;
778b1b9d014Sreyk
779b1b9d014Sreyk if (cu->cu_topology_change)
780b1b9d014Sreyk bp->bp_rcvdtc = 1;
781b1b9d014Sreyk if (cu->cu_topology_change_ack)
782b1b9d014Sreyk bp->bp_rcvdtca = 1;
783b1b9d014Sreyk break;
784b1b9d014Sreyk
785b1b9d014Sreyk case BSTP_PDU_OTHER:
786b1b9d014Sreyk return; /* do nothing */
787b1b9d014Sreyk }
788b1b9d014Sreyk
789b1b9d014Sreyk /* update the state machines with the new data */
790b1b9d014Sreyk bstp_update_state(bs, bp);
7919fa0b7aaSjason }
7929fa0b7aaSjason
7939fa0b7aaSjason int
bstp_pdu_rcvtype(struct bstp_port * bp,struct bstp_config_unit * cu)794b1b9d014Sreyk bstp_pdu_rcvtype(struct bstp_port *bp, struct bstp_config_unit *cu)
7959fa0b7aaSjason {
796b1b9d014Sreyk int type;
7979fa0b7aaSjason
798b1b9d014Sreyk /* default return type */
799b1b9d014Sreyk type = BSTP_PDU_OTHER;
8009fa0b7aaSjason
801b1b9d014Sreyk switch (cu->cu_role) {
802b1b9d014Sreyk case BSTP_ROLE_DESIGNATED:
803b1b9d014Sreyk if (bstp_info_superior(&bp->bp_port_pv, &cu->cu_pv))
804b1b9d014Sreyk /* bpdu priority is superior */
805b1b9d014Sreyk type = BSTP_PDU_SUPERIOR;
806b1b9d014Sreyk else if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) ==
807b1b9d014Sreyk INFO_SAME) {
808b1b9d014Sreyk if (bp->bp_port_msg_age != cu->cu_message_age ||
809b1b9d014Sreyk bp->bp_port_max_age != cu->cu_max_age ||
810b1b9d014Sreyk bp->bp_port_fdelay != cu->cu_forward_delay ||
811b1b9d014Sreyk bp->bp_port_htime != cu->cu_hello_time)
812b1b9d014Sreyk /* bpdu priority is equal and timers differ */
813b1b9d014Sreyk type = BSTP_PDU_SUPERIOR;
814b1b9d014Sreyk else
815b1b9d014Sreyk /* bpdu is equal */
816b1b9d014Sreyk type = BSTP_PDU_REPEATED;
817b1b9d014Sreyk } else
818b1b9d014Sreyk /* bpdu priority is worse */
819b1b9d014Sreyk type = BSTP_PDU_INFERIOR;
820b1b9d014Sreyk
821b1b9d014Sreyk break;
822b1b9d014Sreyk
823b1b9d014Sreyk case BSTP_ROLE_ROOT:
824b1b9d014Sreyk case BSTP_ROLE_ALTERNATE:
825b1b9d014Sreyk case BSTP_ROLE_BACKUP:
826b1b9d014Sreyk if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) <= INFO_SAME)
827b1b9d014Sreyk /*
828b1b9d014Sreyk * not a designated port and priority is the same or
829b1b9d014Sreyk * worse
830b1b9d014Sreyk */
831b1b9d014Sreyk type = BSTP_PDU_INFERIORALT;
832b1b9d014Sreyk break;
8339fa0b7aaSjason }
834b1b9d014Sreyk
835b1b9d014Sreyk return (type);
836b1b9d014Sreyk }
837b1b9d014Sreyk
838b1b9d014Sreyk int
bstp_pdu_bettersame(struct bstp_port * bp,int newinfo)839b1b9d014Sreyk bstp_pdu_bettersame(struct bstp_port *bp, int newinfo)
840b1b9d014Sreyk {
841af52927aSmartynas if (newinfo == BSTP_INFO_RECEIVED &&
842af52927aSmartynas bp->bp_infois == BSTP_INFO_RECEIVED &&
843b1b9d014Sreyk bstp_info_cmp(&bp->bp_port_pv, &bp->bp_msg_cu.cu_pv) >= INFO_SAME)
844b1b9d014Sreyk return (1);
845b1b9d014Sreyk
846b1b9d014Sreyk if (newinfo == BSTP_INFO_MINE &&
847b1b9d014Sreyk bp->bp_infois == BSTP_INFO_MINE &&
848b1b9d014Sreyk bstp_info_cmp(&bp->bp_port_pv, &bp->bp_desg_pv) >= INFO_SAME)
849b1b9d014Sreyk return (1);
850b1b9d014Sreyk
851b1b9d014Sreyk return (0);
852b1b9d014Sreyk }
853b1b9d014Sreyk
854b1b9d014Sreyk int
bstp_info_cmp(struct bstp_pri_vector * pv,struct bstp_pri_vector * cpv)855b1b9d014Sreyk bstp_info_cmp(struct bstp_pri_vector *pv,
856b1b9d014Sreyk struct bstp_pri_vector *cpv)
857b1b9d014Sreyk {
858b1b9d014Sreyk if (cpv->pv_root_id < pv->pv_root_id)
859b1b9d014Sreyk return (INFO_BETTER);
860b1b9d014Sreyk if (cpv->pv_root_id > pv->pv_root_id)
861b1b9d014Sreyk return (INFO_WORSE);
862b1b9d014Sreyk
863b1b9d014Sreyk if (cpv->pv_cost < pv->pv_cost)
864b1b9d014Sreyk return (INFO_BETTER);
865b1b9d014Sreyk if (cpv->pv_cost > pv->pv_cost)
866b1b9d014Sreyk return (INFO_WORSE);
867b1b9d014Sreyk
868b1b9d014Sreyk if (cpv->pv_dbridge_id < pv->pv_dbridge_id)
869b1b9d014Sreyk return (INFO_BETTER);
870b1b9d014Sreyk if (cpv->pv_dbridge_id > pv->pv_dbridge_id)
871b1b9d014Sreyk return (INFO_WORSE);
872b1b9d014Sreyk
873b1b9d014Sreyk if (cpv->pv_dport_id < pv->pv_dport_id)
874b1b9d014Sreyk return (INFO_BETTER);
875b1b9d014Sreyk if (cpv->pv_dport_id > pv->pv_dport_id)
876b1b9d014Sreyk return (INFO_WORSE);
877b1b9d014Sreyk
878b1b9d014Sreyk return (INFO_SAME);
879b1b9d014Sreyk }
880b1b9d014Sreyk
881b1b9d014Sreyk /*
882b1b9d014Sreyk * This message priority vector is superior to the port priority vector and
883b1b9d014Sreyk * will replace it if, and only if, the message priority vector is better than
884b1b9d014Sreyk * the port priority vector, or the message has been transmitted from the same
885b1b9d014Sreyk * designated bridge and designated port as the port priority vector.
886b1b9d014Sreyk */
887b1b9d014Sreyk int
bstp_info_superior(struct bstp_pri_vector * pv,struct bstp_pri_vector * cpv)888b1b9d014Sreyk bstp_info_superior(struct bstp_pri_vector *pv,
889b1b9d014Sreyk struct bstp_pri_vector *cpv)
890b1b9d014Sreyk {
891b1b9d014Sreyk if (bstp_info_cmp(pv, cpv) == INFO_BETTER ||
892b1b9d014Sreyk (bstp_same_bridgeid(pv->pv_dbridge_id, cpv->pv_dbridge_id) &&
893b1b9d014Sreyk (cpv->pv_dport_id & 0xfff) == (pv->pv_dport_id & 0xfff)))
894b1b9d014Sreyk return (1);
8959fa0b7aaSjason return (0);
8969fa0b7aaSjason }
8979fa0b7aaSjason
8989fa0b7aaSjason void
bstp_reset(struct bstp_state * bs)899e380692eSmpi bstp_reset(struct bstp_state *bs)
9009fa0b7aaSjason {
901b1b9d014Sreyk /* default to our priority vector */
902b1b9d014Sreyk bs->bs_root_pv = bs->bs_bridge_pv;
903b1b9d014Sreyk bs->bs_root_msg_age = 0;
904b1b9d014Sreyk bs->bs_root_max_age = bs->bs_bridge_max_age;
905b1b9d014Sreyk bs->bs_root_fdelay = bs->bs_bridge_fdelay;
906b1b9d014Sreyk bs->bs_root_htime = bs->bs_bridge_htime;
907b1b9d014Sreyk bs->bs_root_port = NULL;
908e380692eSmpi }
909e380692eSmpi
910e380692eSmpi void
bstp_assign_roles(struct bstp_state * bs)911e380692eSmpi bstp_assign_roles(struct bstp_state *bs)
912e380692eSmpi {
913e380692eSmpi struct bstp_port *bp, *rbp = NULL;
914e380692eSmpi struct bstp_pri_vector pv;
915e380692eSmpi
916e380692eSmpi bstp_reset(bs);
917b1b9d014Sreyk
918af52927aSmartynas /* check if any received info supersedes us */
919b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
920af52927aSmartynas if (bp->bp_infois != BSTP_INFO_RECEIVED)
921b1b9d014Sreyk continue;
922b1b9d014Sreyk
923b1b9d014Sreyk pv = bp->bp_port_pv;
924b1b9d014Sreyk pv.pv_cost += bp->bp_path_cost;
925b1b9d014Sreyk
926b1b9d014Sreyk /*
927b1b9d014Sreyk * The root priority vector is the best of the set comprising
928b1b9d014Sreyk * the bridge priority vector plus all root path priority
929b1b9d014Sreyk * vectors whose bridge address is not equal to us.
930b1b9d014Sreyk */
931b1b9d014Sreyk if (bstp_same_bridgeid(pv.pv_dbridge_id,
932b1b9d014Sreyk bs->bs_bridge_pv.pv_dbridge_id) == 0 &&
933b1b9d014Sreyk bstp_info_cmp(&bs->bs_root_pv, &pv) == INFO_BETTER) {
934b1b9d014Sreyk /* the port vector replaces the root */
935b1b9d014Sreyk bs->bs_root_pv = pv;
936b1b9d014Sreyk bs->bs_root_msg_age = bp->bp_port_msg_age +
937b1b9d014Sreyk BSTP_MESSAGE_AGE_INCR;
938b1b9d014Sreyk bs->bs_root_max_age = bp->bp_port_max_age;
939b1b9d014Sreyk bs->bs_root_fdelay = bp->bp_port_fdelay;
940b1b9d014Sreyk bs->bs_root_htime = bp->bp_port_htime;
941b1b9d014Sreyk rbp = bp;
942b1b9d014Sreyk }
943b1b9d014Sreyk }
944b1b9d014Sreyk
945b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
946b1b9d014Sreyk /* calculate the port designated vector */
947b1b9d014Sreyk bp->bp_desg_pv.pv_root_id = bs->bs_root_pv.pv_root_id;
948b1b9d014Sreyk bp->bp_desg_pv.pv_cost = bs->bs_root_pv.pv_cost;
949b1b9d014Sreyk bp->bp_desg_pv.pv_dbridge_id = bs->bs_bridge_pv.pv_dbridge_id;
950b1b9d014Sreyk bp->bp_desg_pv.pv_dport_id = bp->bp_port_id;
951b1b9d014Sreyk bp->bp_desg_pv.pv_port_id = bp->bp_port_id;
952b1b9d014Sreyk
953b1b9d014Sreyk /* calculate designated times */
954b1b9d014Sreyk bp->bp_desg_msg_age = bs->bs_root_msg_age;
955b1b9d014Sreyk bp->bp_desg_max_age = bs->bs_root_max_age;
956b1b9d014Sreyk bp->bp_desg_fdelay = bs->bs_root_fdelay;
957b1b9d014Sreyk bp->bp_desg_htime = bs->bs_bridge_htime;
958b1b9d014Sreyk
959b1b9d014Sreyk
960b1b9d014Sreyk switch (bp->bp_infois) {
961b1b9d014Sreyk case BSTP_INFO_DISABLED:
962b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
963b1b9d014Sreyk break;
964b1b9d014Sreyk
965b1b9d014Sreyk case BSTP_INFO_AGED:
966b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
967b1b9d014Sreyk bstp_update_info(bp);
968b1b9d014Sreyk break;
969b1b9d014Sreyk
970b1b9d014Sreyk case BSTP_INFO_MINE:
971b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
972b1b9d014Sreyk /* update the port info if stale */
973b1b9d014Sreyk if (bstp_info_cmp(&bp->bp_port_pv,
974b1b9d014Sreyk &bp->bp_desg_pv) != INFO_SAME ||
975b1b9d014Sreyk (rbp != NULL &&
976b1b9d014Sreyk (bp->bp_port_msg_age != rbp->bp_port_msg_age ||
977b1b9d014Sreyk bp->bp_port_max_age != rbp->bp_port_max_age ||
978b1b9d014Sreyk bp->bp_port_fdelay != rbp->bp_port_fdelay ||
979b1b9d014Sreyk bp->bp_port_htime != rbp->bp_port_htime)))
980b1b9d014Sreyk bstp_update_info(bp);
981b1b9d014Sreyk break;
982b1b9d014Sreyk
983af52927aSmartynas case BSTP_INFO_RECEIVED:
984b1b9d014Sreyk if (bp == rbp) {
985b1b9d014Sreyk /*
986b1b9d014Sreyk * root priority is derived from this
987b1b9d014Sreyk * port, make it the root port.
988b1b9d014Sreyk */
989b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_ROOT);
990b1b9d014Sreyk bs->bs_root_port = bp;
991b1b9d014Sreyk } else if (bstp_info_cmp(&bp->bp_port_pv,
992b1b9d014Sreyk &bp->bp_desg_pv) == INFO_BETTER) {
993b1b9d014Sreyk /*
994b1b9d014Sreyk * the port priority is lower than the root
995b1b9d014Sreyk * port.
996b1b9d014Sreyk */
997b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
998b1b9d014Sreyk bstp_update_info(bp);
999b1b9d014Sreyk } else {
1000b1b9d014Sreyk if (bstp_same_bridgeid(
1001b1b9d014Sreyk bp->bp_port_pv.pv_dbridge_id,
1002b1b9d014Sreyk bs->bs_bridge_pv.pv_dbridge_id)) {
1003b1b9d014Sreyk /*
1004b1b9d014Sreyk * the designated bridge refers to
1005b1b9d014Sreyk * another port on this bridge.
1006b1b9d014Sreyk */
1007b1b9d014Sreyk bstp_set_port_role(bp,
1008b1b9d014Sreyk BSTP_ROLE_BACKUP);
1009b1b9d014Sreyk } else {
1010b1b9d014Sreyk /*
1011b1b9d014Sreyk * the port is an inferior path to the
1012b1b9d014Sreyk * root bridge.
1013b1b9d014Sreyk */
1014b1b9d014Sreyk bstp_set_port_role(bp,
1015b1b9d014Sreyk BSTP_ROLE_ALTERNATE);
1016b1b9d014Sreyk }
1017b1b9d014Sreyk }
1018b1b9d014Sreyk break;
1019b1b9d014Sreyk }
1020b1b9d014Sreyk }
10219fa0b7aaSjason }
10229fa0b7aaSjason
10239fa0b7aaSjason void
bstp_update_state(struct bstp_state * bs,struct bstp_port * bp)1024b1b9d014Sreyk bstp_update_state(struct bstp_state *bs, struct bstp_port *bp)
10259fa0b7aaSjason {
1026b1b9d014Sreyk struct bstp_port *bp2;
1027b1b9d014Sreyk int synced;
1028b1b9d014Sreyk
102900cc46d5Skrw /* check if all the ports have synchronized again */
1030b1b9d014Sreyk if (!bs->bs_allsynced) {
1031b1b9d014Sreyk synced = 1;
1032b1b9d014Sreyk LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
1033e2ba2654Shenning if (!(bp2->bp_synced ||
1034e2ba2654Shenning bp2->bp_role == BSTP_ROLE_ROOT)) {
1035b1b9d014Sreyk synced = 0;
1036b1b9d014Sreyk break;
1037b1b9d014Sreyk }
1038b1b9d014Sreyk }
1039b1b9d014Sreyk bs->bs_allsynced = synced;
1040b1b9d014Sreyk }
1041b1b9d014Sreyk
1042b1b9d014Sreyk bstp_update_roles(bs, bp);
1043b1b9d014Sreyk bstp_update_tc(bp);
10449fa0b7aaSjason }
10459fa0b7aaSjason
10469fa0b7aaSjason void
bstp_update_roles(struct bstp_state * bs,struct bstp_port * bp)1047b1b9d014Sreyk bstp_update_roles(struct bstp_state *bs, struct bstp_port *bp)
10489fa0b7aaSjason {
1049b1b9d014Sreyk switch (bp->bp_role) {
1050b1b9d014Sreyk case BSTP_ROLE_DISABLED:
1051b1b9d014Sreyk /* Clear any flags if set */
1052b1b9d014Sreyk if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
1053b1b9d014Sreyk bp->bp_sync = 0;
1054b1b9d014Sreyk bp->bp_synced = 1;
1055b1b9d014Sreyk bp->bp_reroot = 0;
1056b1b9d014Sreyk }
1057b1b9d014Sreyk break;
1058b1b9d014Sreyk
1059b1b9d014Sreyk case BSTP_ROLE_ALTERNATE:
1060b1b9d014Sreyk case BSTP_ROLE_BACKUP:
1061b1b9d014Sreyk if ((bs->bs_allsynced && !bp->bp_agree) ||
1062b1b9d014Sreyk (bp->bp_proposed && bp->bp_agree)) {
1063b1b9d014Sreyk bp->bp_proposed = 0;
1064b1b9d014Sreyk bp->bp_agree = 1;
1065b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1066793c5642Smvs DPRINTF(bp, "-> ALTERNATE_AGREED\n");
1067b1b9d014Sreyk }
1068b1b9d014Sreyk
1069b1b9d014Sreyk if (bp->bp_proposed && !bp->bp_agree) {
1070b1b9d014Sreyk bstp_set_all_sync(bs);
1071b1b9d014Sreyk bp->bp_proposed = 0;
1072793c5642Smvs DPRINTF(bp, "-> ALTERNATE_PROPOSED\n");
1073b1b9d014Sreyk }
1074b1b9d014Sreyk
1075b1b9d014Sreyk /* Clear any flags if set */
1076b1b9d014Sreyk if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
1077b1b9d014Sreyk bp->bp_sync = 0;
1078b1b9d014Sreyk bp->bp_synced = 1;
1079b1b9d014Sreyk bp->bp_reroot = 0;
1080793c5642Smvs DPRINTF(bp, "-> ALTERNATE_PORT\n");
1081b1b9d014Sreyk }
1082b1b9d014Sreyk break;
1083b1b9d014Sreyk
1084b1b9d014Sreyk case BSTP_ROLE_ROOT:
1085b1b9d014Sreyk if (bp->bp_state != BSTP_IFSTATE_FORWARDING && !bp->bp_reroot) {
1086b1b9d014Sreyk bstp_set_all_reroot(bs);
1087793c5642Smvs DPRINTF(bp, "-> ROOT_REROOT\n");
1088b1b9d014Sreyk }
1089b1b9d014Sreyk
1090b1b9d014Sreyk if ((bs->bs_allsynced && !bp->bp_agree) ||
1091b1b9d014Sreyk (bp->bp_proposed && bp->bp_agree)) {
1092b1b9d014Sreyk bp->bp_proposed = 0;
1093b1b9d014Sreyk bp->bp_sync = 0;
1094b1b9d014Sreyk bp->bp_agree = 1;
1095b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1096793c5642Smvs DPRINTF(bp, "-> ROOT_AGREED\n");
1097b1b9d014Sreyk }
1098b1b9d014Sreyk
1099b1b9d014Sreyk if (bp->bp_proposed && !bp->bp_agree) {
1100b1b9d014Sreyk bstp_set_all_sync(bs);
1101b1b9d014Sreyk bp->bp_proposed = 0;
1102793c5642Smvs DPRINTF(bp, "-> ROOT_PROPOSED\n");
1103b1b9d014Sreyk }
1104b1b9d014Sreyk
1105b1b9d014Sreyk if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1106b1b9d014Sreyk (bp->bp_forward_delay_timer.active == 0 ||
1107b1b9d014Sreyk (bstp_rerooted(bs, bp) &&
1108b1b9d014Sreyk bp->bp_recent_backup_timer.active == 0 &&
1109b1b9d014Sreyk bp->bp_protover == BSTP_PROTO_RSTP))) {
1110b1b9d014Sreyk switch (bp->bp_state) {
1111b1b9d014Sreyk case BSTP_IFSTATE_DISCARDING:
1112b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
1113b1b9d014Sreyk break;
1114b1b9d014Sreyk case BSTP_IFSTATE_LEARNING:
1115b1b9d014Sreyk bstp_set_port_state(bp,
1116b1b9d014Sreyk BSTP_IFSTATE_FORWARDING);
1117b1b9d014Sreyk break;
1118b1b9d014Sreyk }
1119b1b9d014Sreyk }
1120b1b9d014Sreyk
1121b1b9d014Sreyk if (bp->bp_state == BSTP_IFSTATE_FORWARDING && bp->bp_reroot) {
1122b1b9d014Sreyk bp->bp_reroot = 0;
1123793c5642Smvs DPRINTF(bp, "-> ROOT_REROOTED\n");
1124b1b9d014Sreyk }
1125b1b9d014Sreyk break;
1126b1b9d014Sreyk
1127b1b9d014Sreyk case BSTP_ROLE_DESIGNATED:
1128b1b9d014Sreyk if (bp->bp_recent_root_timer.active == 0 && bp->bp_reroot) {
1129b1b9d014Sreyk bp->bp_reroot = 0;
1130793c5642Smvs DPRINTF(bp, "-> DESIGNATED_RETIRED\n");
1131b1b9d014Sreyk }
1132b1b9d014Sreyk
1133b1b9d014Sreyk if ((bp->bp_state == BSTP_IFSTATE_DISCARDING &&
1134b1b9d014Sreyk !bp->bp_synced) || (bp->bp_agreed && !bp->bp_synced) ||
1135b1b9d014Sreyk (bp->bp_operedge && !bp->bp_synced) ||
1136b1b9d014Sreyk (bp->bp_sync && bp->bp_synced)) {
1137b1b9d014Sreyk bstp_timer_stop(&bp->bp_recent_root_timer);
1138b1b9d014Sreyk bp->bp_synced = 1;
1139b1b9d014Sreyk bp->bp_sync = 0;
1140793c5642Smvs DPRINTF(bp, "-> DESIGNATED_SYNCED\n");
1141b1b9d014Sreyk }
1142b1b9d014Sreyk
1143b1b9d014Sreyk if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1144b1b9d014Sreyk !bp->bp_agreed && !bp->bp_proposing &&
1145b1b9d014Sreyk !bp->bp_operedge) {
1146b1b9d014Sreyk bp->bp_proposing = 1;
1147b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1148b1b9d014Sreyk bstp_timer_start(&bp->bp_edge_delay_timer,
1149ff7746e3Sreyk (bp->bp_ptp_link ? BSTP_DEFAULT_MIGRATE_DELAY :
1150b1b9d014Sreyk bp->bp_desg_max_age));
1151793c5642Smvs DPRINTF(bp, "-> DESIGNATED_PROPOSE\n");
1152b1b9d014Sreyk }
1153b1b9d014Sreyk
1154b1b9d014Sreyk if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1155b1b9d014Sreyk (bp->bp_forward_delay_timer.active == 0 || bp->bp_agreed ||
1156b1b9d014Sreyk bp->bp_operedge) &&
1157b1b9d014Sreyk (bp->bp_recent_root_timer.active == 0 || !bp->bp_reroot) &&
1158b1b9d014Sreyk !bp->bp_sync) {
1159b1b9d014Sreyk if (bp->bp_agreed)
1160793c5642Smvs DPRINTF(bp, "-> AGREED\n");
1161b1b9d014Sreyk /*
1162b1b9d014Sreyk * If agreed|operedge then go straight to forwarding,
1163b1b9d014Sreyk * otherwise follow discard -> learn -> forward.
1164b1b9d014Sreyk */
1165b1b9d014Sreyk if (bp->bp_agreed || bp->bp_operedge ||
1166b1b9d014Sreyk bp->bp_state == BSTP_IFSTATE_LEARNING) {
1167b1b9d014Sreyk bstp_set_port_state(bp,
1168b1b9d014Sreyk BSTP_IFSTATE_FORWARDING);
1169b1b9d014Sreyk bp->bp_agreed = bp->bp_protover;
1170b1b9d014Sreyk } else if (bp->bp_state == BSTP_IFSTATE_DISCARDING)
1171b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
1172b1b9d014Sreyk }
1173b1b9d014Sreyk
1174b1b9d014Sreyk if (((bp->bp_sync && !bp->bp_synced) ||
1175b1b9d014Sreyk (bp->bp_reroot && bp->bp_recent_root_timer.active) ||
1176b1b9d014Sreyk (bp->bp_flags & BSTP_PORT_DISPUTED)) && !bp->bp_operedge &&
1177b1b9d014Sreyk bp->bp_state != BSTP_IFSTATE_DISCARDING) {
1178b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1179b1b9d014Sreyk bp->bp_flags &= ~BSTP_PORT_DISPUTED;
1180b1b9d014Sreyk bstp_timer_start(&bp->bp_forward_delay_timer,
1181b1b9d014Sreyk bp->bp_protover == BSTP_PROTO_RSTP ?
1182b1b9d014Sreyk bp->bp_desg_htime : bp->bp_desg_fdelay);
1183793c5642Smvs DPRINTF(bp, "-> DESIGNATED_DISCARD\n");
1184b1b9d014Sreyk }
1185b1b9d014Sreyk break;
1186b1b9d014Sreyk }
1187b1b9d014Sreyk
1188b1b9d014Sreyk if (bp->bp_flags & BSTP_PORT_NEWINFO)
1189b1b9d014Sreyk bstp_transmit(bs, bp);
11909fa0b7aaSjason }
11919fa0b7aaSjason
11929fa0b7aaSjason void
bstp_update_tc(struct bstp_port * bp)1193b1b9d014Sreyk bstp_update_tc(struct bstp_port *bp)
11949fa0b7aaSjason {
1195b1b9d014Sreyk switch (bp->bp_tcstate) {
1196b1b9d014Sreyk case BSTP_TCSTATE_ACTIVE:
1197b1b9d014Sreyk if ((bp->bp_role != BSTP_ROLE_DESIGNATED &&
1198b1b9d014Sreyk bp->bp_role != BSTP_ROLE_ROOT) || bp->bp_operedge)
1199b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
12009fa0b7aaSjason
1201b1b9d014Sreyk if (bp->bp_rcvdtcn)
1202b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_TCN);
1203b1b9d014Sreyk if (bp->bp_rcvdtc)
1204b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_TC);
12059fa0b7aaSjason
1206b1b9d014Sreyk if (bp->bp_tc_prop && !bp->bp_operedge)
1207b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_PROPAG);
1208b1b9d014Sreyk
1209b1b9d014Sreyk if (bp->bp_rcvdtca)
1210b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_ACK);
1211b1b9d014Sreyk break;
1212b1b9d014Sreyk
1213b1b9d014Sreyk case BSTP_TCSTATE_INACTIVE:
1214b1b9d014Sreyk if ((bp->bp_state == BSTP_IFSTATE_LEARNING ||
1215b1b9d014Sreyk bp->bp_state == BSTP_IFSTATE_FORWARDING) &&
1216b1b9d014Sreyk bp->bp_fdbflush == 0)
1217b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
1218b1b9d014Sreyk break;
1219b1b9d014Sreyk
1220b1b9d014Sreyk case BSTP_TCSTATE_LEARNING:
1221b1b9d014Sreyk if (bp->bp_rcvdtc || bp->bp_rcvdtcn || bp->bp_rcvdtca ||
1222b1b9d014Sreyk bp->bp_tc_prop)
1223b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
1224b1b9d014Sreyk else if (bp->bp_role != BSTP_ROLE_DESIGNATED &&
1225b1b9d014Sreyk bp->bp_role != BSTP_ROLE_ROOT &&
1226b1b9d014Sreyk bp->bp_state == BSTP_IFSTATE_DISCARDING)
1227b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
1228b1b9d014Sreyk
1229b1b9d014Sreyk if ((bp->bp_role == BSTP_ROLE_DESIGNATED ||
1230b1b9d014Sreyk bp->bp_role == BSTP_ROLE_ROOT) &&
1231b1b9d014Sreyk bp->bp_state == BSTP_IFSTATE_FORWARDING &&
1232b1b9d014Sreyk !bp->bp_operedge)
1233b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_DETECTED);
1234b1b9d014Sreyk break;
1235b1b9d014Sreyk
1236b1b9d014Sreyk /* these are transient states and go straight back to ACTIVE */
1237b1b9d014Sreyk case BSTP_TCSTATE_DETECTED:
1238b1b9d014Sreyk case BSTP_TCSTATE_TCN:
1239b1b9d014Sreyk case BSTP_TCSTATE_TC:
1240b1b9d014Sreyk case BSTP_TCSTATE_PROPAG:
1241b1b9d014Sreyk case BSTP_TCSTATE_ACK:
1242793c5642Smvs DPRINTF(bp, "Invalid TC state\n");
1243b1b9d014Sreyk break;
1244b1b9d014Sreyk }
1245b1b9d014Sreyk
1246b1b9d014Sreyk }
1247b1b9d014Sreyk
1248b1b9d014Sreyk void
bstp_update_info(struct bstp_port * bp)1249b1b9d014Sreyk bstp_update_info(struct bstp_port *bp)
1250b1b9d014Sreyk {
1251b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
1252b1b9d014Sreyk
1253b1b9d014Sreyk bp->bp_proposing = 0;
1254b1b9d014Sreyk bp->bp_proposed = 0;
1255b1b9d014Sreyk
1256b1b9d014Sreyk if (bp->bp_agreed && !bstp_pdu_bettersame(bp, BSTP_INFO_MINE))
1257b1b9d014Sreyk bp->bp_agreed = 0;
1258b1b9d014Sreyk
1259b1b9d014Sreyk if (bp->bp_synced && !bp->bp_agreed) {
1260b1b9d014Sreyk bp->bp_synced = 0;
1261b1b9d014Sreyk bs->bs_allsynced = 0;
1262b1b9d014Sreyk }
1263b1b9d014Sreyk
1264b1b9d014Sreyk /* copy the designated pv to the port */
1265b1b9d014Sreyk bp->bp_port_pv = bp->bp_desg_pv;
1266b1b9d014Sreyk bp->bp_port_msg_age = bp->bp_desg_msg_age;
1267b1b9d014Sreyk bp->bp_port_max_age = bp->bp_desg_max_age;
1268b1b9d014Sreyk bp->bp_port_fdelay = bp->bp_desg_fdelay;
1269b1b9d014Sreyk bp->bp_port_htime = bp->bp_desg_htime;
1270b1b9d014Sreyk bp->bp_infois = BSTP_INFO_MINE;
1271b1b9d014Sreyk
1272b1b9d014Sreyk /* Set transmit flag but do not immediately send */
1273b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1274b1b9d014Sreyk }
1275b1b9d014Sreyk
1276b1b9d014Sreyk /* set tcprop on every port other than the caller */
1277b1b9d014Sreyk void
bstp_set_other_tcprop(struct bstp_port * bp)1278b1b9d014Sreyk bstp_set_other_tcprop(struct bstp_port *bp)
1279b1b9d014Sreyk {
1280b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
1281b1b9d014Sreyk struct bstp_port *bp2;
1282b1b9d014Sreyk
1283b1b9d014Sreyk LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
1284b1b9d014Sreyk if (bp2 == bp)
12859fa0b7aaSjason continue;
12860205ac8aSreyk bp2->bp_tc_prop = 1;
12879fa0b7aaSjason }
12889fa0b7aaSjason }
1289b1b9d014Sreyk
1290b1b9d014Sreyk void
bstp_set_all_reroot(struct bstp_state * bs)1291b1b9d014Sreyk bstp_set_all_reroot(struct bstp_state *bs)
1292b1b9d014Sreyk {
1293b1b9d014Sreyk struct bstp_port *bp;
1294b1b9d014Sreyk
1295b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
1296b1b9d014Sreyk bp->bp_reroot = 1;
1297b1b9d014Sreyk }
1298b1b9d014Sreyk
1299b1b9d014Sreyk void
bstp_set_all_sync(struct bstp_state * bs)1300b1b9d014Sreyk bstp_set_all_sync(struct bstp_state *bs)
1301b1b9d014Sreyk {
1302b1b9d014Sreyk struct bstp_port *bp;
1303b1b9d014Sreyk
1304b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1305b1b9d014Sreyk bp->bp_sync = 1;
1306b1b9d014Sreyk bp->bp_synced = 0; /* Not explicit in spec */
1307b1b9d014Sreyk }
1308b1b9d014Sreyk
1309b1b9d014Sreyk bs->bs_allsynced = 0;
1310b1b9d014Sreyk }
1311b1b9d014Sreyk
1312b1b9d014Sreyk void
bstp_set_port_state(struct bstp_port * bp,int state)1313b1b9d014Sreyk bstp_set_port_state(struct bstp_port *bp, int state)
1314b1b9d014Sreyk {
1315b1b9d014Sreyk if (bp->bp_state == state)
1316b1b9d014Sreyk return;
1317b1b9d014Sreyk
1318b1b9d014Sreyk bp->bp_state = state;
1319b1b9d014Sreyk
1320b1b9d014Sreyk switch (bp->bp_state) {
1321b1b9d014Sreyk case BSTP_IFSTATE_DISCARDING:
1322793c5642Smvs DPRINTF(bp, "state changed to DISCARDING\n");
1323b1b9d014Sreyk break;
1324b1b9d014Sreyk
1325b1b9d014Sreyk case BSTP_IFSTATE_LEARNING:
1326793c5642Smvs DPRINTF(bp, "state changed to LEARNING\n");
1327b1b9d014Sreyk bstp_timer_start(&bp->bp_forward_delay_timer,
1328b1b9d014Sreyk bp->bp_protover == BSTP_PROTO_RSTP ?
1329b1b9d014Sreyk bp->bp_desg_htime : bp->bp_desg_fdelay);
1330b1b9d014Sreyk break;
1331b1b9d014Sreyk
1332b1b9d014Sreyk case BSTP_IFSTATE_FORWARDING:
1333793c5642Smvs DPRINTF(bp, "state changed to FORWARDING\n");
1334b1b9d014Sreyk bstp_timer_stop(&bp->bp_forward_delay_timer);
1335b1b9d014Sreyk /* Record that we enabled forwarding */
1336b1b9d014Sreyk bp->bp_forward_transitions++;
1337b1b9d014Sreyk break;
1338b1b9d014Sreyk }
1339b1b9d014Sreyk }
1340b1b9d014Sreyk
1341b1b9d014Sreyk void
bstp_set_port_role(struct bstp_port * bp,int role)1342b1b9d014Sreyk bstp_set_port_role(struct bstp_port *bp, int role)
1343b1b9d014Sreyk {
1344b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
1345b1b9d014Sreyk
1346b1b9d014Sreyk if (bp->bp_role == role)
1347b1b9d014Sreyk return;
1348b1b9d014Sreyk
1349b1b9d014Sreyk /* perform pre-change tasks */
1350b1b9d014Sreyk switch (bp->bp_role) {
1351b1b9d014Sreyk case BSTP_ROLE_DISABLED:
1352b1b9d014Sreyk bstp_timer_start(&bp->bp_forward_delay_timer,
1353b1b9d014Sreyk bp->bp_desg_max_age);
1354b1b9d014Sreyk break;
1355b1b9d014Sreyk
1356b1b9d014Sreyk case BSTP_ROLE_BACKUP:
1357b1b9d014Sreyk bstp_timer_start(&bp->bp_recent_backup_timer,
1358b1b9d014Sreyk bp->bp_desg_htime * 2);
1359b1b9d014Sreyk /* FALLTHROUGH */
1360b1b9d014Sreyk case BSTP_ROLE_ALTERNATE:
1361b1b9d014Sreyk bstp_timer_start(&bp->bp_forward_delay_timer,
1362b1b9d014Sreyk bp->bp_desg_fdelay);
1363b1b9d014Sreyk bp->bp_sync = 0;
1364b1b9d014Sreyk bp->bp_synced = 1;
1365b1b9d014Sreyk bp->bp_reroot = 0;
1366b1b9d014Sreyk break;
1367b1b9d014Sreyk
1368b1b9d014Sreyk case BSTP_ROLE_ROOT:
1369b1b9d014Sreyk bstp_timer_start(&bp->bp_recent_root_timer,
1370b1b9d014Sreyk BSTP_DEFAULT_FORWARD_DELAY);
1371b1b9d014Sreyk break;
1372b1b9d014Sreyk }
1373b1b9d014Sreyk
1374b1b9d014Sreyk bp->bp_role = role;
1375b1b9d014Sreyk /* clear values not carried between roles */
1376b1b9d014Sreyk bp->bp_proposing = 0;
1377b1b9d014Sreyk bs->bs_allsynced = 0;
1378b1b9d014Sreyk
1379b1b9d014Sreyk /* initialise the new role */
1380b1b9d014Sreyk switch (bp->bp_role) {
1381b1b9d014Sreyk case BSTP_ROLE_DISABLED:
1382b1b9d014Sreyk case BSTP_ROLE_ALTERNATE:
1383b1b9d014Sreyk case BSTP_ROLE_BACKUP:
1384793c5642Smvs DPRINTF(bp, "role -> ALT/BACK/DISABLED\n");
1385b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1386b1b9d014Sreyk bstp_timer_stop(&bp->bp_recent_root_timer);
1387b1b9d014Sreyk bstp_timer_latch(&bp->bp_forward_delay_timer);
1388b1b9d014Sreyk bp->bp_sync = 0;
1389b1b9d014Sreyk bp->bp_synced = 1;
1390b1b9d014Sreyk bp->bp_reroot = 0;
1391b1b9d014Sreyk break;
1392b1b9d014Sreyk
1393b1b9d014Sreyk case BSTP_ROLE_ROOT:
1394793c5642Smvs DPRINTF(bp, "role -> ROOT\n");
1395b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1396b1b9d014Sreyk bstp_timer_latch(&bp->bp_recent_root_timer);
1397b1b9d014Sreyk bp->bp_proposing = 0;
1398b1b9d014Sreyk break;
1399b1b9d014Sreyk
1400b1b9d014Sreyk case BSTP_ROLE_DESIGNATED:
1401793c5642Smvs DPRINTF(bp, "role -> DESIGNATED\n");
1402b1b9d014Sreyk bstp_timer_start(&bp->bp_hello_timer,
1403b1b9d014Sreyk bp->bp_desg_htime);
1404b1b9d014Sreyk bp->bp_agree = 0;
1405b1b9d014Sreyk break;
1406b1b9d014Sreyk }
1407b1b9d014Sreyk
1408b1b9d014Sreyk /* let the TC state know that the role changed */
1409b1b9d014Sreyk bstp_update_tc(bp);
1410b1b9d014Sreyk }
1411b1b9d014Sreyk
1412b1b9d014Sreyk void
bstp_set_port_proto(struct bstp_port * bp,int proto)1413b1b9d014Sreyk bstp_set_port_proto(struct bstp_port *bp, int proto)
1414b1b9d014Sreyk {
1415b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
1416b1b9d014Sreyk
1417b1b9d014Sreyk /* supported protocol versions */
1418b1b9d014Sreyk switch (proto) {
1419b1b9d014Sreyk case BSTP_PROTO_STP:
1420b1b9d014Sreyk /* we can downgrade protocols only */
1421b1b9d014Sreyk bstp_timer_stop(&bp->bp_migrate_delay_timer);
1422b1b9d014Sreyk /* clear unsupported features */
1423b1b9d014Sreyk bp->bp_operedge = 0;
1424b1b9d014Sreyk break;
1425b1b9d014Sreyk
1426b1b9d014Sreyk case BSTP_PROTO_RSTP:
1427b1b9d014Sreyk bstp_timer_start(&bp->bp_migrate_delay_timer,
1428b1b9d014Sreyk bs->bs_migration_delay);
1429b1b9d014Sreyk break;
1430b1b9d014Sreyk
1431b1b9d014Sreyk default:
1432793c5642Smvs DPRINTF(bp, "Unsupported STP version %d\n", proto);
14339fa0b7aaSjason return;
14349fa0b7aaSjason }
14359fa0b7aaSjason
1436b1b9d014Sreyk bp->bp_protover = proto;
1437b1b9d014Sreyk bp->bp_flags &= ~BSTP_PORT_CANMIGRATE;
14389fa0b7aaSjason }
14399fa0b7aaSjason
14409fa0b7aaSjason void
bstp_set_port_tc(struct bstp_port * bp,int state)1441b1b9d014Sreyk bstp_set_port_tc(struct bstp_port *bp, int state)
14429fa0b7aaSjason {
1443b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
14449fa0b7aaSjason
1445b1b9d014Sreyk bp->bp_tcstate = state;
14469fa0b7aaSjason
1447b1b9d014Sreyk /* initialise the new state */
1448b1b9d014Sreyk switch (bp->bp_tcstate) {
1449b1b9d014Sreyk case BSTP_TCSTATE_ACTIVE:
1450793c5642Smvs DPRINTF(bp, "-> TC_ACTIVE\n");
1451b1b9d014Sreyk /* nothing to do */
1452b1b9d014Sreyk break;
14539fa0b7aaSjason
1454b1b9d014Sreyk case BSTP_TCSTATE_INACTIVE:
1455b1b9d014Sreyk bstp_timer_stop(&bp->bp_tc_timer);
1456b1b9d014Sreyk /* flush routes on the parent bridge */
1457b1b9d014Sreyk bp->bp_fdbflush = 1;
14587426de2dSmpi bstp_notify_rtage(bp, 0);
1459b1b9d014Sreyk bp->bp_tc_ack = 0;
1460793c5642Smvs DPRINTF(bp, "-> TC_INACTIVE\n");
1461b1b9d014Sreyk break;
14629fa0b7aaSjason
1463b1b9d014Sreyk case BSTP_TCSTATE_LEARNING:
1464b1b9d014Sreyk bp->bp_rcvdtc = 0;
1465b1b9d014Sreyk bp->bp_rcvdtcn = 0;
1466b1b9d014Sreyk bp->bp_rcvdtca = 0;
1467b1b9d014Sreyk bp->bp_tc_prop = 0;
1468793c5642Smvs DPRINTF(bp, "-> TC_LEARNING\n");
1469b1b9d014Sreyk break;
14709fa0b7aaSjason
1471b1b9d014Sreyk case BSTP_TCSTATE_DETECTED:
1472b1b9d014Sreyk bstp_set_timer_tc(bp);
1473b1b9d014Sreyk bstp_set_other_tcprop(bp);
1474b1b9d014Sreyk /* send out notification */
1475b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1476b1b9d014Sreyk bstp_transmit(bs, bp);
1477b1b9d014Sreyk getmicrotime(&bs->bs_last_tc_time);
1478793c5642Smvs DPRINTF(bp, "-> TC_DETECTED\n");
1479b1b9d014Sreyk bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1480b1b9d014Sreyk break;
14819fa0b7aaSjason
1482b1b9d014Sreyk case BSTP_TCSTATE_TCN:
1483b1b9d014Sreyk bstp_set_timer_tc(bp);
1484793c5642Smvs DPRINTF(bp, "-> TC_TCN\n");
148583344e41Sjsg /* FALLTHROUGH */
1486b1b9d014Sreyk case BSTP_TCSTATE_TC:
1487b1b9d014Sreyk bp->bp_rcvdtc = 0;
1488b1b9d014Sreyk bp->bp_rcvdtcn = 0;
1489b1b9d014Sreyk if (bp->bp_role == BSTP_ROLE_DESIGNATED)
1490b1b9d014Sreyk bp->bp_tc_ack = 1;
14919fa0b7aaSjason
1492b1b9d014Sreyk bstp_set_other_tcprop(bp);
1493793c5642Smvs DPRINTF(bp, "-> TC_TC\n");
1494b1b9d014Sreyk bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1495b1b9d014Sreyk break;
14969fa0b7aaSjason
1497b1b9d014Sreyk case BSTP_TCSTATE_PROPAG:
1498b1b9d014Sreyk /* flush routes on the parent bridge */
1499b1b9d014Sreyk bp->bp_fdbflush = 1;
15007426de2dSmpi bstp_notify_rtage(bp, 0);
1501b1b9d014Sreyk bp->bp_tc_prop = 0;
1502b1b9d014Sreyk bstp_set_timer_tc(bp);
1503793c5642Smvs DPRINTF(bp, "-> TC_PROPAG\n");
1504b1b9d014Sreyk bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1505b1b9d014Sreyk break;
15069fa0b7aaSjason
1507b1b9d014Sreyk case BSTP_TCSTATE_ACK:
1508b1b9d014Sreyk bstp_timer_stop(&bp->bp_tc_timer);
1509b1b9d014Sreyk bp->bp_rcvdtca = 0;
1510793c5642Smvs DPRINTF(bp, "-> TC_ACK\n");
1511b1b9d014Sreyk bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1512b1b9d014Sreyk break;
15139fa0b7aaSjason }
15149fa0b7aaSjason }
15159fa0b7aaSjason
15169fa0b7aaSjason void
bstp_set_timer_tc(struct bstp_port * bp)1517b1b9d014Sreyk bstp_set_timer_tc(struct bstp_port *bp)
15189fa0b7aaSjason {
1519b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
15209fa0b7aaSjason
1521b1b9d014Sreyk if (bp->bp_tc_timer.active)
1522b1b9d014Sreyk return;
15239fa0b7aaSjason
1524b1b9d014Sreyk switch (bp->bp_protover) {
1525b1b9d014Sreyk case BSTP_PROTO_RSTP:
1526b1b9d014Sreyk bstp_timer_start(&bp->bp_tc_timer,
1527b1b9d014Sreyk bp->bp_desg_htime + BSTP_TICK_VAL);
1528b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1529b1b9d014Sreyk break;
1530b1b9d014Sreyk case BSTP_PROTO_STP:
1531b1b9d014Sreyk bstp_timer_start(&bp->bp_tc_timer,
1532b1b9d014Sreyk bs->bs_root_max_age + bs->bs_root_fdelay);
1533b1b9d014Sreyk break;
1534b1b9d014Sreyk }
1535b1b9d014Sreyk }
1536b1b9d014Sreyk
1537b1b9d014Sreyk void
bstp_set_timer_msgage(struct bstp_port * bp)1538b1b9d014Sreyk bstp_set_timer_msgage(struct bstp_port *bp)
1539b1b9d014Sreyk {
1540b1b9d014Sreyk if (bp->bp_port_msg_age + BSTP_MESSAGE_AGE_INCR <=
1541b1b9d014Sreyk bp->bp_port_max_age) {
1542b1b9d014Sreyk bstp_timer_start(&bp->bp_message_age_timer,
1543b1b9d014Sreyk bp->bp_port_htime * 3);
1544b1b9d014Sreyk } else
1545b1b9d014Sreyk /* expires immediately */
1546b1b9d014Sreyk bstp_timer_start(&bp->bp_message_age_timer, 0);
1547b1b9d014Sreyk }
1548b1b9d014Sreyk
1549b1b9d014Sreyk int
bstp_rerooted(struct bstp_state * bs,struct bstp_port * bp)1550b1b9d014Sreyk bstp_rerooted(struct bstp_state *bs, struct bstp_port *bp)
1551b1b9d014Sreyk {
1552b1b9d014Sreyk struct bstp_port *bp2;
1553b1b9d014Sreyk int rr_set = 0;
1554b1b9d014Sreyk
1555b1b9d014Sreyk LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
1556b1b9d014Sreyk if (bp2 == bp)
15579fa0b7aaSjason continue;
1558b1b9d014Sreyk if (bp2->bp_recent_root_timer.active) {
1559b1b9d014Sreyk rr_set = 1;
1560b1b9d014Sreyk break;
15619fa0b7aaSjason }
15629fa0b7aaSjason }
1563b1b9d014Sreyk return (!rr_set);
1564b1b9d014Sreyk }
15659fa0b7aaSjason
1566b1b9d014Sreyk /*
1567b1b9d014Sreyk * Calculate the path cost according to the link speed.
1568b1b9d014Sreyk */
1569b1b9d014Sreyk u_int32_t
bstp_calc_path_cost(struct bstp_port * bp)1570b1b9d014Sreyk bstp_calc_path_cost(struct bstp_port *bp)
15719fa0b7aaSjason {
1572793c5642Smvs struct ifnet *ifp;
15739fa0b7aaSjason u_int32_t path_cost;
1574b1b9d014Sreyk
1575b1b9d014Sreyk /* If the priority has been manually set then retain the value */
1576b1b9d014Sreyk if (bp->bp_flags & BSTP_PORT_ADMCOST)
1577b1b9d014Sreyk return bp->bp_path_cost;
1578b1b9d014Sreyk
1579793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) == NULL) {
1580793c5642Smvs return bp->bp_path_cost;
1581793c5642Smvs }
1582793c5642Smvs
1583793c5642Smvs if (ifp->if_baudrate < 1000) {
1584793c5642Smvs if_put(ifp);
1585b1b9d014Sreyk return (BSTP_DEFAULT_PATH_COST);
1586793c5642Smvs }
1587b1b9d014Sreyk
1588b1b9d014Sreyk /* formula from section 17.14, IEEE Std 802.1D-2004 */
1589b1b9d014Sreyk path_cost = 20000000000ULL / (ifp->if_baudrate / 1000);
1590793c5642Smvs if_put(ifp);
1591b1b9d014Sreyk
1592b1b9d014Sreyk if (path_cost > BSTP_MAX_PATH_COST)
1593b1b9d014Sreyk path_cost = BSTP_MAX_PATH_COST;
1594b1b9d014Sreyk
1595b1b9d014Sreyk /* STP compat mode only uses 16 bits of the 32 */
1596b1b9d014Sreyk if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535)
1597b1b9d014Sreyk path_cost = 65535;
1598b1b9d014Sreyk
1599b1b9d014Sreyk return (path_cost);
16009fa0b7aaSjason }
16019fa0b7aaSjason
16029fa0b7aaSjason void
bstp_notify_rtage(struct bstp_port * bp,int pending)16037426de2dSmpi bstp_notify_rtage(struct bstp_port *bp, int pending)
16049fa0b7aaSjason {
1605b1b9d014Sreyk int age = 0;
1606b1b9d014Sreyk
16075c15b8bbSmpi KERNEL_ASSERT_LOCKED();
1608b1b9d014Sreyk
1609b1b9d014Sreyk switch (bp->bp_protover) {
1610b1b9d014Sreyk case BSTP_PROTO_STP:
1611b1b9d014Sreyk /* convert to seconds */
1612b1b9d014Sreyk age = bp->bp_desg_fdelay / BSTP_TICK_VAL;
1613b1b9d014Sreyk break;
1614b1b9d014Sreyk case BSTP_PROTO_RSTP:
1615b1b9d014Sreyk age = 0;
1616b1b9d014Sreyk break;
1617b1b9d014Sreyk }
1618b1b9d014Sreyk
1619793c5642Smvs if (bp->bp_active == 1) {
1620793c5642Smvs struct ifnet *ifp;
1621793c5642Smvs
1622793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) != NULL)
1623793c5642Smvs bridge_rtagenode(ifp, age);
1624793c5642Smvs if_put(ifp);
1625793c5642Smvs }
1626b1b9d014Sreyk
1627b1b9d014Sreyk /* flush is complete */
1628b1b9d014Sreyk bp->bp_fdbflush = 0;
16299fa0b7aaSjason }
16309fa0b7aaSjason
16319fa0b7aaSjason void
bstp_ifstate(void * arg)1632b1b9d014Sreyk bstp_ifstate(void *arg)
16339fa0b7aaSjason {
1634b1b9d014Sreyk struct ifnet *ifp = (struct ifnet *)arg;
163567b85364Smpi struct bridge_iflist *bif;
1636b1b9d014Sreyk struct bstp_port *bp;
1637b1b9d014Sreyk struct bstp_state *bs;
1638b1b9d014Sreyk int s;
1639b1b9d014Sreyk
1640b1b9d014Sreyk if (ifp->if_type == IFT_BRIDGE)
1641b1b9d014Sreyk return;
1642b1b9d014Sreyk
1643b1b9d014Sreyk s = splnet();
164496c4247cSmpi if ((bif = bridge_getbif(ifp)) == NULL)
16452d18ef82Scamield goto done;
164667b85364Smpi if ((bif->bif_flags & IFBIF_STP) == 0)
1647b1b9d014Sreyk goto done;
164867b85364Smpi if ((bp = bif->bif_stp) == NULL)
1649b1b9d014Sreyk goto done;
1650b1b9d014Sreyk if ((bs = bp->bp_bs) == NULL)
1651b1b9d014Sreyk goto done;
1652b1b9d014Sreyk
1653b1b9d014Sreyk /* update the link state */
1654b1b9d014Sreyk bstp_ifupdstatus(bs, bp);
1655b1b9d014Sreyk bstp_update_state(bs, bp);
1656b1b9d014Sreyk done:
1657b1b9d014Sreyk splx(s);
1658b1b9d014Sreyk }
1659b1b9d014Sreyk
1660b1b9d014Sreyk void
bstp_ifupdstatus(struct bstp_state * bs,struct bstp_port * bp)1661b1b9d014Sreyk bstp_ifupdstatus(struct bstp_state *bs, struct bstp_port *bp)
16629fa0b7aaSjason {
1663793c5642Smvs struct ifnet *ifp;
1664b1b9d014Sreyk
1665793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) == NULL)
1666b1b9d014Sreyk return;
1667b1b9d014Sreyk
1668b1b9d014Sreyk bp->bp_path_cost = bstp_calc_path_cost(bp);
16699fa0b7aaSjason
1670334091d1Sreyk if ((ifp->if_flags & IFF_UP) &&
1671334091d1Sreyk ifp->if_link_state != LINK_STATE_DOWN) {
1672ff7746e3Sreyk if (bp->bp_flags & BSTP_PORT_AUTOPTP) {
1673ff7746e3Sreyk /* A full-duplex link is assumed to be ptp */
1674ff7746e3Sreyk bp->bp_ptp_link = ifp->if_link_state ==
1675b1b9d014Sreyk LINK_STATE_FULL_DUPLEX ? 1 : 0;
1676b1b9d014Sreyk }
1677b1b9d014Sreyk
1678b1b9d014Sreyk if (bp->bp_infois == BSTP_INFO_DISABLED)
1679b1b9d014Sreyk bstp_enable_port(bs, bp);
1680334091d1Sreyk } else {
1681b1b9d014Sreyk if (bp->bp_infois != BSTP_INFO_DISABLED)
1682b1b9d014Sreyk bstp_disable_port(bs, bp);
16839fa0b7aaSjason }
1684793c5642Smvs
1685793c5642Smvs if_put(ifp);
16869fa0b7aaSjason }
16879fa0b7aaSjason
16889fa0b7aaSjason void
bstp_enable_port(struct bstp_state * bs,struct bstp_port * bp)1689b1b9d014Sreyk bstp_enable_port(struct bstp_state *bs, struct bstp_port *bp)
16909fa0b7aaSjason {
1691b1b9d014Sreyk bp->bp_infois = BSTP_INFO_AGED;
1692b1b9d014Sreyk bstp_assign_roles(bs);
1693b1b9d014Sreyk }
1694b1b9d014Sreyk
1695b1b9d014Sreyk void
bstp_disable_port(struct bstp_state * bs,struct bstp_port * bp)1696b1b9d014Sreyk bstp_disable_port(struct bstp_state *bs, struct bstp_port *bp)
1697b1b9d014Sreyk {
1698b1b9d014Sreyk bp->bp_infois = BSTP_INFO_DISABLED;
1699b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1700b1b9d014Sreyk bstp_assign_roles(bs);
1701b1b9d014Sreyk }
1702b1b9d014Sreyk
1703b1b9d014Sreyk void
bstp_tick(void * arg)1704b1b9d014Sreyk bstp_tick(void *arg)
1705b1b9d014Sreyk {
1706b1b9d014Sreyk struct bstp_state *bs = (struct bstp_state *)arg;
170713dba897Smvs struct ifnet *ifp;
1708b1b9d014Sreyk struct bstp_port *bp;
17099fa0b7aaSjason int s;
17109fa0b7aaSjason
171113dba897Smvs if ((ifp = if_get(bs->bs_ifindex)) == NULL)
171213dba897Smvs return;
171313dba897Smvs
17149fa0b7aaSjason s = splnet();
171513dba897Smvs if ((ifp->if_flags & IFF_RUNNING) == 0) {
1716b1b9d014Sreyk splx(s);
171713dba897Smvs if_put(ifp);
1718b1b9d014Sreyk return;
17199fa0b7aaSjason }
17209fa0b7aaSjason
1721b1b9d014Sreyk /* slow timer to catch missed link events */
1722b1b9d014Sreyk if (bstp_timer_expired(&bs->bs_link_timer)) {
1723b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
1724b1b9d014Sreyk bstp_ifupdstatus(bs, bp);
1725b1b9d014Sreyk bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
17269fa0b7aaSjason }
17279fa0b7aaSjason
1728b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1729b1b9d014Sreyk /* no events need to happen for these */
1730b1b9d014Sreyk bstp_timer_expired(&bp->bp_tc_timer);
1731b1b9d014Sreyk bstp_timer_expired(&bp->bp_recent_root_timer);
1732b1b9d014Sreyk bstp_timer_expired(&bp->bp_forward_delay_timer);
1733b1b9d014Sreyk bstp_timer_expired(&bp->bp_recent_backup_timer);
17349fa0b7aaSjason
1735b1b9d014Sreyk if (bstp_timer_expired(&bp->bp_hello_timer))
1736b1b9d014Sreyk bstp_hello_timer_expiry(bs, bp);
1737b1b9d014Sreyk
1738b1b9d014Sreyk if (bstp_timer_expired(&bp->bp_message_age_timer))
1739b1b9d014Sreyk bstp_message_age_expiry(bs, bp);
1740b1b9d014Sreyk
1741b1b9d014Sreyk if (bstp_timer_expired(&bp->bp_migrate_delay_timer))
1742b1b9d014Sreyk bstp_migrate_delay_expiry(bs, bp);
1743b1b9d014Sreyk
1744b1b9d014Sreyk if (bstp_timer_expired(&bp->bp_edge_delay_timer))
1745b1b9d014Sreyk bstp_edge_delay_expiry(bs, bp);
1746b1b9d014Sreyk
1747b1b9d014Sreyk /* update the various state machines for the port */
1748b1b9d014Sreyk bstp_update_state(bs, bp);
1749b1b9d014Sreyk
1750b1b9d014Sreyk if (bp->bp_txcount > 0)
1751b1b9d014Sreyk bp->bp_txcount--;
17529fa0b7aaSjason }
17539fa0b7aaSjason
175413dba897Smvs if (ifp->if_flags & IFF_RUNNING)
1755e15e1c8cSblambert timeout_add_sec(&bs->bs_bstptimeout, 1);
17569fa0b7aaSjason
17579fa0b7aaSjason splx(s);
175813dba897Smvs if_put(ifp);
17599fa0b7aaSjason }
17609fa0b7aaSjason
17619fa0b7aaSjason void
bstp_timer_start(struct bstp_timer * t,u_int16_t v)1762b1b9d014Sreyk bstp_timer_start(struct bstp_timer *t, u_int16_t v)
17639fa0b7aaSjason {
17649fa0b7aaSjason t->value = v;
17659fa0b7aaSjason t->active = 1;
1766b1b9d014Sreyk t->latched = 0;
17679fa0b7aaSjason }
17689fa0b7aaSjason
17699fa0b7aaSjason void
bstp_timer_stop(struct bstp_timer * t)1770b1b9d014Sreyk bstp_timer_stop(struct bstp_timer *t)
17719fa0b7aaSjason {
17729fa0b7aaSjason t->value = 0;
17739fa0b7aaSjason t->active = 0;
1774b1b9d014Sreyk t->latched = 0;
1775b1b9d014Sreyk }
1776b1b9d014Sreyk
1777b1b9d014Sreyk void
bstp_timer_latch(struct bstp_timer * t)1778b1b9d014Sreyk bstp_timer_latch(struct bstp_timer *t)
1779b1b9d014Sreyk {
1780b1b9d014Sreyk t->latched = 1;
1781b1b9d014Sreyk t->active = 1;
17829fa0b7aaSjason }
17839fa0b7aaSjason
17849fa0b7aaSjason int
bstp_timer_expired(struct bstp_timer * t)1785b1b9d014Sreyk bstp_timer_expired(struct bstp_timer *t)
17869fa0b7aaSjason {
1787b1b9d014Sreyk if (t->active == 0 || t->latched)
17889fa0b7aaSjason return (0);
1789b1b9d014Sreyk t->value -= BSTP_TICK_VAL;
1790b1b9d014Sreyk if (t->value <= 0) {
17919fa0b7aaSjason bstp_timer_stop(t);
17929fa0b7aaSjason return (1);
17939fa0b7aaSjason }
17949fa0b7aaSjason return (0);
1795b1b9d014Sreyk }
17969fa0b7aaSjason
1797b1b9d014Sreyk void
bstp_hello_timer_expiry(struct bstp_state * bs,struct bstp_port * bp)1798b1b9d014Sreyk bstp_hello_timer_expiry(struct bstp_state *bs, struct bstp_port *bp)
1799b1b9d014Sreyk {
1800b1b9d014Sreyk if ((bp->bp_flags & BSTP_PORT_NEWINFO) ||
1801b1b9d014Sreyk bp->bp_role == BSTP_ROLE_DESIGNATED ||
1802b1b9d014Sreyk (bp->bp_role == BSTP_ROLE_ROOT &&
1803b1b9d014Sreyk bp->bp_tc_timer.active == 1)) {
1804b1b9d014Sreyk bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
1805b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
1806b1b9d014Sreyk bstp_transmit(bs, bp);
1807b1b9d014Sreyk }
1808b1b9d014Sreyk }
1809b1b9d014Sreyk
1810b1b9d014Sreyk void
bstp_message_age_expiry(struct bstp_state * bs,struct bstp_port * bp)1811b1b9d014Sreyk bstp_message_age_expiry(struct bstp_state *bs, struct bstp_port *bp)
1812b1b9d014Sreyk {
1813af52927aSmartynas if (bp->bp_infois == BSTP_INFO_RECEIVED) {
1814b1b9d014Sreyk bp->bp_infois = BSTP_INFO_AGED;
1815b1b9d014Sreyk bstp_assign_roles(bs);
1816793c5642Smvs DPRINTF(bp, "aged info\n");
1817b1b9d014Sreyk }
1818b1b9d014Sreyk }
1819b1b9d014Sreyk
1820b1b9d014Sreyk void
bstp_migrate_delay_expiry(struct bstp_state * bs,struct bstp_port * bp)1821b1b9d014Sreyk bstp_migrate_delay_expiry(struct bstp_state *bs, struct bstp_port *bp)
1822b1b9d014Sreyk {
1823b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_CANMIGRATE;
1824b1b9d014Sreyk }
1825b1b9d014Sreyk
1826b1b9d014Sreyk void
bstp_edge_delay_expiry(struct bstp_state * bs,struct bstp_port * bp)1827b1b9d014Sreyk bstp_edge_delay_expiry(struct bstp_state *bs, struct bstp_port *bp)
1828b1b9d014Sreyk {
1829b1b9d014Sreyk if ((bp->bp_flags & BSTP_PORT_AUTOEDGE) &&
1830b1b9d014Sreyk bp->bp_protover == BSTP_PROTO_RSTP && bp->bp_proposing &&
1831b1b9d014Sreyk bp->bp_role == BSTP_ROLE_DESIGNATED)
1832b1b9d014Sreyk bp->bp_operedge = 1;
18339fa0b7aaSjason }
18349fa0b7aaSjason
18359fa0b7aaSjason int
bstp_addr_cmp(const u_int8_t * a,const u_int8_t * b)1836b1b9d014Sreyk bstp_addr_cmp(const u_int8_t *a, const u_int8_t *b)
1837b1b9d014Sreyk {
1838b1b9d014Sreyk int i, d;
1839b1b9d014Sreyk
1840b1b9d014Sreyk for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) {
1841b1b9d014Sreyk d = ((int)a[i]) - ((int)b[i]);
1842b1b9d014Sreyk }
1843b1b9d014Sreyk
1844b1b9d014Sreyk return (d);
1845b1b9d014Sreyk }
1846b1b9d014Sreyk
1847b1b9d014Sreyk /*
1848b1b9d014Sreyk * compare the bridge address component of the bridgeid
1849b1b9d014Sreyk */
1850b1b9d014Sreyk int
bstp_same_bridgeid(u_int64_t id1,u_int64_t id2)1851b1b9d014Sreyk bstp_same_bridgeid(u_int64_t id1, u_int64_t id2)
1852b1b9d014Sreyk {
1853b1b9d014Sreyk u_char addr1[ETHER_ADDR_LEN];
1854b1b9d014Sreyk u_char addr2[ETHER_ADDR_LEN];
1855b1b9d014Sreyk
1856b1b9d014Sreyk PV2ADDR(id1, addr1);
1857b1b9d014Sreyk PV2ADDR(id2, addr2);
1858b1b9d014Sreyk
1859b1b9d014Sreyk if (bstp_addr_cmp(addr1, addr2) == 0)
1860b1b9d014Sreyk return (1);
1861b1b9d014Sreyk
1862b1b9d014Sreyk return (0);
1863b1b9d014Sreyk }
1864b1b9d014Sreyk
1865b1b9d014Sreyk void
bstp_initialization(struct bstp_state * bs)1866b1b9d014Sreyk bstp_initialization(struct bstp_state *bs)
1867b1b9d014Sreyk {
1868b1b9d014Sreyk struct bstp_port *bp;
18692640f3b8Shenning struct ifnet *mif = NULL;
1870b1b9d014Sreyk u_char *e_addr;
1871b1b9d014Sreyk
1872b1b9d014Sreyk /*
1873b1b9d014Sreyk * Search through the Ethernet interfaces and find the one
18742640f3b8Shenning * with the lowest value.
18752640f3b8Shenning * Make sure we take the address from an interface that is
18762640f3b8Shenning * part of the bridge to make sure two bridges on the system
18772640f3b8Shenning * will not use the same one. It is not possible for mif to be
1878b1b9d014Sreyk * null, at this point we have at least one STP port and hence
1879b1b9d014Sreyk * at least one NIC.
1880b1b9d014Sreyk */
18812640f3b8Shenning LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1882793c5642Smvs struct ifnet *ifp;
1883793c5642Smvs
1884b1b9d014Sreyk if (mif == NULL) {
1885793c5642Smvs mif = if_get(bp->bp_ifindex);
1886b1b9d014Sreyk continue;
1887b1b9d014Sreyk }
1888793c5642Smvs
1889793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) == NULL)
1890793c5642Smvs continue;
1891793c5642Smvs
1892793c5642Smvs if (bstp_addr_cmp(LLADDR(ifp->if_sadl),
1893b1b9d014Sreyk LLADDR(mif->if_sadl)) < 0) {
1894793c5642Smvs if_put(mif);
1895793c5642Smvs mif = ifp;
1896b1b9d014Sreyk continue;
1897b1b9d014Sreyk }
1898793c5642Smvs if_put(ifp);
1899793c5642Smvs }
1900793c5642Smvs
1901793c5642Smvs if (mif == NULL) {
1902793c5642Smvs bstp_stop(bs);
1903793c5642Smvs bstp_reset(bs);
1904793c5642Smvs return;
1905b1b9d014Sreyk }
1906b1b9d014Sreyk
1907b1b9d014Sreyk e_addr = LLADDR(mif->if_sadl);
1908793c5642Smvs if_put(mif);
1909b1b9d014Sreyk bs->bs_bridge_pv.pv_dbridge_id =
1910b1b9d014Sreyk (((u_int64_t)bs->bs_bridge_priority) << 48) |
1911b1b9d014Sreyk (((u_int64_t)e_addr[0]) << 40) |
1912b1b9d014Sreyk (((u_int64_t)e_addr[1]) << 32) |
1913b1b9d014Sreyk (((u_int64_t)e_addr[2]) << 24) |
1914b1b9d014Sreyk (((u_int64_t)e_addr[3]) << 16) |
1915b1b9d014Sreyk (((u_int64_t)e_addr[4]) << 8) |
1916b1b9d014Sreyk (((u_int64_t)e_addr[5]));
1917b1b9d014Sreyk
1918b1b9d014Sreyk bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
1919b1b9d014Sreyk bs->bs_bridge_pv.pv_cost = 0;
1920b1b9d014Sreyk bs->bs_bridge_pv.pv_dport_id = 0;
1921b1b9d014Sreyk bs->bs_bridge_pv.pv_port_id = 0;
1922b1b9d014Sreyk
1923b1b9d014Sreyk if (!timeout_initialized(&bs->bs_bstptimeout))
1924b1b9d014Sreyk timeout_set(&bs->bs_bstptimeout, bstp_tick, bs);
19254eefde2cSmpi if (!timeout_pending(&bs->bs_bstptimeout))
1926e15e1c8cSblambert timeout_add_sec(&bs->bs_bstptimeout, 1);
1927b1b9d014Sreyk
1928b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1929b1b9d014Sreyk bp->bp_port_id = (bp->bp_priority << 8) |
1930793c5642Smvs (bp->bp_ifindex & 0xfff);
1931b1b9d014Sreyk bstp_ifupdstatus(bs, bp);
1932b1b9d014Sreyk }
1933b1b9d014Sreyk
1934b1b9d014Sreyk bstp_assign_roles(bs);
1935b1b9d014Sreyk bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
1936b1b9d014Sreyk }
1937b1b9d014Sreyk
1938b1b9d014Sreyk struct bstp_state *
bstp_create(void)193913dba897Smvs bstp_create(void)
1940b1b9d014Sreyk {
1941b1b9d014Sreyk struct bstp_state *bs;
1942b1b9d014Sreyk
19438b28ab37Shenning bs = malloc(sizeof(*bs), M_DEVBUF, M_WAITOK|M_ZERO);
1944b1b9d014Sreyk LIST_INIT(&bs->bs_bplist);
1945b1b9d014Sreyk
1946b1b9d014Sreyk bs->bs_bridge_max_age = BSTP_DEFAULT_MAX_AGE;
1947b1b9d014Sreyk bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
1948b1b9d014Sreyk bs->bs_bridge_fdelay = BSTP_DEFAULT_FORWARD_DELAY;
1949b1b9d014Sreyk bs->bs_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY;
1950b1b9d014Sreyk bs->bs_hold_time = BSTP_DEFAULT_HOLD_TIME;
1951b1b9d014Sreyk bs->bs_migration_delay = BSTP_DEFAULT_MIGRATE_DELAY;
1952b1b9d014Sreyk bs->bs_txholdcount = BSTP_DEFAULT_HOLD_COUNT;
1953b1b9d014Sreyk bs->bs_protover = BSTP_PROTO_RSTP; /* STP instead of RSTP? */
1954b1b9d014Sreyk
1955b1b9d014Sreyk getmicrotime(&bs->bs_last_tc_time);
1956b1b9d014Sreyk
1957b1b9d014Sreyk return (bs);
1958b1b9d014Sreyk }
1959b1b9d014Sreyk
1960b1b9d014Sreyk void
bstp_destroy(struct bstp_state * bs)1961b1b9d014Sreyk bstp_destroy(struct bstp_state *bs)
1962b1b9d014Sreyk {
1963b1b9d014Sreyk if (bs == NULL)
1964b1b9d014Sreyk return;
1965b1b9d014Sreyk
1966b1b9d014Sreyk if (!LIST_EMPTY(&bs->bs_bplist))
1967b1b9d014Sreyk panic("bstp still active");
1968b1b9d014Sreyk
19690985b883Sderaadt free(bs, M_DEVBUF, sizeof *bs);
1970b1b9d014Sreyk }
1971b1b9d014Sreyk
1972b1b9d014Sreyk void
bstp_enable(struct bstp_state * bs,unsigned int ifindex)197313dba897Smvs bstp_enable(struct bstp_state *bs, unsigned int ifindex)
197413dba897Smvs {
197513dba897Smvs bs->bs_ifindex = ifindex;
197613dba897Smvs bstp_initialization(bs);
197713dba897Smvs }
197813dba897Smvs
197913dba897Smvs void
bstp_disable(struct bstp_state * bs)198013dba897Smvs bstp_disable(struct bstp_state *bs)
198113dba897Smvs {
198213dba897Smvs if (timeout_initialized(&bs->bs_bstptimeout))
198313dba897Smvs timeout_del(&bs->bs_bstptimeout);
198413dba897Smvs bs->bs_ifindex = 0;
198513dba897Smvs }
198613dba897Smvs
198713dba897Smvs void
bstp_stop(struct bstp_state * bs)1988b1b9d014Sreyk bstp_stop(struct bstp_state *bs)
1989b1b9d014Sreyk {
1990b1b9d014Sreyk struct bstp_port *bp;
1991b1b9d014Sreyk
1992b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
1993b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1994b1b9d014Sreyk
199542bda5b3Smk if (timeout_initialized(&bs->bs_bstptimeout))
1996b1b9d014Sreyk timeout_del(&bs->bs_bstptimeout);
1997b1b9d014Sreyk }
1998b1b9d014Sreyk
1999b1b9d014Sreyk struct bstp_port *
bstp_add(struct bstp_state * bs,struct ifnet * ifp)2000b1b9d014Sreyk bstp_add(struct bstp_state *bs, struct ifnet *ifp)
2001b1b9d014Sreyk {
2002b1b9d014Sreyk struct bstp_port *bp;
2003b1b9d014Sreyk
2004b1b9d014Sreyk switch (ifp->if_type) {
2005b1b9d014Sreyk case IFT_ETHER: /* These can do spanning tree. */
2006b1b9d014Sreyk break;
2007b1b9d014Sreyk default:
2008b1b9d014Sreyk /* Nothing else can. */
2009b1b9d014Sreyk return (NULL);
2010b1b9d014Sreyk }
2011b1b9d014Sreyk
20128b28ab37Shenning bp = malloc(sizeof(*bp), M_DEVBUF, M_NOWAIT|M_ZERO);
2013b1b9d014Sreyk if (bp == NULL)
2014b1b9d014Sreyk return (NULL);
2015b1b9d014Sreyk
2016793c5642Smvs bp->bp_ifindex = ifp->if_index;
2017b1b9d014Sreyk bp->bp_bs = bs;
2018b1b9d014Sreyk bp->bp_priority = BSTP_DEFAULT_PORT_PRIORITY;
2019b1b9d014Sreyk bp->bp_txcount = 0;
2020b1b9d014Sreyk
2021b1b9d014Sreyk /* Init state */
2022b1b9d014Sreyk bp->bp_infois = BSTP_INFO_DISABLED;
2023ff7746e3Sreyk bp->bp_flags = BSTP_PORT_AUTOEDGE | BSTP_PORT_AUTOPTP;
2024b1b9d014Sreyk bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
2025b1b9d014Sreyk bstp_set_port_proto(bp, bs->bs_protover);
2026b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
2027b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
2028b1b9d014Sreyk bp->bp_path_cost = bstp_calc_path_cost(bp);
2029b1b9d014Sreyk
2030b1b9d014Sreyk LIST_INSERT_HEAD(&bs->bs_bplist, bp, bp_next);
2031b1b9d014Sreyk
2032b1b9d014Sreyk bp->bp_active = 1;
2033b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_NEWINFO;
2034b1b9d014Sreyk bstp_initialization(bs);
2035b1b9d014Sreyk bstp_update_roles(bs, bp);
2036b1b9d014Sreyk
2037b1b9d014Sreyk /* Register callback for physical link state changes */
20384f5e51a4Sdlg task_set(&bp->bp_ltask, bstp_ifstate, ifp);
20394f5e51a4Sdlg if_linkstatehook_add(ifp, &bp->bp_ltask);
2040b1b9d014Sreyk
2041b1b9d014Sreyk return (bp);
2042b1b9d014Sreyk }
2043b1b9d014Sreyk
2044b1b9d014Sreyk void
bstp_delete(struct bstp_port * bp)2045b1b9d014Sreyk bstp_delete(struct bstp_port *bp)
2046b1b9d014Sreyk {
2047b1b9d014Sreyk struct bstp_state *bs = bp->bp_bs;
2048793c5642Smvs struct ifnet *ifp;
2049b1b9d014Sreyk
2050b1b9d014Sreyk if (!bp->bp_active)
2051b1b9d014Sreyk panic("not a bstp member");
2052b1b9d014Sreyk
2053793c5642Smvs if ((ifp = if_get(bp->bp_ifindex)) != NULL)
20544f5e51a4Sdlg if_linkstatehook_del(ifp, &bp->bp_ltask);
2055793c5642Smvs if_put(ifp);
2056b1b9d014Sreyk
2057b1b9d014Sreyk LIST_REMOVE(bp, bp_next);
20580985b883Sderaadt free(bp, M_DEVBUF, sizeof *bp);
2059b1b9d014Sreyk bstp_initialization(bs);
2060b1b9d014Sreyk }
2061b1b9d014Sreyk
2062b1b9d014Sreyk u_int8_t
bstp_getstate(struct bstp_state * bs,struct bstp_port * bp)2063b1b9d014Sreyk bstp_getstate(struct bstp_state *bs, struct bstp_port *bp)
2064b1b9d014Sreyk {
2065b1b9d014Sreyk u_int8_t state = bp->bp_state;
2066b1b9d014Sreyk
2067b1b9d014Sreyk if (bs->bs_protover != BSTP_PROTO_STP)
2068b1b9d014Sreyk return (state);
2069b1b9d014Sreyk
2070b1b9d014Sreyk /*
2071b1b9d014Sreyk * Translate RSTP roles and states to STP port states
2072b1b9d014Sreyk * (IEEE Std 802.1D-2004 Table 17-1).
2073b1b9d014Sreyk */
2074b1b9d014Sreyk if (bp->bp_role == BSTP_ROLE_DISABLED)
2075b1b9d014Sreyk state = BSTP_IFSTATE_DISABLED;
2076b1b9d014Sreyk else if (bp->bp_role == BSTP_ROLE_ALTERNATE ||
2077b1b9d014Sreyk bp->bp_role == BSTP_ROLE_BACKUP)
2078b1b9d014Sreyk state = BSTP_IFSTATE_BLOCKING;
2079b1b9d014Sreyk else if (state == BSTP_IFSTATE_DISCARDING)
2080b1b9d014Sreyk state = BSTP_IFSTATE_LISTENING;
2081b1b9d014Sreyk
2082b1b9d014Sreyk return (state);
2083b1b9d014Sreyk }
2084b1b9d014Sreyk
2085b1b9d014Sreyk void
bstp_ifsflags(struct bstp_port * bp,u_int flags)2086b1b9d014Sreyk bstp_ifsflags(struct bstp_port *bp, u_int flags)
2087b1b9d014Sreyk {
2088b1b9d014Sreyk struct bstp_state *bs;
2089b1b9d014Sreyk
2090b1b9d014Sreyk if ((flags & IFBIF_STP) == 0)
2091b1b9d014Sreyk return;
2092b1b9d014Sreyk bs = bp->bp_bs;
2093b1b9d014Sreyk
2094b1b9d014Sreyk /*
2095b1b9d014Sreyk * Set edge status
2096b1b9d014Sreyk */
2097b1b9d014Sreyk if (flags & IFBIF_BSTP_AUTOEDGE) {
2098b1b9d014Sreyk if ((bp->bp_flags & BSTP_PORT_AUTOEDGE) == 0) {
2099b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_AUTOEDGE;
2100b1b9d014Sreyk
2101b1b9d014Sreyk /* we may be able to transition straight to edge */
2102b1b9d014Sreyk if (bp->bp_edge_delay_timer.active == 0)
2103b1b9d014Sreyk bstp_edge_delay_expiry(bs, bp);
2104b1b9d014Sreyk }
2105b1b9d014Sreyk } else
2106b1b9d014Sreyk bp->bp_flags &= ~BSTP_PORT_AUTOEDGE;
2107b1b9d014Sreyk
2108b1b9d014Sreyk if (flags & IFBIF_BSTP_EDGE)
2109b1b9d014Sreyk bp->bp_operedge = 1;
2110b1b9d014Sreyk else
2111b1b9d014Sreyk bp->bp_operedge = 0;
2112b1b9d014Sreyk
2113b1b9d014Sreyk /*
2114b1b9d014Sreyk * Set point to point status
2115b1b9d014Sreyk */
2116ff7746e3Sreyk if (flags & IFBIF_BSTP_AUTOPTP) {
2117ff7746e3Sreyk if ((bp->bp_flags & BSTP_PORT_AUTOPTP) == 0) {
2118ff7746e3Sreyk bp->bp_flags |= BSTP_PORT_AUTOPTP;
2119b1b9d014Sreyk
2120b1b9d014Sreyk bstp_ifupdstatus(bs, bp);
2121b1b9d014Sreyk }
2122b1b9d014Sreyk } else
2123ff7746e3Sreyk bp->bp_flags &= ~BSTP_PORT_AUTOPTP;
2124b1b9d014Sreyk
2125ff7746e3Sreyk if (flags & IFBIF_BSTP_PTP)
2126ff7746e3Sreyk bp->bp_ptp_link = 1;
2127b1b9d014Sreyk else
2128ff7746e3Sreyk bp->bp_ptp_link = 0;
2129b1b9d014Sreyk }
2130b1b9d014Sreyk
2131b1b9d014Sreyk int
bstp_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)2132b1b9d014Sreyk bstp_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
21339fa0b7aaSjason {
21342d18ef82Scamield struct bridge_softc *sc = (struct bridge_softc *)ifp->if_softc;
2135b1b9d014Sreyk struct bstp_state *bs = sc->sc_stp;
2136b1b9d014Sreyk struct ifbrparam *ifbp = (struct ifbrparam *)data;
2137b1b9d014Sreyk struct ifbreq *ifbr = (struct ifbreq *)data;
213867b85364Smpi struct bridge_iflist *bif;
2139b1b9d014Sreyk struct bstp_port *bp;
2140b1b9d014Sreyk int r = 0, err = 0, val;
2141b1b9d014Sreyk
2142b1b9d014Sreyk switch (cmd) {
2143b1b9d014Sreyk case SIOCBRDGSIFPRIO:
2144b1b9d014Sreyk case SIOCBRDGSIFCOST:
2145044aaac6Smvs err = bridge_findbif(sc, ifbr->ifbr_ifsname, &bif);
2146044aaac6Smvs if (err != 0)
2147b1b9d014Sreyk break;
214867b85364Smpi if ((bif->bif_flags & IFBIF_STP) == 0) {
2149b1b9d014Sreyk err = EINVAL;
2150b1b9d014Sreyk break;
2151b1b9d014Sreyk }
215267b85364Smpi bp = bif->bif_stp;
2153b1b9d014Sreyk break;
2154b1b9d014Sreyk default:
2155b1b9d014Sreyk break;
2156b1b9d014Sreyk }
2157b1b9d014Sreyk if (err)
2158b1b9d014Sreyk return (err);
21599fa0b7aaSjason
21609fa0b7aaSjason switch (cmd) {
21619fa0b7aaSjason case SIOCBRDGGPRI:
2162b1b9d014Sreyk ifbp->ifbrp_prio = bs->bs_bridge_priority;
21639fa0b7aaSjason break;
21649fa0b7aaSjason case SIOCBRDGSPRI:
2165b1b9d014Sreyk val = ifbp->ifbrp_prio;
2166b1b9d014Sreyk if (val < 0 || val > BSTP_MAX_PRIORITY) {
2167b1b9d014Sreyk err = EINVAL;
2168b1b9d014Sreyk break;
2169b1b9d014Sreyk }
2170b1b9d014Sreyk
2171b1b9d014Sreyk /* Limit to steps of 4096 */
2172b1b9d014Sreyk val -= val % 4096;
2173b1b9d014Sreyk bs->bs_bridge_priority = val;
21749fa0b7aaSjason r = 1;
21759fa0b7aaSjason break;
2176b1b9d014Sreyk case SIOCBRDGGMA:
2177b1b9d014Sreyk ifbp->ifbrp_maxage = bs->bs_bridge_max_age >> 8;
2178b1b9d014Sreyk break;
21799fa0b7aaSjason case SIOCBRDGSMA:
2180b1b9d014Sreyk val = ifbp->ifbrp_maxage;
2181b1b9d014Sreyk
2182b1b9d014Sreyk /* convert seconds to ticks */
2183b1b9d014Sreyk val *= BSTP_TICK_VAL;
2184b1b9d014Sreyk
2185b1b9d014Sreyk if (val < BSTP_MIN_MAX_AGE || val > BSTP_MAX_MAX_AGE) {
21869fa0b7aaSjason err = EINVAL;
21879fa0b7aaSjason break;
21889fa0b7aaSjason }
2189b1b9d014Sreyk bs->bs_bridge_max_age = val;
21909fa0b7aaSjason r = 1;
21919fa0b7aaSjason break;
2192b1b9d014Sreyk case SIOCBRDGGHT:
2193b1b9d014Sreyk ifbp->ifbrp_hellotime = bs->bs_bridge_htime >> 8;
2194b1b9d014Sreyk break;
21959fa0b7aaSjason case SIOCBRDGSHT:
2196b1b9d014Sreyk val = ifbp->ifbrp_hellotime;
2197b1b9d014Sreyk
2198b1b9d014Sreyk /* convert seconds to ticks */
2199b1b9d014Sreyk val *= BSTP_TICK_VAL;
2200b1b9d014Sreyk
2201*678831beSjsg /* value can only be changed in legacy stp mode */
2202b1b9d014Sreyk if (bs->bs_protover != BSTP_PROTO_STP) {
2203b1b9d014Sreyk err = EPERM;
2204b1b9d014Sreyk break;
2205b1b9d014Sreyk }
2206b1b9d014Sreyk if (val < BSTP_MIN_HELLO_TIME || val > BSTP_MAX_HELLO_TIME) {
22079fa0b7aaSjason err = EINVAL;
22089fa0b7aaSjason break;
22099fa0b7aaSjason }
2210b1b9d014Sreyk bs->bs_bridge_htime = val;
22119fa0b7aaSjason r = 1;
22129fa0b7aaSjason break;
2213b1b9d014Sreyk case SIOCBRDGGFD:
2214b1b9d014Sreyk ifbp->ifbrp_fwddelay = bs->bs_bridge_fdelay >> 8;
2215b1b9d014Sreyk break;
22169fa0b7aaSjason case SIOCBRDGSFD:
2217b1b9d014Sreyk val = ifbp->ifbrp_fwddelay;
2218b1b9d014Sreyk
2219b1b9d014Sreyk /* convert seconds to ticks */
2220b1b9d014Sreyk val *= BSTP_TICK_VAL;
2221b1b9d014Sreyk
2222b1b9d014Sreyk if (val < BSTP_MIN_FORWARD_DELAY ||
2223b1b9d014Sreyk val > BSTP_MAX_FORWARD_DELAY) {
22249fa0b7aaSjason err = EINVAL;
22259fa0b7aaSjason break;
22269fa0b7aaSjason }
2227b1b9d014Sreyk bs->bs_bridge_fdelay = val;
22289fa0b7aaSjason r = 1;
22299fa0b7aaSjason break;
2230b1b9d014Sreyk case SIOCBRDGSTXHC:
2231b1b9d014Sreyk val = ifbp->ifbrp_txhc;
2232b1b9d014Sreyk
2233b1b9d014Sreyk if (val < BSTP_MIN_HOLD_COUNT || val > BSTP_MAX_HOLD_COUNT) {
2234b1b9d014Sreyk err = EINVAL;
2235b1b9d014Sreyk break;
2236b1b9d014Sreyk }
2237b1b9d014Sreyk bs->bs_txholdcount = val;
2238b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
2239b1b9d014Sreyk bp->bp_txcount = 0;
2240b1b9d014Sreyk break;
22419fa0b7aaSjason case SIOCBRDGSIFPRIO:
2242b1b9d014Sreyk val = ifbr->ifbr_priority;
2243b1b9d014Sreyk if (val < 0 || val > BSTP_MAX_PORT_PRIORITY)
2244b1b9d014Sreyk return (EINVAL);
2245b1b9d014Sreyk
2246b1b9d014Sreyk /* Limit to steps of 16 */
2247b1b9d014Sreyk val -= val % 16;
2248b1b9d014Sreyk bp->bp_priority = val;
22499fa0b7aaSjason r = 1;
22509fa0b7aaSjason break;
2251b1b9d014Sreyk case SIOCBRDGSIFCOST:
2252b1b9d014Sreyk val = ifbr->ifbr_path_cost;
2253b1b9d014Sreyk if (val > BSTP_MAX_PATH_COST) {
2254b1b9d014Sreyk err = EINVAL;
2255b1b9d014Sreyk break;
2256b1b9d014Sreyk }
2257b1b9d014Sreyk if (val == 0) { /* use auto */
2258b1b9d014Sreyk bp->bp_flags &= ~BSTP_PORT_ADMCOST;
2259b1b9d014Sreyk bp->bp_path_cost = bstp_calc_path_cost(bp);
2260b1b9d014Sreyk } else {
2261b1b9d014Sreyk bp->bp_path_cost = val;
2262b1b9d014Sreyk bp->bp_flags |= BSTP_PORT_ADMCOST;
2263b1b9d014Sreyk }
2264b1b9d014Sreyk r = 1;
2265b1b9d014Sreyk break;
2266b1b9d014Sreyk case SIOCBRDGSPROTO:
2267b1b9d014Sreyk val = ifbp->ifbrp_proto;
2268b1b9d014Sreyk
2269b1b9d014Sreyk /* Supported protocol versions */
2270b1b9d014Sreyk switch (val) {
2271b1b9d014Sreyk case BSTP_PROTO_STP:
2272b1b9d014Sreyk case BSTP_PROTO_RSTP:
2273b1b9d014Sreyk bs->bs_protover = val;
2274b1b9d014Sreyk bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
2275b1b9d014Sreyk LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
2276b1b9d014Sreyk /* reinit state */
2277b1b9d014Sreyk bp->bp_infois = BSTP_INFO_DISABLED;
2278b1b9d014Sreyk bp->bp_txcount = 0;
2279b1b9d014Sreyk bstp_set_port_proto(bp, bs->bs_protover);
2280b1b9d014Sreyk bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
2281b1b9d014Sreyk bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
2282b1b9d014Sreyk bstp_timer_stop(&bp->bp_recent_backup_timer);
2283b1b9d014Sreyk }
2284b1b9d014Sreyk r = 1;
2285b1b9d014Sreyk break;
2286b1b9d014Sreyk default:
2287b1b9d014Sreyk err = EINVAL;
2288b1b9d014Sreyk }
2289b1b9d014Sreyk break;
22909fa0b7aaSjason default:
22919fa0b7aaSjason break;
22929fa0b7aaSjason }
22939fa0b7aaSjason
22949fa0b7aaSjason if (r)
2295b1b9d014Sreyk bstp_initialization(bs);
22969fa0b7aaSjason
22979fa0b7aaSjason return (err);
22989fa0b7aaSjason }
2299