tunnel: Geneve TLV handling support for OpenFlow.
[cascardo/ovs.git] / lib / odp-util.c
index c3497ea..efdc651 100644 (file)
 #include "packets.h"
 #include "simap.h"
 #include "timeval.h"
+#include "tun-metadata.h"
 #include "unaligned.h"
 #include "util.h"
+#include "uuid.h"
 #include "openvswitch/vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(odp_util);
@@ -51,8 +53,6 @@ VLOG_DEFINE_THIS_MODULE(odp_util);
  * from another. */
 static const char *delimiters = ", \t\r\n";
 
-static const char *hex_chars = "0123456789abcdefABCDEF";
-
 struct attr_len_tbl {
     int len;
     const struct attr_len_tbl *next;
@@ -1318,41 +1318,10 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
     }
 }
 
-#define GENEVE_OPT(class, type) ((OVS_FORCE uint32_t)(class) << 8 | (type))
-static int
-parse_geneve_opts(const struct nlattr *attr)
-{
-    int opts_len = nl_attr_get_size(attr);
-    const struct geneve_opt *opt = nl_attr_get(attr);
-
-    while (opts_len > 0) {
-        int len;
-
-        if (opts_len < sizeof(*opt)) {
-            return -EINVAL;
-        }
-
-        len = sizeof(*opt) + opt->length * 4;
-        if (len > opts_len) {
-            return -EINVAL;
-        }
-
-        switch (GENEVE_OPT(opt->opt_class, opt->type)) {
-        default:
-            if (opt->type & GENEVE_CRIT_OPT_TYPE) {
-                return -EINVAL;
-            }
-        };
-
-        opt = opt + len / sizeof(*opt);
-        opts_len -= len;
-    };
-
-    return 0;
-}
-
-enum odp_key_fitness
-odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+static enum odp_key_fitness
+odp_tun_key_from_attr__(const struct nlattr *attr,
+                        const struct nlattr *flow_attrs, size_t flow_attr_len,
+                        const struct flow_tnl *src_tun, struct flow_tnl *tun)
 {
     unsigned int left;
     const struct nlattr *a;
@@ -1421,15 +1390,14 @@ odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
 
             break;
         }
-        case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
-            if (parse_geneve_opts(a)) {
+        case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+            if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len,
+                                                &src_tun->metadata,
+                                                &tun->metadata)) {
                 return ODP_FIT_ERROR;
             }
-            /* It is necessary to reproduce options exactly (including order)
-             * so it's easiest to just echo them back. */
-            unknown = true;
             break;
-        }
+
         default:
             /* Allow this to show up as unexpected, if there are unknown
              * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
@@ -1447,8 +1415,16 @@ odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
     return ODP_FIT_PERFECT;
 }
 
+enum odp_key_fitness
+odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+    return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun);
+}
+
 static void
-tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
+                const struct flow_tnl *tun_flow_key,
+                const struct ofpbuf *key_buf)
 {
     size_t tun_key_ofs;
 
@@ -1492,6 +1468,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
         nl_msg_end_nested(a, vxlan_opts_ofs);
     }
 
+    if (tun_key == tun_flow_key) {
+        tun_metadata_to_geneve_nlattr_flow(&tun_key->metadata, a);
+    } else {
+        tun_metadata_to_geneve_nlattr_mask(key_buf, &tun_key->metadata,
+                                           &tun_flow_key->metadata, a);
+    }
+
     nl_msg_end_nested(a, tun_key_ofs);
 }
 
@@ -1521,18 +1504,7 @@ odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
             && ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_dst);
     }
     if (attr == OVS_KEY_ATTR_TUNNEL) {
-        const struct flow_tnl *tun_mask = mask;
-
-        return tun_mask->flags == FLOW_TNL_F_MASK
-            && tun_mask->tun_id == OVS_BE64_MAX
-            && tun_mask->ip_src == OVS_BE32_MAX
-            && tun_mask->ip_dst == OVS_BE32_MAX
-            && tun_mask->ip_tos == UINT8_MAX
-            && tun_mask->ip_ttl == UINT8_MAX
-            && tun_mask->tp_src == OVS_BE16_MAX
-            && tun_mask->tp_dst == OVS_BE16_MAX
-            && tun_mask->gbp_id == OVS_BE16_MAX
-            && tun_mask->gbp_flags == UINT8_MAX;
+        return false;
     }
 
     if (attr == OVS_KEY_ATTR_ARP) {
@@ -1549,16 +1521,12 @@ odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
 static bool
 odp_mask_attr_is_exact(const struct nlattr *ma)
 {
-    struct flow_tnl tun_mask;
     enum ovs_key_attr attr = nl_attr_type(ma);
     const void *mask;
     size_t size;
 
     if (attr == OVS_KEY_ATTR_TUNNEL) {
-        memset(&tun_mask, 0, sizeof tun_mask);
-        odp_tun_key_from_attr(ma, &tun_mask);
-        mask = &tun_mask;
-        size = sizeof tun_mask;
+        return false;
     } else {
         mask = nl_attr_get(ma);
         size = nl_attr_get_size(ma);
@@ -2354,24 +2322,12 @@ odp_ufid_from_string(const char *s_, ovs_u128 *ufid)
     const char *s = s_;
 
     if (ovs_scan(s, "ufid:")) {
-        size_t n;
-
         s += 5;
-        if (ovs_scan(s, "0x")) {
-            s += 2;
-        }
 
-        n = strspn(s, hex_chars);
-        if (n != 32) {
+        if (!uuid_from_string_prefix((struct uuid *)ufid, s)) {
             return -EINVAL;
         }
-
-        if (!ovs_scan(s, "%16"SCNx64"%16"SCNx64, &ufid->u64.hi,
-                      &ufid->u64.lo)) {
-            return -EINVAL;
-        }
-        s += n;
-        s += strspn(s, delimiters);
+        s += UUID_LEN;
 
         return s - s_;
     }
@@ -2382,8 +2338,7 @@ odp_ufid_from_string(const char *s_, ovs_u128 *ufid)
 void
 odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
 {
-    ds_put_format(ds, "ufid:%016"PRIx64"%016"PRIx64, ufid->u64.hi,
-                  ufid->u64.lo);
+    ds_put_format(ds, "ufid:"UUID_FMT, UUID_ARGS((struct uuid *)ufid));
 }
 
 /* Appends to 'ds' a string representation of the 'key_len' bytes of
@@ -2890,14 +2845,12 @@ static int
 scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
 {
     const char *s_base = s;
-    ovs_be16 id, id_mask;
-    uint8_t flags, flags_mask;
+    ovs_be16 id = 0, id_mask = 0;
+    uint8_t flags = 0, flags_mask = 0;
 
     if (!strncmp(s, "id=", 3)) {
         s += 3;
         s += scan_be16(s, &id, mask ? &id_mask : NULL);
-    } else if (mask) {
-        memset(&id_mask, 0, sizeof id_mask);
     }
 
     if (s[0] == ',') {
@@ -2906,8 +2859,6 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
     if (!strncmp(s, "flags=", 6)) {
         s += 6;
         s += scan_u8(s, &flags, mask ? &flags_mask : NULL);
-    } else if (mask) {
-        memset(&flags_mask, 0, sizeof flags_mask);
     }
 
     if (!strncmp(s, "))", 2)) {
@@ -2925,7 +2876,7 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
 }
 
 struct geneve_scan {
-    uint8_t d[252];
+    struct geneve_opt d[63];
     int len;
 };
 
@@ -2933,8 +2884,8 @@ static int
 scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
 {
     const char *s_base = s;
-    struct geneve_opt *opt = (struct geneve_opt *)key->d;
-    struct geneve_opt *opt_mask = (struct geneve_opt *)(mask ? mask->d : NULL);
+    struct geneve_opt *opt = key->d;
+    struct geneve_opt *opt_mask = mask ? mask->d : NULL;
     int len_remain = sizeof key->d;
 
     while (s[0] == '{' && len_remain >= sizeof *opt) {
@@ -3218,15 +3169,13 @@ static int
 parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                         struct ofpbuf *key, struct ofpbuf *mask)
 {
-    if (!strncmp(s, "ufid:", 5)) {
-        const char *start = s;
-
-        /* Skip UFID. */
-        s += 5;
-        s += strspn(s, hex_chars);
-        s += strspn(s, delimiters);
+    ovs_u128 ufid;
+    int len;
 
-        return s - start;
+    /* Skip UFID. */
+    len = odp_ufid_from_string(s, &ufid);
+    if (len) {
+        return len;
     }
 
     SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY);
@@ -3452,31 +3401,32 @@ static void get_tp_key(const struct flow *, union ovs_key_tp *);
 static void put_tp_key(const union ovs_key_tp *, struct flow *);
 
 static void
-odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
-                         const struct flow *mask, odp_port_t odp_in_port,
-                         size_t max_mpls_depth, bool recirc, bool export_mask)
+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;
-    const struct flow *data = export_mask ? mask : flow;
+    const struct flow *flow = parms->flow;
+    const struct flow *data = export_mask ? parms->mask : parms->flow;
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
 
     if (flow->tunnel.ip_dst || export_mask) {
-        tun_key_to_attr(buf, &data->tunnel);
+        tun_key_to_attr(buf, &data->tunnel, &parms->flow->tunnel,
+                        parms->key_buf);
     }
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
 
-    if (recirc) {
+    if (parms->recirc) {
         nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
         nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
     }
 
     /* Add an ingress port attribute if this is a mask or 'odp_in_port'
      * is not the magical value "ODPP_NONE". */
-    if (export_mask || odp_in_port != ODPP_NONE) {
-        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
+    if (export_mask || parms->odp_in_port != ODPP_NONE) {
+        nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, parms->odp_in_port);
     }
 
     eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
@@ -3542,7 +3492,9 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
         int i, n;
 
         n = flow_count_mpls_labels(flow, NULL);
-        n = MIN(n, max_mpls_depth);
+        if (export_mask) {
+            n = MIN(n, parms->max_mpls_depth);
+        }
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
                                             n * sizeof *mpls_key);
         for (i = 0; i < n; i++) {
@@ -3614,43 +3566,26 @@ unencap:
 }
 
 /* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
- * 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
- * number rather than a datapath port number).  Instead, if 'odp_in_port'
- * is anything other than ODPP_NONE, it is included in 'buf' as the input
- * port.
  *
  * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
 void
-odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
-                       const struct flow *mask, odp_port_t odp_in_port,
-                       bool recirc)
+odp_flow_key_from_flow(const struct odp_flow_key_parms *parms,
+                       struct ofpbuf *buf)
 {
-    odp_flow_key_from_flow__(buf, flow, mask, odp_in_port, SIZE_MAX, recirc,
-                             false);
+    odp_flow_key_from_flow__(parms, false, buf);
 }
 
 /* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
- * 'buf'.  'flow' is used as a template to determine how to interpret
- * 'mask'.  For example, the 'dl_type' of 'mask' describes the mask, but
- * it doesn't indicate whether the other fields should be interpreted as
- * ARP, IPv4, IPv6, etc.
+ * 'buf'.
  *
  * 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
- * capable of being expanded to allow for that much space.
- *
- * 'recirc' indicates support for recirculation fields. If this is true, then
- * these fields will always be serialised. */
+ * capable of being expanded to allow for that much space. */
 void
-odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
-                       const struct flow *flow, uint32_t odp_in_port_mask,
-                       size_t max_mpls_depth, bool recirc)
+odp_flow_key_from_mask(const struct odp_flow_key_parms *parms,
+                       struct ofpbuf *buf)
 {
-    odp_flow_key_from_flow__(buf, flow, mask, u32_to_odp(odp_in_port_mask),
-                             max_mpls_depth, recirc, true);
+    odp_flow_key_from_flow__(parms, true, buf);
 }
 
 /* Generate ODP flow key from the given packet metadata */
@@ -3660,7 +3595,7 @@ odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
     nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
 
     if (md->tunnel.ip_dst) {
-        tun_key_to_attr(buf, &md->tunnel);
+        tun_key_to_attr(buf, &md->tunnel, &md->tunnel, NULL);
     }
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
@@ -4224,6 +4159,7 @@ parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
 
 static enum odp_key_fitness
 odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
+                       const struct nlattr *src_key, size_t src_key_len,
                        struct flow *flow, const struct flow *src_flow)
 {
     const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
@@ -4267,7 +4203,9 @@ odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
     if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
         enum odp_key_fitness res;
 
-        res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
+        res = odp_tun_key_from_attr__(attrs[OVS_KEY_ATTR_TUNNEL], src_key,
+                                      src_key_len, &src_flow->tunnel,
+                                      &flow->tunnel);
         if (res == ODP_FIT_ERROR) {
             return ODP_FIT_ERROR;
         } else if (res == ODP_FIT_PERFECT) {
@@ -4339,18 +4277,21 @@ enum odp_key_fitness
 odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
                      struct flow *flow)
 {
-   return odp_flow_key_to_flow__(key, key_len, flow, flow);
+   return odp_flow_key_to_flow__(key, key_len, NULL, 0, flow, flow);
 }
 
-/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
- * structure in 'mask'.  'flow' must be a previously translated flow
- * corresponding to 'mask'.  Returns an ODP_FIT_* value that indicates how well
- * 'key' fits our expectations for what a flow key should contain. */
+/* Converts the 'mask_key_len' bytes of OVS_KEY_ATTR_* attributes in 'mask_key'
+ * to a mask structure in 'mask'.  'flow' must be a previously translated flow
+ * corresponding to 'mask' and similarly flow_key/flow_key_len must be the
+ * attributes from that flow.  Returns an ODP_FIT_* value that indicates how
+ * well 'key' fits our expectations for what a flow key should contain. */
 enum odp_key_fitness
-odp_flow_key_to_mask(const struct nlattr *key, size_t key_len,
+odp_flow_key_to_mask(const struct nlattr *mask_key, size_t mask_key_len,
+                     const struct nlattr *flow_key, size_t flow_key_len,
                      struct flow *mask, const struct flow *flow)
 {
-   return odp_flow_key_to_flow__(key, key_len, mask, flow);
+   return odp_flow_key_to_flow__(mask_key, mask_key_len, flow_key, flow_key_len,
+                                 mask, flow);
 }
 
 /* Returns 'fitness' as a string, for use in debug messages. */
@@ -4420,7 +4361,7 @@ odp_put_tunnel_action(const struct flow_tnl *tunnel,
                       struct ofpbuf *odp_actions)
 {
     size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
-    tun_key_to_attr(odp_actions, tunnel);
+    tun_key_to_attr(odp_actions, tunnel, tunnel, NULL);
     nl_msg_end_nested(odp_actions, offset);
 }