Kernel recognizes and accepts ethertype of provider VLANs (ETH_P_8021AD) along with normal VLAN (ETH_P_8021Q). Also in flow key formation, only the outermost VLAN is considered. Remaining stacked VLANs are skipped.
Signed-off-by: Avinash <avinashand...@gmail.com> --- datapath/actions.c | 10 ++++++---- datapath/flow.c | 34 ++++++++++++++++++++++++++++++++-- datapath/flow_netlink.c | 17 +++++++++++++---- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 603c7cb..8dd11a0 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -84,8 +84,9 @@ static int pop_vlan(struct sk_buff *skb) if (likely(vlan_tx_tag_present(skb))) { vlan_set_tci(skb, 0); } else { - if (unlikely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) + if (unlikely((skb->protocol != htons(ETH_P_8021Q) + && skb->protocol != htons(ETH_P_8021AD)) + || skb->len < VLAN_ETH_HLEN)) return 0; err = __pop_vlan_tci(skb, &tci); @@ -93,8 +94,9 @@ static int pop_vlan(struct sk_buff *skb) return err; } /* move next vlan tag to hw accel tag */ - if (likely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) + if (likely((skb->protocol != htons(ETH_P_8021Q) + && skb->protocol != htons(ETH_P_8021AD)) + || skb->len < VLAN_ETH_HLEN)) return 0; err = __pop_vlan_tci(skb, &tci); diff --git a/datapath/flow.c b/datapath/flow.c index c52081b..c64dfe4 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -290,7 +290,7 @@ static bool icmp6hdr_ok(struct sk_buff *skb) static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) { struct qtag_prefix { - __be16 eth_type; /* ETH_P_8021Q */ + __be16 eth_type; /* ETH_P_8021Q or ETH_P_8021AD */ __be16 tci; }; struct qtag_prefix *qp; @@ -309,6 +309,32 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) return 0; } +static int parse_remaining_vlans(struct sk_buff *skb) +{ + struct qtag_prefix { + __be16 eth_type; + __be16 tci; + }; + + struct qtag_prefix *qp; + if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16))) + return 0; + + if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + + sizeof(__be16)))) + return -ENOMEM; + + qp = (struct qtag_prefix *) skb->data; + while ((qp->eth_type == htons(ETH_P_8021Q) || + qp->eth_type == htons(ETH_P_8021AD))) + { + __skb_pull(skb, sizeof(struct qtag_prefix)); + qp = (struct qtag_prefix *) skb->data; + } + + return 0; +} + static __be16 parse_ethertype(struct sk_buff *skb) { struct llc_snap_hdr { @@ -471,10 +497,14 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) if (vlan_tx_tag_present(skb)) key->eth.tci = htons(vlan_get_tci(skb)); - else if (eth->h_proto == htons(ETH_P_8021Q)) + else if (eth->h_proto == htons(ETH_P_8021Q) || + eth->h_proto == htons(ETH_P_8021AD)) if (unlikely(parse_vlan(skb, key))) return -ENOMEM; + if (unlikely(parse_remaining_vlans(skb))) + return -ENOMEM; + key->eth.type = parse_ethertype(skb); if (unlikely(key->eth.type == htons(0))) return -ENOMEM; diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index 803a94c..54b13fc 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -764,7 +764,8 @@ int ovs_nla_get_match(struct sw_flow_match *match, if ((key_attrs & (1ULL << OVS_KEY_ATTR_ETHERNET)) && (key_attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) && - (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) { + ((nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q)) + || (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021AD)))) { __be16 tci; if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) && @@ -937,9 +938,16 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, ether_addr_copy(eth_key->eth_src, output->eth.src); ether_addr_copy(eth_key->eth_dst, output->eth.dst); - if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { + if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q) + || swkey->eth.type == htons(ETH_P_8021AD)) { __be16 eth_type; - eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); + if (is_mask) + eth_type = htons(0xffff); + else + eth_type = (swkey->eth.type == htons(ETH_P_8021AD)) + ? htons(ETH_P_8021AD) + : htons(ETH_P_8021Q); + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) goto nla_put_failure; @@ -1493,7 +1501,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr, case OVS_ACTION_ATTR_PUSH_VLAN: vlan = nla_data(a); - if (vlan->vlan_tpid != htons(ETH_P_8021Q)) + if (vlan->vlan_tpid != htons(ETH_P_8021Q) + && vlan->vlan_tpid != htons(ETH_P_8021AD)) return -EINVAL; if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT))) return -EINVAL; -- 1.9.0 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev