xref: /dflybsd-src/sys/net/bridge/bridgestp.c (revision c443c74f42cc6a7f2385d56cc2b07c21939330d1)
1ac93838fSSimon Schubert /*
2ac93838fSSimon Schubert  * Copyright (c) 2000 Jason L. Wright (jason@thought.net)
3ac93838fSSimon Schubert  * All rights reserved.
4ac93838fSSimon Schubert  *
5ac93838fSSimon Schubert  * Redistribution and use in source and binary forms, with or without
6ac93838fSSimon Schubert  * modification, are permitted provided that the following conditions
7ac93838fSSimon Schubert  * are met:
8ac93838fSSimon Schubert  * 1. Redistributions of source code must retain the above copyright
9ac93838fSSimon Schubert  *    notice, this list of conditions and the following disclaimer.
10ac93838fSSimon Schubert  * 2. Redistributions in binary form must reproduce the above copyright
11ac93838fSSimon Schubert  *    notice, this list of conditions and the following disclaimer in the
12ac93838fSSimon Schubert  *    documentation and/or other materials provided with the distribution.
13ac93838fSSimon Schubert  * 3. All advertising materials mentioning features or use of this software
14ac93838fSSimon Schubert  *    must display the following acknowledgement:
15ac93838fSSimon Schubert  *      This product includes software developed by Jason L. Wright
16ac93838fSSimon Schubert  * 4. The name of the author may not be used to endorse or promote products
17ac93838fSSimon Schubert  *    derived from this software without specific prior written permission.
18ac93838fSSimon Schubert  *
19ac93838fSSimon Schubert  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20ac93838fSSimon Schubert  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21ac93838fSSimon Schubert  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22ac93838fSSimon Schubert  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23ac93838fSSimon Schubert  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24ac93838fSSimon Schubert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25ac93838fSSimon Schubert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26ac93838fSSimon Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27ac93838fSSimon Schubert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28ac93838fSSimon Schubert  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29ac93838fSSimon Schubert  * POSSIBILITY OF SUCH DAMAGE.
30ac93838fSSimon Schubert  *
31ac93838fSSimon Schubert  * $OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp $
32ac93838fSSimon Schubert  * $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $
33ac93838fSSimon Schubert  * $FreeBSD: src/sys/net/bridgestp.c,v 1.7 2005/10/11 02:58:32 thompsa Exp $
34ac93838fSSimon Schubert  */
35ac93838fSSimon Schubert 
36ac93838fSSimon Schubert /*
37ac93838fSSimon Schubert  * Implementation of the spanning tree protocol as defined in
38ac93838fSSimon Schubert  * ISO/IEC Final DIS 15802-3 (IEEE P802.1D/D17), May 25, 1998.
39ac93838fSSimon Schubert  * (In English: IEEE 802.1D, Draft 17, 1998)
40ac93838fSSimon Schubert  */
41ac93838fSSimon Schubert 
42ac93838fSSimon Schubert #include <sys/param.h>
43ac93838fSSimon Schubert #include <sys/systm.h>
44*c443c74fSzrj #include <sys/malloc.h>	/* for M_NOWAIT */
45ac93838fSSimon Schubert #include <sys/mbuf.h>
46ac93838fSSimon Schubert #include <sys/socket.h>
47ac93838fSSimon Schubert #include <sys/sockio.h>
48ac93838fSSimon Schubert #include <sys/kernel.h>
49ac93838fSSimon Schubert #include <sys/callout.h>
50ac93838fSSimon Schubert #include <sys/proc.h>
51ac93838fSSimon Schubert #include <sys/lock.h>
52ac93838fSSimon Schubert #include <sys/thread.h>
53ac93838fSSimon Schubert #include <sys/thread2.h>
54e9d22060SSepherosa Ziehau #include <sys/msgport2.h>
55ac93838fSSimon Schubert 
56ac93838fSSimon Schubert #include <net/if.h>
57ac93838fSSimon Schubert #include <net/if_dl.h>
58ac93838fSSimon Schubert #include <net/if_types.h>
59ac93838fSSimon Schubert #include <net/if_llc.h>
60ac93838fSSimon Schubert #include <net/if_media.h>
61ac93838fSSimon Schubert 
62ac93838fSSimon Schubert #include <netinet/in.h>
63ac93838fSSimon Schubert #include <netinet/in_systm.h>
64ac93838fSSimon Schubert #include <netinet/in_var.h>
65ac93838fSSimon Schubert #include <netinet/if_ether.h>
66ac93838fSSimon Schubert #include <net/bridge/if_bridgevar.h>
67ac93838fSSimon Schubert 
68ac93838fSSimon Schubert /* BPDU message types */
69ac93838fSSimon Schubert #define	BSTP_MSGTYPE_CFG	0x00		/* Configuration */
70ac93838fSSimon Schubert #define	BSTP_MSGTYPE_TCN	0x80		/* Topology chg notification */
71ac93838fSSimon Schubert 
72ac93838fSSimon Schubert /* BPDU flags */
73ac93838fSSimon Schubert #define	BSTP_FLAG_TC		0x01		/* Topology change */
74ac93838fSSimon Schubert #define	BSTP_FLAG_TCA		0x80		/* Topology change ack */
75ac93838fSSimon Schubert 
76ac93838fSSimon Schubert #define	BSTP_MESSAGE_AGE_INCR	(1 * 256)	/* in 256ths of a second */
77ac93838fSSimon Schubert #define	BSTP_TICK_VAL		(1 * 256)	/* in 256ths of a second */
78ac93838fSSimon Schubert 
79ac93838fSSimon Schubert /*
80ac93838fSSimon Schubert  * Because BPDU's do not make nicely aligned structures, two different
81ac93838fSSimon Schubert  * declarations are used: bstp_?bpdu (wire representation, packed) and
82ac93838fSSimon Schubert  * bstp_*_unit (internal, nicely aligned version).
83ac93838fSSimon Schubert  */
84ac93838fSSimon Schubert 
85ac93838fSSimon Schubert /* configuration bridge protocol data unit */
86ac93838fSSimon Schubert struct bstp_cbpdu {
87ac93838fSSimon Schubert 	uint8_t		cbu_dsap;		/* LLC: destination sap */
88ac93838fSSimon Schubert 	uint8_t		cbu_ssap;		/* LLC: source sap */
89ac93838fSSimon Schubert 	uint8_t		cbu_ctl;		/* LLC: control */
90ac93838fSSimon Schubert 	uint16_t	cbu_protoid;		/* protocol id */
91ac93838fSSimon Schubert 	uint8_t		cbu_protover;		/* protocol version */
92ac93838fSSimon Schubert 	uint8_t		cbu_bpdutype;		/* message type */
93ac93838fSSimon Schubert 	uint8_t		cbu_flags;		/* flags (below) */
94ac93838fSSimon Schubert 
95ac93838fSSimon Schubert 	/* root id */
96ac93838fSSimon Schubert 	uint16_t	cbu_rootpri;		/* root priority */
97ac93838fSSimon Schubert 	uint8_t	cbu_rootaddr[6];	/* root address */
98ac93838fSSimon Schubert 
99ac93838fSSimon Schubert 	uint32_t	cbu_rootpathcost;	/* root path cost */
100ac93838fSSimon Schubert 
101ac93838fSSimon Schubert 	/* bridge id */
102ac93838fSSimon Schubert 	uint16_t	cbu_bridgepri;		/* bridge priority */
103ac93838fSSimon Schubert 	uint8_t		cbu_bridgeaddr[6];	/* bridge address */
104ac93838fSSimon Schubert 
105ac93838fSSimon Schubert 	uint16_t	cbu_portid;		/* port id */
106ac93838fSSimon Schubert 	uint16_t	cbu_messageage;		/* current message age */
107ac93838fSSimon Schubert 	uint16_t	cbu_maxage;		/* maximum age */
108ac93838fSSimon Schubert 	uint16_t	cbu_hellotime;		/* hello time */
109ac93838fSSimon Schubert 	uint16_t	cbu_forwarddelay;	/* forwarding delay */
110ac93838fSSimon Schubert } __attribute__((__packed__));
111ac93838fSSimon Schubert 
112ac93838fSSimon Schubert /* topology change notification bridge protocol data unit */
113ac93838fSSimon Schubert struct bstp_tbpdu {
114ac93838fSSimon Schubert 	uint8_t		tbu_dsap;		/* LLC: destination sap */
115ac93838fSSimon Schubert 	uint8_t		tbu_ssap;		/* LLC: source sap */
116ac93838fSSimon Schubert 	uint8_t		tbu_ctl;		/* LLC: control */
117ac93838fSSimon Schubert 	uint16_t	tbu_protoid;		/* protocol id */
118ac93838fSSimon Schubert 	uint8_t		tbu_protover;		/* protocol version */
119ac93838fSSimon Schubert 	uint8_t		tbu_bpdutype;		/* message type */
120ac93838fSSimon Schubert } __attribute__((__packed__));
121ac93838fSSimon Schubert 
122ac93838fSSimon Schubert const uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
123ac93838fSSimon Schubert 
124d217f5e5SScott Ullrich static void	bstp_initialize_port(struct bridge_softc *,
125d217f5e5SScott Ullrich 		    struct bridge_iflist *);
126d217f5e5SScott Ullrich static void	bstp_ifupdstatus(struct bridge_softc *, struct bridge_iflist *);
127d217f5e5SScott Ullrich static void	bstp_enable_port(struct bridge_softc *, struct bridge_iflist *);
128d217f5e5SScott Ullrich static void	bstp_disable_port(struct bridge_softc *,
129d217f5e5SScott Ullrich 		    struct bridge_iflist *);
130d217f5e5SScott Ullrich #ifdef notused
131d217f5e5SScott Ullrich static void	bstp_enable_change_detection(struct bridge_iflist *);
132d217f5e5SScott Ullrich static void	bstp_disable_change_detection(struct bridge_iflist *);
133d217f5e5SScott Ullrich #endif /* notused */
134d217f5e5SScott Ullrich static int	bstp_root_bridge(struct bridge_softc *sc);
135d217f5e5SScott Ullrich static void	bstp_transmit_config(struct bridge_softc *,
136d217f5e5SScott Ullrich 		    struct bridge_iflist *);
137d217f5e5SScott Ullrich static void	bstp_transmit_tcn(struct bridge_softc *);
138d217f5e5SScott Ullrich static void	bstp_received_config_bpdu(struct bridge_softc *,
139ac93838fSSimon Schubert 		    struct bridge_iflist *, struct bstp_config_unit *);
140d217f5e5SScott Ullrich static void	bstp_received_tcn_bpdu(struct bridge_softc *,
141d217f5e5SScott Ullrich 		    struct bridge_iflist *, struct bstp_tcn_unit *);
142d217f5e5SScott Ullrich static void	bstp_record_config_information(struct bridge_softc *,
143ac93838fSSimon Schubert 		    struct bridge_iflist *, struct bstp_config_unit *);
144d217f5e5SScott Ullrich static void	bstp_record_config_timeout_values(struct bridge_softc *,
145ac93838fSSimon Schubert 		    struct bstp_config_unit *);
146d217f5e5SScott Ullrich static void	bstp_config_bpdu_generation(struct bridge_softc *);
147d217f5e5SScott Ullrich static void	bstp_send_config_bpdu(struct bridge_softc *,
148d217f5e5SScott Ullrich 		    struct bridge_iflist *, struct bstp_config_unit *);
149d217f5e5SScott Ullrich static void	bstp_configuration_update(struct bridge_softc *);
150d217f5e5SScott Ullrich static void	bstp_port_state_selection(struct bridge_softc *);
15170d9a675SMatthew Dillon static void	bstp_clear_peer_info(struct bridge_softc *,
15270d9a675SMatthew Dillon 		    struct bridge_iflist *);
153d217f5e5SScott Ullrich static void	bstp_make_forwarding(struct bridge_softc *,
154d217f5e5SScott Ullrich 		    struct bridge_iflist *);
155d217f5e5SScott Ullrich static void	bstp_make_blocking(struct bridge_softc *,
156d217f5e5SScott Ullrich 		    struct bridge_iflist *);
1573677aae9SMatthew Dillon static void	bstp_make_l1blocking(struct bridge_softc *sc,
1583677aae9SMatthew Dillon 		    struct bridge_iflist *bif);
1591e858374SMatthew Dillon static void	bstp_adjust_bonded_states(struct bridge_softc *sc,
1601e858374SMatthew Dillon 		    struct bridge_iflist *obif);
161d217f5e5SScott Ullrich static void	bstp_set_port_state(struct bridge_iflist *, uint8_t);
162d217f5e5SScott Ullrich #ifdef notused
163d217f5e5SScott Ullrich static void	bstp_set_bridge_priority(struct bridge_softc *, uint64_t);
164d217f5e5SScott Ullrich static void	bstp_set_port_priority(struct bridge_softc *,
165d217f5e5SScott Ullrich 		    struct bridge_iflist *, uint16_t);
166d217f5e5SScott Ullrich static void	bstp_set_path_cost(struct bridge_softc *,
167d217f5e5SScott Ullrich 		    struct bridge_iflist *, uint32_t);
168d217f5e5SScott Ullrich #endif /* notused */
169d217f5e5SScott Ullrich static void	bstp_topology_change_detection(struct bridge_softc *);
170d217f5e5SScott Ullrich static void	bstp_topology_change_acknowledged(struct bridge_softc *);
171d217f5e5SScott Ullrich static void	bstp_acknowledge_topology_change(struct bridge_softc *,
172ac93838fSSimon Schubert 		    struct bridge_iflist *);
173ac93838fSSimon Schubert 
174d217f5e5SScott Ullrich static void	bstp_tick(void *);
175d217f5e5SScott Ullrich static void	bstp_timer_start(struct bridge_timer *, uint16_t);
176d217f5e5SScott Ullrich static void	bstp_timer_stop(struct bridge_timer *);
177d217f5e5SScott Ullrich static int	bstp_timer_expired(struct bridge_timer *, uint16_t);
178ac93838fSSimon Schubert 
179d217f5e5SScott Ullrich static void	bstp_hold_timer_expiry(struct bridge_softc *,
180ac93838fSSimon Schubert 		    struct bridge_iflist *);
181d217f5e5SScott Ullrich static void	bstp_message_age_timer_expiry(struct bridge_softc *,
182ac93838fSSimon Schubert 		    struct bridge_iflist *);
183d217f5e5SScott Ullrich static void	bstp_forward_delay_timer_expiry(struct bridge_softc *,
184d217f5e5SScott Ullrich 		    struct bridge_iflist *);
185d217f5e5SScott Ullrich static void	bstp_topology_change_timer_expiry(struct bridge_softc *);
186d217f5e5SScott Ullrich static void	bstp_tcn_timer_expiry(struct bridge_softc *);
187d217f5e5SScott Ullrich static void	bstp_hello_timer_expiry(struct bridge_softc *);
188d217f5e5SScott Ullrich static int	bstp_addr_cmp(const uint8_t *, const uint8_t *);
189ac93838fSSimon Schubert 
19070d9a675SMatthew Dillon /*
19170d9a675SMatthew Dillon  * When transmitting a config we tack on our path cost to
19270d9a675SMatthew Dillon  * our aggregated path-to-root cost.
1933110e56aSMatthew Dillon  *
1943110e56aSMatthew Dillon  * Note that sc_designated_cost is the lowest root path cost from all
1953110e56aSMatthew Dillon  * incoming links.  When one is talking about two bridges with multiple
1963110e56aSMatthew Dillon  * links between them the root path cost the secondary bridge transmits
1973110e56aSMatthew Dillon  * will be the lowest root path cost it received across those three
1983110e56aSMatthew Dillon  * links and NOT necessarily the root path cost it received specifically
1993110e56aSMatthew Dillon  * on that particular link.  The receiving end must still add its local
2003110e56aSMatthew Dillon  * link cost back in for differentiation purposes.
20170d9a675SMatthew Dillon  */
202d217f5e5SScott Ullrich static void
bstp_transmit_config(struct bridge_softc * sc,struct bridge_iflist * bif)203ac93838fSSimon Schubert bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif)
204ac93838fSSimon Schubert {
205ac93838fSSimon Schubert 	if (bif->bif_hold_timer.active) {
206ac93838fSSimon Schubert 		bif->bif_config_pending = 1;
207ac93838fSSimon Schubert 		return;
208ac93838fSSimon Schubert 	}
209ac93838fSSimon Schubert 
210ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_message_type = BSTP_MSGTYPE_CFG;
211ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_rootid = sc->sc_designated_root;
21270d9a675SMatthew Dillon 	bif->bif_config_bpdu.cu_root_path_cost = sc->sc_designated_cost +
21370d9a675SMatthew Dillon 						 bif->bif_path_cost;
214ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_bridge_id = sc->sc_bridge_id;
215ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_port_id = bif->bif_port_id;
216ac93838fSSimon Schubert 
2178f7b13efSSepherosa Ziehau 	if (bstp_root_bridge(sc)) {
218ac93838fSSimon Schubert 		bif->bif_config_bpdu.cu_message_age = 0;
21970d9a675SMatthew Dillon 	} else if (sc->sc_root_port) {
220ac93838fSSimon Schubert 		bif->bif_config_bpdu.cu_message_age =
22170d9a675SMatthew Dillon 			sc->sc_root_port->bif_message_age_timer.value +
222ac93838fSSimon Schubert 			BSTP_MESSAGE_AGE_INCR;
22370d9a675SMatthew Dillon 	} else {
22470d9a675SMatthew Dillon 		bif->bif_config_bpdu.cu_message_age = BSTP_MESSAGE_AGE_INCR;
2258f7b13efSSepherosa Ziehau 	}
226ac93838fSSimon Schubert 
227ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_max_age = sc->sc_max_age;
228ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_hello_time = sc->sc_hello_time;
229ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_forward_delay = sc->sc_forward_delay;
230ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_topology_change_acknowledgment
231ac93838fSSimon Schubert 	    = bif->bif_topology_change_acknowledge;
232ac93838fSSimon Schubert 	bif->bif_config_bpdu.cu_topology_change = sc->sc_topology_change;
233ac93838fSSimon Schubert 
2343677aae9SMatthew Dillon 	if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age ||
2353677aae9SMatthew Dillon 	    (sc->sc_ifp->if_flags & IFF_LINK1)) {
236ac93838fSSimon Schubert 		bif->bif_topology_change_acknowledge = 0;
237ac93838fSSimon Schubert 		bif->bif_config_pending = 0;
238ac93838fSSimon Schubert 		bstp_send_config_bpdu(sc, bif, &bif->bif_config_bpdu);
239ac93838fSSimon Schubert 		bstp_timer_start(&bif->bif_hold_timer, 0);
240ac93838fSSimon Schubert 	}
241ac93838fSSimon Schubert }
242ac93838fSSimon Schubert 
243d217f5e5SScott Ullrich static void
bstp_send_config_bpdu(struct bridge_softc * sc,struct bridge_iflist * bif,struct bstp_config_unit * cu)244ac93838fSSimon Schubert bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
245ac93838fSSimon Schubert 		      struct bstp_config_unit *cu)
246ac93838fSSimon Schubert {
247ac93838fSSimon Schubert 	struct ifnet *ifp;
248ac93838fSSimon Schubert 	struct mbuf *m;
249ac93838fSSimon Schubert 	struct ether_header *eh;
250ac93838fSSimon Schubert 	struct bstp_cbpdu bpdu;
251ac93838fSSimon Schubert 
252ac93838fSSimon Schubert 	ifp = bif->bif_ifp;
253ac93838fSSimon Schubert 
254ac93838fSSimon Schubert 	if ((ifp->if_flags & IFF_RUNNING) == 0)
255ac93838fSSimon Schubert 		return;
256ac93838fSSimon Schubert 
257b5523eacSSascha Wildner 	MGETHDR(m, M_NOWAIT, MT_DATA);
258ac93838fSSimon Schubert 	if (m == NULL)
259ac93838fSSimon Schubert 		return;
260ac93838fSSimon Schubert 
261ac93838fSSimon Schubert 	eh = mtod(m, struct ether_header *);
262ac93838fSSimon Schubert 
263ac93838fSSimon Schubert 	m->m_pkthdr.rcvif = ifp;
264ac93838fSSimon Schubert 	m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
265ac93838fSSimon Schubert 	m->m_len = m->m_pkthdr.len;
266ac93838fSSimon Schubert 
267ac93838fSSimon Schubert 	bpdu.cbu_ssap = bpdu.cbu_dsap = LLC_8021D_LSAP;
268ac93838fSSimon Schubert 	bpdu.cbu_ctl = LLC_UI;
269ac93838fSSimon Schubert 	bpdu.cbu_protoid = htons(0);
270ac93838fSSimon Schubert 	bpdu.cbu_protover = 0;
271ac93838fSSimon Schubert 	bpdu.cbu_bpdutype = cu->cu_message_type;
272ac93838fSSimon Schubert 	bpdu.cbu_flags = (cu->cu_topology_change ? BSTP_FLAG_TC : 0) |
273ac93838fSSimon Schubert 	    (cu->cu_topology_change_acknowledgment ? BSTP_FLAG_TCA : 0);
274ac93838fSSimon Schubert 
275ac93838fSSimon Schubert 	bpdu.cbu_rootpri = htons(cu->cu_rootid >> 48);
276ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[0] = cu->cu_rootid >> 40;
277ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[1] = cu->cu_rootid >> 32;
278ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[2] = cu->cu_rootid >> 24;
279ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[3] = cu->cu_rootid >> 16;
280ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[4] = cu->cu_rootid >> 8;
281ac93838fSSimon Schubert 	bpdu.cbu_rootaddr[5] = cu->cu_rootid >> 0;
282ac93838fSSimon Schubert 
283ac93838fSSimon Schubert 	bpdu.cbu_rootpathcost = htonl(cu->cu_root_path_cost);
284ac93838fSSimon Schubert 
285d217f5e5SScott Ullrich 	bpdu.cbu_bridgepri = htons(cu->cu_bridge_id >> 48);
286d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[0] = cu->cu_bridge_id >> 40;
287d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[1] = cu->cu_bridge_id >> 32;
288d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[2] = cu->cu_bridge_id >> 24;
289d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[3] = cu->cu_bridge_id >> 16;
290d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[4] = cu->cu_bridge_id >> 8;
291d217f5e5SScott Ullrich 	bpdu.cbu_bridgeaddr[5] = cu->cu_bridge_id >> 0;
292ac93838fSSimon Schubert 
293ac93838fSSimon Schubert 	bpdu.cbu_portid = htons(cu->cu_port_id);
294ac93838fSSimon Schubert 	bpdu.cbu_messageage = htons(cu->cu_message_age);
295ac93838fSSimon Schubert 	bpdu.cbu_maxage = htons(cu->cu_max_age);
296ac93838fSSimon Schubert 	bpdu.cbu_hellotime = htons(cu->cu_hello_time);
297ac93838fSSimon Schubert 	bpdu.cbu_forwarddelay = htons(cu->cu_forward_delay);
298ac93838fSSimon Schubert 
29970d9a675SMatthew Dillon 	/*
30070d9a675SMatthew Dillon 	 * Packets sent from the bridge always use the bridge MAC
30170d9a675SMatthew Dillon 	 * as the source.
30270d9a675SMatthew Dillon 	 */
30370d9a675SMatthew Dillon 	memcpy(eh->ether_shost, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
304ac93838fSSimon Schubert 	memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
305ac93838fSSimon Schubert 	eh->ether_type = htons(sizeof(bpdu));
306ac93838fSSimon Schubert 
307ac93838fSSimon Schubert 	memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
308ac93838fSSimon Schubert 
309708a3bfaSSepherosa Ziehau 	bridge_enqueue(ifp, m);
310ac93838fSSimon Schubert }
311ac93838fSSimon Schubert 
312d217f5e5SScott Ullrich static int
bstp_root_bridge(struct bridge_softc * sc)313ac93838fSSimon Schubert bstp_root_bridge(struct bridge_softc *sc)
314ac93838fSSimon Schubert {
315ac93838fSSimon Schubert 	return (sc->sc_designated_root == sc->sc_bridge_id);
316ac93838fSSimon Schubert }
317ac93838fSSimon Schubert 
31870d9a675SMatthew Dillon /*
31970d9a675SMatthew Dillon  * Returns TRUE if the recorded information from our peer has a shorter
32070d9a675SMatthew Dillon  * graph distance than our current best.
32170d9a675SMatthew Dillon  */
32270d9a675SMatthew Dillon int
bstp_supersedes_port_info(struct bridge_softc * sc,struct bridge_iflist * bif)32370d9a675SMatthew Dillon bstp_supersedes_port_info(struct bridge_softc *sc, struct bridge_iflist *bif)
324ac93838fSSimon Schubert {
32570d9a675SMatthew Dillon 	if (bif->bif_peer_root < sc->sc_designated_root)
326ac93838fSSimon Schubert 		return (1);
32770d9a675SMatthew Dillon 	if (bif->bif_peer_root > sc->sc_designated_root)
328ac93838fSSimon Schubert 		return (0);
329ac93838fSSimon Schubert 
33070d9a675SMatthew Dillon 	/*
3313110e56aSMatthew Dillon 	 * bif_peer_cost and sc_designated_cost do not include the local
3323110e56aSMatthew Dillon 	 * bif_path_cost, otherwise we would not be able to make this
3333110e56aSMatthew Dillon 	 * comparison.
33470d9a675SMatthew Dillon 	 */
33570d9a675SMatthew Dillon 	if (bif->bif_peer_cost < sc->sc_designated_cost)
336ac93838fSSimon Schubert 		return (1);
33770d9a675SMatthew Dillon 	if (bif->bif_peer_cost > sc->sc_designated_cost)
338ac93838fSSimon Schubert 		return (0);
339ac93838fSSimon Schubert 
3403110e56aSMatthew Dillon 	/*
3413110e56aSMatthew Dillon 	 * When the peer costs match we have to check the local path
3423110e56aSMatthew Dillon 	 * cost against the selected root port.  sc_designated_cost
3433110e56aSMatthew Dillon 	 * in an aggregation and cannot be used.
3443110e56aSMatthew Dillon 	 */
3453110e56aSMatthew Dillon 	if (sc->sc_root_port &&
3463110e56aSMatthew Dillon 	    bif->bif_path_cost < sc->sc_root_port->bif_path_cost)
3473110e56aSMatthew Dillon 		return (1);
3483110e56aSMatthew Dillon 	if (sc->sc_root_port &&
3493110e56aSMatthew Dillon 	    bif->bif_path_cost > sc->sc_root_port->bif_path_cost)
3503110e56aSMatthew Dillon 		return (0);
3513110e56aSMatthew Dillon 
3523110e56aSMatthew Dillon 	/*
3533110e56aSMatthew Dillon 	 * If the path costs are identical the bridge with the lowest
3543110e56aSMatthew Dillon 	 * bridge_id wins.
3553110e56aSMatthew Dillon 	 */
35670d9a675SMatthew Dillon 	if (bif->bif_peer_bridge < sc->sc_designated_bridge)
357ac93838fSSimon Schubert 		return (1);
35870d9a675SMatthew Dillon 	if (bif->bif_peer_bridge > sc->sc_designated_bridge)
359ac93838fSSimon Schubert 		return (0);
360ac93838fSSimon Schubert 
3613110e56aSMatthew Dillon 	/*
3623110e56aSMatthew Dillon 	 * bridge_id or bridge+port collision w/peer returns TRUE.
3633110e56aSMatthew Dillon 	 *
3643110e56aSMatthew Dillon 	 * This case can also occur when two bridges are connected
3653110e56aSMatthew Dillon 	 * via multiple links whos path costs have not been set.
3663110e56aSMatthew Dillon 	 */
36770d9a675SMatthew Dillon 	if (bif->bif_peer_bridge != sc->sc_bridge_id)
368ac93838fSSimon Schubert 		return (1);
36970d9a675SMatthew Dillon 	if (bif->bif_peer_port <= sc->sc_designated_port)
370ac93838fSSimon Schubert 		return (1);
371ac93838fSSimon Schubert 	return (0);
372ac93838fSSimon Schubert }
373ac93838fSSimon Schubert 
37470d9a675SMatthew Dillon /*
37570d9a675SMatthew Dillon  * The shorter graph distance represented by cu (most of which is also
37670d9a675SMatthew Dillon  * already stored in our bif_peer_* fields) becomes the designated info.
37770d9a675SMatthew Dillon  *
37870d9a675SMatthew Dillon  * NOTE: sc_designated_cost does not include bif_path_cost, it is added
37970d9a675SMatthew Dillon  *	 in later on a port-by-port basis as needed.
38070d9a675SMatthew Dillon  */
381d217f5e5SScott Ullrich static void
bstp_record_config_information(struct bridge_softc * sc,struct bridge_iflist * bif,struct bstp_config_unit * cu)382ac93838fSSimon Schubert bstp_record_config_information(struct bridge_softc *sc,
38370d9a675SMatthew Dillon 			       struct bridge_iflist *bif,
38470d9a675SMatthew Dillon 			       struct bstp_config_unit *cu)
385ac93838fSSimon Schubert {
38670d9a675SMatthew Dillon 	sc->sc_designated_root = bif->bif_peer_root;
38770d9a675SMatthew Dillon 	sc->sc_designated_cost = bif->bif_peer_cost;
38870d9a675SMatthew Dillon 	sc->sc_designated_bridge = bif->bif_peer_bridge;
38970d9a675SMatthew Dillon 	sc->sc_designated_port = bif->bif_peer_port;
390ac93838fSSimon Schubert 	bstp_timer_start(&bif->bif_message_age_timer, cu->cu_message_age);
391ac93838fSSimon Schubert }
392ac93838fSSimon Schubert 
393d217f5e5SScott Ullrich static void
bstp_record_config_timeout_values(struct bridge_softc * sc,struct bstp_config_unit * config)394ac93838fSSimon Schubert bstp_record_config_timeout_values(struct bridge_softc *sc,
395ac93838fSSimon Schubert 				  struct bstp_config_unit *config)
396ac93838fSSimon Schubert {
397ac93838fSSimon Schubert 	sc->sc_max_age = config->cu_max_age;
398ac93838fSSimon Schubert 	sc->sc_hello_time = config->cu_hello_time;
399ac93838fSSimon Schubert 	sc->sc_forward_delay = config->cu_forward_delay;
400ac93838fSSimon Schubert 	sc->sc_topology_change = config->cu_topology_change;
401ac93838fSSimon Schubert }
402ac93838fSSimon Schubert 
403d217f5e5SScott Ullrich static void
bstp_config_bpdu_generation(struct bridge_softc * sc)404ac93838fSSimon Schubert bstp_config_bpdu_generation(struct bridge_softc *sc)
405ac93838fSSimon Schubert {
4068f7b13efSSepherosa Ziehau 	struct bridge_iflist *bif, *nbif;
407ac93838fSSimon Schubert 
40870d9a675SMatthew Dillon 	TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
409ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
410ac93838fSSimon Schubert 			continue;
4113677aae9SMatthew Dillon 		if (bif->bif_state != BSTP_IFSTATE_DISABLED &&
4123677aae9SMatthew Dillon 		    ((sc->sc_ifp->if_flags & IFF_LINK1) ||
41370d9a675SMatthew Dillon 		     (bif->bif_flags & IFBIF_DESIGNATED))) {
414ac93838fSSimon Schubert 			bstp_transmit_config(sc, bif);
4153677aae9SMatthew Dillon 		}
4168f7b13efSSepherosa Ziehau 
4178f7b13efSSepherosa Ziehau 		if (nbif != NULL && !nbif->bif_onlist) {
4188f7b13efSSepherosa Ziehau 			KKASSERT(bif->bif_onlist);
41970d9a675SMatthew Dillon 			nbif = TAILQ_NEXT(bif, bif_next);
4208f7b13efSSepherosa Ziehau 		}
421ac93838fSSimon Schubert 	}
422ac93838fSSimon Schubert }
423ac93838fSSimon Schubert 
424d217f5e5SScott Ullrich static void
bstp_transmit_tcn(struct bridge_softc * sc)425ac93838fSSimon Schubert bstp_transmit_tcn(struct bridge_softc *sc)
426ac93838fSSimon Schubert {
427ac93838fSSimon Schubert 	struct bstp_tbpdu bpdu;
42870d9a675SMatthew Dillon 	struct ifnet *ifp;
429ac93838fSSimon Schubert 	struct ether_header *eh;
430ac93838fSSimon Schubert 	struct mbuf *m;
431ac93838fSSimon Schubert 
43270d9a675SMatthew Dillon 	if (sc->sc_root_port == NULL)	/* all iterfaces disabled */
43370d9a675SMatthew Dillon 		return;
43470d9a675SMatthew Dillon 
43570d9a675SMatthew Dillon 	ifp = sc->sc_root_port->bif_ifp;
436ac93838fSSimon Schubert 	if ((ifp->if_flags & IFF_RUNNING) == 0)
437ac93838fSSimon Schubert 		return;
438ac93838fSSimon Schubert 
439b5523eacSSascha Wildner 	MGETHDR(m, M_NOWAIT, MT_DATA);
440ac93838fSSimon Schubert 	if (m == NULL)
441ac93838fSSimon Schubert 		return;
442ac93838fSSimon Schubert 
443ac93838fSSimon Schubert 	m->m_pkthdr.rcvif = ifp;
444ac93838fSSimon Schubert 	m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
445ac93838fSSimon Schubert 	m->m_len = m->m_pkthdr.len;
446ac93838fSSimon Schubert 
447ac93838fSSimon Schubert 	eh = mtod(m, struct ether_header *);
448ac93838fSSimon Schubert 
44970d9a675SMatthew Dillon 	/*
45070d9a675SMatthew Dillon 	 * Packets sent from the bridge always use the bridge MAC
45170d9a675SMatthew Dillon 	 * as the source.
45270d9a675SMatthew Dillon 	 */
45370d9a675SMatthew Dillon 	memcpy(eh->ether_shost, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN);
454ac93838fSSimon Schubert 	memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
455ac93838fSSimon Schubert 	eh->ether_type = htons(sizeof(bpdu));
456ac93838fSSimon Schubert 
457ac93838fSSimon Schubert 	bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP;
458ac93838fSSimon Schubert 	bpdu.tbu_ctl = LLC_UI;
459ac93838fSSimon Schubert 	bpdu.tbu_protoid = 0;
460ac93838fSSimon Schubert 	bpdu.tbu_protover = 0;
461ac93838fSSimon Schubert 	bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN;
462ac93838fSSimon Schubert 
463ac93838fSSimon Schubert 	memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
464ac93838fSSimon Schubert 
465708a3bfaSSepherosa Ziehau 	bridge_enqueue(ifp, m);
466ac93838fSSimon Schubert }
467ac93838fSSimon Schubert 
46870d9a675SMatthew Dillon /*
46970d9a675SMatthew Dillon  * Recalculate sc->sc_designated* and sc->sc_root_port (if our bridge
47070d9a675SMatthew Dillon  * is calculated to be the root bridge).  We do this by initializing
47170d9a675SMatthew Dillon  * the designated variables to point at us and then scan our peers.
47270d9a675SMatthew Dillon  * Any uninitialized peers will have a max-value root.
47370d9a675SMatthew Dillon  *
47470d9a675SMatthew Dillon  * Clear IFBIF_DESIGNATED on any ports which no longer match the criteria
47570d9a675SMatthew Dillon  * required to be a designated port.  Only aged out ports and the root
47670d9a675SMatthew Dillon  * port can be designated.
47770d9a675SMatthew Dillon  *
47870d9a675SMatthew Dillon  * If we win we do a second scan to determine which port on our bridge
47970d9a675SMatthew Dillon  * is the best.
48070d9a675SMatthew Dillon  */
481d217f5e5SScott Ullrich static void
bstp_configuration_update(struct bridge_softc * sc)482ac93838fSSimon Schubert bstp_configuration_update(struct bridge_softc *sc)
483ac93838fSSimon Schubert {
48470d9a675SMatthew Dillon 	uint64_t	designated_root = sc->sc_bridge_id;
48570d9a675SMatthew Dillon 	uint64_t	designated_bridge = sc->sc_bridge_id;
48670d9a675SMatthew Dillon 	uint32_t	designated_cost = 0xFFFFFFFFU;
4870e66e711SMatthew Dillon 	uint32_t	designated_root_cost = 0xFFFFFFFFU;
48870d9a675SMatthew Dillon 	uint16_t	designated_port = 65535;
48970d9a675SMatthew Dillon 	struct bridge_iflist *root_port = NULL;
49070d9a675SMatthew Dillon 	struct bridge_iflist *bif;
491ac93838fSSimon Schubert 
49270d9a675SMatthew Dillon 	/*
49370d9a675SMatthew Dillon 	 * Resolve information from our peers.  Aged peers will have
49470d9a675SMatthew Dillon 	 * a maxed bif_peer_root and not come under consideration.
49570d9a675SMatthew Dillon 	 */
49670d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
497ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
498ac93838fSSimon Schubert 			continue;
49970d9a675SMatthew Dillon 		if (bif->bif_state == BSTP_IFSTATE_DISABLED ||
50070d9a675SMatthew Dillon 		    bif->bif_state == BSTP_IFSTATE_L1BLOCKING) {
501ac93838fSSimon Schubert 			continue;
50270d9a675SMatthew Dillon 		}
50370d9a675SMatthew Dillon 
50470d9a675SMatthew Dillon 		if (bif->bif_peer_root > designated_root)
505ac93838fSSimon Schubert 			continue;
50670d9a675SMatthew Dillon 		if (bif->bif_peer_root < designated_root)
507ac93838fSSimon Schubert 			goto set_port;
508ac93838fSSimon Schubert 
50970d9a675SMatthew Dillon 		/*
51070d9a675SMatthew Dillon 		 * NOTE: The designated_cost temporary variable already added
51170d9a675SMatthew Dillon 		 *	 in the path code of the related root port.
51270d9a675SMatthew Dillon 		 */
51370d9a675SMatthew Dillon 		if (bif->bif_peer_cost + bif->bif_path_cost > designated_cost)
51470d9a675SMatthew Dillon 			continue;
51570d9a675SMatthew Dillon 		if (bif->bif_peer_cost + bif->bif_path_cost < designated_cost)
516ac93838fSSimon Schubert 			goto set_port;
51770d9a675SMatthew Dillon 
51870d9a675SMatthew Dillon 		if (bif->bif_peer_bridge > designated_bridge)
51970d9a675SMatthew Dillon 			continue;
52070d9a675SMatthew Dillon 		if (bif->bif_peer_bridge < designated_bridge)
52170d9a675SMatthew Dillon 			goto set_port;
52270d9a675SMatthew Dillon 
52370d9a675SMatthew Dillon 		if (bif->bif_peer_port > designated_port)
52470d9a675SMatthew Dillon 			continue;
52570d9a675SMatthew Dillon 		if (bif->bif_peer_port < designated_port)
52670d9a675SMatthew Dillon 			goto set_port;
52770d9a675SMatthew Dillon 
52870d9a675SMatthew Dillon 		/*
52970d9a675SMatthew Dillon 		 * Same root, path cost, bridge, and port.  Set the root
53070d9a675SMatthew Dillon 		 * only if we do not already have it.
53170d9a675SMatthew Dillon 		 */
53270d9a675SMatthew Dillon 		if (root_port)
533ac93838fSSimon Schubert 			continue;
534ac93838fSSimon Schubert 
53570d9a675SMatthew Dillon 		/*
53670d9a675SMatthew Dillon 		 * New root port (from peers)
53770d9a675SMatthew Dillon 		 *
53870d9a675SMatthew Dillon 		 * NOTE: Temporarily add bif_path_cost into the designated
53970d9a675SMatthew Dillon 		 *	 cost to reduce complexity in the loop, it will be
54070d9a675SMatthew Dillon 		 *	 subtracted out when we are done.
54170d9a675SMatthew Dillon 		 */
542ac93838fSSimon Schubert set_port:
54370d9a675SMatthew Dillon 		designated_root = bif->bif_peer_root;
54470d9a675SMatthew Dillon 		designated_cost = bif->bif_peer_cost + bif->bif_path_cost;
5450e66e711SMatthew Dillon 		designated_root_cost = bif->bif_peer_cost;
54670d9a675SMatthew Dillon 		designated_bridge = bif->bif_peer_bridge;
54770d9a675SMatthew Dillon 		designated_port = bif->bif_peer_port;
548ac93838fSSimon Schubert 		root_port = bif;
549ac93838fSSimon Schubert 	}
550ac93838fSSimon Schubert 
55170d9a675SMatthew Dillon 	/*
55270d9a675SMatthew Dillon 	 * root_port will be NULL at the start here if all of our
55370d9a675SMatthew Dillon 	 * peers are aged or are not as good a root as our bridge would
55470d9a675SMatthew Dillon 	 * be.  It can also be NULL due to all related links being
55570d9a675SMatthew Dillon 	 * disabled.
55670d9a675SMatthew Dillon 	 *
55770d9a675SMatthew Dillon 	 * If the root winds up being our bridge scan again against local
55870d9a675SMatthew Dillon 	 * information.  Unconditionally update IFBIF_DESIGNATED.
55970d9a675SMatthew Dillon 	 */
56070d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
56170d9a675SMatthew Dillon 		bif->bif_flags &= ~(IFBIF_DESIGNATED | IFBIF_ROOT);
562ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
563ac93838fSSimon Schubert 			continue;
56470d9a675SMatthew Dillon 		if (bif->bif_state == BSTP_IFSTATE_DISABLED ||
56570d9a675SMatthew Dillon 		    bif->bif_state == BSTP_IFSTATE_L1BLOCKING) {
566ac93838fSSimon Schubert 			continue;
567ac93838fSSimon Schubert 		}
568ac93838fSSimon Schubert 
56970d9a675SMatthew Dillon 		/*
57070d9a675SMatthew Dillon 		 * Set DESIGNATED for an aged or unknown peer.
57170d9a675SMatthew Dillon 		 */
57270d9a675SMatthew Dillon 		if (bif->bif_peer_bridge == 0xFFFFFFFFFFFFFFFFLLU)
57370d9a675SMatthew Dillon 			bif->bif_flags |= IFBIF_DESIGNATED;
57470d9a675SMatthew Dillon 		if (designated_root != sc->sc_bridge_id)
57570d9a675SMatthew Dillon 			continue;
57670d9a675SMatthew Dillon 
57770d9a675SMatthew Dillon 		/*
57870d9a675SMatthew Dillon 		 * This is only reached if our bridge is the root bridge,
57970d9a675SMatthew Dillon 		 * select the root port (IFBIF_DESIGNATED is set at the
58070d9a675SMatthew Dillon 		 * end).
58170d9a675SMatthew Dillon 		 *
5823110e56aSMatthew Dillon 		 * The peer cost will incorporate the peer bridge's best
5833110e56aSMatthew Dillon 		 * cost to the root bridge (us), which is NOT necessarily
5843110e56aSMatthew Dillon 		 * our path cost, plus the peer bridge's own path cost.
5853110e56aSMatthew Dillon 		 * Because of this we must still add in our path cost for
5863110e56aSMatthew Dillon 		 * further differentiation.  Because the peer's 'best' cost
5873110e56aSMatthew Dillon 		 * is applied to all of its outgoing messages we should
5883110e56aSMatthew Dillon 		 * still come to the same conclusion as the peer.
58970d9a675SMatthew Dillon 		 */
5903110e56aSMatthew Dillon 		if (bif->bif_peer_cost + bif->bif_path_cost > designated_cost)
59170d9a675SMatthew Dillon 			continue;
5923110e56aSMatthew Dillon 		if (bif->bif_peer_cost + bif->bif_path_cost < designated_cost)
59370d9a675SMatthew Dillon 			goto set_port2;
59470d9a675SMatthew Dillon 
59570d9a675SMatthew Dillon 		if (bif->bif_port_id > designated_port)
59670d9a675SMatthew Dillon 			continue;
59770d9a675SMatthew Dillon 		if (bif->bif_port_id < designated_port)
59870d9a675SMatthew Dillon 			goto set_port2;
59970d9a675SMatthew Dillon 		/* degenerate case (possible peer collision w/our key */
60070d9a675SMatthew Dillon 
60170d9a675SMatthew Dillon 		/*
6020e66e711SMatthew Dillon 		 * New port.  Since we are the root, the root cost is always
6033110e56aSMatthew Dillon 		 * 0.  The peer's cu_root_path_cost does not necessarily
6043110e56aSMatthew Dillon 		 * incorporate our peer path cost, but instead incorporates
6053110e56aSMatthew Dillon 		 * the lowest path cost to root for the target bridge
6063110e56aSMatthew Dillon 		 * when multiple links are present between the two bridges.
6073110e56aSMatthew Dillon 		 * Thus for selection purposes we must still add in our
6083110e56aSMatthew Dillon 		 * local path cost.
60970d9a675SMatthew Dillon 		 */
61070d9a675SMatthew Dillon set_port2:
6113110e56aSMatthew Dillon 		designated_cost = bif->bif_peer_cost + bif->bif_path_cost;
6120e66e711SMatthew Dillon 		designated_root_cost = 0;
61370d9a675SMatthew Dillon 		designated_bridge = sc->sc_bridge_id;
61470d9a675SMatthew Dillon 		designated_port = bif->bif_port_id;
61570d9a675SMatthew Dillon 		root_port = bif;
616ac93838fSSimon Schubert 	}
617ac93838fSSimon Schubert 
61870d9a675SMatthew Dillon 	/*
61970d9a675SMatthew Dillon 	 * Update aggregate information.  The selected root port always
62070d9a675SMatthew Dillon 	 * becomes a designated port (along with aged ports).  This can
62170d9a675SMatthew Dillon 	 * either be the port whos peer is closest to the root or it
62270d9a675SMatthew Dillon 	 * can be one of our ports if our bridge is the root.
62370d9a675SMatthew Dillon 	 *
62470d9a675SMatthew Dillon 	 * The root cost we record in sc_designated_root does not include
62570d9a675SMatthew Dillon 	 * bif_path_cost of the root port, since we may transmit this
62670d9a675SMatthew Dillon 	 * out of any port we will add the cost back in later on on
62770d9a675SMatthew Dillon 	 * a per-port basis.
62870d9a675SMatthew Dillon 	 *
62970d9a675SMatthew Dillon 	 * root_port can be NULL here (if all links are disabled)
63070d9a675SMatthew Dillon 	 */
63170d9a675SMatthew Dillon 	if (root_port) {
63270d9a675SMatthew Dillon 		sc->sc_designated_root = designated_root;
6330e66e711SMatthew Dillon 		sc->sc_designated_cost = designated_root_cost;
63470d9a675SMatthew Dillon 		sc->sc_designated_bridge = designated_bridge;
63570d9a675SMatthew Dillon 		sc->sc_designated_port = designated_port;
63670d9a675SMatthew Dillon 		root_port->bif_flags |= IFBIF_DESIGNATED | IFBIF_ROOT;
63770d9a675SMatthew Dillon 	} else {
63870d9a675SMatthew Dillon 		sc->sc_designated_root = designated_root;
63970d9a675SMatthew Dillon 		sc->sc_designated_cost = designated_cost;
64070d9a675SMatthew Dillon 		sc->sc_designated_bridge = designated_bridge;
64170d9a675SMatthew Dillon 		sc->sc_designated_port = designated_port;
64270d9a675SMatthew Dillon 	}
64370d9a675SMatthew Dillon 	sc->sc_root_port = root_port;
64470d9a675SMatthew Dillon }
64570d9a675SMatthew Dillon 
64670d9a675SMatthew Dillon /*
64770d9a675SMatthew Dillon  * Calculate the desired state for each interface link on our bridge.
64870d9a675SMatthew Dillon  *
64970d9a675SMatthew Dillon  * The best port will match against sc->sc_root_port (whether we are root
65070d9a675SMatthew Dillon  * or whether that port is the closest to the root).  We push this port
65170d9a675SMatthew Dillon  * towards a FORWARDING state.
65270d9a675SMatthew Dillon  *
65370d9a675SMatthew Dillon  * Next come designated ports, either aged ports or ports with no peer info
65470d9a675SMatthew Dillon  * (yet), or the peer who is closest to the root. We push this port towards
65570d9a675SMatthew Dillon  * a FORWARDING state as well.
65670d9a675SMatthew Dillon  *
6571e858374SMatthew Dillon  * Any remaining ports are pushed towards a BLOCKING state.  Both sides of
65870d9a675SMatthew Dillon  * the port (us and our peer) should wind up placing the two ends in this
65970d9a675SMatthew Dillon  * state or bad things happen.
66070d9a675SMatthew Dillon  */
661d217f5e5SScott Ullrich static void
bstp_port_state_selection(struct bridge_softc * sc)662ac93838fSSimon Schubert bstp_port_state_selection(struct bridge_softc *sc)
663ac93838fSSimon Schubert {
6648f7b13efSSepherosa Ziehau 	struct bridge_iflist *bif, *nbif;
665ac93838fSSimon Schubert 
66670d9a675SMatthew Dillon 	TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
667ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
668ac93838fSSimon Schubert 			continue;
66970d9a675SMatthew Dillon 		if (sc->sc_root_port &&
67070d9a675SMatthew Dillon 		    bif->bif_info == sc->sc_root_port->bif_info) {
671ac93838fSSimon Schubert 			bif->bif_config_pending = 0;
672ac93838fSSimon Schubert 			bif->bif_topology_change_acknowledge = 0;
673ac93838fSSimon Schubert 			bstp_make_forwarding(sc, bif);
67470d9a675SMatthew Dillon 		} else if (bif->bif_flags & IFBIF_DESIGNATED) {
675ac93838fSSimon Schubert 			bstp_timer_stop(&bif->bif_message_age_timer);
676ac93838fSSimon Schubert 			bstp_make_forwarding(sc, bif);
677ac93838fSSimon Schubert 		} else {
678ac93838fSSimon Schubert 			bif->bif_config_pending = 0;
679ac93838fSSimon Schubert 			bif->bif_topology_change_acknowledge = 0;
680ac93838fSSimon Schubert 			bstp_make_blocking(sc, bif);
681ac93838fSSimon Schubert 		}
6828f7b13efSSepherosa Ziehau 
6838f7b13efSSepherosa Ziehau 		if (nbif != NULL && !nbif->bif_onlist) {
6848f7b13efSSepherosa Ziehau 			KKASSERT(bif->bif_onlist);
68570d9a675SMatthew Dillon 			nbif = TAILQ_NEXT(bif, bif_next);
6868f7b13efSSepherosa Ziehau 		}
687ac93838fSSimon Schubert 	}
688ac93838fSSimon Schubert }
689ac93838fSSimon Schubert 
69070d9a675SMatthew Dillon /*
69170d9a675SMatthew Dillon  * Clear peer info, effectively makes the port looked aged out.
69270d9a675SMatthew Dillon  * It becomes a designated go-to port.
69370d9a675SMatthew Dillon  */
69470d9a675SMatthew Dillon static void
bstp_clear_peer_info(struct bridge_softc * sc,struct bridge_iflist * bif)69570d9a675SMatthew Dillon bstp_clear_peer_info(struct bridge_softc *sc, struct bridge_iflist *bif)
69670d9a675SMatthew Dillon {
69770d9a675SMatthew Dillon 	bif->bif_peer_root = 0xFFFFFFFFFFFFFFFFLLU;
69870d9a675SMatthew Dillon 	bif->bif_peer_cost = 0xFFFFFFFFU;
69970d9a675SMatthew Dillon 	bif->bif_peer_bridge = 0xFFFFFFFFFFFFFFFFLLU;
70070d9a675SMatthew Dillon 	bif->bif_peer_port = 0xFFFFU;
70170d9a675SMatthew Dillon 
70270d9a675SMatthew Dillon 	if (bif->bif_state != BSTP_IFSTATE_DISABLED &&
70370d9a675SMatthew Dillon 	    bif->bif_state != BSTP_IFSTATE_L1BLOCKING) {
70470d9a675SMatthew Dillon 		bif->bif_flags |= IFBIF_DESIGNATED;
70570d9a675SMatthew Dillon 	}
70670d9a675SMatthew Dillon }
70770d9a675SMatthew Dillon 
708d217f5e5SScott Ullrich static void
bstp_make_forwarding(struct bridge_softc * sc,struct bridge_iflist * bif)709ac93838fSSimon Schubert bstp_make_forwarding(struct bridge_softc *sc, struct bridge_iflist *bif)
710ac93838fSSimon Schubert {
7111e858374SMatthew Dillon 	if (bif->bif_state == BSTP_IFSTATE_BLOCKING ||
7121e858374SMatthew Dillon 	    bif->bif_state == BSTP_IFSTATE_BONDED) {
713ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_LISTENING);
714ac93838fSSimon Schubert 		bstp_timer_start(&bif->bif_forward_delay_timer, 0);
715ac93838fSSimon Schubert 	}
716ac93838fSSimon Schubert }
717ac93838fSSimon Schubert 
718d217f5e5SScott Ullrich static void
bstp_make_blocking(struct bridge_softc * sc,struct bridge_iflist * bif)719ac93838fSSimon Schubert bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif)
720ac93838fSSimon Schubert {
7213677aae9SMatthew Dillon 	if (bif->bif_state != BSTP_IFSTATE_DISABLED &&
7223677aae9SMatthew Dillon 	    bif->bif_state != BSTP_IFSTATE_BLOCKING &&
7231e858374SMatthew Dillon 	    bif->bif_state != BSTP_IFSTATE_BONDED &&
7243677aae9SMatthew Dillon 	    bif->bif_state != BSTP_IFSTATE_L1BLOCKING) {
725ac93838fSSimon Schubert 		if ((bif->bif_state == BSTP_IFSTATE_FORWARDING) ||
726ac93838fSSimon Schubert 		    (bif->bif_state == BSTP_IFSTATE_LEARNING)) {
727ac93838fSSimon Schubert 			if (bif->bif_change_detection_enabled) {
728ac93838fSSimon Schubert 				bstp_topology_change_detection(sc);
729ac93838fSSimon Schubert 			}
730ac93838fSSimon Schubert 		}
731ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING);
73230ced003SSepherosa Ziehau 		bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN);
733ac93838fSSimon Schubert 		bstp_timer_stop(&bif->bif_forward_delay_timer);
7341e858374SMatthew Dillon 		if (sc->sc_ifp->if_flags & IFF_LINK2)
7351e858374SMatthew Dillon 			bstp_adjust_bonded_states(sc, bif);
736ac93838fSSimon Schubert 	}
737ac93838fSSimon Schubert }
738ac93838fSSimon Schubert 
739d217f5e5SScott Ullrich static void
bstp_make_l1blocking(struct bridge_softc * sc,struct bridge_iflist * bif)7403677aae9SMatthew Dillon bstp_make_l1blocking(struct bridge_softc *sc, struct bridge_iflist *bif)
7413677aae9SMatthew Dillon {
7421e858374SMatthew Dillon 	int was_forwarding = (bif->bif_state == BSTP_IFSTATE_FORWARDING);
7431e858374SMatthew Dillon 
7443677aae9SMatthew Dillon 	switch(bif->bif_state) {
7453677aae9SMatthew Dillon 	case BSTP_IFSTATE_LISTENING:
7463677aae9SMatthew Dillon 	case BSTP_IFSTATE_LEARNING:
7473677aae9SMatthew Dillon 	case BSTP_IFSTATE_FORWARDING:
7483677aae9SMatthew Dillon 	case BSTP_IFSTATE_BLOCKING:
7491e858374SMatthew Dillon 	case BSTP_IFSTATE_BONDED:
7503677aae9SMatthew Dillon 		bstp_set_port_state(bif, BSTP_IFSTATE_L1BLOCKING);
7513677aae9SMatthew Dillon 		bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN);
7523677aae9SMatthew Dillon 		bstp_timer_stop(&bif->bif_forward_delay_timer);
7533677aae9SMatthew Dillon 		bstp_timer_stop(&bif->bif_link1_timer);
75470d9a675SMatthew Dillon 		if (bif->bif_flags & IFBIF_DESIGNATED) {
75570d9a675SMatthew Dillon 			bif->bif_flags &= ~IFBIF_DESIGNATED;
75670d9a675SMatthew Dillon 			bstp_configuration_update(sc);
75770d9a675SMatthew Dillon 			bstp_port_state_selection(sc);
75870d9a675SMatthew Dillon 		}
7591e858374SMatthew Dillon 		if (was_forwarding && (sc->sc_ifp->if_flags & IFF_LINK2))
7601e858374SMatthew Dillon 			bstp_adjust_bonded_states(sc, bif);
7613677aae9SMatthew Dillon 		break;
7623677aae9SMatthew Dillon 	default:
7633677aae9SMatthew Dillon 		break;
7643677aae9SMatthew Dillon 	}
7653677aae9SMatthew Dillon }
7663677aae9SMatthew Dillon 
7671e858374SMatthew Dillon /*
7681e858374SMatthew Dillon  * Member (bif) changes to or from a FORWARDING state.  All members in the
7691e858374SMatthew Dillon  * same bonding group which are in a BLOCKING or BONDED state must be set
7701e858374SMatthew Dillon  * to either BLOCKING or BONDED based on whether any members in the bonding
7711e858374SMatthew Dillon  * group remain in the FORWARDING state.
7721e858374SMatthew Dillon  *
7731e858374SMatthew Dillon  * Going between the BLOCKING and BONDED states does not require a
7741e858374SMatthew Dillon  * configuration update.
7751e858374SMatthew Dillon  */
7761e858374SMatthew Dillon static void
bstp_adjust_bonded_states(struct bridge_softc * sc,struct bridge_iflist * obif)7771e858374SMatthew Dillon bstp_adjust_bonded_states(struct bridge_softc *sc, struct bridge_iflist *obif)
7781e858374SMatthew Dillon {
7791e858374SMatthew Dillon 	struct bridge_iflist *bif;
7801e858374SMatthew Dillon 	int state = BSTP_IFSTATE_BLOCKING;
7811e858374SMatthew Dillon 
7821e858374SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
7831e858374SMatthew Dillon 		if ((bif->bif_flags & IFBIF_STP) == 0)
7841e858374SMatthew Dillon 			continue;
7851e858374SMatthew Dillon 		if (bif->bif_state != BSTP_IFSTATE_FORWARDING)
7861e858374SMatthew Dillon 			continue;
7871e858374SMatthew Dillon 		if (memcmp(IF_LLADDR(bif->bif_ifp), IF_LLADDR(obif->bif_ifp),
7881e858374SMatthew Dillon 			   ETHER_ADDR_LEN) != 0) {
7891e858374SMatthew Dillon 			continue;
7901e858374SMatthew Dillon 		}
7911e858374SMatthew Dillon 		state = BSTP_IFSTATE_BONDED;
7921e858374SMatthew Dillon 		break;
7931e858374SMatthew Dillon 	}
7941e858374SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
7951e858374SMatthew Dillon 		if ((bif->bif_flags & IFBIF_STP) == 0)
7961e858374SMatthew Dillon 			continue;
7971e858374SMatthew Dillon 		if (bif->bif_state != BSTP_IFSTATE_BLOCKING &&
7981e858374SMatthew Dillon 		    bif->bif_state != BSTP_IFSTATE_BONDED) {
7991e858374SMatthew Dillon 			continue;
8001e858374SMatthew Dillon 		}
8011e858374SMatthew Dillon 		if (memcmp(IF_LLADDR(bif->bif_ifp), IF_LLADDR(obif->bif_ifp),
8021e858374SMatthew Dillon 			   ETHER_ADDR_LEN) != 0) {
8031e858374SMatthew Dillon 			continue;
8041e858374SMatthew Dillon 		}
8051e858374SMatthew Dillon 		if (bif->bif_bond_weight == 0)
8061e858374SMatthew Dillon 			bif->bif_state = BSTP_IFSTATE_BLOCKING;
8071e858374SMatthew Dillon 		else
8081e858374SMatthew Dillon 			bif->bif_state = state;
8091e858374SMatthew Dillon 	}
8101e858374SMatthew Dillon }
8111e858374SMatthew Dillon 
8123677aae9SMatthew Dillon static void
bstp_set_port_state(struct bridge_iflist * bif,uint8_t state)813ac93838fSSimon Schubert bstp_set_port_state(struct bridge_iflist *bif, uint8_t state)
814ac93838fSSimon Schubert {
815ac93838fSSimon Schubert 	bif->bif_state = state;
816ac93838fSSimon Schubert }
817ac93838fSSimon Schubert 
818d217f5e5SScott Ullrich static void
bstp_topology_change_detection(struct bridge_softc * sc)819ac93838fSSimon Schubert bstp_topology_change_detection(struct bridge_softc *sc)
820ac93838fSSimon Schubert {
821ac93838fSSimon Schubert 	if (bstp_root_bridge(sc)) {
822ac93838fSSimon Schubert 		sc->sc_topology_change = 1;
823ac93838fSSimon Schubert 		bstp_timer_start(&sc->sc_topology_change_timer, 0);
824ac93838fSSimon Schubert 	} else if (!sc->sc_topology_change_detected) {
825ac93838fSSimon Schubert 		bstp_transmit_tcn(sc);
826ac93838fSSimon Schubert 		bstp_timer_start(&sc->sc_tcn_timer, 0);
827ac93838fSSimon Schubert 	}
828ac93838fSSimon Schubert 	sc->sc_topology_change_detected = 1;
829ac93838fSSimon Schubert }
830ac93838fSSimon Schubert 
831d217f5e5SScott Ullrich static void
bstp_topology_change_acknowledged(struct bridge_softc * sc)832ac93838fSSimon Schubert bstp_topology_change_acknowledged(struct bridge_softc *sc)
833ac93838fSSimon Schubert {
834ac93838fSSimon Schubert 	sc->sc_topology_change_detected = 0;
835ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_tcn_timer);
836ac93838fSSimon Schubert }
837ac93838fSSimon Schubert 
838d217f5e5SScott Ullrich static void
bstp_acknowledge_topology_change(struct bridge_softc * sc,struct bridge_iflist * bif)839ac93838fSSimon Schubert bstp_acknowledge_topology_change(struct bridge_softc *sc,
840ac93838fSSimon Schubert 				 struct bridge_iflist *bif)
841ac93838fSSimon Schubert {
842ac93838fSSimon Schubert 	bif->bif_topology_change_acknowledge = 1;
843ac93838fSSimon Schubert 	bstp_transmit_config(sc, bif);
844ac93838fSSimon Schubert }
845ac93838fSSimon Schubert 
846b2417333SSepherosa Ziehau void
bstp_input(struct bridge_softc * sc,struct bridge_iflist * bif,struct mbuf * m)84789ea766dSSepherosa Ziehau bstp_input(struct bridge_softc *sc, struct bridge_iflist *bif, struct mbuf *m)
848ac93838fSSimon Schubert {
849ac93838fSSimon Schubert 	struct ether_header *eh;
850ac93838fSSimon Schubert 	struct bstp_tbpdu tpdu;
851ac93838fSSimon Schubert 	struct bstp_cbpdu cpdu;
852ac93838fSSimon Schubert 	struct bstp_config_unit cu;
853ac93838fSSimon Schubert 	struct bstp_tcn_unit tu;
854ac93838fSSimon Schubert 	uint16_t len;
855ac93838fSSimon Schubert 
856ac93838fSSimon Schubert 	if ((bif->bif_flags & IFBIF_STP) == 0)
857ac93838fSSimon Schubert 		goto out;
858ac93838fSSimon Schubert 
8593677aae9SMatthew Dillon 	/*
8603677aae9SMatthew Dillon 	 * The L1BLOCKING (ping pong failover) test needs to reset the
8613677aae9SMatthew Dillon 	 * timer if LINK1 is active.
8623677aae9SMatthew Dillon 	 */
8633677aae9SMatthew Dillon 	if (bif->bif_state == BSTP_IFSTATE_L1BLOCKING) {
8643677aae9SMatthew Dillon 		bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING);
8653677aae9SMatthew Dillon 		if (sc->sc_ifp->if_flags & IFF_LINK1)
8663677aae9SMatthew Dillon 			bstp_timer_start(&bif->bif_link1_timer, 0);
8673677aae9SMatthew Dillon 		bstp_make_forwarding(sc, bif);
8683677aae9SMatthew Dillon 	} else if (sc->sc_ifp->if_flags & IFF_LINK1) {
8693677aae9SMatthew Dillon 		bstp_timer_start(&bif->bif_link1_timer, 0);
8703677aae9SMatthew Dillon 	}
8713677aae9SMatthew Dillon 
87289ea766dSSepherosa Ziehau 	eh = mtod(m, struct ether_header *);
87389ea766dSSepherosa Ziehau 
874ac93838fSSimon Schubert 	len = ntohs(eh->ether_type);
875ac93838fSSimon Schubert 	if (len < sizeof(tpdu))
876ac93838fSSimon Schubert 		goto out;
877ac93838fSSimon Schubert 
878ac93838fSSimon Schubert 	m_adj(m, ETHER_HDR_LEN);
879ac93838fSSimon Schubert 
880ac93838fSSimon Schubert 	if (m->m_pkthdr.len > len)
881ac93838fSSimon Schubert 		m_adj(m, len - m->m_pkthdr.len);
882ac93838fSSimon Schubert 	if (m->m_len < sizeof(tpdu) &&
883ac93838fSSimon Schubert 	    (m = m_pullup(m, sizeof(tpdu))) == NULL)
884ac93838fSSimon Schubert 		goto out;
885ac93838fSSimon Schubert 
886ac93838fSSimon Schubert 	memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu));
887ac93838fSSimon Schubert 
888ac93838fSSimon Schubert 	if (tpdu.tbu_dsap != LLC_8021D_LSAP ||
889ac93838fSSimon Schubert 	    tpdu.tbu_ssap != LLC_8021D_LSAP ||
890ac93838fSSimon Schubert 	    tpdu.tbu_ctl != LLC_UI)
891ac93838fSSimon Schubert 		goto out;
892ac93838fSSimon Schubert 	if (tpdu.tbu_protoid != 0 || tpdu.tbu_protover != 0)
893ac93838fSSimon Schubert 		goto out;
894ac93838fSSimon Schubert 
895ac93838fSSimon Schubert 	switch (tpdu.tbu_bpdutype) {
896ac93838fSSimon Schubert 	case BSTP_MSGTYPE_TCN:
897ac93838fSSimon Schubert 		tu.tu_message_type = tpdu.tbu_bpdutype;
898ac93838fSSimon Schubert 		bstp_received_tcn_bpdu(sc, bif, &tu);
899ac93838fSSimon Schubert 		break;
900ac93838fSSimon Schubert 	case BSTP_MSGTYPE_CFG:
901ac93838fSSimon Schubert 		if (m->m_len < sizeof(cpdu) &&
902ac93838fSSimon Schubert 		    (m = m_pullup(m, sizeof(cpdu))) == NULL)
903ac93838fSSimon Schubert 			goto out;
904ac93838fSSimon Schubert 		memcpy(&cpdu, mtod(m, caddr_t), sizeof(cpdu));
905ac93838fSSimon Schubert 
906ac93838fSSimon Schubert 		cu.cu_rootid =
907ac93838fSSimon Schubert 		    (((uint64_t)ntohs(cpdu.cbu_rootpri)) << 48) |
908ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[0]) << 40) |
909ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[1]) << 32) |
910ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[2]) << 24) |
911ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[3]) << 16) |
912ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[4]) << 8) |
913ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_rootaddr[5]) << 0);
914ac93838fSSimon Schubert 
915ac93838fSSimon Schubert 		cu.cu_bridge_id =
916ac93838fSSimon Schubert 		    (((uint64_t)ntohs(cpdu.cbu_bridgepri)) << 48) |
917ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[0]) << 40) |
918ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[1]) << 32) |
919ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[2]) << 24) |
920ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[3]) << 16) |
921ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[4]) << 8) |
922ac93838fSSimon Schubert 		    (((uint64_t)cpdu.cbu_bridgeaddr[5]) << 0);
923ac93838fSSimon Schubert 
924ac93838fSSimon Schubert 		cu.cu_root_path_cost = ntohl(cpdu.cbu_rootpathcost);
925ac93838fSSimon Schubert 		cu.cu_message_age = ntohs(cpdu.cbu_messageage);
926ac93838fSSimon Schubert 		cu.cu_max_age = ntohs(cpdu.cbu_maxage);
927ac93838fSSimon Schubert 		cu.cu_hello_time = ntohs(cpdu.cbu_hellotime);
928ac93838fSSimon Schubert 		cu.cu_forward_delay = ntohs(cpdu.cbu_forwarddelay);
929ac93838fSSimon Schubert 		cu.cu_port_id = ntohs(cpdu.cbu_portid);
930ac93838fSSimon Schubert 		cu.cu_message_type = cpdu.cbu_bpdutype;
931ac93838fSSimon Schubert 		cu.cu_topology_change_acknowledgment =
932ac93838fSSimon Schubert 		    (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0;
933ac93838fSSimon Schubert 		cu.cu_topology_change =
934ac93838fSSimon Schubert 		    (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0;
935ac93838fSSimon Schubert 		bstp_received_config_bpdu(sc, bif, &cu);
936ac93838fSSimon Schubert 		break;
937ac93838fSSimon Schubert 	default:
938ac93838fSSimon Schubert 		goto out;
939ac93838fSSimon Schubert 	}
940ac93838fSSimon Schubert out:
941ac93838fSSimon Schubert 	if (m)
942ac93838fSSimon Schubert 		m_freem(m);
943ac93838fSSimon Schubert }
944ac93838fSSimon Schubert 
945d217f5e5SScott Ullrich static void
bstp_received_config_bpdu(struct bridge_softc * sc,struct bridge_iflist * bif,struct bstp_config_unit * cu)946ac93838fSSimon Schubert bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
947ac93838fSSimon Schubert 			  struct bstp_config_unit *cu)
948ac93838fSSimon Schubert {
94970d9a675SMatthew Dillon 	int iamroot;
950ac93838fSSimon Schubert 
95170d9a675SMatthew Dillon 	iamroot = bstp_root_bridge(sc);
952ac93838fSSimon Schubert 
953ac93838fSSimon Schubert 	if (bif->bif_state != BSTP_IFSTATE_DISABLED) {
95470d9a675SMatthew Dillon 		/*
95570d9a675SMatthew Dillon 		 * Record information from peer.  The peer_cost field
95670d9a675SMatthew Dillon 		 * does not include the local bif->bif_path_cost, it will
95770d9a675SMatthew Dillon 		 * be added in as needed (since it can be modified manually
95870d9a675SMatthew Dillon 		 * this way we don't have to worry about fixups).
95970d9a675SMatthew Dillon 		 */
96070d9a675SMatthew Dillon 		bif->bif_peer_root = cu->cu_rootid;
96170d9a675SMatthew Dillon 		bif->bif_peer_cost = cu->cu_root_path_cost;
96270d9a675SMatthew Dillon 		bif->bif_peer_bridge = cu->cu_bridge_id;
96370d9a675SMatthew Dillon 		bif->bif_peer_port = cu->cu_port_id;
96470d9a675SMatthew Dillon 
96570d9a675SMatthew Dillon 		if (bstp_supersedes_port_info(sc, bif)) {
966ac93838fSSimon Schubert 			bstp_record_config_information(sc, bif, cu);
967ac93838fSSimon Schubert 			bstp_configuration_update(sc);
968ac93838fSSimon Schubert 			bstp_port_state_selection(sc);
969ac93838fSSimon Schubert 
9703677aae9SMatthew Dillon 			/*
97170d9a675SMatthew Dillon 			 * If our bridge loses its root status (?)
97270d9a675SMatthew Dillon 			 *
97370d9a675SMatthew Dillon 			 * Hello's (unsolicited CFG packets) are generated
97470d9a675SMatthew Dillon 			 * every hello period of LINK1 is set, otherwise
97570d9a675SMatthew Dillon 			 * we are no longer the root bridge and must stop
97670d9a675SMatthew Dillon 			 * generating unsolicited CFG packets.
9773677aae9SMatthew Dillon 			 */
97870d9a675SMatthew Dillon 			if (iamroot && bstp_root_bridge(sc) == 0) {
9793677aae9SMatthew Dillon 				if ((sc->sc_ifp->if_flags & IFF_LINK1) == 0)
980ac93838fSSimon Schubert 					bstp_timer_stop(&sc->sc_hello_timer);
981ac93838fSSimon Schubert 
982ac93838fSSimon Schubert 				if (sc->sc_topology_change_detected) {
983ac93838fSSimon Schubert 					bstp_timer_stop(
984ac93838fSSimon Schubert 					    &sc->sc_topology_change_timer);
985ac93838fSSimon Schubert 					bstp_transmit_tcn(sc);
986ac93838fSSimon Schubert 					bstp_timer_start(&sc->sc_tcn_timer, 0);
987ac93838fSSimon Schubert 				}
988ac93838fSSimon Schubert 			}
989ac93838fSSimon Schubert 
99070d9a675SMatthew Dillon 			if (sc->sc_root_port &&
99170d9a675SMatthew Dillon 			    bif->bif_info == sc->sc_root_port->bif_info) {
992ac93838fSSimon Schubert 				bstp_record_config_timeout_values(sc, cu);
993ac93838fSSimon Schubert 				bstp_config_bpdu_generation(sc);
994ac93838fSSimon Schubert 
995ac93838fSSimon Schubert 				if (cu->cu_topology_change_acknowledgment)
996ac93838fSSimon Schubert 					bstp_topology_change_acknowledged(sc);
997ac93838fSSimon Schubert 			}
99870d9a675SMatthew Dillon 		} else if (bif->bif_flags & IFBIF_DESIGNATED) {
99970d9a675SMatthew Dillon 			/*
100070d9a675SMatthew Dillon 			 * Update designated ports (aged out peers or
100170d9a675SMatthew Dillon 			 * the port closest to the root) at a faster pace.
100270d9a675SMatthew Dillon 			 *
100370d9a675SMatthew Dillon 			 * Clear our designated flag if we aren't marked
100470d9a675SMatthew Dillon 			 * as the root port.
100570d9a675SMatthew Dillon 			 */
1006ac93838fSSimon Schubert 			bstp_transmit_config(sc, bif);
100770d9a675SMatthew Dillon 			if ((bif->bif_flags & IFBIF_ROOT) == 0) {
100870d9a675SMatthew Dillon 				bif->bif_flags &= ~IFBIF_DESIGNATED;
100970d9a675SMatthew Dillon 				bstp_configuration_update(sc);
101070d9a675SMatthew Dillon 				bstp_port_state_selection(sc);
101170d9a675SMatthew Dillon 			}
101270d9a675SMatthew Dillon 		}
1013ac93838fSSimon Schubert 	}
1014ac93838fSSimon Schubert }
1015ac93838fSSimon Schubert 
1016d217f5e5SScott Ullrich static void
bstp_received_tcn_bpdu(struct bridge_softc * sc,struct bridge_iflist * bif,struct bstp_tcn_unit * tcn)1017ac93838fSSimon Schubert bstp_received_tcn_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif,
1018ac93838fSSimon Schubert 		       struct bstp_tcn_unit *tcn)
1019ac93838fSSimon Schubert {
1020ac93838fSSimon Schubert 	if (bif->bif_state != BSTP_IFSTATE_DISABLED &&
102170d9a675SMatthew Dillon 	    (bif->bif_flags & IFBIF_DESIGNATED)) {
1022ac93838fSSimon Schubert 		bstp_topology_change_detection(sc);
1023ac93838fSSimon Schubert 		bstp_acknowledge_topology_change(sc, bif);
1024ac93838fSSimon Schubert 	}
1025ac93838fSSimon Schubert }
1026ac93838fSSimon Schubert 
10273677aae9SMatthew Dillon /*
10283677aae9SMatthew Dillon  * link1 forces continuous hello's (the bridge interface must be cycled
10293677aae9SMatthew Dillon  * to start them up), so keep the timer hot if that is the case, otherwise
10303677aae9SMatthew Dillon  * only send HELLO's if we are the root.
10313677aae9SMatthew Dillon  */
1032d217f5e5SScott Ullrich static void
bstp_hello_timer_expiry(struct bridge_softc * sc)1033ac93838fSSimon Schubert bstp_hello_timer_expiry(struct bridge_softc *sc)
1034ac93838fSSimon Schubert {
1035ac93838fSSimon Schubert 	bstp_config_bpdu_generation(sc);
10363677aae9SMatthew Dillon 
10373677aae9SMatthew Dillon 	if ((sc->sc_ifp->if_flags & IFF_LINK1) || bstp_root_bridge(sc))
1038ac93838fSSimon Schubert 		bstp_timer_start(&sc->sc_hello_timer, 0);
1039ac93838fSSimon Schubert }
1040ac93838fSSimon Schubert 
1041d217f5e5SScott Ullrich static void
bstp_message_age_timer_expiry(struct bridge_softc * sc,struct bridge_iflist * bif)1042ac93838fSSimon Schubert bstp_message_age_timer_expiry(struct bridge_softc *sc,
1043ac93838fSSimon Schubert 			      struct bridge_iflist *bif)
1044ac93838fSSimon Schubert {
104570d9a675SMatthew Dillon 	int iamroot;
1046ac93838fSSimon Schubert 
104770d9a675SMatthew Dillon 	iamroot = bstp_root_bridge(sc);
104870d9a675SMatthew Dillon 	bstp_clear_peer_info(sc, bif);
1049ac93838fSSimon Schubert 	bstp_configuration_update(sc);
1050ac93838fSSimon Schubert 	bstp_port_state_selection(sc);
1051ac93838fSSimon Schubert 
10523677aae9SMatthew Dillon 	/*
10533677aae9SMatthew Dillon 	 * If we've become the root and were not the root before
10543677aae9SMatthew Dillon 	 * we have some cleanup to do.  This also occurs if we
10553677aae9SMatthew Dillon 	 * wind up being completely isolated.
10563677aae9SMatthew Dillon 	 */
105770d9a675SMatthew Dillon 	if (iamroot == 0 && bstp_root_bridge(sc)) {
1058ac93838fSSimon Schubert 		sc->sc_max_age = sc->sc_bridge_max_age;
1059ac93838fSSimon Schubert 		sc->sc_hello_time = sc->sc_bridge_hello_time;
1060ac93838fSSimon Schubert 		sc->sc_forward_delay = sc->sc_bridge_forward_delay;
1061ac93838fSSimon Schubert 
1062ac93838fSSimon Schubert 		bstp_topology_change_detection(sc);
1063ac93838fSSimon Schubert 		bstp_timer_stop(&sc->sc_tcn_timer);
1064ac93838fSSimon Schubert 		bstp_config_bpdu_generation(sc);
1065ac93838fSSimon Schubert 		bstp_timer_start(&sc->sc_hello_timer, 0);
1066ac93838fSSimon Schubert 	}
1067ac93838fSSimon Schubert }
1068ac93838fSSimon Schubert 
1069d217f5e5SScott Ullrich static void
bstp_forward_delay_timer_expiry(struct bridge_softc * sc,struct bridge_iflist * bif)1070ac93838fSSimon Schubert bstp_forward_delay_timer_expiry(struct bridge_softc *sc,
1071ac93838fSSimon Schubert 				struct bridge_iflist *bif)
1072ac93838fSSimon Schubert {
1073ac93838fSSimon Schubert 	if (bif->bif_state == BSTP_IFSTATE_LISTENING) {
1074ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_LEARNING);
1075ac93838fSSimon Schubert 		bstp_timer_start(&bif->bif_forward_delay_timer, 0);
1076ac93838fSSimon Schubert 	} else if (bif->bif_state == BSTP_IFSTATE_LEARNING) {
1077ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_FORWARDING);
107870d9a675SMatthew Dillon 		if (sc->sc_designated_bridge == sc->sc_bridge_id &&
107970d9a675SMatthew Dillon 		    bif->bif_change_detection_enabled) {
1080ac93838fSSimon Schubert 			bstp_topology_change_detection(sc);
1081ac93838fSSimon Schubert 		}
10821885d414SMatthew Dillon 		bstp_configuration_update(sc);
10831885d414SMatthew Dillon 		bstp_port_state_selection(sc);
10841e858374SMatthew Dillon 		if (sc->sc_ifp->if_flags & IFF_LINK2)
10851e858374SMatthew Dillon 			bstp_adjust_bonded_states(sc, bif);
1086ac93838fSSimon Schubert 	}
1087ac93838fSSimon Schubert }
1088ac93838fSSimon Schubert 
1089d217f5e5SScott Ullrich static void
bstp_tcn_timer_expiry(struct bridge_softc * sc)1090ac93838fSSimon Schubert bstp_tcn_timer_expiry(struct bridge_softc *sc)
1091ac93838fSSimon Schubert {
1092ac93838fSSimon Schubert 	bstp_transmit_tcn(sc);
1093ac93838fSSimon Schubert 	bstp_timer_start(&sc->sc_tcn_timer, 0);
1094ac93838fSSimon Schubert }
1095ac93838fSSimon Schubert 
1096d217f5e5SScott Ullrich static void
bstp_topology_change_timer_expiry(struct bridge_softc * sc)1097ac93838fSSimon Schubert bstp_topology_change_timer_expiry(struct bridge_softc *sc)
1098ac93838fSSimon Schubert {
1099ac93838fSSimon Schubert 	sc->sc_topology_change_detected = 0;
1100ac93838fSSimon Schubert 	sc->sc_topology_change = 0;
1101ac93838fSSimon Schubert }
1102ac93838fSSimon Schubert 
1103d217f5e5SScott Ullrich static void
bstp_hold_timer_expiry(struct bridge_softc * sc,struct bridge_iflist * bif)1104ac93838fSSimon Schubert bstp_hold_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif)
1105ac93838fSSimon Schubert {
1106ac93838fSSimon Schubert 	if (bif->bif_config_pending)
1107ac93838fSSimon Schubert 		bstp_transmit_config(sc, bif);
1108ac93838fSSimon Schubert }
1109ac93838fSSimon Schubert 
11103677aae9SMatthew Dillon /*
11113677aae9SMatthew Dillon  * If no traffic received directly on this port for the specified
11123677aae9SMatthew Dillon  * period with link1 set we go into a special blocking mode to
11133677aae9SMatthew Dillon  * fail-over traffic to another port.
11143677aae9SMatthew Dillon  */
11153677aae9SMatthew Dillon static void
bstp_link1_timer_expiry(struct bridge_softc * sc,struct bridge_iflist * bif)11163677aae9SMatthew Dillon bstp_link1_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif)
11173677aae9SMatthew Dillon {
11183677aae9SMatthew Dillon 	if (sc->sc_ifp->if_flags & IFF_LINK1)
11193677aae9SMatthew Dillon 		bstp_make_l1blocking(sc, bif);
11203677aae9SMatthew Dillon }
11213677aae9SMatthew Dillon 
1122d217f5e5SScott Ullrich static int
bstp_addr_cmp(const uint8_t * a,const uint8_t * b)1123d217f5e5SScott Ullrich bstp_addr_cmp(const uint8_t *a, const uint8_t *b)
1124d217f5e5SScott Ullrich {
1125d217f5e5SScott Ullrich 	int i, d;
1126d217f5e5SScott Ullrich 
1127d217f5e5SScott Ullrich 	for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) {
1128d217f5e5SScott Ullrich 		d = ((int)a[i]) - ((int)b[i]);
1129d217f5e5SScott Ullrich 	}
1130d217f5e5SScott Ullrich 
1131d217f5e5SScott Ullrich 	return (d);
1132d217f5e5SScott Ullrich }
1133d217f5e5SScott Ullrich 
1134ac93838fSSimon Schubert void
bstp_initialization(struct bridge_softc * sc)1135ac93838fSSimon Schubert bstp_initialization(struct bridge_softc *sc)
1136ac93838fSSimon Schubert {
11378f7b13efSSepherosa Ziehau 	struct bridge_iflist *bif, *mif, *nbif;
1138d217f5e5SScott Ullrich 	u_char *e_addr;
1139ac93838fSSimon Schubert 
1140e9d22060SSepherosa Ziehau 	KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT);
1141e9d22060SSepherosa Ziehau 
114270d9a675SMatthew Dillon 	/*
114370d9a675SMatthew Dillon 	 * Figure out our bridge ID, use the lowest-valued MAC.
114470d9a675SMatthew Dillon 	 * Include the bridge's own random MAC in the calculation.
114570d9a675SMatthew Dillon 	 */
1146ac93838fSSimon Schubert 	mif = NULL;
114770d9a675SMatthew Dillon 
114870d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1149ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
1150ac93838fSSimon Schubert 			continue;
1151ac93838fSSimon Schubert 		if (bif->bif_ifp->if_type != IFT_ETHER)
1152ac93838fSSimon Schubert 			continue;
1153ac93838fSSimon Schubert 		if (mif == NULL) {
1154ac93838fSSimon Schubert 			mif = bif;
1155ac93838fSSimon Schubert 			continue;
1156ac93838fSSimon Schubert 		}
115770d9a675SMatthew Dillon 
115870d9a675SMatthew Dillon 		bif->bif_port_id = (bif->bif_priority << 8) |
115970d9a675SMatthew Dillon 				   (bif->bif_ifp->if_index & 0xff);
1160d217f5e5SScott Ullrich 		if (bstp_addr_cmp(IF_LLADDR(bif->bif_ifp),
1161d217f5e5SScott Ullrich 				  IF_LLADDR(mif->bif_ifp)) < 0) {
1162ac93838fSSimon Schubert 			mif = bif;
1163ac93838fSSimon Schubert 			continue;
1164ac93838fSSimon Schubert 		}
1165ac93838fSSimon Schubert 	}
1166ac93838fSSimon Schubert 	if (mif == NULL) {
1167ac93838fSSimon Schubert 		bstp_stop(sc);
1168ac93838fSSimon Schubert 		return;
1169ac93838fSSimon Schubert 	}
1170ac93838fSSimon Schubert 
117170d9a675SMatthew Dillon 	if (bstp_addr_cmp(IF_LLADDR(sc->sc_ifp), IF_LLADDR(mif->bif_ifp)) < 0)
117270d9a675SMatthew Dillon 		e_addr = IF_LLADDR(sc->sc_ifp);
117370d9a675SMatthew Dillon 	else
1174d217f5e5SScott Ullrich 		e_addr = IF_LLADDR(mif->bif_ifp);
117570d9a675SMatthew Dillon 
1176ac93838fSSimon Schubert 	sc->sc_bridge_id =
1177ac93838fSSimon Schubert 	    (((uint64_t)sc->sc_bridge_priority) << 48) |
1178d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[0]) << 40) |
1179d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[1]) << 32) |
1180d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[2]) << 24) |
1181d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[3]) << 16) |
1182d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[4]) << 8) |
1183d217f5e5SScott Ullrich 	    (((uint64_t)e_addr[5]));
1184ac93838fSSimon Schubert 
118570d9a675SMatthew Dillon 	/*
118670d9a675SMatthew Dillon 	 * Remainder of setup.
118770d9a675SMatthew Dillon 	 */
118870d9a675SMatthew Dillon 
1189ac93838fSSimon Schubert 	sc->sc_designated_root = sc->sc_bridge_id;
119070d9a675SMatthew Dillon 	sc->sc_designated_cost = 0;
1191ac93838fSSimon Schubert 	sc->sc_root_port = NULL;
1192ac93838fSSimon Schubert 
1193ac93838fSSimon Schubert 	sc->sc_max_age = sc->sc_bridge_max_age;
1194ac93838fSSimon Schubert 	sc->sc_hello_time = sc->sc_bridge_hello_time;
1195ac93838fSSimon Schubert 	sc->sc_forward_delay = sc->sc_bridge_forward_delay;
1196ac93838fSSimon Schubert 	sc->sc_topology_change_detected = 0;
1197ac93838fSSimon Schubert 	sc->sc_topology_change = 0;
1198ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_tcn_timer);
1199ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_topology_change_timer);
1200ac93838fSSimon Schubert 
1201ac93838fSSimon Schubert 	if (callout_pending(&sc->sc_bstpcallout) == 0)
1202ac93838fSSimon Schubert 		callout_reset(&sc->sc_bstpcallout, hz,
1203ac93838fSSimon Schubert 		    bstp_tick, sc);
1204ac93838fSSimon Schubert 
120570d9a675SMatthew Dillon 	TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) {
12063677aae9SMatthew Dillon 		if (sc->sc_ifp->if_flags & IFF_LINK1)
12073677aae9SMatthew Dillon 			bstp_timer_start(&bif->bif_link1_timer, 0);
1208ac93838fSSimon Schubert 		if (bif->bif_flags & IFBIF_STP)
1209ac93838fSSimon Schubert 			bstp_ifupdstatus(sc, bif);
1210ac93838fSSimon Schubert 		else
1211ac93838fSSimon Schubert 			bstp_disable_port(sc, bif);
12128f7b13efSSepherosa Ziehau 
12138f7b13efSSepherosa Ziehau 		if (nbif != NULL && !nbif->bif_onlist) {
12148f7b13efSSepherosa Ziehau 			KKASSERT(bif->bif_onlist);
121570d9a675SMatthew Dillon 			nbif = TAILQ_NEXT(bif, bif_next);
12168f7b13efSSepherosa Ziehau 		}
1217ac93838fSSimon Schubert 	}
1218ac93838fSSimon Schubert 
1219ac93838fSSimon Schubert 	bstp_port_state_selection(sc);
1220ac93838fSSimon Schubert 	bstp_config_bpdu_generation(sc);
1221ac93838fSSimon Schubert 	bstp_timer_start(&sc->sc_hello_timer, 0);
1222ac93838fSSimon Schubert }
1223ac93838fSSimon Schubert 
1224ac93838fSSimon Schubert void
bstp_stop(struct bridge_softc * sc)1225ac93838fSSimon Schubert bstp_stop(struct bridge_softc *sc)
1226ac93838fSSimon Schubert {
1227ac93838fSSimon Schubert 	struct bridge_iflist *bif;
1228e9d22060SSepherosa Ziehau 
1229e9d22060SSepherosa Ziehau 	KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT);
1230ac93838fSSimon Schubert 
123170d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1232ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED);
1233ac93838fSSimon Schubert 		bstp_timer_stop(&bif->bif_hold_timer);
1234ac93838fSSimon Schubert 		bstp_timer_stop(&bif->bif_message_age_timer);
1235ac93838fSSimon Schubert 		bstp_timer_stop(&bif->bif_forward_delay_timer);
12363677aae9SMatthew Dillon 		bstp_timer_stop(&bif->bif_link1_timer);
1237ac93838fSSimon Schubert 	}
1238ac93838fSSimon Schubert 
1239ac93838fSSimon Schubert 	callout_stop(&sc->sc_bstpcallout);
1240ac93838fSSimon Schubert 
1241ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_topology_change_timer);
1242ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_tcn_timer);
1243ac93838fSSimon Schubert 	bstp_timer_stop(&sc->sc_hello_timer);
1244ac93838fSSimon Schubert 
1245e9d22060SSepherosa Ziehau 	crit_enter();
12466999cd81SMatthew Dillon 	lwkt_dropmsg(&sc->sc_bstptimemsg.lmsg);
1247e9d22060SSepherosa Ziehau 	crit_exit();
1248ac93838fSSimon Schubert }
1249ac93838fSSimon Schubert 
12501885d414SMatthew Dillon /*
12511885d414SMatthew Dillon  * [re]initialize a port.  The port is initialized to a L1BLOCKING state
12521885d414SMatthew Dillon  * or a BLOCKING state.  When link ping/pong is enabled (LINK1) we start
12531885d414SMatthew Dillon  * out in the L1BLOCKING state to prevent flapping from blowing up the
12541885d414SMatthew Dillon  * state of the other ports.
12551885d414SMatthew Dillon  *
12561885d414SMatthew Dillon  * Either way the first config packet received will knock the port into
12571885d414SMatthew Dillon  * the LISTENING state.
12581885d414SMatthew Dillon  */
1259d217f5e5SScott Ullrich static void
bstp_initialize_port(struct bridge_softc * sc,struct bridge_iflist * bif)1260ac93838fSSimon Schubert bstp_initialize_port(struct bridge_softc *sc, struct bridge_iflist *bif)
1261ac93838fSSimon Schubert {
12621e858374SMatthew Dillon 	int needs_adjust = (bif->bif_state == BSTP_IFSTATE_FORWARDING ||
12631e858374SMatthew Dillon 			    bif->bif_state == BSTP_IFSTATE_BLOCKING ||
12641e858374SMatthew Dillon 			    bif->bif_state == BSTP_IFSTATE_BONDED);
12651e858374SMatthew Dillon 
12661885d414SMatthew Dillon 	if (sc->sc_ifp->if_flags & IFF_LINK1)
12671885d414SMatthew Dillon 		bstp_set_port_state(bif, BSTP_IFSTATE_L1BLOCKING);
12681885d414SMatthew Dillon 	else
1269ac93838fSSimon Schubert 		bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING);
127070d9a675SMatthew Dillon 	bstp_clear_peer_info(sc, bif);
1271ac93838fSSimon Schubert 	bif->bif_topology_change_acknowledge = 0;
1272ac93838fSSimon Schubert 	bif->bif_config_pending = 0;
1273ac93838fSSimon Schubert 	bif->bif_change_detection_enabled = 1;
1274ac93838fSSimon Schubert 	bstp_timer_stop(&bif->bif_message_age_timer);
1275ac93838fSSimon Schubert 	bstp_timer_stop(&bif->bif_forward_delay_timer);
1276ac93838fSSimon Schubert 	bstp_timer_stop(&bif->bif_hold_timer);
12773677aae9SMatthew Dillon 	bstp_timer_stop(&bif->bif_link1_timer);
12781e858374SMatthew Dillon 	if (needs_adjust && (sc->sc_ifp->if_flags & IFF_LINK2))
12791e858374SMatthew Dillon 		bstp_adjust_bonded_states(sc, bif);
1280ac93838fSSimon Schubert }
1281ac93838fSSimon Schubert 
12821885d414SMatthew Dillon /*
12831885d414SMatthew Dillon  * When enabling a port that was previously disabled no configuration
12841885d414SMatthew Dillon  * update occurs, otherwise down/up or running/not-running flapping
12851885d414SMatthew Dillon  * (e.g. by openvpn on a TAP interface) will blow up the other ports.
12861885d414SMatthew Dillon  */
1287d217f5e5SScott Ullrich static void
bstp_enable_port(struct bridge_softc * sc,struct bridge_iflist * bif)1288ac93838fSSimon Schubert bstp_enable_port(struct bridge_softc *sc, struct bridge_iflist *bif)
1289ac93838fSSimon Schubert {
12901885d414SMatthew Dillon 	int was_disabled = (bif->bif_state == BSTP_IFSTATE_DISABLED ||
12911885d414SMatthew Dillon 			    bif->bif_state == BSTP_IFSTATE_L1BLOCKING);
12921885d414SMatthew Dillon 
1293ac93838fSSimon Schubert 	bstp_initialize_port(sc, bif);
12943677aae9SMatthew Dillon 	if (sc->sc_ifp->if_flags & IFF_LINK1)
12953677aae9SMatthew Dillon 		bstp_timer_start(&bif->bif_link1_timer, 0);
12961885d414SMatthew Dillon 	if (was_disabled == 0) {
12979b42fdc9SMatthew Dillon 		bstp_configuration_update(sc);
1298ac93838fSSimon Schubert 		bstp_port_state_selection(sc);
1299ac93838fSSimon Schubert 	}
13001885d414SMatthew Dillon 	bstp_adjust_bonded_states(sc, bif);
13011885d414SMatthew Dillon }
1302ac93838fSSimon Schubert 
13031885d414SMatthew Dillon /*
13041885d414SMatthew Dillon  * When disabling a port that was previously in a non-forwarding or bonded
13051885d414SMatthew Dillon  * state no configuration update occurs, otherwise down/up or
13061885d414SMatthew Dillon  * running/not-running flapping (e.g. by openvpn on a TAP interface) will
13071885d414SMatthew Dillon  * blow up the other ports.
13081885d414SMatthew Dillon  */
1309d217f5e5SScott Ullrich static void
bstp_disable_port(struct bridge_softc * sc,struct bridge_iflist * bif)1310ac93838fSSimon Schubert bstp_disable_port(struct bridge_softc *sc, struct bridge_iflist *bif)
1311ac93838fSSimon Schubert {
13121e858374SMatthew Dillon 	int was_forwarding = (bif->bif_state == BSTP_IFSTATE_FORWARDING);
13131885d414SMatthew Dillon 	int was_bonded = (bif->bif_state == BSTP_IFSTATE_BONDED);
131470d9a675SMatthew Dillon 	int iamroot;
1315ac93838fSSimon Schubert 
131670d9a675SMatthew Dillon 	iamroot = bstp_root_bridge(sc);
131770d9a675SMatthew Dillon 
131870d9a675SMatthew Dillon 	bstp_clear_peer_info(sc, bif);
1319ac93838fSSimon Schubert 	bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED);
1320ac93838fSSimon Schubert 	bif->bif_topology_change_acknowledge = 0;
1321ac93838fSSimon Schubert 	bif->bif_config_pending = 0;
1322ac93838fSSimon Schubert 	bstp_timer_stop(&bif->bif_message_age_timer);
1323ac93838fSSimon Schubert 	bstp_timer_stop(&bif->bif_forward_delay_timer);
13243677aae9SMatthew Dillon 	bstp_timer_stop(&bif->bif_link1_timer);
13251885d414SMatthew Dillon 	if (was_forwarding || was_bonded) {
1326ac93838fSSimon Schubert 		bstp_configuration_update(sc);
1327ac93838fSSimon Schubert 		bstp_port_state_selection(sc);
13281885d414SMatthew Dillon 	}
132930ced003SSepherosa Ziehau 	bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN);
13301e858374SMatthew Dillon 	if (was_forwarding && (sc->sc_ifp->if_flags & IFF_LINK2))
13311e858374SMatthew Dillon 		bstp_adjust_bonded_states(sc, bif);
1332ac93838fSSimon Schubert 
133370d9a675SMatthew Dillon 	if (iamroot == 0 && bstp_root_bridge(sc)) {
1334ac93838fSSimon Schubert 		sc->sc_max_age = sc->sc_bridge_max_age;
1335ac93838fSSimon Schubert 		sc->sc_hello_time = sc->sc_bridge_hello_time;
1336ac93838fSSimon Schubert 		sc->sc_forward_delay = sc->sc_bridge_forward_delay;
1337ac93838fSSimon Schubert 
1338ac93838fSSimon Schubert 		bstp_topology_change_detection(sc);
1339ac93838fSSimon Schubert 		bstp_timer_stop(&sc->sc_tcn_timer);
1340ac93838fSSimon Schubert 		bstp_config_bpdu_generation(sc);
1341ac93838fSSimon Schubert 		bstp_timer_start(&sc->sc_hello_timer, 0);
1342ac93838fSSimon Schubert 	}
1343ac93838fSSimon Schubert }
1344ac93838fSSimon Schubert 
1345ac93838fSSimon Schubert void
bstp_linkstate(struct ifnet * ifp,int state)1346ac93838fSSimon Schubert bstp_linkstate(struct ifnet *ifp, int state)
1347ac93838fSSimon Schubert {
1348ac93838fSSimon Schubert 	struct bridge_softc *sc;
1349ac93838fSSimon Schubert 	struct bridge_iflist *bif;
1350ac93838fSSimon Schubert 
1351ac93838fSSimon Schubert 	sc = ifp->if_bridge;
1352a3dd34d2SSepherosa Ziehau 	ifnet_serialize_all(sc->sc_ifp);
1353ac93838fSSimon Schubert 
13548f7b13efSSepherosa Ziehau 	/*
13558f7b13efSSepherosa Ziehau 	 * bstp_ifupdstatus() may block, but it is the last
13568f7b13efSSepherosa Ziehau 	 * operation of the member iface iteration, so we
13578f7b13efSSepherosa Ziehau 	 * don't need to use LIST_FOREACH_MUTABLE()+bif_onlist
13588f7b13efSSepherosa Ziehau 	 * check here.
13598f7b13efSSepherosa Ziehau 	 */
136070d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1361ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
1362ac93838fSSimon Schubert 			continue;
1363ac93838fSSimon Schubert 
1364ac93838fSSimon Schubert 		if (bif->bif_ifp == ifp) {
1365ac93838fSSimon Schubert 			bstp_ifupdstatus(sc, bif);
1366ac93838fSSimon Schubert 			break;
1367ac93838fSSimon Schubert 		}
1368ac93838fSSimon Schubert 	}
1369a3dd34d2SSepherosa Ziehau 	ifnet_deserialize_all(sc->sc_ifp);
1370ac93838fSSimon Schubert }
1371ac93838fSSimon Schubert 
1372d217f5e5SScott Ullrich static void
bstp_ifupdstatus(struct bridge_softc * sc,struct bridge_iflist * bif)1373ac93838fSSimon Schubert bstp_ifupdstatus(struct bridge_softc *sc, struct bridge_iflist *bif)
1374ac93838fSSimon Schubert {
1375ac93838fSSimon Schubert 	struct ifnet *ifp = bif->bif_ifp;
1376ac93838fSSimon Schubert 	struct ifmediareq ifmr;
1377ac93838fSSimon Schubert 	int error = 0;
1378ac93838fSSimon Schubert 
1379ac93838fSSimon Schubert 	bzero((char *)&ifmr, sizeof(ifmr));
1380a3dd34d2SSepherosa Ziehau 	ifnet_serialize_all(ifp);
1381d217f5e5SScott Ullrich 	error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr, NULL);
1382a3dd34d2SSepherosa Ziehau 	ifnet_deserialize_all(ifp);
1383ac93838fSSimon Schubert 
1384ac93838fSSimon Schubert 	if ((error == 0) && (ifp->if_flags & IFF_UP)) {
1385ac93838fSSimon Schubert 	 	if (ifmr.ifm_status & IFM_ACTIVE) {
1386ac93838fSSimon Schubert 			if (bif->bif_state == BSTP_IFSTATE_DISABLED)
1387ac93838fSSimon Schubert 				bstp_enable_port(sc, bif);
1388ac93838fSSimon Schubert 
1389ac93838fSSimon Schubert 		} else {
1390ac93838fSSimon Schubert 			if (bif->bif_state != BSTP_IFSTATE_DISABLED)
1391ac93838fSSimon Schubert 				bstp_disable_port(sc, bif);
1392ac93838fSSimon Schubert 		}
1393ac93838fSSimon Schubert 		return;
1394ac93838fSSimon Schubert 	}
1395ac93838fSSimon Schubert 
1396ac93838fSSimon Schubert 	if (bif->bif_state != BSTP_IFSTATE_DISABLED)
1397ac93838fSSimon Schubert 		bstp_disable_port(sc, bif);
1398ac93838fSSimon Schubert }
1399ac93838fSSimon Schubert 
1400d217f5e5SScott Ullrich static void
bstp_tick(void * arg)1401ac93838fSSimon Schubert bstp_tick(void *arg)
1402ac93838fSSimon Schubert {
1403ac93838fSSimon Schubert 	struct bridge_softc *sc = arg;
1404e9d22060SSepherosa Ziehau 	struct lwkt_msg *lmsg;
1405e9d22060SSepherosa Ziehau 
1406e9d22060SSepherosa Ziehau 	KKASSERT(mycpuid == BRIDGE_CFGCPU);
1407e9d22060SSepherosa Ziehau 
1408e9d22060SSepherosa Ziehau 	crit_enter();
1409e9d22060SSepherosa Ziehau 
1410e9d22060SSepherosa Ziehau 	if (callout_pending(&sc->sc_bstpcallout) ||
1411e9d22060SSepherosa Ziehau 	    !callout_active(&sc->sc_bstpcallout)) {
1412e9d22060SSepherosa Ziehau 		crit_exit();
1413e9d22060SSepherosa Ziehau 		return;
1414e9d22060SSepherosa Ziehau 	}
1415e9d22060SSepherosa Ziehau 	callout_deactivate(&sc->sc_bstpcallout);
1416e9d22060SSepherosa Ziehau 
1417002c1265SMatthew Dillon 	lmsg = &sc->sc_bstptimemsg.lmsg;
1418e9d22060SSepherosa Ziehau 	KKASSERT(lmsg->ms_flags & MSGF_DONE);
14193a51da79SSepherosa Ziehau 	lwkt_sendmsg_oncpu(BRIDGE_CFGPORT, lmsg);
1420e9d22060SSepherosa Ziehau 
1421e9d22060SSepherosa Ziehau 	crit_exit();
1422e9d22060SSepherosa Ziehau }
1423e9d22060SSepherosa Ziehau 
1424e9d22060SSepherosa Ziehau void
bstp_tick_handler(netmsg_t msg)1425002c1265SMatthew Dillon bstp_tick_handler(netmsg_t msg)
1426e9d22060SSepherosa Ziehau {
1427002c1265SMatthew Dillon 	struct bridge_softc *sc = msg->lmsg.u.ms_resultp;
1428ac93838fSSimon Schubert 	struct bridge_iflist *bif;
1429ac93838fSSimon Schubert 
1430e9d22060SSepherosa Ziehau 	KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT);
1431e9d22060SSepherosa Ziehau 	crit_enter();
1432e9d22060SSepherosa Ziehau 	/* Reply ASAP */
1433002c1265SMatthew Dillon 	lwkt_replymsg(&msg->lmsg, 0);
1434e9d22060SSepherosa Ziehau 	crit_exit();
1435e9d22060SSepherosa Ziehau 
1436a3dd34d2SSepherosa Ziehau 	ifnet_serialize_all(sc->sc_ifp);
1437ac93838fSSimon Schubert 
14388f7b13efSSepherosa Ziehau 	/*
14398f7b13efSSepherosa Ziehau 	 * NOTE:
14408f7b13efSSepherosa Ziehau 	 * We don't need to worry that member iface is ripped
14418f7b13efSSepherosa Ziehau 	 * from the per-cpu list during the blocking operation
14428f7b13efSSepherosa Ziehau 	 * in the loop body, since deletion is serialized by
14438f7b13efSSepherosa Ziehau 	 * BRIDGE_CFGPORT
14448f7b13efSSepherosa Ziehau 	 */
14458f7b13efSSepherosa Ziehau 
144670d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1447ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
1448ac93838fSSimon Schubert 			continue;
1449ac93838fSSimon Schubert 		/*
1450ac93838fSSimon Schubert 		 * XXX This can cause a lag in "link does away"
1451ac93838fSSimon Schubert 		 * XXX and "spanning tree gets updated".  We need
1452ac93838fSSimon Schubert 		 * XXX come sort of callback from the link state
1453ac93838fSSimon Schubert 		 * XXX update code to kick spanning tree.
1454ac93838fSSimon Schubert 		 * XXX --thorpej@NetBSD.org
1455ac93838fSSimon Schubert 		 */
1456ac93838fSSimon Schubert 		bstp_ifupdstatus(sc, bif);
1457ac93838fSSimon Schubert 	}
1458ac93838fSSimon Schubert 
1459ac93838fSSimon Schubert 	if (bstp_timer_expired(&sc->sc_hello_timer, sc->sc_hello_time))
1460ac93838fSSimon Schubert 		bstp_hello_timer_expiry(sc);
1461ac93838fSSimon Schubert 
1462ac93838fSSimon Schubert 	if (bstp_timer_expired(&sc->sc_tcn_timer, sc->sc_bridge_hello_time))
1463ac93838fSSimon Schubert 		bstp_tcn_timer_expiry(sc);
1464ac93838fSSimon Schubert 
1465ac93838fSSimon Schubert 	if (bstp_timer_expired(&sc->sc_topology_change_timer,
1466ac93838fSSimon Schubert 	    sc->sc_topology_change_time))
1467ac93838fSSimon Schubert 		bstp_topology_change_timer_expiry(sc);
1468ac93838fSSimon Schubert 
146970d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1470ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
1471ac93838fSSimon Schubert 			continue;
1472ac93838fSSimon Schubert 		if (bstp_timer_expired(&bif->bif_message_age_timer,
1473ac93838fSSimon Schubert 		    sc->sc_max_age))
1474ac93838fSSimon Schubert 			bstp_message_age_timer_expiry(sc, bif);
1475ac93838fSSimon Schubert 	}
1476ac93838fSSimon Schubert 
147770d9a675SMatthew Dillon 	TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) {
1478ac93838fSSimon Schubert 		if ((bif->bif_flags & IFBIF_STP) == 0)
1479ac93838fSSimon Schubert 			continue;
1480ac93838fSSimon Schubert 		if (bstp_timer_expired(&bif->bif_forward_delay_timer,
1481ac93838fSSimon Schubert 		    sc->sc_forward_delay))
1482ac93838fSSimon Schubert 			bstp_forward_delay_timer_expiry(sc, bif);
1483ac93838fSSimon Schubert 
1484ac93838fSSimon Schubert 		if (bstp_timer_expired(&bif->bif_hold_timer,
1485ac93838fSSimon Schubert 		    sc->sc_hold_time))
1486ac93838fSSimon Schubert 			bstp_hold_timer_expiry(sc, bif);
14873677aae9SMatthew Dillon 
14883677aae9SMatthew Dillon 		if (bstp_timer_expired(&bif->bif_link1_timer,
14893677aae9SMatthew Dillon 		    sc->sc_hello_time * 10))
14903677aae9SMatthew Dillon 			bstp_link1_timer_expiry(sc, bif);
1491ac93838fSSimon Schubert 	}
1492ac93838fSSimon Schubert 
1493ac93838fSSimon Schubert 	if (sc->sc_ifp->if_flags & IFF_RUNNING)
1494ac93838fSSimon Schubert 		callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc);
1495ac93838fSSimon Schubert 
1496a3dd34d2SSepherosa Ziehau 	ifnet_deserialize_all(sc->sc_ifp);
1497ac93838fSSimon Schubert }
1498ac93838fSSimon Schubert 
1499d217f5e5SScott Ullrich static void
bstp_timer_start(struct bridge_timer * t,uint16_t v)1500ac93838fSSimon Schubert bstp_timer_start(struct bridge_timer *t, uint16_t v)
1501ac93838fSSimon Schubert {
1502ac93838fSSimon Schubert 	t->value = v;
1503ac93838fSSimon Schubert 	t->active = 1;
1504ac93838fSSimon Schubert }
1505ac93838fSSimon Schubert 
1506d217f5e5SScott Ullrich static void
bstp_timer_stop(struct bridge_timer * t)1507ac93838fSSimon Schubert bstp_timer_stop(struct bridge_timer *t)
1508ac93838fSSimon Schubert {
1509ac93838fSSimon Schubert 	t->value = 0;
1510ac93838fSSimon Schubert 	t->active = 0;
1511ac93838fSSimon Schubert }
1512ac93838fSSimon Schubert 
1513d217f5e5SScott Ullrich static int
bstp_timer_expired(struct bridge_timer * t,uint16_t v)1514ac93838fSSimon Schubert bstp_timer_expired(struct bridge_timer *t, uint16_t v)
1515ac93838fSSimon Schubert {
1516ac93838fSSimon Schubert 	if (t->active == 0)
1517ac93838fSSimon Schubert 		return (0);
1518ac93838fSSimon Schubert 	t->value += BSTP_TICK_VAL;
1519ac93838fSSimon Schubert 	if (t->value >= v) {
1520ac93838fSSimon Schubert 		bstp_timer_stop(t);
1521ac93838fSSimon Schubert 		return (1);
1522ac93838fSSimon Schubert 	}
1523ac93838fSSimon Schubert 	return (0);
1524ac93838fSSimon Schubert 
1525ac93838fSSimon Schubert }
1526