Blame view
net/8021q/vlan.c
16.6 KB
1da177e4c
|
1 2 3 4 5 |
/* * INET 802.1Q VLAN * Ethernet-type device handling. * * Authors: Ben Greear <greearb@candelatech.com> |
ad712087f
|
6 |
* Please send support related email to: netdev@vger.kernel.org |
1da177e4c
|
7 |
* VLAN Home Page: http://www.candelatech.com/~greear/vlan.html |
122952fc2
|
8 |
* |
1da177e4c
|
9 10 11 12 13 14 15 16 17 18 19 |
* Fixes: * Fix for packet capture - Nick Eggleston <nick@dccinc.com>; * 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> * * 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. */ |
afab2d299
|
20 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
4fc268d24
|
21 |
#include <linux/capability.h> |
1da177e4c
|
22 23 24 |
#include <linux/module.h> #include <linux/netdevice.h> #include <linux/skbuff.h> |
5a0e3ad6a
|
25 |
#include <linux/slab.h> |
1da177e4c
|
26 |
#include <linux/init.h> |
82524746c
|
27 |
#include <linux/rculist.h> |
1da177e4c
|
28 29 30 31 |
#include <net/p8022.h> #include <net/arp.h> #include <linux/rtnetlink.h> #include <linux/notifier.h> |
61362766d
|
32 |
#include <net/rtnetlink.h> |
e9dc86534
|
33 |
#include <net/net_namespace.h> |
d9ed0f0e2
|
34 |
#include <net/netns/generic.h> |
61362766d
|
35 |
#include <asm/uaccess.h> |
1da177e4c
|
36 37 38 39 40 41 42 43 |
#include <linux/if_vlan.h> #include "vlan.h" #include "vlanproc.h" #define DRV_VERSION "1.8" /* Global VLAN variables */ |
f99189b18
|
44 |
int vlan_net_id __read_mostly; |
d9ed0f0e2
|
45 |
|
b30200616
|
46 47 |
const char vlan_fullname[] = "802.1Q VLAN Support"; const char vlan_version[] = DRV_VERSION; |
1da177e4c
|
48 |
|
1da177e4c
|
49 |
/* End of global variables definitions. */ |
1fd9b1fc3
|
50 51 |
static int vlan_group_prealloc_vid(struct vlan_group *vg, __be16 vlan_proto, u16 vlan_id) |
67727184f
|
52 53 |
{ struct net_device **array; |
1fd9b1fc3
|
54 |
unsigned int pidx, vidx; |
67727184f
|
55 56 57 |
unsigned int size; ASSERT_RTNL(); |
1fd9b1fc3
|
58 59 60 |
pidx = vlan_proto_idx(vlan_proto); vidx = vlan_id / VLAN_GROUP_ARRAY_PART_LEN; array = vg->vlan_devices_arrays[pidx][vidx]; |
67727184f
|
61 62 63 64 65 66 67 |
if (array != NULL) return 0; size = sizeof(struct net_device *) * VLAN_GROUP_ARRAY_PART_LEN; array = kzalloc(size, GFP_KERNEL); if (array == NULL) return -ENOBUFS; |
1fd9b1fc3
|
68 |
vg->vlan_devices_arrays[pidx][vidx] = array; |
67727184f
|
69 |
return 0; |
42429aaee
|
70 |
} |
23289a37e
|
71 |
void unregister_vlan_dev(struct net_device *dev, struct list_head *head) |
1da177e4c
|
72 |
{ |
7da82c06d
|
73 |
struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
af3015170
|
74 |
struct net_device *real_dev = vlan->real_dev; |
5b9ea6e02
|
75 |
struct vlan_info *vlan_info; |
1da177e4c
|
76 |
struct vlan_group *grp; |
9bb8582ef
|
77 |
u16 vlan_id = vlan->vlan_id; |
1da177e4c
|
78 79 |
ASSERT_RTNL(); |
acc5efbcd
|
80 |
|
5b9ea6e02
|
81 82 83 84 |
vlan_info = rtnl_dereference(real_dev->vlan_info); BUG_ON(!vlan_info); grp = &vlan_info->grp; |
acc5efbcd
|
85 |
|
5b9ea6e02
|
86 |
grp->nr_vlan_devs--; |
acc5efbcd
|
87 |
|
86fbe9bb5
|
88 89 |
if (vlan->flags & VLAN_FLAG_MVRP) vlan_mvrp_request_leave(dev); |
55aee10de
|
90 91 |
if (vlan->flags & VLAN_FLAG_GVRP) vlan_gvrp_request_leave(dev); |
1fd9b1fc3
|
92 |
vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL); |
47701a36a
|
93 94 |
netdev_upper_dev_unlink(real_dev, dev); |
48752e1b1
|
95 96 97 98 |
/* Because unregister_netdevice_queue() makes sure at least one rcu * grace period is respected before device freeing, * we dont need to call synchronize_net() here. */ |
23289a37e
|
99 |
unregister_netdevice_queue(dev, head); |
ce305002e
|
100 |
|
86fbe9bb5
|
101 102 |
if (grp->nr_vlan_devs == 0) { vlan_mvrp_uninit_applicant(real_dev); |
70c03b49b
|
103 |
vlan_gvrp_uninit_applicant(real_dev); |
86fbe9bb5
|
104 |
} |
70c03b49b
|
105 |
|
4a7df340e
|
106 107 108 109 110 |
/* Take it out of our own structures, but be sure to interlock with * HW accelerating devices or SW vlan input packet processing if * VLAN is not 0 (leave it there for 802.1p). */ if (vlan_id) |
1fd9b1fc3
|
111 |
vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); |
4a7df340e
|
112 |
|
af3015170
|
113 114 |
/* Get rid of the vlan's reference to real_dev */ dev_put(real_dev); |
1da177e4c
|
115 |
} |
1fd9b1fc3
|
116 117 |
int vlan_check_real_dev(struct net_device *real_dev, __be16 protocol, u16 vlan_id) |
1da177e4c
|
118 |
{ |
656299f70
|
119 |
const char *name = real_dev->name; |
40f98e1af
|
120 |
|
1da177e4c
|
121 |
if (real_dev->features & NETIF_F_VLAN_CHALLENGED) { |
afab2d299
|
122 123 |
pr_info("VLANs not supported on %s ", name); |
c1d3ee992
|
124 |
return -EOPNOTSUPP; |
1da177e4c
|
125 |
} |
1fd9b1fc3
|
126 |
if (vlan_find_dev(real_dev, protocol, vlan_id) != NULL) |
c1d3ee992
|
127 |
return -EEXIST; |
1da177e4c
|
128 |
|
c1d3ee992
|
129 130 |
return 0; } |
07b5b17e1
|
131 |
int register_vlan_dev(struct net_device *dev) |
e89fe42cd
|
132 |
{ |
7da82c06d
|
133 |
struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
e89fe42cd
|
134 |
struct net_device *real_dev = vlan->real_dev; |
9bb8582ef
|
135 |
u16 vlan_id = vlan->vlan_id; |
5b9ea6e02
|
136 137 |
struct vlan_info *vlan_info; struct vlan_group *grp; |
e89fe42cd
|
138 |
int err; |
1fd9b1fc3
|
139 |
err = vlan_vid_add(real_dev, vlan->vlan_proto, vlan_id); |
5b9ea6e02
|
140 141 142 143 144 145 146 147 148 |
if (err) return err; vlan_info = rtnl_dereference(real_dev->vlan_info); /* vlan_info should be there now. vlan_vid_add took care of it */ BUG_ON(!vlan_info); grp = &vlan_info->grp; if (grp->nr_vlan_devs == 0) { |
70c03b49b
|
149 150 |
err = vlan_gvrp_init_applicant(real_dev); if (err < 0) |
5b9ea6e02
|
151 |
goto out_vid_del; |
86fbe9bb5
|
152 153 154 |
err = vlan_mvrp_init_applicant(real_dev); if (err < 0) goto out_uninit_gvrp; |
e89fe42cd
|
155 |
} |
1fd9b1fc3
|
156 |
err = vlan_group_prealloc_vid(grp, vlan->vlan_proto, vlan_id); |
67727184f
|
157 |
if (err < 0) |
86fbe9bb5
|
158 |
goto out_uninit_mvrp; |
67727184f
|
159 |
|
d38569ab2
|
160 |
vlan->nest_level = dev_get_nest_level(real_dev, is_vlan_dev) + 1; |
e89fe42cd
|
161 162 |
err = register_netdevice(dev); if (err < 0) |
5df27e6cb
|
163 164 165 166 167 |
goto out_uninit_mvrp; err = netdev_upper_dev_link(real_dev, dev); if (err) goto out_unregister_netdev; |
e89fe42cd
|
168 |
|
7da82c06d
|
169 |
/* Account for reference in struct vlan_dev_priv */ |
e89fe42cd
|
170 |
dev_hold(real_dev); |
fc4a74896
|
171 |
netif_stacked_transfer_operstate(real_dev, dev); |
e89fe42cd
|
172 173 174 175 176 |
linkwatch_fire_event(dev); /* _MUST_ call rfc2863_policy() */ /* So, got the sucker initialized, now lets place * it into our local structure. */ |
1fd9b1fc3
|
177 |
vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, dev); |
5b9ea6e02
|
178 |
grp->nr_vlan_devs++; |
e89fe42cd
|
179 |
|
e89fe42cd
|
180 |
return 0; |
5df27e6cb
|
181 182 |
out_unregister_netdev: unregister_netdevice(dev); |
86fbe9bb5
|
183 184 185 186 |
out_uninit_mvrp: if (grp->nr_vlan_devs == 0) vlan_mvrp_uninit_applicant(real_dev); out_uninit_gvrp: |
5b9ea6e02
|
187 |
if (grp->nr_vlan_devs == 0) |
70c03b49b
|
188 |
vlan_gvrp_uninit_applicant(real_dev); |
5b9ea6e02
|
189 |
out_vid_del: |
1fd9b1fc3
|
190 |
vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id); |
e89fe42cd
|
191 192 |
return err; } |
c1d3ee992
|
193 |
/* Attach a VLAN device to a mac address (ie Ethernet Card). |
2ae0bf69b
|
194 |
* Returns 0 if the device was created or a negative error code otherwise. |
c1d3ee992
|
195 |
*/ |
9bb8582ef
|
196 |
static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) |
c1d3ee992
|
197 |
{ |
c1d3ee992
|
198 |
struct net_device *new_dev; |
0c0667a85
|
199 |
struct vlan_dev_priv *vlan; |
7a17a2f79
|
200 201 |
struct net *net = dev_net(real_dev); struct vlan_net *vn = net_generic(net, vlan_net_id); |
c1d3ee992
|
202 |
char name[IFNAMSIZ]; |
2ae0bf69b
|
203 |
int err; |
c1d3ee992
|
204 |
|
9bb8582ef
|
205 |
if (vlan_id >= VLAN_VID_MASK) |
2ae0bf69b
|
206 |
return -ERANGE; |
c1d3ee992
|
207 |
|
1fd9b1fc3
|
208 |
err = vlan_check_real_dev(real_dev, htons(ETH_P_8021Q), vlan_id); |
2ae0bf69b
|
209 210 |
if (err < 0) return err; |
c1d3ee992
|
211 |
|
1da177e4c
|
212 |
/* Gotta set up the fields for the device. */ |
7a17a2f79
|
213 |
switch (vn->name_type) { |
1da177e4c
|
214 215 |
case VLAN_NAME_TYPE_RAW_PLUS_VID: /* name will look like: eth1.0005 */ |
9bb8582ef
|
216 |
snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, vlan_id); |
1da177e4c
|
217 218 219 220 221 |
break; case VLAN_NAME_TYPE_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: vlan5 */ |
9bb8582ef
|
222 |
snprintf(name, IFNAMSIZ, "vlan%i", vlan_id); |
1da177e4c
|
223 224 225 226 227 |
break; case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD: /* Put our vlan.VID in the name. * Name will look like: eth0.5 */ |
9bb8582ef
|
228 |
snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, vlan_id); |
1da177e4c
|
229 230 231 232 233 234 |
break; case VLAN_NAME_TYPE_PLUS_VID: /* Put our vlan.VID in the name. * Name will look like: vlan0005 */ default: |
9bb8582ef
|
235 |
snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id); |
3ff50b799
|
236 |
} |
122952fc2
|
237 |
|
c835a6773
|
238 239 |
new_dev = alloc_netdev(sizeof(struct vlan_dev_priv), name, NET_NAME_UNKNOWN, vlan_setup); |
5dd8d1e9e
|
240 |
|
1da177e4c
|
241 |
if (new_dev == NULL) |
2ae0bf69b
|
242 |
return -ENOBUFS; |
1da177e4c
|
243 |
|
65d292a2e
|
244 |
dev_net_set(new_dev, net); |
1da177e4c
|
245 246 247 248 |
/* need 4 bytes for extra VLAN header info, * hope the underlying device can handle it. */ new_dev->mtu = real_dev->mtu; |
6e22ce2c6
|
249 |
new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT); |
1da177e4c
|
250 |
|
0c0667a85
|
251 252 253 254 255 256 |
vlan = vlan_dev_priv(new_dev); vlan->vlan_proto = htons(ETH_P_8021Q); vlan->vlan_id = vlan_id; vlan->real_dev = real_dev; vlan->dent = NULL; vlan->flags = VLAN_FLAG_REORDER_HDR; |
1da177e4c
|
257 |
|
07b5b17e1
|
258 |
new_dev->rtnl_link_ops = &vlan_link_ops; |
2ae0bf69b
|
259 260 |
err = register_vlan_dev(new_dev); if (err < 0) |
e89fe42cd
|
261 |
goto out_free_newdev; |
1da177e4c
|
262 |
|
2ae0bf69b
|
263 |
return 0; |
1da177e4c
|
264 |
|
1da177e4c
|
265 266 |
out_free_newdev: free_netdev(new_dev); |
2ae0bf69b
|
267 |
return err; |
1da177e4c
|
268 |
} |
8c979c26a
|
269 270 271 |
static void vlan_sync_address(struct net_device *dev, struct net_device *vlandev) { |
7da82c06d
|
272 |
struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); |
8c979c26a
|
273 274 |
/* May be called without an actual change */ |
53a2b3a18
|
275 |
if (ether_addr_equal(vlan->real_dev_addr, dev->dev_addr)) |
8c979c26a
|
276 277 278 279 |
return; /* vlan address was different from the old address and is equal to * the new address */ |
53a2b3a18
|
280 281 |
if (!ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) |
a748ee242
|
282 |
dev_uc_del(dev, vlandev->dev_addr); |
8c979c26a
|
283 284 285 |
/* vlan address was equal to the old address and is different from * the new address */ |
53a2b3a18
|
286 287 |
if (ether_addr_equal(vlandev->dev_addr, vlan->real_dev_addr) && !ether_addr_equal(vlandev->dev_addr, dev->dev_addr)) |
a748ee242
|
288 |
dev_uc_add(dev, vlandev->dev_addr); |
8c979c26a
|
289 |
|
07fc67bef
|
290 |
ether_addr_copy(vlan->real_dev_addr, dev->dev_addr); |
8c979c26a
|
291 |
} |
5fb135705
|
292 293 294 |
static void vlan_transfer_features(struct net_device *dev, struct net_device *vlandev) { |
fc0d48b8f
|
295 |
struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev); |
1ae4be22f
|
296 |
vlandev->gso_max_size = dev->gso_max_size; |
029f5fc31
|
297 |
|
fc0d48b8f
|
298 |
if (vlan_hw_offload_capable(dev->features, vlan->vlan_proto)) |
029f5fc31
|
299 300 301 |
vlandev->hard_header_len = dev->hard_header_len; else vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN; |
f4d5392e5
|
302 |
#if IS_ENABLED(CONFIG_FCOE) |
b85daa532
|
303 304 |
vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid; #endif |
8a0427bb6
|
305 306 |
netdev_update_features(vlandev); |
5fb135705
|
307 |
} |
9c5ff24f9
|
308 |
static int __vlan_device_event(struct net_device *dev, unsigned long event) |
802fb176d
|
309 |
{ |
9c5ff24f9
|
310 |
int err = 0; |
802fb176d
|
311 312 313 |
switch (event) { case NETDEV_CHANGENAME: vlan_proc_rem_dev(dev); |
9c5ff24f9
|
314 |
err = vlan_proc_add_dev(dev); |
802fb176d
|
315 |
break; |
30688a9a3
|
316 |
case NETDEV_REGISTER: |
9c5ff24f9
|
317 |
err = vlan_proc_add_dev(dev); |
30688a9a3
|
318 319 320 321 |
break; case NETDEV_UNREGISTER: vlan_proc_rem_dev(dev); break; |
802fb176d
|
322 |
} |
9c5ff24f9
|
323 324 |
return err; |
802fb176d
|
325 |
} |
2029cc2c8
|
326 327 |
static int vlan_device_event(struct notifier_block *unused, unsigned long event, void *ptr) |
1da177e4c
|
328 |
{ |
351638e7d
|
329 |
struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
802fb176d
|
330 |
struct vlan_group *grp; |
5b9ea6e02
|
331 |
struct vlan_info *vlan_info; |
1da177e4c
|
332 333 |
int i, flgs; struct net_device *vlandev; |
7da82c06d
|
334 |
struct vlan_dev_priv *vlan; |
1fd9b1fc3
|
335 |
bool last = false; |
29906f6a4
|
336 |
LIST_HEAD(list); |
1da177e4c
|
337 |
|
9c5ff24f9
|
338 339 340 341 342 343 |
if (is_vlan_dev(dev)) { int err = __vlan_device_event(dev, event); if (err) return notifier_from_errno(err); } |
802fb176d
|
344 |
|
ad1afb003
|
345 |
if ((event == NETDEV_UP) && |
f646968f8
|
346 |
(dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) { |
afab2d299
|
347 348 |
pr_info("adding VLAN 0 to HW filter on device %s ", |
ad1afb003
|
349 |
dev->name); |
80d5c3689
|
350 |
vlan_vid_add(dev, htons(ETH_P_8021Q), 0); |
ad1afb003
|
351 |
} |
5b9ea6e02
|
352 353 |
vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) |
1da177e4c
|
354 |
goto out; |
5b9ea6e02
|
355 |
grp = &vlan_info->grp; |
1da177e4c
|
356 357 358 359 360 361 362 363 |
/* It is OK that we do not hold the group lock right now, * as we run under the RTNL lock. */ switch (event) { case NETDEV_CHANGE: /* Propagate real device state to vlan devices */ |
1fd9b1fc3
|
364 |
vlan_group_for_each_dev(grp, i, vlandev) |
fc4a74896
|
365 |
netif_stacked_transfer_operstate(dev, vlandev); |
1da177e4c
|
366 |
break; |
8c979c26a
|
367 368 |
case NETDEV_CHANGEADDR: /* Adjust unicast filters on underlying device */ |
1fd9b1fc3
|
369 |
vlan_group_for_each_dev(grp, i, vlandev) { |
d932e04a5
|
370 371 372 |
flgs = vlandev->flags; if (!(flgs & IFF_UP)) continue; |
8c979c26a
|
373 374 |
vlan_sync_address(dev, vlandev); } |
2e477c9bd
|
375 376 377 |
break; case NETDEV_CHANGEMTU: |
1fd9b1fc3
|
378 |
vlan_group_for_each_dev(grp, i, vlandev) { |
2e477c9bd
|
379 380 381 382 383 |
if (vlandev->mtu <= dev->mtu) continue; dev_set_mtu(vlandev, dev->mtu); } |
8c979c26a
|
384 |
break; |
5fb135705
|
385 386 |
case NETDEV_FEAT_CHANGE: /* Propagate device features to underlying device */ |
1fd9b1fc3
|
387 |
vlan_group_for_each_dev(grp, i, vlandev) |
5fb135705
|
388 |
vlan_transfer_features(dev, vlandev); |
5fb135705
|
389 |
break; |
99c4a26a1
|
390 391 392 |
case NETDEV_DOWN: { struct net_device *tmp; LIST_HEAD(close_list); |
f646968f8
|
393 |
if (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) |
80d5c3689
|
394 |
vlan_vid_del(dev, htons(ETH_P_8021Q), 0); |
efc73f4bb
|
395 |
|
1da177e4c
|
396 |
/* Put all VLANs for this dev in the down state too. */ |
1fd9b1fc3
|
397 |
vlan_group_for_each_dev(grp, i, vlandev) { |
1da177e4c
|
398 399 400 |
flgs = vlandev->flags; if (!(flgs & IFF_UP)) continue; |
7da82c06d
|
401 |
vlan = vlan_dev_priv(vlandev); |
5e7565930
|
402 |
if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) |
99c4a26a1
|
403 404 405 406 407 408 |
list_add(&vlandev->close_list, &close_list); } dev_close_many(&close_list, false); list_for_each_entry_safe(vlandev, tmp, &close_list, close_list) { |
fc4a74896
|
409 |
netif_stacked_transfer_operstate(dev, vlandev); |
99c4a26a1
|
410 |
list_del_init(&vlandev->close_list); |
1da177e4c
|
411 |
} |
99c4a26a1
|
412 |
list_del(&close_list); |
1da177e4c
|
413 |
break; |
99c4a26a1
|
414 |
} |
1da177e4c
|
415 416 |
case NETDEV_UP: /* Put all VLANs for this dev in the up state too. */ |
1fd9b1fc3
|
417 |
vlan_group_for_each_dev(grp, i, vlandev) { |
be346ffaa
|
418 |
flgs = dev_get_flags(vlandev); |
1da177e4c
|
419 420 |
if (flgs & IFF_UP) continue; |
7da82c06d
|
421 |
vlan = vlan_dev_priv(vlandev); |
5e7565930
|
422 423 |
if (!(vlan->flags & VLAN_FLAG_LOOSE_BINDING)) dev_change_flags(vlandev, flgs | IFF_UP); |
fc4a74896
|
424 |
netif_stacked_transfer_operstate(dev, vlandev); |
1da177e4c
|
425 426 |
} break; |
122952fc2
|
427 |
|
1da177e4c
|
428 |
case NETDEV_UNREGISTER: |
3b27e1055
|
429 430 431 |
/* twiddle thumbs on netns device moves */ if (dev->reg_state != NETREG_UNREGISTERING) break; |
1fd9b1fc3
|
432 |
vlan_group_for_each_dev(grp, i, vlandev) { |
5b9ea6e02
|
433 |
/* removal of last vid destroys vlan_info, abort |
29906f6a4
|
434 |
* afterwards */ |
5b9ea6e02
|
435 |
if (vlan_info->nr_vids == 1) |
1fd9b1fc3
|
436 |
last = true; |
29906f6a4
|
437 438 |
unregister_vlan_dev(vlandev, &list); |
1fd9b1fc3
|
439 440 |
if (last) break; |
29906f6a4
|
441 442 |
} unregister_netdevice_many(&list); |
1da177e4c
|
443 |
break; |
1c01fe14a
|
444 445 446 |
case NETDEV_PRE_TYPE_CHANGE: /* Forbid underlaying device to change its type. */ |
18c22a03a
|
447 448 449 |
if (vlan_uses_dev(dev)) return NOTIFY_BAD; break; |
99606477a
|
450 451 |
case NETDEV_NOTIFY_PEERS: |
7c8994323
|
452 |
case NETDEV_BONDING_FAILOVER: |
4aa5dee4d
|
453 |
case NETDEV_RESEND_IGMP: |
99606477a
|
454 |
/* Propagate to vlan devices */ |
1fd9b1fc3
|
455 |
vlan_group_for_each_dev(grp, i, vlandev) |
7c8994323
|
456 |
call_netdevice_notifiers(event, vlandev); |
99606477a
|
457 |
break; |
3ff50b799
|
458 |
} |
1da177e4c
|
459 460 461 462 |
out: return NOTIFY_DONE; } |
69ab4b7d6
|
463 464 465 |
static struct notifier_block vlan_notifier_block __read_mostly = { .notifier_call = vlan_device_event, }; |
1da177e4c
|
466 467 468 469 470 |
/* * VLAN IOCTL handler. * o execute requested action or pass command to the device driver * arg is really a struct vlan_ioctl_args __user *. */ |
881d966b4
|
471 |
static int vlan_ioctl_handler(struct net *net, void __user *arg) |
1da177e4c
|
472 |
{ |
c17d8874f
|
473 |
int err; |
1da177e4c
|
474 |
struct vlan_ioctl_args args; |
c17d8874f
|
475 |
struct net_device *dev = NULL; |
1da177e4c
|
476 477 478 479 480 481 482 |
if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args))) return -EFAULT; /* Null terminate this sucker, just in case. */ args.device1[23] = 0; args.u.device2[23] = 0; |
c17d8874f
|
483 |
rtnl_lock(); |
1da177e4c
|
484 485 |
switch (args.cmd) { case SET_VLAN_INGRESS_PRIORITY_CMD: |
c17d8874f
|
486 487 488 489 490 491 492 |
case SET_VLAN_EGRESS_PRIORITY_CMD: case SET_VLAN_FLAG_CMD: case ADD_VLAN_CMD: case DEL_VLAN_CMD: case GET_VLAN_REALDEV_NAME_CMD: case GET_VLAN_VID_CMD: err = -ENODEV; |
65d292a2e
|
493 |
dev = __dev_get_by_name(net, args.device1); |
c17d8874f
|
494 495 496 497 |
if (!dev) goto out; err = -EINVAL; |
26a25239d
|
498 |
if (args.cmd != ADD_VLAN_CMD && !is_vlan_dev(dev)) |
c17d8874f
|
499 500 501 502 503 504 |
goto out; } switch (args.cmd) { case SET_VLAN_INGRESS_PRIORITY_CMD: err = -EPERM; |
276996fda
|
505 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
c17d8874f
|
506 507 508 509 |
break; vlan_dev_set_ingress_priority(dev, args.u.skb_priority, args.vlan_qos); |
fffe470a8
|
510 |
err = 0; |
1da177e4c
|
511 512 513 |
break; case SET_VLAN_EGRESS_PRIORITY_CMD: |
c17d8874f
|
514 |
err = -EPERM; |
276996fda
|
515 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
c17d8874f
|
516 517 |
break; err = vlan_dev_set_egress_priority(dev, |
1da177e4c
|
518 519 520 521 522 |
args.u.skb_priority, args.vlan_qos); break; case SET_VLAN_FLAG_CMD: |
c17d8874f
|
523 |
err = -EPERM; |
276996fda
|
524 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
c17d8874f
|
525 |
break; |
b3ce0325f
|
526 527 528 |
err = vlan_dev_change_flags(dev, args.vlan_qos ? args.u.flag : 0, args.u.flag); |
1da177e4c
|
529 530 531 |
break; case SET_VLAN_NAME_TYPE_CMD: |
c17d8874f
|
532 |
err = -EPERM; |
276996fda
|
533 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
e35de0261
|
534 |
break; |
c17d8874f
|
535 536 |
if ((args.u.name_type >= 0) && (args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) { |
7a17a2f79
|
537 538 539 540 |
struct vlan_net *vn; vn = net_generic(net, vlan_net_id); vn->name_type = args.u.name_type; |
1da177e4c
|
541 542 543 544 545 546 547 |
err = 0; } else { err = -EINVAL; } break; case ADD_VLAN_CMD: |
c17d8874f
|
548 |
err = -EPERM; |
276996fda
|
549 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
c17d8874f
|
550 |
break; |
2ae0bf69b
|
551 |
err = register_vlan_device(dev, args.u.VID); |
1da177e4c
|
552 553 554 |
break; case DEL_VLAN_CMD: |
c17d8874f
|
555 |
err = -EPERM; |
276996fda
|
556 |
if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
c17d8874f
|
557 |
break; |
23289a37e
|
558 |
unregister_vlan_dev(dev, NULL); |
af3015170
|
559 |
err = 0; |
1da177e4c
|
560 |
break; |
1da177e4c
|
561 |
case GET_VLAN_REALDEV_NAME_CMD: |
3f5f4346b
|
562 |
err = 0; |
c17d8874f
|
563 |
vlan_dev_get_realdev_name(dev, args.u.device2); |
1da177e4c
|
564 |
if (copy_to_user(arg, &args, |
2029cc2c8
|
565 |
sizeof(struct vlan_ioctl_args))) |
1da177e4c
|
566 |
err = -EFAULT; |
1da177e4c
|
567 568 569 |
break; case GET_VLAN_VID_CMD: |
3f5f4346b
|
570 |
err = 0; |
22d1ba74b
|
571 |
args.u.VID = vlan_dev_vlan_id(dev); |
1da177e4c
|
572 |
if (copy_to_user(arg, &args, |
2029cc2c8
|
573 |
sizeof(struct vlan_ioctl_args))) |
122952fc2
|
574 |
err = -EFAULT; |
1da177e4c
|
575 576 577 |
break; default: |
198a291ce
|
578 |
err = -EOPNOTSUPP; |
c17d8874f
|
579 |
break; |
3ff50b799
|
580 |
} |
7eb1b3d37
|
581 |
out: |
c17d8874f
|
582 |
rtnl_unlock(); |
1da177e4c
|
583 584 |
return err; } |
2c8c1e729
|
585 |
static int __net_init vlan_init_net(struct net *net) |
d9ed0f0e2
|
586 |
{ |
946d1a929
|
587 |
struct vlan_net *vn = net_generic(net, vlan_net_id); |
d9ed0f0e2
|
588 |
int err; |
d9ed0f0e2
|
589 |
|
7a17a2f79
|
590 |
vn->name_type = VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD; |
cd1c70143
|
591 |
err = vlan_proc_init(net); |
cd1c70143
|
592 |
|
d9ed0f0e2
|
593 594 |
return err; } |
2c8c1e729
|
595 |
static void __net_exit vlan_exit_net(struct net *net) |
d9ed0f0e2
|
596 |
{ |
cd1c70143
|
597 |
vlan_proc_cleanup(net); |
d9ed0f0e2
|
598 599 600 601 602 |
} static struct pernet_operations vlan_net_ops = { .init = vlan_init_net, .exit = vlan_exit_net, |
946d1a929
|
603 604 |
.id = &vlan_net_id, .size = sizeof(struct vlan_net), |
d9ed0f0e2
|
605 |
}; |
69ab4b7d6
|
606 607 608 |
static int __init vlan_proto_init(void) { int err; |
da7c06c4a
|
609 610 |
pr_info("%s v%s ", vlan_fullname, vlan_version); |
69ab4b7d6
|
611 |
|
91e2ff352
|
612 |
err = register_pernet_subsys(&vlan_net_ops); |
d9ed0f0e2
|
613 614 |
if (err < 0) goto err0; |
69ab4b7d6
|
615 616 617 |
err = register_netdevice_notifier(&vlan_notifier_block); if (err < 0) goto err2; |
70c03b49b
|
618 |
err = vlan_gvrp_init(); |
69ab4b7d6
|
619 620 |
if (err < 0) goto err3; |
86fbe9bb5
|
621 |
err = vlan_mvrp_init(); |
70c03b49b
|
622 623 |
if (err < 0) goto err4; |
86fbe9bb5
|
624 625 626 |
err = vlan_netlink_init(); if (err < 0) goto err5; |
69ab4b7d6
|
627 628 |
vlan_ioctl_set(vlan_ioctl_handler); return 0; |
86fbe9bb5
|
629 630 |
err5: vlan_mvrp_uninit(); |
70c03b49b
|
631 632 |
err4: vlan_gvrp_uninit(); |
69ab4b7d6
|
633 634 635 |
err3: unregister_netdevice_notifier(&vlan_notifier_block); err2: |
91e2ff352
|
636 |
unregister_pernet_subsys(&vlan_net_ops); |
d9ed0f0e2
|
637 |
err0: |
69ab4b7d6
|
638 639 640 641 642 |
return err; } static void __exit vlan_cleanup_module(void) { |
69ab4b7d6
|
643 644 645 646 |
vlan_ioctl_set(NULL); vlan_netlink_fini(); unregister_netdevice_notifier(&vlan_notifier_block); |
91e2ff352
|
647 |
unregister_pernet_subsys(&vlan_net_ops); |
6e327c11a
|
648 |
rcu_barrier(); /* Wait for completion of call_rcu()'s */ |
70c03b49b
|
649 |
|
86fbe9bb5
|
650 |
vlan_mvrp_uninit(); |
70c03b49b
|
651 |
vlan_gvrp_uninit(); |
69ab4b7d6
|
652 653 654 655 |
} module_init(vlan_proto_init); module_exit(vlan_cleanup_module); |
1da177e4c
|
656 657 |
MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); |