12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * INET 802.1Q VLAN
41da177e4SLinus Torvalds * Ethernet-type device handling.
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Authors: Ben Greear <greearb@candelatech.com>
7ad712087SPatrick McHardy * Please send support related email to: netdev@vger.kernel.org
81da177e4SLinus Torvalds * VLAN Home Page: http://www.candelatech.com/~greear/vlan.html
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * Fixes:
111da177e4SLinus Torvalds * Fix for packet capture - Nick Eggleston <nick@dccinc.com>;
121da177e4SLinus Torvalds * Add HW acceleration hooks - David S. Miller <davem@redhat.com>;
131da177e4SLinus Torvalds * Correct all the locking - David S. Miller <davem@redhat.com>;
141da177e4SLinus Torvalds * Use hash table for VLAN groups - David S. Miller <davem@redhat.com>
151da177e4SLinus Torvalds */
161da177e4SLinus Torvalds
17afab2d29SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18afab2d29SJoe Perches
194fc268d2SRandy Dunlap #include <linux/capability.h>
201da177e4SLinus Torvalds #include <linux/module.h>
211da177e4SLinus Torvalds #include <linux/netdevice.h>
221da177e4SLinus Torvalds #include <linux/skbuff.h>
235a0e3ad6STejun Heo #include <linux/slab.h>
241da177e4SLinus Torvalds #include <linux/init.h>
2582524746SFranck Bui-Huu #include <linux/rculist.h>
261da177e4SLinus Torvalds #include <net/p8022.h>
271da177e4SLinus Torvalds #include <net/arp.h>
281da177e4SLinus Torvalds #include <linux/rtnetlink.h>
291da177e4SLinus Torvalds #include <linux/notifier.h>
3061362766SPatrick McHardy #include <net/rtnetlink.h>
31e9dc8653SEric W. Biederman #include <net/net_namespace.h>
32d9ed0f0eSPavel Emelyanov #include <net/netns/generic.h>
337c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds #include <linux/if_vlan.h>
361da177e4SLinus Torvalds #include "vlan.h"
371da177e4SLinus Torvalds #include "vlanproc.h"
381da177e4SLinus Torvalds
391da177e4SLinus Torvalds #define DRV_VERSION "1.8"
401da177e4SLinus Torvalds
411da177e4SLinus Torvalds /* Global VLAN variables */
421da177e4SLinus Torvalds
43c7d03a00SAlexey Dobriyan unsigned int vlan_net_id __read_mostly;
44d9ed0f0eSPavel Emelyanov
45b3020061SStephen Hemminger const char vlan_fullname[] = "802.1Q VLAN Support";
46b3020061SStephen Hemminger const char vlan_version[] = DRV_VERSION;
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds /* End of global variables definitions. */
491da177e4SLinus Torvalds
vlan_group_prealloc_vid(struct vlan_group * vg,__be16 vlan_proto,u16 vlan_id)501fd9b1fcSPatrick McHardy static int vlan_group_prealloc_vid(struct vlan_group *vg,
511fd9b1fcSPatrick McHardy __be16 vlan_proto, u16 vlan_id)
5267727184SPavel Emelyanov {
5367727184SPavel Emelyanov struct net_device **array;
540675c285SFlorian Fainelli unsigned int vidx;
5567727184SPavel Emelyanov unsigned int size;
560675c285SFlorian Fainelli int pidx;
5767727184SPavel Emelyanov
5867727184SPavel Emelyanov ASSERT_RTNL();
5967727184SPavel Emelyanov
601fd9b1fcSPatrick McHardy pidx = vlan_proto_idx(vlan_proto);
61d0186842SFlorian Fainelli if (pidx < 0)
62d0186842SFlorian Fainelli return -EINVAL;
63d0186842SFlorian Fainelli
641fd9b1fcSPatrick McHardy vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN;
651fd9b1fcSPatrick McHardy array = vg->vlan_devices_arrays[pidx][vidx];
6667727184SPavel Emelyanov if (array != NULL)
6767727184SPavel Emelyanov return 0;
6867727184SPavel Emelyanov
6967727184SPavel Emelyanov size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN;
70a89893ddSVasily Averin array = kzalloc(size, GFP_KERNEL_ACCOUNT);
7167727184SPavel Emelyanov if (array == NULL)
7267727184SPavel Emelyanov return -ENOBUFS;
7367727184SPavel Emelyanov
74c1102e9dSDi Zhu /* paired with smp_rmb() in __vlan_group_get_device() */
75c1102e9dSDi Zhu smp_wmb();
76c1102e9dSDi Zhu
771fd9b1fcSPatrick McHardy vg->vlan_devices_arrays[pidx][vidx] = array;
7867727184SPavel Emelyanov return 0;
7942429aaeSPatrick McHardy }
8042429aaeSPatrick McHardy
vlan_stacked_transfer_operstate(const struct net_device * rootdev,struct net_device * dev,struct vlan_dev_priv * vlan)8176052d8cSMike Manning static void vlan_stacked_transfer_operstate(const struct net_device *rootdev,
8276052d8cSMike Manning struct net_device *dev,
8376052d8cSMike Manning struct vlan_dev_priv *vlan)
8476052d8cSMike Manning {
8576052d8cSMike Manning if (!(vlan->flags & VLAN_FLAG_BRIDGE_BINDING))
8676052d8cSMike Manning netif_stacked_transfer_operstate(rootdev, dev);
8776052d8cSMike Manning }
8876052d8cSMike Manning
unregister_vlan_dev(struct net_device * dev,struct list_head * head)8923289a37SEric Dumazet void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
901da177e4SLinus Torvalds {
917da82c06SJiri Pirko struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
92af301517SPatrick McHardy struct net_device *real_dev = vlan->real_dev;
935b9ea6e0SJiri Pirko struct vlan_info *vlan_info;
941da177e4SLinus Torvalds struct vlan_group *grp;
959bb8582eSPatrick McHardy u16 vlan_id = vlan->vlan_id;
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds ASSERT_RTNL();
981da177e4SLinus Torvalds
995b9ea6e0SJiri Pirko vlan_info = rtnl_dereference(real_dev->vlan_info);
1005b9ea6e0SJiri Pirko BUG_ON(!vlan_info);
1015b9ea6e0SJiri Pirko
1025b9ea6e0SJiri Pirko grp = &vlan_info->grp;
103acc5efbcSPatrick McHardy
1045b9ea6e0SJiri Pirko grp->nr_vlan_devs--;
105af301517SPatrick McHardy
10686fbe9bbSDavid Ward if (vlan->flags & VLAN_FLAG_MVRP)
10786fbe9bbSDavid Ward vlan_mvrp_request_leave(dev);
10855aee10dSEric Dumazet if (vlan->flags & VLAN_FLAG_GVRP)
10955aee10dSEric Dumazet vlan_gvrp_request_leave(dev);
11055aee10dSEric Dumazet
1111fd9b1fcSPatrick McHardy vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL);
11247701a36SVeaceslav Falico
11347701a36SVeaceslav Falico netdev_upper_dev_unlink(real_dev, dev);
11448752e1bSEric Dumazet /* Because unregister_netdevice_queue() makes sure at least one rcu
11548752e1bSEric Dumazet * grace period is respected before device freeing,
11648752e1bSEric Dumazet * we dont need to call synchronize_net() here.
11748752e1bSEric Dumazet */
11823289a37SEric Dumazet unregister_netdevice_queue(dev, head);
119ce305002SPatrick McHardy
12086fbe9bbSDavid Ward if (grp->nr_vlan_devs == 0) {
12186fbe9bbSDavid Ward vlan_mvrp_uninit_applicant(real_dev);
12270c03b49SPatrick McHardy vlan_gvrp_uninit_applicant(real_dev);
12386fbe9bbSDavid Ward }
12470c03b49SPatrick McHardy
1251fd9b1fcSPatrick McHardy vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds
vlan_check_real_dev(struct net_device * real_dev,__be16 protocol,u16 vlan_id,struct netlink_ext_ack * extack)1281fd9b1fcSPatrick McHardy int vlan_check_real_dev(struct net_device *real_dev,
12933fa3823SDavid Ahern __be16 protocol, u16 vlan_id,
13033fa3823SDavid Ahern struct netlink_ext_ack *extack)
131c1d3ee99SPatrick McHardy {
132656299f7SStephen Hemminger const char *name = real_dev->name;
13340f98e1aSPatrick McHardy
134c1d3ee99SPatrick McHardy if (real_dev->features & NETIF_F_VLAN_CHALLENGED) {
135afab2d29SJoe Perches pr_info("VLANs not supported on %s\n", name);
13633fa3823SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "VLANs not supported on device");
137c1d3ee99SPatrick McHardy return -EOPNOTSUPP;
138c1d3ee99SPatrick McHardy }
139c1d3ee99SPatrick McHardy
14033fa3823SDavid Ahern if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) {
14133fa3823SDavid Ahern NL_SET_ERR_MSG_MOD(extack, "VLAN device already exists");
142c1d3ee99SPatrick McHardy return -EEXIST;
14333fa3823SDavid Ahern }
144c1d3ee99SPatrick McHardy
145c1d3ee99SPatrick McHardy return 0;
146c1d3ee99SPatrick McHardy }
147c1d3ee99SPatrick McHardy
register_vlan_dev(struct net_device * dev,struct netlink_ext_ack * extack)14842ab19eeSDavid Ahern int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack)
149e89fe42cSPatrick McHardy {
1507da82c06SJiri Pirko struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
151e89fe42cSPatrick McHardy struct net_device *real_dev = vlan->real_dev;
1529bb8582eSPatrick McHardy u16 vlan_id = vlan->vlan_id;
1535b9ea6e0SJiri Pirko struct vlan_info *vlan_info;
1545b9ea6e0SJiri Pirko struct vlan_group *grp;
155e89fe42cSPatrick McHardy int err;
156e89fe42cSPatrick McHardy
1571fd9b1fcSPatrick McHardy err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id);
1585b9ea6e0SJiri Pirko if (err)
1595b9ea6e0SJiri Pirko return err;
1605b9ea6e0SJiri Pirko
1615b9ea6e0SJiri Pirko vlan_info = rtnl_dereference(real_dev->vlan_info);
1625b9ea6e0SJiri Pirko /* vlan_info should be there now. vlan_vid_add took care of it */
1635b9ea6e0SJiri Pirko BUG_ON(!vlan_info);
1645b9ea6e0SJiri Pirko
1655b9ea6e0SJiri Pirko grp = &vlan_info->grp;
1665b9ea6e0SJiri Pirko if (grp->nr_vlan_devs == 0) {
16770c03b49SPatrick McHardy err = vlan_gvrp_init_applicant(real_dev);
16870c03b49SPatrick McHardy if (err < 0)
1695b9ea6e0SJiri Pirko goto out_vid_del;
17086fbe9bbSDavid Ward err = vlan_mvrp_init_applicant(real_dev);
17186fbe9bbSDavid Ward if (err < 0)
17286fbe9bbSDavid Ward goto out_uninit_gvrp;
173e89fe42cSPatrick McHardy }
174e89fe42cSPatrick McHardy
1751fd9b1fcSPatrick McHardy err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id);
17667727184SPavel Emelyanov if (err < 0)
17786fbe9bbSDavid Ward goto out_uninit_mvrp;
17867727184SPavel Emelyanov
179e89fe42cSPatrick McHardy err = register_netdevice(dev);
180e89fe42cSPatrick McHardy if (err < 0)
1815df27e6cSVeaceslav Falico goto out_uninit_mvrp;
1825df27e6cSVeaceslav Falico
18342ab19eeSDavid Ahern err = netdev_upper_dev_link(real_dev, dev, extack);
1845df27e6cSVeaceslav Falico if (err)
1855df27e6cSVeaceslav Falico goto out_unregister_netdev;
186e89fe42cSPatrick McHardy
18776052d8cSMike Manning vlan_stacked_transfer_operstate(real_dev, dev, vlan);
188e89fe42cSPatrick McHardy linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */
189e89fe42cSPatrick McHardy
190e89fe42cSPatrick McHardy /* So, got the sucker initialized, now lets place
191e89fe42cSPatrick McHardy * it into our local structure.
192e89fe42cSPatrick McHardy */
1931fd9b1fcSPatrick McHardy vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev);
1945b9ea6e0SJiri Pirko grp->nr_vlan_devs++;
195e89fe42cSPatrick McHardy
196e89fe42cSPatrick McHardy return 0;
197e89fe42cSPatrick McHardy
1985df27e6cSVeaceslav Falico out_unregister_netdev:
1995df27e6cSVeaceslav Falico unregister_netdevice(dev);
20086fbe9bbSDavid Ward out_uninit_mvrp:
20186fbe9bbSDavid Ward if (grp->nr_vlan_devs == 0)
20286fbe9bbSDavid Ward vlan_mvrp_uninit_applicant(real_dev);
20386fbe9bbSDavid Ward out_uninit_gvrp:
2045b9ea6e0SJiri Pirko if (grp->nr_vlan_devs == 0)
20570c03b49SPatrick McHardy vlan_gvrp_uninit_applicant(real_dev);
2065b9ea6e0SJiri Pirko out_vid_del:
2071fd9b1fcSPatrick McHardy vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
208e89fe42cSPatrick McHardy return err;
209e89fe42cSPatrick McHardy }
210e89fe42cSPatrick McHardy
2111da177e4SLinus Torvalds /* Attach a VLAN device to a mac address (ie Ethernet Card).
2122ae0bf69SPatrick McHardy * Returns 0 if the device was created or a negative error code otherwise.
2131da177e4SLinus Torvalds */
register_vlan_device(struct net_device * real_dev,u16 vlan_id)2149bb8582eSPatrick McHardy static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds struct net_device *new_dev;
2170c0667a8SWang Sheng-Hui struct vlan_dev_priv *vlan;
2187a17a2f7SPavel Emelyanov struct net *net = dev_net(real_dev);
2197a17a2f7SPavel Emelyanov struct vlan_net *vn = net_generic(net, vlan_net_id);
2201da177e4SLinus Torvalds char name[IFNAMSIZ];
2212ae0bf69SPatrick McHardy int err;
2221da177e4SLinus Torvalds
2239bb8582eSPatrick McHardy if (vlan_id >= VLAN_VID_MASK)
2242ae0bf69SPatrick McHardy return -ERANGE;
2251da177e4SLinus Torvalds
22633fa3823SDavid Ahern err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id,
22733fa3823SDavid Ahern NULL);
2282ae0bf69SPatrick McHardy if (err < 0)
2292ae0bf69SPatrick McHardy return err;
2301da177e4SLinus Torvalds
2311da177e4SLinus Torvalds /* Gotta set up the fields for the device. */
2327a17a2f7SPavel Emelyanov switch (vn->name_type) {
2331da177e4SLinus Torvalds case VLAN_NAME_TYPE_RAW_PLUS_VID:
2341da177e4SLinus Torvalds /* name will look like: eth1.0005 */
2359bb8582eSPatrick McHardy snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id);
2361da177e4SLinus Torvalds break;
2371da177e4SLinus Torvalds case VLAN_NAME_TYPE_PLUS_VID_NO_PAD:
2381da177e4SLinus Torvalds /* Put our vlan.VID in the name.
2391da177e4SLinus Torvalds * Name will look like: vlan5
2401da177e4SLinus Torvalds */
2419bb8582eSPatrick McHardy snprintf(name, IFNAMSIZ, "vlan%i", vlan_id);
2421da177e4SLinus Torvalds break;
2431da177e4SLinus Torvalds case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD:
2441da177e4SLinus Torvalds /* Put our vlan.VID in the name.
2451da177e4SLinus Torvalds * Name will look like: eth0.5
2461da177e4SLinus Torvalds */
2479bb8582eSPatrick McHardy snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id);
2481da177e4SLinus Torvalds break;
2491da177e4SLinus Torvalds case VLAN_NAME_TYPE_PLUS_VID:
2501da177e4SLinus Torvalds /* Put our vlan.VID in the name.
2511da177e4SLinus Torvalds * Name will look like: vlan0005
2521da177e4SLinus Torvalds */
2531da177e4SLinus Torvalds default:
2549bb8582eSPatrick McHardy snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id);
2553ff50b79SStephen Hemminger }
2561da177e4SLinus Torvalds
257c835a677STom Gundersen new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name,
258c835a677STom Gundersen NET_NAME_UNKNOWN, vlan_setup);
2595dd8d1e9SArjan van de Ven
2601da177e4SLinus Torvalds if (new_dev == NULL)
2612ae0bf69SPatrick McHardy return -ENOBUFS;
2621da177e4SLinus Torvalds
26365d292a2SPavel Emelyanov dev_net_set(new_dev, net);
2641da177e4SLinus Torvalds /* need 4 bytes for extra VLAN header info,
2651da177e4SLinus Torvalds * hope the underlying device can handle it.
2661da177e4SLinus Torvalds */
2671da177e4SLinus Torvalds new_dev->mtu = real_dev->mtu;
2681da177e4SLinus Torvalds
2690c0667a8SWang Sheng-Hui vlan = vlan_dev_priv(new_dev);
2700c0667a8SWang Sheng-Hui vlan->vlan_proto = htons(ETH_P_8021Q);
2710c0667a8SWang Sheng-Hui vlan->vlan_id = vlan_id;
2720c0667a8SWang Sheng-Hui vlan->real_dev = real_dev;
2730c0667a8SWang Sheng-Hui vlan->dent = NULL;
2740c0667a8SWang Sheng-Hui vlan->flags = VLAN_FLAG_REORDER_HDR;
2751da177e4SLinus Torvalds
27607b5b17eSPatrick McHardy new_dev->rtnl_link_ops = &vlan_link_ops;
27742ab19eeSDavid Ahern err = register_vlan_dev(new_dev, NULL);
2782ae0bf69SPatrick McHardy if (err < 0)
27942429aaeSPatrick McHardy goto out_free_newdev;
2801da177e4SLinus Torvalds
2812ae0bf69SPatrick McHardy return 0;
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds out_free_newdev:
2841da177e4SLinus Torvalds free_netdev(new_dev);
2852ae0bf69SPatrick McHardy return err;
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds
vlan_sync_address(struct net_device * dev,struct net_device * vlandev)2888c979c26SPatrick McHardy static void vlan_sync_address(struct net_device *dev,
2898c979c26SPatrick McHardy struct net_device *vlandev)
2908c979c26SPatrick McHardy {
2917da82c06SJiri Pirko struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
2928c979c26SPatrick McHardy
2938c979c26SPatrick McHardy /* May be called without an actual change */
29453a2b3a1SJoe Perches if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr))
2958c979c26SPatrick McHardy return;
2968c979c26SPatrick McHardy
297308453aaSMike Manning /* vlan continues to inherit address of lower device */
298308453aaSMike Manning if (vlan_dev_inherit_address(vlandev, dev))
299308453aaSMike Manning goto out;
300308453aaSMike Manning
3018c979c26SPatrick McHardy /* vlan address was different from the old address and is equal to
3028c979c26SPatrick McHardy * the new address */
30353a2b3a1SJoe Perches if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) &&
30453a2b3a1SJoe Perches ether_addr_equal(vlandev->dev_addr, dev->dev_addr))
305a748ee24SJiri Pirko dev_uc_del(dev, vlandev->dev_addr);
3068c979c26SPatrick McHardy
3078c979c26SPatrick McHardy /* vlan address was equal to the old address and is different from
3088c979c26SPatrick McHardy * the new address */
30953a2b3a1SJoe Perches if (ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) &&
31053a2b3a1SJoe Perches !ether_addr_equal(vlandev->dev_addr, dev->dev_addr))
311a748ee24SJiri Pirko dev_uc_add(dev, vlandev->dev_addr);
3128c979c26SPatrick McHardy
313308453aaSMike Manning out:
31407fc67beSJoe Perches ether_addr_copy(vlan->real_dev_addr, dev->dev_addr);
3158c979c26SPatrick McHardy }
3168c979c26SPatrick McHardy
vlan_transfer_features(struct net_device * dev,struct net_device * vlandev)3175fb13570SPatrick McHardy static void vlan_transfer_features(struct net_device *dev,
3185fb13570SPatrick McHardy struct net_device *vlandev)
3195fb13570SPatrick McHardy {
320fc0d48b8SVlad Yasevich struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
321fc0d48b8SVlad Yasevich
3226df6398fSJakub Kicinski netif_inherit_tso_max(vlandev, dev);
323029f5fc3SJohn Fastabend
324fc0d48b8SVlad Yasevich if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto))
325029f5fc3SJohn Fastabend vlandev->hard_header_len = dev->hard_header_len;
326029f5fc3SJohn Fastabend else
327029f5fc3SJohn Fastabend vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN;
328029f5fc3SJohn Fastabend
329f4d5392eSAmerigo Wang #if IS_ENABLED(CONFIG_FCOE)
330b85daa53SVasu Dev vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
331b85daa53SVasu Dev #endif
3328a0427bbSMichał Mirosław
3339d917c20SVadim Fedorenko vlandev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
3349d917c20SVadim Fedorenko vlandev->priv_flags |= (vlan->real_dev->priv_flags & IFF_XMIT_DST_RELEASE);
3357dad9937SDavide Caratti vlandev->hw_enc_features = vlan_tnl_features(vlan->real_dev);
3369d917c20SVadim Fedorenko
3378a0427bbSMichał Mirosław netdev_update_features(vlandev);
3385fb13570SPatrick McHardy }
3395fb13570SPatrick McHardy
__vlan_device_event(struct net_device * dev,unsigned long event)3409c5ff24fSWANG Cong static int __vlan_device_event(struct net_device *dev, unsigned long event)
341802fb176SPavel Emelyanov {
3429c5ff24fSWANG Cong int err = 0;
3439c5ff24fSWANG Cong
344802fb176SPavel Emelyanov switch (event) {
345802fb176SPavel Emelyanov case NETDEV_CHANGENAME:
346802fb176SPavel Emelyanov vlan_proc_rem_dev(dev);
3479c5ff24fSWANG Cong err = vlan_proc_add_dev(dev);
348802fb176SPavel Emelyanov break;
34930688a9aSPavel Emelyanov case NETDEV_REGISTER:
3509c5ff24fSWANG Cong err = vlan_proc_add_dev(dev);
35130688a9aSPavel Emelyanov break;
35230688a9aSPavel Emelyanov case NETDEV_UNREGISTER:
35330688a9aSPavel Emelyanov vlan_proc_rem_dev(dev);
35430688a9aSPavel Emelyanov break;
355802fb176SPavel Emelyanov }
3569c5ff24fSWANG Cong
3579c5ff24fSWANG Cong return err;
358802fb176SPavel Emelyanov }
359802fb176SPavel Emelyanov
vlan_device_event(struct notifier_block * unused,unsigned long event,void * ptr)3602029cc2cSPatrick McHardy static int vlan_device_event(struct notifier_block *unused, unsigned long event,
3612029cc2cSPatrick McHardy void *ptr)
3621da177e4SLinus Torvalds {
363567c5e13SPetr Machata struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
364351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr);
365802fb176SPavel Emelyanov struct vlan_group *grp;
3665b9ea6e0SJiri Pirko struct vlan_info *vlan_info;
3671da177e4SLinus Torvalds int i, flgs;
3681da177e4SLinus Torvalds struct net_device *vlandev;
3697da82c06SJiri Pirko struct vlan_dev_priv *vlan;
3701fd9b1fcSPatrick McHardy bool last = false;
37129906f6aSPatrick McHardy LIST_HEAD(list);
3729daae9bdSGal Pressman int err;
3731da177e4SLinus Torvalds
3749c5ff24fSWANG Cong if (is_vlan_dev(dev)) {
3759c5ff24fSWANG Cong int err = __vlan_device_event(dev, event);
3769c5ff24fSWANG Cong
3779c5ff24fSWANG Cong if (err)
3789c5ff24fSWANG Cong return notifier_from_errno(err);
3799c5ff24fSWANG Cong }
380802fb176SPavel Emelyanov
381ad1afb00SPedro Garcia if ((event == NETDEV_UP) &&
382f646968fSPatrick McHardy (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
383afab2d29SJoe Perches pr_info("adding VLAN 0 to HW filter on device %s\n",
384ad1afb00SPedro Garcia dev->name);
38580d5c368SPatrick McHardy vlan_vid_add(dev, htons(ETH_P_8021Q), 0);
386ad1afb00SPedro Garcia }
387*718cb09aSVlad Buslov if (event == NETDEV_DOWN &&
388052d41c0SCong Wang (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER))
389ad1afb00SPedro Garcia vlan_vid_del(dev, htons(ETH_P_8021Q), 0);
3905b9ea6e0SJiri Pirko
3915b9ea6e0SJiri Pirko vlan_info = rtnl_dereference(dev->vlan_info);
3921da177e4SLinus Torvalds if (!vlan_info)
3935b9ea6e0SJiri Pirko goto out;
3941da177e4SLinus Torvalds grp = &vlan_info->grp;
3951da177e4SLinus Torvalds
3961da177e4SLinus Torvalds /* It is OK that we do not hold the group lock right now,
3971da177e4SLinus Torvalds * as we run under the RTNL lock.
3981da177e4SLinus Torvalds */
3991da177e4SLinus Torvalds
4001da177e4SLinus Torvalds switch (event) {
4011da177e4SLinus Torvalds case NETDEV_CHANGE:
4021fd9b1fcSPatrick McHardy /* Propagate real device state to vlan devices */
40376052d8cSMike Manning vlan_group_for_each_dev(grp, i, vlandev)
40476052d8cSMike Manning vlan_stacked_transfer_operstate(dev, vlandev,
4051da177e4SLinus Torvalds vlan_dev_priv(vlandev));
4061da177e4SLinus Torvalds break;
4078c979c26SPatrick McHardy
4088c979c26SPatrick McHardy case NETDEV_CHANGEADDR:
4091fd9b1fcSPatrick McHardy /* Adjust unicast filters on underlying device */
410d932e04aSPatrick McHardy vlan_group_for_each_dev(grp, i, vlandev) {
411d932e04aSPatrick McHardy flgs = vlandev->flags;
412d932e04aSPatrick McHardy if (!(flgs & IFF_UP))
413d932e04aSPatrick McHardy continue;
4148c979c26SPatrick McHardy
4158c979c26SPatrick McHardy vlan_sync_address(dev, vlandev);
4168c979c26SPatrick McHardy }
4178c979c26SPatrick McHardy break;
4182e477c9bSHerbert Xu
4191fd9b1fcSPatrick McHardy case NETDEV_CHANGEMTU:
4202e477c9bSHerbert Xu vlan_group_for_each_dev(grp, i, vlandev) {
4212e477c9bSHerbert Xu if (vlandev->mtu <= dev->mtu)
4222e477c9bSHerbert Xu continue;
4232e477c9bSHerbert Xu
4242e477c9bSHerbert Xu dev_set_mtu(vlandev, dev->mtu);
4252e477c9bSHerbert Xu }
4262e477c9bSHerbert Xu break;
4275fb13570SPatrick McHardy
4285fb13570SPatrick McHardy case NETDEV_FEAT_CHANGE:
4291fd9b1fcSPatrick McHardy /* Propagate device features to underlying device */
4305fb13570SPatrick McHardy vlan_group_for_each_dev(grp, i, vlandev)
4315fb13570SPatrick McHardy vlan_transfer_features(dev, vlandev);
4325fb13570SPatrick McHardy break;
43399c4a26aSDavid S. Miller
43499c4a26aSDavid S. Miller case NETDEV_DOWN: {
43599c4a26aSDavid S. Miller struct net_device *tmp;
43699c4a26aSDavid S. Miller LIST_HEAD(close_list);
4371da177e4SLinus Torvalds
4381fd9b1fcSPatrick McHardy /* Put all VLANs for this dev in the down state too. */
4391da177e4SLinus Torvalds vlan_group_for_each_dev(grp, i, vlandev) {
4401da177e4SLinus Torvalds flgs = vlandev->flags;
4411da177e4SLinus Torvalds if (!(flgs & IFF_UP))
4421da177e4SLinus Torvalds continue;
4437da82c06SJiri Pirko
4445e756593SPatrick McHardy vlan = vlan_dev_priv(vlandev);
44599c4a26aSDavid S. Miller if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
4461da177e4SLinus Torvalds list_add(&vlandev->close_list, &close_list);
4471da177e4SLinus Torvalds }
44899c4a26aSDavid S. Miller
44999c4a26aSDavid S. Miller dev_close_many(&close_list, false);
45099c4a26aSDavid S. Miller
45176052d8cSMike Manning list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) {
45276052d8cSMike Manning vlan_stacked_transfer_operstate(dev, vlandev,
45399c4a26aSDavid S. Miller vlan_dev_priv(vlandev));
45499c4a26aSDavid S. Miller list_del_init(&vlandev->close_list);
45599c4a26aSDavid S. Miller }
45699c4a26aSDavid S. Miller list_del(&close_list);
45799c4a26aSDavid S. Miller break;
4581da177e4SLinus Torvalds }
4591da177e4SLinus Torvalds case NETDEV_UP:
4601fd9b1fcSPatrick McHardy /* Put all VLANs for this dev in the up state too. */
461be346ffaSVlad Yasevich vlan_group_for_each_dev(grp, i, vlandev) {
4621da177e4SLinus Torvalds flgs = dev_get_flags(vlandev);
4631da177e4SLinus Torvalds if (flgs & IFF_UP)
4641da177e4SLinus Torvalds continue;
4657da82c06SJiri Pirko
4665e756593SPatrick McHardy vlan = vlan_dev_priv(vlandev);
467567c5e13SPetr Machata if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING))
468567c5e13SPetr Machata dev_change_flags(vlandev, flgs | IFF_UP,
46976052d8cSMike Manning extack);
4701da177e4SLinus Torvalds vlan_stacked_transfer_operstate(dev, vlandev, vlan);
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds break;
4731da177e4SLinus Torvalds
4743b27e105SDavid Lamparter case NETDEV_UNREGISTER:
4753b27e105SDavid Lamparter /* twiddle thumbs on netns device moves */
4763b27e105SDavid Lamparter if (dev->reg_state != NETREG_UNREGISTERING)
4773b27e105SDavid Lamparter break;
4781fd9b1fcSPatrick McHardy
4795b9ea6e0SJiri Pirko vlan_group_for_each_dev(grp, i, vlandev) {
48029906f6aSPatrick McHardy /* removal of last vid destroys vlan_info, abort
4815b9ea6e0SJiri Pirko * afterwards */
4821fd9b1fcSPatrick McHardy if (vlan_info->nr_vids == 1)
48329906f6aSPatrick McHardy last = true;
48429906f6aSPatrick McHardy
4851fd9b1fcSPatrick McHardy unregister_vlan_dev(vlandev, &list);
4861fd9b1fcSPatrick McHardy if (last)
48729906f6aSPatrick McHardy break;
48829906f6aSPatrick McHardy }
4891da177e4SLinus Torvalds unregister_netdevice_many(&list);
4901c01fe14SJiri Pirko break;
4911c01fe14SJiri Pirko
4921c01fe14SJiri Pirko case NETDEV_PRE_TYPE_CHANGE:
49318c22a03SJiri Pirko /* Forbid underlaying device to change its type. */
4941c01fe14SJiri Pirko if (vlan_uses_dev(dev))
49518c22a03SJiri Pirko return NOTIFY_BAD;
49699606477SBen Hutchings break;
49799606477SBen Hutchings
4987c899432SBen Hutchings case NETDEV_NOTIFY_PEERS:
4994aa5dee4SJiri Pirko case NETDEV_BONDING_FAILOVER:
50099606477SBen Hutchings case NETDEV_RESEND_IGMP:
5011fd9b1fcSPatrick McHardy /* Propagate to vlan devices */
5027c899432SBen Hutchings vlan_group_for_each_dev(grp, i, vlandev)
50399606477SBen Hutchings call_netdevice_notifiers(event, vlandev);
5049daae9bdSGal Pressman break;
5059daae9bdSGal Pressman
5069daae9bdSGal Pressman case NETDEV_CVLAN_FILTER_PUSH_INFO:
5079daae9bdSGal Pressman err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021Q));
5089daae9bdSGal Pressman if (err)
5099daae9bdSGal Pressman return notifier_from_errno(err);
5109daae9bdSGal Pressman break;
5119daae9bdSGal Pressman
5129daae9bdSGal Pressman case NETDEV_CVLAN_FILTER_DROP_INFO:
5139daae9bdSGal Pressman vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021Q));
5149daae9bdSGal Pressman break;
5159daae9bdSGal Pressman
5169daae9bdSGal Pressman case NETDEV_SVLAN_FILTER_PUSH_INFO:
5179daae9bdSGal Pressman err = vlan_filter_push_vids(vlan_info, htons(ETH_P_8021AD));
5189daae9bdSGal Pressman if (err)
5199daae9bdSGal Pressman return notifier_from_errno(err);
5209daae9bdSGal Pressman break;
5219daae9bdSGal Pressman
5229daae9bdSGal Pressman case NETDEV_SVLAN_FILTER_DROP_INFO:
5239daae9bdSGal Pressman vlan_filter_drop_vids(vlan_info, htons(ETH_P_8021AD));
5243ff50b79SStephen Hemminger break;
5251da177e4SLinus Torvalds }
5261da177e4SLinus Torvalds
5271da177e4SLinus Torvalds out:
5281da177e4SLinus Torvalds return NOTIFY_DONE;
5291da177e4SLinus Torvalds }
53069ab4b7dSPatrick McHardy
53169ab4b7dSPatrick McHardy static struct notifier_block vlan_notifier_block __read_mostly = {
53269ab4b7dSPatrick McHardy .notifier_call = vlan_device_event,
53369ab4b7dSPatrick McHardy };
5341da177e4SLinus Torvalds
5351da177e4SLinus Torvalds /*
5361da177e4SLinus Torvalds * VLAN IOCTL handler.
5371da177e4SLinus Torvalds * o execute requested action or pass command to the device driver
5381da177e4SLinus Torvalds * arg is really a struct vlan_ioctl_args __user *.
539881d966bSEric W. Biederman */
vlan_ioctl_handler(struct net * net,void __user * arg)5401da177e4SLinus Torvalds static int vlan_ioctl_handler(struct net *net, void __user *arg)
541c17d8874SPatrick McHardy {
5421da177e4SLinus Torvalds int err;
543c17d8874SPatrick McHardy struct vlan_ioctl_args args;
5441da177e4SLinus Torvalds struct net_device *dev = NULL;
5451da177e4SLinus Torvalds
5461da177e4SLinus Torvalds if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args)))
5471da177e4SLinus Torvalds return -EFAULT;
5481da177e4SLinus Torvalds
5499c403b6bSGao Feng /* Null terminate this sucker, just in case. */
5509c403b6bSGao Feng args.device1[sizeof(args.device1) - 1] = 0;
5511da177e4SLinus Torvalds args.u.device2[sizeof(args.u.device2) - 1] = 0;
552c17d8874SPatrick McHardy
553c17d8874SPatrick McHardy rtnl_lock();
5541da177e4SLinus Torvalds
5551da177e4SLinus Torvalds switch (args.cmd) {
556c17d8874SPatrick McHardy case SET_VLAN_INGRESS_PRIORITY_CMD:
557c17d8874SPatrick McHardy case SET_VLAN_EGRESS_PRIORITY_CMD:
558c17d8874SPatrick McHardy case SET_VLAN_FLAG_CMD:
559c17d8874SPatrick McHardy case ADD_VLAN_CMD:
560c17d8874SPatrick McHardy case DEL_VLAN_CMD:
561c17d8874SPatrick McHardy case GET_VLAN_REALDEV_NAME_CMD:
562c17d8874SPatrick McHardy case GET_VLAN_VID_CMD:
56365d292a2SPavel Emelyanov err = -ENODEV;
564c17d8874SPatrick McHardy dev = __dev_get_by_name(net, args.device1);
565c17d8874SPatrick McHardy if (!dev)
566c17d8874SPatrick McHardy goto out;
567c17d8874SPatrick McHardy
56826a25239SJoonwoo Park err = -EINVAL;
569c17d8874SPatrick McHardy if (args.cmd != ADD_VLAN_CMD && !is_vlan_dev(dev))
570c17d8874SPatrick McHardy goto out;
571c17d8874SPatrick McHardy }
572c17d8874SPatrick McHardy
573c17d8874SPatrick McHardy switch (args.cmd) {
574c17d8874SPatrick McHardy case SET_VLAN_INGRESS_PRIORITY_CMD:
575276996fdSEric W. Biederman err = -EPERM;
576c17d8874SPatrick McHardy if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
577c17d8874SPatrick McHardy break;
5781da177e4SLinus Torvalds vlan_dev_set_ingress_priority(dev,
5791da177e4SLinus Torvalds args.u.skb_priority,
580fffe470aSPatrick McHardy args.vlan_qos);
5811da177e4SLinus Torvalds err = 0;
5821da177e4SLinus Torvalds break;
5831da177e4SLinus Torvalds
584c17d8874SPatrick McHardy case SET_VLAN_EGRESS_PRIORITY_CMD:
585276996fdSEric W. Biederman err = -EPERM;
586c17d8874SPatrick McHardy if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
587c17d8874SPatrick McHardy break;
5881da177e4SLinus Torvalds err = vlan_dev_set_egress_priority(dev,
5891da177e4SLinus Torvalds args.u.skb_priority,
5901da177e4SLinus Torvalds args.vlan_qos);
5911da177e4SLinus Torvalds break;
5921da177e4SLinus Torvalds
593c17d8874SPatrick McHardy case SET_VLAN_FLAG_CMD:
594276996fdSEric W. Biederman err = -EPERM;
595c17d8874SPatrick McHardy if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
596b3ce0325SPatrick McHardy break;
597b3ce0325SPatrick McHardy err = vlan_dev_change_flags(dev,
598b3ce0325SPatrick McHardy args.vlan_qos ? args.u.flag : 0,
5991da177e4SLinus Torvalds args.u.flag);
6001da177e4SLinus Torvalds break;
6011da177e4SLinus Torvalds
602c17d8874SPatrick McHardy case SET_VLAN_NAME_TYPE_CMD:
603276996fdSEric W. Biederman err = -EPERM;
604e35de026SPavel Emelyanov if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
605403f0727STobias Klauser break;
6067a17a2f7SPavel Emelyanov if (args.u.name_type < VLAN_NAME_TYPE_HIGHEST) {
6077a17a2f7SPavel Emelyanov struct vlan_net *vn;
6087a17a2f7SPavel Emelyanov
6097a17a2f7SPavel Emelyanov vn = net_generic(net, vlan_net_id);
6101da177e4SLinus Torvalds vn->name_type = args.u.name_type;
6111da177e4SLinus Torvalds err = 0;
6121da177e4SLinus Torvalds } else {
6131da177e4SLinus Torvalds err = -EINVAL;
6141da177e4SLinus Torvalds }
6151da177e4SLinus Torvalds break;
6161da177e4SLinus Torvalds
617c17d8874SPatrick McHardy case ADD_VLAN_CMD:
618276996fdSEric W. Biederman err = -EPERM;
619c17d8874SPatrick McHardy if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
6202ae0bf69SPatrick McHardy break;
6211da177e4SLinus Torvalds err = register_vlan_device(dev, args.u.VID);
6221da177e4SLinus Torvalds break;
6231da177e4SLinus Torvalds
624c17d8874SPatrick McHardy case DEL_VLAN_CMD:
625276996fdSEric W. Biederman err = -EPERM;
626c17d8874SPatrick McHardy if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
62723289a37SEric Dumazet break;
628af301517SPatrick McHardy unregister_vlan_dev(dev, NULL);
6291da177e4SLinus Torvalds err = 0;
6301da177e4SLinus Torvalds break;
6311da177e4SLinus Torvalds
6323f5f4346SAndrew Morton case GET_VLAN_REALDEV_NAME_CMD:
6339c153d38SKees Cook err = 0;
6349c153d38SKees Cook vlan_dev_get_realdev_name(dev, args.u.device2,
6351da177e4SLinus Torvalds sizeof(args.u.device2));
6362029cc2cSPatrick McHardy if (copy_to_user(arg, &args,
6371da177e4SLinus Torvalds sizeof(struct vlan_ioctl_args)))
6381da177e4SLinus Torvalds err = -EFAULT;
6391da177e4SLinus Torvalds break;
6401da177e4SLinus Torvalds
6413f5f4346SAndrew Morton case GET_VLAN_VID_CMD:
64222d1ba74SPatrick McHardy err = 0;
6431da177e4SLinus Torvalds args.u.VID = vlan_dev_vlan_id(dev);
6442029cc2cSPatrick McHardy if (copy_to_user(arg, &args,
6451da177e4SLinus Torvalds sizeof(struct vlan_ioctl_args)))
6461da177e4SLinus Torvalds err = -EFAULT;
6471da177e4SLinus Torvalds break;
6481da177e4SLinus Torvalds
649198a291cSPatrick McHardy default:
650c17d8874SPatrick McHardy err = -EOPNOTSUPP;
6513ff50b79SStephen Hemminger break;
6527eb1b3d3SMika Kukkonen }
653c17d8874SPatrick McHardy out:
6541da177e4SLinus Torvalds rtnl_unlock();
6551da177e4SLinus Torvalds return err;
6561da177e4SLinus Torvalds }
6572c8c1e72SAlexey Dobriyan
vlan_init_net(struct net * net)658d9ed0f0eSPavel Emelyanov static int __net_init vlan_init_net(struct net *net)
659946d1a92SEric W. Biederman {
660d9ed0f0eSPavel Emelyanov struct vlan_net *vn = net_generic(net, vlan_net_id);
661d9ed0f0eSPavel Emelyanov int err;
6627a17a2f7SPavel Emelyanov
6637a17a2f7SPavel Emelyanov vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD;
664cd1c7014SPavel Emelyanov
665cd1c7014SPavel Emelyanov err = vlan_proc_init(net);
666d9ed0f0eSPavel Emelyanov
667d9ed0f0eSPavel Emelyanov return err;
668d9ed0f0eSPavel Emelyanov }
6692c8c1e72SAlexey Dobriyan
vlan_exit_net(struct net * net)670d9ed0f0eSPavel Emelyanov static void __net_exit vlan_exit_net(struct net *net)
671cd1c7014SPavel Emelyanov {
672d9ed0f0eSPavel Emelyanov vlan_proc_cleanup(net);
673d9ed0f0eSPavel Emelyanov }
674d9ed0f0eSPavel Emelyanov
675d9ed0f0eSPavel Emelyanov static struct pernet_operations vlan_net_ops = {
676d9ed0f0eSPavel Emelyanov .init = vlan_init_net,
677946d1a92SEric W. Biederman .exit = vlan_exit_net,
678946d1a92SEric W. Biederman .id = &vlan_net_id,
679d9ed0f0eSPavel Emelyanov .size = sizeof(struct vlan_net),
680d9ed0f0eSPavel Emelyanov };
68169ab4b7dSPatrick McHardy
vlan_proto_init(void)68269ab4b7dSPatrick McHardy static int __init vlan_proto_init(void)
68369ab4b7dSPatrick McHardy {
68469ab4b7dSPatrick McHardy int err;
685da7c06c4SJustin Mattock
68669ab4b7dSPatrick McHardy pr_info("%s v%s\n", vlan_fullname, vlan_version);
68791e2ff35SEric W. Biederman
688d9ed0f0eSPavel Emelyanov err = register_pernet_subsys(&vlan_net_ops);
689d9ed0f0eSPavel Emelyanov if (err < 0)
690d9ed0f0eSPavel Emelyanov goto err0;
69169ab4b7dSPatrick McHardy
69269ab4b7dSPatrick McHardy err = register_netdevice_notifier(&vlan_notifier_block);
69369ab4b7dSPatrick McHardy if (err < 0)
69469ab4b7dSPatrick McHardy goto err2;
69570c03b49SPatrick McHardy
69669ab4b7dSPatrick McHardy err = vlan_gvrp_init();
69769ab4b7dSPatrick McHardy if (err < 0)
69869ab4b7dSPatrick McHardy goto err3;
69986fbe9bbSDavid Ward
70070c03b49SPatrick McHardy err = vlan_mvrp_init();
70170c03b49SPatrick McHardy if (err < 0)
70270c03b49SPatrick McHardy goto err4;
70386fbe9bbSDavid Ward
70486fbe9bbSDavid Ward err = vlan_netlink_init();
70586fbe9bbSDavid Ward if (err < 0)
70686fbe9bbSDavid Ward goto err5;
70769ab4b7dSPatrick McHardy
70869ab4b7dSPatrick McHardy vlan_ioctl_set(vlan_ioctl_handler);
70969ab4b7dSPatrick McHardy return 0;
71086fbe9bbSDavid Ward
71186fbe9bbSDavid Ward err5:
71270c03b49SPatrick McHardy vlan_mvrp_uninit();
71370c03b49SPatrick McHardy err4:
71469ab4b7dSPatrick McHardy vlan_gvrp_uninit();
71569ab4b7dSPatrick McHardy err3:
71669ab4b7dSPatrick McHardy unregister_netdevice_notifier(&vlan_notifier_block);
71791e2ff35SEric W. Biederman err2:
718d9ed0f0eSPavel Emelyanov unregister_pernet_subsys(&vlan_net_ops);
71969ab4b7dSPatrick McHardy err0:
72069ab4b7dSPatrick McHardy return err;
72169ab4b7dSPatrick McHardy }
72269ab4b7dSPatrick McHardy
vlan_cleanup_module(void)72369ab4b7dSPatrick McHardy static void __exit vlan_cleanup_module(void)
72469ab4b7dSPatrick McHardy {
72566e5133fSToshiaki Makita vlan_ioctl_set(NULL);
72669ab4b7dSPatrick McHardy
72769ab4b7dSPatrick McHardy vlan_netlink_fini();
72869ab4b7dSPatrick McHardy
72969ab4b7dSPatrick McHardy unregister_netdevice_notifier(&vlan_notifier_block);
73091e2ff35SEric W. Biederman
7316e327c11SJesper Dangaard Brouer unregister_pernet_subsys(&vlan_net_ops);
73270c03b49SPatrick McHardy rcu_barrier(); /* Wait for completion of call_rcu()'s */
73386fbe9bbSDavid Ward
73470c03b49SPatrick McHardy vlan_mvrp_uninit();
73569ab4b7dSPatrick McHardy vlan_gvrp_uninit();
73669ab4b7dSPatrick McHardy }
73769ab4b7dSPatrick McHardy
73869ab4b7dSPatrick McHardy module_init(vlan_proto_init);
73969ab4b7dSPatrick McHardy module_exit(vlan_cleanup_module);
7401da177e4SLinus Torvalds
7411da177e4SLinus Torvalds MODULE_LICENSE("GPL");
742 MODULE_VERSION(DRV_VERSION);
743