diff options
Diffstat (limited to 'net/8021q/vlan.c')
-rw-r--r-- | net/8021q/vlan.c | 147 |
1 files changed, 104 insertions, 43 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index b33410abfd6b..2a739adaa92b 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/rtnetlink.h> | 32 | #include <linux/rtnetlink.h> |
33 | #include <linux/notifier.h> | 33 | #include <linux/notifier.h> |
34 | #include <net/net_namespace.h> | 34 | #include <net/net_namespace.h> |
35 | #include <net/netns/generic.h> | ||
35 | 36 | ||
36 | #include <linux/if_vlan.h> | 37 | #include <linux/if_vlan.h> |
37 | #include "vlan.h" | 38 | #include "vlan.h" |
@@ -41,6 +42,8 @@ | |||
41 | 42 | ||
42 | /* Global VLAN variables */ | 43 | /* Global VLAN variables */ |
43 | 44 | ||
45 | int vlan_net_id; | ||
46 | |||
44 | /* Our listing of VLAN group(s) */ | 47 | /* Our listing of VLAN group(s) */ |
45 | static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE]; | 48 | static struct hlist_head vlan_group_hash[VLAN_GRP_HASH_SIZE]; |
46 | 49 | ||
@@ -49,9 +52,6 @@ static char vlan_version[] = DRV_VERSION; | |||
49 | static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>"; | 52 | static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>"; |
50 | static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>"; | 53 | static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>"; |
51 | 54 | ||
52 | /* Determines interface naming scheme. */ | ||
53 | unsigned short vlan_name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; | ||
54 | |||
55 | static struct packet_type vlan_packet_type = { | 55 | static struct packet_type vlan_packet_type = { |
56 | .type = __constant_htons(ETH_P_8021Q), | 56 | .type = __constant_htons(ETH_P_8021Q), |
57 | .func = vlan_skb_recv, /* VLAN receive method */ | 57 | .func = vlan_skb_recv, /* VLAN receive method */ |
@@ -65,14 +65,14 @@ static inline unsigned int vlan_grp_hashfn(unsigned int idx) | |||
65 | } | 65 | } |
66 | 66 | ||
67 | /* Must be invoked with RCU read lock (no preempt) */ | 67 | /* Must be invoked with RCU read lock (no preempt) */ |
68 | static struct vlan_group *__vlan_find_group(int real_dev_ifindex) | 68 | static struct vlan_group *__vlan_find_group(struct net_device *real_dev) |
69 | { | 69 | { |
70 | struct vlan_group *grp; | 70 | struct vlan_group *grp; |
71 | struct hlist_node *n; | 71 | struct hlist_node *n; |
72 | int hash = vlan_grp_hashfn(real_dev_ifindex); | 72 | int hash = vlan_grp_hashfn(real_dev->ifindex); |
73 | 73 | ||
74 | hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) { | 74 | hlist_for_each_entry_rcu(grp, n, &vlan_group_hash[hash], hlist) { |
75 | if (grp->real_dev_ifindex == real_dev_ifindex) | 75 | if (grp->real_dev == real_dev) |
76 | return grp; | 76 | return grp; |
77 | } | 77 | } |
78 | 78 | ||
@@ -86,7 +86,7 @@ static struct vlan_group *__vlan_find_group(int real_dev_ifindex) | |||
86 | struct net_device *__find_vlan_dev(struct net_device *real_dev, | 86 | struct net_device *__find_vlan_dev(struct net_device *real_dev, |
87 | unsigned short VID) | 87 | unsigned short VID) |
88 | { | 88 | { |
89 | struct vlan_group *grp = __vlan_find_group(real_dev->ifindex); | 89 | struct vlan_group *grp = __vlan_find_group(real_dev); |
90 | 90 | ||
91 | if (grp) | 91 | if (grp) |
92 | return vlan_group_get_device(grp, VID); | 92 | return vlan_group_get_device(grp, VID); |
@@ -103,32 +103,38 @@ static void vlan_group_free(struct vlan_group *grp) | |||
103 | kfree(grp); | 103 | kfree(grp); |
104 | } | 104 | } |
105 | 105 | ||
106 | static struct vlan_group *vlan_group_alloc(int ifindex) | 106 | static struct vlan_group *vlan_group_alloc(struct net_device *real_dev) |
107 | { | 107 | { |
108 | struct vlan_group *grp; | 108 | struct vlan_group *grp; |
109 | unsigned int size; | ||
110 | unsigned int i; | ||
111 | 109 | ||
112 | grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL); | 110 | grp = kzalloc(sizeof(struct vlan_group), GFP_KERNEL); |
113 | if (!grp) | 111 | if (!grp) |
114 | return NULL; | 112 | return NULL; |
115 | 113 | ||
116 | size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; | 114 | grp->real_dev = real_dev; |
117 | |||
118 | for (i = 0; i < VLAN_GROUP_ARRAY_SPLIT_PARTS; i++) { | ||
119 | grp->vlan_devices_arrays[i] = kzalloc(size, GFP_KERNEL); | ||
120 | if (!grp->vlan_devices_arrays[i]) | ||
121 | goto err; | ||
122 | } | ||
123 | |||
124 | grp->real_dev_ifindex = ifindex; | ||
125 | hlist_add_head_rcu(&grp->hlist, | 115 | hlist_add_head_rcu(&grp->hlist, |
126 | &vlan_group_hash[vlan_grp_hashfn(ifindex)]); | 116 | &vlan_group_hash[vlan_grp_hashfn(real_dev->ifindex)]); |
127 | return grp; | 117 | return grp; |
118 | } | ||
128 | 119 | ||
129 | err: | 120 | static int vlan_group_prealloc_vid(struct vlan_group *vg, int vid) |
130 | vlan_group_free(grp); | 121 | { |
131 | return NULL; | 122 | struct net_device **array; |
123 | unsigned int size; | ||
124 | |||
125 | ASSERT_RTNL(); | ||
126 | |||
127 | array = vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN]; | ||
128 | if (array != NULL) | ||
129 | return 0; | ||
130 | |||
131 | size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; | ||
132 | array = kzalloc(size, GFP_KERNEL); | ||
133 | if (array == NULL) | ||
134 | return -ENOBUFS; | ||
135 | |||
136 | vg->vlan_devices_arrays[vid / VLAN_GROUP_ARRAY_PART_LEN] = array; | ||
137 | return 0; | ||
132 | } | 138 | } |
133 | 139 | ||
134 | static void vlan_rcu_free(struct rcu_head *rcu) | 140 | static void vlan_rcu_free(struct rcu_head *rcu) |
@@ -145,11 +151,9 @@ void unregister_vlan_dev(struct net_device *dev) | |||
145 | 151 | ||
146 | ASSERT_RTNL(); | 152 | ASSERT_RTNL(); |
147 | 153 | ||
148 | grp = __vlan_find_group(real_dev->ifindex); | 154 | grp = __vlan_find_group(real_dev); |
149 | BUG_ON(!grp); | 155 | BUG_ON(!grp); |
150 | 156 | ||
151 | vlan_proc_rem_dev(dev); | ||
152 | |||
153 | /* Take it out of our own structures, but be sure to interlock with | 157 | /* Take it out of our own structures, but be sure to interlock with |
154 | * HW accelerating devices or SW vlan input packet processing. | 158 | * HW accelerating devices or SW vlan input packet processing. |
155 | */ | 159 | */ |
@@ -240,13 +244,17 @@ int register_vlan_dev(struct net_device *dev) | |||
240 | struct vlan_group *grp, *ngrp = NULL; | 244 | struct vlan_group *grp, *ngrp = NULL; |
241 | int err; | 245 | int err; |
242 | 246 | ||
243 | grp = __vlan_find_group(real_dev->ifindex); | 247 | grp = __vlan_find_group(real_dev); |
244 | if (!grp) { | 248 | if (!grp) { |
245 | ngrp = grp = vlan_group_alloc(real_dev->ifindex); | 249 | ngrp = grp = vlan_group_alloc(real_dev); |
246 | if (!grp) | 250 | if (!grp) |
247 | return -ENOBUFS; | 251 | return -ENOBUFS; |
248 | } | 252 | } |
249 | 253 | ||
254 | err = vlan_group_prealloc_vid(grp, vlan_id); | ||
255 | if (err < 0) | ||
256 | goto out_free_group; | ||
257 | |||
250 | err = register_netdevice(dev); | 258 | err = register_netdevice(dev); |
251 | if (err < 0) | 259 | if (err < 0) |
252 | goto out_free_group; | 260 | goto out_free_group; |
@@ -268,9 +276,6 @@ int register_vlan_dev(struct net_device *dev) | |||
268 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) | 276 | if (real_dev->features & NETIF_F_HW_VLAN_FILTER) |
269 | real_dev->vlan_rx_add_vid(real_dev, vlan_id); | 277 | real_dev->vlan_rx_add_vid(real_dev, vlan_id); |
270 | 278 | ||
271 | if (vlan_proc_add_dev(dev) < 0) | ||
272 | pr_warning("8021q: failed to add proc entry for %s\n", | ||
273 | dev->name); | ||
274 | return 0; | 279 | return 0; |
275 | 280 | ||
276 | out_free_group: | 281 | out_free_group: |
@@ -286,6 +291,8 @@ static int register_vlan_device(struct net_device *real_dev, | |||
286 | unsigned short VLAN_ID) | 291 | unsigned short VLAN_ID) |
287 | { | 292 | { |
288 | struct net_device *new_dev; | 293 | struct net_device *new_dev; |
294 | struct net *net = dev_net(real_dev); | ||
295 | struct vlan_net *vn = net_generic(net, vlan_net_id); | ||
289 | char name[IFNAMSIZ]; | 296 | char name[IFNAMSIZ]; |
290 | int err; | 297 | int err; |
291 | 298 | ||
@@ -297,7 +304,7 @@ static int register_vlan_device(struct net_device *real_dev, | |||
297 | return err; | 304 | return err; |
298 | 305 | ||
299 | /* Gotta set up the fields for the device. */ | 306 | /* Gotta set up the fields for the device. */ |
300 | switch (vlan_name_type) { | 307 | switch (vn->name_type) { |
301 | case VLAN_NAME_TYPE_RAW_PLUS_VID: | 308 | case VLAN_NAME_TYPE_RAW_PLUS_VID: |
302 | /* name will look like: eth1.0005 */ | 309 | /* name will look like: eth1.0005 */ |
303 | snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID); | 310 | snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID); |
@@ -328,6 +335,7 @@ static int register_vlan_device(struct net_device *real_dev, | |||
328 | if (new_dev == NULL) | 335 | if (new_dev == NULL) |
329 | return -ENOBUFS; | 336 | return -ENOBUFS; |
330 | 337 | ||
338 | dev_net_set(new_dev, net); | ||
331 | /* need 4 bytes for extra VLAN header info, | 339 | /* need 4 bytes for extra VLAN header info, |
332 | * hope the underlying device can handle it. | 340 | * hope the underlying device can handle it. |
333 | */ | 341 | */ |
@@ -383,6 +391,14 @@ static void __vlan_device_event(struct net_device *dev, unsigned long event) | |||
383 | pr_warning("8021q: failed to change proc name for %s\n", | 391 | pr_warning("8021q: failed to change proc name for %s\n", |
384 | dev->name); | 392 | dev->name); |
385 | break; | 393 | break; |
394 | case NETDEV_REGISTER: | ||
395 | if (vlan_proc_add_dev(dev) < 0) | ||
396 | pr_warning("8021q: failed to add proc entry for %s\n", | ||
397 | dev->name); | ||
398 | break; | ||
399 | case NETDEV_UNREGISTER: | ||
400 | vlan_proc_rem_dev(dev); | ||
401 | break; | ||
386 | } | 402 | } |
387 | } | 403 | } |
388 | 404 | ||
@@ -394,15 +410,12 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event, | |||
394 | int i, flgs; | 410 | int i, flgs; |
395 | struct net_device *vlandev; | 411 | struct net_device *vlandev; |
396 | 412 | ||
397 | if (dev->nd_net != &init_net) | ||
398 | return NOTIFY_DONE; | ||
399 | |||
400 | if (is_vlan_dev(dev)) { | 413 | if (is_vlan_dev(dev)) { |
401 | __vlan_device_event(dev, event); | 414 | __vlan_device_event(dev, event); |
402 | goto out; | 415 | goto out; |
403 | } | 416 | } |
404 | 417 | ||
405 | grp = __vlan_find_group(dev->ifindex); | 418 | grp = __vlan_find_group(dev); |
406 | if (!grp) | 419 | if (!grp) |
407 | goto out; | 420 | goto out; |
408 | 421 | ||
@@ -522,7 +535,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) | |||
522 | case GET_VLAN_REALDEV_NAME_CMD: | 535 | case GET_VLAN_REALDEV_NAME_CMD: |
523 | case GET_VLAN_VID_CMD: | 536 | case GET_VLAN_VID_CMD: |
524 | err = -ENODEV; | 537 | err = -ENODEV; |
525 | dev = __dev_get_by_name(&init_net, args.device1); | 538 | dev = __dev_get_by_name(net, args.device1); |
526 | if (!dev) | 539 | if (!dev) |
527 | goto out; | 540 | goto out; |
528 | 541 | ||
@@ -567,7 +580,10 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg) | |||
567 | break; | 580 | break; |
568 | if ((args.u.name_type >= 0) && | 581 | if ((args.u.name_type >= 0) && |
569 | (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { | 582 | (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { |
570 | vlan_name_type = args.u.name_type; | 583 | struct vlan_net *vn; |
584 | |||
585 | vn = net_generic(net, vlan_net_id); | ||
586 | vn->name_type = args.u.name_type; | ||
571 | err = 0; | 587 | err = 0; |
572 | } else { | 588 | } else { |
573 | err = -EINVAL; | 589 | err = -EINVAL; |
@@ -615,6 +631,51 @@ out: | |||
615 | return err; | 631 | return err; |
616 | } | 632 | } |
617 | 633 | ||
634 | static int vlan_init_net(struct net *net) | ||
635 | { | ||
636 | int err; | ||
637 | struct vlan_net *vn; | ||
638 | |||
639 | err = -ENOMEM; | ||
640 | vn = kzalloc(sizeof(struct vlan_net), GFP_KERNEL); | ||
641 | if (vn == NULL) | ||
642 | goto err_alloc; | ||
643 | |||
644 | err = net_assign_generic(net, vlan_net_id, vn); | ||
645 | if (err < 0) | ||
646 | goto err_assign; | ||
647 | |||
648 | vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; | ||
649 | |||
650 | err = vlan_proc_init(net); | ||
651 | if (err < 0) | ||
652 | goto err_proc; | ||
653 | |||
654 | return 0; | ||
655 | |||
656 | err_proc: | ||
657 | /* nothing */ | ||
658 | err_assign: | ||
659 | kfree(vn); | ||
660 | err_alloc: | ||
661 | return err; | ||
662 | } | ||
663 | |||
664 | static void vlan_exit_net(struct net *net) | ||
665 | { | ||
666 | struct vlan_net *vn; | ||
667 | |||
668 | vn = net_generic(net, vlan_net_id); | ||
669 | rtnl_kill_links(net, &vlan_link_ops); | ||
670 | vlan_proc_cleanup(net); | ||
671 | kfree(vn); | ||
672 | } | ||
673 | |||
674 | static struct pernet_operations vlan_net_ops = { | ||
675 | .init = vlan_init_net, | ||
676 | .exit = vlan_exit_net, | ||
677 | }; | ||
678 | |||
618 | static int __init vlan_proto_init(void) | 679 | static int __init vlan_proto_init(void) |
619 | { | 680 | { |
620 | int err; | 681 | int err; |
@@ -622,9 +683,9 @@ static int __init vlan_proto_init(void) | |||
622 | pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); | 683 | pr_info("%s v%s %s\n", vlan_fullname, vlan_version, vlan_copyright); |
623 | pr_info("All bugs added by %s\n", vlan_buggyright); | 684 | pr_info("All bugs added by %s\n", vlan_buggyright); |
624 | 685 | ||
625 | err = vlan_proc_init(); | 686 | err = register_pernet_gen_device(&vlan_net_id, &vlan_net_ops); |
626 | if (err < 0) | 687 | if (err < 0) |
627 | goto err1; | 688 | goto err0; |
628 | 689 | ||
629 | err = register_netdevice_notifier(&vlan_notifier_block); | 690 | err = register_netdevice_notifier(&vlan_notifier_block); |
630 | if (err < 0) | 691 | if (err < 0) |
@@ -641,8 +702,8 @@ static int __init vlan_proto_init(void) | |||
641 | err3: | 702 | err3: |
642 | unregister_netdevice_notifier(&vlan_notifier_block); | 703 | unregister_netdevice_notifier(&vlan_notifier_block); |
643 | err2: | 704 | err2: |
644 | vlan_proc_cleanup(); | 705 | unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); |
645 | err1: | 706 | err0: |
646 | return err; | 707 | return err; |
647 | } | 708 | } |
648 | 709 | ||
@@ -661,7 +722,7 @@ static void __exit vlan_cleanup_module(void) | |||
661 | for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) | 722 | for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) |
662 | BUG_ON(!hlist_empty(&vlan_group_hash[i])); | 723 | BUG_ON(!hlist_empty(&vlan_group_hash[i])); |
663 | 724 | ||
664 | vlan_proc_cleanup(); | 725 | unregister_pernet_gen_device(vlan_net_id, &vlan_net_ops); |
665 | 726 | ||
666 | synchronize_net(); | 727 | synchronize_net(); |
667 | } | 728 | } |