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 /net/8021q | |
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>
Diffstat (limited to 'net/8021q')
-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 |
4 files changed, 40 insertions, 8 deletions
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; |