xref: /linux/net/bridge/br_stp_if.c (revision 11dc1f36a6701b502ecb695f308aae46ede8bac6)
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  *	$Id: br_stp_if.c,v 1.4 2001/04/14 21:14:39 davem Exp $
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *	This program is free software; you can redistribute it and/or
111da177e4SLinus Torvalds  *	modify it under the terms of the GNU General Public License
121da177e4SLinus Torvalds  *	as published by the Free Software Foundation; either version
131da177e4SLinus Torvalds  *	2 of the License, or (at your option) any later version.
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/smp_lock.h>
186ede2463SStephen Hemminger #include <linux/etherdevice.h>
19*11dc1f36SStephen Hemminger #include <linux/rtnetlink.h>
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include "br_private.h"
221da177e4SLinus Torvalds #include "br_private_stp.h"
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds /* Port id is composed of priority and port number.
261da177e4SLinus Torvalds  * NB: least significant bits of priority are dropped to
271da177e4SLinus Torvalds  *     make room for more ports.
281da177e4SLinus Torvalds  */
291da177e4SLinus Torvalds static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
301da177e4SLinus Torvalds {
311da177e4SLinus Torvalds 	return ((u16)priority << BR_PORT_BITS)
321da177e4SLinus Torvalds 		| (port_no & ((1<<BR_PORT_BITS)-1));
331da177e4SLinus Torvalds }
341da177e4SLinus Torvalds 
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);
90*11dc1f36SStephen Hemminger 	br_ifinfo_notify(RTM_NEWLINK, p);
911da177e4SLinus Torvalds 	br_port_state_selection(p->br);
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds /* called under bridge lock */
951da177e4SLinus Torvalds void br_stp_disable_port(struct net_bridge_port *p)
961da177e4SLinus Torvalds {
971da177e4SLinus Torvalds 	struct net_bridge *br;
981da177e4SLinus Torvalds 	int wasroot;
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds 	br = p->br;
1011da177e4SLinus Torvalds 	printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
1021da177e4SLinus Torvalds 	       br->dev->name, p->port_no, p->dev->name, "disabled");
1031da177e4SLinus Torvalds 
104*11dc1f36SStephen Hemminger 	br_ifinfo_notify(RTM_DELLINK, p);
105*11dc1f36SStephen Hemminger 
1061da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1071da177e4SLinus Torvalds 	br_become_designated_port(p);
1081da177e4SLinus Torvalds 	p->state = BR_STATE_DISABLED;
1091da177e4SLinus Torvalds 	p->topology_change_ack = 0;
1101da177e4SLinus Torvalds 	p->config_pending = 0;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	del_timer(&p->message_age_timer);
1131da177e4SLinus Torvalds 	del_timer(&p->forward_delay_timer);
1141da177e4SLinus Torvalds 	del_timer(&p->hold_timer);
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	br_configuration_update(br);
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	br_port_state_selection(br);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1211da177e4SLinus Torvalds 		br_become_root_bridge(br);
1221da177e4SLinus Torvalds }
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /* called under bridge lock */
1254505a3efSStephen Hemminger void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
1261da177e4SLinus Torvalds {
1271da177e4SLinus Torvalds 	unsigned char oldaddr[6];
1281da177e4SLinus Torvalds 	struct net_bridge_port *p;
1291da177e4SLinus Torvalds 	int wasroot;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
1341da177e4SLinus Torvalds 	memcpy(br->bridge_id.addr, addr, ETH_ALEN);
1351da177e4SLinus Torvalds 	memcpy(br->dev->dev_addr, addr, ETH_ALEN);
1361da177e4SLinus Torvalds 
1371da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1386ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
1391da177e4SLinus Torvalds 			memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
1401da177e4SLinus Torvalds 
1416ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_root.addr, oldaddr))
1421da177e4SLinus Torvalds 			memcpy(p->designated_root.addr, addr, ETH_ALEN);
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	}
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	br_configuration_update(br);
1471da177e4SLinus Torvalds 	br_port_state_selection(br);
1481da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1491da177e4SLinus Torvalds 		br_become_root_bridge(br);
1501da177e4SLinus Torvalds }
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds static const unsigned char br_mac_zero[6];
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* called under bridge lock */
1551da177e4SLinus Torvalds void br_stp_recalculate_bridge_id(struct net_bridge *br)
1561da177e4SLinus Torvalds {
1571da177e4SLinus Torvalds 	const unsigned char *addr = br_mac_zero;
1581da177e4SLinus Torvalds 	struct net_bridge_port *p;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1611da177e4SLinus Torvalds 		if (addr == br_mac_zero ||
162554c9a8eSStephen Hemminger 		    memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
1631da177e4SLinus Torvalds 			addr = p->dev->dev_addr;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	}
1661da177e4SLinus Torvalds 
1676ede2463SStephen Hemminger 	if (compare_ether_addr(br->bridge_id.addr, addr))
1681da177e4SLinus Torvalds 		br_stp_change_bridge_id(br, addr);
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds /* called under bridge lock */
1721da177e4SLinus Torvalds void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
1731da177e4SLinus Torvalds {
1741da177e4SLinus Torvalds 	struct net_bridge_port *p;
1751da177e4SLinus Torvalds 	int wasroot;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1801da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED &&
1811da177e4SLinus Torvalds 		    br_is_designated_port(p)) {
1821da177e4SLinus Torvalds 			p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
1831da177e4SLinus Torvalds 			p->designated_bridge.prio[1] = newprio & 0xFF;
1841da177e4SLinus Torvalds 		}
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds 	br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
1891da177e4SLinus Torvalds 	br->bridge_id.prio[1] = newprio & 0xFF;
1901da177e4SLinus Torvalds 	br_configuration_update(br);
1911da177e4SLinus Torvalds 	br_port_state_selection(br);
1921da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1931da177e4SLinus Torvalds 		br_become_root_bridge(br);
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds /* called under bridge lock */
1971da177e4SLinus Torvalds void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds 	port_id new_port_id = br_make_port_id(newprio, p->port_no);
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds 	if (br_is_designated_port(p))
2021da177e4SLinus Torvalds 		p->designated_port = new_port_id;
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	p->port_id = new_port_id;
2051da177e4SLinus Torvalds 	p->priority = newprio;
2061da177e4SLinus Torvalds 	if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
2071da177e4SLinus Torvalds 	    p->port_id < p->designated_port) {
2081da177e4SLinus Torvalds 		br_become_designated_port(p);
2091da177e4SLinus Torvalds 		br_port_state_selection(p->br);
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds /* called under bridge lock */
2141da177e4SLinus Torvalds void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	p->path_cost = path_cost;
2171da177e4SLinus Torvalds 	br_configuration_update(p->br);
2181da177e4SLinus Torvalds 	br_port_state_selection(p->br);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds 	return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
2241da177e4SLinus Torvalds 	       id->prio[0], id->prio[1],
2251da177e4SLinus Torvalds 	       id->addr[0], id->addr[1], id->addr[2],
2261da177e4SLinus Torvalds 	       id->addr[3], id->addr[4], id->addr[5]);
2271da177e4SLinus Torvalds }
228