[ovs-dev,V14,2/4] Flow key and netlink parsing changes for 802.1AD
diff mbox

Message ID 1443821521-14770-3-git-send-email-thomasfherbert@gmail.com
State Deferred
Headers show

Commit Message

Thomas F Herbert Oct. 2, 2015, 9:31 p.m. UTC
From: "Thomas F. Herbert" <thomasfherbert@gmail.com>

Netlink parsing and flow key conversion. Netlink attribute encoding is done
with double encap attributes. Netlink attributes for 802.1ad look like
the following:

    eth_type(0x88a8),vlan(vid=100),encap(eth_type(0x8100), vlan(vid=200),
                encap(eth_type(0x0800), ...))

V14 is rebased to recent OVS.
V11 fixes issues in the inner vlan encapsulation.

Signed-off-by: Thomas F Herbert <thomasfherbert@gmail.com>
---
 lib/odp-util.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 242 insertions(+), 39 deletions(-)

Patch
diff mbox

diff --git a/lib/odp-util.c b/lib/odp-util.c
index d7696c9..120657b 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3348,7 +3348,8 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
                          bool export_mask, struct ofpbuf *buf)
 {
     struct ovs_key_ethernet *eth_key;
-    size_t encap;
+    size_t encap = 0;
+    size_t inner_encap = 0;
     const struct flow *flow = parms->flow;
     const struct flow *data = export_mask ? parms->mask : parms->flow;
 
@@ -3376,19 +3377,33 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
                                        sizeof *eth_key);
     get_ethernet_key(data, eth_key);
 
-    if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
+    if ((flow->vlan_tci != htons(0)) || eth_type_vlan(flow->dl_type)) {
         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));
+            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);
         if (flow->vlan_tci == htons(0)) {
             goto unencap;
         }
-    } else {
-        encap = 0;
+        /* Now, code the inner customer vlan. */
+        if ((flow->vlan_ctci != htons(0)) || eth_type_vlan(flow->vlan_tpid)) {
+            if (export_mask) {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
+            } else {
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->vlan_tpid);
+                nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_ctci);
+            }
+            /* Now, code another layer of nesting for the payload. */
+            inner_encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
+            if (flow->vlan_ctci == htons(0)) {
+                goto unencap;
+            }
+        }
     }
 
     if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
@@ -3503,6 +3518,9 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
     }
 
 unencap:
+    if (inner_encap) {
+        nl_msg_end_nested(buf, inner_encap);
+    }
     if (encap) {
         nl_msg_end_nested(buf, encap);
     }
@@ -4024,9 +4042,36 @@  done:
                               key, key_len);
 }
 
-/* Parse 802.1Q header then encapsulated L3 attributes. */
 static enum odp_key_fitness
-parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
+parse_encap_and_vlan(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)
+{
+    bool is_mask = src_flow != flow;
+    enum odp_key_fitness fitness;
+
+    /* Calculate fitness of vlan and encap 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);
+
+    return fitness;
+}
+
+/* Parse 802.1AD inner vlan and then parse encapsulated Attributes. */
+static enum odp_key_fitness
+parse_cvlan_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,
@@ -4041,27 +4086,89 @@  parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     enum odp_key_fitness encap_fitness;
     enum odp_key_fitness fitness;
 
-    /* Calculate fitness of outer attributes. */
+    /* Calculate fitness of inner vlan attributes. */
+    fitness = parse_encap_and_vlan(present_attrs, out_of_range_attr,
+                                   expected_attrs, flow, key, key_len,
+                                   src_flow);
+
+    /* Set vlan_ctci and vlan inner tpid. */
+    flow->vlan_ctci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
+                       ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
+                       : htons(0));
     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_VLAN))) {
+            return ODP_FIT_TOO_LITTLE;
+        } else if (flow->vlan_ctci == htons(0)) {
+            /* Corner case for a truncated 802.1AD header. */
+            if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
+                return ODP_FIT_TOO_MUCH;
+            }
+            return fitness;
+        } else if (!(flow->vlan_ctci & htons(VLAN_CFI))) {
+            VLOG_ERR_RL(&rl, "Cust OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
+                        "but CFI bit is not set", ntohs(flow->vlan_ctci));
+            return ODP_FIT_ERROR;
         }
-        if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
-            expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
+    } else {
+        if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
+            return fitness;
         }
     }
-    fitness = check_expectations(present_attrs, out_of_range_attr,
-                                 expected_attrs, key, key_len);
 
-    /* Set vlan_tci.
+    /* 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_l2_5_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);
+}
+
+/* Parse 802.1AD or 802.1Q and then parse encapsulated Attributes. */
+static enum odp_key_fitness
+parse_vlan_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;
+    uint16_t dl_type;
+
+    /* Calculate fitness of outer vlan attributes */
+    fitness = parse_encap_and_vlan(present_attrs, out_of_range_attr,
+                                   expected_attrs, flow, key, key_len,
+                                   src_flow);
+
+    /* 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));
+    /* Save a copy of the TPID though. If it is 802.1AD, we know to look for
+     * inner ctci in encapsulated attributes.
+     */
+    dl_type = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)
+               ? nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE])
+               : htons(0));
     if (!is_mask) {
         if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
             return ODP_FIT_TOO_LITTLE;
@@ -4072,8 +4179,8 @@  parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
             }
             return fitness;
         } else if (!(flow->vlan_tci & htons(VLAN_CFI))) {
-            VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
-                        "but CFI bit is not set", ntohs(flow->vlan_tci));
+            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 {
@@ -4089,6 +4196,23 @@  parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
     }
     expected_attrs = 0;
 
+    if (dl_type == htons(ETH_TYPE_VLAN_8021AD)) {
+        if (is_mask
+            ? (src_flow->vlan_ctci & htons(VLAN_CFI)) != 0
+            : eth_type_vlan(dl_type)) {
+            /* Get inner vlan tag and tpid from the encapsulated attributes. */
+            return parse_cvlan_onward(attrs, present_attrs, out_of_range_attr,
+                                      expected_attrs, flow, key, key_len,
+                                      src_flow);
+            }
+        if (is_mask) {
+            flow->vlan_ctci = htons(0xffff);
+            if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
+                flow->vlan_ctci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
+                expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
+            }
+        }
+    }
     if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
         return ODP_FIT_ERROR;
     }
@@ -4180,7 +4304,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;
@@ -4188,9 +4312,9 @@  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)) {
+        return parse_vlan_onward(attrs, present_attrs, out_of_range_attr,
+                                 expected_attrs, flow, key, key_len, src_flow);
     }
     if (is_mask) {
         flow->vlan_tci = htons(0xffff);
@@ -4199,6 +4323,7 @@  odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
             expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
         }
     }
+
     return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
                              expected_attrs, flow, key, key_len, src_flow);
 }
@@ -4460,37 +4585,115 @@  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)
 {
+    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. */
@@ -4879,7 +5082,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);