Blame view

net/bridge/br_stp_if.c 7.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *	Spanning tree protocol; interface code
   *	Linux ethernet bridge
   *
   *	Authors:
   *	Lennert Buytenhek		<buytenh@gnu.org>
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
14
   *	This program is free software; you can redistribute it and/or
   *	modify it under the terms of the GNU General Public License
   *	as published by the Free Software Foundation; either version
   *	2 of the License, or (at your option) any later version.
   */
  
  #include <linux/kernel.h>
79bb1ee46   Paul Gortmaker   net: fix implicit...
15
  #include <linux/kmod.h>
6ede2463c   Stephen Hemminger   [BRIDGE]: Use eth...
16
  #include <linux/etherdevice.h>
11dc1f36a   Stephen Hemminger   [BRIDGE]: netlink...
17
  #include <linux/rtnetlink.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
22
23
  
  #include "br_private.h"
  #include "br_private_stp.h"
  
  
  /* Port id is composed of priority and port number.
14f98f258   stephen hemminger   bridge: range che...
24
   * NB: some bits of priority are dropped to
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
28
   *     make room for more ports.
   */
  static inline port_id br_make_port_id(__u8 priority, __u16 port_no)
  {
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
29
  	return ((u16)priority << BR_PORT_BITS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
  		| (port_no & ((1<<BR_PORT_BITS)-1));
  }
14f98f258   stephen hemminger   bridge: range che...
32
  #define BR_MAX_PORT_PRIORITY ((u16)~0 >> BR_PORT_BITS)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
39
40
  /* called under bridge lock */
  void br_init_port(struct net_bridge_port *p)
  {
  	p->port_id = br_make_port_id(p->priority, p->port_no);
  	br_become_designated_port(p);
  	p->state = BR_STATE_BLOCKING;
  	p->topology_change_ack = 0;
  	p->config_pending = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
48
49
50
  }
  
  /* called under bridge lock */
  void br_stp_enable_bridge(struct net_bridge *br)
  {
  	struct net_bridge_port *p;
  
  	spin_lock_bh(&br->lock);
  	mod_timer(&br->hello_timer, jiffies + br->hello_time);
  	mod_timer(&br->gc_timer, jiffies + HZ/10);
9d6f229fc   YOSHIFUJI Hideaki   [NET] BRIDGE: Fix...
51

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  	br_config_bpdu_generation(br);
  
  	list_for_each_entry(p, &br->port_list, list) {
  		if ((p->dev->flags & IFF_UP) && netif_carrier_ok(p->dev))
  			br_stp_enable_port(p);
  
  	}
  	spin_unlock_bh(&br->lock);
  }
  
  /* NO locks held */
  void br_stp_disable_bridge(struct net_bridge *br)
  {
  	struct net_bridge_port *p;
78872ccb6   Adrian Drzewiecki   [BRIDGE]: Fix dea...
66
  	spin_lock_bh(&br->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
72
73
74
  	list_for_each_entry(p, &br->port_list, list) {
  		if (p->state != BR_STATE_DISABLED)
  			br_stp_disable_port(p);
  
  	}
  
  	br->topology_change = 0;
  	br->topology_change_detected = 0;
78872ccb6   Adrian Drzewiecki   [BRIDGE]: Fix dea...
75
  	spin_unlock_bh(&br->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
77
78
79
80
81
82
83
84
85
86
87
  
  	del_timer_sync(&br->hello_timer);
  	del_timer_sync(&br->topology_change_timer);
  	del_timer_sync(&br->tcn_timer);
  	del_timer_sync(&br->gc_timer);
  }
  
  /* called under bridge lock */
  void br_stp_enable_port(struct net_bridge_port *p)
  {
  	br_init_port(p);
  	br_port_state_selection(p->br);
28a16c979   stephen hemminger   bridge: change co...
88
  	br_log_state(p);
4ecb961c8   stephen hemminger   bridge: add notif...
89
  	br_ifinfo_notify(RTM_NEWLINK, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
92
93
94
  }
  
  /* called under bridge lock */
  void br_stp_disable_port(struct net_bridge_port *p)
  {
28a16c979   stephen hemminger   bridge: change co...
95
  	struct net_bridge *br = p->br;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  	int wasroot;
28a16c979   stephen hemminger   bridge: change co...
97
  	br_log_state(p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
  
  	wasroot = br_is_root_bridge(br);
  	br_become_designated_port(p);
  	p->state = BR_STATE_DISABLED;
  	p->topology_change_ack = 0;
  	p->config_pending = 0;
4ecb961c8   stephen hemminger   bridge: add notif...
104
  	br_ifinfo_notify(RTM_NEWLINK, p);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105
106
107
  	del_timer(&p->message_age_timer);
  	del_timer(&p->forward_delay_timer);
  	del_timer(&p->hold_timer);
1a620698c   Stephen Hemminger   [BRIDGE]: flush f...
108
  	br_fdb_delete_by_port(br, p, 0);
3fe2d7c70   Herbert Xu   bridge: Add multi...
109
  	br_multicast_disable_port(p);
1a620698c   Stephen Hemminger   [BRIDGE]: flush f...
110

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
114
115
116
117
  	br_configuration_update(br);
  
  	br_port_state_selection(br);
  
  	if (br_is_root_bridge(br) && !wasroot)
  		br_become_root_bridge(br);
  }
9cde07087   Stephen Hemminger   bridge: add suppo...
118
119
120
121
122
  static void br_stp_start(struct net_bridge *br)
  {
  	int r;
  	char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL };
  	char *envp[] = { NULL };
86313c488   Jeremy Fitzhardinge   usermodehelper: T...
123
  	r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
9cde07087   Stephen Hemminger   bridge: add suppo...
124
125
  	if (r == 0) {
  		br->stp_enabled = BR_USER_STP;
28a16c979   stephen hemminger   bridge: change co...
126
127
  		br_debug(br, "userspace STP started
  ");
9cde07087   Stephen Hemminger   bridge: add suppo...
128
129
  	} else {
  		br->stp_enabled = BR_KERNEL_STP;
28a16c979   stephen hemminger   bridge: change co...
130
131
  		br_debug(br, "using kernel STP
  ");
9cde07087   Stephen Hemminger   bridge: add suppo...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
  
  		/* To start timers on any ports left in blocking */
  		spin_lock_bh(&br->lock);
  		br_port_state_selection(br);
  		spin_unlock_bh(&br->lock);
  	}
  }
  
  static void br_stp_stop(struct net_bridge *br)
  {
  	int r;
  	char *argv[] = { BR_STP_PROG, br->dev->name, "stop", NULL };
  	char *envp[] = { NULL };
  
  	if (br->stp_enabled == BR_USER_STP) {
1a9180a20   Tomas Winkler   net/bridge: fix t...
147
  		r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC);
28a16c979   stephen hemminger   bridge: change co...
148
149
  		br_info(br, "userspace STP stopped, return code %d
  ", r);
9cde07087   Stephen Hemminger   bridge: add suppo...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
  
  		/* To start timers on any ports left in blocking */
  		spin_lock_bh(&br->lock);
  		br_port_state_selection(br);
  		spin_unlock_bh(&br->lock);
  	}
  
  	br->stp_enabled = BR_NO_STP;
  }
  
  void br_stp_set_enabled(struct net_bridge *br, unsigned long val)
  {
  	ASSERT_RTNL();
  
  	if (val) {
  		if (br->stp_enabled == BR_NO_STP)
  			br_stp_start(br);
  	} else {
  		if (br->stp_enabled != BR_NO_STP)
  			br_stp_stop(br);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
  /* called under bridge lock */
4505a3ef7   Stephen Hemminger   [BRIDGE]: allow s...
173
  void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *addr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  {
19bb3506e   Evgeny Kravtsunov   [BRIDGE]: Unalign...
175
176
177
  	/* should be aligned on 2 bytes for compare_ether_addr() */
  	unsigned short oldaddr_aligned[ETH_ALEN >> 1];
  	unsigned char *oldaddr = (unsigned char *)oldaddr_aligned;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
180
181
182
183
184
185
186
187
  	struct net_bridge_port *p;
  	int wasroot;
  
  	wasroot = br_is_root_bridge(br);
  
  	memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
  	memcpy(br->bridge_id.addr, addr, ETH_ALEN);
  	memcpy(br->dev->dev_addr, addr, ETH_ALEN);
  
  	list_for_each_entry(p, &br->port_list, list) {
6ede2463c   Stephen Hemminger   [BRIDGE]: Use eth...
188
  		if (!compare_ether_addr(p->designated_bridge.addr, oldaddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  			memcpy(p->designated_bridge.addr, addr, ETH_ALEN);
6ede2463c   Stephen Hemminger   [BRIDGE]: Use eth...
190
  		if (!compare_ether_addr(p->designated_root.addr, oldaddr))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
196
197
198
199
  			memcpy(p->designated_root.addr, addr, ETH_ALEN);
  
  	}
  
  	br_configuration_update(br);
  	br_port_state_selection(br);
  	if (br_is_root_bridge(br) && !wasroot)
  		br_become_root_bridge(br);
  }
19bb3506e   Evgeny Kravtsunov   [BRIDGE]: Unalign...
200
201
  /* should be aligned on 2 bytes for compare_ether_addr() */
  static const unsigned short br_mac_zero_aligned[ETH_ALEN >> 1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
  
  /* called under bridge lock */
edf947f10   stephen hemminger   bridge: notify ap...
204
  bool br_stp_recalculate_bridge_id(struct net_bridge *br)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  {
19bb3506e   Evgeny Kravtsunov   [BRIDGE]: Unalign...
206
207
  	const unsigned char *br_mac_zero =
  			(const unsigned char *)br_mac_zero_aligned;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
  	const unsigned char *addr = br_mac_zero;
  	struct net_bridge_port *p;
92c0574f1   Stephen Hemminger   bridge: make brid...
210
211
  	/* user has chosen a value so keep it */
  	if (br->flags & BR_SET_MAC_ADDR)
1459a3cc5   Balaji G   bridge: Fix compi...
212
  		return false;
92c0574f1   Stephen Hemminger   bridge: make brid...
213

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  	list_for_each_entry(p, &br->port_list, list) {
  		if (addr == br_mac_zero ||
554c9a8ec   Stephen Hemminger   [BRIDGE]: Fix fau...
216
  		    memcmp(p->dev->dev_addr, addr, ETH_ALEN) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
218
219
  			addr = p->dev->dev_addr;
  
  	}
edf947f10   stephen hemminger   bridge: notify ap...
220
221
222
223
224
  	if (compare_ether_addr(br->bridge_id.addr, addr) == 0)
  		return false;	/* no change */
  
  	br_stp_change_bridge_id(br, addr);
  	return true;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  }
  
  /* called under bridge lock */
  void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio)
  {
  	struct net_bridge_port *p;
  	int wasroot;
  
  	wasroot = br_is_root_bridge(br);
  
  	list_for_each_entry(p, &br->port_list, list) {
  		if (p->state != BR_STATE_DISABLED &&
  		    br_is_designated_port(p)) {
  			p->designated_bridge.prio[0] = (newprio >> 8) & 0xFF;
  			p->designated_bridge.prio[1] = newprio & 0xFF;
  		}
  
  	}
  
  	br->bridge_id.prio[0] = (newprio >> 8) & 0xFF;
  	br->bridge_id.prio[1] = newprio & 0xFF;
  	br_configuration_update(br);
  	br_port_state_selection(br);
  	if (br_is_root_bridge(br) && !wasroot)
  		br_become_root_bridge(br);
  }
  
  /* called under bridge lock */
14f98f258   stephen hemminger   bridge: range che...
253
  int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
  {
14f98f258   stephen hemminger   bridge: range che...
255
256
257
258
  	port_id new_port_id;
  
  	if (newprio > BR_MAX_PORT_PRIORITY)
  		return -ERANGE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259

14f98f258   stephen hemminger   bridge: range che...
260
  	new_port_id = br_make_port_id(newprio, p->port_no);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
262
263
264
265
266
267
268
269
270
  	if (br_is_designated_port(p))
  		p->designated_port = new_port_id;
  
  	p->port_id = new_port_id;
  	p->priority = newprio;
  	if (!memcmp(&p->br->bridge_id, &p->designated_bridge, 8) &&
  	    p->port_id < p->designated_port) {
  		br_become_designated_port(p);
  		br_port_state_selection(p->br);
  	}
14f98f258   stephen hemminger   bridge: range che...
271
272
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
274
275
  }
  
  /* called under bridge lock */
14f98f258   stephen hemminger   bridge: range che...
276
  int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
  {
14f98f258   stephen hemminger   bridge: range che...
278
279
280
  	if (path_cost < BR_MIN_PATH_COST ||
  	    path_cost > BR_MAX_PATH_COST)
  		return -ERANGE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
283
  	p->path_cost = path_cost;
  	br_configuration_update(p->br);
  	br_port_state_selection(p->br);
14f98f258   stephen hemminger   bridge: range che...
284
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
286
287
288
289
290
291
292
293
294
  }
  
  ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id)
  {
  	return sprintf(buf, "%.2x%.2x.%.2x%.2x%.2x%.2x%.2x%.2x
  ",
  	       id->prio[0], id->prio[1],
  	       id->addr[0], id->addr[1], id->addr[2],
  	       id->addr[3], id->addr[4], id->addr[5]);
  }