br_if.c 10 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 *	Userspace interface
 *	Linux ethernet bridge
 *
 *	Authors:
 *	Lennert Buytenhek		<buytenh@gnu.org>
 *
 *	$Id: br_if.c,v 1.7 2001/12/24 00:59:55 davem Exp $
 *
 *	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>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/if_arp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
23
#include <linux/if_ether.h>
Linus Torvalds's avatar
Linus Torvalds committed
24
25
26
27
28
29
30
31
32
33
34
35
#include <net/sock.h>

#include "br_private.h"

/*
 * Determine initial path cost based on speed.
 * using recommendations from 802.1d standard
 *
 * Need to simulate user ioctl because not all device's that support
 * ethtool, use ethtool_ops.  Also, since driver might sleep need to
 * not be holding any locks.
 */
36
static int port_cost(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
37
38
39
40
41
42
43
44
45
46
47
48
49
{
	struct ethtool_cmd ecmd = { ETHTOOL_GSET };
	struct ifreq ifr;
	mm_segment_t old_fs;
	int err;

	strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
	ifr.ifr_data = (void __user *) &ecmd;

	old_fs = get_fs();
	set_fs(KERNEL_DS);
	err = dev_ethtool(&ifr);
	set_fs(old_fs);
50

Linus Torvalds's avatar
Linus Torvalds committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
	if (!err) {
		switch(ecmd.speed) {
		case SPEED_100:
			return 19;
		case SPEED_1000:
			return 4;
		case SPEED_10000:
			return 2;
		case SPEED_10:
			return 100;
		}
	}

	/* Old silly heuristics based on name */
	if (!strncmp(dev->name, "lec", 3))
		return 7;

	if (!strncmp(dev->name, "plip", 4))
		return 2500;

	return 100;	/* assume old 10Mbps */
}

74
75
76
77
78
79

/*
 * Check for port carrier transistions.
 * Called from work queue to allow for calling functions that
 * might sleep (such as speed check), and to debounce.
 */
David Howells's avatar
David Howells committed
80
static void port_carrier_check(struct work_struct *work)
81
{
82
	struct net_bridge_port *p;
David Howells's avatar
David Howells committed
83
	struct net_device *dev;
Stephen Hemminger's avatar
Stephen Hemminger committed
84
	struct net_bridge *br;
85

David Howells's avatar
David Howells committed
86
87
88
89
	dev = container_of(work, struct net_bridge_port,
			   carrier_check.work)->dev;
	work_release(work);

90
	rtnl_lock();
91
92
93
	p = dev->br_port;
	if (!p)
		goto done;
Stephen Hemminger's avatar
Stephen Hemminger committed
94
95
96
97
98
99
100
101
102
103
104
105
106
	br = p->br;

	if (netif_carrier_ok(dev))
		p->path_cost = port_cost(dev);

	if (br->dev->flags & IFF_UP) {
		spin_lock_bh(&br->lock);
		if (netif_carrier_ok(dev)) {
			if (p->state == BR_STATE_DISABLED)
				br_stp_enable_port(p);
		} else {
			if (p->state != BR_STATE_DISABLED)
				br_stp_disable_port(p);
107
		}
Stephen Hemminger's avatar
Stephen Hemminger committed
108
		spin_unlock_bh(&br->lock);
109
	}
110
done:
111
112
113
	rtnl_unlock();
}

114
115
116
117
118
119
120
121
122
123
124
125
126
127
static void release_nbp(struct kobject *kobj)
{
	struct net_bridge_port *p
		= container_of(kobj, struct net_bridge_port, kobj);
	kfree(p);
}

static struct kobj_type brport_ktype = {
#ifdef CONFIG_SYSFS
	.sysfs_ops = &brport_sysfs_ops,
#endif
	.release = release_nbp,
};

Linus Torvalds's avatar
Linus Torvalds committed
128
129
130
131
132
133
134
135
static void destroy_nbp(struct net_bridge_port *p)
{
	struct net_device *dev = p->dev;

	p->br = NULL;
	p->dev = NULL;
	dev_put(dev);

136
	kobject_put(&p->kobj);
Linus Torvalds's avatar
Linus Torvalds committed
137
138
139
140
141
142
143
144
145
}

static void destroy_nbp_rcu(struct rcu_head *head)
{
	struct net_bridge_port *p =
			container_of(head, struct net_bridge_port, rcu);
	destroy_nbp(p);
}

146
147
148
149
150
151
152
153
154
/* Delete port(interface) from bridge is done in two steps.
 * via RCU. First step, marks device as down. That deletes
 * all the timers and stops new packets from flowing through.
 *
 * Final cleanup doesn't occur until after all CPU's finished
 * processing packets.
 *
 * Protected from multiple admin operations by RTNL mutex
 */
Linus Torvalds's avatar
Linus Torvalds committed
155
156
157
158
159
static void del_nbp(struct net_bridge_port *p)
{
	struct net_bridge *br = p->br;
	struct net_device *dev = p->dev;

160
161
	sysfs_remove_link(&br->ifobj, dev->name);

Linus Torvalds's avatar
Linus Torvalds committed
162
163
	dev_set_promiscuity(dev, -1);

164
165
	cancel_delayed_work(&p->carrier_check);

Linus Torvalds's avatar
Linus Torvalds committed
166
167
168
169
	spin_lock_bh(&br->lock);
	br_stp_disable_port(p);
	spin_unlock_bh(&br->lock);

170
	br_fdb_delete_by_port(br, p, 1);
Linus Torvalds's avatar
Linus Torvalds committed
171
172
173

	list_del_rcu(&p->list);

174
175
	rcu_assign_pointer(dev->br_port, NULL);

176
	kobject_uevent(&p->kobj, KOBJ_REMOVE);
177
178
	kobject_del(&p->kobj);

Linus Torvalds's avatar
Linus Torvalds committed
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
	call_rcu(&p->rcu, destroy_nbp_rcu);
}

/* called with RTNL */
static void del_br(struct net_bridge *br)
{
	struct net_bridge_port *p, *n;

	list_for_each_entry_safe(p, n, &br->port_list, list) {
		del_nbp(p);
	}

	del_timer_sync(&br->gc_timer);

	br_sysfs_delbr(br->dev);
194
	unregister_netdevice(br->dev);
Linus Torvalds's avatar
Linus Torvalds committed
195
196
197
198
199
200
201
202
203
}

static struct net_device *new_bridge_dev(const char *name)
{
	struct net_bridge *br;
	struct net_device *dev;

	dev = alloc_netdev(sizeof(struct net_bridge), name,
			   br_dev_setup);
204

Linus Torvalds's avatar
Linus Torvalds committed
205
206
207
208
209
210
211
212
213
214
215
216
	if (!dev)
		return NULL;

	br = netdev_priv(dev);
	br->dev = dev;

	spin_lock_init(&br->lock);
	INIT_LIST_HEAD(&br->port_list);
	spin_lock_init(&br->hash_lock);

	br->bridge_id.prio[0] = 0x80;
	br->bridge_id.prio[1] = 0x00;
Stephen Hemminger's avatar
Stephen Hemminger committed
217
218

	memcpy(br->group_addr, br_group_address, ETH_ALEN);
Linus Torvalds's avatar
Linus Torvalds committed
219

220
	br->feature_mask = dev->features;
Linus Torvalds's avatar
Linus Torvalds committed
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
	br->stp_enabled = 0;
	br->designated_root = br->bridge_id;
	br->root_path_cost = 0;
	br->root_port = 0;
	br->bridge_max_age = br->max_age = 20 * HZ;
	br->bridge_hello_time = br->hello_time = 2 * HZ;
	br->bridge_forward_delay = br->forward_delay = 15 * HZ;
	br->topology_change = 0;
	br->topology_change_detected = 0;
	br->ageing_time = 300 * HZ;
	INIT_LIST_HEAD(&br->age_list);

	br_stp_timer_init(br);

	return dev;
}

/* find an available port number */
static int find_portno(struct net_bridge *br)
{
	int index;
	struct net_bridge_port *p;
	unsigned long *inuse;

Stephen Hemminger's avatar
Stephen Hemminger committed
245
	inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long),
Linus Torvalds's avatar
Linus Torvalds committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
			GFP_KERNEL);
	if (!inuse)
		return -ENOMEM;

	set_bit(0, inuse);	/* zero is reserved */
	list_for_each_entry(p, &br->port_list, list) {
		set_bit(p->port_no, inuse);
	}
	index = find_first_zero_bit(inuse, BR_MAX_PORTS);
	kfree(inuse);

	return (index >= BR_MAX_PORTS) ? -EXFULL : index;
}

260
/* called with RTNL but without bridge lock */
261
static struct net_bridge_port *new_nbp(struct net_bridge *br,
262
				       struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
263
264
265
{
	int index;
	struct net_bridge_port *p;
266

Linus Torvalds's avatar
Linus Torvalds committed
267
268
269
270
	index = find_portno(br);
	if (index < 0)
		return ERR_PTR(index);

Stephen Hemminger's avatar
Stephen Hemminger committed
271
	p = kzalloc(sizeof(*p), GFP_KERNEL);
Linus Torvalds's avatar
Linus Torvalds committed
272
273
274
275
276
277
	if (p == NULL)
		return ERR_PTR(-ENOMEM);

	p->br = br;
	dev_hold(dev);
	p->dev = dev;
278
	p->path_cost = port_cost(dev);
279
	p->priority = 0x8000 >> BR_PORT_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
280
281
282
	p->port_no = index;
	br_init_port(p);
	p->state = BR_STATE_DISABLED;
David Howells's avatar
David Howells committed
283
	INIT_DELAYED_WORK_NAR(&p->carrier_check, port_carrier_check);
284
	br_stp_port_timer_init(p);
Linus Torvalds's avatar
Linus Torvalds committed
285

286
	kobject_init(&p->kobj);
287
288
	kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR);
	p->kobj.ktype = &brport_ktype;
289
	p->kobj.parent = &(dev->dev.kobj);
290
291
	p->kobj.kset = NULL;

Linus Torvalds's avatar
Linus Torvalds committed
292
293
294
295
296
297
298
299
300
	return p;
}

int br_add_bridge(const char *name)
{
	struct net_device *dev;
	int ret;

	dev = new_bridge_dev(name);
301
	if (!dev)
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
305
306
		return -ENOMEM;

	rtnl_lock();
	if (strchr(dev->name, '%')) {
		ret = dev_alloc_name(dev, dev->name);
307
308
309
310
		if (ret < 0) {
			free_netdev(dev);
			goto out;
		}
Linus Torvalds's avatar
Linus Torvalds committed
311
312
313
314
	}

	ret = register_netdevice(dev);
	if (ret)
315
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
316
317

	ret = br_sysfs_addbr(dev);
318
	if (ret)
319
320
		unregister_netdevice(dev);
 out:
Linus Torvalds's avatar
Linus Torvalds committed
321
	rtnl_unlock();
322
	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
323
324
325
326
327
328
329
330
331
}

int br_del_bridge(const char *name)
{
	struct net_device *dev;
	int ret = 0;

	rtnl_lock();
	dev = __dev_get_by_name(name);
332
	if (dev == NULL)
Linus Torvalds's avatar
Linus Torvalds committed
333
334
335
336
337
338
339
340
341
342
		ret =  -ENXIO; 	/* Could not find device */

	else if (!(dev->priv_flags & IFF_EBRIDGE)) {
		/* Attempt to delete non bridge device! */
		ret = -EPERM;
	}

	else if (dev->flags & IFF_UP) {
		/* Not shutdown yet. */
		ret = -EBUSY;
343
	}
Linus Torvalds's avatar
Linus Torvalds committed
344

345
	else
Linus Torvalds's avatar
Linus Torvalds committed
346
347
348
349
350
351
		del_br(netdev_priv(dev));

	rtnl_unlock();
	return ret;
}

352
/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
Linus Torvalds's avatar
Linus Torvalds committed
353
354
355
356
357
358
359
360
int br_min_mtu(const struct net_bridge *br)
{
	const struct net_bridge_port *p;
	int mtu = 0;

	ASSERT_RTNL();

	if (list_empty(&br->port_list))
361
		mtu = ETH_DATA_LEN;
Linus Torvalds's avatar
Linus Torvalds committed
362
363
364
365
366
367
368
369
370
	else {
		list_for_each_entry(p, &br->port_list, list) {
			if (!mtu  || p->dev->mtu < mtu)
				mtu = p->dev->mtu;
		}
	}
	return mtu;
}

371
372
373
374
375
376
377
378
/*
 * Recomputes features using slave's features
 */
void br_features_recompute(struct net_bridge *br)
{
	struct net_bridge_port *p;
	unsigned long features, checksum;

379
380
	checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0;
	features = br->feature_mask & ~NETIF_F_ALL_CSUM;
381
382

	list_for_each_entry(p, &br->port_list, list) {
Herbert Xu's avatar
Herbert Xu committed
383
384
385
		unsigned long feature = p->dev->features;

		if (checksum & NETIF_F_NO_CSUM && !(feature & NETIF_F_NO_CSUM))
386
			checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
Herbert Xu's avatar
Herbert Xu committed
387
		if (checksum & NETIF_F_HW_CSUM && !(feature & NETIF_F_HW_CSUM))
388
			checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM;
Herbert Xu's avatar
Herbert Xu committed
389
		if (!(feature & NETIF_F_IP_CSUM))
390
			checksum = 0;
Herbert Xu's avatar
Herbert Xu committed
391
392

		if (feature & NETIF_F_GSO)
393
			feature |= NETIF_F_GSO_SOFTWARE;
Herbert Xu's avatar
Herbert Xu committed
394
395
396
		feature |= NETIF_F_GSO;

		features &= feature;
397
398
	}

399
400
401
402
403
	if (!(checksum & NETIF_F_ALL_CSUM))
		features &= ~NETIF_F_SG;
	if (!(features & NETIF_F_SG))
		features &= ~NETIF_F_GSO_MASK;

404
405
	br->dev->features = features | checksum | NETIF_F_LLTX |
			    NETIF_F_GSO_ROBUST;
406
407
}

Linus Torvalds's avatar
Linus Torvalds committed
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/* called with RTNL */
int br_add_if(struct net_bridge *br, struct net_device *dev)
{
	struct net_bridge_port *p;
	int err = 0;

	if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
		return -EINVAL;

	if (dev->hard_start_xmit == br_dev_xmit)
		return -ELOOP;

	if (dev->br_port != NULL)
		return -EBUSY;

423
424
	p = new_nbp(br, dev);
	if (IS_ERR(p))
Linus Torvalds's avatar
Linus Torvalds committed
425
426
		return PTR_ERR(p);

427
428
429
	err = kobject_add(&p->kobj);
	if (err)
		goto err0;
Linus Torvalds's avatar
Linus Torvalds committed
430

431
	err = br_fdb_insert(br, p, dev->dev_addr);
432
433
	if (err)
		goto err1;
Linus Torvalds's avatar
Linus Torvalds committed
434

435
436
437
	err = br_sysfs_addif(p);
	if (err)
		goto err2;
Linus Torvalds's avatar
Linus Torvalds committed
438

439
440
441
442
443
444
445
446
447
448
449
450
451
	rcu_assign_pointer(dev->br_port, p);
	dev_set_promiscuity(dev, 1);

	list_add_rcu(&p->list, &br->port_list);

	spin_lock_bh(&br->lock);
	br_stp_recalculate_bridge_id(br);
	br_features_recompute(br);
	schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE);
	spin_unlock_bh(&br->lock);

	dev_set_mtu(br->dev, br_min_mtu(br));
	kobject_uevent(&p->kobj, KOBJ_ADD);
Linus Torvalds's avatar
Linus Torvalds committed
452

453
454
	return 0;
err2:
455
	br_fdb_delete_by_port(br, p, 1);
456
457
458
459
err1:
	kobject_del(&p->kobj);
err0:
	kobject_put(&p->kobj);
Linus Torvalds's avatar
Linus Torvalds committed
460
461
462
463
464
465
466
	return err;
}

/* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev)
{
	struct net_bridge_port *p = dev->br_port;
467
468

	if (!p || p->br != br)
Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
472
473
474
		return -EINVAL;

	del_nbp(p);

	spin_lock_bh(&br->lock);
	br_stp_recalculate_bridge_id(br);
475
	br_features_recompute(br);
Linus Torvalds's avatar
Linus Torvalds committed
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
	spin_unlock_bh(&br->lock);

	return 0;
}

void __exit br_cleanup_bridges(void)
{
	struct net_device *dev, *nxt;

	rtnl_lock();
	for (dev = dev_base; dev; dev = nxt) {
		nxt = dev->next;
		if (dev->priv_flags & IFF_EBRIDGE)
			del_br(dev->priv);
	}
	rtnl_unlock();

}