Skip to content

Commit 8ad227f

Browse files
kaberdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 86a9bad commit 8ad227f

File tree

8 files changed

+61
-16
lines changed

8 files changed

+61
-16
lines changed

include/linux/if_vlan.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ static inline bool vlan_hw_offload_capable(netdev_features_t features,
162162
{
163163
if (proto == htons(ETH_P_8021Q) && features & NETIF_F_HW_VLAN_CTAG_TX)
164164
return true;
165+
if (proto == htons(ETH_P_8021AD) && features & NETIF_F_HW_VLAN_STAG_TX)
166+
return true;
165167
return false;
166168
}
167169

@@ -271,9 +273,9 @@ static inline int __vlan_get_tag(const struct sk_buff *skb, u16 *vlan_tci)
271273
{
272274
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data;
273275

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))
275278
return -EINVAL;
276-
}
277279

278280
*vlan_tci = ntohs(veth->h_vlan_TCI);
279281
return 0;

include/linux/netdev_features.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ enum {
2525
NETIF_F_HW_VLAN_CTAG_TX_BIT, /* Transmit VLAN CTAG HW acceleration */
2626
NETIF_F_HW_VLAN_CTAG_RX_BIT, /* Receive VLAN CTAG HW acceleration */
2727
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 */
2831
NETIF_F_VLAN_CHALLENGED_BIT, /* Device cannot handle VLAN packets */
2932
NETIF_F_GSO_BIT, /* Enable software GSO. */
3033
NETIF_F_LLTX_BIT, /* LockLess TX - deprecated. Please */
@@ -83,6 +86,9 @@ enum {
8386
#define NETIF_F_HW_VLAN_CTAG_FILTER __NETIF_F(HW_VLAN_CTAG_FILTER)
8487
#define NETIF_F_HW_VLAN_CTAG_RX __NETIF_F(HW_VLAN_CTAG_RX)
8588
#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)
8692
#define NETIF_F_IP_CSUM __NETIF_F(IP_CSUM)
8793
#define NETIF_F_IPV6_CSUM __NETIF_F(IPV6_CSUM)
8894
#define NETIF_F_LLTX __NETIF_F(LLTX)

include/uapi/linux/if_link.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ enum {
250250
IFLA_VLAN_FLAGS,
251251
IFLA_VLAN_EGRESS_QOS,
252252
IFLA_VLAN_INGRESS_QOS,
253+
IFLA_VLAN_PROTOCOL,
253254
__IFLA_VLAN_MAX,
254255
};
255256

net/8021q/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#
44

55
config VLAN_8021Q
6-
tristate "802.1Q VLAN Support"
6+
tristate "802.1Q/802.1ad VLAN Support"
77
---help---
88
Select this and you will be able to create 802.1Q VLAN interfaces
99
on your ethernet interfaces. 802.1Q VLAN supports almost

net/8021q/vlan.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev)
9191

9292
enum vlan_protos {
9393
VLAN_PROTO_8021Q = 0,
94+
VLAN_PROTO_8021AD,
9495
VLAN_PROTO_NUM,
9596
};
9697

@@ -116,6 +117,8 @@ static inline unsigned int vlan_proto_idx(__be16 proto)
116117
switch (proto) {
117118
case __constant_htons(ETH_P_8021Q):
118119
return VLAN_PROTO_8021Q;
120+
case __constant_htons(ETH_P_8021AD):
121+
return VLAN_PROTO_8021AD;
119122
default:
120123
BUG();
121124
}

net/8021q/vlan_core.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,18 @@ struct vlan_vid_info {
194194
int refcount;
195195
};
196196

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+
197209
static struct vlan_vid_info *vlan_vid_info_get(struct vlan_info *vlan_info,
198210
__be16 proto, u16 vid)
199211
{
@@ -231,8 +243,7 @@ static int __vlan_vid_add(struct vlan_info *vlan_info, __be16 proto, u16 vid,
231243
if (!vid_info)
232244
return -ENOMEM;
233245

234-
if (proto == htons(ETH_P_8021Q) &&
235-
dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
246+
if (vlan_hw_filter_capable(dev, vid_info)) {
236247
err = ops->ndo_vlan_rx_add_vid(dev, proto, vid);
237248
if (err) {
238249
kfree(vid_info);
@@ -290,8 +301,7 @@ static void __vlan_vid_del(struct vlan_info *vlan_info,
290301
u16 vid = vid_info->vid;
291302
int err;
292303

293-
if (proto == htons(ETH_P_8021Q) &&
294-
dev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
304+
if (vlan_hw_filter_capable(dev, vid_info)) {
295305
err = ops->ndo_vlan_rx_kill_vid(dev, proto, vid);
296306
if (err) {
297307
pr_warn("failed to kill vid %04x/%d for device %s\n",

net/8021q/vlan_netlink.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ static const struct nla_policy vlan_policy[IFLA_VLAN_MAX + 1] = {
2323
[IFLA_VLAN_FLAGS] = { .len = sizeof(struct ifla_vlan_flags) },
2424
[IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
2525
[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
26+
[IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 },
2627
};
2728

2829
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[])
5354
if (!data)
5455
return -EINVAL;
5556

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+
5667
if (data[IFLA_VLAN_ID]) {
5768
id = nla_get_u16(data[IFLA_VLAN_ID]);
5869
if (id >= VLAN_VID_MASK)
@@ -107,6 +118,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
107118
{
108119
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
109120
struct net_device *real_dev;
121+
__be16 proto;
110122
int err;
111123

112124
if (!data[IFLA_VLAN_ID])
@@ -118,7 +130,12 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev,
118130
if (!real_dev)
119131
return -ENODEV;
120132

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;
122139
vlan->vlan_id = nla_get_u16(data[IFLA_VLAN_ID]);
123140
vlan->real_dev = real_dev;
124141
vlan->flags = VLAN_FLAG_REORDER_HDR;
@@ -152,7 +169,8 @@ static size_t vlan_get_size(const struct net_device *dev)
152169
{
153170
struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
154171

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 */
156174
sizeof(struct ifla_vlan_flags) + /* IFLA_VLAN_FLAGS */
157175
vlan_qos_map_size(vlan->nr_ingress_mappings) +
158176
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)
167185
struct nlattr *nest;
168186
unsigned int i;
169187

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))
171190
goto nla_put_failure;
172191
if (vlan->flags) {
173192
f.flags = vlan->flags;

net/core/dev.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,7 +2212,7 @@ __be16 skb_network_protocol(struct sk_buff *skb)
22122212
__be16 type = skb->protocol;
22132213
int vlan_depth = ETH_HLEN;
22142214

2215-
while (type == htons(ETH_P_8021Q)) {
2215+
while (type == htons(ETH_P_8021Q) || type == htons(ETH_P_8021AD)) {
22162216
struct vlan_hdr *vh;
22172217

22182218
if (unlikely(!pskb_may_pull(skb, vlan_depth + VLAN_HLEN)))
@@ -2428,20 +2428,22 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
24282428
if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
24292429
features &= ~NETIF_F_GSO_MASK;
24302430

2431-
if (protocol == htons(ETH_P_8021Q)) {
2431+
if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) {
24322432
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
24332433
protocol = veh->h_vlan_encapsulated_proto;
24342434
} else if (!vlan_tx_tag_present(skb)) {
24352435
return harmonize_features(skb, protocol, features);
24362436
}
24372437

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);
24392440

2440-
if (protocol != htons(ETH_P_8021Q)) {
2441+
if (protocol != htons(ETH_P_8021Q) && protocol != htons(ETH_P_8021AD)) {
24412442
return harmonize_features(skb, protocol, features);
24422443
} else {
24432444
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;
24452447
return harmonize_features(skb, protocol, features);
24462448
}
24472449
}
@@ -3360,6 +3362,7 @@ static bool skb_pfmemalloc_protocol(struct sk_buff *skb)
33603362
case __constant_htons(ETH_P_IP):
33613363
case __constant_htons(ETH_P_IPV6):
33623364
case __constant_htons(ETH_P_8021Q):
3365+
case __constant_htons(ETH_P_8021AD):
33633366
return true;
33643367
default:
33653368
return false;
@@ -3400,7 +3403,8 @@ static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
34003403

34013404
__this_cpu_inc(softnet_data.processed);
34023405

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)) {
34043408
skb = vlan_untag(skb);
34053409
if (unlikely(!skb))
34063410
goto unlock;

0 commit comments

Comments
 (0)