vlan.c 18.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
Linus Torvalds's avatar
Linus Torvalds committed
2
 * INET		802.1Q VLAN
Linus Torvalds's avatar
Linus Torvalds committed
3 4
 *		Ethernet-type device handling.
 *
Linus Torvalds's avatar
Linus Torvalds committed
5 6 7
 * Authors:	Ben Greear <greearb@candelatech.com>
 *              Please send support related email to: vlan@scry.wanfear.com
 *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
Linus Torvalds's avatar
Linus Torvalds committed
8 9 10
 * 
 * Fixes:
 *              Fix for packet capture - Nick Eggleston <nick@dccinc.com>;
11 12 13
 *		Add HW acceleration hooks - David S. Miller <davem@redhat.com>;
 *		Correct all the locking - David S. Miller <davem@redhat.com>;
 *		Use hash table for VLAN groups - David S. Miller <davem@redhat.com>
Linus Torvalds's avatar
Linus Torvalds committed
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 *
 *		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 <asm/uaccess.h> /* for copy_from_user */
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/datalink.h>
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/init.h>
#include <net/p8022.h>
#include <net/arp.h>
#include <linux/rtnetlink.h>
#include <linux/notifier.h>

#include <linux/if_vlan.h>
#include "vlan.h"
#include "vlanproc.h"

/* Global VLAN variables */

/* Our listing of VLAN group(s) */
41
struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE];
42
#define vlan_grp_hashfn(IDX)	((((IDX) >> VLAN_GRP_HASH_SHIFT) ^ (IDX)) & VLAN_GRP_HASH_MASK)
Linus Torvalds's avatar
Linus Torvalds committed
43 44 45

static char vlan_fullname[] = "802.1Q VLAN Support";
static unsigned int vlan_version = 1;
46
static unsigned int vlan_release = 8;
47 48
static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
Linus Torvalds's avatar
Linus Torvalds committed
49 50

static int vlan_device_event(struct notifier_block *, unsigned long, void *);
51
static int vlan_ioctl_handler(void __user *);
52
static int unregister_vlan_dev(struct net_device *, unsigned short );
Linus Torvalds's avatar
Linus Torvalds committed
53 54

struct notifier_block vlan_notifier_block = {
55
	.notifier_call = vlan_device_event,
Linus Torvalds's avatar
Linus Torvalds committed
56 57 58 59 60 61 62 63 64 65 66
};

/* These may be changed at run-time through IOCTLs */

/* Determines interface naming scheme. */
unsigned short vlan_name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD;

/* DO reorder the header by default */
unsigned short vlan_default_dev_flags = 1;

static struct packet_type vlan_packet_type = {
67 68
	.type = __constant_htons(ETH_P_8021Q),
	.func = vlan_skb_recv, /* VLAN receive method */
Linus Torvalds's avatar
Linus Torvalds committed
69 70
};

71 72 73 74
/* Bits of netdev state that are propogated from real device to virtual */
#define VLAN_LINK_STATE_MASK \
	((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER))

Linus Torvalds's avatar
Linus Torvalds committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88
/* End of global variables definitions. */

/*
 * Function vlan_proto_init (pro)
 *
 *    Initialize VLAN protocol layer, 
 *
 */
static int __init vlan_proto_init(void)
{
	int err;

	printk(VLAN_INF "%s v%u.%u %s\n",
	       vlan_fullname, vlan_version, vlan_release, vlan_copyright);
89 90
	printk(VLAN_INF "All bugs added by %s\n",
	       vlan_buggyright);
Linus Torvalds's avatar
Linus Torvalds committed
91 92 93 94

	/* proc file system initialization */
	err = vlan_proc_init();
	if (err < 0) {
Alan Cox's avatar
Alan Cox committed
95 96 97
		printk(KERN_ERR 
		       "%s %s: can't create entry in proc filesystem!\n",
		       __FUNCTION__, VLAN_NAME);
Linus Torvalds's avatar
Linus Torvalds committed
98 99 100 101 102 103 104 105
		return 1;
	}

	dev_add_pack(&vlan_packet_type);

	/* Register us to receive netdevice events */
	register_netdevice_notifier(&vlan_notifier_block);

106
	vlan_ioctl_set(vlan_ioctl_handler);
Linus Torvalds's avatar
Linus Torvalds committed
107 108 109 110

	return 0;
}

111 112 113 114 115 116
/* Cleanup all vlan devices 
 * Note: devices that have been registered that but not
 * brought up will exist but have no module ref count.
 */
static void __exit vlan_cleanup_devices(void)
{
117
	struct net_device *dev, *nxt;
118

119
	rtnl_lock();
120 121 122 123 124 125 126 127 128 129 130 131
	for (dev = dev_base; dev; dev = nxt) {
		nxt = dev->next;
		if (dev->priv_flags & IFF_802_1Q_VLAN) {
			unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev,
					    VLAN_DEV_INFO(dev)->vlan_id);

			unregister_netdevice(dev);
		}
	}
	rtnl_unlock();
}

Linus Torvalds's avatar
Linus Torvalds committed
132 133 134 135 136 137
/*
 *     Module 'remove' entry point.
 *     o delete /proc/net/router directory and static entries.
 */ 
static void __exit vlan_cleanup_module(void)
{
138 139
	int i;

140 141 142 143 144 145 146 147
	vlan_ioctl_set(NULL);

	/* Un-register us from receiving netdevice events */
	unregister_netdevice_notifier(&vlan_notifier_block);

	dev_remove_pack(&vlan_packet_type);
	vlan_cleanup_devices();

148 149 150 151
	/* This table must be empty if there are no module
	 * references left.
	 */
	for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) {
152
		BUG_ON(!hlist_empty(&vlan_group_hash[i]));
153
	}
Linus Torvalds's avatar
Linus Torvalds committed
154
	vlan_proc_cleanup();
155 156

	synchronize_net();
Linus Torvalds's avatar
Linus Torvalds committed
157 158 159 160 161
}

module_init(vlan_proto_init);
module_exit(vlan_cleanup_module);

162
/* Must be invoked with RCU read lock (no preempt) */
163
static struct vlan_group *__vlan_find_group(int real_dev_ifindex)
Linus Torvalds's avatar
Linus Torvalds committed
164
{
165
	struct vlan_group *grp;
166 167
	struct hlist_node *n;
	int hash = vlan_grp_hashfn(real_dev_ifindex);
Linus Torvalds's avatar
Linus Torvalds committed
168

169
	hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) {
170
		if (grp->real_dev_ifindex == real_dev_ifindex)
171
			return grp;
Linus Torvalds's avatar
Linus Torvalds committed
172 173
	}

174
	return NULL;
175 176 177 178
}

/*  Find the protocol handler.  Assumes VID < VLAN_VID_MASK.
 *
179
 * Must be invoked with RCU read lock (no preempt)
Linus Torvalds's avatar
Linus Torvalds committed
180
 */
181 182
struct net_device *__find_vlan_dev(struct net_device *real_dev,
				   unsigned short VID)
Linus Torvalds's avatar
Linus Torvalds committed
183
{
184
	struct vlan_group *grp = __vlan_find_group(real_dev->ifindex);
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188 189 190 191

	if (grp)
                return grp->vlan_devices[VID];

	return NULL;
}

192 193 194 195 196 197
static void vlan_rcu_free(struct rcu_head *rcu)
{
	kfree(container_of(rcu, struct vlan_group, rcu));
}


198 199 200 201 202
/* This returns 0 if everything went fine.
 * It will return 1 if the group was killed as a result.
 * A negative return indicates failure.
 *
 * The RTNL lock must be held.
Linus Torvalds's avatar
Linus Torvalds committed
203
 */
204 205
static int unregister_vlan_dev(struct net_device *real_dev,
			       unsigned short vlan_id)
Linus Torvalds's avatar
Linus Torvalds committed
206 207
{
	struct net_device *dev = NULL;
208
	int real_dev_ifindex = real_dev->ifindex;
Linus Torvalds's avatar
Linus Torvalds committed
209
	struct vlan_group *grp;
210
	int i, ret;
Linus Torvalds's avatar
Linus Torvalds committed
211 212

#ifdef VLAN_DEBUG
Alan Cox's avatar
Alan Cox committed
213
	printk(VLAN_DBG "%s: VID: %i\n", __FUNCTION__, vlan_id);
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216
#endif

	/* sanity check */
217
	if (vlan_id >= VLAN_VID_MASK)
Linus Torvalds's avatar
Linus Torvalds committed
218 219
		return -EINVAL;

220
	ASSERT_RTNL();
221 222 223 224
	grp = __vlan_find_group(real_dev_ifindex);

	ret = 0;

Linus Torvalds's avatar
Linus Torvalds committed
225 226 227 228 229 230
	if (grp) {
		dev = grp->vlan_devices[vlan_id];
		if (dev) {
			/* Remove proc entry */
			vlan_proc_rem_dev(dev);

231 232 233 234 235 236 237 238
			/* Take it out of our own structures, but be sure to
			 * interlock with HW accelerating devices or SW vlan
			 * input packet processing.
			 */
			if (real_dev->features &
			    (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER)) {
				real_dev->vlan_rx_kill_vid(real_dev, vlan_id);
			}
Linus Torvalds's avatar
Linus Torvalds committed
239

240
			grp->vlan_devices[vlan_id] = NULL;
241
			synchronize_net();
242 243


244 245 246
			/* Caller unregisters (and if necessary, puts)
			 * VLAN device, but we get rid of the reference to
			 * real_dev here.
Linus Torvalds's avatar
Linus Torvalds committed
247
			 */
248
			dev_put(real_dev);
Linus Torvalds's avatar
Linus Torvalds committed
249

250 251 252 253 254 255
			/* If the group is now empty, kill off the
			 * group.
			 */
			for (i = 0; i < VLAN_VID_MASK; i++)
				if (grp->vlan_devices[i])
					break;
Linus Torvalds's avatar
Linus Torvalds committed
256

257 258 259 260
			if (i == VLAN_VID_MASK) {
				if (real_dev->features & NETIF_F_HW_VLAN_RX)
					real_dev->vlan_rx_register(real_dev, NULL);

261
				hlist_del_rcu(&grp->hlist);
262

263 264
				/* Free the group, after all cpu's are done. */
				call_rcu(&grp->rcu, vlan_rcu_free);
265

266
				grp = NULL;
267
				ret = 1;
Linus Torvalds's avatar
Linus Torvalds committed
268 269 270
			}
		}
	}
271 272

        return ret;
Linus Torvalds's avatar
Linus Torvalds committed
273 274
}

275
static int unregister_vlan_device(const char *vlan_IF_name)
Linus Torvalds's avatar
Linus Torvalds committed
276 277
{
	struct net_device *dev = NULL;
278
	int ret;
Linus Torvalds's avatar
Linus Torvalds committed
279 280 281


	dev = dev_get_by_name(vlan_IF_name);
282
	ret = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
283 284
	if (dev) {
		if (dev->priv_flags & IFF_802_1Q_VLAN) {
285
			rtnl_lock();
286 287 288 289 290 291 292 293 294 295 296

			ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev,
						  VLAN_DEV_INFO(dev)->vlan_id);

			dev_put(dev);
			unregister_netdevice(dev);

			rtnl_unlock();

			if (ret == 1)
				ret = 0;
Linus Torvalds's avatar
Linus Torvalds committed
297
		} else {
Alan Cox's avatar
Alan Cox committed
298 299
			printk(VLAN_ERR 
			       "%s: ERROR:	Tried to remove a non-vlan device "
Linus Torvalds's avatar
Linus Torvalds committed
300
			       "with VLAN code, name: %s  priv_flags: %hX\n",
Alan Cox's avatar
Alan Cox committed
301
			       __FUNCTION__, dev->name, dev->priv_flags);
Linus Torvalds's avatar
Linus Torvalds committed
302
			dev_put(dev);
303
			ret = -EPERM;
Linus Torvalds's avatar
Linus Torvalds committed
304 305 306
		}
	} else {
#ifdef VLAN_DEBUG
Alan Cox's avatar
Alan Cox committed
307
		printk(VLAN_DBG "%s: WARNING: Could not find dev.\n", __FUNCTION__);
Linus Torvalds's avatar
Linus Torvalds committed
308
#endif
309
		ret = -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
310
	}
311 312

	return ret;
Linus Torvalds's avatar
Linus Torvalds committed
313 314
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
static void vlan_setup(struct net_device *new_dev)
{
	SET_MODULE_OWNER(new_dev);
	    
	/* new_dev->ifindex = 0;  it will be set when added to
	 * the global list.
	 * iflink is set as well.
	 */
	new_dev->get_stats = vlan_dev_get_stats;

	/* Make this thing known as a VLAN device */
	new_dev->priv_flags |= IFF_802_1Q_VLAN;
				
	/* Set us up to have no queue, as the underlying Hardware device
	 * can do all the queueing we could want.
	 */
	new_dev->tx_queue_len = 0;

	/* set up method calls */
	new_dev->change_mtu = vlan_dev_change_mtu;
	new_dev->open = vlan_dev_open;
	new_dev->stop = vlan_dev_stop;
	new_dev->set_mac_address = vlan_dev_set_mac_address;
	new_dev->set_multicast_list = vlan_dev_set_multicast_list;
339
	new_dev->destructor = free_netdev;
340
	new_dev->do_ioctl = vlan_dev_ioctl;
341 342
}

Linus Torvalds's avatar
Linus Torvalds committed
343 344 345 346
/*  Attach a VLAN device to a mac address (ie Ethernet Card).
 *  Returns the device that was created, or NULL if there was
 *  an error of some kind.
 */
347
static struct net_device *register_vlan_device(const char *eth_IF_name,
Linus Torvalds's avatar
Linus Torvalds committed
348 349 350 351 352
					       unsigned short VLAN_ID)
{
	struct vlan_group *grp;
	struct net_device *new_dev;
	struct net_device *real_dev; /* the ethernet device */
353
	char name[IFNAMSIZ];
Linus Torvalds's avatar
Linus Torvalds committed
354 355

#ifdef VLAN_DEBUG
Alan Cox's avatar
Alan Cox committed
356 357
	printk(VLAN_DBG "%s: if_name -:%s:-	vid: %i\n",
		__FUNCTION__, eth_IF_name, VLAN_ID);
Linus Torvalds's avatar
Linus Torvalds committed
358 359
#endif

360
	if (VLAN_ID >= VLAN_VID_MASK)
Linus Torvalds's avatar
Linus Torvalds committed
361 362 363 364 365 366 367
		goto out_ret_null;

	/* find the device relating to eth_IF_name. */
	real_dev = dev_get_by_name(eth_IF_name);
	if (!real_dev)
		goto out_ret_null;

368
	if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
Alan Cox's avatar
Alan Cox committed
369 370
		printk(VLAN_DBG "%s: VLANs not supported on %s.\n",
			__FUNCTION__, real_dev->name);
371 372 373 374 375 376
		goto out_put_dev;
	}

	if ((real_dev->features & NETIF_F_HW_VLAN_RX) &&
	    (real_dev->vlan_rx_register == NULL ||
	     real_dev->vlan_rx_kill_vid == NULL)) {
Alan Cox's avatar
Alan Cox committed
377 378
		printk(VLAN_DBG "%s: Device %s has buggy VLAN hw accel.\n",
			__FUNCTION__, real_dev->name);
379 380 381 382 383 384
		goto out_put_dev;
	}

	if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) &&
	    (real_dev->vlan_rx_add_vid == NULL ||
	     real_dev->vlan_rx_kill_vid == NULL)) {
Alan Cox's avatar
Alan Cox committed
385 386
		printk(VLAN_DBG "%s: Device %s has buggy VLAN hw accel.\n",
			__FUNCTION__, real_dev->name);
387 388 389 390 391 392
		goto out_put_dev;
	}

	/* From this point on, all the data structures must remain
	 * consistent.
	 */
393
	rtnl_lock();
394 395 396

	/* The real device must be up and operating in order to
	 * assosciate a VLAN device with it.
Linus Torvalds's avatar
Linus Torvalds committed
397
	 */
398 399 400
	if (!(real_dev->flags & IFF_UP))
		goto out_unlock;

401
	if (__find_vlan_dev(real_dev, VLAN_ID) != NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
402
		/* was already registered. */
Alan Cox's avatar
Alan Cox committed
403
		printk(VLAN_DBG "%s: ALREADY had VLAN registered\n", __FUNCTION__);
404
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
405 406 407 408 409 410 411 412 413 414
	}

	/* Gotta set up the fields for the device. */
#ifdef VLAN_DEBUG
	printk(VLAN_DBG "About to allocate name, vlan_name_type: %i\n",
	       vlan_name_type);
#endif
	switch (vlan_name_type) {
	case VLAN_NAME_TYPE_RAW_PLUS_VID:
		/* name will look like:	 eth1.0005 */
415
		snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID);
Linus Torvalds's avatar
Linus Torvalds committed
416 417 418 419 420
		break;
	case VLAN_NAME_TYPE_PLUS_VID_NO_PAD:
		/* Put our vlan.VID in the name.
		 * Name will look like:	 vlan5
		 */
421
		snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID);
Linus Torvalds's avatar
Linus Torvalds committed
422 423 424 425 426
		break;
	case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD:
		/* Put our vlan.VID in the name.
		 * Name will look like:	 eth0.5
		 */
427
		snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID);
Linus Torvalds's avatar
Linus Torvalds committed
428 429 430 431 432 433
		break;
	case VLAN_NAME_TYPE_PLUS_VID:
		/* Put our vlan.VID in the name.
		 * Name will look like:	 vlan0005
		 */
	default:
434
		snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID);
Linus Torvalds's avatar
Linus Torvalds committed
435 436
	};
		    
437 438 439 440 441
	new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name,
			       vlan_setup);
	if (new_dev == NULL)
		goto out_unlock;

Linus Torvalds's avatar
Linus Torvalds committed
442 443 444 445 446 447 448
#ifdef VLAN_DEBUG
	printk(VLAN_DBG "Allocated new name -:%s:-\n", new_dev->name);
#endif
	/* IFF_BROADCAST|IFF_MULTICAST; ??? */
	new_dev->flags = real_dev->flags;
	new_dev->flags &= ~IFF_UP;

449 450
	new_dev->state = real_dev->state & VLAN_LINK_STATE_MASK;

Linus Torvalds's avatar
Linus Torvalds committed
451 452 453 454 455 456 457 458
	/* need 4 bytes for extra VLAN header info,
	 * hope the underlying device can handle it.
	 */
	new_dev->mtu = real_dev->mtu;

	/* TODO: maybe just assign it to be ETHERNET? */
	new_dev->type = real_dev->type;

459 460 461 462 463
	new_dev->hard_header_len = real_dev->hard_header_len;
	if (!(real_dev->features & NETIF_F_HW_VLAN_TX)) {
		/* Regular ethernet + 4 bytes (18 total). */
		new_dev->hard_header_len += VLAN_HLEN;
	}
Linus Torvalds's avatar
Linus Torvalds committed
464 465 466 467 468 469 470 471 472

	VLAN_MEM_DBG("new_dev->priv malloc, addr: %p  size: %i\n",
		     new_dev->priv,
		     sizeof(struct vlan_dev_info));
	    
	memcpy(new_dev->broadcast, real_dev->broadcast, real_dev->addr_len);
	memcpy(new_dev->dev_addr, real_dev->dev_addr, real_dev->addr_len);
	new_dev->addr_len = real_dev->addr_len;

473 474 475 476 477 478 479 480 481
	if (real_dev->features & NETIF_F_HW_VLAN_TX) {
		new_dev->hard_header = real_dev->hard_header;
		new_dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
		new_dev->rebuild_header = real_dev->rebuild_header;
	} else {
		new_dev->hard_header = vlan_dev_hard_header;
		new_dev->hard_start_xmit = vlan_dev_hard_start_xmit;
		new_dev->rebuild_header = vlan_dev_rebuild_header;
	}
Linus Torvalds's avatar
Linus Torvalds committed
482 483
	new_dev->hard_header_parse = real_dev->hard_header_parse;

484
	VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
Linus Torvalds's avatar
Linus Torvalds committed
485 486 487 488 489 490 491 492 493
	VLAN_DEV_INFO(new_dev)->real_dev = real_dev;
	VLAN_DEV_INFO(new_dev)->dent = NULL;
	VLAN_DEV_INFO(new_dev)->flags = vlan_default_dev_flags;

#ifdef VLAN_DEBUG
	printk(VLAN_DBG "About to go find the group for idx: %i\n",
	       real_dev->ifindex);
#endif
	    
494
	if (register_netdevice(new_dev))
495
		goto out_free_newdev;
496

Linus Torvalds's avatar
Linus Torvalds committed
497 498 499
	/* So, got the sucker initialized, now lets place
	 * it into our local structure.
	 */
500 501 502 503 504
	grp = __vlan_find_group(real_dev->ifindex);

	/* Note, we are running under the RTNL semaphore
	 * so it cannot "appear" on us.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
505 506
	if (!grp) { /* need to add a new group */
		grp = kmalloc(sizeof(struct vlan_group), GFP_KERNEL);
507
		if (!grp)
508
			goto out_free_unregister;
Linus Torvalds's avatar
Linus Torvalds committed
509
					
510
		/* printk(KERN_ALERT "VLAN REGISTER:  Allocated new group.\n"); */
Linus Torvalds's avatar
Linus Torvalds committed
511 512 513
		memset(grp, 0, sizeof(struct vlan_group));
		grp->real_dev_ifindex = real_dev->ifindex;

514 515
		hlist_add_head_rcu(&grp->hlist, 
				   &vlan_group_hash[vlan_grp_hashfn(real_dev->ifindex)]);
516 517 518

		if (real_dev->features & NETIF_F_HW_VLAN_RX)
			real_dev->vlan_rx_register(real_dev, grp);
Linus Torvalds's avatar
Linus Torvalds committed
519 520 521
	}
	    
	grp->vlan_devices[VLAN_ID] = new_dev;
522

523 524 525
	if (vlan_proc_add_dev(new_dev)<0)/* create it's proc entry */
            	printk(KERN_WARNING "VLAN: failed to add proc entry for %s\n",
					                 new_dev->name);
Linus Torvalds's avatar
Linus Torvalds committed
526

527 528 529
	if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
		real_dev->vlan_rx_add_vid(real_dev, VLAN_ID);

Linus Torvalds's avatar
Linus Torvalds committed
530
	rtnl_unlock();
531 532


Linus Torvalds's avatar
Linus Torvalds committed
533 534 535 536 537
#ifdef VLAN_DEBUG
	printk(VLAN_DBG "Allocated new device successfully, returning.\n");
#endif
	return new_dev;

538 539
out_free_unregister:
	unregister_netdev(new_dev);
540
	goto out_put_dev;
541 542

out_free_newdev:
543
	free_netdev(new_dev);
544 545 546 547

out_unlock:
	rtnl_unlock();

Linus Torvalds's avatar
Linus Torvalds committed
548 549 550 551 552 553 554 555 556
out_put_dev:
	dev_put(real_dev);

out_ret_null:
	return NULL;
}

static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
557 558
	struct net_device *dev = ptr;
	struct vlan_group *grp = __vlan_find_group(dev->ifindex);
559
	int i, flgs;
560
	struct net_device *vlandev;
561 562 563 564 565 566 567 568

	if (!grp)
		goto out;

	/* It is OK that we do not hold the group lock right now,
	 * as we run under the RTNL lock.
	 */

Linus Torvalds's avatar
Linus Torvalds committed
569
	switch (event) {
570 571 572 573 574 575 576 577 578 579 580 581 582 583
	case NETDEV_CHANGE:
		/* Propogate real device state to vlan devices */
		flgs = dev->state & VLAN_LINK_STATE_MASK;
		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
			vlandev = grp->vlan_devices[i];
			if (!vlandev)
				continue;

			if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
				vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK) 
					| flgs;
				netdev_state_change(vlandev);
			}
		}
Linus Torvalds's avatar
Linus Torvalds committed
584 585 586
		break;

	case NETDEV_DOWN:
587 588 589 590 591 592 593 594 595 596 597
		/* Put all VLANs for this dev in the down state too.  */
		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
			vlandev = grp->vlan_devices[i];
			if (!vlandev)
				continue;

			flgs = vlandev->flags;
			if (!(flgs & IFF_UP))
				continue;

			dev_change_flags(vlandev, flgs & ~IFF_UP);
Linus Torvalds's avatar
Linus Torvalds committed
598 599 600 601
		}
		break;

	case NETDEV_UP:
602 603 604 605 606
		/* Put all VLANs for this dev in the up state too.  */
		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
			vlandev = grp->vlan_devices[i];
			if (!vlandev)
				continue;
Linus Torvalds's avatar
Linus Torvalds committed
607
				
608 609 610 611 612
			flgs = vlandev->flags;
			if (flgs & IFF_UP)
				continue;

			dev_change_flags(vlandev, flgs | IFF_UP);
Linus Torvalds's avatar
Linus Torvalds committed
613 614 615 616
		}
		break;
		
	case NETDEV_UNREGISTER:
617 618 619 620 621 622 623 624 625 626 627
		/* Delete all VLANs for this dev. */
		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
			int ret;

			vlandev = grp->vlan_devices[i];
			if (!vlandev)
				continue;

			ret = unregister_vlan_dev(dev,
						  VLAN_DEV_INFO(vlandev)->vlan_id);

628
			dev_put(vlandev);
629
			unregister_netdevice(vlandev);
630 631 632 633

			/* Group was destroyed? */
			if (ret == 1)
				break;
Linus Torvalds's avatar
Linus Torvalds committed
634 635 636 637
		}
		break;
	};

638
out:
Linus Torvalds's avatar
Linus Torvalds committed
639 640 641 642 643 644
	return NOTIFY_DONE;
}

/*
 *	VLAN IOCTL handler.
 *	o execute requested action or pass command to the device driver
645
 *   arg is really a struct vlan_ioctl_args __user *.
Linus Torvalds's avatar
Linus Torvalds committed
646
 */
647
static int vlan_ioctl_handler(void __user *arg)
Linus Torvalds's avatar
Linus Torvalds committed
648 649 650 651 652 653 654 655 656 657 658
{
	int err = 0;
	struct vlan_ioctl_args args;

	/* everything here needs root permissions, except aguably the
	 * hack ioctls for sending packets.  However, I know _I_ don't
	 * want users running that on my network! --BLG
	 */
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

659
	if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args)))
Linus Torvalds's avatar
Linus Torvalds committed
660 661 662 663 664 665 666
		return -EFAULT;

	/* Null terminate this sucker, just in case. */
	args.device1[23] = 0;
	args.u.device2[23] = 0;

#ifdef VLAN_DEBUG
Alan Cox's avatar
Alan Cox committed
667
	printk(VLAN_DBG "%s: args.cmd: %x\n", __FUNCTION__, args.cmd);
Linus Torvalds's avatar
Linus Torvalds committed
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
#endif

	switch (args.cmd) {
	case SET_VLAN_INGRESS_PRIORITY_CMD:
		err = vlan_dev_set_ingress_priority(args.device1,
						    args.u.skb_priority,
						    args.vlan_qos);
		break;

	case SET_VLAN_EGRESS_PRIORITY_CMD:
		err = vlan_dev_set_egress_priority(args.device1,
						   args.u.skb_priority,
						   args.vlan_qos);
		break;

	case SET_VLAN_FLAG_CMD:
		err = vlan_dev_set_vlan_flag(args.device1,
					     args.u.flag,
					     args.vlan_qos);
		break;

	case SET_VLAN_NAME_TYPE_CMD:
		if ((args.u.name_type >= 0) &&
		    (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
			vlan_name_type = args.u.name_type;
			err = 0;
		} else {
			err = -EINVAL;
		}
		break;

		/* TODO:  Figure out how to pass info back...
		   case GET_VLAN_INGRESS_PRIORITY_IOCTL:
		   err = vlan_dev_get_ingress_priority(args);
		   break;

		   case GET_VLAN_EGRESS_PRIORITY_IOCTL:
		   err = vlan_dev_get_egress_priority(args);
		   break;
		*/

	case ADD_VLAN_CMD:
		/* we have been given the name of the Ethernet Device we want to
		 * talk to:  args.dev1	 We also have the
		 * VLAN ID:  args.u.VID
		 */
714
		if (register_vlan_device(args.device1, args.u.VID)) {
Linus Torvalds's avatar
Linus Torvalds committed
715 716 717 718 719 720 721 722 723 724
			err = 0;
		} else {
			err = -EINVAL;
		}
		break;

	case DEL_VLAN_CMD:
		/* Here, the args.dev1 is the actual VLAN we want
		 * to get rid of.
		 */
725
		err = unregister_vlan_device(args.device1);
Linus Torvalds's avatar
Linus Torvalds committed
726 727 728 729
		break;

	default:
		/* pass on to underlying device instead?? */
Alan Cox's avatar
Alan Cox committed
730 731
		printk(VLAN_DBG "%s: Unknown VLAN CMD: %x \n",
			__FUNCTION__, args.cmd);
Linus Torvalds's avatar
Linus Torvalds committed
732 733 734 735 736 737
		return -EINVAL;
	};

	return err;
}

738
MODULE_LICENSE("GPL");