diff options
author | Patrick McHardy <kaber@trash.net> | 2013-04-18 22:04:31 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-04-19 14:46:06 -0400 |
commit | 8ad227ff89a7e6f05d07cd0acfd95ed3a24450ca (patch) | |
tree | 90cb532df2523a011e47844434cc423664441d71 | |
parent | 86a9bad3ab6b6f858fd4443b48738cabbb6d094c (diff) |
net: vlan: add 802.1ad support
Add support for 802.1ad VLAN devices. This mainly consists of checking for
ETH_P_8021AD in addition to ETH_P_8021Q in a couple of places and check
offloading capabilities based on the used protocol.
Configuration is done using "ip link":
# ip link add link eth0 eth0.1000 \
type vlan proto 802.1ad id 1000
# ip link add link eth0.1000 eth0.1000.1000 \
type vlan proto 802.1q id 1000
52:54:00:12:34:56 > 92:b1:54:28:e4:8c, ethertype 802.1Q (0x8100), length 106: vlan 1000, p 0, ethertype 802.1Q, vlan 1000, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
20.1.0.2 > 20.1.0.1: ICMP echo request, id 3003, seq 8, length 64
92:b1:54:28:e4:8c > 52:54:00:12:34:56, ethertype 802.1Q-QinQ (0x88a8), length 106: vlan 1000, p 0, ethertype 802.1Q, vlan 1000, p 0, ethertype IPv4, (tos 0x0, ttl 64, id 47944, offset 0, flags [none], proto ICMP (1), length 84)
20.1.0.1 > 20.1.0.2: ICMP echo reply, id 3003, seq 8, length 64
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/if_vlan.h | 6 | ||||
-rw-r--r-- | include/linux/netdev_features.h | 6 | ||||
-rw-r--r-- | include/uapi/linux/if_link.h | 1 | ||||
-rw-r--r-- | net/8021q/Kconfig | 2 | ||||
-rw-r--r-- | net/8021q/vlan.h | 3 | ||||
-rw-r--r-- | net/8021q/vlan_core.c | 18 | ||||
-rw-r--r-- | net/8021q/vlan_netlink.c | 25 | ||||
-rw-r--r-- | net/core/dev.c | 16 |
8 files changed, 61 insertions, 16 deletions
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 8086ff9988b1..a78f9390da87 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h | |||
@@ -162,6 +162,8 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features, | |||
162 | { | 162 | { |
163 | if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX) | 163 | if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX) |
164 | return true; | 164 | return true; |
165 | if (proto == htons(ETH_P_8021AD) && features & NETIF_F_HW_VLAN_STAG_TX) | ||
166 | return true; | ||
165 | return false; | 167 | return false; |
166 | } | 168 | } |
167 | 169 | ||
@@ -271,9 +273,9 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci) | |||
271 | { | 273 | { |
272 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data; | 274 | struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data; |
273 | 275 | ||
274 | if (veth->h_vlan_proto != htons(ETH_P_8021Q)) { | 276 | if (veth->h_vlan_proto != htons(ETH_P_8021Q) && |
277 | veth->h_vlan_proto != htons(ETH_P_8021AD)) | ||
275 | return -EINVAL; | 278 | return -EINVAL; |
276 | } | ||
277 | 279 | ||
278 | *vlan_tci = ntohs(veth->h_vlan_TCI); | 280 | *vlan_tci = ntohs(veth->h_vlan_TCI); |
279 | return 0; | 281 | return 0; |
diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index 785913b8983d..cbaa027ef5a7 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h | |||
@@ -25,6 +25,9 @@ enum { | |||
25 | NETIF_F_HW_VLAN_CTAG_TX_BIT, /* Transmit VLAN CTAG HW acceleration */ | 25 | NETIF_F_HW_VLAN_CTAG_TX_BIT, /* Transmit VLAN CTAG HW acceleration */ |
26 | NETIF_F_HW_VLAN_CTAG_RX_BIT, /* Receive VLAN CTAG HW acceleration */ | 26 | NETIF_F_HW_VLAN_CTAG_RX_BIT, /* Receive VLAN CTAG HW acceleration */ |
27 | NETIF_F_HW_VLAN_CTAG_FILTER_BIT,/* Receive filtering on VLAN CTAGs */ | 27 | NETIF_F_HW_VLAN_CTAG_FILTER_BIT,/* Receive filtering on VLAN CTAGs */ |
28 | NETIF_F_HW_VLAN_STAG_TX_BIT, /* Transmit VLAN STAG HW acceleration */ | ||
29 | NETIF_F_HW_VLAN_STAG_RX_BIT, /* Receive VLAN STAG HW acceleration */ | ||
30 | NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */ | ||
28 | NETIF_F_VLAN_CHALLENGED_BIT, /* Device cannot handle VLAN packets */ | 31 | NETIF_F_VLAN_CHALLENGED_BIT, /* Device cannot handle VLAN packets */ |
29 | NETIF_F_GSO_BIT, /* Enable software GSO. */ | 32 | NETIF_F_GSO_BIT, /* Enable software GSO. */ |
30 | NETIF_F_LLTX_BIT, /* LockLess TX - deprecated. Please */ | 33 | NETIF_F_LLTX_BIT, /* LockLess TX - deprecated. Please */ |
@@ -83,6 +86,9 @@ enum { | |||
83 | #define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER) | 86 | #define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER) |
84 | #define NETIF_F_HW_VLAN_CTAG_RX __NETIF_F(HW_VLAN_CTAG_RX) | 87 | #define NETIF_F_HW_VLAN_CTAG_RX __NETIF_F(HW_VLAN_CTAG_RX) |
85 | #define NETIF_F_HW_VLAN_CTAG_TX __NETIF_F(HW_VLAN_CTAG_TX) | 88 | #define NETIF_F_HW_VLAN_CTAG_TX __NETIF_F(HW_VLAN_CTAG_TX) |
89 | #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) | ||
90 | #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) | ||
91 | #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) | ||
86 | #define NETIF_F_IP_CSUM __NETIF_F(IP_CSUM) | 92 | #define NETIF_F_IP_CSUM __NETIF_F(IP_CSUM) |
87 | #define NETIF_F_IPV6_CSUM __NETIF_F(IPV6_CSUM) | 93 | #define NETIF_F_IPV6_CSUM __NETIF_F(IPV6_CSUM) |
88 | #define NETIF_F_LLTX __NETIF_F(LLTX) | 94 | #define NETIF_F_LLTX __NETIF_F(LLTX) |
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 9922704f08af..e3163544f339 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h | |||
@@ -250,6 +250,7 @@ enum { | |||
250 | IFLA_VLAN_FLAGS, | 250 | IFLA_VLAN_FLAGS, |
251 | IFLA_VLAN_EGRESS_QOS, | 251 | IFLA_VLAN_EGRESS_QOS, |
252 | IFLA_VLAN_INGRESS_QOS, | 252 | IFLA_VLAN_INGRESS_QOS, |
253 | IFLA_VLAN_PROTOCOL, | ||
253 | __IFLA_VLAN_MAX, | 254 | __IFLA_VLAN_MAX, |
254 | }; | 255 | }; |
255 | 256 | ||
diff --git a/net/8021q/Kconfig b/net/8021q/Kconfig index 8f7517df41a5..b85a91fa61f1 100644 --- a/net/8021q/Kconfig +++ b/net/8021q/Kconfig | |||
@@ -3,7 +3,7 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | config VLAN_8021Q | 5 | config VLAN_8021Q |
6 | tristate "802.1Q VLAN Support" | 6 | tristate "802.1Q/802.1ad VLAN Support" |
7 | ---help--- | 7 | ---help--- |
8 | Select this and you will be able to create 802.1Q VLAN interfaces | 8 | Select this and you will be able to create 802.1Q VLAN interfaces |
9 | on your ethernet interfaces. 802.1Q VLAN supports almost | 9 | on your ethernet interfaces. 802.1Q VLAN supports almost |
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index 245de9653db0..abc9cb631c47 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h | |||
@@ -91,6 +91,7 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) | |||
91 | 91 | ||
92 | enum vlan_protos { | 92 | enum vlan_protos { |
93 | VLAN_PROTO_8021Q = 0, | 93 | VLAN_PROTO_8021Q = 0, |
94 | VLAN_PROTO_8021AD, | ||
94 | VLAN_PROTO_NUM, | 95 | VLAN_PROTO_NUM, |
95 | }; | 96 | }; |
96 | 97 | ||
@@ -116,6 +117,8 @@ static inline unsigned int vlan_proto_idx(__be16 proto) | |||
116 | switch (proto) { | 117 | switch (proto) { |
117 | case __constant_htons(ETH_P_8021Q): | 118 | case __constant_htons(ETH_P_8021Q): |
118 | return VLAN_PROTO_8021Q; | 119 | return VLAN_PROTO_8021Q; |
120 | case __constant_htons(ETH_P_8021AD): | ||
121 | return VLAN_PROTO_8021AD; | ||
119 | default: | 122 | default: |
120 | BUG(); | 123 | BUG(); |
121 | } | 124 | } |
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index bdb0b9d2e9cf..ebfa2fceb88b 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c | |||
@@ -194,6 +194,18 @@ struct vlan_vid_info { | |||
194 | int refcount; | 194 | int refcount; |
195 | }; | 195 | }; |
196 | 196 | ||
197 | static bool vlan_hw_filter_capable(const struct net_device *dev, | ||
198 | const struct vlan_vid_info *vid_info) | ||
199 | { | ||
200 | if (vid_info->proto == htons(ETH_P_8021Q) && | ||
201 | dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) | ||
202 | return true; | ||
203 | if (vid_info->proto == htons(ETH_P_8021AD) && | ||
204 | dev->features & NETIF_F_HW_VLAN_STAG_FILTER) | ||
205 | return true; | ||
206 | return false; | ||
207 | } | ||
208 | |||
197 | static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, | 209 | static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, |
198 | __be16 proto, u16 vid) | 210 | __be16 proto, u16 vid) |
199 | { | 211 | { |
@@ -231,8 +243,7 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid, | |||
231 | if (!vid_info) | 243 | if (!vid_info) |
232 | return -ENOMEM; | 244 | return -ENOMEM; |
233 | 245 | ||
234 | if (proto == htons(ETH_P_8021Q) && | 246 | if (vlan_hw_filter_capable(dev, vid_info)) { |
235 | dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { | ||
236 | err = ops->ndo_vlan_rx_add_vid(dev, proto, vid); | 247 | err = ops->ndo_vlan_rx_add_vid(dev, proto, vid); |
237 | if (err) { | 248 | if (err) { |
238 | kfree(vid_info); | 249 | kfree(vid_info); |
@@ -290,8 +301,7 @@ static void __vlan_vid_del(struct vlan_info *vlan_info, | |||
290 | u16 vid = vid_info->vid; | 301 | u16 vid = vid_info->vid; |
291 | int err; | 302 | int err; |
292 | 303 | ||
293 | if (proto == htons(ETH_P_8021Q) && | 304 | if (vlan_hw_filter_capable(dev, vid_info)) { |
294 | dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) { | ||
295 | err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid); | 305 | err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid); |
296 | if (err) { | 306 | if (err) { |
297 | pr_warn("failed to kill vid %04x/%d for device %s\n", | 307 | pr_warn("failed to kill vid %04x/%d for device %s\n", |
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index a1a956ab39a5..309129732285 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c | |||
@@ -23,6 +23,7 @@ static const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = { | |||
23 | [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, | 23 | [IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) }, |
24 | [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, | 24 | [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, |
25 | [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, | 25 | [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, |
26 | [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, | ||
26 | }; | 27 | }; |
27 | 28 | ||
28 | static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = { | 29 | static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = { |
@@ -53,6 +54,16 @@ static int vlan_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
53 | if (!data) | 54 | if (!data) |
54 | return -EINVAL; | 55 | return -EINVAL; |
55 | 56 | ||
57 | if (data[IFLA_VLAN_PROTOCOL]) { | ||
58 | switch (nla_get_be16(data[IFLA_VLAN_PROTOCOL])) { | ||
59 | case __constant_htons(ETH_P_8021Q): | ||
60 | case __constant_htons(ETH_P_8021AD): | ||
61 | break; | ||
62 | default: | ||
63 | return -EPROTONOSUPPORT; | ||
64 | } | ||
65 | } | ||
66 | |||
56 | if (data[IFLA_VLAN_ID]) { | 67 | if (data[IFLA_VLAN_ID]) { |
57 | id = nla_get_u16(data[IFLA_VLAN_ID]); | 68 | id = nla_get_u16(data[IFLA_VLAN_ID]); |
58 | if (id >= VLAN_VID_MASK) | 69 | if (id >= VLAN_VID_MASK) |
@@ -107,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev, | |||
107 | { | 118 | { |
108 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 119 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
109 | struct net_device *real_dev; | 120 | struct net_device *real_dev; |
121 | __be16 proto; | ||
110 | int err; | 122 | int err; |
111 | 123 | ||
112 | if (!data[IFLA_VLAN_ID]) | 124 | if (!data[IFLA_VLAN_ID]) |
@@ -118,7 +130,12 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev, | |||
118 | if (!real_dev) | 130 | if (!real_dev) |
119 | return -ENODEV; | 131 | return -ENODEV; |
120 | 132 | ||
121 | vlan->vlan_proto = htons(ETH_P_8021Q); | 133 | if (data[IFLA_VLAN_PROTOCOL]) |
134 | proto = nla_get_be16(data[IFLA_VLAN_PROTOCOL]); | ||
135 | else | ||
136 | proto = htons(ETH_P_8021Q); | ||
137 | |||
138 | vlan->vlan_proto = proto; | ||
122 | vlan->vlan_id = nla_get_u16(data[IFLA_VLAN_ID]); | 139 | vlan->vlan_id = nla_get_u16(data[IFLA_VLAN_ID]); |
123 | vlan->real_dev = real_dev; | 140 | vlan->real_dev = real_dev; |
124 | vlan->flags = VLAN_FLAG_REORDER_HDR; | 141 | vlan->flags = VLAN_FLAG_REORDER_HDR; |
@@ -152,7 +169,8 @@ static size_t vlan_get_size(const struct net_device *dev) | |||
152 | { | 169 | { |
153 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); | 170 | struct vlan_dev_priv *vlan = vlan_dev_priv(dev); |
154 | 171 | ||
155 | return nla_total_size(2) + /* IFLA_VLAN_ID */ | 172 | return nla_total_size(2) + /* IFLA_VLAN_PROTOCOL */ |
173 | nla_total_size(2) + /* IFLA_VLAN_ID */ | ||
156 | sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */ | 174 | sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */ |
157 | vlan_qos_map_size(vlan->nr_ingress_mappings) + | 175 | vlan_qos_map_size(vlan->nr_ingress_mappings) + |
158 | vlan_qos_map_size(vlan->nr_egress_mappings); | 176 | vlan_qos_map_size(vlan->nr_egress_mappings); |
@@ -167,7 +185,8 @@ static int vlan_fill_info(struct sk_buff *skb, const struct net_device *dev) | |||
167 | struct nlattr *nest; | 185 | struct nlattr *nest; |
168 | unsigned int i; | 186 | unsigned int i; |
169 | 187 | ||
170 | if (nla_put_u16(skb, IFLA_VLAN_ID, vlan_dev_priv(dev)->vlan_id)) | 188 | if (nla_put_be16(skb, IFLA_VLAN_PROTOCOL, vlan->vlan_proto) || |
189 | nla_put_u16(skb, IFLA_VLAN_ID, vlan->vlan_id)) | ||
171 | goto nla_put_failure; | 190 | goto nla_put_failure; |
172 | if (vlan->flags) { | 191 | if (vlan->flags) { |
173 | f.flags = vlan->flags; | 192 | f.flags = vlan->flags; |
diff --git a/net/core/dev.c b/net/core/dev.c index 3a12ee132b59..fad4c385f7a1 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -2212,7 +2212,7 @@ __be16 skb_network_protocol(struct sk_buff *skb) | |||
2212 | __be16 type = skb->protocol; | 2212 | __be16 type = skb->protocol; |
2213 | int vlan_depth = ETH_HLEN; | 2213 | int vlan_depth = ETH_HLEN; |
2214 | 2214 | ||
2215 | while (type == htons(ETH_P_8021Q)) { | 2215 | while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) { |
2216 | struct vlan_hdr *vh; | 2216 | struct vlan_hdr *vh; |
2217 | 2217 | ||
2218 | if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) | 2218 | if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN))) |
@@ -2428,20 +2428,22 @@ netdev_features_t netif_skb_features(struct sk_buff *skb) | |||
2428 | if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs) | 2428 | if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs) |
2429 | features &= ~NETIF_F_GSO_MASK; | 2429 | features &= ~NETIF_F_GSO_MASK; |
2430 | 2430 | ||
2431 | if (protocol == htons(ETH_P_8021Q)) { | 2431 | if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) { |
2432 | struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; | 2432 | struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data; |
2433 | protocol = veh->h_vlan_encapsulated_proto; | 2433 | protocol = veh->h_vlan_encapsulated_proto; |
2434 | } else if (!vlan_tx_tag_present(skb)) { | 2434 | } else if (!vlan_tx_tag_present(skb)) { |
2435 | return harmonize_features(skb, protocol, features); | 2435 | return harmonize_features(skb, protocol, features); |
2436 | } | 2436 | } |
2437 | 2437 | ||
2438 | features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX); | 2438 | features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX | |
2439 | NETIF_F_HW_VLAN_STAG_TX); | ||
2439 | 2440 | ||
2440 | if (protocol != htons(ETH_P_8021Q)) { | 2441 | if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) { |
2441 | return harmonize_features(skb, protocol, features); | 2442 | return harmonize_features(skb, protocol, features); |
2442 | } else { | 2443 | } else { |
2443 | features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | | 2444 | features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | |
2444 | NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX; | 2445 | NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX | |
2446 | NETIF_F_HW_VLAN_STAG_TX; | ||
2445 | return harmonize_features(skb, protocol, features); | 2447 | return harmonize_features(skb, protocol, features); |
2446 | } | 2448 | } |
2447 | } | 2449 | } |
@@ -3360,6 +3362,7 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb) | |||
3360 | case __constant_htons(ETH_P_IP): | 3362 | case __constant_htons(ETH_P_IP): |
3361 | case __constant_htons(ETH_P_IPV6): | 3363 | case __constant_htons(ETH_P_IPV6): |
3362 | case __constant_htons(ETH_P_8021Q): | 3364 | case __constant_htons(ETH_P_8021Q): |
3365 | case __constant_htons(ETH_P_8021AD): | ||
3363 | return true; | 3366 | return true; |
3364 | default: | 3367 | default: |
3365 | return false; | 3368 | return false; |
@@ -3400,7 +3403,8 @@ another_round: | |||
3400 | 3403 | ||
3401 | __this_cpu_inc(softnet_data.processed); | 3404 | __this_cpu_inc(softnet_data.processed); |
3402 | 3405 | ||
3403 | if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) { | 3406 | if (skb->protocol == cpu_to_be16(ETH_P_8021Q) || |
3407 | skb->protocol == cpu_to_be16(ETH_P_8021AD)) { | ||
3404 | skb = vlan_untag(skb); | 3408 | skb = vlan_untag(skb); |
3405 | if (unlikely(!skb)) | 3409 | if (unlikely(!skb)) |
3406 | goto unlock; | 3410 | goto unlock; |