diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/bridge/br_if.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r-- | net/bridge/br_if.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c new file mode 100644 index 00000000000..69872bf3b87 --- /dev/null +++ b/net/bridge/br_if.c | |||
@@ -0,0 +1,388 @@ | |||
1 | /* | ||
2 | * Userspace interface | ||
3 | * Linux ethernet bridge | ||
4 | * | ||
5 | * Authors: | ||
6 | * Lennert Buytenhek <buytenh@gnu.org> | ||
7 | * | ||
8 | * $Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $ | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License | ||
12 | * as published by the Free Software Foundation; either version | ||
13 | * 2 of the License, or (at your option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/netdevice.h> | ||
18 | #include <linux/ethtool.h> | ||
19 | #include <linux/if_arp.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/rtnetlink.h> | ||
23 | #include <net/sock.h> | ||
24 | |||
25 | #include "br_private.h" | ||
26 | |||
27 | /* | ||
28 | * Determine initial path cost based on speed. | ||
29 | * using recommendations from 802.1d standard | ||
30 | * | ||
31 | * Need to simulate user ioctl because not all device's that support | ||
32 | * ethtool, use ethtool_ops. Also, since driver might sleep need to | ||
33 | * not be holding any locks. | ||
34 | */ | ||
35 | static int br_initial_port_cost(struct net_device *dev) | ||
36 | { | ||
37 | |||
38 | struct ethtool_cmd ecmd = { ETHTOOL_GSET }; | ||
39 | struct ifreq ifr; | ||
40 | mm_segment_t old_fs; | ||
41 | int err; | ||
42 | |||
43 | strncpy(ifr.ifr_name, dev->name, IFNAMSIZ); | ||
44 | ifr.ifr_data = (void __user *) &ecmd; | ||
45 | |||
46 | old_fs = get_fs(); | ||
47 | set_fs(KERNEL_DS); | ||
48 | err = dev_ethtool(&ifr); | ||
49 | set_fs(old_fs); | ||
50 | |||
51 | if (!err) { | ||
52 | switch(ecmd.speed) { | ||
53 | case SPEED_100: | ||
54 | return 19; | ||
55 | case SPEED_1000: | ||
56 | return 4; | ||
57 | case SPEED_10000: | ||
58 | return 2; | ||
59 | case SPEED_10: | ||
60 | return 100; | ||
61 | default: | ||
62 | pr_info("bridge: can't decode speed from %s: %d\n", | ||
63 | dev->name, ecmd.speed); | ||
64 | return 100; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | /* Old silly heuristics based on name */ | ||
69 | if (!strncmp(dev->name, "lec", 3)) | ||
70 | return 7; | ||
71 | |||
72 | if (!strncmp(dev->name, "plip", 4)) | ||
73 | return 2500; | ||
74 | |||
75 | return 100; /* assume old 10Mbps */ | ||
76 | } | ||
77 | |||
78 | static void destroy_nbp(struct net_bridge_port *p) | ||
79 | { | ||
80 | struct net_device *dev = p->dev; | ||
81 | |||
82 | dev->br_port = NULL; | ||
83 | p->br = NULL; | ||
84 | p->dev = NULL; | ||
85 | dev_put(dev); | ||
86 | |||
87 | br_sysfs_freeif(p); | ||
88 | } | ||
89 | |||
90 | static void destroy_nbp_rcu(struct rcu_head *head) | ||
91 | { | ||
92 | struct net_bridge_port *p = | ||
93 | container_of(head, struct net_bridge_port, rcu); | ||
94 | destroy_nbp(p); | ||
95 | } | ||
96 | |||
97 | /* called with RTNL */ | ||
98 | static void del_nbp(struct net_bridge_port *p) | ||
99 | { | ||
100 | struct net_bridge *br = p->br; | ||
101 | struct net_device *dev = p->dev; | ||
102 | |||
103 | dev_set_promiscuity(dev, -1); | ||
104 | |||
105 | spin_lock_bh(&br->lock); | ||
106 | br_stp_disable_port(p); | ||
107 | spin_unlock_bh(&br->lock); | ||
108 | |||
109 | br_fdb_delete_by_port(br, p); | ||
110 | |||
111 | list_del_rcu(&p->list); | ||
112 | |||
113 | del_timer_sync(&p->message_age_timer); | ||
114 | del_timer_sync(&p->forward_delay_timer); | ||
115 | del_timer_sync(&p->hold_timer); | ||
116 | |||
117 | call_rcu(&p->rcu, destroy_nbp_rcu); | ||
118 | } | ||
119 | |||
120 | /* called with RTNL */ | ||
121 | static void del_br(struct net_bridge *br) | ||
122 | { | ||
123 | struct net_bridge_port *p, *n; | ||
124 | |||
125 | list_for_each_entry_safe(p, n, &br->port_list, list) { | ||
126 | br_sysfs_removeif(p); | ||
127 | del_nbp(p); | ||
128 | } | ||
129 | |||
130 | del_timer_sync(&br->gc_timer); | ||
131 | |||
132 | br_sysfs_delbr(br->dev); | ||
133 | unregister_netdevice(br->dev); | ||
134 | } | ||
135 | |||
136 | static struct net_device *new_bridge_dev(const char *name) | ||
137 | { | ||
138 | struct net_bridge *br; | ||
139 | struct net_device *dev; | ||
140 | |||
141 | dev = alloc_netdev(sizeof(struct net_bridge), name, | ||
142 | br_dev_setup); | ||
143 | |||
144 | if (!dev) | ||
145 | return NULL; | ||
146 | |||
147 | br = netdev_priv(dev); | ||
148 | br->dev = dev; | ||
149 | |||
150 | spin_lock_init(&br->lock); | ||
151 | INIT_LIST_HEAD(&br->port_list); | ||
152 | spin_lock_init(&br->hash_lock); | ||
153 | |||
154 | br->bridge_id.prio[0] = 0x80; | ||
155 | br->bridge_id.prio[1] = 0x00; | ||
156 | memset(br->bridge_id.addr, 0, ETH_ALEN); | ||
157 | |||
158 | br->stp_enabled = 0; | ||
159 | br->designated_root = br->bridge_id; | ||
160 | br->root_path_cost = 0; | ||
161 | br->root_port = 0; | ||
162 | br->bridge_max_age = br->max_age = 20 * HZ; | ||
163 | br->bridge_hello_time = br->hello_time = 2 * HZ; | ||
164 | br->bridge_forward_delay = br->forward_delay = 15 * HZ; | ||
165 | br->topology_change = 0; | ||
166 | br->topology_change_detected = 0; | ||
167 | br->ageing_time = 300 * HZ; | ||
168 | INIT_LIST_HEAD(&br->age_list); | ||
169 | |||
170 | br_stp_timer_init(br); | ||
171 | |||
172 | return dev; | ||
173 | } | ||
174 | |||
175 | /* find an available port number */ | ||
176 | static int find_portno(struct net_bridge *br) | ||
177 | { | ||
178 | int index; | ||
179 | struct net_bridge_port *p; | ||
180 | unsigned long *inuse; | ||
181 | |||
182 | inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long), | ||
183 | GFP_KERNEL); | ||
184 | if (!inuse) | ||
185 | return -ENOMEM; | ||
186 | |||
187 | memset(inuse, 0, BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long)); | ||
188 | set_bit(0, inuse); /* zero is reserved */ | ||
189 | list_for_each_entry(p, &br->port_list, list) { | ||
190 | set_bit(p->port_no, inuse); | ||
191 | } | ||
192 | index = find_first_zero_bit(inuse, BR_MAX_PORTS); | ||
193 | kfree(inuse); | ||
194 | |||
195 | return (index >= BR_MAX_PORTS) ? -EXFULL : index; | ||
196 | } | ||
197 | |||
198 | /* called with RTNL */ | ||
199 | static struct net_bridge_port *new_nbp(struct net_bridge *br, | ||
200 | struct net_device *dev, | ||
201 | unsigned long cost) | ||
202 | { | ||
203 | int index; | ||
204 | struct net_bridge_port *p; | ||
205 | |||
206 | index = find_portno(br); | ||
207 | if (index < 0) | ||
208 | return ERR_PTR(index); | ||
209 | |||
210 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
211 | if (p == NULL) | ||
212 | return ERR_PTR(-ENOMEM); | ||
213 | |||
214 | memset(p, 0, sizeof(*p)); | ||
215 | p->br = br; | ||
216 | dev_hold(dev); | ||
217 | p->dev = dev; | ||
218 | p->path_cost = cost; | ||
219 | p->priority = 0x8000 >> BR_PORT_BITS; | ||
220 | dev->br_port = p; | ||
221 | p->port_no = index; | ||
222 | br_init_port(p); | ||
223 | p->state = BR_STATE_DISABLED; | ||
224 | kobject_init(&p->kobj); | ||
225 | |||
226 | return p; | ||
227 | } | ||
228 | |||
229 | int br_add_bridge(const char *name) | ||
230 | { | ||
231 | struct net_device *dev; | ||
232 | int ret; | ||
233 | |||
234 | dev = new_bridge_dev(name); | ||
235 | if (!dev) | ||
236 | return -ENOMEM; | ||
237 | |||
238 | rtnl_lock(); | ||
239 | if (strchr(dev->name, '%')) { | ||
240 | ret = dev_alloc_name(dev, dev->name); | ||
241 | if (ret < 0) | ||
242 | goto err1; | ||
243 | } | ||
244 | |||
245 | ret = register_netdevice(dev); | ||
246 | if (ret) | ||
247 | goto err2; | ||
248 | |||
249 | /* network device kobject is not setup until | ||
250 | * after rtnl_unlock does it's hotplug magic. | ||
251 | * so hold reference to avoid race. | ||
252 | */ | ||
253 | dev_hold(dev); | ||
254 | rtnl_unlock(); | ||
255 | |||
256 | ret = br_sysfs_addbr(dev); | ||
257 | dev_put(dev); | ||
258 | |||
259 | if (ret) | ||
260 | unregister_netdev(dev); | ||
261 | out: | ||
262 | return ret; | ||
263 | |||
264 | err2: | ||
265 | free_netdev(dev); | ||
266 | err1: | ||
267 | rtnl_unlock(); | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | int br_del_bridge(const char *name) | ||
272 | { | ||
273 | struct net_device *dev; | ||
274 | int ret = 0; | ||
275 | |||
276 | rtnl_lock(); | ||
277 | dev = __dev_get_by_name(name); | ||
278 | if (dev == NULL) | ||
279 | ret = -ENXIO; /* Could not find device */ | ||
280 | |||
281 | else if (!(dev->priv_flags & IFF_EBRIDGE)) { | ||
282 | /* Attempt to delete non bridge device! */ | ||
283 | ret = -EPERM; | ||
284 | } | ||
285 | |||
286 | else if (dev->flags & IFF_UP) { | ||
287 | /* Not shutdown yet. */ | ||
288 | ret = -EBUSY; | ||
289 | } | ||
290 | |||
291 | else | ||
292 | del_br(netdev_priv(dev)); | ||
293 | |||
294 | rtnl_unlock(); | ||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | /* Mtu of the bridge pseudo-device 1500 or the minimum of the ports */ | ||
299 | int br_min_mtu(const struct net_bridge *br) | ||
300 | { | ||
301 | const struct net_bridge_port *p; | ||
302 | int mtu = 0; | ||
303 | |||
304 | ASSERT_RTNL(); | ||
305 | |||
306 | if (list_empty(&br->port_list)) | ||
307 | mtu = 1500; | ||
308 | else { | ||
309 | list_for_each_entry(p, &br->port_list, list) { | ||
310 | if (!mtu || p->dev->mtu < mtu) | ||
311 | mtu = p->dev->mtu; | ||
312 | } | ||
313 | } | ||
314 | return mtu; | ||
315 | } | ||
316 | |||
317 | /* called with RTNL */ | ||
318 | int br_add_if(struct net_bridge *br, struct net_device *dev) | ||
319 | { | ||
320 | struct net_bridge_port *p; | ||
321 | int err = 0; | ||
322 | |||
323 | if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER) | ||
324 | return -EINVAL; | ||
325 | |||
326 | if (dev->hard_start_xmit == br_dev_xmit) | ||
327 | return -ELOOP; | ||
328 | |||
329 | if (dev->br_port != NULL) | ||
330 | return -EBUSY; | ||
331 | |||
332 | if (IS_ERR(p = new_nbp(br, dev, br_initial_port_cost(dev)))) | ||
333 | return PTR_ERR(p); | ||
334 | |||
335 | if ((err = br_fdb_insert(br, p, dev->dev_addr))) | ||
336 | destroy_nbp(p); | ||
337 | |||
338 | else if ((err = br_sysfs_addif(p))) | ||
339 | del_nbp(p); | ||
340 | else { | ||
341 | dev_set_promiscuity(dev, 1); | ||
342 | |||
343 | list_add_rcu(&p->list, &br->port_list); | ||
344 | |||
345 | spin_lock_bh(&br->lock); | ||
346 | br_stp_recalculate_bridge_id(br); | ||
347 | if ((br->dev->flags & IFF_UP) | ||
348 | && (dev->flags & IFF_UP) && netif_carrier_ok(dev)) | ||
349 | br_stp_enable_port(p); | ||
350 | spin_unlock_bh(&br->lock); | ||
351 | |||
352 | dev_set_mtu(br->dev, br_min_mtu(br)); | ||
353 | } | ||
354 | |||
355 | return err; | ||
356 | } | ||
357 | |||
358 | /* called with RTNL */ | ||
359 | int br_del_if(struct net_bridge *br, struct net_device *dev) | ||
360 | { | ||
361 | struct net_bridge_port *p = dev->br_port; | ||
362 | |||
363 | if (!p || p->br != br) | ||
364 | return -EINVAL; | ||
365 | |||
366 | br_sysfs_removeif(p); | ||
367 | del_nbp(p); | ||
368 | |||
369 | spin_lock_bh(&br->lock); | ||
370 | br_stp_recalculate_bridge_id(br); | ||
371 | spin_unlock_bh(&br->lock); | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | void __exit br_cleanup_bridges(void) | ||
377 | { | ||
378 | struct net_device *dev, *nxt; | ||
379 | |||
380 | rtnl_lock(); | ||
381 | for (dev = dev_base; dev; dev = nxt) { | ||
382 | nxt = dev->next; | ||
383 | if (dev->priv_flags & IFF_EBRIDGE) | ||
384 | del_br(dev->priv); | ||
385 | } | ||
386 | rtnl_unlock(); | ||
387 | |||
388 | } | ||