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