xref: /openbmc/linux/net/bridge/br_stp_if.c (revision 6ede2463)
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>
191da177e4SLinus Torvalds 
201da177e4SLinus Torvalds #include "br_private.h"
211da177e4SLinus Torvalds #include "br_private_stp.h"
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds 
241da177e4SLinus Torvalds /* Port id is composed of priority and port number.
251da177e4SLinus Torvalds  * NB: least significant bits of priority are dropped to
261da177e4SLinus Torvalds  *     make room for more ports.
271da177e4SLinus Torvalds  */
281da177e4SLinus Torvalds static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
291da177e4SLinus Torvalds {
301da177e4SLinus Torvalds 	return ((u16)priority << BR_PORT_BITS)
311da177e4SLinus Torvalds 		| (port_no & ((1<<BR_PORT_BITS)-1));
321da177e4SLinus Torvalds }
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds /* called under bridge lock */
351da177e4SLinus Torvalds void br_init_port(struct net_bridge_port *p)
361da177e4SLinus Torvalds {
371da177e4SLinus Torvalds 	p->port_id = br_make_port_id(p->priority, p->port_no);
381da177e4SLinus Torvalds 	br_become_designated_port(p);
391da177e4SLinus Torvalds 	p->state = BR_STATE_BLOCKING;
401da177e4SLinus Torvalds 	p->topology_change_ack = 0;
411da177e4SLinus Torvalds 	p->config_pending = 0;
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds 	br_stp_port_timer_init(p);
441da177e4SLinus Torvalds }
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds /* called under bridge lock */
471da177e4SLinus Torvalds void br_stp_enable_bridge(struct net_bridge *br)
481da177e4SLinus Torvalds {
491da177e4SLinus Torvalds 	struct net_bridge_port *p;
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds 	spin_lock_bh(&br->lock);
521da177e4SLinus Torvalds 	mod_timer(&br->hello_timer, jiffies + br->hello_time);
531da177e4SLinus Torvalds 	mod_timer(&br->gc_timer, jiffies + HZ/10);
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	br_config_bpdu_generation(br);
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
581da177e4SLinus Torvalds 		if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev))
591da177e4SLinus Torvalds 			br_stp_enable_port(p);
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds 	}
621da177e4SLinus Torvalds 	spin_unlock_bh(&br->lock);
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds 
651da177e4SLinus Torvalds /* NO locks held */
661da177e4SLinus Torvalds void br_stp_disable_bridge(struct net_bridge *br)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds 	struct net_bridge_port *p;
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	spin_lock(&br->lock);
711da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
721da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED)
731da177e4SLinus Torvalds 			br_stp_disable_port(p);
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	}
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	br->topology_change = 0;
781da177e4SLinus Torvalds 	br->topology_change_detected = 0;
791da177e4SLinus Torvalds 	spin_unlock(&br->lock);
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	del_timer_sync(&br->hello_timer);
821da177e4SLinus Torvalds 	del_timer_sync(&br->topology_change_timer);
831da177e4SLinus Torvalds 	del_timer_sync(&br->tcn_timer);
841da177e4SLinus Torvalds 	del_timer_sync(&br->gc_timer);
851da177e4SLinus Torvalds }
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /* called under bridge lock */
881da177e4SLinus Torvalds void br_stp_enable_port(struct net_bridge_port *p)
891da177e4SLinus Torvalds {
901da177e4SLinus Torvalds 	br_init_port(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 
1041da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1051da177e4SLinus Torvalds 	br_become_designated_port(p);
1061da177e4SLinus Torvalds 	p->state = BR_STATE_DISABLED;
1071da177e4SLinus Torvalds 	p->topology_change_ack = 0;
1081da177e4SLinus Torvalds 	p->config_pending = 0;
1091da177e4SLinus Torvalds 
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 
1141da177e4SLinus Torvalds 	br_configuration_update(br);
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds 	br_port_state_selection(br);
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1191da177e4SLinus Torvalds 		br_become_root_bridge(br);
1201da177e4SLinus Torvalds }
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds /* called under bridge lock */
1231da177e4SLinus Torvalds static void br_stp_change_bridge_id(struct net_bridge *br,
1241da177e4SLinus Torvalds 				    const unsigned char *addr)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds 	unsigned char oldaddr[6];
1271da177e4SLinus Torvalds 	struct net_bridge_port *p;
1281da177e4SLinus Torvalds 	int wasroot;
1291da177e4SLinus Torvalds 
1301da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds 	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
1331da177e4SLinus Torvalds 	memcpy(br->bridge_id.addr, addr, ETH_ALEN);
1341da177e4SLinus Torvalds 	memcpy(br->dev->dev_addr, addr, ETH_ALEN);
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1376ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
1381da177e4SLinus Torvalds 			memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
1391da177e4SLinus Torvalds 
1406ede2463SStephen Hemminger 		if (!compare_ether_addr(p->designated_root.addr, oldaddr))
1411da177e4SLinus Torvalds 			memcpy(p->designated_root.addr, addr, ETH_ALEN);
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	}
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	br_configuration_update(br);
1461da177e4SLinus Torvalds 	br_port_state_selection(br);
1471da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1481da177e4SLinus Torvalds 		br_become_root_bridge(br);
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds static const unsigned char br_mac_zero[6];
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds /* called under bridge lock */
1541da177e4SLinus Torvalds void br_stp_recalculate_bridge_id(struct net_bridge *br)
1551da177e4SLinus Torvalds {
1561da177e4SLinus Torvalds 	const unsigned char *addr = br_mac_zero;
1571da177e4SLinus Torvalds 	struct net_bridge_port *p;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1601da177e4SLinus Torvalds 		if (addr == br_mac_zero ||
1616ede2463SStephen Hemminger 		    compare_ether_addr(p->dev->dev_addr, addr) < 0)
1621da177e4SLinus Torvalds 			addr = p->dev->dev_addr;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	}
1651da177e4SLinus Torvalds 
1666ede2463SStephen Hemminger 	if (compare_ether_addr(br->bridge_id.addr, addr))
1671da177e4SLinus Torvalds 		br_stp_change_bridge_id(br, addr);
1681da177e4SLinus Torvalds }
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds /* called under bridge lock */
1711da177e4SLinus Torvalds void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
1721da177e4SLinus Torvalds {
1731da177e4SLinus Torvalds 	struct net_bridge_port *p;
1741da177e4SLinus Torvalds 	int wasroot;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	wasroot = br_is_root_bridge(br);
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	list_for_each_entry(p, &br->port_list, list) {
1791da177e4SLinus Torvalds 		if (p->state != BR_STATE_DISABLED &&
1801da177e4SLinus Torvalds 		    br_is_designated_port(p)) {
1811da177e4SLinus Torvalds 			p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
1821da177e4SLinus Torvalds 			p->designated_bridge.prio[1] = newprio & 0xFF;
1831da177e4SLinus Torvalds 		}
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds 	}
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
1881da177e4SLinus Torvalds 	br->bridge_id.prio[1] = newprio & 0xFF;
1891da177e4SLinus Torvalds 	br_configuration_update(br);
1901da177e4SLinus Torvalds 	br_port_state_selection(br);
1911da177e4SLinus Torvalds 	if (br_is_root_bridge(br) && !wasroot)
1921da177e4SLinus Torvalds 		br_become_root_bridge(br);
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /* called under bridge lock */
1961da177e4SLinus Torvalds void br_stp_set_port_priority(struct net_bridge_port *p, u8 newprio)
1971da177e4SLinus Torvalds {
1981da177e4SLinus Torvalds 	port_id new_port_id = br_make_port_id(newprio, p->port_no);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	if (br_is_designated_port(p))
2011da177e4SLinus Torvalds 		p->designated_port = new_port_id;
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds 	p->port_id = new_port_id;
2041da177e4SLinus Torvalds 	p->priority = newprio;
2051da177e4SLinus Torvalds 	if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
2061da177e4SLinus Torvalds 	    p->port_id < p->designated_port) {
2071da177e4SLinus Torvalds 		br_become_designated_port(p);
2081da177e4SLinus Torvalds 		br_port_state_selection(p->br);
2091da177e4SLinus Torvalds 	}
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds /* called under bridge lock */
2131da177e4SLinus Torvalds void br_stp_set_path_cost(struct net_bridge_port *p, u32 path_cost)
2141da177e4SLinus Torvalds {
2151da177e4SLinus Torvalds 	p->path_cost = path_cost;
2161da177e4SLinus Torvalds 	br_configuration_update(p->br);
2171da177e4SLinus Torvalds 	br_port_state_selection(p->br);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds 	return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x\n",
2231da177e4SLinus Torvalds 	       id->prio[0], id->prio[1],
2241da177e4SLinus Torvalds 	       id->addr[0], id->addr[1], id->addr[2],
2251da177e4SLinus Torvalds 	       id->addr[3], id->addr[4], id->addr[5]);
2261da177e4SLinus Torvalds }
227