xref: /linux/net/bridge/br_stp_if.c (revision 5200959b833ddacf28b6ffce8c331dfd6e0ca797)
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>
1579bb1ee4SPaul Gortmaker #include <linux/kmod.h>
166ede2463SStephen Hemminger #include <linux/etherdevice.h>
1711dc1f36SStephen Hemminger #include <linux/rtnetlink.h>
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include "br_private.h"
201da177e4SLinus Torvalds #include "br_private_stp.h"
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds /* Port id is composed of priority and port number.
2414f98f25Sstephen hemminger  * NB: some bits of priority are dropped to
251da177e4SLinus Torvalds  *     make room for more ports.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
281da177e4SLinus Torvalds {
291da177e4SLinus Torvalds 	return ((u16)priority << BR_PORT_BITS)
301da177e4SLinus Torvalds 		| (port_no & ((1<<BR_PORT_BITS)-1));
311da177e4SLinus Torvalds }
321da177e4SLinus Torvalds 
3314f98f25Sstephen hemminger #define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS)
3414f98f25Sstephen hemminger 
351da177e4SLinus Torvalds /* called under bridge lock */
361da177e4SLinus Torvalds void br_init_port(struct net_bridge_port *p)
371da177e4SLinus Torvalds {
381da177e4SLinus Torvalds 	p->port_id = br_make_port_id(p->priority, p->port_no);
391da177e4SLinus Torvalds 	br_become_designated_port(p);
401da177e4SLinus Torvalds 	p->state = BR_STATE_BLOCKING;
411da177e4SLinus Torvalds 	p->topology_change_ack = 0;
421da177e4SLinus Torvalds 	p->config_pending = 0;
431da177e4SLinus Torvalds }
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /* called under bridge lock */
461da177e4SLinus Torvalds void br_stp_enable_bridge(struct net_bridge *br)
471da177e4SLinus Torvalds {
481da177e4SLinus Torvalds 	struct net_bridge_port *p;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	spin_lock_bh(&br->lock);
511da177e4SLinus Torvalds 	mod_timer(&br->hello_timer, jiffies + br->hello_time);
521da177e4SLinus Torvalds 	mod_timer(&br->gc_timer, jiffies + HZ/10);
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	br_config_bpdu_generation(br);
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
571da177e4SLinus Torvalds 		if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev))
581da177e4SLinus Torvalds 			br_stp_enable_port(p);
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 	spin_unlock_bh(&br->lock);
621da177e4SLinus Torvalds }
631da177e4SLinus Torvalds 
641da177e4SLinus Torvalds /* NO locks held */
651da177e4SLinus Torvalds void br_stp_disable_bridge(struct net_bridge *br)
661da177e4SLinus Torvalds {
671da177e4SLinus Torvalds 	struct net_bridge_port *p;
681da177e4SLinus Torvalds 
6978872ccbSAdrian Drzewiecki 	spin_lock_bh(&br->lock);
701da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
711da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED)
721da177e4SLinus Torvalds 			br_stp_disable_port(p);
731da177e4SLinus Torvalds 
741da177e4SLinus Torvalds 	}
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	br->topology_change = 0;
771da177e4SLinus Torvalds 	br->topology_change_detected = 0;
7878872ccbSAdrian Drzewiecki 	spin_unlock_bh(&br->lock);
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds 	del_timer_sync(&br->hello_timer);
811da177e4SLinus Torvalds 	del_timer_sync(&br->topology_change_timer);
821da177e4SLinus Torvalds 	del_timer_sync(&br->tcn_timer);
831da177e4SLinus Torvalds 	del_timer_sync(&br->gc_timer);
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds 
861da177e4SLinus Torvalds /* called under bridge lock */
871da177e4SLinus Torvalds void br_stp_enable_port(struct net_bridge_port *p)
881da177e4SLinus Torvalds {
891da177e4SLinus Torvalds 	br_init_port(p);
901da177e4SLinus Torvalds 	br_port_state_selection(p->br);
9128a16c97Sstephen hemminger 	br_log_state(p);
924ecb961cSstephen hemminger 	br_ifinfo_notify(RTM_NEWLINK, p);
931da177e4SLinus Torvalds }
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds /* called under bridge lock */
961da177e4SLinus Torvalds void br_stp_disable_port(struct net_bridge_port *p)
971da177e4SLinus Torvalds {
9828a16c97Sstephen hemminger 	struct net_bridge *br = p->br;
991da177e4SLinus Torvalds 	int wasroot;
1001da177e4SLinus Torvalds 
1011da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1021da177e4SLinus Torvalds 	br_become_designated_port(p);
1031da177e4SLinus Torvalds 	p->state = BR_STATE_DISABLED;
1041da177e4SLinus Torvalds 	p->topology_change_ack = 0;
1051da177e4SLinus Torvalds 	p->config_pending = 0;
1061da177e4SLinus Torvalds 
107*5200959bSPaulius Zaleckas 	br_log_state(p);
1084ecb961cSstephen hemminger 	br_ifinfo_notify(RTM_NEWLINK, p);
1094ecb961cSstephen hemminger 
1101da177e4SLinus Torvalds 	del_timer(&p->message_age_timer);
1111da177e4SLinus Torvalds 	del_timer(&p->forward_delay_timer);
1121da177e4SLinus Torvalds 	del_timer(&p->hold_timer);
1131da177e4SLinus Torvalds 
1141a620698SStephen Hemminger 	br_fdb_delete_by_port(br, p, 0);
1153fe2d7c7SHerbert Xu 	br_multicast_disable_port(p);
1161a620698SStephen Hemminger 
1171da177e4SLinus Torvalds 	br_configuration_update(br);
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds 	br_port_state_selection(br);
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1221da177e4SLinus Torvalds 		br_become_root_bridge(br);
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
1259cde0708SStephen Hemminger static void br_stp_start(struct net_bridge *br)
1269cde0708SStephen Hemminger {
1279cde0708SStephen Hemminger 	int r;
1289cde0708SStephen Hemminger 	char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
1299cde0708SStephen Hemminger 	char *envp[] = { NULL };
1309cde0708SStephen Hemminger 
13186313c48SJeremy Fitzhardinge 	r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
1329cde0708SStephen Hemminger 	if (r == 0) {
1339cde0708SStephen Hemminger 		br->stp_enabled = BR_USER_STP;
13428a16c97Sstephen hemminger 		br_debug(br, "userspace STP started\n");
1359cde0708SStephen Hemminger 	} else {
1369cde0708SStephen Hemminger 		br->stp_enabled = BR_KERNEL_STP;
13728a16c97Sstephen hemminger 		br_debug(br, "using kernel STP\n");
1389cde0708SStephen Hemminger 
1399cde0708SStephen Hemminger 		/* To start timers on any ports left in blocking */
1409cde0708SStephen Hemminger 		spin_lock_bh(&br->lock);
1419cde0708SStephen Hemminger 		br_port_state_selection(br);
1429cde0708SStephen Hemminger 		spin_unlock_bh(&br->lock);
1439cde0708SStephen Hemminger 	}
1449cde0708SStephen Hemminger }
1459cde0708SStephen Hemminger 
1469cde0708SStephen Hemminger static void br_stp_stop(struct net_bridge *br)
1479cde0708SStephen Hemminger {
1489cde0708SStephen Hemminger 	int r;
1499cde0708SStephen Hemminger 	char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
1509cde0708SStephen Hemminger 	char *envp[] = { NULL };
1519cde0708SStephen Hemminger 
1529cde0708SStephen Hemminger 	if (br->stp_enabled == BR_USER_STP) {
1531a9180a2STomas Winkler 		r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
15428a16c97Sstephen hemminger 		br_info(br, "userspace STP stopped, return code %d\n", r);
1559cde0708SStephen Hemminger 
1569cde0708SStephen Hemminger 		/* To start timers on any ports left in blocking */
1579cde0708SStephen Hemminger 		spin_lock_bh(&br->lock);
1589cde0708SStephen Hemminger 		br_port_state_selection(br);
1599cde0708SStephen Hemminger 		spin_unlock_bh(&br->lock);
1609cde0708SStephen Hemminger 	}
1619cde0708SStephen Hemminger 
1629cde0708SStephen Hemminger 	br->stp_enabled = BR_NO_STP;
1639cde0708SStephen Hemminger }
1649cde0708SStephen Hemminger 
1659cde0708SStephen Hemminger void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
1669cde0708SStephen Hemminger {
1679cde0708SStephen Hemminger 	ASSERT_RTNL();
1689cde0708SStephen Hemminger 
1699cde0708SStephen Hemminger 	if (val) {
1709cde0708SStephen Hemminger 		if (br->stp_enabled == BR_NO_STP)
1719cde0708SStephen Hemminger 			br_stp_start(br);
1729cde0708SStephen Hemminger 	} else {
1739cde0708SStephen Hemminger 		if (br->stp_enabled != BR_NO_STP)
1749cde0708SStephen Hemminger 			br_stp_stop(br);
1759cde0708SStephen Hemminger 	}
1769cde0708SStephen Hemminger }
1779cde0708SStephen Hemminger 
1781da177e4SLinus Torvalds /* called under bridge lock */
1794505a3efSStephen Hemminger void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
1801da177e4SLinus Torvalds {
18119bb3506SEvgeny Kravtsunov 	/* should be aligned on 2 bytes for compare_ether_addr() */
18219bb3506SEvgeny Kravtsunov 	unsigned short oldaddr_aligned[ETH_ALEN >> 1];
18319bb3506SEvgeny Kravtsunov 	unsigned char *oldaddr = (unsigned char *)oldaddr_aligned;
1841da177e4SLinus Torvalds 	struct net_bridge_port *p;
1851da177e4SLinus Torvalds 	int wasroot;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
1901da177e4SLinus Torvalds 	memcpy(br->bridge_id.addr, addr, ETH_ALEN);
1911da177e4SLinus Torvalds 	memcpy(br->dev->dev_addr, addr, ETH_ALEN);
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1946ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
1951da177e4SLinus Torvalds 			memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
1961da177e4SLinus Torvalds 
1976ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_root.addr, oldaddr))
1981da177e4SLinus Torvalds 			memcpy(p->designated_root.addr, addr, ETH_ALEN);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	}
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	br_configuration_update(br);
2031da177e4SLinus Torvalds 	br_port_state_selection(br);
2041da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
2051da177e4SLinus Torvalds 		br_become_root_bridge(br);
2061da177e4SLinus Torvalds }
2071da177e4SLinus Torvalds 
20819bb3506SEvgeny Kravtsunov /* should be aligned on 2 bytes for compare_ether_addr() */
20919bb3506SEvgeny Kravtsunov static const unsigned short br_mac_zero_aligned[ETH_ALEN >> 1];
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds /* called under bridge lock */
212edf947f1Sstephen hemminger bool br_stp_recalculate_bridge_id(struct net_bridge *br)
2131da177e4SLinus Torvalds {
21419bb3506SEvgeny Kravtsunov 	const unsigned char *br_mac_zero =
21519bb3506SEvgeny Kravtsunov 			(const unsigned char *)br_mac_zero_aligned;
2161da177e4SLinus Torvalds 	const unsigned char *addr = br_mac_zero;
2171da177e4SLinus Torvalds 	struct net_bridge_port *p;
2181da177e4SLinus Torvalds 
21992c0574fSStephen Hemminger 	/* user has chosen a value so keep it */
22092c0574fSStephen Hemminger 	if (br->flags & BR_SET_MAC_ADDR)
2211459a3ccSBalaji G 		return false;
22292c0574fSStephen Hemminger 
2231da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
2241da177e4SLinus Torvalds 		if (addr == br_mac_zero ||
225554c9a8eSStephen Hemminger 		    memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
2261da177e4SLinus Torvalds 			addr = p->dev->dev_addr;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds 	}
2291da177e4SLinus Torvalds 
230edf947f1Sstephen hemminger 	if (compare_ether_addr(br->bridge_id.addr, addr) == 0)
231edf947f1Sstephen hemminger 		return false;	/* no change */
232edf947f1Sstephen hemminger 
2331da177e4SLinus Torvalds 	br_stp_change_bridge_id(br, addr);
234edf947f1Sstephen hemminger 	return true;
2351da177e4SLinus Torvalds }
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds /* called under bridge lock */
2381da177e4SLinus Torvalds void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
2391da177e4SLinus Torvalds {
2401da177e4SLinus Torvalds 	struct net_bridge_port *p;
2411da177e4SLinus Torvalds 	int wasroot;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
2461da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED &&
2471da177e4SLinus Torvalds 		    br_is_designated_port(p)) {
2481da177e4SLinus Torvalds 			p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
2491da177e4SLinus Torvalds 			p->designated_bridge.prio[1] = newprio & 0xFF;
2501da177e4SLinus Torvalds 		}
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	}
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
2551da177e4SLinus Torvalds 	br->bridge_id.prio[1] = newprio & 0xFF;
2561da177e4SLinus Torvalds 	br_configuration_update(br);
2571da177e4SLinus Torvalds 	br_port_state_selection(br);
2581da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
2591da177e4SLinus Torvalds 		br_become_root_bridge(br);
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds /* called under bridge lock */
26314f98f25Sstephen hemminger int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio)
2641da177e4SLinus Torvalds {
26514f98f25Sstephen hemminger 	port_id new_port_id;
2661da177e4SLinus Torvalds 
26714f98f25Sstephen hemminger 	if (newprio > BR_MAX_PORT_PRIORITY)
26814f98f25Sstephen hemminger 		return -ERANGE;
26914f98f25Sstephen hemminger 
27014f98f25Sstephen hemminger 	new_port_id = br_make_port_id(newprio, p->port_no);
2711da177e4SLinus Torvalds 	if (br_is_designated_port(p))
2721da177e4SLinus Torvalds 		p->designated_port = new_port_id;
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	p->port_id = new_port_id;
2751da177e4SLinus Torvalds 	p->priority = newprio;
2761da177e4SLinus Torvalds 	if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
2771da177e4SLinus Torvalds 	    p->port_id < p->designated_port) {
2781da177e4SLinus Torvalds 		br_become_designated_port(p);
2791da177e4SLinus Torvalds 		br_port_state_selection(p->br);
2801da177e4SLinus Torvalds 	}
28114f98f25Sstephen hemminger 
28214f98f25Sstephen hemminger 	return 0;
2831da177e4SLinus Torvalds }
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds /* called under bridge lock */
28614f98f25Sstephen hemminger int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost)
2871da177e4SLinus Torvalds {
28814f98f25Sstephen hemminger 	if (path_cost < BR_MIN_PATH_COST ||
28914f98f25Sstephen hemminger 	    path_cost > BR_MAX_PATH_COST)
29014f98f25Sstephen hemminger 		return -ERANGE;
29114f98f25Sstephen hemminger 
2921da177e4SLinus Torvalds 	p->path_cost = path_cost;
2931da177e4SLinus Torvalds 	br_configuration_update(p->br);
2941da177e4SLinus Torvalds 	br_port_state_selection(p->br);
29514f98f25Sstephen hemminger 	return 0;
2961da177e4SLinus Torvalds }
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
3011da177e4SLinus Torvalds 	       id->prio[0], id->prio[1],
3021da177e4SLinus Torvalds 	       id->addr[0], id->addr[1], id->addr[2],
3031da177e4SLinus Torvalds 	       id->addr[3], id->addr[4], id->addr[5]);
3041da177e4SLinus Torvalds }
305