xref: /linux/net/bridge/br_stp_if.c (revision 1a9180a20f3a314fda3e96b77570cad3864b2896)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *	Spanning tree protocol; interface code
31da177e4SLinus Torvalds  *	Linux ethernet bridge
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *	Authors:
61da177e4SLinus Torvalds  *	Lennert Buytenhek		<buytenh@gnu.org>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
91da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
101da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
111da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
121da177e4SLinus Torvalds  */
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds #include <linux/kernel.h>
156ede2463SStephen Hemminger #include <linux/etherdevice.h>
1611dc1f36SStephen Hemminger #include <linux/rtnetlink.h>
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds #include "br_private.h"
191da177e4SLinus Torvalds #include "br_private_stp.h"
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds /* Port id is composed of priority and port number.
231da177e4SLinus Torvalds  * NB: least significant bits of priority are dropped to
241da177e4SLinus Torvalds  *     make room for more ports.
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
271da177e4SLinus Torvalds {
281da177e4SLinus Torvalds 	return ((u16)priority << BR_PORT_BITS)
291da177e4SLinus Torvalds 		| (port_no & ((1<<BR_PORT_BITS)-1));
301da177e4SLinus Torvalds }
311da177e4SLinus Torvalds 
321da177e4SLinus Torvalds /* called under bridge lock */
331da177e4SLinus Torvalds void br_init_port(struct net_bridge_port *p)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds 	p->port_id = br_make_port_id(p->priority, p->port_no);
361da177e4SLinus Torvalds 	br_become_designated_port(p);
371da177e4SLinus Torvalds 	p->state = BR_STATE_BLOCKING;
381da177e4SLinus Torvalds 	p->topology_change_ack = 0;
391da177e4SLinus Torvalds 	p->config_pending = 0;
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds /* called under bridge lock */
431da177e4SLinus Torvalds void br_stp_enable_bridge(struct net_bridge *br)
441da177e4SLinus Torvalds {
451da177e4SLinus Torvalds 	struct net_bridge_port *p;
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds 	spin_lock_bh(&br->lock);
481da177e4SLinus Torvalds 	mod_timer(&br->hello_timer, jiffies + br->hello_time);
491da177e4SLinus Torvalds 	mod_timer(&br->gc_timer, jiffies + HZ/10);
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	br_config_bpdu_generation(br);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
541da177e4SLinus Torvalds 		if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev))
551da177e4SLinus Torvalds 			br_stp_enable_port(p);
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	}
581da177e4SLinus Torvalds 	spin_unlock_bh(&br->lock);
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /* NO locks held */
621da177e4SLinus Torvalds void br_stp_disable_bridge(struct net_bridge *br)
631da177e4SLinus Torvalds {
641da177e4SLinus Torvalds 	struct net_bridge_port *p;
651da177e4SLinus Torvalds 
6678872ccbSAdrian Drzewiecki 	spin_lock_bh(&br->lock);
671da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
681da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED)
691da177e4SLinus Torvalds 			br_stp_disable_port(p);
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	}
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds 	br->topology_change = 0;
741da177e4SLinus Torvalds 	br->topology_change_detected = 0;
7578872ccbSAdrian Drzewiecki 	spin_unlock_bh(&br->lock);
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	del_timer_sync(&br->hello_timer);
781da177e4SLinus Torvalds 	del_timer_sync(&br->topology_change_timer);
791da177e4SLinus Torvalds 	del_timer_sync(&br->tcn_timer);
801da177e4SLinus Torvalds 	del_timer_sync(&br->gc_timer);
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds /* called under bridge lock */
841da177e4SLinus Torvalds void br_stp_enable_port(struct net_bridge_port *p)
851da177e4SLinus Torvalds {
861da177e4SLinus Torvalds 	br_init_port(p);
871da177e4SLinus Torvalds 	br_port_state_selection(p->br);
8828a16c97Sstephen hemminger 	br_log_state(p);
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
911da177e4SLinus Torvalds /* called under bridge lock */
921da177e4SLinus Torvalds void br_stp_disable_port(struct net_bridge_port *p)
931da177e4SLinus Torvalds {
9428a16c97Sstephen hemminger 	struct net_bridge *br = p->br;
951da177e4SLinus Torvalds 	int wasroot;
961da177e4SLinus Torvalds 
9728a16c97Sstephen hemminger 	br_log_state(p);
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1001da177e4SLinus Torvalds 	br_become_designated_port(p);
1011da177e4SLinus Torvalds 	p->state = BR_STATE_DISABLED;
1021da177e4SLinus Torvalds 	p->topology_change_ack = 0;
1031da177e4SLinus Torvalds 	p->config_pending = 0;
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	del_timer(&p->message_age_timer);
1061da177e4SLinus Torvalds 	del_timer(&p->forward_delay_timer);
1071da177e4SLinus Torvalds 	del_timer(&p->hold_timer);
1081da177e4SLinus Torvalds 
1091a620698SStephen Hemminger 	br_fdb_delete_by_port(br, p, 0);
1103fe2d7c7SHerbert Xu 	br_multicast_disable_port(p);
1111a620698SStephen Hemminger 
1121da177e4SLinus Torvalds 	br_configuration_update(br);
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	br_port_state_selection(br);
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1171da177e4SLinus Torvalds 		br_become_root_bridge(br);
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1209cde0708SStephen Hemminger static void br_stp_start(struct net_bridge *br)
1219cde0708SStephen Hemminger {
1229cde0708SStephen Hemminger 	int r;
1239cde0708SStephen Hemminger 	char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
1249cde0708SStephen Hemminger 	char *envp[] = { NULL };
1259cde0708SStephen Hemminger 
12686313c48SJeremy Fitzhardinge 	r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
1279cde0708SStephen Hemminger 	if (r == 0) {
1289cde0708SStephen Hemminger 		br->stp_enabled = BR_USER_STP;
12928a16c97Sstephen hemminger 		br_debug(br, "userspace STP started\n");
1309cde0708SStephen Hemminger 	} else {
1319cde0708SStephen Hemminger 		br->stp_enabled = BR_KERNEL_STP;
13228a16c97Sstephen hemminger 		br_debug(br, "using kernel STP\n");
1339cde0708SStephen Hemminger 
1349cde0708SStephen Hemminger 		/* To start timers on any ports left in blocking */
1359cde0708SStephen Hemminger 		spin_lock_bh(&br->lock);
1369cde0708SStephen Hemminger 		br_port_state_selection(br);
1379cde0708SStephen Hemminger 		spin_unlock_bh(&br->lock);
1389cde0708SStephen Hemminger 	}
1399cde0708SStephen Hemminger }
1409cde0708SStephen Hemminger 
1419cde0708SStephen Hemminger static void br_stp_stop(struct net_bridge *br)
1429cde0708SStephen Hemminger {
1439cde0708SStephen Hemminger 	int r;
1449cde0708SStephen Hemminger 	char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
1459cde0708SStephen Hemminger 	char *envp[] = { NULL };
1469cde0708SStephen Hemminger 
1479cde0708SStephen Hemminger 	if (br->stp_enabled == BR_USER_STP) {
148*1a9180a2STomas Winkler 		r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
14928a16c97Sstephen hemminger 		br_info(br, "userspace STP stopped, return code %d\n", r);
1509cde0708SStephen Hemminger 
1519cde0708SStephen Hemminger 		/* To start timers on any ports left in blocking */
1529cde0708SStephen Hemminger 		spin_lock_bh(&br->lock);
1539cde0708SStephen Hemminger 		br_port_state_selection(br);
1549cde0708SStephen Hemminger 		spin_unlock_bh(&br->lock);
1559cde0708SStephen Hemminger 	}
1569cde0708SStephen Hemminger 
1579cde0708SStephen Hemminger 	br->stp_enabled = BR_NO_STP;
1589cde0708SStephen Hemminger }
1599cde0708SStephen Hemminger 
1609cde0708SStephen Hemminger void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
1619cde0708SStephen Hemminger {
1629cde0708SStephen Hemminger 	ASSERT_RTNL();
1639cde0708SStephen Hemminger 
1649cde0708SStephen Hemminger 	if (val) {
1659cde0708SStephen Hemminger 		if (br->stp_enabled == BR_NO_STP)
1669cde0708SStephen Hemminger 			br_stp_start(br);
1679cde0708SStephen Hemminger 	} else {
1689cde0708SStephen Hemminger 		if (br->stp_enabled != BR_NO_STP)
1699cde0708SStephen Hemminger 			br_stp_stop(br);
1709cde0708SStephen Hemminger 	}
1719cde0708SStephen Hemminger }
1729cde0708SStephen Hemminger 
1731da177e4SLinus Torvalds /* called under bridge lock */
1744505a3efSStephen Hemminger void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
1751da177e4SLinus Torvalds {
17619bb3506SEvgeny Kravtsunov 	/* should be aligned on 2 bytes for compare_ether_addr() */
17719bb3506SEvgeny Kravtsunov 	unsigned short oldaddr_aligned[ETH_ALEN >> 1];
17819bb3506SEvgeny Kravtsunov 	unsigned char *oldaddr = (unsigned char *)oldaddr_aligned;
1791da177e4SLinus Torvalds 	struct net_bridge_port *p;
1801da177e4SLinus Torvalds 	int wasroot;
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds 	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
1851da177e4SLinus Torvalds 	memcpy(br->bridge_id.addr, addr, ETH_ALEN);
1861da177e4SLinus Torvalds 	memcpy(br->dev->dev_addr, addr, ETH_ALEN);
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1896ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
1901da177e4SLinus Torvalds 			memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
1911da177e4SLinus Torvalds 
1926ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_root.addr, oldaddr))
1931da177e4SLinus Torvalds 			memcpy(p->designated_root.addr, addr, ETH_ALEN);
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	br_configuration_update(br);
1981da177e4SLinus Torvalds 	br_port_state_selection(br);
1991da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
2001da177e4SLinus Torvalds 		br_become_root_bridge(br);
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds 
20319bb3506SEvgeny Kravtsunov /* should be aligned on 2 bytes for compare_ether_addr() */
20419bb3506SEvgeny Kravtsunov static const unsigned short br_mac_zero_aligned[ETH_ALEN >> 1];
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds /* called under bridge lock */
2071da177e4SLinus Torvalds void br_stp_recalculate_bridge_id(struct net_bridge *br)
2081da177e4SLinus Torvalds {
20919bb3506SEvgeny Kravtsunov 	const unsigned char *br_mac_zero =
21019bb3506SEvgeny Kravtsunov 			(const unsigned char *)br_mac_zero_aligned;
2111da177e4SLinus Torvalds 	const unsigned char *addr = br_mac_zero;
2121da177e4SLinus Torvalds 	struct net_bridge_port *p;
2131da177e4SLinus Torvalds 
21492c0574fSStephen Hemminger 	/* user has chosen a value so keep it */
21592c0574fSStephen Hemminger 	if (br->flags & BR_SET_MAC_ADDR)
21692c0574fSStephen Hemminger 		return;
21792c0574fSStephen Hemminger 
2181da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
2191da177e4SLinus Torvalds 		if (addr == br_mac_zero ||
220554c9a8eSStephen Hemminger 		    memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
2211da177e4SLinus Torvalds 			addr = p->dev->dev_addr;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds 	}
2241da177e4SLinus Torvalds 
2256ede2463SStephen Hemminger 	if (compare_ether_addr(br->bridge_id.addr, addr))
2261da177e4SLinus Torvalds 		br_stp_change_bridge_id(br, addr);
2271da177e4SLinus Torvalds }
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds /* called under bridge lock */
2301da177e4SLinus Torvalds void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
2311da177e4SLinus Torvalds {
2321da177e4SLinus Torvalds 	struct net_bridge_port *p;
2331da177e4SLinus Torvalds 	int wasroot;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
2381da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED &&
2391da177e4SLinus Torvalds 		    br_is_designated_port(p)) {
2401da177e4SLinus Torvalds 			p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
2411da177e4SLinus Torvalds 			p->designated_bridge.prio[1] = newprio & 0xFF;
2421da177e4SLinus Torvalds 		}
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	}
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
2471da177e4SLinus Torvalds 	br->bridge_id.prio[1] = newprio & 0xFF;
2481da177e4SLinus Torvalds 	br_configuration_update(br);
2491da177e4SLinus Torvalds 	br_port_state_selection(br);
2501da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
2511da177e4SLinus Torvalds 		br_become_root_bridge(br);
2521da177e4SLinus Torvalds }
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds /* called under bridge lock */
2551da177e4SLinus Torvalds void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
2561da177e4SLinus Torvalds {
2571da177e4SLinus Torvalds 	port_id new_port_id = br_make_port_id(newprio, p->port_no);
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds 	if (br_is_designated_port(p))
2601da177e4SLinus Torvalds 		p->designated_port = new_port_id;
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	p->port_id = new_port_id;
2631da177e4SLinus Torvalds 	p->priority = newprio;
2641da177e4SLinus Torvalds 	if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
2651da177e4SLinus Torvalds 	    p->port_id < p->designated_port) {
2661da177e4SLinus Torvalds 		br_become_designated_port(p);
2671da177e4SLinus Torvalds 		br_port_state_selection(p->br);
2681da177e4SLinus Torvalds 	}
2691da177e4SLinus Torvalds }
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds /* called under bridge lock */
2721da177e4SLinus Torvalds void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
2731da177e4SLinus Torvalds {
2741da177e4SLinus Torvalds 	p->path_cost = path_cost;
2751da177e4SLinus Torvalds 	br_configuration_update(p->br);
2761da177e4SLinus Torvalds 	br_port_state_selection(p->br);
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds 
2791da177e4SLinus Torvalds ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
2801da177e4SLinus Torvalds {
2811da177e4SLinus Torvalds 	return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
2821da177e4SLinus Torvalds 	       id->prio[0], id->prio[1],
2831da177e4SLinus Torvalds 	       id->addr[0], id->addr[1], id->addr[2],
2841da177e4SLinus Torvalds 	       id->addr[3], id->addr[4], id->addr[5]);
2851da177e4SLinus Torvalds }
286