]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/bridgestp.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / net / bridgestp.c
CommitLineData
6d2010ae
A
1/* $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $ */
2
3/*
39236c6e 4 * Copyright (c) 2009-2012 Apple Inc. All rights reserved.
6d2010ae
A
5 *
6 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. The rights granted to you under the License
12 * may not be used to create, or enable the creation or redistribution of,
13 * unlawful or unlicensed copies of an Apple operating system, or to
14 * circumvent, violate, or enable the circumvention or violation of, any
15 * terms of an Apple operating system software license agreement.
16 *
17 * Please obtain a copy of the License at
18 * http://www.opensource.apple.com/apsl/ and read it before using this file.
19 *
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
27 *
28 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 */
30
31/*
32 * Copyright (c) 2000 Jason L. Wright (jason@thought.net)
33 * Copyright (c) 2006 Andrew Thompson (thompsa@FreeBSD.org)
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
47 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
49 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
50 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
54 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
55 * POSSIBILITY OF SUCH DAMAGE.
56 *
57 * OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp
58 */
59
60/*
61 * Implementation of the spanning tree protocol as defined in
62 * ISO/IEC 802.1D-2004, June 9, 2004.
63 */
64
65#include <sys/cdefs.h>
66//__FBSDID("$FreeBSD$");
67
68#include <sys/param.h>
69#include <sys/systm.h>
70#include <sys/mbuf.h>
71#include <sys/socket.h>
72#include <sys/sockio.h>
73#include <sys/kernel.h>
6d2010ae
A
74//#include <sys/module.h>
75#include <sys/proc.h>
76#include <sys/lock.h>
77//#include <sys/mutex.h>
78//#include <sys/taskqueue.h>
79
80#include <net/if.h>
81#include <net/if_dl.h>
82#include <net/if_types.h>
83#include <net/if_llc.h>
84#include <net/if_media.h>
85
86#include <net/kpi_interface.h>
87
88#include <netinet/in.h>
89#include <netinet/in_systm.h>
90#include <netinet/in_var.h>
91#include <netinet/if_ether.h>
92#include <net/bridgestp.h>
93
94#include <kern/thread.h>
95
316670eb 96decl_lck_mtx_data(static, bstp_task_mtx_data);
0a7de745
A
97static lck_mtx_t *bstp_task_mtx = &bstp_task_mtx_data;
98static lck_grp_t *bstp_task_grp = NULL;
99static lck_attr_t *bstp_task_attr = NULL;
100static thread_t bstp_task_thread;
101static TAILQ_HEAD(bstp_task_queue, bstp_task)
102bstp_task_queue = TAILQ_HEAD_INITIALIZER(bstp_task_queue);
6d2010ae
A
103static struct bstp_task *bstp_task_queue_running = NULL;
104
105static void bstp_create_task_thread(void);
106static void bstp_task_thread_func(void);
107
108static void bstp_task_enqueue(struct bstp_task *);
109static void bstp_task_drain(struct bstp_task *);
110
111#define BSTP_TASK_INIT(bt, func, context) do { \
112 (bt)->bt_count = 0; \
113 (bt)->bt_func = func; \
114 (bt)->bt_context = context; \
115} while(0)
116
117
118
0a7de745
A
119#define BSTP_LOCK_INIT(_bs) (_bs)->bs_mtx = lck_mtx_alloc_init(bstp_lock_grp, bstp_lock_attr)
120#define BSTP_LOCK_DESTROY(_bs) lck_mtx_free((_bs)->bs_mtx, bstp_lock_grp)
121#define BSTP_LOCK(_bs) lck_mtx_lock((_bs)->bs_mtx)
122#define BSTP_UNLOCK(_bs) lck_mtx_unlock((_bs)->bs_mtx)
123#define BSTP_LOCK_ASSERT(_bs) LCK_MTX_ASSERT((_bs)->bs_mtx, LCK_MTX_ASSERT_OWNED)
6d2010ae
A
124
125
0a7de745
A
126#ifdef BRIDGESTP_DEBUG
127#define DPRINTF(fmt, arg...) printf("bstp: " fmt, ##arg)
6d2010ae 128#else
0a7de745 129#define DPRINTF(fmt, arg...)
6d2010ae
A
130#endif
131
0a7de745
A
132#define PV2ADDR(pv, eaddr) do { \
133 eaddr[0] = pv >> 40; \
134 eaddr[1] = pv >> 32; \
135 eaddr[2] = pv >> 24; \
136 eaddr[3] = pv >> 16; \
137 eaddr[4] = pv >> 8; \
138 eaddr[5] = pv >> 0; \
6d2010ae
A
139} while (0)
140
0a7de745
A
141#define INFO_BETTER 1
142#define INFO_SAME 0
143#define INFO_WORSE -1
6d2010ae 144
6d2010ae 145LIST_HEAD(, bstp_state) bstp_list;
316670eb 146decl_lck_mtx_data(static, bstp_list_mtx_data);
0a7de745
A
147static lck_mtx_t *bstp_list_mtx = &bstp_list_mtx_data;
148static lck_grp_t *bstp_lock_grp = NULL;
149static lck_attr_t *bstp_lock_attr = NULL;
150
151static void bstp_transmit(struct bstp_state *, struct bstp_port *);
152static void bstp_transmit_bpdu(struct bstp_state *, struct bstp_port *);
153static void bstp_transmit_tcn(struct bstp_state *, struct bstp_port *);
154static void bstp_decode_bpdu(struct bstp_port *, struct bstp_cbpdu *,
155 struct bstp_config_unit *);
156static void bstp_send_bpdu(struct bstp_state *, struct bstp_port *,
157 struct bstp_cbpdu *);
158static void bstp_enqueue(struct ifnet *, struct mbuf *);
159static int bstp_pdu_flags(struct bstp_port *);
160static void bstp_received_stp(struct bstp_state *, struct bstp_port *,
161 struct mbuf **, struct bstp_tbpdu *);
162static void bstp_received_rstp(struct bstp_state *, struct bstp_port *,
163 struct mbuf **, struct bstp_tbpdu *);
164static void bstp_received_tcn(struct bstp_state *, struct bstp_port *,
165 struct bstp_tcn_unit *);
166static void bstp_received_bpdu(struct bstp_state *, struct bstp_port *,
167 struct bstp_config_unit *);
168static int bstp_pdu_rcvtype(struct bstp_port *, struct bstp_config_unit *);
169static int bstp_pdu_bettersame(struct bstp_port *, int);
170static int bstp_info_cmp(struct bstp_pri_vector *,
171 struct bstp_pri_vector *);
172static int bstp_info_superior(struct bstp_pri_vector *,
173 struct bstp_pri_vector *);
174static void bstp_assign_roles(struct bstp_state *);
175static void bstp_update_roles(struct bstp_state *, struct bstp_port *);
176static void bstp_update_state(struct bstp_state *, struct bstp_port *);
177static void bstp_update_tc(struct bstp_port *);
178static void bstp_update_info(struct bstp_port *);
179static void bstp_set_other_tcprop(struct bstp_port *);
180static void bstp_set_all_reroot(struct bstp_state *);
181static void bstp_set_all_sync(struct bstp_state *);
182static void bstp_set_port_state(struct bstp_port *, int);
183static void bstp_set_port_role(struct bstp_port *, int);
184static void bstp_set_port_proto(struct bstp_port *, int);
185static void bstp_set_port_tc(struct bstp_port *, int);
186static void bstp_set_timer_tc(struct bstp_port *);
187static void bstp_set_timer_msgage(struct bstp_port *);
188static int bstp_rerooted(struct bstp_state *, struct bstp_port *);
189static uint32_t bstp_calc_path_cost(struct bstp_port *);
190static void bstp_notify_state(void *, int);
191static void bstp_notify_rtage(void *, int);
192static void bstp_ifupdstatus(struct bstp_state *, struct bstp_port *);
193static void bstp_enable_port(struct bstp_state *, struct bstp_port *);
194static void bstp_disable_port(struct bstp_state *, struct bstp_port *);
195static void bstp_tick(void *);
196static void bstp_timer_start(struct bstp_timer *, uint16_t);
197static void bstp_timer_stop(struct bstp_timer *);
198static void bstp_timer_latch(struct bstp_timer *);
199static int bstp_timer_expired(struct bstp_timer *);
200static void bstp_hello_timer_expiry(struct bstp_state *,
201 struct bstp_port *);
202static void bstp_message_age_expiry(struct bstp_state *,
203 struct bstp_port *);
204static void bstp_migrate_delay_expiry(struct bstp_state *,
205 struct bstp_port *);
206static void bstp_edge_delay_expiry(struct bstp_state *,
207 struct bstp_port *);
208static int bstp_addr_cmp(const uint8_t *, const uint8_t *);
209static int bstp_same_bridgeid(uint64_t, uint64_t);
210static void bstp_reinit(struct bstp_state *);
6d2010ae
A
211
212static void
213bstp_transmit(struct bstp_state *bs, struct bstp_port *bp)
214{
0a7de745 215 if (bs->bs_running == 0) {
6d2010ae 216 return;
0a7de745 217 }
6d2010ae
A
218
219 /*
220 * a PDU can only be sent if we have tx quota left and the
221 * hello timer is running.
222 */
223 if (bp->bp_hello_timer.active == 0) {
224 /* Test if it needs to be reset */
225 bstp_hello_timer_expiry(bs, bp);
226 return;
227 }
0a7de745 228 if (bp->bp_txcount > bs->bs_txholdcount) {
6d2010ae
A
229 /* Ran out of karma */
230 return;
0a7de745 231 }
6d2010ae
A
232
233 if (bp->bp_protover == BSTP_PROTO_RSTP) {
234 bstp_transmit_bpdu(bs, bp);
235 bp->bp_tc_ack = 0;
236 } else { /* STP */
237 switch (bp->bp_role) {
0a7de745
A
238 case BSTP_ROLE_DESIGNATED:
239 bstp_transmit_bpdu(bs, bp);
240 bp->bp_tc_ack = 0;
241 break;
6d2010ae 242
0a7de745
A
243 case BSTP_ROLE_ROOT:
244 bstp_transmit_tcn(bs, bp);
245 break;
6d2010ae
A
246 }
247 }
248 bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
249 bp->bp_flags &= ~BSTP_PORT_NEWINFO;
250}
251
252static void
253bstp_transmit_bpdu(struct bstp_state *bs, struct bstp_port *bp)
254{
255 struct bstp_cbpdu bpdu;
256
257 BSTP_LOCK_ASSERT(bs);
258
259 bpdu.cbu_rootpri = htons(bp->bp_desg_pv.pv_root_id >> 48);
260 PV2ADDR(bp->bp_desg_pv.pv_root_id, bpdu.cbu_rootaddr);
261
262 bpdu.cbu_rootpathcost = htonl(bp->bp_desg_pv.pv_cost);
263
264 bpdu.cbu_bridgepri = htons(bp->bp_desg_pv.pv_dbridge_id >> 48);
265 PV2ADDR(bp->bp_desg_pv.pv_dbridge_id, bpdu.cbu_bridgeaddr);
266
267 bpdu.cbu_portid = htons(bp->bp_port_id);
268 bpdu.cbu_messageage = htons(bp->bp_desg_msg_age);
269 bpdu.cbu_maxage = htons(bp->bp_desg_max_age);
270 bpdu.cbu_hellotime = htons(bp->bp_desg_htime);
271 bpdu.cbu_forwarddelay = htons(bp->bp_desg_fdelay);
272
273 bpdu.cbu_flags = bstp_pdu_flags(bp);
274
275 switch (bp->bp_protover) {
0a7de745
A
276 case BSTP_PROTO_STP:
277 bpdu.cbu_bpdutype = BSTP_MSGTYPE_CFG;
278 break;
6d2010ae 279
0a7de745
A
280 case BSTP_PROTO_RSTP:
281 bpdu.cbu_bpdutype = BSTP_MSGTYPE_RSTP;
282 break;
6d2010ae
A
283 }
284
285 bstp_send_bpdu(bs, bp, &bpdu);
286}
287
288static void
289bstp_transmit_tcn(struct bstp_state *bs, struct bstp_port *bp)
290{
291 struct bstp_tbpdu bpdu;
292 struct ifnet *ifp = bp->bp_ifp;
293 struct ether_header *eh;
294 struct mbuf *m;
295 int touched = bs ? 1 : 0;
0a7de745 296
6d2010ae
A
297 touched++;
298
299 KASSERT(bp == bs->bs_root_port, ("%s: bad root port\n", __func__));
300
0a7de745 301 if ((ifp->if_flags & IFF_RUNNING) == 0) {
6d2010ae 302 return;
0a7de745 303 }
6d2010ae
A
304
305 MGETHDR(m, M_DONTWAIT, MT_DATA);
0a7de745 306 if (m == NULL) {
6d2010ae 307 return;
0a7de745 308 }
6d2010ae
A
309
310 m->m_pkthdr.rcvif = ifp;
311 m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu);
312 m->m_len = m->m_pkthdr.len;
313
314 eh = mtod(m, struct ether_header *);
315
39236c6e 316 memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
6d2010ae
A
317 memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
318 eh->ether_type = htons(sizeof(bpdu));
319
320 bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP;
321 bpdu.tbu_ctl = LLC_UI;
322 bpdu.tbu_protoid = 0;
323 bpdu.tbu_protover = 0;
324 bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN;
325
326 memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu));
327
328 bp->bp_txcount++;
329 bstp_enqueue(ifp, m);
330}
331
332static void
333bstp_decode_bpdu(struct bstp_port *bp, struct bstp_cbpdu *cpdu,
334 struct bstp_config_unit *cu)
335{
336 int flags;
337
338 cu->cu_pv.pv_root_id =
339 (((uint64_t)ntohs(cpdu->cbu_rootpri)) << 48) |
340 (((uint64_t)cpdu->cbu_rootaddr[0]) << 40) |
341 (((uint64_t)cpdu->cbu_rootaddr[1]) << 32) |
342 (((uint64_t)cpdu->cbu_rootaddr[2]) << 24) |
343 (((uint64_t)cpdu->cbu_rootaddr[3]) << 16) |
344 (((uint64_t)cpdu->cbu_rootaddr[4]) << 8) |
345 (((uint64_t)cpdu->cbu_rootaddr[5]) << 0);
346
347 cu->cu_pv.pv_dbridge_id =
348 (((uint64_t)ntohs(cpdu->cbu_bridgepri)) << 48) |
349 (((uint64_t)cpdu->cbu_bridgeaddr[0]) << 40) |
350 (((uint64_t)cpdu->cbu_bridgeaddr[1]) << 32) |
351 (((uint64_t)cpdu->cbu_bridgeaddr[2]) << 24) |
352 (((uint64_t)cpdu->cbu_bridgeaddr[3]) << 16) |
353 (((uint64_t)cpdu->cbu_bridgeaddr[4]) << 8) |
354 (((uint64_t)cpdu->cbu_bridgeaddr[5]) << 0);
355
356 cu->cu_pv.pv_cost = ntohl(cpdu->cbu_rootpathcost);
357 cu->cu_message_age = ntohs(cpdu->cbu_messageage);
358 cu->cu_max_age = ntohs(cpdu->cbu_maxage);
359 cu->cu_hello_time = ntohs(cpdu->cbu_hellotime);
360 cu->cu_forward_delay = ntohs(cpdu->cbu_forwarddelay);
361 cu->cu_pv.pv_dport_id = ntohs(cpdu->cbu_portid);
362 cu->cu_pv.pv_port_id = bp->bp_port_id;
363 cu->cu_message_type = cpdu->cbu_bpdutype;
364
365 /* Strip off unused flags in STP mode */
366 flags = cpdu->cbu_flags;
367 switch (cpdu->cbu_protover) {
0a7de745
A
368 case BSTP_PROTO_STP:
369 flags &= BSTP_PDU_STPMASK;
370 /* A STP BPDU explicitly conveys a Designated Port */
371 cu->cu_role = BSTP_ROLE_DESIGNATED;
372 break;
6d2010ae 373
0a7de745
A
374 case BSTP_PROTO_RSTP:
375 flags &= BSTP_PDU_RSTPMASK;
376 break;
6d2010ae
A
377 }
378
379 cu->cu_topology_change_ack =
0a7de745 380 (flags & BSTP_PDU_F_TCA) ? 1 : 0;
6d2010ae 381 cu->cu_proposal =
0a7de745 382 (flags & BSTP_PDU_F_P) ? 1 : 0;
6d2010ae 383 cu->cu_agree =
0a7de745 384 (flags & BSTP_PDU_F_A) ? 1 : 0;
6d2010ae 385 cu->cu_learning =
0a7de745 386 (flags & BSTP_PDU_F_L) ? 1 : 0;
6d2010ae 387 cu->cu_forwarding =
0a7de745 388 (flags & BSTP_PDU_F_F) ? 1 : 0;
6d2010ae 389 cu->cu_topology_change =
0a7de745 390 (flags & BSTP_PDU_F_TC) ? 1 : 0;
6d2010ae
A
391
392 switch ((flags & BSTP_PDU_PRMASK) >> BSTP_PDU_PRSHIFT) {
0a7de745
A
393 case BSTP_PDU_F_ROOT:
394 cu->cu_role = BSTP_ROLE_ROOT;
395 break;
396 case BSTP_PDU_F_ALT:
397 cu->cu_role = BSTP_ROLE_ALTERNATE;
398 break;
399 case BSTP_PDU_F_DESG:
400 cu->cu_role = BSTP_ROLE_DESIGNATED;
401 break;
6d2010ae
A
402 }
403}
404
405static void
406bstp_send_bpdu(struct bstp_state *bs, struct bstp_port *bp,
407 struct bstp_cbpdu *bpdu)
408{
409 struct ifnet *ifp;
410 struct mbuf *m;
411 struct ether_header *eh;
412
413 BSTP_LOCK_ASSERT(bs);
414
415 ifp = bp->bp_ifp;
416
0a7de745 417 if ((ifp->if_flags & IFF_RUNNING) == 0) {
6d2010ae 418 return;
0a7de745 419 }
6d2010ae
A
420
421 MGETHDR(m, M_DONTWAIT, MT_DATA);
0a7de745 422 if (m == NULL) {
6d2010ae 423 return;
0a7de745 424 }
6d2010ae
A
425
426 eh = mtod(m, struct ether_header *);
427
428 bpdu->cbu_ssap = bpdu->cbu_dsap = LLC_8021D_LSAP;
429 bpdu->cbu_ctl = LLC_UI;
430 bpdu->cbu_protoid = htons(BSTP_PROTO_ID);
431
39236c6e 432 memcpy(eh->ether_shost, IF_LLADDR(ifp), ETHER_ADDR_LEN);
6d2010ae
A
433 memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN);
434
435 switch (bpdu->cbu_bpdutype) {
0a7de745
A
436 case BSTP_MSGTYPE_CFG:
437 bpdu->cbu_protover = BSTP_PROTO_STP;
438 m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_STP_LEN;
439 eh->ether_type = htons(BSTP_BPDU_STP_LEN);
440 memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
441 BSTP_BPDU_STP_LEN);
442 break;
6d2010ae 443
0a7de745
A
444 case BSTP_MSGTYPE_RSTP:
445 bpdu->cbu_protover = BSTP_PROTO_RSTP;
446 bpdu->cbu_versionlen = htons(0);
447 m->m_pkthdr.len = sizeof(*eh) + BSTP_BPDU_RSTP_LEN;
448 eh->ether_type = htons(BSTP_BPDU_RSTP_LEN);
449 memcpy(mtod(m, caddr_t) + sizeof(*eh), bpdu,
450 BSTP_BPDU_RSTP_LEN);
451 break;
6d2010ae 452
0a7de745
A
453 default:
454 panic("not implemented");
6d2010ae
A
455 }
456 m->m_pkthdr.rcvif = ifp;
457 m->m_len = m->m_pkthdr.len;
458
459 bp->bp_txcount++;
460 bstp_enqueue(ifp, m);
461}
462
463static void
464bstp_enqueue(struct ifnet *dst_ifp, struct mbuf *m)
465{
466 errno_t error = 0;
467 u_int32_t len = m->m_pkthdr.len;
0a7de745
A
468
469 m->m_flags |= M_PROTO1; //set to avoid loops
470
6d2010ae
A
471 error = ifnet_output_raw(dst_ifp, 0, m);
472 if (error == 0) {
473 (void) ifnet_stat_increment_out(dst_ifp, 1, len, 0);
474 } else {
475 (void) ifnet_stat_increment_out(dst_ifp, 0, 0, 1);
476 }
477}
478
479static int
480bstp_pdu_flags(struct bstp_port *bp)
481{
482 int flags = 0;
483
0a7de745 484 if (bp->bp_proposing && bp->bp_state != BSTP_IFSTATE_FORWARDING) {
6d2010ae 485 flags |= BSTP_PDU_F_P;
0a7de745 486 }
6d2010ae 487
0a7de745 488 if (bp->bp_agree) {
6d2010ae 489 flags |= BSTP_PDU_F_A;
0a7de745 490 }
6d2010ae 491
0a7de745 492 if (bp->bp_tc_timer.active) {
6d2010ae 493 flags |= BSTP_PDU_F_TC;
0a7de745 494 }
6d2010ae 495
0a7de745 496 if (bp->bp_tc_ack) {
6d2010ae 497 flags |= BSTP_PDU_F_TCA;
0a7de745 498 }
6d2010ae
A
499
500 switch (bp->bp_state) {
0a7de745
A
501 case BSTP_IFSTATE_LEARNING:
502 flags |= BSTP_PDU_F_L;
503 break;
6d2010ae 504
0a7de745
A
505 case BSTP_IFSTATE_FORWARDING:
506 flags |= (BSTP_PDU_F_L | BSTP_PDU_F_F);
507 break;
6d2010ae
A
508 }
509
510 switch (bp->bp_role) {
0a7de745
A
511 case BSTP_ROLE_ROOT:
512 flags |=
513 (BSTP_PDU_F_ROOT << BSTP_PDU_PRSHIFT);
514 break;
6d2010ae 515
0a7de745
A
516 case BSTP_ROLE_ALTERNATE:
517 case BSTP_ROLE_BACKUP: /* fall through */
518 flags |=
519 (BSTP_PDU_F_ALT << BSTP_PDU_PRSHIFT);
520 break;
6d2010ae 521
0a7de745
A
522 case BSTP_ROLE_DESIGNATED:
523 flags |=
524 (BSTP_PDU_F_DESG << BSTP_PDU_PRSHIFT);
525 break;
6d2010ae
A
526 }
527
528 /* Strip off unused flags in either mode */
529 switch (bp->bp_protover) {
0a7de745
A
530 case BSTP_PROTO_STP:
531 flags &= BSTP_PDU_STPMASK;
532 break;
533 case BSTP_PROTO_RSTP:
534 flags &= BSTP_PDU_RSTPMASK;
535 break;
6d2010ae 536 }
0a7de745 537 return flags;
6d2010ae
A
538}
539
540struct mbuf *
541bstp_input(struct bstp_port *bp, __unused struct ifnet *ifp, struct mbuf *m)
542{
543 struct bstp_state *bs = bp->bp_bs;
544 struct ether_header *eh;
545 struct bstp_tbpdu tpdu;
546 uint16_t len;
547
548 if (bp->bp_active == 0) {
549 m_freem(m);
0a7de745 550 return NULL;
6d2010ae
A
551 }
552
553 BSTP_LOCK(bs);
554
555 eh = mtod(m, struct ether_header *);
556
557 len = ntohs(eh->ether_type);
0a7de745 558 if (len < sizeof(tpdu)) {
6d2010ae 559 goto out;
0a7de745 560 }
6d2010ae
A
561
562 m_adj(m, ETHER_HDR_LEN);
563
0a7de745 564 if (m->m_pkthdr.len > len) {
6d2010ae 565 m_adj(m, len - m->m_pkthdr.len);
0a7de745 566 }
6d2010ae 567 if ((unsigned int)m->m_len < sizeof(tpdu) &&
0a7de745 568 (m = m_pullup(m, sizeof(tpdu))) == NULL) {
6d2010ae 569 goto out;
0a7de745 570 }
6d2010ae
A
571
572 memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu));
573
574 /* basic packet checks */
575 if (tpdu.tbu_dsap != LLC_8021D_LSAP ||
576 tpdu.tbu_ssap != LLC_8021D_LSAP ||
0a7de745 577 tpdu.tbu_ctl != LLC_UI) {
6d2010ae 578 goto out;
0a7de745
A
579 }
580 if (tpdu.tbu_protoid != BSTP_PROTO_ID) {
6d2010ae 581 goto out;
0a7de745 582 }
6d2010ae
A
583
584 /*
585 * We can treat later versions of the PDU as the same as the maximum
586 * version we implement. All additional parameters/flags are ignored.
587 */
0a7de745 588 if (tpdu.tbu_protover > BSTP_PROTO_MAX) {
6d2010ae 589 tpdu.tbu_protover = BSTP_PROTO_MAX;
0a7de745 590 }
6d2010ae
A
591
592 if (tpdu.tbu_protover != bp->bp_protover) {
593 /*
594 * Wait for the migration delay timer to expire before changing
595 * protocol version to avoid flip-flops.
596 */
0a7de745 597 if (bp->bp_flags & BSTP_PORT_CANMIGRATE) {
6d2010ae 598 bstp_set_port_proto(bp, tpdu.tbu_protover);
0a7de745 599 } else {
6d2010ae 600 goto out;
0a7de745 601 }
6d2010ae
A
602 }
603
604 /* Clear operedge upon receiving a PDU on the port */
605 bp->bp_operedge = 0;
606 bstp_timer_start(&bp->bp_edge_delay_timer,
607 BSTP_DEFAULT_MIGRATE_DELAY);
608
609 switch (tpdu.tbu_protover) {
0a7de745
A
610 case BSTP_PROTO_STP:
611 bstp_received_stp(bs, bp, &m, &tpdu);
612 break;
6d2010ae 613
0a7de745
A
614 case BSTP_PROTO_RSTP:
615 bstp_received_rstp(bs, bp, &m, &tpdu);
616 break;
6d2010ae
A
617 }
618out:
619 BSTP_UNLOCK(bs);
0a7de745 620 if (m) {
6d2010ae 621 m_freem(m);
0a7de745
A
622 }
623 return NULL;
6d2010ae
A
624}
625
626static void
627bstp_received_stp(struct bstp_state *bs, struct bstp_port *bp,
628 struct mbuf **mp, struct bstp_tbpdu *tpdu)
629{
630 struct bstp_cbpdu cpdu;
631 struct bstp_config_unit *cu = &bp->bp_msg_cu;
632 struct bstp_tcn_unit tu;
633
634 switch (tpdu->tbu_bpdutype) {
635 case BSTP_MSGTYPE_TCN:
636 tu.tu_message_type = tpdu->tbu_bpdutype;
637 bstp_received_tcn(bs, bp, &tu);
638 break;
639 case BSTP_MSGTYPE_CFG:
640 if ((*mp)->m_len < BSTP_BPDU_STP_LEN &&
0a7de745 641 (*mp = m_pullup(*mp, BSTP_BPDU_STP_LEN)) == NULL) {
6d2010ae 642 return;
0a7de745 643 }
6d2010ae
A
644 memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_STP_LEN);
645
646 bstp_decode_bpdu(bp, &cpdu, cu);
647 bstp_received_bpdu(bs, bp, cu);
648 break;
649 }
650}
651
652static void
653bstp_received_rstp(struct bstp_state *bs, struct bstp_port *bp,
654 struct mbuf **mp, struct bstp_tbpdu *tpdu)
655{
656 struct bstp_cbpdu cpdu;
657 struct bstp_config_unit *cu = &bp->bp_msg_cu;
658
0a7de745 659 if (tpdu->tbu_bpdutype != BSTP_MSGTYPE_RSTP) {
6d2010ae 660 return;
0a7de745 661 }
6d2010ae
A
662
663 if ((*mp)->m_len < BSTP_BPDU_RSTP_LEN &&
0a7de745 664 (*mp = m_pullup(*mp, BSTP_BPDU_RSTP_LEN)) == NULL) {
6d2010ae 665 return;
0a7de745 666 }
6d2010ae
A
667 memcpy(&cpdu, mtod(*mp, caddr_t), BSTP_BPDU_RSTP_LEN);
668
669 bstp_decode_bpdu(bp, &cpdu, cu);
670 bstp_received_bpdu(bs, bp, cu);
671}
672
673static void
674bstp_received_tcn(__unused struct bstp_state *bs, struct bstp_port *bp,
675 __unused struct bstp_tcn_unit *tcn)
676{
677 bp->bp_rcvdtcn = 1;
678 bstp_update_tc(bp);
679}
680
681static void
682bstp_received_bpdu(struct bstp_state *bs, struct bstp_port *bp,
683 struct bstp_config_unit *cu)
684{
685 int type;
686
687 BSTP_LOCK_ASSERT(bs);
688
689 /* We need to have transitioned to INFO_MINE before proceeding */
690 switch (bp->bp_infois) {
0a7de745
A
691 case BSTP_INFO_DISABLED:
692 case BSTP_INFO_AGED:
693 return;
6d2010ae
A
694 }
695
696 type = bstp_pdu_rcvtype(bp, cu);
697
698 switch (type) {
0a7de745
A
699 case BSTP_PDU_SUPERIOR:
700 bs->bs_allsynced = 0;
701 bp->bp_agreed = 0;
702 bp->bp_proposing = 0;
6d2010ae 703
0a7de745
A
704 if (cu->cu_proposal && cu->cu_forwarding == 0) {
705 bp->bp_proposed = 1;
706 }
707 if (cu->cu_topology_change) {
708 bp->bp_rcvdtc = 1;
709 }
710 if (cu->cu_topology_change_ack) {
711 bp->bp_rcvdtca = 1;
712 }
6d2010ae 713
0a7de745
A
714 if (bp->bp_agree &&
715 !bstp_pdu_bettersame(bp, BSTP_INFO_RECEIVED)) {
716 bp->bp_agree = 0;
717 }
6d2010ae 718
0a7de745
A
719 /* copy the received priority and timers to the port */
720 bp->bp_port_pv = cu->cu_pv;
721 bp->bp_port_msg_age = cu->cu_message_age;
722 bp->bp_port_max_age = cu->cu_max_age;
723 bp->bp_port_fdelay = cu->cu_forward_delay;
724 bp->bp_port_htime =
725 (cu->cu_hello_time > BSTP_MIN_HELLO_TIME ?
726 cu->cu_hello_time : BSTP_MIN_HELLO_TIME);
6d2010ae 727
0a7de745
A
728 /* set expiry for the new info */
729 bstp_set_timer_msgage(bp);
6d2010ae 730
0a7de745
A
731 bp->bp_infois = BSTP_INFO_RECEIVED;
732 bstp_assign_roles(bs);
733 break;
734
735 case BSTP_PDU_REPEATED:
736 if (cu->cu_proposal && cu->cu_forwarding == 0) {
737 bp->bp_proposed = 1;
738 }
739 if (cu->cu_topology_change) {
740 bp->bp_rcvdtc = 1;
741 }
742 if (cu->cu_topology_change_ack) {
743 bp->bp_rcvdtca = 1;
744 }
745
746 /* rearm the age timer */
747 bstp_set_timer_msgage(bp);
748 break;
749
750 case BSTP_PDU_INFERIOR:
751 if (cu->cu_learning) {
752 bp->bp_agreed = 1;
753 bp->bp_proposing = 0;
754 }
755 break;
756
757 case BSTP_PDU_INFERIORALT:
758 /*
759 * only point to point links are allowed fast
760 * transitions to forwarding.
761 */
762 if (cu->cu_agree && bp->bp_ptp_link) {
763 bp->bp_agreed = 1;
764 bp->bp_proposing = 0;
765 } else {
766 bp->bp_agreed = 0;
767 }
768
769 if (cu->cu_topology_change) {
770 bp->bp_rcvdtc = 1;
771 }
772 if (cu->cu_topology_change_ack) {
773 bp->bp_rcvdtca = 1;
774 }
775 break;
6d2010ae 776
0a7de745
A
777 case BSTP_PDU_OTHER:
778 return; /* do nothing */
6d2010ae
A
779 }
780 /* update the state machines with the new data */
781 bstp_update_state(bs, bp);
782}
783
784static int
785bstp_pdu_rcvtype(struct bstp_port *bp, struct bstp_config_unit *cu)
786{
787 int type;
788
789 /* default return type */
790 type = BSTP_PDU_OTHER;
791
792 switch (cu->cu_role) {
793 case BSTP_ROLE_DESIGNATED:
0a7de745 794 if (bstp_info_superior(&bp->bp_port_pv, &cu->cu_pv)) {
6d2010ae
A
795 /* bpdu priority is superior */
796 type = BSTP_PDU_SUPERIOR;
0a7de745 797 } else if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) ==
6d2010ae
A
798 INFO_SAME) {
799 if (bp->bp_port_msg_age != cu->cu_message_age ||
800 bp->bp_port_max_age != cu->cu_max_age ||
801 bp->bp_port_fdelay != cu->cu_forward_delay ||
0a7de745 802 bp->bp_port_htime != cu->cu_hello_time) {
6d2010ae
A
803 /* bpdu priority is equal and timers differ */
804 type = BSTP_PDU_SUPERIOR;
0a7de745 805 } else {
6d2010ae
A
806 /* bpdu is equal */
807 type = BSTP_PDU_REPEATED;
0a7de745
A
808 }
809 } else {
6d2010ae
A
810 /* bpdu priority is worse */
811 type = BSTP_PDU_INFERIOR;
0a7de745 812 }
6d2010ae
A
813
814 break;
815
816 case BSTP_ROLE_ROOT:
817 case BSTP_ROLE_ALTERNATE:
818 case BSTP_ROLE_BACKUP:
0a7de745 819 if (bstp_info_cmp(&bp->bp_port_pv, &cu->cu_pv) <= INFO_SAME) {
6d2010ae
A
820 /*
821 * not a designated port and priority is the same or
822 * worse
823 */
824 type = BSTP_PDU_INFERIORALT;
0a7de745 825 }
6d2010ae
A
826 break;
827 }
828
0a7de745 829 return type;
6d2010ae
A
830}
831
832static int
833bstp_pdu_bettersame(struct bstp_port *bp, int newinfo)
834{
835 if (newinfo == BSTP_INFO_RECEIVED &&
836 bp->bp_infois == BSTP_INFO_RECEIVED &&
0a7de745
A
837 bstp_info_cmp(&bp->bp_port_pv, &bp->bp_msg_cu.cu_pv) >= INFO_SAME) {
838 return 1;
839 }
6d2010ae
A
840
841 if (newinfo == BSTP_INFO_MINE &&
842 bp->bp_infois == BSTP_INFO_MINE &&
0a7de745
A
843 bstp_info_cmp(&bp->bp_port_pv, &bp->bp_desg_pv) >= INFO_SAME) {
844 return 1;
845 }
6d2010ae 846
0a7de745 847 return 0;
6d2010ae
A
848}
849
850static int
851bstp_info_cmp(struct bstp_pri_vector *pv,
852 struct bstp_pri_vector *cpv)
853{
0a7de745
A
854 if (cpv->pv_root_id < pv->pv_root_id) {
855 return INFO_BETTER;
856 }
857 if (cpv->pv_root_id > pv->pv_root_id) {
858 return INFO_WORSE;
859 }
6d2010ae 860
0a7de745
A
861 if (cpv->pv_cost < pv->pv_cost) {
862 return INFO_BETTER;
863 }
864 if (cpv->pv_cost > pv->pv_cost) {
865 return INFO_WORSE;
866 }
6d2010ae 867
0a7de745
A
868 if (cpv->pv_dbridge_id < pv->pv_dbridge_id) {
869 return INFO_BETTER;
870 }
871 if (cpv->pv_dbridge_id > pv->pv_dbridge_id) {
872 return INFO_WORSE;
873 }
6d2010ae 874
0a7de745
A
875 if (cpv->pv_dport_id < pv->pv_dport_id) {
876 return INFO_BETTER;
877 }
878 if (cpv->pv_dport_id > pv->pv_dport_id) {
879 return INFO_WORSE;
880 }
6d2010ae 881
0a7de745 882 return INFO_SAME;
6d2010ae
A
883}
884
885/*
886 * This message priority vector is superior to the port priority vector and
887 * will replace it if, and only if, the message priority vector is better than
888 * the port priority vector, or the message has been transmitted from the same
889 * designated bridge and designated port as the port priority vector.
890 */
891static int
892bstp_info_superior(struct bstp_pri_vector *pv,
893 struct bstp_pri_vector *cpv)
894{
895 if (bstp_info_cmp(pv, cpv) == INFO_BETTER ||
896 (bstp_same_bridgeid(pv->pv_dbridge_id, cpv->pv_dbridge_id) &&
0a7de745
A
897 (cpv->pv_dport_id & 0xfff) == (pv->pv_dport_id & 0xfff))) {
898 return 1;
899 }
900 return 0;
6d2010ae
A
901}
902
903static void
904bstp_assign_roles(struct bstp_state *bs)
905{
906 struct bstp_port *bp, *rbp = NULL;
907 struct bstp_pri_vector pv;
908
909 /* default to our priority vector */
910 bs->bs_root_pv = bs->bs_bridge_pv;
911 bs->bs_root_msg_age = 0;
912 bs->bs_root_max_age = bs->bs_bridge_max_age;
913 bs->bs_root_fdelay = bs->bs_bridge_fdelay;
914 bs->bs_root_htime = bs->bs_bridge_htime;
915 bs->bs_root_port = NULL;
916
917 /* check if any recieved info supersedes us */
918 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
0a7de745 919 if (bp->bp_infois != BSTP_INFO_RECEIVED) {
6d2010ae 920 continue;
0a7de745 921 }
6d2010ae
A
922
923 pv = bp->bp_port_pv;
924 pv.pv_cost += bp->bp_path_cost;
925
926 /*
927 * The root priority vector is the best of the set comprising
928 * the bridge priority vector plus all root path priority
929 * vectors whose bridge address is not equal to us.
930 */
931 if (bstp_same_bridgeid(pv.pv_dbridge_id,
932 bs->bs_bridge_pv.pv_dbridge_id) == 0 &&
933 bstp_info_cmp(&bs->bs_root_pv, &pv) == INFO_BETTER) {
934 /* the port vector replaces the root */
935 bs->bs_root_pv = pv;
936 bs->bs_root_msg_age = bp->bp_port_msg_age +
937 BSTP_MESSAGE_AGE_INCR;
938 bs->bs_root_max_age = bp->bp_port_max_age;
939 bs->bs_root_fdelay = bp->bp_port_fdelay;
940 bs->bs_root_htime = bp->bp_port_htime;
941 rbp = bp;
942 }
943 }
944
945 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
946 /* calculate the port designated vector */
947 bp->bp_desg_pv.pv_root_id = bs->bs_root_pv.pv_root_id;
948 bp->bp_desg_pv.pv_cost = bs->bs_root_pv.pv_cost;
949 bp->bp_desg_pv.pv_dbridge_id = bs->bs_bridge_pv.pv_dbridge_id;
950 bp->bp_desg_pv.pv_dport_id = bp->bp_port_id;
951 bp->bp_desg_pv.pv_port_id = bp->bp_port_id;
952
953 /* calculate designated times */
954 bp->bp_desg_msg_age = bs->bs_root_msg_age;
955 bp->bp_desg_max_age = bs->bs_root_max_age;
956 bp->bp_desg_fdelay = bs->bs_root_fdelay;
957 bp->bp_desg_htime = bs->bs_bridge_htime;
958
959
960 switch (bp->bp_infois) {
961 case BSTP_INFO_DISABLED:
962 bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
963 break;
964
965 case BSTP_INFO_AGED:
966 bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
967 bstp_update_info(bp);
968 break;
969
970 case BSTP_INFO_MINE:
971 bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
972 /* update the port info if stale */
973 if (bstp_info_cmp(&bp->bp_port_pv,
974 &bp->bp_desg_pv) != INFO_SAME ||
975 (rbp != NULL &&
976 (bp->bp_port_msg_age != rbp->bp_port_msg_age ||
977 bp->bp_port_max_age != rbp->bp_port_max_age ||
978 bp->bp_port_fdelay != rbp->bp_port_fdelay ||
0a7de745 979 bp->bp_port_htime != rbp->bp_port_htime))) {
6d2010ae 980 bstp_update_info(bp);
0a7de745 981 }
6d2010ae
A
982 break;
983
984 case BSTP_INFO_RECEIVED:
985 if (bp == rbp) {
986 /*
987 * root priority is derived from this
988 * port, make it the root port.
989 */
990 bstp_set_port_role(bp, BSTP_ROLE_ROOT);
991 bs->bs_root_port = bp;
992 } else if (bstp_info_cmp(&bp->bp_port_pv,
0a7de745 993 &bp->bp_desg_pv) == INFO_BETTER) {
6d2010ae
A
994 /*
995 * the port priority is lower than the root
996 * port.
997 */
998 bstp_set_port_role(bp, BSTP_ROLE_DESIGNATED);
999 bstp_update_info(bp);
1000 } else {
1001 if (bstp_same_bridgeid(
0a7de745
A
1002 bp->bp_port_pv.pv_dbridge_id,
1003 bs->bs_bridge_pv.pv_dbridge_id)) {
6d2010ae
A
1004 /*
1005 * the designated bridge refers to
1006 * another port on this bridge.
1007 */
1008 bstp_set_port_role(bp,
1009 BSTP_ROLE_BACKUP);
1010 } else {
1011 /*
1012 * the port is an inferior path to the
1013 * root bridge.
1014 */
1015 bstp_set_port_role(bp,
1016 BSTP_ROLE_ALTERNATE);
1017 }
1018 }
1019 break;
1020 }
1021 }
1022}
1023
1024static void
1025bstp_update_state(struct bstp_state *bs, struct bstp_port *bp)
1026{
1027 struct bstp_port *bp2;
1028 int synced;
1029
1030 BSTP_LOCK_ASSERT(bs);
1031
1032 /* check if all the ports have syncronised again */
1033 if (!bs->bs_allsynced) {
1034 synced = 1;
1035 LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
1036 if (!(bp2->bp_synced ||
0a7de745 1037 bp2->bp_role == BSTP_ROLE_ROOT)) {
6d2010ae
A
1038 synced = 0;
1039 break;
1040 }
1041 }
1042 bs->bs_allsynced = synced;
1043 }
1044
1045 bstp_update_roles(bs, bp);
1046 bstp_update_tc(bp);
1047}
1048
1049static void
1050bstp_update_roles(struct bstp_state *bs, struct bstp_port *bp)
1051{
1052 switch (bp->bp_role) {
1053 case BSTP_ROLE_DISABLED:
1054 /* Clear any flags if set */
1055 if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
1056 bp->bp_sync = 0;
1057 bp->bp_synced = 1;
1058 bp->bp_reroot = 0;
1059 }
1060 break;
1061
1062 case BSTP_ROLE_ALTERNATE:
1063 case BSTP_ROLE_BACKUP:
1064 if ((bs->bs_allsynced && !bp->bp_agree) ||
1065 (bp->bp_proposed && bp->bp_agree)) {
1066 bp->bp_proposed = 0;
1067 bp->bp_agree = 1;
1068 bp->bp_flags |= BSTP_PORT_NEWINFO;
1069 DPRINTF("%s -> ALTERNATE_AGREED\n",
1070 bp->bp_ifp->if_xname);
1071 }
1072
1073 if (bp->bp_proposed && !bp->bp_agree) {
1074 bstp_set_all_sync(bs);
1075 bp->bp_proposed = 0;
1076 DPRINTF("%s -> ALTERNATE_PROPOSED\n",
1077 bp->bp_ifp->if_xname);
1078 }
1079
1080 /* Clear any flags if set */
1081 if (bp->bp_sync || !bp->bp_synced || bp->bp_reroot) {
1082 bp->bp_sync = 0;
1083 bp->bp_synced = 1;
1084 bp->bp_reroot = 0;
1085 DPRINTF("%s -> ALTERNATE_PORT\n", bp->bp_ifp->if_xname);
1086 }
1087 break;
1088
1089 case BSTP_ROLE_ROOT:
1090 if (bp->bp_state != BSTP_IFSTATE_FORWARDING && !bp->bp_reroot) {
1091 bstp_set_all_reroot(bs);
1092 DPRINTF("%s -> ROOT_REROOT\n", bp->bp_ifp->if_xname);
1093 }
1094
1095 if ((bs->bs_allsynced && !bp->bp_agree) ||
1096 (bp->bp_proposed && bp->bp_agree)) {
1097 bp->bp_proposed = 0;
1098 bp->bp_sync = 0;
1099 bp->bp_agree = 1;
1100 bp->bp_flags |= BSTP_PORT_NEWINFO;
1101 DPRINTF("%s -> ROOT_AGREED\n", bp->bp_ifp->if_xname);
1102 }
1103
1104 if (bp->bp_proposed && !bp->bp_agree) {
1105 bstp_set_all_sync(bs);
1106 bp->bp_proposed = 0;
1107 DPRINTF("%s -> ROOT_PROPOSED\n", bp->bp_ifp->if_xname);
1108 }
1109
1110 if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1111 (bp->bp_forward_delay_timer.active == 0 ||
1112 (bstp_rerooted(bs, bp) &&
1113 bp->bp_recent_backup_timer.active == 0 &&
1114 bp->bp_protover == BSTP_PROTO_RSTP))) {
1115 switch (bp->bp_state) {
1116 case BSTP_IFSTATE_DISCARDING:
1117 bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
1118 break;
1119 case BSTP_IFSTATE_LEARNING:
1120 bstp_set_port_state(bp,
1121 BSTP_IFSTATE_FORWARDING);
1122 break;
1123 }
1124 }
1125
1126 if (bp->bp_state == BSTP_IFSTATE_FORWARDING && bp->bp_reroot) {
1127 bp->bp_reroot = 0;
1128 DPRINTF("%s -> ROOT_REROOTED\n", bp->bp_ifp->if_xname);
1129 }
1130 break;
1131
1132 case BSTP_ROLE_DESIGNATED:
1133 if (bp->bp_recent_root_timer.active == 0 && bp->bp_reroot) {
1134 bp->bp_reroot = 0;
1135 DPRINTF("%s -> DESIGNATED_RETIRED\n",
1136 bp->bp_ifp->if_xname);
1137 }
1138
1139 if ((bp->bp_state == BSTP_IFSTATE_DISCARDING &&
1140 !bp->bp_synced) || (bp->bp_agreed && !bp->bp_synced) ||
1141 (bp->bp_operedge && !bp->bp_synced) ||
1142 (bp->bp_sync && bp->bp_synced)) {
1143 bstp_timer_stop(&bp->bp_recent_root_timer);
1144 bp->bp_synced = 1;
1145 bp->bp_sync = 0;
1146 DPRINTF("%s -> DESIGNATED_SYNCED\n",
1147 bp->bp_ifp->if_xname);
1148 }
1149
1150 if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1151 !bp->bp_agreed && !bp->bp_proposing &&
1152 !bp->bp_operedge) {
1153 bp->bp_proposing = 1;
1154 bp->bp_flags |= BSTP_PORT_NEWINFO;
1155 bstp_timer_start(&bp->bp_edge_delay_timer,
1156 (bp->bp_ptp_link ? BSTP_DEFAULT_MIGRATE_DELAY :
0a7de745 1157 bp->bp_desg_max_age));
6d2010ae
A
1158 DPRINTF("%s -> DESIGNATED_PROPOSE\n",
1159 bp->bp_ifp->if_xname);
1160 }
1161
1162 if (bp->bp_state != BSTP_IFSTATE_FORWARDING &&
1163 (bp->bp_forward_delay_timer.active == 0 || bp->bp_agreed ||
1164 bp->bp_operedge) &&
1165 (bp->bp_recent_root_timer.active == 0 || !bp->bp_reroot) &&
1166 !bp->bp_sync) {
1167#ifdef BRIDGESTP_DEBUG
0a7de745 1168 if (bp->bp_agreed) {
6d2010ae 1169 DPRINTF("%s -> AGREED\n", bp->bp_ifp->if_xname);
0a7de745 1170 }
6d2010ae
A
1171#endif /* BRIDGESTP_DEBUG */
1172 /*
1173 * If agreed|operedge then go straight to forwarding,
1174 * otherwise follow discard -> learn -> forward.
1175 */
1176 if (bp->bp_agreed || bp->bp_operedge ||
1177 bp->bp_state == BSTP_IFSTATE_LEARNING) {
1178 bstp_set_port_state(bp,
1179 BSTP_IFSTATE_FORWARDING);
1180 bp->bp_agreed = bp->bp_protover;
0a7de745 1181 } else if (bp->bp_state == BSTP_IFSTATE_DISCARDING) {
6d2010ae 1182 bstp_set_port_state(bp, BSTP_IFSTATE_LEARNING);
0a7de745 1183 }
6d2010ae
A
1184 }
1185
1186 if (((bp->bp_sync && !bp->bp_synced) ||
1187 (bp->bp_reroot && bp->bp_recent_root_timer.active) ||
1188 (bp->bp_flags & BSTP_PORT_DISPUTED)) && !bp->bp_operedge &&
1189 bp->bp_state != BSTP_IFSTATE_DISCARDING) {
1190 bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1191 bp->bp_flags &= ~BSTP_PORT_DISPUTED;
1192 bstp_timer_start(&bp->bp_forward_delay_timer,
1193 bp->bp_protover == BSTP_PROTO_RSTP ?
1194 bp->bp_desg_htime : bp->bp_desg_fdelay);
1195 DPRINTF("%s -> DESIGNATED_DISCARD\n",
1196 bp->bp_ifp->if_xname);
1197 }
1198 break;
1199 }
1200
0a7de745 1201 if (bp->bp_flags & BSTP_PORT_NEWINFO) {
6d2010ae 1202 bstp_transmit(bs, bp);
0a7de745 1203 }
6d2010ae
A
1204}
1205
1206static void
1207bstp_update_tc(struct bstp_port *bp)
1208{
1209 switch (bp->bp_tcstate) {
0a7de745
A
1210 case BSTP_TCSTATE_ACTIVE:
1211 if ((bp->bp_role != BSTP_ROLE_DESIGNATED &&
1212 bp->bp_role != BSTP_ROLE_ROOT) || bp->bp_operedge) {
1213 bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
1214 }
6d2010ae 1215
0a7de745
A
1216 if (bp->bp_rcvdtcn) {
1217 bstp_set_port_tc(bp, BSTP_TCSTATE_TCN);
1218 }
1219 if (bp->bp_rcvdtc) {
1220 bstp_set_port_tc(bp, BSTP_TCSTATE_TC);
1221 }
6d2010ae 1222
0a7de745
A
1223 if (bp->bp_tc_prop && !bp->bp_operedge) {
1224 bstp_set_port_tc(bp, BSTP_TCSTATE_PROPAG);
1225 }
6d2010ae 1226
0a7de745
A
1227 if (bp->bp_rcvdtca) {
1228 bstp_set_port_tc(bp, BSTP_TCSTATE_ACK);
1229 }
1230 break;
6d2010ae 1231
0a7de745
A
1232 case BSTP_TCSTATE_INACTIVE:
1233 if ((bp->bp_state == BSTP_IFSTATE_LEARNING ||
1234 bp->bp_state == BSTP_IFSTATE_FORWARDING) &&
1235 bp->bp_fdbflush == 0) {
1236 bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
1237 }
1238 break;
6d2010ae 1239
0a7de745
A
1240 case BSTP_TCSTATE_LEARNING:
1241 if (bp->bp_rcvdtc || bp->bp_rcvdtcn || bp->bp_rcvdtca ||
1242 bp->bp_tc_prop) {
1243 bstp_set_port_tc(bp, BSTP_TCSTATE_LEARNING);
1244 } else if (bp->bp_role != BSTP_ROLE_DESIGNATED &&
1245 bp->bp_role != BSTP_ROLE_ROOT &&
1246 bp->bp_state == BSTP_IFSTATE_DISCARDING) {
1247 bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
1248 }
6d2010ae 1249
0a7de745
A
1250 if ((bp->bp_role == BSTP_ROLE_DESIGNATED ||
1251 bp->bp_role == BSTP_ROLE_ROOT) &&
1252 bp->bp_state == BSTP_IFSTATE_FORWARDING &&
1253 !bp->bp_operedge) {
1254 bstp_set_port_tc(bp, BSTP_TCSTATE_DETECTED);
1255 }
1256 break;
6d2010ae 1257
0a7de745
A
1258 /* these are transient states and go straight back to ACTIVE */
1259 case BSTP_TCSTATE_DETECTED:
1260 case BSTP_TCSTATE_TCN:
1261 case BSTP_TCSTATE_TC:
1262 case BSTP_TCSTATE_PROPAG:
1263 case BSTP_TCSTATE_ACK:
1264 DPRINTF("Invalid TC state for %s\n",
1265 bp->bp_ifp->if_xname);
1266 break;
1267 }
6d2010ae
A
1268}
1269
1270static void
1271bstp_update_info(struct bstp_port *bp)
1272{
1273 struct bstp_state *bs = bp->bp_bs;
1274
1275 bp->bp_proposing = 0;
1276 bp->bp_proposed = 0;
1277
0a7de745 1278 if (bp->bp_agreed && !bstp_pdu_bettersame(bp, BSTP_INFO_MINE)) {
6d2010ae 1279 bp->bp_agreed = 0;
0a7de745 1280 }
6d2010ae
A
1281
1282 if (bp->bp_synced && !bp->bp_agreed) {
1283 bp->bp_synced = 0;
1284 bs->bs_allsynced = 0;
1285 }
1286
1287 /* copy the designated pv to the port */
1288 bp->bp_port_pv = bp->bp_desg_pv;
1289 bp->bp_port_msg_age = bp->bp_desg_msg_age;
1290 bp->bp_port_max_age = bp->bp_desg_max_age;
1291 bp->bp_port_fdelay = bp->bp_desg_fdelay;
1292 bp->bp_port_htime = bp->bp_desg_htime;
1293 bp->bp_infois = BSTP_INFO_MINE;
1294
1295 /* Set transmit flag but do not immediately send */
1296 bp->bp_flags |= BSTP_PORT_NEWINFO;
1297}
1298
1299/* set tcprop on every port other than the caller */
1300static void
1301bstp_set_other_tcprop(struct bstp_port *bp)
1302{
1303 struct bstp_state *bs = bp->bp_bs;
1304 struct bstp_port *bp2;
1305
1306 BSTP_LOCK_ASSERT(bs);
1307
1308 LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
0a7de745 1309 if (bp2 == bp) {
6d2010ae 1310 continue;
0a7de745 1311 }
6d2010ae
A
1312 bp2->bp_tc_prop = 1;
1313 }
1314}
1315
1316static void
1317bstp_set_all_reroot(struct bstp_state *bs)
1318{
1319 struct bstp_port *bp;
1320
1321 BSTP_LOCK_ASSERT(bs);
1322
1323 LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
0a7de745 1324 bp->bp_reroot = 1;
6d2010ae
A
1325}
1326
1327static void
1328bstp_set_all_sync(struct bstp_state *bs)
1329{
1330 struct bstp_port *bp;
1331
1332 BSTP_LOCK_ASSERT(bs);
1333
1334 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1335 bp->bp_sync = 1;
0a7de745 1336 bp->bp_synced = 0; /* Not explicit in spec */
6d2010ae
A
1337 }
1338
1339 bs->bs_allsynced = 0;
1340}
1341
1342static void
1343bstp_set_port_state(struct bstp_port *bp, int state)
1344{
0a7de745 1345 if (bp->bp_state == state) {
6d2010ae 1346 return;
0a7de745 1347 }
6d2010ae
A
1348
1349 bp->bp_state = state;
1350
1351 switch (bp->bp_state) {
0a7de745
A
1352 case BSTP_IFSTATE_DISCARDING:
1353 DPRINTF("state changed to DISCARDING on %s\n",
1354 bp->bp_ifp->if_xname);
1355 break;
6d2010ae 1356
0a7de745
A
1357 case BSTP_IFSTATE_LEARNING:
1358 DPRINTF("state changed to LEARNING on %s\n",
1359 bp->bp_ifp->if_xname);
6d2010ae 1360
0a7de745
A
1361 bstp_timer_start(&bp->bp_forward_delay_timer,
1362 bp->bp_protover == BSTP_PROTO_RSTP ?
1363 bp->bp_desg_htime : bp->bp_desg_fdelay);
1364 break;
6d2010ae 1365
0a7de745
A
1366 case BSTP_IFSTATE_FORWARDING:
1367 DPRINTF("state changed to FORWARDING on %s\n",
1368 bp->bp_ifp->if_xname);
6d2010ae 1369
0a7de745
A
1370 bstp_timer_stop(&bp->bp_forward_delay_timer);
1371 /* Record that we enabled forwarding */
1372 bp->bp_forward_transitions++;
1373 break;
6d2010ae
A
1374 }
1375
1376 /* notify the parent bridge */
1377 bstp_task_enqueue(&bp->bp_statetask);
1378}
1379
1380static void
1381bstp_set_port_role(struct bstp_port *bp, int role)
1382{
1383 struct bstp_state *bs = bp->bp_bs;
1384
0a7de745 1385 if (bp->bp_role == role) {
6d2010ae 1386 return;
0a7de745 1387 }
6d2010ae
A
1388
1389 /* perform pre-change tasks */
1390 switch (bp->bp_role) {
0a7de745
A
1391 case BSTP_ROLE_DISABLED:
1392 bstp_timer_start(&bp->bp_forward_delay_timer,
1393 bp->bp_desg_max_age);
1394 break;
6d2010ae 1395
0a7de745
A
1396 case BSTP_ROLE_BACKUP:
1397 bstp_timer_start(&bp->bp_recent_backup_timer,
1398 bp->bp_desg_htime * 2);
1399 /* fall through */
1400 case BSTP_ROLE_ALTERNATE:
1401 bstp_timer_start(&bp->bp_forward_delay_timer,
1402 bp->bp_desg_fdelay);
1403 bp->bp_sync = 0;
1404 bp->bp_synced = 1;
1405 bp->bp_reroot = 0;
1406 break;
6d2010ae 1407
0a7de745
A
1408 case BSTP_ROLE_ROOT:
1409 bstp_timer_start(&bp->bp_recent_root_timer,
1410 BSTP_DEFAULT_FORWARD_DELAY);
1411 break;
6d2010ae
A
1412 }
1413
1414 bp->bp_role = role;
1415 /* clear values not carried between roles */
1416 bp->bp_proposing = 0;
1417 bs->bs_allsynced = 0;
1418
1419 /* initialise the new role */
1420 switch (bp->bp_role) {
0a7de745
A
1421 case BSTP_ROLE_DISABLED:
1422 case BSTP_ROLE_ALTERNATE:
1423 case BSTP_ROLE_BACKUP:
1424 DPRINTF("%s role -> ALT/BACK/DISABLED\n",
1425 bp->bp_ifp->if_xname);
1426 bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1427 bstp_timer_stop(&bp->bp_recent_root_timer);
1428 bstp_timer_latch(&bp->bp_forward_delay_timer);
1429 bp->bp_sync = 0;
1430 bp->bp_synced = 1;
1431 bp->bp_reroot = 0;
1432 break;
6d2010ae 1433
0a7de745
A
1434 case BSTP_ROLE_ROOT:
1435 DPRINTF("%s role -> ROOT\n",
1436 bp->bp_ifp->if_xname);
1437 bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
1438 bstp_timer_latch(&bp->bp_recent_root_timer);
1439 bp->bp_proposing = 0;
1440 break;
6d2010ae 1441
0a7de745
A
1442 case BSTP_ROLE_DESIGNATED:
1443 DPRINTF("%s role -> DESIGNATED\n",
1444 bp->bp_ifp->if_xname);
1445 bstp_timer_start(&bp->bp_hello_timer,
1446 bp->bp_desg_htime);
1447 bp->bp_agree = 0;
1448 break;
6d2010ae
A
1449 }
1450
1451 /* let the TC state know that the role changed */
1452 bstp_update_tc(bp);
1453}
1454
1455static void
1456bstp_set_port_proto(struct bstp_port *bp, int proto)
1457{
1458 struct bstp_state *bs = bp->bp_bs;
1459
1460 /* supported protocol versions */
1461 switch (proto) {
0a7de745
A
1462 case BSTP_PROTO_STP:
1463 /* we can downgrade protocols only */
1464 bstp_timer_stop(&bp->bp_migrate_delay_timer);
1465 /* clear unsupported features */
1466 bp->bp_operedge = 0;
1467 /* STP compat mode only uses 16 bits of the 32 */
1468 if (bp->bp_path_cost > 65535) {
1469 bp->bp_path_cost = 65535;
1470 }
1471 break;
6d2010ae 1472
0a7de745
A
1473 case BSTP_PROTO_RSTP:
1474 bstp_timer_start(&bp->bp_migrate_delay_timer,
1475 bs->bs_migration_delay);
1476 break;
6d2010ae 1477
0a7de745
A
1478 default:
1479 DPRINTF("Unsupported STP version %d\n", proto);
1480 return;
6d2010ae
A
1481 }
1482
1483 bp->bp_protover = proto;
1484 bp->bp_flags &= ~BSTP_PORT_CANMIGRATE;
1485}
1486
1487static void
1488bstp_set_port_tc(struct bstp_port *bp, int state)
1489{
1490 struct bstp_state *bs = bp->bp_bs;
1491
1492 bp->bp_tcstate = state;
1493
1494 /* initialise the new state */
1495 switch (bp->bp_tcstate) {
0a7de745
A
1496 case BSTP_TCSTATE_ACTIVE:
1497 DPRINTF("%s -> TC_ACTIVE\n", bp->bp_ifp->if_xname);
1498 /* nothing to do */
1499 break;
6d2010ae 1500
0a7de745
A
1501 case BSTP_TCSTATE_INACTIVE:
1502 bstp_timer_stop(&bp->bp_tc_timer);
1503 /* flush routes on the parent bridge */
1504 bp->bp_fdbflush = 1;
1505 bstp_task_enqueue(&bp->bp_rtagetask);
1506 bp->bp_tc_ack = 0;
1507 DPRINTF("%s -> TC_INACTIVE\n", bp->bp_ifp->if_xname);
1508 break;
6d2010ae 1509
0a7de745
A
1510 case BSTP_TCSTATE_LEARNING:
1511 bp->bp_rcvdtc = 0;
1512 bp->bp_rcvdtcn = 0;
1513 bp->bp_rcvdtca = 0;
1514 bp->bp_tc_prop = 0;
1515 DPRINTF("%s -> TC_LEARNING\n", bp->bp_ifp->if_xname);
1516 break;
6d2010ae 1517
0a7de745
A
1518 case BSTP_TCSTATE_DETECTED:
1519 bstp_set_timer_tc(bp);
1520 bstp_set_other_tcprop(bp);
1521 /* send out notification */
1522 bp->bp_flags |= BSTP_PORT_NEWINFO;
1523 bstp_transmit(bs, bp);
1524 /* reviewed for getmicrotime usage */
1525 getmicrotime(&bs->bs_last_tc_time);
1526 DPRINTF("%s -> TC_DETECTED\n", bp->bp_ifp->if_xname);
1527 bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1528 break;
6d2010ae 1529
0a7de745
A
1530 case BSTP_TCSTATE_TCN:
1531 bstp_set_timer_tc(bp);
1532 DPRINTF("%s -> TC_TCN\n", bp->bp_ifp->if_xname);
1533 /* fall through */
1534 case BSTP_TCSTATE_TC:
1535 bp->bp_rcvdtc = 0;
1536 bp->bp_rcvdtcn = 0;
1537 if (bp->bp_role == BSTP_ROLE_DESIGNATED) {
1538 bp->bp_tc_ack = 1;
1539 }
6d2010ae 1540
0a7de745
A
1541 bstp_set_other_tcprop(bp);
1542 DPRINTF("%s -> TC_TC\n", bp->bp_ifp->if_xname);
1543 bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1544 break;
6d2010ae 1545
0a7de745
A
1546 case BSTP_TCSTATE_PROPAG:
1547 /* flush routes on the parent bridge */
1548 bp->bp_fdbflush = 1;
1549 bstp_task_enqueue(&bp->bp_rtagetask);
1550 bp->bp_tc_prop = 0;
1551 bstp_set_timer_tc(bp);
1552 DPRINTF("%s -> TC_PROPAG\n", bp->bp_ifp->if_xname);
1553 bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1554 break;
1555
1556 case BSTP_TCSTATE_ACK:
1557 bstp_timer_stop(&bp->bp_tc_timer);
1558 bp->bp_rcvdtca = 0;
1559 DPRINTF("%s -> TC_ACK\n", bp->bp_ifp->if_xname);
1560 bp->bp_tcstate = BSTP_TCSTATE_ACTIVE; /* UCT */
1561 break;
6d2010ae
A
1562 }
1563}
1564
1565static void
1566bstp_set_timer_tc(struct bstp_port *bp)
1567{
1568 struct bstp_state *bs = bp->bp_bs;
1569
0a7de745 1570 if (bp->bp_tc_timer.active) {
6d2010ae 1571 return;
0a7de745 1572 }
6d2010ae
A
1573
1574 switch (bp->bp_protover) {
0a7de745
A
1575 case BSTP_PROTO_RSTP:
1576 bstp_timer_start(&bp->bp_tc_timer,
1577 bp->bp_desg_htime + BSTP_TICK_VAL);
1578 bp->bp_flags |= BSTP_PORT_NEWINFO;
1579 break;
6d2010ae 1580
0a7de745
A
1581 case BSTP_PROTO_STP:
1582 bstp_timer_start(&bp->bp_tc_timer,
1583 bs->bs_root_max_age + bs->bs_root_fdelay);
1584 break;
6d2010ae
A
1585 }
1586}
1587
1588static void
1589bstp_set_timer_msgage(struct bstp_port *bp)
1590{
1591 if (bp->bp_port_msg_age + BSTP_MESSAGE_AGE_INCR <=
1592 bp->bp_port_max_age) {
1593 bstp_timer_start(&bp->bp_message_age_timer,
1594 bp->bp_port_htime * 3);
0a7de745 1595 } else {
6d2010ae
A
1596 /* expires immediately */
1597 bstp_timer_start(&bp->bp_message_age_timer, 0);
0a7de745 1598 }
6d2010ae
A
1599}
1600
1601static int
1602bstp_rerooted(struct bstp_state *bs, struct bstp_port *bp)
1603{
1604 struct bstp_port *bp2;
1605 int rr_set = 0;
1606
1607 LIST_FOREACH(bp2, &bs->bs_bplist, bp_next) {
0a7de745 1608 if (bp2 == bp) {
6d2010ae 1609 continue;
0a7de745 1610 }
6d2010ae
A
1611 if (bp2->bp_recent_root_timer.active) {
1612 rr_set = 1;
1613 break;
1614 }
1615 }
0a7de745 1616 return !rr_set;
6d2010ae
A
1617}
1618
1619int
1620bstp_set_htime(struct bstp_state *bs, int t)
1621{
1622 /* convert seconds to ticks */
1623 t *= BSTP_TICK_VAL;
1624
1625 /* value can only be changed in leagacy stp mode */
0a7de745
A
1626 if (bs->bs_protover != BSTP_PROTO_STP) {
1627 return EPERM;
1628 }
6d2010ae 1629
0a7de745
A
1630 if (t < BSTP_MIN_HELLO_TIME || t > BSTP_MAX_HELLO_TIME) {
1631 return EINVAL;
1632 }
6d2010ae
A
1633
1634 BSTP_LOCK(bs);
1635 bs->bs_bridge_htime = t;
1636 bstp_reinit(bs);
1637 BSTP_UNLOCK(bs);
0a7de745 1638 return 0;
6d2010ae
A
1639}
1640
1641int
1642bstp_set_fdelay(struct bstp_state *bs, int t)
1643{
1644 /* convert seconds to ticks */
1645 t *= BSTP_TICK_VAL;
1646
0a7de745
A
1647 if (t < BSTP_MIN_FORWARD_DELAY || t > BSTP_MAX_FORWARD_DELAY) {
1648 return EINVAL;
1649 }
6d2010ae
A
1650
1651 BSTP_LOCK(bs);
1652 bs->bs_bridge_fdelay = t;
1653 bstp_reinit(bs);
1654 BSTP_UNLOCK(bs);
0a7de745 1655 return 0;
6d2010ae
A
1656}
1657
1658int
1659bstp_set_maxage(struct bstp_state *bs, int t)
1660{
1661 /* convert seconds to ticks */
1662 t *= BSTP_TICK_VAL;
1663
0a7de745
A
1664 if (t < BSTP_MIN_MAX_AGE || t > BSTP_MAX_MAX_AGE) {
1665 return EINVAL;
1666 }
6d2010ae
A
1667
1668 BSTP_LOCK(bs);
1669 bs->bs_bridge_max_age = t;
1670 bstp_reinit(bs);
1671 BSTP_UNLOCK(bs);
0a7de745 1672 return 0;
6d2010ae
A
1673}
1674
1675int
1676bstp_set_holdcount(struct bstp_state *bs, int count)
1677{
1678 struct bstp_port *bp;
1679
1680 if (count < BSTP_MIN_HOLD_COUNT ||
0a7de745
A
1681 count > BSTP_MAX_HOLD_COUNT) {
1682 return EINVAL;
1683 }
6d2010ae
A
1684
1685 BSTP_LOCK(bs);
1686 bs->bs_txholdcount = count;
1687 LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
0a7de745 1688 bp->bp_txcount = 0;
6d2010ae 1689 BSTP_UNLOCK(bs);
0a7de745 1690 return 0;
6d2010ae
A
1691}
1692
1693int
1694bstp_set_protocol(struct bstp_state *bs, int proto)
1695{
1696 struct bstp_port *bp;
1697
1698 switch (proto) {
0a7de745
A
1699 /* Supported protocol versions */
1700 case BSTP_PROTO_STP:
1701 case BSTP_PROTO_RSTP:
1702 break;
6d2010ae 1703
0a7de745
A
1704 default:
1705 return EINVAL;
6d2010ae
A
1706 }
1707
1708 BSTP_LOCK(bs);
1709 bs->bs_protover = proto;
1710 bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
1711 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1712 /* reinit state */
1713 bp->bp_infois = BSTP_INFO_DISABLED;
1714 bp->bp_txcount = 0;
1715 bstp_set_port_proto(bp, bs->bs_protover);
1716 bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
1717 bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
1718 bstp_timer_stop(&bp->bp_recent_backup_timer);
1719 }
1720 bstp_reinit(bs);
1721 BSTP_UNLOCK(bs);
0a7de745 1722 return 0;
6d2010ae
A
1723}
1724
1725int
1726bstp_set_priority(struct bstp_state *bs, int pri)
1727{
0a7de745
A
1728 if (pri < 0 || pri > BSTP_MAX_PRIORITY) {
1729 return EINVAL;
1730 }
6d2010ae
A
1731
1732 /* Limit to steps of 4096 */
1733 pri -= pri % 4096;
1734
1735 BSTP_LOCK(bs);
1736 bs->bs_bridge_priority = pri;
1737 bstp_reinit(bs);
1738 BSTP_UNLOCK(bs);
0a7de745 1739 return 0;
6d2010ae
A
1740}
1741
1742int
1743bstp_set_port_priority(struct bstp_port *bp, int pri)
1744{
1745 struct bstp_state *bs = bp->bp_bs;
1746
0a7de745
A
1747 if (pri < 0 || pri > BSTP_MAX_PORT_PRIORITY) {
1748 return EINVAL;
1749 }
6d2010ae
A
1750
1751 /* Limit to steps of 16 */
1752 pri -= pri % 16;
1753
1754 BSTP_LOCK(bs);
1755 bp->bp_priority = pri;
1756 bstp_reinit(bs);
1757 BSTP_UNLOCK(bs);
0a7de745 1758 return 0;
6d2010ae
A
1759}
1760
1761int
1762bstp_set_path_cost(struct bstp_port *bp, uint32_t path_cost)
1763{
1764 struct bstp_state *bs = bp->bp_bs;
1765
0a7de745
A
1766 if (path_cost > BSTP_MAX_PATH_COST) {
1767 return EINVAL;
1768 }
6d2010ae
A
1769
1770 /* STP compat mode only uses 16 bits of the 32 */
0a7de745 1771 if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535) {
6d2010ae 1772 path_cost = 65535;
0a7de745 1773 }
6d2010ae
A
1774
1775 BSTP_LOCK(bs);
1776
0a7de745 1777 if (path_cost == 0) { /* use auto */
6d2010ae
A
1778 bp->bp_flags &= ~BSTP_PORT_ADMCOST;
1779 bp->bp_path_cost = bstp_calc_path_cost(bp);
1780 } else {
1781 bp->bp_path_cost = path_cost;
1782 bp->bp_flags |= BSTP_PORT_ADMCOST;
1783 }
1784 bstp_reinit(bs);
1785 BSTP_UNLOCK(bs);
0a7de745 1786 return 0;
6d2010ae
A
1787}
1788
1789int
1790bstp_set_edge(struct bstp_port *bp, int set)
1791{
1792 struct bstp_state *bs = bp->bp_bs;
1793
1794 BSTP_LOCK(bs);
0a7de745 1795 if ((bp->bp_operedge = set) == 0) {
6d2010ae 1796 bp->bp_flags &= ~BSTP_PORT_ADMEDGE;
0a7de745 1797 } else {
6d2010ae 1798 bp->bp_flags |= BSTP_PORT_ADMEDGE;
0a7de745 1799 }
6d2010ae 1800 BSTP_UNLOCK(bs);
0a7de745 1801 return 0;
6d2010ae
A
1802}
1803
1804int
1805bstp_set_autoedge(struct bstp_port *bp, int set)
1806{
1807 struct bstp_state *bs = bp->bp_bs;
1808
1809 BSTP_LOCK(bs);
1810 if (set) {
1811 bp->bp_flags |= BSTP_PORT_AUTOEDGE;
1812 /* we may be able to transition straight to edge */
0a7de745 1813 if (bp->bp_edge_delay_timer.active == 0) {
6d2010ae 1814 bstp_edge_delay_expiry(bs, bp);
0a7de745
A
1815 }
1816 } else {
6d2010ae 1817 bp->bp_flags &= ~BSTP_PORT_AUTOEDGE;
0a7de745 1818 }
6d2010ae 1819 BSTP_UNLOCK(bs);
0a7de745 1820 return 0;
6d2010ae
A
1821}
1822
1823int
1824bstp_set_ptp(struct bstp_port *bp, int set)
1825{
1826 struct bstp_state *bs = bp->bp_bs;
1827
1828 BSTP_LOCK(bs);
1829 bp->bp_ptp_link = set;
1830 BSTP_UNLOCK(bs);
0a7de745 1831 return 0;
6d2010ae
A
1832}
1833
1834int
1835bstp_set_autoptp(struct bstp_port *bp, int set)
1836{
1837 struct bstp_state *bs = bp->bp_bs;
1838
1839 BSTP_LOCK(bs);
1840 if (set) {
1841 bp->bp_flags |= BSTP_PORT_AUTOPTP;
0a7de745 1842 if (bp->bp_role != BSTP_ROLE_DISABLED) {
6d2010ae 1843 bstp_ifupdstatus(bs, bp);
0a7de745
A
1844 }
1845 } else {
6d2010ae 1846 bp->bp_flags &= ~BSTP_PORT_AUTOPTP;
0a7de745 1847 }
6d2010ae 1848 BSTP_UNLOCK(bs);
0a7de745 1849 return 0;
6d2010ae
A
1850}
1851
1852/*
1853 * Calculate the path cost according to the link speed.
1854 */
1855static uint32_t
1856bstp_calc_path_cost(struct bstp_port *bp)
1857{
1858 struct ifnet *ifp = bp->bp_ifp;
1859 uint32_t path_cost;
1860
1861 /* If the priority has been manually set then retain the value */
0a7de745 1862 if (bp->bp_flags & BSTP_PORT_ADMCOST) {
6d2010ae 1863 return bp->bp_path_cost;
0a7de745 1864 }
6d2010ae
A
1865
1866 if (bp->bp_if_link_state == LINK_STATE_DOWN) {
1867 /* Recalc when the link comes up again */
1868 bp->bp_flags |= BSTP_PORT_PNDCOST;
0a7de745 1869 return BSTP_DEFAULT_PATH_COST;
6d2010ae
A
1870 }
1871
0a7de745
A
1872 if (ifp->if_baudrate < 1000) {
1873 return BSTP_DEFAULT_PATH_COST;
1874 }
6d2010ae 1875
0a7de745 1876 /* formula from section 17.14, IEEE Std 802.1D-2004 */
6d2010ae
A
1877 path_cost = 20000000000ULL / (ifp->if_baudrate / 1000);
1878
0a7de745 1879 if (path_cost > BSTP_MAX_PATH_COST) {
6d2010ae 1880 path_cost = BSTP_MAX_PATH_COST;
0a7de745 1881 }
6d2010ae
A
1882
1883 /* STP compat mode only uses 16 bits of the 32 */
0a7de745 1884 if (bp->bp_protover == BSTP_PROTO_STP && path_cost > 65535) {
6d2010ae 1885 path_cost = 65535;
0a7de745 1886 }
6d2010ae 1887
0a7de745 1888 return path_cost;
6d2010ae
A
1889}
1890
1891/*
1892 * Notify the bridge that a port state has changed, we need to do this from a
1893 * taskqueue to avoid a LOR.
1894 */
1895static void
1896bstp_notify_state(void *arg, __unused int pending)
1897{
1898 struct bstp_port *bp = (struct bstp_port *)arg;
1899 struct bstp_state *bs = bp->bp_bs;
1900
0a7de745 1901 if (bp->bp_active == 1 && bs->bs_state_cb != NULL) {
6d2010ae 1902 (*bs->bs_state_cb)(bp->bp_ifp, bp->bp_state);
0a7de745 1903 }
6d2010ae
A
1904}
1905
1906/*
1907 * Flush the routes on the bridge port, we need to do this from a
1908 * taskqueue to avoid a LOR.
1909 */
1910static void
1911bstp_notify_rtage(void *arg, __unused int pending)
1912{
1913 struct bstp_port *bp = (struct bstp_port *)arg;
1914 struct bstp_state *bs = bp->bp_bs;
1915 int age = 0;
1916
1917 BSTP_LOCK(bs);
1918 switch (bp->bp_protover) {
0a7de745
A
1919 case BSTP_PROTO_STP:
1920 /* convert to seconds */
1921 age = bp->bp_desg_fdelay / BSTP_TICK_VAL;
1922 break;
6d2010ae 1923
0a7de745
A
1924 case BSTP_PROTO_RSTP:
1925 age = 0;
1926 break;
6d2010ae
A
1927 }
1928 BSTP_UNLOCK(bs);
1929
0a7de745 1930 if (bp->bp_active == 1 && bs->bs_rtage_cb != NULL) {
6d2010ae 1931 (*bs->bs_rtage_cb)(bp->bp_ifp, age);
0a7de745 1932 }
6d2010ae
A
1933
1934 /* flush is complete */
1935 BSTP_LOCK(bs);
1936 bp->bp_fdbflush = 0;
1937 BSTP_UNLOCK(bs);
1938}
1939
1940void
1941bstp_linkstate(struct ifnet *ifp, __unused int state)
1942{
1943 struct bstp_state *bs;
1944 struct bstp_port *bp;
1945
1946 /* search for the stp port */
1947 lck_mtx_lock(bstp_list_mtx);
1948 LIST_FOREACH(bs, &bstp_list, bs_list) {
1949 BSTP_LOCK(bs);
1950 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
1951 if (bp->bp_ifp == ifp) {
1952 bstp_ifupdstatus(bs, bp);
1953 bstp_update_state(bs, bp);
1954 /* it only exists once so return */
1955 BSTP_UNLOCK(bs);
1956 lck_mtx_unlock(bstp_list_mtx);
1957 return;
1958 }
1959 }
1960 BSTP_UNLOCK(bs);
1961 }
1962 lck_mtx_unlock(bstp_list_mtx);
1963}
1964
1965static void
1966bstp_ifupdstatus(struct bstp_state *bs, struct bstp_port *bp)
1967{
1968 struct ifnet *ifp = bp->bp_ifp;
1969 struct ifmediareq ifmr;
1970 int error = 0;
1971
1972 BSTP_LOCK_ASSERT(bs);
1973
1974 bzero((char *)&ifmr, sizeof(ifmr));
1975 error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
1976
1977 if ((error == 0) && (ifp->if_flags & IFF_UP)) {
1978 if (ifmr.ifm_status & IFM_ACTIVE) {
1979 /* A full-duplex link is assumed to be point to point */
1980 if (bp->bp_flags & BSTP_PORT_AUTOPTP) {
1981 bp->bp_ptp_link =
1982 ifmr.ifm_active & IFM_FDX ? 1 : 0;
1983 }
1984
1985 /* Calc the cost if the link was down previously */
1986 if (bp->bp_flags & BSTP_PORT_PNDCOST) {
1987 bp->bp_path_cost = bstp_calc_path_cost(bp);
1988 bp->bp_flags &= ~BSTP_PORT_PNDCOST;
1989 }
1990
0a7de745 1991 if (bp->bp_role == BSTP_ROLE_DISABLED) {
6d2010ae 1992 bstp_enable_port(bs, bp);
0a7de745 1993 }
6d2010ae
A
1994 } else {
1995 if (bp->bp_role != BSTP_ROLE_DISABLED) {
1996 bstp_disable_port(bs, bp);
1997 if ((bp->bp_flags & BSTP_PORT_ADMEDGE) &&
0a7de745 1998 bp->bp_protover == BSTP_PROTO_RSTP) {
6d2010ae 1999 bp->bp_operedge = 1;
0a7de745 2000 }
6d2010ae
A
2001 }
2002 }
2003 return;
2004 }
2005
0a7de745 2006 if (bp->bp_infois != BSTP_INFO_DISABLED) {
6d2010ae 2007 bstp_disable_port(bs, bp);
0a7de745 2008 }
6d2010ae
A
2009}
2010
2011static void
2012bstp_enable_port(struct bstp_state *bs, struct bstp_port *bp)
2013{
2014 bp->bp_infois = BSTP_INFO_AGED;
2015 bstp_assign_roles(bs);
2016}
2017
2018static void
2019bstp_disable_port(struct bstp_state *bs, struct bstp_port *bp)
2020{
2021 bp->bp_infois = BSTP_INFO_DISABLED;
2022 bstp_assign_roles(bs);
2023}
2024
2025static void
2026bstp_tick(void *arg)
2027{
2028 struct bstp_state *bs = arg;
2029 struct bstp_port *bp;
2030 struct timespec ts;
2031
2032 BSTP_LOCK(bs);
2033
0a7de745 2034 if (bs->bs_running == 0) {
6d2010ae 2035 return;
0a7de745 2036 }
6d2010ae
A
2037
2038 /* slow timer to catch missed link events */
2039 if (bstp_timer_expired(&bs->bs_link_timer)) {
2040 LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
0a7de745 2041 bstp_ifupdstatus(bs, bp);
6d2010ae
A
2042 bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
2043 }
2044
2045 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
2046 /* no events need to happen for these */
2047 bstp_timer_expired(&bp->bp_tc_timer);
2048 bstp_timer_expired(&bp->bp_recent_root_timer);
2049 bstp_timer_expired(&bp->bp_forward_delay_timer);
2050 bstp_timer_expired(&bp->bp_recent_backup_timer);
2051
0a7de745 2052 if (bstp_timer_expired(&bp->bp_hello_timer)) {
6d2010ae 2053 bstp_hello_timer_expiry(bs, bp);
0a7de745 2054 }
6d2010ae 2055
0a7de745 2056 if (bstp_timer_expired(&bp->bp_message_age_timer)) {
6d2010ae 2057 bstp_message_age_expiry(bs, bp);
0a7de745 2058 }
6d2010ae 2059
0a7de745 2060 if (bstp_timer_expired(&bp->bp_migrate_delay_timer)) {
6d2010ae 2061 bstp_migrate_delay_expiry(bs, bp);
0a7de745 2062 }
6d2010ae 2063
0a7de745 2064 if (bstp_timer_expired(&bp->bp_edge_delay_timer)) {
6d2010ae 2065 bstp_edge_delay_expiry(bs, bp);
0a7de745 2066 }
6d2010ae
A
2067
2068 /* update the various state machines for the port */
2069 bstp_update_state(bs, bp);
2070
0a7de745 2071 if (bp->bp_txcount > 0) {
6d2010ae 2072 bp->bp_txcount--;
0a7de745 2073 }
6d2010ae
A
2074 }
2075
2076 BSTP_UNLOCK(bs);
2077
2078 ts.tv_sec = 1;
2079 ts.tv_nsec = 0;
2080 bsd_timeout(bstp_tick, bs, &ts);
2081}
2082
2083static void
2084bstp_timer_start(struct bstp_timer *t, uint16_t v)
2085{
2086 t->value = v;
2087 t->active = 1;
2088 t->latched = 0;
2089}
2090
2091static void
2092bstp_timer_stop(struct bstp_timer *t)
2093{
2094 t->value = 0;
2095 t->active = 0;
2096 t->latched = 0;
2097}
2098
2099static void
2100bstp_timer_latch(struct bstp_timer *t)
2101{
2102 t->latched = 1;
2103 t->active = 1;
2104}
2105
2106static int
2107bstp_timer_expired(struct bstp_timer *t)
2108{
0a7de745
A
2109 if (t->active == 0 || t->latched) {
2110 return 0;
2111 }
6d2010ae
A
2112 t->value -= BSTP_TICK_VAL;
2113 if (t->value <= 0) {
2114 bstp_timer_stop(t);
0a7de745 2115 return 1;
6d2010ae 2116 }
0a7de745 2117 return 0;
6d2010ae
A
2118}
2119
2120static void
2121bstp_hello_timer_expiry(struct bstp_state *bs, struct bstp_port *bp)
2122{
2123 if ((bp->bp_flags & BSTP_PORT_NEWINFO) ||
2124 bp->bp_role == BSTP_ROLE_DESIGNATED ||
2125 (bp->bp_role == BSTP_ROLE_ROOT &&
0a7de745 2126 bp->bp_tc_timer.active == 1)) {
6d2010ae
A
2127 bstp_timer_start(&bp->bp_hello_timer, bp->bp_desg_htime);
2128 bp->bp_flags |= BSTP_PORT_NEWINFO;
2129 bstp_transmit(bs, bp);
2130 }
2131}
2132
2133static void
2134bstp_message_age_expiry(struct bstp_state *bs, struct bstp_port *bp)
2135{
2136 if (bp->bp_infois == BSTP_INFO_RECEIVED) {
2137 bp->bp_infois = BSTP_INFO_AGED;
2138 bstp_assign_roles(bs);
2139 DPRINTF("aged info on %s\n", bp->bp_ifp->if_xname);
2140 }
2141}
2142
2143static void
2144bstp_migrate_delay_expiry(__unused struct bstp_state *bs, struct bstp_port *bp)
2145{
2146 bp->bp_flags |= BSTP_PORT_CANMIGRATE;
2147}
2148
2149static void
2150bstp_edge_delay_expiry(__unused struct bstp_state *bs, struct bstp_port *bp)
2151{
2152 if ((bp->bp_flags & BSTP_PORT_AUTOEDGE) &&
2153 bp->bp_protover == BSTP_PROTO_RSTP && bp->bp_proposing &&
2154 bp->bp_role == BSTP_ROLE_DESIGNATED) {
2155 bp->bp_operedge = 1;
2156 DPRINTF("%s -> edge port\n", bp->bp_ifp->if_xname);
2157 }
2158}
2159
2160static int
2161bstp_addr_cmp(const uint8_t *a, const uint8_t *b)
2162{
2163 int i, d;
2164
2165 for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) {
2166 d = ((int)a[i]) - ((int)b[i]);
2167 }
2168
0a7de745 2169 return d;
6d2010ae
A
2170}
2171
2172/*
2173 * compare the bridge address component of the bridgeid
2174 */
2175static int
2176bstp_same_bridgeid(uint64_t id1, uint64_t id2)
2177{
2178 u_char addr1[ETHER_ADDR_LEN];
2179 u_char addr2[ETHER_ADDR_LEN];
2180
2181 PV2ADDR(id1, addr1);
2182 PV2ADDR(id2, addr2);
2183
0a7de745
A
2184 if (bstp_addr_cmp(addr1, addr2) == 0) {
2185 return 1;
2186 }
6d2010ae 2187
0a7de745 2188 return 0;
6d2010ae
A
2189}
2190
2191void
2192bstp_reinit(struct bstp_state *bs)
2193{
2194 struct bstp_port *bp;
2195 struct ifnet *ifp, *mif;
2196 u_char *e_addr;
0a7de745 2197 static const u_char llzero[ETHER_ADDR_LEN]; /* 00:00:00:00:00:00 */
6d2010ae
A
2198
2199 BSTP_LOCK_ASSERT(bs);
2200
2201 mif = NULL;
2202 /*
2203 * Search through the Ethernet adapters and find the one with the
2204 * lowest value. The adapter which we take the MAC address from does
2205 * not need to be part of the bridge, it just needs to be a unique
2206 * value.
2207 */
2208 ifnet_head_lock_shared();
2209 TAILQ_FOREACH(ifp, &ifnet_head, if_link) {
0a7de745 2210 if (ifp->if_type != IFT_ETHER) {
6d2010ae 2211 continue;
0a7de745 2212 }
6d2010ae 2213
0a7de745 2214 if (bstp_addr_cmp(IF_LLADDR(ifp), llzero) == 0) {
6d2010ae 2215 continue;
0a7de745 2216 }
6d2010ae
A
2217
2218 if (mif == NULL) {
2219 mif = ifp;
2220 continue;
2221 }
39236c6e 2222 if (bstp_addr_cmp(IF_LLADDR(ifp), IF_LLADDR(mif)) < 0) {
6d2010ae
A
2223 mif = ifp;
2224 continue;
2225 }
2226 }
2227 ifnet_head_done();
2228
2229 if (LIST_EMPTY(&bs->bs_bplist) || mif == NULL) {
2230 /* Set the bridge and root id (lower bits) to zero */
2231 bs->bs_bridge_pv.pv_dbridge_id =
2232 ((uint64_t)bs->bs_bridge_priority) << 48;
2233 bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
2234 bs->bs_root_pv = bs->bs_bridge_pv;
2235 /* Disable any remaining ports, they will have no MAC address */
2236 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
2237 bp->bp_infois = BSTP_INFO_DISABLED;
2238 bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
2239 }
2240 bsd_untimeout(bstp_tick, bs);
2241 return;
2242 }
2243
39236c6e 2244 e_addr = IF_LLADDR(mif);
6d2010ae
A
2245 bs->bs_bridge_pv.pv_dbridge_id =
2246 (((uint64_t)bs->bs_bridge_priority) << 48) |
2247 (((uint64_t)e_addr[0]) << 40) |
2248 (((uint64_t)e_addr[1]) << 32) |
2249 (((uint64_t)e_addr[2]) << 24) |
2250 (((uint64_t)e_addr[3]) << 16) |
2251 (((uint64_t)e_addr[4]) << 8) |
2252 (((uint64_t)e_addr[5]));
2253
2254 bs->bs_bridge_pv.pv_root_id = bs->bs_bridge_pv.pv_dbridge_id;
2255 bs->bs_bridge_pv.pv_cost = 0;
2256 bs->bs_bridge_pv.pv_dport_id = 0;
2257 bs->bs_bridge_pv.pv_port_id = 0;
2258
0a7de745 2259 if (bs->bs_running) {
6d2010ae 2260 bsd_untimeout(bstp_tick, bs);
0a7de745 2261 }
6d2010ae
A
2262
2263 LIST_FOREACH(bp, &bs->bs_bplist, bp_next) {
2264 bp->bp_port_id = (bp->bp_priority << 8) |
2265 (bp->bp_ifp->if_index & 0xfff);
2266 bstp_ifupdstatus(bs, bp);
2267 }
2268
2269 bstp_assign_roles(bs);
2270 bstp_timer_start(&bs->bs_link_timer, BSTP_LINK_TIMER);
2271}
2272
2273void
2274bstp_attach(struct bstp_state *bs, struct bstp_cb_ops *cb)
2275{
2276 BSTP_LOCK_INIT(bs);
2277 LIST_INIT(&bs->bs_bplist);
2278
2279 bs->bs_bridge_max_age = BSTP_DEFAULT_MAX_AGE;
2280 bs->bs_bridge_htime = BSTP_DEFAULT_HELLO_TIME;
2281 bs->bs_bridge_fdelay = BSTP_DEFAULT_FORWARD_DELAY;
2282 bs->bs_bridge_priority = BSTP_DEFAULT_BRIDGE_PRIORITY;
2283 bs->bs_hold_time = BSTP_DEFAULT_HOLD_TIME;
2284 bs->bs_migration_delay = BSTP_DEFAULT_MIGRATE_DELAY;
2285 bs->bs_txholdcount = BSTP_DEFAULT_HOLD_COUNT;
2286 bs->bs_protover = BSTP_PROTO_RSTP;
2287 bs->bs_state_cb = cb->bcb_state;
2288 bs->bs_rtage_cb = cb->bcb_rtage;
2289
2290 /* reviewed for getmicrotime usage */
2291 getmicrotime(&bs->bs_last_tc_time);
2292
2293 lck_mtx_lock(bstp_list_mtx);
2294 LIST_INSERT_HEAD(&bstp_list, bs, bs_list);
2295 lck_mtx_unlock(bstp_list_mtx);
2296}
2297
2298void
2299bstp_detach(struct bstp_state *bs)
2300{
2301 KASSERT(LIST_EMPTY(&bs->bs_bplist), ("bstp still active"));
2302
2303 lck_mtx_lock(bstp_list_mtx);
2304 LIST_REMOVE(bs, bs_list);
2305 lck_mtx_unlock(bstp_list_mtx);
2306 bsd_untimeout(bstp_tick, bs);
2307 BSTP_LOCK_DESTROY(bs);
2308}
2309
2310void
2311bstp_init(struct bstp_state *bs)
2312{
2313 struct timespec ts;
2314
2315 ts.tv_sec = 1;
2316 ts.tv_nsec = 0;
2317
2318 BSTP_LOCK(bs);
2319 bsd_timeout(bstp_tick, bs, &ts);
2320 bs->bs_running = 1;
2321 bstp_reinit(bs);
2322 BSTP_UNLOCK(bs);
2323}
2324
2325void
2326bstp_stop(struct bstp_state *bs)
2327{
2328 struct bstp_port *bp;
2329
2330 BSTP_LOCK(bs);
2331
2332 LIST_FOREACH(bp, &bs->bs_bplist, bp_next)
0a7de745 2333 bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
6d2010ae
A
2334
2335 bs->bs_running = 0;
2336 bsd_untimeout(bstp_tick, bs);
2337 BSTP_UNLOCK(bs);
2338}
2339
2340int
2341bstp_create(struct bstp_state *bs, struct bstp_port *bp, struct ifnet *ifp)
2342{
2343 bzero(bp, sizeof(struct bstp_port));
2344
2345 BSTP_LOCK(bs);
2346 bp->bp_ifp = ifp;
2347 bp->bp_bs = bs;
2348 bp->bp_priority = BSTP_DEFAULT_PORT_PRIORITY;
2349 BSTP_TASK_INIT(&bp->bp_statetask, bstp_notify_state, bp);
2350 BSTP_TASK_INIT(&bp->bp_rtagetask, bstp_notify_rtage, bp);
2351
2352 /* Init state */
2353 bp->bp_infois = BSTP_INFO_DISABLED;
0a7de745 2354 bp->bp_flags = BSTP_PORT_AUTOEDGE | BSTP_PORT_AUTOPTP;
6d2010ae
A
2355 bstp_set_port_state(bp, BSTP_IFSTATE_DISCARDING);
2356 bstp_set_port_proto(bp, bs->bs_protover);
2357 bstp_set_port_role(bp, BSTP_ROLE_DISABLED);
2358 bstp_set_port_tc(bp, BSTP_TCSTATE_INACTIVE);
2359 bp->bp_path_cost = bstp_calc_path_cost(bp);
2360 BSTP_UNLOCK(bs);
0a7de745 2361 return 0;
6d2010ae
A
2362}
2363
2364int
2365bstp_enable(struct bstp_port *bp)
2366{
2367 struct bstp_state *bs = bp->bp_bs;
2368 struct ifnet *ifp = bp->bp_ifp;
2369
2370 KASSERT(bp->bp_active == 0, ("already a bstp member"));
2371
2372 switch (ifp->if_type) {
0a7de745
A
2373 case IFT_ETHER: /* These can do spanning tree. */
2374 break;
2375 default:
2376 /* Nothing else can. */
2377 return EINVAL;
6d2010ae
A
2378 }
2379
2380 BSTP_LOCK(bs);
2381 LIST_INSERT_HEAD(&bs->bs_bplist, bp, bp_next);
2382 bp->bp_active = 1;
2383 bp->bp_flags |= BSTP_PORT_NEWINFO;
2384 bstp_reinit(bs);
2385 bstp_update_roles(bs, bp);
2386 BSTP_UNLOCK(bs);
0a7de745 2387 return 0;
6d2010ae
A
2388}
2389
2390void
2391bstp_disable(struct bstp_port *bp)
2392{
2393 struct bstp_state *bs = bp->bp_bs;
2394
2395 KASSERT(bp->bp_active == 1, ("not a bstp member"));
2396
2397 BSTP_LOCK(bs);
2398 bstp_disable_port(bs, bp);
2399 LIST_REMOVE(bp, bp_next);
2400 bp->bp_active = 0;
2401 bstp_reinit(bs);
2402 BSTP_UNLOCK(bs);
2403}
2404
2405/*
2406 * The bstp_port structure is about to be freed by the parent bridge.
2407 */
2408void
2409bstp_destroy(struct bstp_port *bp)
2410{
2411 KASSERT(bp->bp_active == 0, ("port is still attached"));
2412 bstp_task_drain(&bp->bp_statetask);
2413 bstp_task_drain(&bp->bp_rtagetask);
2414}
2415
2416
2417__private_extern__ void
2418bstp_sys_init(void)
2419{
2420 lck_grp_attr_t *lck_grp_attr = NULL;
2421
2422 lck_grp_attr = lck_grp_attr_alloc_init();
2423 bstp_lock_grp = lck_grp_alloc_init("bstp", lck_grp_attr);
2424 bstp_lock_attr = lck_attr_alloc_init();
2425#if BRIDGE_DEBUG
2426 lck_attr_setdebug(bstp_lock_attr);
2427#endif
316670eb 2428 lck_mtx_init(bstp_list_mtx, bstp_lock_grp, bstp_lock_attr);
6d2010ae
A
2429 lck_grp_attr_free(lck_grp_attr);
2430
2431 LIST_INIT(&bstp_list);
2432
2433 bstp_create_task_thread();
2434}
2435
2436
2437
2438static void
2439bstp_create_task_thread(void)
2440{
2441 kern_return_t error;
0a7de745 2442
6d2010ae
A
2443 lck_grp_attr_t *lck_grp_attr = NULL;
2444
2445 lck_grp_attr = lck_grp_attr_alloc_init();
2446 bstp_task_grp = lck_grp_alloc_init("bstp_task", lck_grp_attr);
2447 bstp_task_attr = lck_attr_alloc_init();
2448#if BRIDGE_DEBUG
2449 lck_attr_setdebug(bstp_task_attr);
2450#endif
316670eb 2451 lck_mtx_init(bstp_task_mtx, bstp_lock_grp, bstp_lock_attr);
6d2010ae
A
2452 lck_grp_attr_free(lck_grp_attr);
2453
2454 error = kernel_thread_start((thread_continue_t)bstp_task_thread_func, NULL, &bstp_task_thread);
2455}
2456
2457
2458static void
2459bstp_task_thread_func(void)
2460{
2461 struct bstp_task *bt, *tvar;
2462
2463 lck_mtx_lock(bstp_task_mtx);
0a7de745 2464
6d2010ae 2465 do {
0a7de745 2466 while (TAILQ_EMPTY(&bstp_task_queue)) {
6d2010ae
A
2467 wakeup(&bstp_task_queue_running);
2468 msleep(&bstp_task_queue, bstp_task_mtx, PZERO, "bstp_task_queue", NULL);
2469 }
0a7de745
A
2470
2471 TAILQ_FOREACH_SAFE(bt, &bstp_task_queue, bt_next, tvar) {
6d2010ae 2472 int count = bt->bt_count;
0a7de745 2473
6d2010ae 2474 bt->bt_count = 0;
0a7de745 2475
6d2010ae
A
2476 bstp_task_queue_running = bt;
2477 lck_mtx_unlock(bstp_task_mtx);
0a7de745 2478
6d2010ae 2479 (*bt->bt_func)(bt->bt_context, count);
0a7de745 2480
6d2010ae
A
2481 lck_mtx_lock(bstp_task_mtx);
2482 bstp_task_queue_running = NULL;
2483
0a7de745
A
2484 if (bt->bt_count == 0) {
2485 TAILQ_REMOVE(&bstp_task_queue, bt, bt_next);
2486 }
6d2010ae
A
2487 }
2488 } while (1);
0a7de745 2489
6d2010ae
A
2490 /* UNREACHED */
2491}
2492
2493static void
2494bstp_task_enqueue(struct bstp_task *bt)
2495{
2496 lck_mtx_lock(bstp_task_mtx);
2497
2498 if (bt->bt_count) {
2499 bt->bt_count++;
2500 lck_mtx_unlock(bstp_task_mtx);
2501 wakeup(&bstp_task_queue);
2502 return;
2503 }
0a7de745 2504
6d2010ae
A
2505 bt->bt_count = 1;
2506 TAILQ_INSERT_TAIL(&bstp_task_queue, bt, bt_next);
0a7de745 2507
6d2010ae 2508 lck_mtx_unlock(bstp_task_mtx);
0a7de745 2509
6d2010ae
A
2510 wakeup(&bstp_task_queue);
2511}
2512
2513static void
2514bstp_task_drain(struct bstp_task *bt)
2515{
2516 lck_mtx_lock(bstp_task_mtx);
2517
2518 while (bt->bt_count != 0 || bstp_task_queue_running == bt) {
2519 wakeup(&bstp_task_queue);
2520 msleep(&bstp_task_queue_running, bstp_task_mtx, PZERO, "bstp_task_queue", NULL);
2521 }
2522 lck_mtx_unlock(bstp_task_mtx);
2523}