diff options
| author | Eric Garver <e@erig.me> | 2016-09-07 12:56:59 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-09-08 20:10:28 -0400 |
| commit | 018c1dda5ff1e7bd1fe2d9fd1d0f5b82dc6fc0cd (patch) | |
| tree | ccedb1acda9922d975903894776dcfb853fc3c2d /net/openvswitch | |
| parent | fe19c4f971a55cea3be442d8032a5f6021702791 (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.c | 16 | ||||
| -rw-r--r-- | net/openvswitch/flow.c | 65 | ||||
| -rw-r--r-- | net/openvswitch/flow.h | 8 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 310 | ||||
| -rw-r--r-- | net/openvswitch/vport.c | 7 |
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 | ||
| 256 | static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, | 258 | static 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 | ||
| 305 | static 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 | */ | ||
| 311 | static 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 | |||
| 333 | static 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 | ||
| 53 | struct 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 | ||
| 811 | static 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 | |||
| 834 | static 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; | ||
