| Commit | Line | Data |
|---|---|---|
| ac93838f SS |
1 | /* |
| 2 | * Copyright (c) 2000 Jason L. Wright (jason@thought.net) | |
| 3 | * All rights reserved. | |
| 4 | * | |
| 5 | * Redistribution and use in source and binary forms, with or without | |
| 6 | * modification, are permitted provided that the following conditions | |
| 7 | * are met: | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in the | |
| 12 | * documentation and/or other materials provided with the distribution. | |
| 13 | * 3. All advertising materials mentioning features or use of this software | |
| 14 | * must display the following acknowledgement: | |
| 15 | * This product includes software developed by Jason L. Wright | |
| 16 | * 4. The name of the author may not be used to endorse or promote products | |
| 17 | * derived from this software without specific prior written permission. | |
| 18 | * | |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 22 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |
| 23 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| 25 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
| 27 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
| 28 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| 29 | * POSSIBILITY OF SUCH DAMAGE. | |
| 30 | * | |
| 31 | * $OpenBSD: bridgestp.c,v 1.5 2001/03/22 03:48:29 jason Exp $ | |
| 32 | * $NetBSD: bridgestp.c,v 1.5 2003/11/28 08:56:48 keihan Exp $ | |
| 33 | * $FreeBSD: src/sys/net/bridgestp.c,v 1.7 2005/10/11 02:58:32 thompsa Exp $ | |
| ac93838f SS |
34 | */ |
| 35 | ||
| 36 | /* | |
| 37 | * Implementation of the spanning tree protocol as defined in | |
| 38 | * ISO/IEC Final DIS 15802-3 (IEEE P802.1D/D17), May 25, 1998. | |
| 39 | * (In English: IEEE 802.1D, Draft 17, 1998) | |
| 40 | */ | |
| 41 | ||
| ac93838f SS |
42 | #include <sys/param.h> |
| 43 | #include <sys/systm.h> | |
| 44 | #include <sys/mbuf.h> | |
| 45 | #include <sys/socket.h> | |
| 46 | #include <sys/sockio.h> | |
| 47 | #include <sys/kernel.h> | |
| 48 | #include <sys/callout.h> | |
| 49 | #include <sys/proc.h> | |
| 50 | #include <sys/lock.h> | |
| 51 | #include <sys/thread.h> | |
| 52 | #include <sys/thread2.h> | |
| e9d22060 | 53 | #include <sys/msgport2.h> |
| ac93838f SS |
54 | |
| 55 | #include <net/if.h> | |
| 56 | #include <net/if_dl.h> | |
| 57 | #include <net/if_types.h> | |
| 58 | #include <net/if_llc.h> | |
| 59 | #include <net/if_media.h> | |
| 60 | ||
| 61 | #include <netinet/in.h> | |
| 62 | #include <netinet/in_systm.h> | |
| 63 | #include <netinet/in_var.h> | |
| 64 | #include <netinet/if_ether.h> | |
| 65 | #include <net/bridge/if_bridgevar.h> | |
| 66 | ||
| 67 | /* BPDU message types */ | |
| 68 | #define BSTP_MSGTYPE_CFG 0x00 /* Configuration */ | |
| 69 | #define BSTP_MSGTYPE_TCN 0x80 /* Topology chg notification */ | |
| 70 | ||
| 71 | /* BPDU flags */ | |
| 72 | #define BSTP_FLAG_TC 0x01 /* Topology change */ | |
| 73 | #define BSTP_FLAG_TCA 0x80 /* Topology change ack */ | |
| 74 | ||
| 75 | #define BSTP_MESSAGE_AGE_INCR (1 * 256) /* in 256ths of a second */ | |
| 76 | #define BSTP_TICK_VAL (1 * 256) /* in 256ths of a second */ | |
| 77 | ||
| 78 | /* | |
| 79 | * Because BPDU's do not make nicely aligned structures, two different | |
| 80 | * declarations are used: bstp_?bpdu (wire representation, packed) and | |
| 81 | * bstp_*_unit (internal, nicely aligned version). | |
| 82 | */ | |
| 83 | ||
| 84 | /* configuration bridge protocol data unit */ | |
| 85 | struct bstp_cbpdu { | |
| 86 | uint8_t cbu_dsap; /* LLC: destination sap */ | |
| 87 | uint8_t cbu_ssap; /* LLC: source sap */ | |
| 88 | uint8_t cbu_ctl; /* LLC: control */ | |
| 89 | uint16_t cbu_protoid; /* protocol id */ | |
| 90 | uint8_t cbu_protover; /* protocol version */ | |
| 91 | uint8_t cbu_bpdutype; /* message type */ | |
| 92 | uint8_t cbu_flags; /* flags (below) */ | |
| 93 | ||
| 94 | /* root id */ | |
| 95 | uint16_t cbu_rootpri; /* root priority */ | |
| 96 | uint8_t cbu_rootaddr[6]; /* root address */ | |
| 97 | ||
| 98 | uint32_t cbu_rootpathcost; /* root path cost */ | |
| 99 | ||
| 100 | /* bridge id */ | |
| 101 | uint16_t cbu_bridgepri; /* bridge priority */ | |
| 102 | uint8_t cbu_bridgeaddr[6]; /* bridge address */ | |
| 103 | ||
| 104 | uint16_t cbu_portid; /* port id */ | |
| 105 | uint16_t cbu_messageage; /* current message age */ | |
| 106 | uint16_t cbu_maxage; /* maximum age */ | |
| 107 | uint16_t cbu_hellotime; /* hello time */ | |
| 108 | uint16_t cbu_forwarddelay; /* forwarding delay */ | |
| 109 | } __attribute__((__packed__)); | |
| 110 | ||
| 111 | /* topology change notification bridge protocol data unit */ | |
| 112 | struct bstp_tbpdu { | |
| 113 | uint8_t tbu_dsap; /* LLC: destination sap */ | |
| 114 | uint8_t tbu_ssap; /* LLC: source sap */ | |
| 115 | uint8_t tbu_ctl; /* LLC: control */ | |
| 116 | uint16_t tbu_protoid; /* protocol id */ | |
| 117 | uint8_t tbu_protover; /* protocol version */ | |
| 118 | uint8_t tbu_bpdutype; /* message type */ | |
| 119 | } __attribute__((__packed__)); | |
| 120 | ||
| 121 | const uint8_t bstp_etheraddr[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; | |
| 122 | ||
| d217f5e5 SU |
123 | static void bstp_initialize_port(struct bridge_softc *, |
| 124 | struct bridge_iflist *); | |
| 125 | static void bstp_ifupdstatus(struct bridge_softc *, struct bridge_iflist *); | |
| 126 | static void bstp_enable_port(struct bridge_softc *, struct bridge_iflist *); | |
| 127 | static void bstp_disable_port(struct bridge_softc *, | |
| 128 | struct bridge_iflist *); | |
| 129 | #ifdef notused | |
| 130 | static void bstp_enable_change_detection(struct bridge_iflist *); | |
| 131 | static void bstp_disable_change_detection(struct bridge_iflist *); | |
| 132 | #endif /* notused */ | |
| 133 | static int bstp_root_bridge(struct bridge_softc *sc); | |
| d217f5e5 SU |
134 | static void bstp_transmit_config(struct bridge_softc *, |
| 135 | struct bridge_iflist *); | |
| 136 | static void bstp_transmit_tcn(struct bridge_softc *); | |
| 137 | static void bstp_received_config_bpdu(struct bridge_softc *, | |
| 138 | struct bridge_iflist *, struct bstp_config_unit *); | |
| 139 | static void bstp_received_tcn_bpdu(struct bridge_softc *, | |
| 140 | struct bridge_iflist *, struct bstp_tcn_unit *); | |
| 141 | static void bstp_record_config_information(struct bridge_softc *, | |
| 142 | struct bridge_iflist *, struct bstp_config_unit *); | |
| 143 | static void bstp_record_config_timeout_values(struct bridge_softc *, | |
| 144 | struct bstp_config_unit *); | |
| 145 | static void bstp_config_bpdu_generation(struct bridge_softc *); | |
| 146 | static void bstp_send_config_bpdu(struct bridge_softc *, | |
| 147 | struct bridge_iflist *, struct bstp_config_unit *); | |
| 148 | static void bstp_configuration_update(struct bridge_softc *); | |
| d217f5e5 | 149 | static void bstp_port_state_selection(struct bridge_softc *); |
| 70d9a675 MD |
150 | static void bstp_clear_peer_info(struct bridge_softc *, |
| 151 | struct bridge_iflist *); | |
| d217f5e5 SU |
152 | static void bstp_make_forwarding(struct bridge_softc *, |
| 153 | struct bridge_iflist *); | |
| 154 | static void bstp_make_blocking(struct bridge_softc *, | |
| 155 | struct bridge_iflist *); | |
| 3677aae9 MD |
156 | static void bstp_make_l1blocking(struct bridge_softc *sc, |
| 157 | struct bridge_iflist *bif); | |
| d217f5e5 SU |
158 | static void bstp_set_port_state(struct bridge_iflist *, uint8_t); |
| 159 | #ifdef notused | |
| 160 | static void bstp_set_bridge_priority(struct bridge_softc *, uint64_t); | |
| 161 | static void bstp_set_port_priority(struct bridge_softc *, | |
| 162 | struct bridge_iflist *, uint16_t); | |
| 163 | static void bstp_set_path_cost(struct bridge_softc *, | |
| 164 | struct bridge_iflist *, uint32_t); | |
| 165 | #endif /* notused */ | |
| 166 | static void bstp_topology_change_detection(struct bridge_softc *); | |
| 167 | static void bstp_topology_change_acknowledged(struct bridge_softc *); | |
| 168 | static void bstp_acknowledge_topology_change(struct bridge_softc *, | |
| 169 | struct bridge_iflist *); | |
| 170 | ||
| 171 | static void bstp_tick(void *); | |
| 172 | static void bstp_timer_start(struct bridge_timer *, uint16_t); | |
| 173 | static void bstp_timer_stop(struct bridge_timer *); | |
| 174 | static int bstp_timer_expired(struct bridge_timer *, uint16_t); | |
| 175 | ||
| 176 | static void bstp_hold_timer_expiry(struct bridge_softc *, | |
| 177 | struct bridge_iflist *); | |
| 178 | static void bstp_message_age_timer_expiry(struct bridge_softc *, | |
| 179 | struct bridge_iflist *); | |
| 180 | static void bstp_forward_delay_timer_expiry(struct bridge_softc *, | |
| 181 | struct bridge_iflist *); | |
| 182 | static void bstp_topology_change_timer_expiry(struct bridge_softc *); | |
| 183 | static void bstp_tcn_timer_expiry(struct bridge_softc *); | |
| 184 | static void bstp_hello_timer_expiry(struct bridge_softc *); | |
| 185 | static int bstp_addr_cmp(const uint8_t *, const uint8_t *); | |
| 186 | ||
| 70d9a675 MD |
187 | /* |
| 188 | * When transmitting a config we tack on our path cost to | |
| 189 | * our aggregated path-to-root cost. | |
| 190 | */ | |
| d217f5e5 | 191 | static void |
| ac93838f SS |
192 | bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 193 | { | |
| 194 | if (bif->bif_hold_timer.active) { | |
| 195 | bif->bif_config_pending = 1; | |
| 196 | return; | |
| 197 | } | |
| 198 | ||
| 199 | bif->bif_config_bpdu.cu_message_type = BSTP_MSGTYPE_CFG; | |
| 200 | bif->bif_config_bpdu.cu_rootid = sc->sc_designated_root; | |
| 70d9a675 MD |
201 | bif->bif_config_bpdu.cu_root_path_cost = sc->sc_designated_cost + |
| 202 | bif->bif_path_cost; | |
| ac93838f SS |
203 | bif->bif_config_bpdu.cu_bridge_id = sc->sc_bridge_id; |
| 204 | bif->bif_config_bpdu.cu_port_id = bif->bif_port_id; | |
| 205 | ||
| 8f7b13ef | 206 | if (bstp_root_bridge(sc)) { |
| ac93838f | 207 | bif->bif_config_bpdu.cu_message_age = 0; |
| 70d9a675 | 208 | } else if (sc->sc_root_port) { |
| ac93838f | 209 | bif->bif_config_bpdu.cu_message_age = |
| 70d9a675 MD |
210 | sc->sc_root_port->bif_message_age_timer.value + |
| 211 | BSTP_MESSAGE_AGE_INCR; | |
| 212 | } else { | |
| 213 | bif->bif_config_bpdu.cu_message_age = BSTP_MESSAGE_AGE_INCR; | |
| 8f7b13ef | 214 | } |
| ac93838f SS |
215 | |
| 216 | bif->bif_config_bpdu.cu_max_age = sc->sc_max_age; | |
| 217 | bif->bif_config_bpdu.cu_hello_time = sc->sc_hello_time; | |
| 218 | bif->bif_config_bpdu.cu_forward_delay = sc->sc_forward_delay; | |
| 219 | bif->bif_config_bpdu.cu_topology_change_acknowledgment | |
| 220 | = bif->bif_topology_change_acknowledge; | |
| 221 | bif->bif_config_bpdu.cu_topology_change = sc->sc_topology_change; | |
| 222 | ||
| 3677aae9 MD |
223 | if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age || |
| 224 | (sc->sc_ifp->if_flags & IFF_LINK1)) { | |
| ac93838f SS |
225 | bif->bif_topology_change_acknowledge = 0; |
| 226 | bif->bif_config_pending = 0; | |
| 227 | bstp_send_config_bpdu(sc, bif, &bif->bif_config_bpdu); | |
| 228 | bstp_timer_start(&bif->bif_hold_timer, 0); | |
| 229 | } | |
| 230 | } | |
| 231 | ||
| d217f5e5 | 232 | static void |
| ac93838f | 233 | bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, |
| 3677aae9 | 234 | struct bstp_config_unit *cu) |
| ac93838f SS |
235 | { |
| 236 | struct ifnet *ifp; | |
| 237 | struct mbuf *m; | |
| 238 | struct ether_header *eh; | |
| 239 | struct bstp_cbpdu bpdu; | |
| 240 | ||
| 241 | ifp = bif->bif_ifp; | |
| 242 | ||
| 243 | if ((ifp->if_flags & IFF_RUNNING) == 0) | |
| 244 | return; | |
| 245 | ||
| 246 | MGETHDR(m, MB_DONTWAIT, MT_DATA); | |
| 247 | if (m == NULL) | |
| 248 | return; | |
| 249 | ||
| 250 | eh = mtod(m, struct ether_header *); | |
| 251 | ||
| 252 | m->m_pkthdr.rcvif = ifp; | |
| 253 | m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu); | |
| 254 | m->m_len = m->m_pkthdr.len; | |
| 255 | ||
| 256 | bpdu.cbu_ssap = bpdu.cbu_dsap = LLC_8021D_LSAP; | |
| 257 | bpdu.cbu_ctl = LLC_UI; | |
| 258 | bpdu.cbu_protoid = htons(0); | |
| 259 | bpdu.cbu_protover = 0; | |
| 260 | bpdu.cbu_bpdutype = cu->cu_message_type; | |
| 261 | bpdu.cbu_flags = (cu->cu_topology_change ? BSTP_FLAG_TC : 0) | | |
| 262 | (cu->cu_topology_change_acknowledgment ? BSTP_FLAG_TCA : 0); | |
| 263 | ||
| 264 | bpdu.cbu_rootpri = htons(cu->cu_rootid >> 48); | |
| 265 | bpdu.cbu_rootaddr[0] = cu->cu_rootid >> 40; | |
| 266 | bpdu.cbu_rootaddr[1] = cu->cu_rootid >> 32; | |
| 267 | bpdu.cbu_rootaddr[2] = cu->cu_rootid >> 24; | |
| 268 | bpdu.cbu_rootaddr[3] = cu->cu_rootid >> 16; | |
| 269 | bpdu.cbu_rootaddr[4] = cu->cu_rootid >> 8; | |
| 270 | bpdu.cbu_rootaddr[5] = cu->cu_rootid >> 0; | |
| 271 | ||
| 272 | bpdu.cbu_rootpathcost = htonl(cu->cu_root_path_cost); | |
| 273 | ||
| d217f5e5 SU |
274 | bpdu.cbu_bridgepri = htons(cu->cu_bridge_id >> 48); |
| 275 | bpdu.cbu_bridgeaddr[0] = cu->cu_bridge_id >> 40; | |
| 276 | bpdu.cbu_bridgeaddr[1] = cu->cu_bridge_id >> 32; | |
| 277 | bpdu.cbu_bridgeaddr[2] = cu->cu_bridge_id >> 24; | |
| 278 | bpdu.cbu_bridgeaddr[3] = cu->cu_bridge_id >> 16; | |
| 279 | bpdu.cbu_bridgeaddr[4] = cu->cu_bridge_id >> 8; | |
| 280 | bpdu.cbu_bridgeaddr[5] = cu->cu_bridge_id >> 0; | |
| ac93838f SS |
281 | |
| 282 | bpdu.cbu_portid = htons(cu->cu_port_id); | |
| 283 | bpdu.cbu_messageage = htons(cu->cu_message_age); | |
| 284 | bpdu.cbu_maxage = htons(cu->cu_max_age); | |
| 285 | bpdu.cbu_hellotime = htons(cu->cu_hello_time); | |
| 286 | bpdu.cbu_forwarddelay = htons(cu->cu_forward_delay); | |
| 287 | ||
| 70d9a675 MD |
288 | /* |
| 289 | * Packets sent from the bridge always use the bridge MAC | |
| 290 | * as the source. | |
| 291 | */ | |
| 292 | memcpy(eh->ether_shost, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | |
| ac93838f SS |
293 | memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); |
| 294 | eh->ether_type = htons(sizeof(bpdu)); | |
| 295 | ||
| 296 | memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu)); | |
| 297 | ||
| 708a3bfa | 298 | bridge_enqueue(ifp, m); |
| ac93838f SS |
299 | } |
| 300 | ||
| d217f5e5 | 301 | static int |
| ac93838f SS |
302 | bstp_root_bridge(struct bridge_softc *sc) |
| 303 | { | |
| 304 | return (sc->sc_designated_root == sc->sc_bridge_id); | |
| 305 | } | |
| 306 | ||
| 70d9a675 MD |
307 | /* |
| 308 | * Returns TRUE if the recorded information from our peer has a shorter | |
| 309 | * graph distance than our current best. | |
| 310 | */ | |
| 311 | int | |
| 312 | bstp_supersedes_port_info(struct bridge_softc *sc, struct bridge_iflist *bif) | |
| ac93838f | 313 | { |
| 70d9a675 | 314 | if (bif->bif_peer_root < sc->sc_designated_root) |
| ac93838f | 315 | return (1); |
| 70d9a675 | 316 | if (bif->bif_peer_root > sc->sc_designated_root) |
| ac93838f SS |
317 | return (0); |
| 318 | ||
| 70d9a675 MD |
319 | /* |
| 320 | * Both bif_peer_cost and sc_designated_cost have NOT added in | |
| 321 | * bif->bif_path_cost, so we can optimize it out. | |
| 322 | */ | |
| 323 | if (bif->bif_peer_cost < sc->sc_designated_cost) | |
| ac93838f | 324 | return (1); |
| 70d9a675 | 325 | if (bif->bif_peer_cost > sc->sc_designated_cost) |
| ac93838f SS |
326 | return (0); |
| 327 | ||
| 70d9a675 | 328 | if (bif->bif_peer_bridge < sc->sc_designated_bridge) |
| ac93838f | 329 | return (1); |
| 70d9a675 | 330 | if (bif->bif_peer_bridge > sc->sc_designated_bridge) |
| ac93838f SS |
331 | return (0); |
| 332 | ||
| 70d9a675 MD |
333 | /* bridge_id or bridge+port collision w/peer returns TRUE */ |
| 334 | if (bif->bif_peer_bridge != sc->sc_bridge_id) | |
| ac93838f | 335 | return (1); |
| 70d9a675 | 336 | if (bif->bif_peer_port <= sc->sc_designated_port) |
| ac93838f SS |
337 | return (1); |
| 338 | return (0); | |
| 339 | } | |
| 340 | ||
| 70d9a675 MD |
341 | /* |
| 342 | * The shorter graph distance represented by cu (most of which is also | |
| 343 | * already stored in our bif_peer_* fields) becomes the designated info. | |
| 344 | * | |
| 345 | * NOTE: sc_designated_cost does not include bif_path_cost, it is added | |
| 346 | * in later on a port-by-port basis as needed. | |
| 347 | */ | |
| d217f5e5 | 348 | static void |
| ac93838f | 349 | bstp_record_config_information(struct bridge_softc *sc, |
| 70d9a675 MD |
350 | struct bridge_iflist *bif, |
| 351 | struct bstp_config_unit *cu) | |
| ac93838f | 352 | { |
| 70d9a675 MD |
353 | sc->sc_designated_root = bif->bif_peer_root; |
| 354 | sc->sc_designated_cost = bif->bif_peer_cost; | |
| 355 | sc->sc_designated_bridge = bif->bif_peer_bridge; | |
| 356 | sc->sc_designated_port = bif->bif_peer_port; | |
| ac93838f SS |
357 | bstp_timer_start(&bif->bif_message_age_timer, cu->cu_message_age); |
| 358 | } | |
| 359 | ||
| d217f5e5 | 360 | static void |
| ac93838f | 361 | bstp_record_config_timeout_values(struct bridge_softc *sc, |
| 70d9a675 | 362 | struct bstp_config_unit *config) |
| ac93838f SS |
363 | { |
| 364 | sc->sc_max_age = config->cu_max_age; | |
| 365 | sc->sc_hello_time = config->cu_hello_time; | |
| 366 | sc->sc_forward_delay = config->cu_forward_delay; | |
| 367 | sc->sc_topology_change = config->cu_topology_change; | |
| 368 | } | |
| 369 | ||
| d217f5e5 | 370 | static void |
| ac93838f SS |
371 | bstp_config_bpdu_generation(struct bridge_softc *sc) |
| 372 | { | |
| 8f7b13ef | 373 | struct bridge_iflist *bif, *nbif; |
| ac93838f | 374 | |
| 70d9a675 | 375 | TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) { |
| ac93838f SS |
376 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 377 | continue; | |
| 3677aae9 MD |
378 | if (bif->bif_state != BSTP_IFSTATE_DISABLED && |
| 379 | ((sc->sc_ifp->if_flags & IFF_LINK1) || | |
| 70d9a675 | 380 | (bif->bif_flags & IFBIF_DESIGNATED))) { |
| ac93838f | 381 | bstp_transmit_config(sc, bif); |
| 3677aae9 | 382 | } |
| 8f7b13ef SZ |
383 | |
| 384 | if (nbif != NULL && !nbif->bif_onlist) { | |
| 385 | KKASSERT(bif->bif_onlist); | |
| 70d9a675 | 386 | nbif = TAILQ_NEXT(bif, bif_next); |
| 8f7b13ef | 387 | } |
| ac93838f SS |
388 | } |
| 389 | } | |
| 390 | ||
| d217f5e5 | 391 | static void |
| ac93838f SS |
392 | bstp_transmit_tcn(struct bridge_softc *sc) |
| 393 | { | |
| 394 | struct bstp_tbpdu bpdu; | |
| 70d9a675 | 395 | struct ifnet *ifp; |
| ac93838f SS |
396 | struct ether_header *eh; |
| 397 | struct mbuf *m; | |
| 398 | ||
| 70d9a675 MD |
399 | if (sc->sc_root_port == NULL) /* all iterfaces disabled */ |
| 400 | return; | |
| 401 | ||
| 402 | ifp = sc->sc_root_port->bif_ifp; | |
| ac93838f SS |
403 | if ((ifp->if_flags & IFF_RUNNING) == 0) |
| 404 | return; | |
| 405 | ||
| 406 | MGETHDR(m, MB_DONTWAIT, MT_DATA); | |
| 407 | if (m == NULL) | |
| 408 | return; | |
| 409 | ||
| 410 | m->m_pkthdr.rcvif = ifp; | |
| 411 | m->m_pkthdr.len = sizeof(*eh) + sizeof(bpdu); | |
| 412 | m->m_len = m->m_pkthdr.len; | |
| 413 | ||
| 414 | eh = mtod(m, struct ether_header *); | |
| 415 | ||
| 70d9a675 MD |
416 | /* |
| 417 | * Packets sent from the bridge always use the bridge MAC | |
| 418 | * as the source. | |
| 419 | */ | |
| 420 | memcpy(eh->ether_shost, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); | |
| ac93838f SS |
421 | memcpy(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN); |
| 422 | eh->ether_type = htons(sizeof(bpdu)); | |
| 423 | ||
| 424 | bpdu.tbu_ssap = bpdu.tbu_dsap = LLC_8021D_LSAP; | |
| 425 | bpdu.tbu_ctl = LLC_UI; | |
| 426 | bpdu.tbu_protoid = 0; | |
| 427 | bpdu.tbu_protover = 0; | |
| 428 | bpdu.tbu_bpdutype = BSTP_MSGTYPE_TCN; | |
| 429 | ||
| 430 | memcpy(mtod(m, caddr_t) + sizeof(*eh), &bpdu, sizeof(bpdu)); | |
| 431 | ||
| 708a3bfa | 432 | bridge_enqueue(ifp, m); |
| ac93838f SS |
433 | } |
| 434 | ||
| 70d9a675 MD |
435 | /* |
| 436 | * Recalculate sc->sc_designated* and sc->sc_root_port (if our bridge | |
| 437 | * is calculated to be the root bridge). We do this by initializing | |
| 438 | * the designated variables to point at us and then scan our peers. | |
| 439 | * Any uninitialized peers will have a max-value root. | |
| 440 | * | |
| 441 | * Clear IFBIF_DESIGNATED on any ports which no longer match the criteria | |
| 442 | * required to be a designated port. Only aged out ports and the root | |
| 443 | * port can be designated. | |
| 444 | * | |
| 445 | * If we win we do a second scan to determine which port on our bridge | |
| 446 | * is the best. | |
| 447 | */ | |
| d217f5e5 | 448 | static void |
| ac93838f SS |
449 | bstp_configuration_update(struct bridge_softc *sc) |
| 450 | { | |
| 70d9a675 MD |
451 | uint64_t designated_root = sc->sc_bridge_id; |
| 452 | uint64_t designated_bridge = sc->sc_bridge_id; | |
| 453 | uint32_t designated_cost = 0xFFFFFFFFU; | |
| 454 | uint16_t designated_port = 65535; | |
| 455 | struct bridge_iflist *root_port = NULL; | |
| 456 | struct bridge_iflist *bif; | |
| ac93838f | 457 | |
| 70d9a675 MD |
458 | /* |
| 459 | * Resolve information from our peers. Aged peers will have | |
| 460 | * a maxed bif_peer_root and not come under consideration. | |
| 461 | */ | |
| 462 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { | |
| ac93838f SS |
463 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 464 | continue; | |
| 70d9a675 MD |
465 | if (bif->bif_state == BSTP_IFSTATE_DISABLED || |
| 466 | bif->bif_state == BSTP_IFSTATE_L1BLOCKING) { | |
| ac93838f | 467 | continue; |
| 70d9a675 MD |
468 | } |
| 469 | ||
| 470 | if (bif->bif_peer_root > designated_root) | |
| ac93838f | 471 | continue; |
| 70d9a675 | 472 | if (bif->bif_peer_root < designated_root) |
| ac93838f SS |
473 | goto set_port; |
| 474 | ||
| 70d9a675 MD |
475 | /* |
| 476 | * NOTE: The designated_cost temporary variable already added | |
| 477 | * in the path code of the related root port. | |
| 478 | */ | |
| 479 | if (bif->bif_peer_cost + bif->bif_path_cost > designated_cost) | |
| ac93838f | 480 | continue; |
| 70d9a675 | 481 | if (bif->bif_peer_cost + bif->bif_path_cost < designated_cost) |
| ac93838f | 482 | goto set_port; |
| ac93838f | 483 | |
| 70d9a675 | 484 | if (bif->bif_peer_bridge > designated_bridge) |
| ac93838f | 485 | continue; |
| 70d9a675 | 486 | if (bif->bif_peer_bridge < designated_bridge) |
| ac93838f | 487 | goto set_port; |
| 70d9a675 MD |
488 | |
| 489 | if (bif->bif_peer_port > designated_port) | |
| ac93838f | 490 | continue; |
| 70d9a675 MD |
491 | if (bif->bif_peer_port < designated_port) |
| 492 | goto set_port; | |
| ac93838f | 493 | |
| 70d9a675 MD |
494 | /* |
| 495 | * Same root, path cost, bridge, and port. Set the root | |
| 496 | * only if we do not already have it. | |
| 497 | */ | |
| 498 | if (root_port) | |
| ac93838f | 499 | continue; |
| 70d9a675 MD |
500 | |
| 501 | /* | |
| 502 | * New root port (from peers) | |
| 503 | * | |
| 504 | * NOTE: Temporarily add bif_path_cost into the designated | |
| 505 | * cost to reduce complexity in the loop, it will be | |
| 506 | * subtracted out when we are done. | |
| 507 | */ | |
| ac93838f | 508 | set_port: |
| 70d9a675 MD |
509 | designated_root = bif->bif_peer_root; |
| 510 | designated_cost = bif->bif_peer_cost + bif->bif_path_cost; | |
| 511 | designated_bridge = bif->bif_peer_bridge; | |
| 512 | designated_port = bif->bif_peer_port; | |
| ac93838f SS |
513 | root_port = bif; |
| 514 | } | |
| 515 | ||
| 70d9a675 MD |
516 | /* |
| 517 | * root_port will be NULL at the start here if all of our | |
| 518 | * peers are aged or are not as good a root as our bridge would | |
| 519 | * be. It can also be NULL due to all related links being | |
| 520 | * disabled. | |
| 521 | * | |
| 522 | * If the root winds up being our bridge scan again against local | |
| 523 | * information. Unconditionally update IFBIF_DESIGNATED. | |
| 524 | */ | |
| 525 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { | |
| 526 | bif->bif_flags &= ~(IFBIF_DESIGNATED | IFBIF_ROOT); | |
| ac93838f SS |
527 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 528 | continue; | |
| 70d9a675 MD |
529 | if (bif->bif_state == BSTP_IFSTATE_DISABLED || |
| 530 | bif->bif_state == BSTP_IFSTATE_L1BLOCKING) { | |
| 531 | continue; | |
| 532 | } | |
| 533 | ||
| 534 | /* | |
| 535 | * Set DESIGNATED for an aged or unknown peer. | |
| 536 | */ | |
| 537 | if (bif->bif_peer_bridge == 0xFFFFFFFFFFFFFFFFLLU) | |
| 538 | bif->bif_flags |= IFBIF_DESIGNATED; | |
| 539 | if (designated_root != sc->sc_bridge_id) | |
| ac93838f SS |
540 | continue; |
| 541 | ||
| 70d9a675 MD |
542 | /* |
| 543 | * This is only reached if our bridge is the root bridge, | |
| 544 | * select the root port (IFBIF_DESIGNATED is set at the | |
| 545 | * end). | |
| 546 | * | |
| 547 | * We do NOT use peer info here. | |
| 548 | */ | |
| 549 | if (bif->bif_path_cost > designated_cost) | |
| ac93838f | 550 | continue; |
| 70d9a675 MD |
551 | if (bif->bif_path_cost < designated_cost) |
| 552 | goto set_port2; | |
| ac93838f | 553 | |
| 70d9a675 | 554 | if (bif->bif_port_id > designated_port) |
| ac93838f | 555 | continue; |
| 70d9a675 MD |
556 | if (bif->bif_port_id < designated_port) |
| 557 | goto set_port2; | |
| 558 | /* degenerate case (possible peer collision w/our key */ | |
| 559 | ||
| 560 | /* | |
| 561 | * New port | |
| 562 | */ | |
| 563 | set_port2: | |
| 564 | designated_cost = bif->bif_path_cost; | |
| 565 | designated_bridge = sc->sc_bridge_id; | |
| 566 | designated_port = bif->bif_port_id; | |
| 567 | root_port = bif; | |
| ac93838f | 568 | } |
| ac93838f | 569 | |
| 70d9a675 MD |
570 | /* |
| 571 | * Update aggregate information. The selected root port always | |
| 572 | * becomes a designated port (along with aged ports). This can | |
| 573 | * either be the port whos peer is closest to the root or it | |
| 574 | * can be one of our ports if our bridge is the root. | |
| 575 | * | |
| 576 | * The root cost we record in sc_designated_root does not include | |
| 577 | * bif_path_cost of the root port, since we may transmit this | |
| 578 | * out of any port we will add the cost back in later on on | |
| 579 | * a per-port basis. | |
| 580 | * | |
| 581 | * root_port can be NULL here (if all links are disabled) | |
| 582 | */ | |
| 583 | if (root_port) { | |
| 584 | sc->sc_designated_root = designated_root; | |
| 585 | sc->sc_designated_cost = designated_cost - | |
| 586 | root_port->bif_path_cost; | |
| 587 | sc->sc_designated_bridge = designated_bridge; | |
| 588 | sc->sc_designated_port = designated_port; | |
| 589 | root_port->bif_flags |= IFBIF_DESIGNATED | IFBIF_ROOT; | |
| 590 | } else { | |
| 591 | sc->sc_designated_root = designated_root; | |
| 592 | sc->sc_designated_cost = designated_cost; | |
| 593 | sc->sc_designated_bridge = designated_bridge; | |
| 594 | sc->sc_designated_port = designated_port; | |
| 595 | } | |
| 596 | sc->sc_root_port = root_port; | |
| ac93838f SS |
597 | } |
| 598 | ||
| 70d9a675 MD |
599 | /* |
| 600 | * Calculate the desired state for each interface link on our bridge. | |
| 601 | * | |
| 602 | * The best port will match against sc->sc_root_port (whether we are root | |
| 603 | * or whether that port is the closest to the root). We push this port | |
| 604 | * towards a FORWARDING state. | |
| 605 | * | |
| 606 | * Next come designated ports, either aged ports or ports with no peer info | |
| 607 | * (yet), or the peer who is closest to the root. We push this port towards | |
| 608 | * a FORWARDING state as well. | |
| 609 | * | |
| 610 | * Any remaining ports are pushed towards a BLOCKED state. Both sides of | |
| 611 | * the port (us and our peer) should wind up placing the two ends in this | |
| 612 | * state or bad things happen. | |
| 613 | */ | |
| d217f5e5 | 614 | static void |
| ac93838f SS |
615 | bstp_port_state_selection(struct bridge_softc *sc) |
| 616 | { | |
| 8f7b13ef | 617 | struct bridge_iflist *bif, *nbif; |
| ac93838f | 618 | |
| 70d9a675 | 619 | TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) { |
| ac93838f SS |
620 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 621 | continue; | |
| 70d9a675 MD |
622 | if (sc->sc_root_port && |
| 623 | bif->bif_info == sc->sc_root_port->bif_info) { | |
| ac93838f SS |
624 | bif->bif_config_pending = 0; |
| 625 | bif->bif_topology_change_acknowledge = 0; | |
| 626 | bstp_make_forwarding(sc, bif); | |
| 70d9a675 | 627 | } else if (bif->bif_flags & IFBIF_DESIGNATED) { |
| ac93838f SS |
628 | bstp_timer_stop(&bif->bif_message_age_timer); |
| 629 | bstp_make_forwarding(sc, bif); | |
| 630 | } else { | |
| 631 | bif->bif_config_pending = 0; | |
| 632 | bif->bif_topology_change_acknowledge = 0; | |
| 633 | bstp_make_blocking(sc, bif); | |
| 634 | } | |
| 8f7b13ef SZ |
635 | |
| 636 | if (nbif != NULL && !nbif->bif_onlist) { | |
| 637 | KKASSERT(bif->bif_onlist); | |
| 70d9a675 | 638 | nbif = TAILQ_NEXT(bif, bif_next); |
| 8f7b13ef | 639 | } |
| ac93838f SS |
640 | } |
| 641 | } | |
| 642 | ||
| 70d9a675 MD |
643 | /* |
| 644 | * Clear peer info, effectively makes the port looked aged out. | |
| 645 | * It becomes a designated go-to port. | |
| 646 | */ | |
| 647 | static void | |
| 648 | bstp_clear_peer_info(struct bridge_softc *sc, struct bridge_iflist *bif) | |
| 649 | { | |
| 650 | bif->bif_peer_root = 0xFFFFFFFFFFFFFFFFLLU; | |
| 651 | bif->bif_peer_cost = 0xFFFFFFFFU; | |
| 652 | bif->bif_peer_bridge = 0xFFFFFFFFFFFFFFFFLLU; | |
| 653 | bif->bif_peer_port = 0xFFFFU; | |
| 654 | ||
| 655 | if (bif->bif_state != BSTP_IFSTATE_DISABLED && | |
| 656 | bif->bif_state != BSTP_IFSTATE_L1BLOCKING) { | |
| 657 | bif->bif_flags |= IFBIF_DESIGNATED; | |
| 658 | } | |
| 659 | } | |
| 660 | ||
| d217f5e5 | 661 | static void |
| ac93838f SS |
662 | bstp_make_forwarding(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 663 | { | |
| 664 | if (bif->bif_state == BSTP_IFSTATE_BLOCKING) { | |
| 665 | bstp_set_port_state(bif, BSTP_IFSTATE_LISTENING); | |
| 666 | bstp_timer_start(&bif->bif_forward_delay_timer, 0); | |
| 667 | } | |
| 668 | } | |
| 669 | ||
| d217f5e5 | 670 | static void |
| ac93838f SS |
671 | bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 672 | { | |
| 3677aae9 MD |
673 | if (bif->bif_state != BSTP_IFSTATE_DISABLED && |
| 674 | bif->bif_state != BSTP_IFSTATE_BLOCKING && | |
| 675 | bif->bif_state != BSTP_IFSTATE_L1BLOCKING) { | |
| ac93838f SS |
676 | if ((bif->bif_state == BSTP_IFSTATE_FORWARDING) || |
| 677 | (bif->bif_state == BSTP_IFSTATE_LEARNING)) { | |
| 678 | if (bif->bif_change_detection_enabled) { | |
| 679 | bstp_topology_change_detection(sc); | |
| 680 | } | |
| 681 | } | |
| 682 | bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); | |
| 30ced003 | 683 | bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); |
| ac93838f SS |
684 | bstp_timer_stop(&bif->bif_forward_delay_timer); |
| 685 | } | |
| 686 | } | |
| 687 | ||
| d217f5e5 | 688 | static void |
| 3677aae9 MD |
689 | bstp_make_l1blocking(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 690 | { | |
| 691 | switch(bif->bif_state) { | |
| 692 | case BSTP_IFSTATE_LISTENING: | |
| 693 | case BSTP_IFSTATE_LEARNING: | |
| 694 | case BSTP_IFSTATE_FORWARDING: | |
| 695 | case BSTP_IFSTATE_BLOCKING: | |
| 696 | bstp_set_port_state(bif, BSTP_IFSTATE_L1BLOCKING); | |
| 697 | bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); | |
| 698 | bstp_timer_stop(&bif->bif_forward_delay_timer); | |
| 699 | bstp_timer_stop(&bif->bif_link1_timer); | |
| 70d9a675 MD |
700 | if (bif->bif_flags & IFBIF_DESIGNATED) { |
| 701 | bif->bif_flags &= ~IFBIF_DESIGNATED; | |
| 702 | bstp_configuration_update(sc); | |
| 703 | bstp_port_state_selection(sc); | |
| 704 | } | |
| 3677aae9 MD |
705 | break; |
| 706 | default: | |
| 707 | break; | |
| 708 | } | |
| 709 | } | |
| 710 | ||
| 711 | static void | |
| ac93838f SS |
712 | bstp_set_port_state(struct bridge_iflist *bif, uint8_t state) |
| 713 | { | |
| 714 | bif->bif_state = state; | |
| 715 | } | |
| 716 | ||
| d217f5e5 | 717 | static void |
| ac93838f SS |
718 | bstp_topology_change_detection(struct bridge_softc *sc) |
| 719 | { | |
| 720 | if (bstp_root_bridge(sc)) { | |
| 721 | sc->sc_topology_change = 1; | |
| 722 | bstp_timer_start(&sc->sc_topology_change_timer, 0); | |
| 723 | } else if (!sc->sc_topology_change_detected) { | |
| 724 | bstp_transmit_tcn(sc); | |
| 725 | bstp_timer_start(&sc->sc_tcn_timer, 0); | |
| 726 | } | |
| 727 | sc->sc_topology_change_detected = 1; | |
| 728 | } | |
| 729 | ||
| d217f5e5 | 730 | static void |
| ac93838f SS |
731 | bstp_topology_change_acknowledged(struct bridge_softc *sc) |
| 732 | { | |
| 733 | sc->sc_topology_change_detected = 0; | |
| 734 | bstp_timer_stop(&sc->sc_tcn_timer); | |
| 735 | } | |
| 736 | ||
| d217f5e5 | 737 | static void |
| ac93838f | 738 | bstp_acknowledge_topology_change(struct bridge_softc *sc, |
| 70d9a675 | 739 | struct bridge_iflist *bif) |
| ac93838f SS |
740 | { |
| 741 | bif->bif_topology_change_acknowledge = 1; | |
| 742 | bstp_transmit_config(sc, bif); | |
| 743 | } | |
| 744 | ||
| b2417333 | 745 | void |
| 89ea766d | 746 | bstp_input(struct bridge_softc *sc, struct bridge_iflist *bif, struct mbuf *m) |
| ac93838f | 747 | { |
| ac93838f SS |
748 | struct ether_header *eh; |
| 749 | struct bstp_tbpdu tpdu; | |
| 750 | struct bstp_cbpdu cpdu; | |
| 751 | struct bstp_config_unit cu; | |
| 752 | struct bstp_tcn_unit tu; | |
| 753 | uint16_t len; | |
| 754 | ||
| 89ea766d | 755 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| ac93838f SS |
756 | goto out; |
| 757 | ||
| 3677aae9 MD |
758 | /* |
| 759 | * The L1BLOCKING (ping pong failover) test needs to reset the | |
| 760 | * timer if LINK1 is active. | |
| 761 | */ | |
| 762 | if (bif->bif_state == BSTP_IFSTATE_L1BLOCKING) { | |
| 763 | bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); | |
| 764 | if (sc->sc_ifp->if_flags & IFF_LINK1) | |
| 765 | bstp_timer_start(&bif->bif_link1_timer, 0); | |
| 766 | bstp_make_forwarding(sc, bif); | |
| 767 | } else if (sc->sc_ifp->if_flags & IFF_LINK1) { | |
| 768 | bstp_timer_start(&bif->bif_link1_timer, 0); | |
| 769 | } | |
| 770 | ||
| 89ea766d SZ |
771 | eh = mtod(m, struct ether_header *); |
| 772 | ||
| ac93838f SS |
773 | len = ntohs(eh->ether_type); |
| 774 | if (len < sizeof(tpdu)) | |
| 775 | goto out; | |
| 776 | ||
| 777 | m_adj(m, ETHER_HDR_LEN); | |
| 778 | ||
| 779 | if (m->m_pkthdr.len > len) | |
| 780 | m_adj(m, len - m->m_pkthdr.len); | |
| 781 | if (m->m_len < sizeof(tpdu) && | |
| 782 | (m = m_pullup(m, sizeof(tpdu))) == NULL) | |
| 783 | goto out; | |
| 784 | ||
| 785 | memcpy(&tpdu, mtod(m, caddr_t), sizeof(tpdu)); | |
| 786 | ||
| 787 | if (tpdu.tbu_dsap != LLC_8021D_LSAP || | |
| 788 | tpdu.tbu_ssap != LLC_8021D_LSAP || | |
| 789 | tpdu.tbu_ctl != LLC_UI) | |
| 790 | goto out; | |
| 791 | if (tpdu.tbu_protoid != 0 || tpdu.tbu_protover != 0) | |
| 792 | goto out; | |
| 793 | ||
| 794 | switch (tpdu.tbu_bpdutype) { | |
| 795 | case BSTP_MSGTYPE_TCN: | |
| 796 | tu.tu_message_type = tpdu.tbu_bpdutype; | |
| 797 | bstp_received_tcn_bpdu(sc, bif, &tu); | |
| 798 | break; | |
| 799 | case BSTP_MSGTYPE_CFG: | |
| 800 | if (m->m_len < sizeof(cpdu) && | |
| 801 | (m = m_pullup(m, sizeof(cpdu))) == NULL) | |
| 802 | goto out; | |
| 803 | memcpy(&cpdu, mtod(m, caddr_t), sizeof(cpdu)); | |
| 804 | ||
| 805 | cu.cu_rootid = | |
| 806 | (((uint64_t)ntohs(cpdu.cbu_rootpri)) << 48) | | |
| 807 | (((uint64_t)cpdu.cbu_rootaddr[0]) << 40) | | |
| 808 | (((uint64_t)cpdu.cbu_rootaddr[1]) << 32) | | |
| 809 | (((uint64_t)cpdu.cbu_rootaddr[2]) << 24) | | |
| 810 | (((uint64_t)cpdu.cbu_rootaddr[3]) << 16) | | |
| 811 | (((uint64_t)cpdu.cbu_rootaddr[4]) << 8) | | |
| 812 | (((uint64_t)cpdu.cbu_rootaddr[5]) << 0); | |
| 813 | ||
| 814 | cu.cu_bridge_id = | |
| 815 | (((uint64_t)ntohs(cpdu.cbu_bridgepri)) << 48) | | |
| 816 | (((uint64_t)cpdu.cbu_bridgeaddr[0]) << 40) | | |
| 817 | (((uint64_t)cpdu.cbu_bridgeaddr[1]) << 32) | | |
| 818 | (((uint64_t)cpdu.cbu_bridgeaddr[2]) << 24) | | |
| 819 | (((uint64_t)cpdu.cbu_bridgeaddr[3]) << 16) | | |
| 820 | (((uint64_t)cpdu.cbu_bridgeaddr[4]) << 8) | | |
| 821 | (((uint64_t)cpdu.cbu_bridgeaddr[5]) << 0); | |
| 822 | ||
| 823 | cu.cu_root_path_cost = ntohl(cpdu.cbu_rootpathcost); | |
| 824 | cu.cu_message_age = ntohs(cpdu.cbu_messageage); | |
| 825 | cu.cu_max_age = ntohs(cpdu.cbu_maxage); | |
| 826 | cu.cu_hello_time = ntohs(cpdu.cbu_hellotime); | |
| 827 | cu.cu_forward_delay = ntohs(cpdu.cbu_forwarddelay); | |
| 828 | cu.cu_port_id = ntohs(cpdu.cbu_portid); | |
| 829 | cu.cu_message_type = cpdu.cbu_bpdutype; | |
| 830 | cu.cu_topology_change_acknowledgment = | |
| 831 | (cpdu.cbu_flags & BSTP_FLAG_TCA) ? 1 : 0; | |
| 832 | cu.cu_topology_change = | |
| 833 | (cpdu.cbu_flags & BSTP_FLAG_TC) ? 1 : 0; | |
| 834 | bstp_received_config_bpdu(sc, bif, &cu); | |
| 835 | break; | |
| 836 | default: | |
| 837 | goto out; | |
| 838 | } | |
| d217f5e5 | 839 | out: |
| ac93838f SS |
840 | if (m) |
| 841 | m_freem(m); | |
| ac93838f SS |
842 | } |
| 843 | ||
| d217f5e5 | 844 | static void |
| ac93838f | 845 | bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, |
| 70d9a675 | 846 | struct bstp_config_unit *cu) |
| ac93838f | 847 | { |
| 70d9a675 | 848 | int iamroot; |
| ac93838f | 849 | |
| 70d9a675 | 850 | iamroot = bstp_root_bridge(sc); |
| ac93838f SS |
851 | |
| 852 | if (bif->bif_state != BSTP_IFSTATE_DISABLED) { | |
| 70d9a675 MD |
853 | /* |
| 854 | * Record information from peer. The peer_cost field | |
| 855 | * does not include the local bif->bif_path_cost, it will | |
| 856 | * be added in as needed (since it can be modified manually | |
| 857 | * this way we don't have to worry about fixups). | |
| 858 | */ | |
| 859 | bif->bif_peer_root = cu->cu_rootid; | |
| 860 | bif->bif_peer_cost = cu->cu_root_path_cost; | |
| 861 | bif->bif_peer_bridge = cu->cu_bridge_id; | |
| 862 | bif->bif_peer_port = cu->cu_port_id; | |
| 863 | ||
| 864 | if (bstp_supersedes_port_info(sc, bif)) { | |
| ac93838f SS |
865 | bstp_record_config_information(sc, bif, cu); |
| 866 | bstp_configuration_update(sc); | |
| 867 | bstp_port_state_selection(sc); | |
| 868 | ||
| 70d9a675 MD |
869 | /* |
| 870 | * If our bridge loses its root status (?) | |
| 871 | * | |
| 872 | * Hello's (unsolicited CFG packets) are generated | |
| 873 | * every hello period of LINK1 is set, otherwise | |
| 874 | * we are no longer the root bridge and must stop | |
| 875 | * generating unsolicited CFG packets. | |
| 876 | */ | |
| 877 | if (iamroot && bstp_root_bridge(sc) == 0) { | |
| 3677aae9 MD |
878 | if ((sc->sc_ifp->if_flags & IFF_LINK1) == 0) |
| 879 | bstp_timer_stop(&sc->sc_hello_timer); | |
| ac93838f SS |
880 | |
| 881 | if (sc->sc_topology_change_detected) { | |
| 882 | bstp_timer_stop( | |
| 883 | &sc->sc_topology_change_timer); | |
| 884 | bstp_transmit_tcn(sc); | |
| 885 | bstp_timer_start(&sc->sc_tcn_timer, 0); | |
| 886 | } | |
| 887 | } | |
| 888 | ||
| 70d9a675 MD |
889 | if (sc->sc_root_port && |
| 890 | bif->bif_info == sc->sc_root_port->bif_info) { | |
| ac93838f SS |
891 | bstp_record_config_timeout_values(sc, cu); |
| 892 | bstp_config_bpdu_generation(sc); | |
| 893 | ||
| 894 | if (cu->cu_topology_change_acknowledgment) | |
| 895 | bstp_topology_change_acknowledged(sc); | |
| 896 | } | |
| 70d9a675 MD |
897 | } else if (bif->bif_flags & IFBIF_DESIGNATED) { |
| 898 | /* | |
| 899 | * Update designated ports (aged out peers or | |
| 900 | * the port closest to the root) at a faster pace. | |
| 901 | * | |
| 902 | * Clear our designated flag if we aren't marked | |
| 903 | * as the root port. | |
| 904 | */ | |
| ac93838f | 905 | bstp_transmit_config(sc, bif); |
| 70d9a675 MD |
906 | if ((bif->bif_flags & IFBIF_ROOT) == 0) { |
| 907 | bif->bif_flags &= ~IFBIF_DESIGNATED; | |
| 908 | bstp_configuration_update(sc); | |
| 909 | bstp_port_state_selection(sc); | |
| 910 | } | |
| 911 | } | |
| ac93838f SS |
912 | } |
| 913 | } | |
| 914 | ||
| d217f5e5 | 915 | static void |
| ac93838f | 916 | bstp_received_tcn_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, |
| 70d9a675 | 917 | struct bstp_tcn_unit *tcn) |
| ac93838f SS |
918 | { |
| 919 | if (bif->bif_state != BSTP_IFSTATE_DISABLED && | |
| 70d9a675 | 920 | (bif->bif_flags & IFBIF_DESIGNATED)) { |
| ac93838f SS |
921 | bstp_topology_change_detection(sc); |
| 922 | bstp_acknowledge_topology_change(sc, bif); | |
| 923 | } | |
| 924 | } | |
| 925 | ||
| 3677aae9 MD |
926 | /* |
| 927 | * link1 forces continuous hello's (the bridge interface must be cycled | |
| 928 | * to start them up), so keep the timer hot if that is the case, otherwise | |
| 929 | * only send HELLO's if we are the root. | |
| 930 | */ | |
| d217f5e5 | 931 | static void |
| ac93838f SS |
932 | bstp_hello_timer_expiry(struct bridge_softc *sc) |
| 933 | { | |
| 934 | bstp_config_bpdu_generation(sc); | |
| 3677aae9 MD |
935 | |
| 936 | if ((sc->sc_ifp->if_flags & IFF_LINK1) || bstp_root_bridge(sc)) | |
| 937 | bstp_timer_start(&sc->sc_hello_timer, 0); | |
| ac93838f SS |
938 | } |
| 939 | ||
| d217f5e5 | 940 | static void |
| ac93838f | 941 | bstp_message_age_timer_expiry(struct bridge_softc *sc, |
| 3677aae9 | 942 | struct bridge_iflist *bif) |
| ac93838f | 943 | { |
| 70d9a675 | 944 | int iamroot; |
| ac93838f | 945 | |
| 70d9a675 MD |
946 | iamroot = bstp_root_bridge(sc); |
| 947 | bstp_clear_peer_info(sc, bif); | |
| ac93838f SS |
948 | bstp_configuration_update(sc); |
| 949 | bstp_port_state_selection(sc); | |
| 950 | ||
| 3677aae9 MD |
951 | /* |
| 952 | * If we've become the root and were not the root before | |
| 953 | * we have some cleanup to do. This also occurs if we | |
| 954 | * wind up being completely isolated. | |
| 955 | */ | |
| 70d9a675 | 956 | if (iamroot == 0 && bstp_root_bridge(sc)) { |
| ac93838f SS |
957 | sc->sc_max_age = sc->sc_bridge_max_age; |
| 958 | sc->sc_hello_time = sc->sc_bridge_hello_time; | |
| 959 | sc->sc_forward_delay = sc->sc_bridge_forward_delay; | |
| 960 | ||
| 961 | bstp_topology_change_detection(sc); | |
| 962 | bstp_timer_stop(&sc->sc_tcn_timer); | |
| 963 | bstp_config_bpdu_generation(sc); | |
| 964 | bstp_timer_start(&sc->sc_hello_timer, 0); | |
| 965 | } | |
| 966 | } | |
| 967 | ||
| d217f5e5 | 968 | static void |
| ac93838f | 969 | bstp_forward_delay_timer_expiry(struct bridge_softc *sc, |
| 70d9a675 | 970 | struct bridge_iflist *bif) |
| ac93838f SS |
971 | { |
| 972 | if (bif->bif_state == BSTP_IFSTATE_LISTENING) { | |
| 973 | bstp_set_port_state(bif, BSTP_IFSTATE_LEARNING); | |
| 974 | bstp_timer_start(&bif->bif_forward_delay_timer, 0); | |
| 975 | } else if (bif->bif_state == BSTP_IFSTATE_LEARNING) { | |
| 976 | bstp_set_port_state(bif, BSTP_IFSTATE_FORWARDING); | |
| 70d9a675 MD |
977 | if (sc->sc_designated_bridge == sc->sc_bridge_id && |
| 978 | bif->bif_change_detection_enabled) { | |
| ac93838f | 979 | bstp_topology_change_detection(sc); |
| 70d9a675 | 980 | } |
| ac93838f SS |
981 | } |
| 982 | } | |
| 983 | ||
| d217f5e5 | 984 | static void |
| ac93838f SS |
985 | bstp_tcn_timer_expiry(struct bridge_softc *sc) |
| 986 | { | |
| 987 | bstp_transmit_tcn(sc); | |
| 988 | bstp_timer_start(&sc->sc_tcn_timer, 0); | |
| 989 | } | |
| 990 | ||
| d217f5e5 | 991 | static void |
| ac93838f SS |
992 | bstp_topology_change_timer_expiry(struct bridge_softc *sc) |
| 993 | { | |
| 994 | sc->sc_topology_change_detected = 0; | |
| 995 | sc->sc_topology_change = 0; | |
| 996 | } | |
| 997 | ||
| d217f5e5 | 998 | static void |
| ac93838f SS |
999 | bstp_hold_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 1000 | { | |
| 1001 | if (bif->bif_config_pending) | |
| 1002 | bstp_transmit_config(sc, bif); | |
| 1003 | } | |
| 1004 | ||
| 3677aae9 MD |
1005 | /* |
| 1006 | * If no traffic received directly on this port for the specified | |
| 1007 | * period with link1 set we go into a special blocking mode to | |
| 1008 | * fail-over traffic to another port. | |
| 1009 | */ | |
| 1010 | static void | |
| 1011 | bstp_link1_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif) | |
| 1012 | { | |
| 1013 | if (sc->sc_ifp->if_flags & IFF_LINK1) | |
| 1014 | bstp_make_l1blocking(sc, bif); | |
| 1015 | } | |
| 1016 | ||
| d217f5e5 SU |
1017 | static int |
| 1018 | bstp_addr_cmp(const uint8_t *a, const uint8_t *b) | |
| 1019 | { | |
| 1020 | int i, d; | |
| 1021 | ||
| 1022 | for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) { | |
| 1023 | d = ((int)a[i]) - ((int)b[i]); | |
| 1024 | } | |
| 1025 | ||
| 1026 | return (d); | |
| 1027 | } | |
| 1028 | ||
| ac93838f SS |
1029 | void |
| 1030 | bstp_initialization(struct bridge_softc *sc) | |
| 1031 | { | |
| 8f7b13ef | 1032 | struct bridge_iflist *bif, *mif, *nbif; |
| d217f5e5 | 1033 | u_char *e_addr; |
| ac93838f | 1034 | |
| e9d22060 SZ |
1035 | KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT); |
| 1036 | ||
| 70d9a675 MD |
1037 | /* |
| 1038 | * Figure out our bridge ID, use the lowest-valued MAC. | |
| 1039 | * Include the bridge's own random MAC in the calculation. | |
| 1040 | */ | |
| ac93838f | 1041 | mif = NULL; |
| 70d9a675 MD |
1042 | |
| 1043 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { | |
| ac93838f SS |
1044 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 1045 | continue; | |
| 1046 | if (bif->bif_ifp->if_type != IFT_ETHER) | |
| 1047 | continue; | |
| ac93838f SS |
1048 | if (mif == NULL) { |
| 1049 | mif = bif; | |
| 1050 | continue; | |
| 1051 | } | |
| 70d9a675 MD |
1052 | |
| 1053 | bif->bif_port_id = (bif->bif_priority << 8) | | |
| 1054 | (bif->bif_ifp->if_index & 0xff); | |
| d217f5e5 | 1055 | if (bstp_addr_cmp(IF_LLADDR(bif->bif_ifp), |
| 70d9a675 | 1056 | IF_LLADDR(mif->bif_ifp)) < 0) { |
| ac93838f SS |
1057 | mif = bif; |
| 1058 | continue; | |
| 1059 | } | |
| 1060 | } | |
| 1061 | if (mif == NULL) { | |
| 1062 | bstp_stop(sc); | |
| 1063 | return; | |
| 1064 | } | |
| 1065 | ||
| 70d9a675 MD |
1066 | if (bstp_addr_cmp(IF_LLADDR(sc->sc_ifp), IF_LLADDR(mif->bif_ifp)) < 0) |
| 1067 | e_addr = IF_LLADDR(sc->sc_ifp); | |
| 1068 | else | |
| 1069 | e_addr = IF_LLADDR(mif->bif_ifp); | |
| 1070 | ||
| ac93838f SS |
1071 | sc->sc_bridge_id = |
| 1072 | (((uint64_t)sc->sc_bridge_priority) << 48) | | |
| d217f5e5 SU |
1073 | (((uint64_t)e_addr[0]) << 40) | |
| 1074 | (((uint64_t)e_addr[1]) << 32) | | |
| 1075 | (((uint64_t)e_addr[2]) << 24) | | |
| 1076 | (((uint64_t)e_addr[3]) << 16) | | |
| 1077 | (((uint64_t)e_addr[4]) << 8) | | |
| 1078 | (((uint64_t)e_addr[5])); | |
| ac93838f | 1079 | |
| 70d9a675 MD |
1080 | /* |
| 1081 | * Remainder of setup. | |
| 1082 | */ | |
| 1083 | ||
| ac93838f | 1084 | sc->sc_designated_root = sc->sc_bridge_id; |
| 70d9a675 | 1085 | sc->sc_designated_cost = 0; |
| ac93838f SS |
1086 | sc->sc_root_port = NULL; |
| 1087 | ||
| 1088 | sc->sc_max_age = sc->sc_bridge_max_age; | |
| 1089 | sc->sc_hello_time = sc->sc_bridge_hello_time; | |
| 1090 | sc->sc_forward_delay = sc->sc_bridge_forward_delay; | |
| 1091 | sc->sc_topology_change_detected = 0; | |
| 1092 | sc->sc_topology_change = 0; | |
| 1093 | bstp_timer_stop(&sc->sc_tcn_timer); | |
| 1094 | bstp_timer_stop(&sc->sc_topology_change_timer); | |
| 1095 | ||
| 1096 | if (callout_pending(&sc->sc_bstpcallout) == 0) | |
| 1097 | callout_reset(&sc->sc_bstpcallout, hz, | |
| 1098 | bstp_tick, sc); | |
| 1099 | ||
| 70d9a675 | 1100 | TAILQ_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) { |
| 3677aae9 MD |
1101 | if (sc->sc_ifp->if_flags & IFF_LINK1) |
| 1102 | bstp_timer_start(&bif->bif_link1_timer, 0); | |
| ac93838f SS |
1103 | if (bif->bif_flags & IFBIF_STP) |
| 1104 | bstp_ifupdstatus(sc, bif); | |
| 1105 | else | |
| 1106 | bstp_disable_port(sc, bif); | |
| 8f7b13ef SZ |
1107 | |
| 1108 | if (nbif != NULL && !nbif->bif_onlist) { | |
| 1109 | KKASSERT(bif->bif_onlist); | |
| 70d9a675 | 1110 | nbif = TAILQ_NEXT(bif, bif_next); |
| 8f7b13ef | 1111 | } |
| ac93838f SS |
1112 | } |
| 1113 | ||
| 1114 | bstp_port_state_selection(sc); | |
| 1115 | bstp_config_bpdu_generation(sc); | |
| 1116 | bstp_timer_start(&sc->sc_hello_timer, 0); | |
| 1117 | } | |
| 1118 | ||
| 1119 | void | |
| 1120 | bstp_stop(struct bridge_softc *sc) | |
| 1121 | { | |
| 1122 | struct bridge_iflist *bif; | |
| e9d22060 SZ |
1123 | struct lwkt_msg *lmsg; |
| 1124 | ||
| 1125 | KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT); | |
| ac93838f | 1126 | |
| 70d9a675 | 1127 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { |
| ac93838f SS |
1128 | bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED); |
| 1129 | bstp_timer_stop(&bif->bif_hold_timer); | |
| 1130 | bstp_timer_stop(&bif->bif_message_age_timer); | |
| 1131 | bstp_timer_stop(&bif->bif_forward_delay_timer); | |
| 3677aae9 | 1132 | bstp_timer_stop(&bif->bif_link1_timer); |
| ac93838f SS |
1133 | } |
| 1134 | ||
| 1135 | callout_stop(&sc->sc_bstpcallout); | |
| 1136 | ||
| 1137 | bstp_timer_stop(&sc->sc_topology_change_timer); | |
| 1138 | bstp_timer_stop(&sc->sc_tcn_timer); | |
| 1139 | bstp_timer_stop(&sc->sc_hello_timer); | |
| 1140 | ||
| e9d22060 | 1141 | crit_enter(); |
| 002c1265 | 1142 | lmsg = &sc->sc_bstptimemsg.lmsg; |
| e9d22060 SZ |
1143 | if ((lmsg->ms_flags & MSGF_DONE) == 0) { |
| 1144 | /* Pending to be processed; drop it */ | |
| 1145 | lwkt_dropmsg(lmsg); | |
| 1146 | } | |
| 1147 | crit_exit(); | |
| ac93838f SS |
1148 | } |
| 1149 | ||
| d217f5e5 | 1150 | static void |
| ac93838f SS |
1151 | bstp_initialize_port(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 1152 | { | |
| ac93838f | 1153 | bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); |
| 70d9a675 | 1154 | bstp_clear_peer_info(sc, bif); |
| ac93838f SS |
1155 | bif->bif_topology_change_acknowledge = 0; |
| 1156 | bif->bif_config_pending = 0; | |
| 1157 | bif->bif_change_detection_enabled = 1; | |
| 1158 | bstp_timer_stop(&bif->bif_message_age_timer); | |
| 1159 | bstp_timer_stop(&bif->bif_forward_delay_timer); | |
| 1160 | bstp_timer_stop(&bif->bif_hold_timer); | |
| 3677aae9 | 1161 | bstp_timer_stop(&bif->bif_link1_timer); |
| ac93838f SS |
1162 | } |
| 1163 | ||
| d217f5e5 | 1164 | static void |
| ac93838f SS |
1165 | bstp_enable_port(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 1166 | { | |
| 1167 | bstp_initialize_port(sc, bif); | |
| 3677aae9 MD |
1168 | if (sc->sc_ifp->if_flags & IFF_LINK1) |
| 1169 | bstp_timer_start(&bif->bif_link1_timer, 0); | |
| ac93838f SS |
1170 | bstp_port_state_selection(sc); |
| 1171 | } | |
| 1172 | ||
| d217f5e5 | 1173 | static void |
| ac93838f SS |
1174 | bstp_disable_port(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 1175 | { | |
| 70d9a675 | 1176 | int iamroot; |
| ac93838f | 1177 | |
| 70d9a675 MD |
1178 | iamroot = bstp_root_bridge(sc); |
| 1179 | ||
| 1180 | bstp_clear_peer_info(sc, bif); | |
| ac93838f SS |
1181 | bstp_set_port_state(bif, BSTP_IFSTATE_DISABLED); |
| 1182 | bif->bif_topology_change_acknowledge = 0; | |
| 1183 | bif->bif_config_pending = 0; | |
| 1184 | bstp_timer_stop(&bif->bif_message_age_timer); | |
| 1185 | bstp_timer_stop(&bif->bif_forward_delay_timer); | |
| 3677aae9 | 1186 | bstp_timer_stop(&bif->bif_link1_timer); |
| ac93838f SS |
1187 | bstp_configuration_update(sc); |
| 1188 | bstp_port_state_selection(sc); | |
| 30ced003 | 1189 | bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); |
| ac93838f | 1190 | |
| 70d9a675 | 1191 | if (iamroot == 0 && bstp_root_bridge(sc)) { |
| ac93838f SS |
1192 | sc->sc_max_age = sc->sc_bridge_max_age; |
| 1193 | sc->sc_hello_time = sc->sc_bridge_hello_time; | |
| 1194 | sc->sc_forward_delay = sc->sc_bridge_forward_delay; | |
| 1195 | ||
| 1196 | bstp_topology_change_detection(sc); | |
| 1197 | bstp_timer_stop(&sc->sc_tcn_timer); | |
| 1198 | bstp_config_bpdu_generation(sc); | |
| 1199 | bstp_timer_start(&sc->sc_hello_timer, 0); | |
| 1200 | } | |
| 1201 | } | |
| 1202 | ||
| ac93838f SS |
1203 | void |
| 1204 | bstp_linkstate(struct ifnet *ifp, int state) | |
| 1205 | { | |
| 1206 | struct bridge_softc *sc; | |
| 1207 | struct bridge_iflist *bif; | |
| 1208 | ||
| 1209 | sc = ifp->if_bridge; | |
| a3dd34d2 | 1210 | ifnet_serialize_all(sc->sc_ifp); |
| ac93838f | 1211 | |
| 8f7b13ef SZ |
1212 | /* |
| 1213 | * bstp_ifupdstatus() may block, but it is the last | |
| 1214 | * operation of the member iface iteration, so we | |
| 1215 | * don't need to use LIST_FOREACH_MUTABLE()+bif_onlist | |
| 1216 | * check here. | |
| 1217 | */ | |
| 70d9a675 | 1218 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { |
| ac93838f SS |
1219 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 1220 | continue; | |
| 1221 | ||
| 1222 | if (bif->bif_ifp == ifp) { | |
| 1223 | bstp_ifupdstatus(sc, bif); | |
| 1224 | break; | |
| 1225 | } | |
| 1226 | } | |
| a3dd34d2 | 1227 | ifnet_deserialize_all(sc->sc_ifp); |
| ac93838f SS |
1228 | } |
| 1229 | ||
| d217f5e5 | 1230 | static void |
| ac93838f SS |
1231 | bstp_ifupdstatus(struct bridge_softc *sc, struct bridge_iflist *bif) |
| 1232 | { | |
| 1233 | struct ifnet *ifp = bif->bif_ifp; | |
| 1234 | struct ifmediareq ifmr; | |
| 1235 | int error = 0; | |
| 1236 | ||
| 1237 | bzero((char *)&ifmr, sizeof(ifmr)); | |
| a3dd34d2 | 1238 | ifnet_serialize_all(ifp); |
| d217f5e5 | 1239 | error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr, NULL); |
| a3dd34d2 | 1240 | ifnet_deserialize_all(ifp); |
| ac93838f SS |
1241 | |
| 1242 | if ((error == 0) && (ifp->if_flags & IFF_UP)) { | |
| 1243 | if (ifmr.ifm_status & IFM_ACTIVE) { | |
| 1244 | if (bif->bif_state == BSTP_IFSTATE_DISABLED) | |
| 1245 | bstp_enable_port(sc, bif); | |
| 1246 | ||
| 1247 | } else { | |
| 1248 | if (bif->bif_state != BSTP_IFSTATE_DISABLED) | |
| 1249 | bstp_disable_port(sc, bif); | |
| 1250 | } | |
| 1251 | return; | |
| 1252 | } | |
| 1253 | ||
| 1254 | if (bif->bif_state != BSTP_IFSTATE_DISABLED) | |
| 1255 | bstp_disable_port(sc, bif); | |
| 1256 | } | |
| 1257 | ||
| d217f5e5 | 1258 | static void |
| ac93838f SS |
1259 | bstp_tick(void *arg) |
| 1260 | { | |
| 1261 | struct bridge_softc *sc = arg; | |
| e9d22060 SZ |
1262 | struct lwkt_msg *lmsg; |
| 1263 | ||
| 1264 | KKASSERT(mycpuid == BRIDGE_CFGCPU); | |
| 1265 | ||
| 1266 | crit_enter(); | |
| 1267 | ||
| 1268 | if (callout_pending(&sc->sc_bstpcallout) || | |
| 1269 | !callout_active(&sc->sc_bstpcallout)) { | |
| 1270 | crit_exit(); | |
| 1271 | return; | |
| 1272 | } | |
| 1273 | callout_deactivate(&sc->sc_bstpcallout); | |
| 1274 | ||
| 002c1265 | 1275 | lmsg = &sc->sc_bstptimemsg.lmsg; |
| e9d22060 SZ |
1276 | KKASSERT(lmsg->ms_flags & MSGF_DONE); |
| 1277 | lwkt_sendmsg(BRIDGE_CFGPORT, lmsg); | |
| 1278 | ||
| 1279 | crit_exit(); | |
| 1280 | } | |
| 1281 | ||
| 1282 | void | |
| 002c1265 | 1283 | bstp_tick_handler(netmsg_t msg) |
| e9d22060 | 1284 | { |
| 002c1265 | 1285 | struct bridge_softc *sc = msg->lmsg.u.ms_resultp; |
| ac93838f SS |
1286 | struct bridge_iflist *bif; |
| 1287 | ||
| e9d22060 SZ |
1288 | KKASSERT(&curthread->td_msgport == BRIDGE_CFGPORT); |
| 1289 | crit_enter(); | |
| 1290 | /* Reply ASAP */ | |
| 002c1265 | 1291 | lwkt_replymsg(&msg->lmsg, 0); |
| e9d22060 SZ |
1292 | crit_exit(); |
| 1293 | ||
| a3dd34d2 | 1294 | ifnet_serialize_all(sc->sc_ifp); |
| ac93838f | 1295 | |
| 8f7b13ef SZ |
1296 | /* |
| 1297 | * NOTE: | |
| 1298 | * We don't need to worry that member iface is ripped | |
| 1299 | * from the per-cpu list during the blocking operation | |
| 1300 | * in the loop body, since deletion is serialized by | |
| 1301 | * BRIDGE_CFGPORT | |
| 1302 | */ | |
| 1303 | ||
| 70d9a675 | 1304 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { |
| ac93838f SS |
1305 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 1306 | continue; | |
| 1307 | /* | |
| 1308 | * XXX This can cause a lag in "link does away" | |
| 1309 | * XXX and "spanning tree gets updated". We need | |
| 1310 | * XXX come sort of callback from the link state | |
| 1311 | * XXX update code to kick spanning tree. | |
| 1312 | * XXX --thorpej@NetBSD.org | |
| 1313 | */ | |
| 1314 | bstp_ifupdstatus(sc, bif); | |
| 1315 | } | |
| ac93838f SS |
1316 | |
| 1317 | if (bstp_timer_expired(&sc->sc_hello_timer, sc->sc_hello_time)) | |
| 1318 | bstp_hello_timer_expiry(sc); | |
| 1319 | ||
| 1320 | if (bstp_timer_expired(&sc->sc_tcn_timer, sc->sc_bridge_hello_time)) | |
| 1321 | bstp_tcn_timer_expiry(sc); | |
| 1322 | ||
| 1323 | if (bstp_timer_expired(&sc->sc_topology_change_timer, | |
| 1324 | sc->sc_topology_change_time)) | |
| 1325 | bstp_topology_change_timer_expiry(sc); | |
| 1326 | ||
| 70d9a675 | 1327 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { |
| ac93838f SS |
1328 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 1329 | continue; | |
| 1330 | if (bstp_timer_expired(&bif->bif_message_age_timer, | |
| 1331 | sc->sc_max_age)) | |
| 1332 | bstp_message_age_timer_expiry(sc, bif); | |
| 1333 | } | |
| 1334 | ||
| 70d9a675 | 1335 | TAILQ_FOREACH(bif, &sc->sc_iflists[mycpuid], bif_next) { |
| ac93838f SS |
1336 | if ((bif->bif_flags & IFBIF_STP) == 0) |
| 1337 | continue; | |
| 1338 | if (bstp_timer_expired(&bif->bif_forward_delay_timer, | |
| 1339 | sc->sc_forward_delay)) | |
| 1340 | bstp_forward_delay_timer_expiry(sc, bif); | |
| 1341 | ||
| 1342 | if (bstp_timer_expired(&bif->bif_hold_timer, | |
| 1343 | sc->sc_hold_time)) | |
| 1344 | bstp_hold_timer_expiry(sc, bif); | |
| 3677aae9 MD |
1345 | |
| 1346 | if (bstp_timer_expired(&bif->bif_link1_timer, | |
| 1347 | sc->sc_hello_time * 10)) | |
| 1348 | bstp_link1_timer_expiry(sc, bif); | |
| ac93838f SS |
1349 | } |
| 1350 | ||
| 1351 | if (sc->sc_ifp->if_flags & IFF_RUNNING) | |
| 1352 | callout_reset(&sc->sc_bstpcallout, hz, bstp_tick, sc); | |
| 1353 | ||
| a3dd34d2 | 1354 | ifnet_deserialize_all(sc->sc_ifp); |
| ac93838f SS |
1355 | } |
| 1356 | ||
| d217f5e5 | 1357 | static void |
| ac93838f SS |
1358 | bstp_timer_start(struct bridge_timer *t, uint16_t v) |
| 1359 | { | |
| 1360 | t->value = v; | |
| 1361 | t->active = 1; | |
| 1362 | } | |
| 1363 | ||
| d217f5e5 | 1364 | static void |
| ac93838f SS |
1365 | bstp_timer_stop(struct bridge_timer *t) |
| 1366 | { | |
| 1367 | t->value = 0; | |
| 1368 | t->active = 0; | |
| 1369 | } | |
| 1370 | ||
| d217f5e5 | 1371 | static int |
| ac93838f SS |
1372 | bstp_timer_expired(struct bridge_timer *t, uint16_t v) |
| 1373 | { | |
| 1374 | if (t->active == 0) | |
| 1375 | return (0); | |
| 1376 | t->value += BSTP_TICK_VAL; | |
| 1377 | if (t->value >= v) { | |
| 1378 | bstp_timer_stop(t); | |
| 1379 | return (1); | |
| 1380 | } | |
| 1381 | return (0); | |
| 1382 | ||
| 1383 | } |