aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2013-04-18 22:04:31 -0400
committerDavid S. Miller <davem@davemloft.net>2013-04-19 14:46:06 -0400
commit8ad227ff89a7e6f05d07cd0acfd95ed3a24450ca (patch)
tree90cb532df2523a011e47844434cc423664441d71
parent86a9bad3ab6b6f858fd4443b48738cabbb6d094c (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.h6
-rw-r--r--include/linux/netdev_features.h6
-rw-r--r--include/uapi/linux/if_link.h1
-rw-r--r--net/8021q/Kconfig2
-rw-r--r--net/8021q/vlan.h3
-rw-r--r--net/8021q/vlan_core.c18
-rw-r--r--net/8021q/vlan_netlink.c25
-rw-r--r--net/core/dev.c16
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
5config VLAN_8021Q 5config 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
92enum vlan_protos { 92enum 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
197static 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
197static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info, 209static 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
28static const struct nla_policy vlan_map_policy[IFLA_VLAN_QOS_MAX + 1] = { 29static 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;