diff options
| -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; | ||
| 845 | } | ||
| 846 | |||
| 847 | if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) && | ||
| 848 | (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) { | ||
| 849 | OVS_NLERR(log, "Invalid %s frame", (inner) ? "C-VLAN" : "VLAN"); | ||
| 850 | return -EINVAL; | ||
| 851 | } | ||
| 852 | |||
| 853 | if (a[OVS_KEY_ATTR_VLAN]) | ||
