From 3677aae9940f65b6eccefd9b6742704ed7c0582c Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 22 Feb 2011 20:56:42 -0800 Subject: [PATCH] kernel - Greatly enhance if_bridge * Document the link0 feature, which enables transparent bridging. * Implement the link1 feature, automatic failover using a slight mangling of the 802.11d protocol. Both ends must implement the feature for this to work. Essentially this causes CFG 802.11d messages to be generated on the hello interval even if a bridge is not the root bridge. The bridge also monitors for this traffic and places the link in a special L1BLOCKING state if it fails to receive any frames in (10 x hello) (around 20 seconds usually). This will automatically cause the bridge to failover to other links. This only operates on links participating in the STP protocol (see man ifconfig), when link1 is set on the bridge interface. For ethernet bridging the link interfaces are typically multiple TAP interfaces. * Allow all link interfaces participating in a bridge to have the same MAC address (used with TAP interfaces typically). This is mandatory if you also intend to use the link1 feature and want your failover to be reasonably smooth. The feature can be useful regardless. * The ifconfig bridge output now shows additional information about link state and who it thinks the root node is. --- sbin/ifconfig/ifbridge.c | 9 ++++ share/man/man4/bridge.4 | 47 +++++++++++++++-- sys/net/bridge/bridgestp.c | 96 +++++++++++++++++++++++++++++++---- sys/net/bridge/if_bridge.c | 57 +++++++++++++++++++-- sys/net/bridge/if_bridgevar.h | 8 +++ 5 files changed, 202 insertions(+), 15 deletions(-) diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index b01ffcda5d..5f86d6ef55 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -116,6 +116,7 @@ bridge_interfaces(int s, const char *prefix) "learning", "forwarding", "blocking", + "blocking (link1)" }; struct ifbifconf bifc; struct ifbreq *req; @@ -163,6 +164,14 @@ bridge_interfaces(int s, const char *prefix) printf(" ", req->ifbr_state); printf("\n"); + printf("%sdesignated root: %016jx\n", + pad, (intmax_t)req->ifbr_designated_root); + printf("%sdesignated bridge: %016jx\n", + pad, (intmax_t)req->ifbr_designated_bridge); + printf("%sdesignated cost: %u\n", + pad, req->ifbr_designated_cost); + printf("%sdesignated port: %u\n", + pad, req->ifbr_designated_port); } } diff --git a/share/man/man4/bridge.4 b/share/man/man4/bridge.4 index 14f0293e5e..ef7b50f998 100644 --- a/share/man/man4/bridge.4 +++ b/share/man/man4/bridge.4 @@ -2,6 +2,7 @@ .\" All rights reserved. .\" .\" Written by Jason R. Thorpe for Wasabi Systems, Inc. +.\" Spanning tree modifications by Matthew Dillon .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -31,10 +32,8 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.\" $NetBSD: bridge.4,v 1.7 2005/06/23 12:47:38 peter Exp $ -.\" $DragonFly: src/share/man/man4/bridge.4,v 1.10 2007/07/29 17:27:45 swildner Exp $ .\" -.Dd August 30, 2006 +.Dd February 22, 2011 .Dt BRIDGE 4 .Os .Sh NAME @@ -124,6 +123,48 @@ as on the interface on which the packet arrives or departs. .Pp The MTU of the first member interface to be added is used as the bridge MTU, all additional members are required to have exactly the same value. +.Sh EXTRA FEATURES +.Dx +implements two additional features to make spanning tree operation more +resilient. +.Pp +Specifying +.Cm link0 +on the bridge interface places the bridge in transparent bridging mode. +The bridge will make every attempt to retain the original source MAC in +the ethernet link header. +.Pp +Specifying +.Cm link1 +on the bridge interface forces the bridge to generate a 802.11d CFG +message on every hello interval for all interfaces participating +in the STP protocol. +Normally CFG messages are only generated by the root bridge interface +or during topology changes. +In addition the bridge code expects to receive 802.11d frames from +all interface participating in the STP protocol. +.Pp +An interface which fails to receive a 802.11d frame within 10 times +the hello interval (usually 20 seconds) automatically goes into +l1blocking mode, which can be observed in the ifconfig output for +the bridge. This removes the interface from consideration and the +bridge code automatically routes around it. +.Pp +Using +.Cm link0 +and +.Cm link1 +together between two +.Dx +boxes allows you to maintain multiple parallel vpns between those +boxes via different networks (if you happen to be on more than one +with internet access). +Use separate openvpn instances and tap devices for each vpn link +to accomplish this, placing them in the same bridge interface on +the two endpoints. +The tap devices do not need any IP configuration when bridged and +can be assigned the same ether MAC (in fact they have to be +if you want the failover to work nicely). .Sh SEE ALSO .Xr pf 4 , .Xr ifconfig 8 diff --git a/sys/net/bridge/bridgestp.c b/sys/net/bridge/bridgestp.c index 7c7edc5be8..f3a4690286 100644 --- a/sys/net/bridge/bridgestp.c +++ b/sys/net/bridge/bridgestp.c @@ -160,6 +160,8 @@ static void bstp_make_forwarding(struct bridge_softc *, struct bridge_iflist *); static void bstp_make_blocking(struct bridge_softc *, struct bridge_iflist *); +static void bstp_make_l1blocking(struct bridge_softc *sc, + struct bridge_iflist *bif); static void bstp_set_port_state(struct bridge_iflist *, uint8_t); #ifdef notused static void bstp_set_bridge_priority(struct bridge_softc *, uint64_t); @@ -218,7 +220,8 @@ bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif) = bif->bif_topology_change_acknowledge; bif->bif_config_bpdu.cu_topology_change = sc->sc_topology_change; - if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age) { + if (bif->bif_config_bpdu.cu_message_age < sc->sc_max_age || + (sc->sc_ifp->if_flags & IFF_LINK1)) { bif->bif_topology_change_acknowledge = 0; bif->bif_config_pending = 0; bstp_send_config_bpdu(sc, bif, &bif->bif_config_bpdu); @@ -228,7 +231,7 @@ bstp_transmit_config(struct bridge_softc *sc, struct bridge_iflist *bif) static void bstp_send_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, - struct bstp_config_unit *cu) + struct bstp_config_unit *cu) { struct ifnet *ifp; struct mbuf *m; @@ -352,9 +355,11 @@ bstp_config_bpdu_generation(struct bridge_softc *sc) LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) { if ((bif->bif_flags & IFBIF_STP) == 0) continue; - if (bstp_designated_port(sc, bif) && - (bif->bif_state != BSTP_IFSTATE_DISABLED)) + if (bif->bif_state != BSTP_IFSTATE_DISABLED && + ((sc->sc_ifp->if_flags & IFF_LINK1) || + bstp_designated_port(sc, bif))) { bstp_transmit_config(sc, bif); + } if (nbif != NULL && !nbif->bif_onlist) { KKASSERT(bif->bif_onlist); @@ -551,8 +556,9 @@ bstp_make_forwarding(struct bridge_softc *sc, struct bridge_iflist *bif) static void bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif) { - if ((bif->bif_state != BSTP_IFSTATE_DISABLED) && - (bif->bif_state != BSTP_IFSTATE_BLOCKING)) { + if (bif->bif_state != BSTP_IFSTATE_DISABLED && + bif->bif_state != BSTP_IFSTATE_BLOCKING && + bif->bif_state != BSTP_IFSTATE_L1BLOCKING) { if ((bif->bif_state == BSTP_IFSTATE_FORWARDING) || (bif->bif_state == BSTP_IFSTATE_LEARNING)) { if (bif->bif_change_detection_enabled) { @@ -565,6 +571,24 @@ bstp_make_blocking(struct bridge_softc *sc, struct bridge_iflist *bif) } } +static void +bstp_make_l1blocking(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + switch(bif->bif_state) { + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_LEARNING: + case BSTP_IFSTATE_FORWARDING: + case BSTP_IFSTATE_BLOCKING: + bstp_set_port_state(bif, BSTP_IFSTATE_L1BLOCKING); + bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); + bstp_timer_stop(&bif->bif_forward_delay_timer); + bstp_timer_stop(&bif->bif_link1_timer); + break; + default: + break; + } +} + static void bstp_set_port_state(struct bridge_iflist *bif, uint8_t state) { @@ -612,6 +636,19 @@ bstp_input(struct bridge_softc *sc, struct bridge_iflist *bif, struct mbuf *m) if ((bif->bif_flags & IFBIF_STP) == 0) goto out; + /* + * The L1BLOCKING (ping pong failover) test needs to reset the + * timer if LINK1 is active. + */ + if (bif->bif_state == BSTP_IFSTATE_L1BLOCKING) { + bstp_set_port_state(bif, BSTP_IFSTATE_BLOCKING); + if (sc->sc_ifp->if_flags & IFF_LINK1) + bstp_timer_start(&bif->bif_link1_timer, 0); + bstp_make_forwarding(sc, bif); + } else if (sc->sc_ifp->if_flags & IFF_LINK1) { + bstp_timer_start(&bif->bif_link1_timer, 0); + } + eh = mtod(m, struct ether_header *); len = ntohs(eh->ether_type); @@ -700,7 +737,13 @@ bstp_received_config_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, bstp_port_state_selection(sc); if ((bstp_root_bridge(sc) == 0) && root) { - bstp_timer_stop(&sc->sc_hello_timer); + /* + * We continuously transmit hello's if + * link1 is set (topology change bit will + * be zero so they shouldn't propagate). + */ + if ((sc->sc_ifp->if_flags & IFF_LINK1) == 0) + bstp_timer_stop(&sc->sc_hello_timer); if (sc->sc_topology_change_detected) { bstp_timer_stop( @@ -733,16 +776,23 @@ bstp_received_tcn_bpdu(struct bridge_softc *sc, struct bridge_iflist *bif, } } +/* + * link1 forces continuous hello's (the bridge interface must be cycled + * to start them up), so keep the timer hot if that is the case, otherwise + * only send HELLO's if we are the root. + */ static void bstp_hello_timer_expiry(struct bridge_softc *sc) { bstp_config_bpdu_generation(sc); - bstp_timer_start(&sc->sc_hello_timer, 0); + + if ((sc->sc_ifp->if_flags & IFF_LINK1) || bstp_root_bridge(sc)) + bstp_timer_start(&sc->sc_hello_timer, 0); } static void bstp_message_age_timer_expiry(struct bridge_softc *sc, - struct bridge_iflist *bif) + struct bridge_iflist *bif) { int root; @@ -751,6 +801,11 @@ bstp_message_age_timer_expiry(struct bridge_softc *sc, bstp_configuration_update(sc); bstp_port_state_selection(sc); + /* + * If we've become the root and were not the root before + * we have some cleanup to do. This also occurs if we + * wind up being completely isolated. + */ if ((bstp_root_bridge(sc)) && (root == 0)) { sc->sc_max_age = sc->sc_bridge_max_age; sc->sc_hello_time = sc->sc_bridge_hello_time; @@ -814,6 +869,18 @@ bstp_hold_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif) bstp_transmit_config(sc, bif); } +/* + * If no traffic received directly on this port for the specified + * period with link1 set we go into a special blocking mode to + * fail-over traffic to another port. + */ +static void +bstp_link1_timer_expiry(struct bridge_softc *sc, struct bridge_iflist *bif) +{ + if (sc->sc_ifp->if_flags & IFF_LINK1) + bstp_make_l1blocking(sc, bif); +} + static int bstp_addr_cmp(const uint8_t *a, const uint8_t *b) { @@ -885,6 +952,8 @@ bstp_initialization(struct bridge_softc *sc) bstp_tick, sc); LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], bif_next, nbif) { + if (sc->sc_ifp->if_flags & IFF_LINK1) + bstp_timer_start(&bif->bif_link1_timer, 0); if (bif->bif_flags & IFBIF_STP) bstp_ifupdstatus(sc, bif); else @@ -914,6 +983,7 @@ bstp_stop(struct bridge_softc *sc) bstp_timer_stop(&bif->bif_hold_timer); bstp_timer_stop(&bif->bif_message_age_timer); bstp_timer_stop(&bif->bif_forward_delay_timer); + bstp_timer_stop(&bif->bif_link1_timer); } callout_stop(&sc->sc_bstpcallout); @@ -942,12 +1012,15 @@ bstp_initialize_port(struct bridge_softc *sc, struct bridge_iflist *bif) bstp_timer_stop(&bif->bif_message_age_timer); bstp_timer_stop(&bif->bif_forward_delay_timer); bstp_timer_stop(&bif->bif_hold_timer); + bstp_timer_stop(&bif->bif_link1_timer); } static void bstp_enable_port(struct bridge_softc *sc, struct bridge_iflist *bif) { bstp_initialize_port(sc, bif); + if (sc->sc_ifp->if_flags & IFF_LINK1) + bstp_timer_start(&bif->bif_link1_timer, 0); bstp_port_state_selection(sc); } @@ -963,6 +1036,7 @@ bstp_disable_port(struct bridge_softc *sc, struct bridge_iflist *bif) bif->bif_config_pending = 0; bstp_timer_stop(&bif->bif_message_age_timer); bstp_timer_stop(&bif->bif_forward_delay_timer); + bstp_timer_stop(&bif->bif_link1_timer); bstp_configuration_update(sc); bstp_port_state_selection(sc); bridge_rtdelete(sc, bif->bif_ifp, IFBF_FLUSHDYN); @@ -1192,6 +1266,10 @@ bstp_tick_handler(netmsg_t msg) if (bstp_timer_expired(&bif->bif_hold_timer, sc->sc_hold_time)) bstp_hold_timer_expiry(sc, bif); + + if (bstp_timer_expired(&bif->bif_link1_timer, + sc->sc_hello_time * 10)) + bstp_link1_timer_expiry(sc, bif); } if (sc->sc_ifp->if_flags & IFF_RUNNING) diff --git a/sys/net/bridge/if_bridge.c b/sys/net/bridge/if_bridge.c index 3720f5c0d6..a9f35f0de3 100644 --- a/sys/net/bridge/if_bridge.c +++ b/sys/net/bridge/if_bridge.c @@ -1251,6 +1251,10 @@ bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) req->ifbr_priority = bif->bif_priority; req->ifbr_path_cost = bif->bif_path_cost; req->ifbr_portno = bif->bif_ifp->if_index & 0xff; + req->ifbr_designated_root = bif->bif_designated_root; + req->ifbr_designated_bridge = bif->bif_designated_bridge; + req->ifbr_designated_cost = bif->bif_designated_cost; + req->ifbr_designated_port = bif->bif_designated_port; return (0); } @@ -1363,6 +1367,10 @@ bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) breq->ifbr_priority = bif->bif_priority; breq->ifbr_path_cost = bif->bif_path_cost; breq->ifbr_portno = bif->bif_ifp->if_index & 0xff; + breq->ifbr_designated_root = bif->bif_designated_root; + breq->ifbr_designated_bridge = bif->bif_designated_bridge; + breq->ifbr_designated_cost = bif->bif_designated_cost; + breq->ifbr_designated_port = bif->bif_designated_port; breq++; count++; len -= sizeof(*breq); @@ -1841,6 +1849,7 @@ static int bridge_output(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc = ifp->if_bridge; + struct bridge_iflist *bif, *nbif; struct ether_header *eh; struct ifnet *dst_if, *bifp; int from_us; @@ -1887,7 +1896,6 @@ bridge_output(struct ifnet *ifp, struct mbuf *m) else dst_if = bridge_rtlookup(sc, eh->ether_dhost); if (dst_if == NULL) { - struct bridge_iflist *bif, *nbif; struct mbuf *mc; int used = 0; @@ -1909,6 +1917,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m) if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) != 0) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: @@ -1944,8 +1953,44 @@ bridge_output(struct ifnet *ifp, struct mbuf *m) sendunicast: /* - * XXX Spanning tree consideration here? + * If STP is enabled on the target and it is not in a good state + * scan all bridged interfaces for any with a matching MAC which is + * in a good state and use that one. + * + * We need to do this because arp entries tag onto a particular + * interface and if it happens to be dead then the packets will + * go into a bit bucket. */ + bif = bridge_lookup_member_if(sc, dst_if); + if (bif->bif_flags & IFBIF_STP) { + switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: + case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_LISTENING: + case BSTP_IFSTATE_DISABLED: + LIST_FOREACH_MUTABLE(bif, &sc->sc_iflists[mycpuid], + bif_next, nbif) { + if (memcmp(IF_LLADDR(bif->bif_ifp), + IF_LLADDR(dst_if), + ETHER_ADDR_LEN) != 0) { + continue; + } + if (bif->bif_state == BSTP_IFSTATE_L1BLOCKING|| + bif->bif_state == BSTP_IFSTATE_BLOCKING || + bif->bif_state == BSTP_IFSTATE_LISTENING|| + bif->bif_state == BSTP_IFSTATE_DISABLED) { + continue; + } + dst_if = bif->bif_ifp; + break; + } + break; + default: + /* keep dst_if */ + break; + } + } + if (sc->sc_span) bridge_span(sc, m); if ((dst_if->if_flags & IFF_RUNNING) == 0) @@ -2035,6 +2080,7 @@ bridge_forward(struct bridge_softc *sc, struct mbuf *m) if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: @@ -2111,6 +2157,7 @@ bridge_forward(struct bridge_softc *sc, struct mbuf *m) switch (bif->bif_state) { case BSTP_IFSTATE_DISABLED: case BSTP_IFSTATE_BLOCKING: + case BSTP_IFSTATE_L1BLOCKING: m_freem(m); return; } @@ -2238,7 +2285,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) if (m->m_flags & (M_BCAST | M_MCAST)) { /* Tap off 802.1D packets; they do not get forwarded. */ if (memcmp(eh->ether_dhost, bstp_etheraddr, - ETHER_ADDR_LEN) == 0) { + ETHER_ADDR_LEN) == 0) { ifnet_serialize_all(bifp); bstp_input(sc, bif, m); ifnet_deserialize_all(bifp); @@ -2250,6 +2297,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: @@ -2305,6 +2353,7 @@ bridge_input(struct ifnet *ifp, struct mbuf *m) if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_LISTENING: case BSTP_IFSTATE_DISABLED: @@ -2401,6 +2450,7 @@ bridge_start_bcast(struct bridge_softc *sc, struct mbuf *m) if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_DISABLED: continue; @@ -2481,6 +2531,7 @@ bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, if (bif->bif_flags & IFBIF_STP) { switch (bif->bif_state) { + case BSTP_IFSTATE_L1BLOCKING: case BSTP_IFSTATE_BLOCKING: case BSTP_IFSTATE_DISABLED: continue; diff --git a/sys/net/bridge/if_bridgevar.h b/sys/net/bridge/if_bridgevar.h index ab6bb5f4aa..0bb866ff8c 100644 --- a/sys/net/bridge/if_bridgevar.h +++ b/sys/net/bridge/if_bridgevar.h @@ -120,6 +120,11 @@ struct ifbreq { uint8_t ifbr_priority; /* member if STP priority */ uint8_t ifbr_path_cost; /* member if STP cost */ uint8_t ifbr_portno; /* member if port number */ + uint64_t ifbr_designated_root; /* current root id */ + uint64_t ifbr_designated_bridge; /* current bridge id */ + uint32_t ifbr_designated_cost; /* current cost calc */ + uint16_t ifbr_designated_port; /* current port calc */ + uint16_t unused01; }; /* BRDGGIFFLAGS, BRDGSIFFLAGS */ @@ -141,6 +146,7 @@ struct ifbreq { #define BSTP_IFSTATE_LEARNING 2 #define BSTP_IFSTATE_FORWARDING 3 #define BSTP_IFSTATE_BLOCKING 4 +#define BSTP_IFSTATE_L1BLOCKING 5 /* link1 blocking mode no-activity */ /* * Interface list structure. @@ -239,6 +245,7 @@ struct bridge_ifinfo { struct bridge_timer bifi_hold_timer; struct bridge_timer bifi_message_age_timer; struct bridge_timer bifi_forward_delay_timer; + struct bridge_timer bifi_link1_timer; struct bstp_config_unit bifi_config_bpdu; uint16_t bifi_port_id; uint16_t bifi_designated_port; @@ -258,6 +265,7 @@ struct bridge_ifinfo { #define bif_hold_timer bif_info->bifi_hold_timer #define bif_message_age_timer bif_info->bifi_message_age_timer #define bif_forward_delay_timer bif_info->bifi_forward_delay_timer +#define bif_link1_timer bif_info->bifi_link1_timer #define bif_config_bpdu bif_info->bifi_config_bpdu #define bif_port_id bif_info->bifi_port_id #define bif_designated_port bif_info->bifi_designated_port -- 2.41.0