This is the linux kernel datapath portion of the 802.1AD patch.
Signed-off-by: Thomas F Herbert <thomasfherb...@entpnt.com> --- datapath/actions.c | 32 ++++++--- datapath/flow.c | 80 +++++++++++++++++++---- datapath/flow.h | 1 + datapath/flow_netlink.c | 62 ++++++++++++++++-- datapath/linux/compat/include/linux/openvswitch.h | 16 ++--- 5 files changed, 153 insertions(+), 38 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 5a1dbe2..baa82e6 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -250,17 +250,18 @@ static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci) return 0; } - static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) { __be16 tci; int err; + __be16 prot; 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); @@ -269,9 +270,9 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) } /* move next vlan tag to hw accel tag */ if (likely(skb->protocol != htons(ETH_P_8021Q) || - skb->len < VLAN_ETH_HLEN)) { - key->eth.tci = 0; - return 0; + skb->len < VLAN_ETH_HLEN)) { + key->eth.tci = 0; + return 0; } invalidate_flow_key(key); @@ -279,7 +280,13 @@ static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key) if (unlikely(err)) return err; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci)); + if (unlikely(skb->protocol == htons(ETH_P_8021AD))) + prot = htons(ETH_P_8021AD); + else + prot = htons(ETH_P_8021Q); + + __vlan_hwaccel_put_tag(skb, prot, ntohs(tci)); + return 0; } @@ -297,15 +304,20 @@ static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key, /* Update mac_len for subsequent MPLS actions */ skb->mac_len += VLAN_HLEN; - if (skb->ip_summed == CHECKSUM_COMPLETE) - skb->csum = csum_add(skb->csum, csum_partial(skb->data + skb->csum = csum_add(skb->csum, csum_partial(skb->data + (2 * ETH_ALEN), VLAN_HLEN, 0)); invalidate_flow_key(key); } else { key->eth.tci = vlan->vlan_tci; } - __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); + + if (likely(vlan->vlan_tpid == htons(ETH_P_8021Q))) { + __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); + } else { + __vlan_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT); + skb->vlan_tci &= ~VLAN_TAG_PRESENT; + } return 0; } diff --git a/datapath/flow.c b/datapath/flow.c index 69b13b3..d549fae 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -296,21 +296,75 @@ 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; + struct qtag_prefix *qp = (struct qtag_prefix *) skb->data; - if (unlikely(skb->len < sizeof(struct qtag_prefix) + sizeof(__be16))) - return 0; + struct qinqtag_prefix { + __be16 eth_type; /* ETH_P_8021Q or ETH_P_8021AD */ + __be16 tci; + __be16 encapsulated_proto; /* ETH_P_8021Q */ + __be16 ctci; + }; - if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + - sizeof(__be16)))) - return -ENOMEM; + if (likely(skb->vlan_tci | htons(VLAN_TAG_PRESENT))) { + + key->eth.tci = skb->vlan_tci | htons(VLAN_TAG_PRESENT); + + if(unlikely(skb->vlan_proto == htons(ETH_P_8021AD))) { + struct qtag_prefix *qp = (struct qtag_prefix *) skb->data; + + 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; + } + + if (likely(qp->eth_type == htons(ETH_P_8021Q))) { + key->eth.ctci = qp->tci | htons(VLAN_TAG_PRESENT); + __skb_pull(skb, sizeof(struct qtag_prefix)); + } + } + return 0; + } + + + if (qp->eth_type == htons(ETH_P_8021AD)) { + struct qinqtag_prefix *qinqp = (struct qinqtag_prefix *) skb->data; - qp = (struct qtag_prefix *) skb->data; - key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT); - __skb_pull(skb, sizeof(struct qtag_prefix)); + if (unlikely(skb->len < sizeof(struct qinqtag_prefix) + + sizeof(__be16))) + return 0; + + if (unlikely(!pskb_may_pull(skb, sizeof(struct qinqtag_prefix) + + sizeof(__be16)))) { + return -ENOMEM; + } + key->eth.tci = qinqp->tci | htons(VLAN_TAG_PRESENT); + key->eth.ctci = qinqp->ctci | htons(VLAN_TAG_PRESENT); + + __skb_pull(skb, sizeof(struct qinqtag_prefix)); + + return 0; + + } + if (qp->eth_type == htons(ETH_P_8021Q)) { + + if (unlikely(skb->len < sizeof(struct qtag_prefix) + + sizeof(__be16))) + return -ENOMEM; + + if (unlikely(!pskb_may_pull(skb, sizeof(struct qtag_prefix) + + sizeof(__be16)))) + return 0; + key->eth.tci = qp->tci | htons(VLAN_TAG_PRESENT); + + __skb_pull(skb, sizeof(struct qtag_prefix)); + } return 0; } @@ -472,9 +526,9 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) */ key->eth.tci = 0; - if (vlan_tx_tag_present(skb)) - key->eth.tci = htons(vlan_get_tci(skb)); - else if (eth->h_proto == htons(ETH_P_8021Q)) + if ((vlan_tx_tag_present(skb)) || + (eth->h_proto == htons(ETH_P_8021Q)) || + (eth->h_proto == htons(ETH_P_8021AD))) if (unlikely(parse_vlan(skb, key))) return -ENOMEM; diff --git a/datapath/flow.h b/datapath/flow.h index 2bbf789..0bf0477 100644 --- a/datapath/flow.h +++ b/datapath/flow.h @@ -134,6 +134,7 @@ struct sw_flow_key { u8 src[ETH_ALEN]; /* Ethernet source address. */ u8 dst[ETH_ALEN]; /* Ethernet destination address. */ __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ + __be16 ctci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */ __be16 type; /* Ethernet frame type. */ } eth; union { diff --git a/datapath/flow_netlink.c b/datapath/flow_netlink.c index c611e71..2e307ba 100644 --- a/datapath/flow_netlink.c +++ b/datapath/flow_netlink.c @@ -274,7 +274,7 @@ size_t ovs_key_attr_size(void) return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ - + ovs_tun_key_attr_size() + + ovs_tun_key_attr_size() + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */ @@ -681,6 +681,28 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, } return 0; } +static int ovs_nested_vlan_from_nlattrs(struct sw_flow_match *match, + u64 attrs, const struct nlattr **a, + bool is_mask, bool log) +{ + if (attrs & (1ULL << OVS_KEY_ATTR_VLAN)) { /* This should be nested inner or "customer" tci" */ + __be16 ctci; + + ctci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + if (!(ctci & htons(VLAN_TAG_PRESENT))) { + if (is_mask) + OVS_NLERR(log, "VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit."); + else + OVS_NLERR(log, "VLAN TCI does not have VLAN_TAG_PRESENT bit set."); + + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, eth.ctci, ctci, is_mask); + attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN); + } + return 0; +} static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, const struct nlattr **a, bool is_mask, @@ -720,6 +742,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, attrs &= ~(1ULL << OVS_KEY_ATTR_VLAN); } + if (attrs & (1ULL << OVS_KEY_ATTR_ETHERTYPE)) { __be16 eth_type; @@ -969,8 +992,9 @@ int ovs_nla_get_match(struct sw_flow_match *match, return err; 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))) { + (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_8021AD)))) { __be16 tci; if (!((key_attrs & (1ULL << OVS_KEY_ATTR_VLAN)) && @@ -986,9 +1010,23 @@ int ovs_nla_get_match(struct sw_flow_match *match, encap_valid = true; if (tci & htons(VLAN_TAG_PRESENT)) { - err = parse_flow_nlattrs(encap, a, &key_attrs, log); - if (err) - return err; + u64 v_attrs = 0; + + if (unlikely((nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021AD)))) { + + err = parse_flow_nlattrs(encap, a, &v_attrs, log); + if (err) + return err; + if (v_attrs) { + err = ovs_nested_vlan_from_nlattrs(match, v_attrs, a, false, log); + if (err) + return err; + } + } else { + err = parse_flow_nlattrs(encap, a, &key_attrs, log); + if (err) + return err; + } } else if (!tci) { /* Corner case for truncated 802.1Q header. */ if (nla_len(encap)) { @@ -1191,6 +1229,15 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey, encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); if (!swkey->eth.tci) goto unencap; + } else if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021AD)) { + __be16 eth_type; + eth_type = !is_mask ? htons(ETH_P_8021AD) : htons(0xffff); + 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; + encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); + if (!swkey->eth.tci) + goto unencap; } else encap = NULL; @@ -1828,7 +1875,8 @@ static 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; diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 67715f8..6b5538a 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -570,14 +570,14 @@ struct ovs_action_push_mpls { * @vlan_tci: Tag control identifier (TCI) to push. The CFI bit must be set * (but it will not be set in the 802.1Q header that is pushed). * - * The @vlan_tpid value is typically %ETH_P_8021Q. The only acceptable TPID - * values are those that the kernel module also parses as 802.1Q headers, to - * prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by %OVS_ACTION_ATTR_POP_VLAN - * from having surprising results. + * The @vlan_tpid value is typically %ETH_P_8021Q or %ETH_P_8021AD. + * The only acceptable TPID values are those that the kernel module also parses + * as 802.1Q headers, to prevent %OVS_ACTION_ATTR_PUSH_VLAN followed by + * %OVS_ACTION_ATTR_POP_VLAN from having surprising results. */ struct ovs_action_push_vlan { - __be16 vlan_tpid; /* 802.1Q TPID. */ - __be16 vlan_tci; /* 802.1Q TCI (VLAN ID and priority). */ + __be16 vlan_tpid; /* 802.1Q or 802.1ad TPID. */ + __be16 vlan_tci; /* 802.1Q or 802.1ad TCI (VLAN ID and priority). */ }; /* Data path hash algorithm for computing Datapath hash. @@ -629,9 +629,9 @@ struct ovs_action_push_tnl { * @OVS_ACTION_ATTR_OUTPUT: Output packet to port. * @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested * %OVS_USERSPACE_ATTR_* attributes. - * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q header onto the + * @OVS_ACTION_ATTR_PUSH_VLAN: Push a new outermost 802.1Q or 802.1ad header onto the * packet. - * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q header off the packet. + * @OVS_ACTION_ATTR_POP_VLAN: Pop the outermost 802.1Q or 802.1ad header off the packet. * @OVS_ACTION_ATTR_SAMPLE: Probabilitically executes actions, as specified in * the nested %OVS_SAMPLE_ATTR_* attributes. * @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The -- 1.8.3.2 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev