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