aboutsummaryrefslogtreecommitdiffstats
path: root/net/openvswitch
diff options
context:
space:
mode:
authorEric Garver <e@erig.me>2016-09-07 12:56:59 -0400
committerDavid S. Miller <davem@davemloft.net>2016-09-08 20:10:28 -0400
commit018c1dda5ff1e7bd1fe2d9fd1d0f5b82dc6fc0cd (patch)
treeccedb1acda9922d975903894776dcfb853fc3c2d /net/openvswitch
parentfe19c4f971a55cea3be442d8032a5f6021702791 (diff)
openvswitch: 802.1AD Flow handling, actions, vlan parsing, netlink attributes
Add support for 802.1ad including the ability to push and pop double tagged vlans. Add support for 802.1ad to netlink parsing and flow conversion. Uses double nested encap attributes to represent double tagged vlan. Inner TPID encoded along with ctci in nested attributes. This is based on Thomas F Herbert's original v20 patch. I made some small clean ups and bug fixes. Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com> Signed-off-by: Eric Garver <e@erig.me> Acked-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/openvswitch')
-rw-r--r--net/openvswitch/actions.c16
-rw-r--r--net/openvswitch/flow.c65
-rw-r--r--net/openvswitch/flow.h8
-rw-r--r--net/openvswitch/flow_netlink.c310
-rw-r--r--net/openvswitch/vport.c7
5 files changed, 282 insertions, 124 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index ca91fc33f8a9..4fe9032b1160 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -246,20 +246,24 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
246 int err; 246 int err;
247 247
248 err = skb_vlan_pop(skb); 248 err = skb_vlan_pop(skb);
249 if (skb_vlan_tag_present(skb)) 249 if (skb_vlan_tag_present(skb)) {
250 invalidate_flow_key(key); 250 invalidate_flow_key(key);
251 else 251 } else {
252 key->eth.tci = 0; 252 key->eth.vlan.tci = 0;
253 key->eth.vlan.tpid = 0;
254 }
253 return err; 255 return err;
254} 256}
255 257
256static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, 258static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
257 const struct ovs_action_push_vlan *vlan) 259 const struct ovs_action_push_vlan *vlan)
258{ 260{
259 if (skb_vlan_tag_present(skb)) 261 if (skb_vlan_tag_present(skb)) {
260 invalidate_flow_key(key); 262 invalidate_flow_key(key);
261 else 263 } else {
262 key->eth.tci = vlan->vlan_tci; 264 key->eth.vlan.tci = vlan->vlan_tci;
265 key->eth.vlan.tpid = vlan->vlan_tpid;
266 }
263 return skb_vlan_push(skb, vlan->vlan_tpid, 267 return skb_vlan_push(skb, vlan->vlan_tpid,
264 ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); 268 ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
265} 269}
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 0ea128eeeab2..1240ae3b88d2 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -302,24 +302,57 @@ static bool icmp6hdr_ok(struct sk_buff *skb)
302 sizeof(struct icmp6hdr)); 302 sizeof(struct icmp6hdr));
303} 303}
304 304
305static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) 305/**
306 * Parse vlan tag from vlan header.
307 * Returns ERROR on memory error.
308 * Returns 0 if it encounters a non-vlan or incomplete packet.
309 * Returns 1 after successfully parsing vlan tag.
310 */
311static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh)
306{ 312{
307 struct qtag_prefix { 313 struct vlan_head *vh = (struct vlan_head *)skb->data;
308 __be16 eth_type; /* ETH_P_8021Q */
309 __be16 tci;
310 };
311 struct qtag_prefix *qp;
312 314
313 if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16))) 315 if (likely(!eth_type_vlan(vh->tpid)))
314 return 0; 316 return 0;
315 317
316 if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + 318 if (unlikely(skb->len < sizeof(struct vlan_head) + sizeof(__be16)))
317 sizeof(__be16)))) 319 return 0;
320
321 if (unlikely(!pskb_may_pull(skb, sizeof(struct vlan_head) +
322 sizeof(__be16))))
318 return -ENOMEM; 323 return -ENOMEM;
319 324
320 qp = (struct qtag_prefix *) skb->data; 325 vh = (struct vlan_head *)skb->data;
321 key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT); 326 key_vh->tci = vh->tci | htons(VLAN_TAG_PRESENT);
322 __skb_pull(skb, sizeof(struct qtag_prefix)); 327 key_vh->tpid = vh->tpid;
328
329 __skb_pull(skb, sizeof(struct vlan_head));
330 return 1;
331}
332
333static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
334{
335 int res;
336
337 key->eth.vlan.tci = 0;
338 key->eth.vlan.tpid = 0;
339 key->eth.cvlan.tci = 0;
340 key->eth.cvlan.tpid = 0;
341
342 if (likely(skb_vlan_tag_present(skb))) {
343 key->eth.vlan.tci = htons(skb->vlan_tci);
344 key->eth.vlan.tpid = skb->vlan_proto;
345 } else {
346 /* Parse outer vlan tag in the non-accelerated case. */
347 res = parse_vlan_tag(skb, &key->eth.vlan);
348 if (res <= 0)
349 return res;
350 }
351
352 /* Parse inner vlan tag. */
353 res = parse_vlan_tag(skb, &key->eth.cvlan);
354 if (res <= 0)
355 return res;
323 356
324 return 0; 357 return 0;
325} 358}
@@ -480,12 +513,8 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
480 * update skb->csum here. 513 * update skb->csum here.
481 */ 514 */
482 515
483 key->eth.tci = 0; 516 if (unlikely(parse_vlan(skb, key)))
484 if (skb_vlan_tag_present(skb)) 517 return -ENOMEM;
485 key->eth.tci = htons(skb->vlan_tci);
486 else if (eth->h_proto == htons(ETH_P_8021Q))
487 if (unlikely(parse_vlan(skb, key)))
488 return -ENOMEM;
489 518
490 key->eth.type = parse_ethertype(skb); 519 key->eth.type = parse_ethertype(skb);
491 if (unlikely(key->eth.type == htons(0))) 520 if (unlikely(key->eth.type == htons(0)))
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 03378e75a67c..156a3029c17b 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -50,6 +50,11 @@ struct ovs_tunnel_info {
50 struct metadata_dst *tun_dst; 50 struct metadata_dst *tun_dst;
51}; 51};
52 52
53struct vlan_head {
54 __be16 tpid; /* Vlan type. Generally 802.1q or 802.1ad.*/
55 __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
56};
57
53#define OVS_SW_FLOW_KEY_METADATA_SIZE \ 58#define OVS_SW_FLOW_KEY_METADATA_SIZE \
54 (offsetof(struct sw_flow_key, recirc_id) + \ 59 (offsetof(struct sw_flow_key, recirc_id) + \
55 FIELD_SIZEOF(struct sw_flow_key, recirc_id)) 60 FIELD_SIZEOF(struct sw_flow_key, recirc_id))
@@ -69,7 +74,8 @@ struct sw_flow_key {
69 struct { 74 struct {
70 u8 src[ETH_ALEN]; /* Ethernet source address. */ 75 u8 src[ETH_ALEN]; /* Ethernet source address. */
71 u8 dst[ETH_ALEN]; /* Ethernet destination address. */ 76 u8 dst[ETH_ALEN]; /* Ethernet destination address. */
72 __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ 77 struct vlan_head vlan;
78 struct vlan_head cvlan;
73 __be16 type; /* Ethernet frame type. */ 79 __be16 type; /* Ethernet frame type. */
74 } eth; 80 } eth;
75 union { 81 union {
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index c78a6a1476fb..8efa718ddb5e 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -808,6 +808,167 @@ int ovs_nla_put_tunnel_info(struct sk_buff *skb,
808 ip_tunnel_info_af(tun_info)); 808 ip_tunnel_info_af(tun_info));
809} 809}
810 810
811static int encode_vlan_from_nlattrs(struct sw_flow_match *match,
812 const struct nlattr *a[],
813 bool is_mask, bool inner)
814{
815 __be16 tci = 0;
816 __be16 tpid = 0;
817
818 if (a[OVS_KEY_ATTR_VLAN])
819 tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
820
821 if (a[OVS_KEY_ATTR_ETHERTYPE])
822 tpid = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]);
823
824 if (likely(!inner)) {
825 SW_FLOW_KEY_PUT(match, eth.vlan.tpid, tpid, is_mask);
826 SW_FLOW_KEY_PUT(match, eth.vlan.tci, tci, is_mask);
827 } else {
828 SW_FLOW_KEY_PUT(match, eth.cvlan.tpid, tpid, is_mask);
829 SW_FLOW_KEY_PUT(match, eth.cvlan.tci, tci, is_mask);
830 }
831 return 0;
832}
833
834static int validate_vlan_from_nlattrs(const struct sw_flow_match *match,
835 u64 key_attrs, bool inner,
836 const struct nlattr **a, bool log)
837{
838 __be16 tci = 0;
839
840 if (!((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) &&
841 (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) &&
842 eth_type_vlan(nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE])))) {
843 /* Not a VLAN. */
844 return 0;