Blame view
net/8021q/vlan_core.c
8.69 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
7750f403c vlan: uninline __... |
2 3 4 |
#include <linux/skbuff.h> #include <linux/netdevice.h> #include <linux/if_vlan.h> |
4ead44316 netpoll: Add drop... |
5 |
#include <linux/netpoll.h> |
bc3b2d7fb net: Add export.h... |
6 |
#include <linux/export.h> |
7750f403c vlan: uninline __... |
7 |
#include "vlan.h" |
48cc32d38 vlan: don't deliv... |
8 |
bool vlan_do_receive(struct sk_buff **skbp) |
7750f403c vlan: uninline __... |
9 |
{ |
3701e5138 vlan: Centralize ... |
10 |
struct sk_buff *skb = *skbp; |
86a9bad3a net: vlan: add pr... |
11 |
__be16 vlan_proto = skb->vlan_proto; |
df8a39def net: rename vlan_... |
12 |
u16 vlan_id = skb_vlan_tag_get_id(skb); |
ad1afb003 vlan_dev: VLAN 0 ... |
13 |
struct net_device *vlan_dev; |
4af429d29 vlan: lockless tr... |
14 |
struct vlan_pcpu_stats *rx_stats; |
7750f403c vlan: uninline __... |
15 |
|
86a9bad3a net: vlan: add pr... |
16 |
vlan_dev = vlan_find_dev(skb->dev, vlan_proto, vlan_id); |
48cc32d38 vlan: don't deliv... |
17 |
if (!vlan_dev) |
3701e5138 vlan: Centralize ... |
18 |
return false; |
9b22ea560 net: fix packet s... |
19 |
|
3701e5138 vlan: Centralize ... |
20 21 22 |
skb = *skbp = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) return false; |
e769fcec6 net: 8021q: skip ... |
23 24 25 26 27 28 |
if (unlikely(!(vlan_dev->flags & IFF_UP))) { kfree_skb(skb); *skbp = NULL; return false; } |
e1c096e25 vlan: Add GRO int... |
29 |
|
3701e5138 vlan: Centralize ... |
30 |
skb->dev = vlan_dev; |
375f67df2 vlan: slight opti... |
31 |
if (unlikely(skb->pkt_type == PACKET_OTHERHOST)) { |
0b5c9db1b vlan: Fix the ing... |
32 33 34 |
/* Our lower layer thinks this is not local, let's make sure. * This allows the VLAN to have a different MAC than the * underlying device, and still route correctly. */ |
be14cc98e vlan: use use eth... |
35 |
if (ether_addr_equal_64bits(eth_hdr(skb)->h_dest, vlan_dev->dev_addr)) |
0b5c9db1b vlan: Fix the ing... |
36 37 |
skb->pkt_type = PACKET_HOST; } |
28f9ee22b vlan: Do not put ... |
38 39 40 |
if (!(vlan_dev_priv(vlan_dev)->flags & VLAN_FLAG_REORDER_HDR) && !netif_is_macvlan_port(vlan_dev) && !netif_is_bridge_port(vlan_dev)) { |
0b5c9db1b vlan: Fix the ing... |
41 42 43 44 45 46 47 48 |
unsigned int offset = skb->data - skb_mac_header(skb); /* * vlan_insert_tag expect skb->data pointing to mac header. * So change skb->data before calling it and change back to * original position later */ skb_push(skb, offset); |
99ba9a972 vlan: Fix out of ... |
49 50 |
skb = *skbp = vlan_insert_inner_tag(skb, skb->vlan_proto, skb->vlan_tci, skb->mac_len); |
0b5c9db1b vlan: Fix the ing... |
51 52 53 54 55 |
if (!skb) return false; skb_pull(skb, offset + VLAN_HLEN); skb_reset_mac_len(skb); } |
3701e5138 vlan: Centralize ... |
56 |
skb->priority = vlan_get_ingress_priority(vlan_dev, skb->vlan_tci); |
bc1d0411b vlan: deliver pac... |
57 |
skb->vlan_tci = 0; |
7750f403c vlan: uninline __... |
58 |
|
7da82c06d vlan: rename vlan... |
59 |
rx_stats = this_cpu_ptr(vlan_dev_priv(vlan_dev)->vlan_pcpu_stats); |
9793241fe vlan: Precise RX ... |
60 |
|
9618e2ffd vlan: 64 bit rx c... |
61 |
u64_stats_update_begin(&rx_stats->syncp); |
9793241fe vlan: Precise RX ... |
62 63 |
rx_stats->rx_packets++; rx_stats->rx_bytes += skb->len; |
0b5c9db1b vlan: Fix the ing... |
64 |
if (skb->pkt_type == PACKET_MULTICAST) |
9618e2ffd vlan: 64 bit rx c... |
65 |
rx_stats->rx_multicast++; |
9618e2ffd vlan: 64 bit rx c... |
66 |
u64_stats_update_end(&rx_stats->syncp); |
3701e5138 vlan: Centralize ... |
67 68 |
return true; |
7750f403c vlan: uninline __... |
69 |
} |
22d1ba74b vlan: move struct... |
70 |
|
1cdfd72f7 vlan: remove usag... |
71 |
/* Must be invoked with rcu_read_lock. */ |
f06c7f9f9 vlan: rename __vl... |
72 |
struct net_device *__vlan_find_dev_deep_rcu(struct net_device *dev, |
1fd9b1fc3 net: vlan: prepar... |
73 |
__be16 vlan_proto, u16 vlan_id) |
cec9c1336 vlan: introduce _... |
74 |
{ |
1cdfd72f7 vlan: remove usag... |
75 |
struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); |
cec9c1336 vlan: introduce _... |
76 |
|
5b9ea6e02 vlan: introduce v... |
77 |
if (vlan_info) { |
1fd9b1fc3 net: vlan: prepar... |
78 79 |
return vlan_group_get_device(&vlan_info->grp, vlan_proto, vlan_id); |
cec9c1336 vlan: introduce _... |
80 81 |
} else { /* |
1cdfd72f7 vlan: remove usag... |
82 83 84 |
* Lower devices of master uppers (bonding, team) do not have * grp assigned to themselves. Grp is assigned to upper device * instead. |
cec9c1336 vlan: introduce _... |
85 |
*/ |
1cdfd72f7 vlan: remove usag... |
86 87 88 89 |
struct net_device *upper_dev; upper_dev = netdev_master_upper_dev_get_rcu(dev); if (upper_dev) |
f06c7f9f9 vlan: rename __vl... |
90 |
return __vlan_find_dev_deep_rcu(upper_dev, |
1fd9b1fc3 net: vlan: prepar... |
91 |
vlan_proto, vlan_id); |
cec9c1336 vlan: introduce _... |
92 93 94 95 |
} return NULL; } |
f06c7f9f9 vlan: rename __vl... |
96 |
EXPORT_SYMBOL(__vlan_find_dev_deep_rcu); |
cec9c1336 vlan: introduce _... |
97 |
|
22d1ba74b vlan: move struct... |
98 99 |
struct net_device *vlan_dev_real_dev(const struct net_device *dev) { |
0369722f0 vlan: make vlan_d... |
100 101 102 103 104 105 |
struct net_device *ret = vlan_dev_priv(dev)->real_dev; while (is_vlan_dev(ret)) ret = vlan_dev_priv(ret)->real_dev; return ret; |
22d1ba74b vlan: move struct... |
106 |
} |
116cb4285 vlan: Export symb... |
107 |
EXPORT_SYMBOL(vlan_dev_real_dev); |
22d1ba74b vlan: move struct... |
108 109 110 |
u16 vlan_dev_vlan_id(const struct net_device *dev) { |
7da82c06d vlan: rename vlan... |
111 |
return vlan_dev_priv(dev)->vlan_id; |
22d1ba74b vlan: move struct... |
112 |
} |
116cb4285 vlan: Export symb... |
113 |
EXPORT_SYMBOL(vlan_dev_vlan_id); |
e1c096e25 vlan: Add GRO int... |
114 |
|
71e415e44 vlan: make a new ... |
115 116 117 118 119 |
__be16 vlan_dev_vlan_proto(const struct net_device *dev) { return vlan_dev_priv(dev)->vlan_proto; } EXPORT_SYMBOL(vlan_dev_vlan_proto); |
5b9ea6e02 vlan: introduce v... |
120 121 122 123 124 125 |
/* * vlan info and vid list */ static void vlan_group_free(struct vlan_group *grp) { |
cf2c014ad net: vlan: fix me... |
126 |
int i, j; |
5b9ea6e02 vlan: introduce v... |
127 |
|
cf2c014ad net: vlan: fix me... |
128 129 130 |
for (i = 0; i < VLAN_PROTO_NUM; i++) for (j = 0; j < VLAN_GROUP_ARRAY_SPLIT_PARTS; j++) kfree(grp->vlan_devices_arrays[i][j]); |
5b9ea6e02 vlan: introduce v... |
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
} static void vlan_info_free(struct vlan_info *vlan_info) { vlan_group_free(&vlan_info->grp); kfree(vlan_info); } static void vlan_info_rcu_free(struct rcu_head *rcu) { vlan_info_free(container_of(rcu, struct vlan_info, rcu)); } static struct vlan_info *vlan_info_alloc(struct net_device *dev) { struct vlan_info *vlan_info; vlan_info = kzalloc(sizeof(struct vlan_info), GFP_KERNEL); if (!vlan_info) return NULL; vlan_info->real_dev = dev; INIT_LIST_HEAD(&vlan_info->vid_list); return vlan_info; } struct vlan_vid_info { struct list_head list; |
80d5c3689 net: vlan: prepar... |
159 160 |
__be16 proto; u16 vid; |
5b9ea6e02 vlan: introduce v... |
161 162 |
int refcount; }; |
8ad227ff8 net: vlan: add 80... |
163 164 165 166 167 168 169 170 171 172 173 |
static bool vlan_hw_filter_capable(const struct net_device *dev, const struct vlan_vid_info *vid_info) { if (vid_info->proto == htons(ETH_P_8021Q) && dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) return true; if (vid_info->proto == htons(ETH_P_8021AD) && dev->features & NETIF_F_HW_VLAN_STAG_FILTER) return true; return false; } |
5b9ea6e02 vlan: introduce v... |
174 |
static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, |
80d5c3689 net: vlan: prepar... |
175 |
__be16 proto, u16 vid) |
5b9ea6e02 vlan: introduce v... |
176 177 178 179 |
{ struct vlan_vid_info *vid_info; list_for_each_entry(vid_info, &vlan_info->vid_list, list) { |
80d5c3689 net: vlan: prepar... |
180 |
if (vid_info->proto == proto && vid_info->vid == vid) |
5b9ea6e02 vlan: introduce v... |
181 182 183 184 |
return vid_info; } return NULL; } |
80d5c3689 net: vlan: prepar... |
185 |
static struct vlan_vid_info *vlan_vid_info_alloc(__be16 proto, u16 vid) |
5b9ea6e02 vlan: introduce v... |
186 187 188 189 190 191 |
{ struct vlan_vid_info *vid_info; vid_info = kzalloc(sizeof(struct vlan_vid_info), GFP_KERNEL); if (!vid_info) return NULL; |
80d5c3689 net: vlan: prepar... |
192 |
vid_info->proto = proto; |
5b9ea6e02 vlan: introduce v... |
193 194 195 196 |
vid_info->vid = vid; return vid_info; } |
80d5c3689 net: vlan: prepar... |
197 |
static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid, |
5b9ea6e02 vlan: introduce v... |
198 |
struct vlan_vid_info **pvid_info) |
87002b03b net: introduce vl... |
199 |
{ |
5b9ea6e02 vlan: introduce v... |
200 |
struct net_device *dev = vlan_info->real_dev; |
87002b03b net: introduce vl... |
201 |
const struct net_device_ops *ops = dev->netdev_ops; |
5b9ea6e02 vlan: introduce v... |
202 203 |
struct vlan_vid_info *vid_info; int err; |
80d5c3689 net: vlan: prepar... |
204 |
vid_info = vlan_vid_info_alloc(proto, vid); |
5b9ea6e02 vlan: introduce v... |
205 206 |
if (!vid_info) return -ENOMEM; |
87002b03b net: introduce vl... |
207 |
|
8ad227ff8 net: vlan: add 80... |
208 |
if (vlan_hw_filter_capable(dev, vid_info)) { |
74f2d19ca vlan: Invoke driv... |
209 210 211 212 |
if (netif_device_present(dev)) err = ops->ndo_vlan_rx_add_vid(dev, proto, vid); else err = -ENODEV; |
5b9ea6e02 vlan: introduce v... |
213 214 215 216 |
if (err) { kfree(vid_info); return err; } |
87002b03b net: introduce vl... |
217 |
} |
5b9ea6e02 vlan: introduce v... |
218 219 220 |
list_add(&vid_info->list, &vlan_info->vid_list); vlan_info->nr_vids++; *pvid_info = vid_info; |
87002b03b net: introduce vl... |
221 222 |
return 0; } |
5b9ea6e02 vlan: introduce v... |
223 |
|
80d5c3689 net: vlan: prepar... |
224 |
int vlan_vid_add(struct net_device *dev, __be16 proto, u16 vid) |
5b9ea6e02 vlan: introduce v... |
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
{ struct vlan_info *vlan_info; struct vlan_vid_info *vid_info; bool vlan_info_created = false; int err; ASSERT_RTNL(); vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) { vlan_info = vlan_info_alloc(dev); if (!vlan_info) return -ENOMEM; vlan_info_created = true; } |
80d5c3689 net: vlan: prepar... |
240 |
vid_info = vlan_vid_info_get(vlan_info, proto, vid); |
5b9ea6e02 vlan: introduce v... |
241 |
if (!vid_info) { |
80d5c3689 net: vlan: prepar... |
242 |
err = __vlan_vid_add(vlan_info, proto, vid, &vid_info); |
5b9ea6e02 vlan: introduce v... |
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
if (err) goto out_free_vlan_info; } vid_info->refcount++; if (vlan_info_created) rcu_assign_pointer(dev->vlan_info, vlan_info); return 0; out_free_vlan_info: if (vlan_info_created) kfree(vlan_info); return err; } |
87002b03b net: introduce vl... |
258 |
EXPORT_SYMBOL(vlan_vid_add); |
5b9ea6e02 vlan: introduce v... |
259 260 |
static void __vlan_vid_del(struct vlan_info *vlan_info, struct vlan_vid_info *vid_info) |
87002b03b net: introduce vl... |
261 |
{ |
5b9ea6e02 vlan: introduce v... |
262 |
struct net_device *dev = vlan_info->real_dev; |
87002b03b net: introduce vl... |
263 |
const struct net_device_ops *ops = dev->netdev_ops; |
80d5c3689 net: vlan: prepar... |
264 265 |
__be16 proto = vid_info->proto; u16 vid = vid_info->vid; |
5b9ea6e02 vlan: introduce v... |
266 |
int err; |
87002b03b net: introduce vl... |
267 |
|
8ad227ff8 net: vlan: add 80... |
268 |
if (vlan_hw_filter_capable(dev, vid_info)) { |
74f2d19ca vlan: Invoke driv... |
269 270 271 272 |
if (netif_device_present(dev)) err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid); else err = -ENODEV; |
5b9ea6e02 vlan: introduce v... |
273 |
if (err) { |
80d5c3689 net: vlan: prepar... |
274 275 276 |
pr_warn("failed to kill vid %04x/%d for device %s ", proto, vid, dev->name); |
5b9ea6e02 vlan: introduce v... |
277 278 279 280 281 282 |
} } list_del(&vid_info->list); kfree(vid_info); vlan_info->nr_vids--; } |
80d5c3689 net: vlan: prepar... |
283 |
void vlan_vid_del(struct net_device *dev, __be16 proto, u16 vid) |
5b9ea6e02 vlan: introduce v... |
284 285 286 287 288 289 290 291 292 |
{ struct vlan_info *vlan_info; struct vlan_vid_info *vid_info; ASSERT_RTNL(); vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) return; |
80d5c3689 net: vlan: prepar... |
293 |
vid_info = vlan_vid_info_get(vlan_info, proto, vid); |
5b9ea6e02 vlan: introduce v... |
294 295 296 297 298 299 300 301 302 |
if (!vid_info) return; vid_info->refcount--; if (vid_info->refcount == 0) { __vlan_vid_del(vlan_info, vid_info); if (vlan_info->nr_vids == 0) { RCU_INIT_POINTER(dev->vlan_info, NULL); call_rcu(&vlan_info->rcu, vlan_info_rcu_free); } |
87002b03b net: introduce vl... |
303 304 305 |
} } EXPORT_SYMBOL(vlan_vid_del); |
348a1443c vlan: introduce f... |
306 307 308 309 310 |
int vlan_vids_add_by_dev(struct net_device *dev, const struct net_device *by_dev) { struct vlan_vid_info *vid_info; |
f9586f79b vlan: add rtnl_de... |
311 |
struct vlan_info *vlan_info; |
348a1443c vlan: introduce f... |
312 313 314 |
int err; ASSERT_RTNL(); |
f9586f79b vlan: add rtnl_de... |
315 316 |
vlan_info = rtnl_dereference(by_dev->vlan_info); if (!vlan_info) |
348a1443c vlan: introduce f... |
317 |
return 0; |
f9586f79b vlan: add rtnl_de... |
318 |
list_for_each_entry(vid_info, &vlan_info->vid_list, list) { |
80d5c3689 net: vlan: prepar... |
319 |
err = vlan_vid_add(dev, vid_info->proto, vid_info->vid); |
348a1443c vlan: introduce f... |
320 321 322 323 324 325 326 |
if (err) goto unwind; } return 0; unwind: list_for_each_entry_continue_reverse(vid_info, |
f9586f79b vlan: add rtnl_de... |
327 |
&vlan_info->vid_list, |
348a1443c vlan: introduce f... |
328 |
list) { |
80d5c3689 net: vlan: prepar... |
329 |
vlan_vid_del(dev, vid_info->proto, vid_info->vid); |
348a1443c vlan: introduce f... |
330 331 332 333 334 335 336 337 338 339 |
} return err; } EXPORT_SYMBOL(vlan_vids_add_by_dev); void vlan_vids_del_by_dev(struct net_device *dev, const struct net_device *by_dev) { struct vlan_vid_info *vid_info; |
f9586f79b vlan: add rtnl_de... |
340 |
struct vlan_info *vlan_info; |
348a1443c vlan: introduce f... |
341 342 |
ASSERT_RTNL(); |
f9586f79b vlan: add rtnl_de... |
343 344 |
vlan_info = rtnl_dereference(by_dev->vlan_info); if (!vlan_info) |
348a1443c vlan: introduce f... |
345 |
return; |
f9586f79b vlan: add rtnl_de... |
346 |
list_for_each_entry(vid_info, &vlan_info->vid_list, list) |
80d5c3689 net: vlan: prepar... |
347 |
vlan_vid_del(dev, vid_info->proto, vid_info->vid); |
348a1443c vlan: introduce f... |
348 349 |
} EXPORT_SYMBOL(vlan_vids_del_by_dev); |
9b361c13c vlan: add helper ... |
350 351 352 |
bool vlan_uses_dev(const struct net_device *dev) { |
55462cf30 vlan: fix bond/te... |
353 354 355 356 357 358 359 360 |
struct vlan_info *vlan_info; ASSERT_RTNL(); vlan_info = rtnl_dereference(dev->vlan_info); if (!vlan_info) return false; return vlan_info->grp.nr_vlan_devs ? true : false; |
9b361c13c vlan: add helper ... |
361 362 |
} EXPORT_SYMBOL(vlan_uses_dev); |