This patch adds support for 802.1AD by adding parsing of 802.1AD double stacked vlans.
Signed-off-by: Thomas F Herbert <thomasfherb...@entpnt.com> --- lib/odp-execute.c | 2 +- lib/odp-util.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 192 insertions(+), 27 deletions(-) diff --git a/lib/odp-execute.c b/lib/odp-execute.c index 9ff418d..79d39b1 100644 --- a/lib/odp-execute.c +++ b/lib/odp-execute.c @@ -478,7 +478,7 @@ odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt, bool steal, for (i = 0; i < cnt; i++) { struct ofpbuf *buf = &packets[i]->ofpbuf; - eth_push_vlan(buf, htons(ETH_TYPE_VLAN), vlan->vlan_tci); + eth_push_vlan(buf, vlan->vlan_tpid, vlan->vlan_tci); } break; } diff --git a/lib/odp-util.c b/lib/odp-util.c index d52c172..06ddfaf 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -2861,7 +2861,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, size_t max_mpls_depth, bool recirc, bool export_mask) { struct ovs_key_ethernet *eth_key; - size_t encap; + size_t encap = 0; const struct flow *data = export_mask ? mask : flow; nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority); @@ -2887,11 +2887,19 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow, sizeof *eth_key); get_ethernet_key(data, eth_key); - if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) { - if (export_mask) { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + if (flow->vlan_tci != htons(0) || eth_type_vlan(flow->dl_type)) { + if (flow->vlan_ctci != htons(0)) { + if (export_mask) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + } else { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN_8021AD)); + } } else { - nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN)); + if (export_mask) { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX); + } else { + nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN_8021Q)); + } } nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci); encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP); @@ -3624,6 +3632,81 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], return MAX(fitness, encap_fitness); } +/* Parse 802.1ADQ header then parse encapsulated Customer Vlan. */ +static enum odp_key_fitness +parse_8021ad_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1], + uint64_t present_attrs, int out_of_range_attr, + uint64_t expected_attrs, struct flow *flow, + const struct nlattr *key, size_t key_len, + const struct flow *src_flow) +{ + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); + bool is_mask = src_flow != flow; + + const struct nlattr *encap + = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP) + ? attrs[OVS_KEY_ATTR_ENCAP] : NULL); + enum odp_key_fitness encap_fitness; + enum odp_key_fitness fitness; + + /* Calculate fitness of outer attributes. */ + if (!is_mask) { + expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) | + (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)); + } else { + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) { + expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN); + } + if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) { + expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP); + } + } + fitness = check_expectations(present_attrs, out_of_range_attr, + expected_attrs, key, key_len); + + /* Set outertag, vlan_tci. + * Remove the TPID from dl_type since it's not the real Ethertype. */ + flow->dl_type = htons(0); + flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN) + ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]) + : htons(0)); + if (!is_mask) { + if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) { + return ODP_FIT_TOO_LITTLE; + } else if (flow->vlan_tci == htons(0)) { + /* Corner case for a truncated 802.1Q header. */ + if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) { + return ODP_FIT_TOO_MUCH; + } + return fitness; + } else if (!(flow->vlan_tci & htons(VLAN_CFI))) { + VLOG_ERR_RL(&rl, "Outer OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero " + "but CFI bit is not set", ntohs(flow->vlan_tci)); + return ODP_FIT_ERROR; + } + } else { + if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) { + return fitness; + } + } + + /* Now parse the encapsulated attributes. */ + if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap), + attrs, &present_attrs, &out_of_range_attr)) { + return ODP_FIT_ERROR; + } + expected_attrs = 0; + + if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { + return ODP_FIT_ERROR; + } + encap_fitness = parse_8021q_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + + /* The overall fitness is the worse of the outer and inner attributes. */ + return MAX(fitness, encap_fitness); +} + static enum odp_key_fitness odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, struct flow *flow, const struct flow *src_flow) @@ -3699,7 +3782,7 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET; } - /* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */ + /* Get Ethertype or VLAN TPID or FLOW_DL_TYPE_NONE. */ if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) { return ODP_FIT_ERROR; @@ -3707,9 +3790,14 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len, if (is_mask ? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0 - : src_flow->dl_type == htons(ETH_TYPE_VLAN)) { - return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, - expected_attrs, flow, key, key_len, src_flow); + : eth_type_vlan(src_flow->dl_type)) { + if (src_flow->dl_type == ETH_TYPE_VLAN_8021AD) { + return parse_8021ad_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + } else { + return parse_8021q_onward(attrs, present_attrs, out_of_range_attr, + expected_attrs, flow, key, key_len, src_flow); + } } if (is_mask) { flow->vlan_tci = htons(0xffff); @@ -3947,37 +4035,114 @@ commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow, put_ethernet_key(&mask, &wc->masks); } } - +static int +flow_get_vlan_depth(const struct flow *flow) +{ + int n = 0; + if (flow->vlan_tci & htons(VLAN_CFI)) + n++; + if (flow->vlan_ctci & htons(VLAN_CFI)) + n++; + return n; +} static void -pop_vlan(struct flow *base, - struct ofpbuf *odp_actions, struct flow_wildcards *wc) +pop_vlan(struct flow *base, struct ofpbuf *odp_actions, struct flow_wildcards *wc) { + int base_n = flow_get_vlan_depth(base); + + if (base_n == 0) + return; + memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); - if (base->vlan_tci & htons(VLAN_CFI)) { - nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); + if (base_n == 1) { + base->vlan_tci = 0; + base->vlan_tpid = 0; + base->vlan_ctci = 0; + + } else if (base_n ==2) { + + base->vlan_tci = base->vlan_ctci; + base->vlan_ctci = 0; + base->vlan_tpid = htons(ETH_TYPE_VLAN_8021Q); } + nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN); } static void -commit_vlan_action(ovs_be16 vlan_tci, struct flow *base, - struct ofpbuf *odp_actions, struct flow_wildcards *wc) +push_vlan(struct flow *base, ovs_be16 vlan_tci, ovs_be16 vlan_tpid, + struct ofpbuf *odp_actions, struct flow_wildcards *wc) { - if (base->vlan_tci == vlan_tci) { + struct ovs_action_push_vlan vlan; + int base_n; + ovs_be16 tpid = htons(ETH_TYPE_VLAN_8021Q); + + if (!(vlan_tci & htons(VLAN_CFI))) return; - } - pop_vlan(base, odp_actions, wc); - if (vlan_tci & htons(VLAN_CFI)) { - struct ovs_action_push_vlan vlan; + memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci); + - vlan.vlan_tpid = htons(ETH_TYPE_VLAN); - vlan.vlan_tci = vlan_tci; - nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, - &vlan, sizeof vlan); + base_n = flow_get_vlan_depth(base); + + if (base_n == 2) { + return; + } else if (base_n == 1) { + if (!vlan_tpid) { + tpid = htons(ETH_TYPE_VLAN_8021AD); + } else { + tpid = vlan_tpid; + } + base->vlan_ctci = base->vlan_tci; + } else if (base_n == 0) { + if (!vlan_tpid) { + tpid = htons(ETH_TYPE_VLAN_8021Q); + } else { + tpid = vlan_tpid; + } + base->vlan_ctci = 0; } base->vlan_tci = vlan_tci; + base->vlan_tpid = tpid; + + vlan.vlan_tpid = tpid; + vlan.vlan_tci = vlan_tci; + nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN, + &vlan, sizeof vlan); +} + +static void +commit_vlan_action(const struct flow *flow, struct flow *base, + struct ofpbuf *odp_actions, struct flow_wildcards *wc) +{ + ovs_be16 vlan_tci = flow->vlan_tci; + ovs_be16 vlan_tpid = flow->vlan_tpid; + int base_n; + int flow_n; + + + if ((base->vlan_tci == vlan_tci) && (base->vlan_ctci == vlan_tci)) + return; + + base_n = flow_get_vlan_depth(base); + flow_n = flow_get_vlan_depth(flow); + + + if (flow_n == base_n) { + if (vlan_tci == base->vlan_tci) { + return; + } else { + pop_vlan(base, odp_actions, wc); + push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc); + } + } + else if (flow_n > base_n) { + push_vlan(base, vlan_tci, vlan_tpid, odp_actions, wc); + } + else if (flow_n < base_n) { + pop_vlan(base, odp_actions, wc); + } } /* Wildcarding already done at action translation time. */ @@ -4327,7 +4492,7 @@ commit_odp_actions(const struct flow *flow, struct flow *base, slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked); commit_set_port_action(flow, base, odp_actions, wc, use_masked); commit_mpls_action(flow, base, odp_actions); - commit_vlan_action(flow->vlan_tci, base, odp_actions, wc); + commit_vlan_action(flow, base, odp_actions, wc); commit_set_priority_action(flow, base, odp_actions, wc, use_masked); commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked); -- 1.8.3.2 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev