xref: /openbmc/linux/net/8021q/vlan.c (revision 69ab4b7d6db68396dbfa827daa8d6f30f9b546a8)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * INET		802.1Q VLAN
31da177e4SLinus Torvalds  *		Ethernet-type device handling.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Ben Greear <greearb@candelatech.com>
61da177e4SLinus Torvalds  *              Please send support related email to: vlan@scry.wanfear.com
71da177e4SLinus Torvalds  *              VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Fixes:
101da177e4SLinus Torvalds  *              Fix for packet capture - Nick Eggleston <nick@dccinc.com>;
111da177e4SLinus Torvalds  *		Add HW acceleration hooks - David S. Miller <davem@redhat.com>;
121da177e4SLinus Torvalds  *		Correct all the locking - David S. Miller <davem@redhat.com>;
131da177e4SLinus Torvalds  *		Use hash table for VLAN groups - David S. Miller <davem@redhat.com>
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *		This program is free software; you can redistribute it and/or
161da177e4SLinus Torvalds  *		modify it under the terms of the GNU General Public License
171da177e4SLinus Torvalds  *		as published by the Free Software Foundation; either version
181da177e4SLinus Torvalds  *		2 of the License, or (at your option) any later version.
191da177e4SLinus Torvalds  */
201da177e4SLinus Torvalds 
211da177e4SLinus Torvalds #include <asm/uaccess.h> /* for copy_from_user */
224fc268d2SRandy Dunlap #include <linux/capability.h>
231da177e4SLinus Torvalds #include <linux/module.h>
241da177e4SLinus Torvalds #include <linux/netdevice.h>
251da177e4SLinus Torvalds #include <linux/skbuff.h>
261da177e4SLinus Torvalds #include <net/datalink.h>
271da177e4SLinus Torvalds #include <linux/mm.h>
281da177e4SLinus Torvalds #include <linux/in.h>
291da177e4SLinus Torvalds #include <linux/init.h>
301da177e4SLinus Torvalds #include <net/p8022.h>
311da177e4SLinus Torvalds #include <net/arp.h>
321da177e4SLinus Torvalds #include <linux/rtnetlink.h>
331da177e4SLinus Torvalds #include <linux/notifier.h>
34e9dc8653SEric W. Biederman #include <net/net_namespace.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #include <linux/if_vlan.h>
371da177e4SLinus Torvalds #include "vlan.h"
381da177e4SLinus Torvalds #include "vlanproc.h"
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define DRV_VERSION "1.8"
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds /* Global VLAN variables */
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /* Our listing of VLAN group(s) */
451da177e4SLinus Torvalds static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE];
461da177e4SLinus Torvalds #define vlan_grp_hashfn(IDX)	((((IDX) >> VLAN_GRP_HASH_SHIFT) ^ (IDX)) & VLAN_GRP_HASH_MASK)
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds static char vlan_fullname[] = "802.1Q VLAN Support";
491da177e4SLinus Torvalds static char vlan_version[] = DRV_VERSION;
501da177e4SLinus Torvalds static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
511da177e4SLinus Torvalds static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds /* Determines interface naming scheme. */
541da177e4SLinus Torvalds unsigned short vlan_name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD;
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds static struct packet_type vlan_packet_type = {
571da177e4SLinus Torvalds 	.type = __constant_htons(ETH_P_8021Q),
581da177e4SLinus Torvalds 	.func = vlan_skb_recv, /* VLAN receive method */
591da177e4SLinus Torvalds };
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /* End of global variables definitions. */
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds /* Must be invoked with RCU read lock (no preempt) */
641da177e4SLinus Torvalds static struct vlan_group *__vlan_find_group(int real_dev_ifindex)
651da177e4SLinus Torvalds {
661da177e4SLinus Torvalds 	struct vlan_group *grp;
671da177e4SLinus Torvalds 	struct hlist_node *n;
681da177e4SLinus Torvalds 	int hash = vlan_grp_hashfn(real_dev_ifindex);
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds 	hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) {
711da177e4SLinus Torvalds 		if (grp->real_dev_ifindex == real_dev_ifindex)
721da177e4SLinus Torvalds 			return grp;
731da177e4SLinus Torvalds 	}
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	return NULL;
761da177e4SLinus Torvalds }
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds /*  Find the protocol handler.  Assumes VID < VLAN_VID_MASK.
791da177e4SLinus Torvalds  *
801da177e4SLinus Torvalds  * Must be invoked with RCU read lock (no preempt)
811da177e4SLinus Torvalds  */
821da177e4SLinus Torvalds struct net_device *__find_vlan_dev(struct net_device *real_dev,
831da177e4SLinus Torvalds 				   unsigned short VID)
841da177e4SLinus Torvalds {
851da177e4SLinus Torvalds 	struct vlan_group *grp = __vlan_find_group(real_dev->ifindex);
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds 	if (grp)
885c15bdecSDan Aloni 		return vlan_group_get_device(grp, VID);
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds 	return NULL;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
935c15bdecSDan Aloni static void vlan_group_free(struct vlan_group *grp)
945c15bdecSDan Aloni {
955c15bdecSDan Aloni 	int i;
965c15bdecSDan Aloni 
975c15bdecSDan Aloni 	for (i=0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++)
985c15bdecSDan Aloni 		kfree(grp->vlan_devices_arrays[i]);
995c15bdecSDan Aloni 	kfree(grp);
1005c15bdecSDan Aloni }
1015c15bdecSDan Aloni 
10242429aaeSPatrick McHardy static struct vlan_group *vlan_group_alloc(int ifindex)
10342429aaeSPatrick McHardy {
10442429aaeSPatrick McHardy 	struct vlan_group *grp;
10542429aaeSPatrick McHardy 	unsigned int size;
10642429aaeSPatrick McHardy 	unsigned int i;
10742429aaeSPatrick McHardy 
10842429aaeSPatrick McHardy 	grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL);
10942429aaeSPatrick McHardy 	if (!grp)
11042429aaeSPatrick McHardy 		return NULL;
11142429aaeSPatrick McHardy 
11242429aaeSPatrick McHardy 	size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN;
11342429aaeSPatrick McHardy 
11442429aaeSPatrick McHardy 	for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) {
11542429aaeSPatrick McHardy 		grp->vlan_devices_arrays[i] = kzalloc(size, GFP_KERNEL);
11642429aaeSPatrick McHardy 		if (!grp->vlan_devices_arrays[i])
11742429aaeSPatrick McHardy 			goto err;
11842429aaeSPatrick McHardy 	}
11942429aaeSPatrick McHardy 
12042429aaeSPatrick McHardy 	grp->real_dev_ifindex = ifindex;
12142429aaeSPatrick McHardy 	hlist_add_head_rcu(&grp->hlist,
12242429aaeSPatrick McHardy 			   &vlan_group_hash[vlan_grp_hashfn(ifindex)]);
12342429aaeSPatrick McHardy 	return grp;
12442429aaeSPatrick McHardy 
12542429aaeSPatrick McHardy err:
12642429aaeSPatrick McHardy 	vlan_group_free(grp);
12742429aaeSPatrick McHardy 	return NULL;
12842429aaeSPatrick McHardy }
12942429aaeSPatrick McHardy 
1301da177e4SLinus Torvalds static void vlan_rcu_free(struct rcu_head *rcu)
1311da177e4SLinus Torvalds {
1325c15bdecSDan Aloni 	vlan_group_free(container_of(rcu, struct vlan_group, rcu));
1331da177e4SLinus Torvalds }
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds /* This returns 0 if everything went fine.
1371da177e4SLinus Torvalds  * It will return 1 if the group was killed as a result.
1381da177e4SLinus Torvalds  * A negative return indicates failure.
1391da177e4SLinus Torvalds  *
1401da177e4SLinus Torvalds  * The RTNL lock must be held.
1411da177e4SLinus Torvalds  */
1421da177e4SLinus Torvalds static int unregister_vlan_dev(struct net_device *real_dev,
1431da177e4SLinus Torvalds 			       unsigned short vlan_id)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	struct net_device *dev = NULL;
1461da177e4SLinus Torvalds 	int real_dev_ifindex = real_dev->ifindex;
1471da177e4SLinus Torvalds 	struct vlan_group *grp;
1481da177e4SLinus Torvalds 	int i, ret;
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds 	/* sanity check */
1511da177e4SLinus Torvalds 	if (vlan_id >= VLAN_VID_MASK)
1521da177e4SLinus Torvalds 		return -EINVAL;
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds 	ASSERT_RTNL();
1551da177e4SLinus Torvalds 	grp = __vlan_find_group(real_dev_ifindex);
1561da177e4SLinus Torvalds 
1571da177e4SLinus Torvalds 	ret = 0;
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds 	if (grp) {
1605c15bdecSDan Aloni 		dev = vlan_group_get_device(grp, vlan_id);
1611da177e4SLinus Torvalds 		if (dev) {
1621da177e4SLinus Torvalds 			/* Remove proc entry */
1631da177e4SLinus Torvalds 			vlan_proc_rem_dev(dev);
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 			/* Take it out of our own structures, but be sure to
1661da177e4SLinus Torvalds 			 * interlock with HW accelerating devices or SW vlan
1671da177e4SLinus Torvalds 			 * input packet processing.
1681da177e4SLinus Torvalds 			 */
169d2d1acdbSStephen Hemminger 			if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
1701da177e4SLinus Torvalds 				real_dev->vlan_rx_kill_vid(real_dev, vlan_id);
1711da177e4SLinus Torvalds 
1725c15bdecSDan Aloni 			vlan_group_set_device(grp, vlan_id, NULL);
1731da177e4SLinus Torvalds 			synchronize_net();
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 			/* Caller unregisters (and if necessary, puts)
1771da177e4SLinus Torvalds 			 * VLAN device, but we get rid of the reference to
1781da177e4SLinus Torvalds 			 * real_dev here.
1791da177e4SLinus Torvalds 			 */
1801da177e4SLinus Torvalds 			dev_put(real_dev);
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds 			/* If the group is now empty, kill off the
1831da177e4SLinus Torvalds 			 * group.
1841da177e4SLinus Torvalds 			 */
1851da177e4SLinus Torvalds 			for (i = 0; i < VLAN_VID_MASK; i++)
1865c15bdecSDan Aloni 				if (vlan_group_get_device(grp, i))
1871da177e4SLinus Torvalds 					break;
1881da177e4SLinus Torvalds 
1891da177e4SLinus Torvalds 			if (i == VLAN_VID_MASK) {
1901da177e4SLinus Torvalds 				if (real_dev->features & NETIF_F_HW_VLAN_RX)
1911da177e4SLinus Torvalds 					real_dev->vlan_rx_register(real_dev, NULL);
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds 				hlist_del_rcu(&grp->hlist);
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds 				/* Free the group, after all cpu's are done. */
1961da177e4SLinus Torvalds 				call_rcu(&grp->rcu, vlan_rcu_free);
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds 				grp = NULL;
1991da177e4SLinus Torvalds 				ret = 1;
2001da177e4SLinus Torvalds 			}
2011da177e4SLinus Torvalds 		}
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	return ret;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds 
20707b5b17eSPatrick McHardy int unregister_vlan_device(struct net_device *dev)
2081da177e4SLinus Torvalds {
2091da177e4SLinus Torvalds 	int ret;
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds 	ret = unregister_vlan_dev(VLAN_DEV_INFO(dev)->real_dev,
2121da177e4SLinus Torvalds 				  VLAN_DEV_INFO(dev)->vlan_id);
2131da177e4SLinus Torvalds 	unregister_netdevice(dev);
2141da177e4SLinus Torvalds 
2151da177e4SLinus Torvalds 	if (ret == 1)
2161da177e4SLinus Torvalds 		ret = 0;
2171da177e4SLinus Torvalds 	return ret;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
220ddd7bf9fSStefan Rompf static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev)
221ddd7bf9fSStefan Rompf {
222ddd7bf9fSStefan Rompf 	/* Have to respect userspace enforced dormant state
223ddd7bf9fSStefan Rompf 	 * of real device, also must allow supplicant running
224ddd7bf9fSStefan Rompf 	 * on VLAN device
225ddd7bf9fSStefan Rompf 	 */
226ddd7bf9fSStefan Rompf 	if (dev->operstate == IF_OPER_DORMANT)
227ddd7bf9fSStefan Rompf 		netif_dormant_on(vlandev);
228ddd7bf9fSStefan Rompf 	else
229ddd7bf9fSStefan Rompf 		netif_dormant_off(vlandev);
230ddd7bf9fSStefan Rompf 
231ddd7bf9fSStefan Rompf 	if (netif_carrier_ok(dev)) {
232ddd7bf9fSStefan Rompf 		if (!netif_carrier_ok(vlandev))
233ddd7bf9fSStefan Rompf 			netif_carrier_on(vlandev);
234ddd7bf9fSStefan Rompf 	} else {
235ddd7bf9fSStefan Rompf 		if (netif_carrier_ok(vlandev))
236ddd7bf9fSStefan Rompf 			netif_carrier_off(vlandev);
237ddd7bf9fSStefan Rompf 	}
238ddd7bf9fSStefan Rompf }
239ddd7bf9fSStefan Rompf 
24007b5b17eSPatrick McHardy int vlan_check_real_dev(struct net_device *real_dev, unsigned short vlan_id)
241c1d3ee99SPatrick McHardy {
24240f98e1aSPatrick McHardy 	char *name = real_dev->name;
24340f98e1aSPatrick McHardy 
244c1d3ee99SPatrick McHardy 	if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
24540f98e1aSPatrick McHardy 		pr_info("8021q: VLANs not supported on %s\n", name);
246c1d3ee99SPatrick McHardy 		return -EOPNOTSUPP;
247c1d3ee99SPatrick McHardy 	}
248c1d3ee99SPatrick McHardy 
249c1d3ee99SPatrick McHardy 	if ((real_dev->features & NETIF_F_HW_VLAN_RX) &&
250c1d3ee99SPatrick McHardy 	    !real_dev->vlan_rx_register) {
25140f98e1aSPatrick McHardy 		pr_info("8021q: device %s has buggy VLAN hw accel\n", name);
252c1d3ee99SPatrick McHardy 		return -EOPNOTSUPP;
253c1d3ee99SPatrick McHardy 	}
254c1d3ee99SPatrick McHardy 
255c1d3ee99SPatrick McHardy 	if ((real_dev->features & NETIF_F_HW_VLAN_FILTER) &&
256c1d3ee99SPatrick McHardy 	    (!real_dev->vlan_rx_add_vid || !real_dev->vlan_rx_kill_vid)) {
25740f98e1aSPatrick McHardy 		pr_info("8021q: Device %s has buggy VLAN hw accel\n", name);
258c1d3ee99SPatrick McHardy 		return -EOPNOTSUPP;
259c1d3ee99SPatrick McHardy 	}
260c1d3ee99SPatrick McHardy 
261c1d3ee99SPatrick McHardy 	/* The real device must be up and operating in order to
262c1d3ee99SPatrick McHardy 	 * assosciate a VLAN device with it.
263c1d3ee99SPatrick McHardy 	 */
264c1d3ee99SPatrick McHardy 	if (!(real_dev->flags & IFF_UP))
265c1d3ee99SPatrick McHardy 		return -ENETDOWN;
266c1d3ee99SPatrick McHardy 
26740f98e1aSPatrick McHardy 	if (__find_vlan_dev(real_dev, vlan_id) != NULL)
268c1d3ee99SPatrick McHardy 		return -EEXIST;
269c1d3ee99SPatrick McHardy 
270c1d3ee99SPatrick McHardy 	return 0;
271c1d3ee99SPatrick McHardy }
272c1d3ee99SPatrick McHardy 
27307b5b17eSPatrick McHardy int register_vlan_dev(struct net_device *dev)
274e89fe42cSPatrick McHardy {
275e89fe42cSPatrick McHardy 	struct vlan_dev_info *vlan = VLAN_DEV_INFO(dev);
276e89fe42cSPatrick McHardy 	struct net_device *real_dev = vlan->real_dev;
277e89fe42cSPatrick McHardy 	unsigned short vlan_id = vlan->vlan_id;
278e89fe42cSPatrick McHardy 	struct vlan_group *grp, *ngrp = NULL;
279e89fe42cSPatrick McHardy 	int err;
280e89fe42cSPatrick McHardy 
281e89fe42cSPatrick McHardy 	grp = __vlan_find_group(real_dev->ifindex);
282e89fe42cSPatrick McHardy 	if (!grp) {
283e89fe42cSPatrick McHardy 		ngrp = grp = vlan_group_alloc(real_dev->ifindex);
284e89fe42cSPatrick McHardy 		if (!grp)
285e89fe42cSPatrick McHardy 			return -ENOBUFS;
286e89fe42cSPatrick McHardy 	}
287e89fe42cSPatrick McHardy 
288e89fe42cSPatrick McHardy 	err = register_netdevice(dev);
289e89fe42cSPatrick McHardy 	if (err < 0)
290e89fe42cSPatrick McHardy 		goto out_free_group;
291e89fe42cSPatrick McHardy 
292e89fe42cSPatrick McHardy 	/* Account for reference in struct vlan_dev_info */
293e89fe42cSPatrick McHardy 	dev_hold(real_dev);
294e89fe42cSPatrick McHardy 
295e89fe42cSPatrick McHardy 	vlan_transfer_operstate(real_dev, dev);
296e89fe42cSPatrick McHardy 	linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */
297e89fe42cSPatrick McHardy 
298e89fe42cSPatrick McHardy 	/* So, got the sucker initialized, now lets place
299e89fe42cSPatrick McHardy 	 * it into our local structure.
300e89fe42cSPatrick McHardy 	 */
301e89fe42cSPatrick McHardy 	vlan_group_set_device(grp, vlan_id, dev);
302e89fe42cSPatrick McHardy 	if (ngrp && real_dev->features & NETIF_F_HW_VLAN_RX)
303e89fe42cSPatrick McHardy 		real_dev->vlan_rx_register(real_dev, ngrp);
304e89fe42cSPatrick McHardy 	if (real_dev->features & NETIF_F_HW_VLAN_FILTER)
305e89fe42cSPatrick McHardy 		real_dev->vlan_rx_add_vid(real_dev, vlan_id);
306e89fe42cSPatrick McHardy 
307e89fe42cSPatrick McHardy 	if (vlan_proc_add_dev(dev) < 0)
30840f98e1aSPatrick McHardy 		pr_warning("8021q: failed to add proc entry for %s\n",
309e89fe42cSPatrick McHardy 			   dev->name);
310e89fe42cSPatrick McHardy 	return 0;
311e89fe42cSPatrick McHardy 
312e89fe42cSPatrick McHardy out_free_group:
313e89fe42cSPatrick McHardy 	if (ngrp)
314e89fe42cSPatrick McHardy 		vlan_group_free(ngrp);
315e89fe42cSPatrick McHardy 	return err;
316e89fe42cSPatrick McHardy }
317e89fe42cSPatrick McHardy 
3181da177e4SLinus Torvalds /*  Attach a VLAN device to a mac address (ie Ethernet Card).
3192ae0bf69SPatrick McHardy  *  Returns 0 if the device was created or a negative error code otherwise.
3201da177e4SLinus Torvalds  */
3212ae0bf69SPatrick McHardy static int register_vlan_device(struct net_device *real_dev,
3221da177e4SLinus Torvalds 				unsigned short VLAN_ID)
3231da177e4SLinus Torvalds {
3241da177e4SLinus Torvalds 	struct net_device *new_dev;
3251da177e4SLinus Torvalds 	char name[IFNAMSIZ];
3262ae0bf69SPatrick McHardy 	int err;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	if (VLAN_ID >= VLAN_VID_MASK)
3292ae0bf69SPatrick McHardy 		return -ERANGE;
3301da177e4SLinus Torvalds 
3312ae0bf69SPatrick McHardy 	err = vlan_check_real_dev(real_dev, VLAN_ID);
3322ae0bf69SPatrick McHardy 	if (err < 0)
3332ae0bf69SPatrick McHardy 		return err;
3341da177e4SLinus Torvalds 
3351da177e4SLinus Torvalds 	/* Gotta set up the fields for the device. */
3361da177e4SLinus Torvalds 	switch (vlan_name_type) {
3371da177e4SLinus Torvalds 	case VLAN_NAME_TYPE_RAW_PLUS_VID:
3381da177e4SLinus Torvalds 		/* name will look like:	 eth1.0005 */
3391da177e4SLinus Torvalds 		snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID);
3401da177e4SLinus Torvalds 		break;
3411da177e4SLinus Torvalds 	case VLAN_NAME_TYPE_PLUS_VID_NO_PAD:
3421da177e4SLinus Torvalds 		/* Put our vlan.VID in the name.
3431da177e4SLinus Torvalds 		 * Name will look like:	 vlan5
3441da177e4SLinus Torvalds 		 */
3451da177e4SLinus Torvalds 		snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID);
3461da177e4SLinus Torvalds 		break;
3471da177e4SLinus Torvalds 	case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD:
3481da177e4SLinus Torvalds 		/* Put our vlan.VID in the name.
3491da177e4SLinus Torvalds 		 * Name will look like:	 eth0.5
3501da177e4SLinus Torvalds 		 */
3511da177e4SLinus Torvalds 		snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID);
3521da177e4SLinus Torvalds 		break;
3531da177e4SLinus Torvalds 	case VLAN_NAME_TYPE_PLUS_VID:
3541da177e4SLinus Torvalds 		/* Put our vlan.VID in the name.
3551da177e4SLinus Torvalds 		 * Name will look like:	 vlan0005
3561da177e4SLinus Torvalds 		 */
3571da177e4SLinus Torvalds 	default:
3581da177e4SLinus Torvalds 		snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID);
3593ff50b79SStephen Hemminger 	}
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name,
3621da177e4SLinus Torvalds 			       vlan_setup);
3635dd8d1e9SArjan van de Ven 
3641da177e4SLinus Torvalds 	if (new_dev == NULL)
3652ae0bf69SPatrick McHardy 		return -ENOBUFS;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	/* need 4 bytes for extra VLAN header info,
3681da177e4SLinus Torvalds 	 * hope the underlying device can handle it.
3691da177e4SLinus Torvalds 	 */
3701da177e4SLinus Torvalds 	new_dev->mtu = real_dev->mtu;
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds 	VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
3731da177e4SLinus Torvalds 	VLAN_DEV_INFO(new_dev)->real_dev = real_dev;
3741da177e4SLinus Torvalds 	VLAN_DEV_INFO(new_dev)->dent = NULL;
375a4bf3af4SPatrick McHardy 	VLAN_DEV_INFO(new_dev)->flags = VLAN_FLAG_REORDER_HDR;
3761da177e4SLinus Torvalds 
37707b5b17eSPatrick McHardy 	new_dev->rtnl_link_ops = &vlan_link_ops;
3782ae0bf69SPatrick McHardy 	err = register_vlan_dev(new_dev);
3792ae0bf69SPatrick McHardy 	if (err < 0)
38042429aaeSPatrick McHardy 		goto out_free_newdev;
3811da177e4SLinus Torvalds 
3822ae0bf69SPatrick McHardy 	return 0;
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds out_free_newdev:
3851da177e4SLinus Torvalds 	free_netdev(new_dev);
3862ae0bf69SPatrick McHardy 	return err;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds 
3898c979c26SPatrick McHardy static void vlan_sync_address(struct net_device *dev,
3908c979c26SPatrick McHardy 			      struct net_device *vlandev)
3918c979c26SPatrick McHardy {
3928c979c26SPatrick McHardy 	struct vlan_dev_info *vlan = VLAN_DEV_INFO(vlandev);
3938c979c26SPatrick McHardy 
3948c979c26SPatrick McHardy 	/* May be called without an actual change */
3958c979c26SPatrick McHardy 	if (!compare_ether_addr(vlan->real_dev_addr, dev->dev_addr))
3968c979c26SPatrick McHardy 		return;
3978c979c26SPatrick McHardy 
3988c979c26SPatrick McHardy 	/* vlan address was different from the old address and is equal to
3998c979c26SPatrick McHardy 	 * the new address */
4008c979c26SPatrick McHardy 	if (compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
4018c979c26SPatrick McHardy 	    !compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
4028c979c26SPatrick McHardy 		dev_unicast_delete(dev, vlandev->dev_addr, ETH_ALEN);
4038c979c26SPatrick McHardy 
4048c979c26SPatrick McHardy 	/* vlan address was equal to the old address and is different from
4058c979c26SPatrick McHardy 	 * the new address */
4068c979c26SPatrick McHardy 	if (!compare_ether_addr(vlandev->dev_addr, vlan->real_dev_addr) &&
4078c979c26SPatrick McHardy 	    compare_ether_addr(vlandev->dev_addr, dev->dev_addr))
4088c979c26SPatrick McHardy 		dev_unicast_add(dev, vlandev->dev_addr, ETH_ALEN);
4098c979c26SPatrick McHardy 
4108c979c26SPatrick McHardy 	memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
4118c979c26SPatrick McHardy }
4128c979c26SPatrick McHardy 
4131da177e4SLinus Torvalds static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
4141da177e4SLinus Torvalds {
4151da177e4SLinus Torvalds 	struct net_device *dev = ptr;
4161da177e4SLinus Torvalds 	struct vlan_group *grp = __vlan_find_group(dev->ifindex);
4171da177e4SLinus Torvalds 	int i, flgs;
4181da177e4SLinus Torvalds 	struct net_device *vlandev;
4191da177e4SLinus Torvalds 
420e9dc8653SEric W. Biederman 	if (dev->nd_net != &init_net)
421e9dc8653SEric W. Biederman 		return NOTIFY_DONE;
422e9dc8653SEric W. Biederman 
4231da177e4SLinus Torvalds 	if (!grp)
4241da177e4SLinus Torvalds 		goto out;
4251da177e4SLinus Torvalds 
4261da177e4SLinus Torvalds 	/* It is OK that we do not hold the group lock right now,
4271da177e4SLinus Torvalds 	 * as we run under the RTNL lock.
4281da177e4SLinus Torvalds 	 */
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	switch (event) {
4311da177e4SLinus Torvalds 	case NETDEV_CHANGE:
4321da177e4SLinus Torvalds 		/* Propagate real device state to vlan devices */
4331da177e4SLinus Torvalds 		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
4345c15bdecSDan Aloni 			vlandev = vlan_group_get_device(grp, i);
4351da177e4SLinus Torvalds 			if (!vlandev)
4361da177e4SLinus Torvalds 				continue;
4371da177e4SLinus Torvalds 
438ddd7bf9fSStefan Rompf 			vlan_transfer_operstate(dev, vlandev);
4391da177e4SLinus Torvalds 		}
4401da177e4SLinus Torvalds 		break;
4411da177e4SLinus Torvalds 
4428c979c26SPatrick McHardy 	case NETDEV_CHANGEADDR:
4438c979c26SPatrick McHardy 		/* Adjust unicast filters on underlying device */
4448c979c26SPatrick McHardy 		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
4458c979c26SPatrick McHardy 			vlandev = vlan_group_get_device(grp, i);
4468c979c26SPatrick McHardy 			if (!vlandev)
4478c979c26SPatrick McHardy 				continue;
4488c979c26SPatrick McHardy 
449d932e04aSPatrick McHardy 			flgs = vlandev->flags;
450d932e04aSPatrick McHardy 			if (!(flgs & IFF_UP))
451d932e04aSPatrick McHardy 				continue;
452d932e04aSPatrick McHardy 
4538c979c26SPatrick McHardy 			vlan_sync_address(dev, vlandev);
4548c979c26SPatrick McHardy 		}
4558c979c26SPatrick McHardy 		break;
4568c979c26SPatrick McHardy 
4571da177e4SLinus Torvalds 	case NETDEV_DOWN:
4581da177e4SLinus Torvalds 		/* Put all VLANs for this dev in the down state too.  */
4591da177e4SLinus Torvalds 		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
4605c15bdecSDan Aloni 			vlandev = vlan_group_get_device(grp, i);
4611da177e4SLinus Torvalds 			if (!vlandev)
4621da177e4SLinus Torvalds 				continue;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 			flgs = vlandev->flags;
4651da177e4SLinus Torvalds 			if (!(flgs & IFF_UP))
4661da177e4SLinus Torvalds 				continue;
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds 			dev_change_flags(vlandev, flgs & ~IFF_UP);
4691da177e4SLinus Torvalds 		}
4701da177e4SLinus Torvalds 		break;
4711da177e4SLinus Torvalds 
4721da177e4SLinus Torvalds 	case NETDEV_UP:
4731da177e4SLinus Torvalds 		/* Put all VLANs for this dev in the up state too.  */
4741da177e4SLinus Torvalds 		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
4755c15bdecSDan Aloni 			vlandev = vlan_group_get_device(grp, i);
4761da177e4SLinus Torvalds 			if (!vlandev)
4771da177e4SLinus Torvalds 				continue;
4781da177e4SLinus Torvalds 
4791da177e4SLinus Torvalds 			flgs = vlandev->flags;
4801da177e4SLinus Torvalds 			if (flgs & IFF_UP)
4811da177e4SLinus Torvalds 				continue;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 			dev_change_flags(vlandev, flgs | IFF_UP);
4841da177e4SLinus Torvalds 		}
4851da177e4SLinus Torvalds 		break;
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	case NETDEV_UNREGISTER:
4881da177e4SLinus Torvalds 		/* Delete all VLANs for this dev. */
4891da177e4SLinus Torvalds 		for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
4901da177e4SLinus Torvalds 			int ret;
4911da177e4SLinus Torvalds 
4925c15bdecSDan Aloni 			vlandev = vlan_group_get_device(grp, i);
4931da177e4SLinus Torvalds 			if (!vlandev)
4941da177e4SLinus Torvalds 				continue;
4951da177e4SLinus Torvalds 
4961da177e4SLinus Torvalds 			ret = unregister_vlan_dev(dev,
4971da177e4SLinus Torvalds 						  VLAN_DEV_INFO(vlandev)->vlan_id);
4981da177e4SLinus Torvalds 
4991da177e4SLinus Torvalds 			unregister_netdevice(vlandev);
5001da177e4SLinus Torvalds 
5011da177e4SLinus Torvalds 			/* Group was destroyed? */
5021da177e4SLinus Torvalds 			if (ret == 1)
5031da177e4SLinus Torvalds 				break;
5041da177e4SLinus Torvalds 		}
5051da177e4SLinus Torvalds 		break;
5063ff50b79SStephen Hemminger 	}
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds out:
5091da177e4SLinus Torvalds 	return NOTIFY_DONE;
5101da177e4SLinus Torvalds }
5111da177e4SLinus Torvalds 
512*69ab4b7dSPatrick McHardy static struct notifier_block vlan_notifier_block __read_mostly = {
513*69ab4b7dSPatrick McHardy 	.notifier_call = vlan_device_event,
514*69ab4b7dSPatrick McHardy };
515*69ab4b7dSPatrick McHardy 
5161da177e4SLinus Torvalds /*
5171da177e4SLinus Torvalds  *	VLAN IOCTL handler.
5181da177e4SLinus Torvalds  *	o execute requested action or pass command to the device driver
5191da177e4SLinus Torvalds  *   arg is really a struct vlan_ioctl_args __user *.
5201da177e4SLinus Torvalds  */
521881d966bSEric W. Biederman static int vlan_ioctl_handler(struct net *net, void __user *arg)
5221da177e4SLinus Torvalds {
523c17d8874SPatrick McHardy 	int err;
5241da177e4SLinus Torvalds 	unsigned short vid = 0;
5251da177e4SLinus Torvalds 	struct vlan_ioctl_args args;
526c17d8874SPatrick McHardy 	struct net_device *dev = NULL;
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds 	if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args)))
5291da177e4SLinus Torvalds 		return -EFAULT;
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds 	/* Null terminate this sucker, just in case. */
5321da177e4SLinus Torvalds 	args.device1[23] = 0;
5331da177e4SLinus Torvalds 	args.u.device2[23] = 0;
5341da177e4SLinus Torvalds 
535c17d8874SPatrick McHardy 	rtnl_lock();
536c17d8874SPatrick McHardy 
5371da177e4SLinus Torvalds 	switch (args.cmd) {
5381da177e4SLinus Torvalds 	case SET_VLAN_INGRESS_PRIORITY_CMD:
539c17d8874SPatrick McHardy 	case SET_VLAN_EGRESS_PRIORITY_CMD:
540c17d8874SPatrick McHardy 	case SET_VLAN_FLAG_CMD:
541c17d8874SPatrick McHardy 	case ADD_VLAN_CMD:
542c17d8874SPatrick McHardy 	case DEL_VLAN_CMD:
543c17d8874SPatrick McHardy 	case GET_VLAN_REALDEV_NAME_CMD:
544c17d8874SPatrick McHardy 	case GET_VLAN_VID_CMD:
545c17d8874SPatrick McHardy 		err = -ENODEV;
546881d966bSEric W. Biederman 		dev = __dev_get_by_name(&init_net, args.device1);
547c17d8874SPatrick McHardy 		if (!dev)
548c17d8874SPatrick McHardy 			goto out;
549c17d8874SPatrick McHardy 
550c17d8874SPatrick McHardy 		err = -EINVAL;
551c17d8874SPatrick McHardy 		if (args.cmd != ADD_VLAN_CMD &&
552c17d8874SPatrick McHardy 		    !(dev->priv_flags & IFF_802_1Q_VLAN))
553c17d8874SPatrick McHardy 			goto out;
554c17d8874SPatrick McHardy 	}
555c17d8874SPatrick McHardy 
556c17d8874SPatrick McHardy 	switch (args.cmd) {
557c17d8874SPatrick McHardy 	case SET_VLAN_INGRESS_PRIORITY_CMD:
558c17d8874SPatrick McHardy 		err = -EPERM;
5591da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
560c17d8874SPatrick McHardy 			break;
561c17d8874SPatrick McHardy 		vlan_dev_set_ingress_priority(dev,
5621da177e4SLinus Torvalds 					      args.u.skb_priority,
5631da177e4SLinus Torvalds 					      args.vlan_qos);
564fffe470aSPatrick McHardy 		err = 0;
5651da177e4SLinus Torvalds 		break;
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	case SET_VLAN_EGRESS_PRIORITY_CMD:
568c17d8874SPatrick McHardy 		err = -EPERM;
5691da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
570c17d8874SPatrick McHardy 			break;
571c17d8874SPatrick McHardy 		err = vlan_dev_set_egress_priority(dev,
5721da177e4SLinus Torvalds 						   args.u.skb_priority,
5731da177e4SLinus Torvalds 						   args.vlan_qos);
5741da177e4SLinus Torvalds 		break;
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	case SET_VLAN_FLAG_CMD:
577c17d8874SPatrick McHardy 		err = -EPERM;
5781da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
579c17d8874SPatrick McHardy 			break;
580c17d8874SPatrick McHardy 		err = vlan_dev_set_vlan_flag(dev,
5811da177e4SLinus Torvalds 					     args.u.flag,
5821da177e4SLinus Torvalds 					     args.vlan_qos);
5831da177e4SLinus Torvalds 		break;
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	case SET_VLAN_NAME_TYPE_CMD:
586c17d8874SPatrick McHardy 		err = -EPERM;
5871da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
588e35de026SPavel Emelyanov 			break;
589c17d8874SPatrick McHardy 		if ((args.u.name_type >= 0) &&
590c17d8874SPatrick McHardy 		    (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
5911da177e4SLinus Torvalds 			vlan_name_type = args.u.name_type;
5921da177e4SLinus Torvalds 			err = 0;
5931da177e4SLinus Torvalds 		} else {
5941da177e4SLinus Torvalds 			err = -EINVAL;
5951da177e4SLinus Torvalds 		}
5961da177e4SLinus Torvalds 		break;
5971da177e4SLinus Torvalds 
5981da177e4SLinus Torvalds 	case ADD_VLAN_CMD:
599c17d8874SPatrick McHardy 		err = -EPERM;
6001da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
601c17d8874SPatrick McHardy 			break;
6022ae0bf69SPatrick McHardy 		err = register_vlan_device(dev, args.u.VID);
6031da177e4SLinus Torvalds 		break;
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 	case DEL_VLAN_CMD:
606c17d8874SPatrick McHardy 		err = -EPERM;
6071da177e4SLinus Torvalds 		if (!capable(CAP_NET_ADMIN))
608c17d8874SPatrick McHardy 			break;
609c17d8874SPatrick McHardy 		err = unregister_vlan_device(dev);
6101da177e4SLinus Torvalds 		break;
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	case GET_VLAN_REALDEV_NAME_CMD:
6133f5f4346SAndrew Morton 		err = 0;
614c17d8874SPatrick McHardy 		vlan_dev_get_realdev_name(dev, args.u.device2);
6151da177e4SLinus Torvalds 		if (copy_to_user(arg, &args,
6161da177e4SLinus Torvalds 				 sizeof(struct vlan_ioctl_args))) {
6171da177e4SLinus Torvalds 			err = -EFAULT;
6181da177e4SLinus Torvalds 		}
6191da177e4SLinus Torvalds 		break;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	case GET_VLAN_VID_CMD:
6223f5f4346SAndrew Morton 		err = 0;
623c17d8874SPatrick McHardy 		vlan_dev_get_vid(dev, &vid);
6241da177e4SLinus Torvalds 		args.u.VID = vid;
6251da177e4SLinus Torvalds 		if (copy_to_user(arg, &args,
6261da177e4SLinus Torvalds 				 sizeof(struct vlan_ioctl_args))) {
6271da177e4SLinus Torvalds 		      err = -EFAULT;
6281da177e4SLinus Torvalds 		}
6291da177e4SLinus Torvalds 		break;
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds 	default:
632198a291cSPatrick McHardy 		err = -EOPNOTSUPP;
633c17d8874SPatrick McHardy 		break;
6343ff50b79SStephen Hemminger 	}
6357eb1b3d3SMika Kukkonen out:
636c17d8874SPatrick McHardy 	rtnl_unlock();
6371da177e4SLinus Torvalds 	return err;
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds 
640*69ab4b7dSPatrick McHardy static int __init vlan_proto_init(void)
641*69ab4b7dSPatrick McHardy {
642*69ab4b7dSPatrick McHardy 	int err;
643*69ab4b7dSPatrick McHardy 
644*69ab4b7dSPatrick McHardy 	pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright);
645*69ab4b7dSPatrick McHardy 	pr_info("All bugs added by %s\n", vlan_buggyright);
646*69ab4b7dSPatrick McHardy 
647*69ab4b7dSPatrick McHardy 	err = vlan_proc_init();
648*69ab4b7dSPatrick McHardy 	if (err < 0)
649*69ab4b7dSPatrick McHardy 		goto err1;
650*69ab4b7dSPatrick McHardy 
651*69ab4b7dSPatrick McHardy 	err = register_netdevice_notifier(&vlan_notifier_block);
652*69ab4b7dSPatrick McHardy 	if (err < 0)
653*69ab4b7dSPatrick McHardy 		goto err2;
654*69ab4b7dSPatrick McHardy 
655*69ab4b7dSPatrick McHardy 	err = vlan_netlink_init();
656*69ab4b7dSPatrick McHardy 	if (err < 0)
657*69ab4b7dSPatrick McHardy 		goto err3;
658*69ab4b7dSPatrick McHardy 
659*69ab4b7dSPatrick McHardy 	dev_add_pack(&vlan_packet_type);
660*69ab4b7dSPatrick McHardy 	vlan_ioctl_set(vlan_ioctl_handler);
661*69ab4b7dSPatrick McHardy 	return 0;
662*69ab4b7dSPatrick McHardy 
663*69ab4b7dSPatrick McHardy err3:
664*69ab4b7dSPatrick McHardy 	unregister_netdevice_notifier(&vlan_notifier_block);
665*69ab4b7dSPatrick McHardy err2:
666*69ab4b7dSPatrick McHardy 	vlan_proc_cleanup();
667*69ab4b7dSPatrick McHardy err1:
668*69ab4b7dSPatrick McHardy 	return err;
669*69ab4b7dSPatrick McHardy }
670*69ab4b7dSPatrick McHardy 
671*69ab4b7dSPatrick McHardy static void __exit vlan_cleanup_module(void)
672*69ab4b7dSPatrick McHardy {
673*69ab4b7dSPatrick McHardy 	unsigned int i;
674*69ab4b7dSPatrick McHardy 
675*69ab4b7dSPatrick McHardy 	vlan_ioctl_set(NULL);
676*69ab4b7dSPatrick McHardy 	vlan_netlink_fini();
677*69ab4b7dSPatrick McHardy 
678*69ab4b7dSPatrick McHardy 	unregister_netdevice_notifier(&vlan_notifier_block);
679*69ab4b7dSPatrick McHardy 
680*69ab4b7dSPatrick McHardy 	dev_remove_pack(&vlan_packet_type);
681*69ab4b7dSPatrick McHardy 
682*69ab4b7dSPatrick McHardy 	/* This table must be empty if there are no module references left. */
683*69ab4b7dSPatrick McHardy 	for (i = 0; i < VLAN_GRP_HASH_SIZE; i++)
684*69ab4b7dSPatrick McHardy 		BUG_ON(!hlist_empty(&vlan_group_hash[i]));
685*69ab4b7dSPatrick McHardy 
686*69ab4b7dSPatrick McHardy 	vlan_proc_cleanup();
687*69ab4b7dSPatrick McHardy 
688*69ab4b7dSPatrick McHardy 	synchronize_net();
689*69ab4b7dSPatrick McHardy }
690*69ab4b7dSPatrick McHardy 
691*69ab4b7dSPatrick McHardy module_init(vlan_proto_init);
692*69ab4b7dSPatrick McHardy module_exit(vlan_cleanup_module);
693*69ab4b7dSPatrick McHardy 
6941da177e4SLinus Torvalds MODULE_LICENSE("GPL");
6951da177e4SLinus Torvalds MODULE_VERSION(DRV_VERSION);
696