Extend sFlow agent to report tunnel and MPLS structures
[cascardo/ovs.git] / lib / odp-util.c
index 75f64c5..eec0bfb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
 #include "packets.h"
 #include "simap.h"
 #include "timeval.h"
+#include "tun-metadata.h"
 #include "unaligned.h"
 #include "util.h"
 #include "uuid.h"
@@ -68,6 +69,17 @@ static void format_odp_key_attr(const struct nlattr *a,
                                 const struct hmap *portno_names, struct ds *ds,
                                 bool verbose);
 
+struct geneve_scan {
+    struct geneve_opt d[63];
+    int len;
+};
+
+static int scan_geneve(const char *s, struct geneve_scan *key,
+                       struct geneve_scan *mask);
+static void format_geneve_opts(const struct geneve_opt *opt,
+                               const struct geneve_opt *mask, int opts_len,
+                               struct ds *, bool verbose);
+
 static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[],
                                                  int max, struct ofpbuf *,
                                                  const struct nlattr *key);
@@ -223,130 +235,11 @@ slow_path_reason_to_explanation(enum slow_path_reason reason)
 }
 
 static int
-parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
-            uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
+parse_odp_flags(const char *s, const char *(*bit_to_string)(uint32_t),
+                uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
 {
-    uint32_t result = 0;
-    int n;
-
-    /* Parse masked flags in numeric format? */
-    if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n",
-                             res_flags, res_mask, &n) && n > 0) {
-        if (*res_flags & ~allowed || *res_mask & ~allowed) {
-            return -EINVAL;
-        }
-        return n;
-    }
-
-    n = 0;
-
-    if (res_mask && (*s == '+' || *s == '-')) {
-        uint32_t flags = 0, mask = 0;
-
-        /* Parse masked flags. */
-        while (s[0] != ')') {
-            bool set;
-            uint32_t bit;
-            int name_len;
-
-            if (s[0] == '+') {
-                set = true;
-            } else if (s[0] == '-') {
-                set = false;
-            } else {
-                return -EINVAL;
-            }
-            s++;
-            n++;
-
-            name_len = strcspn(s, "+-)");
-
-            for (bit = 1; bit; bit <<= 1) {
-                const char *fname = bit_to_string(bit);
-                size_t len;
-
-                if (!fname) {
-                    continue;
-                }
-
-                len = strlen(fname);
-                if (len != name_len) {
-                    continue;
-                }
-                if (!strncmp(s, fname, len)) {
-                    if (mask & bit) {
-                        /* bit already set. */
-                        return -EINVAL;
-                    }
-                    if (!(bit & allowed)) {
-                        return -EINVAL;
-                    }
-                    if (set) {
-                        flags |= bit;
-                    }
-                    mask |= bit;
-                    break;
-                }
-            }
-
-            if (!bit) {
-                return -EINVAL; /* Unknown flag name */
-            }
-            s += name_len;
-            n += name_len;
-        }
-
-        *res_flags = flags;
-        *res_mask = mask;
-        return n;
-    }
-
-    /* Parse unmasked flags.  If a flag is present, it is set, otherwise
-     * it is not set. */
-    while (s[n] != ')') {
-        unsigned long long int flags;
-        uint32_t bit;
-        int n0;
-
-        if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
-            if (flags & ~allowed) {
-                return -EINVAL;
-            }
-            n += n0 + (s[n + n0] == ',');
-            result |= flags;
-            continue;
-        }
-
-        for (bit = 1; bit; bit <<= 1) {
-            const char *name = bit_to_string(bit);
-            size_t len;
-
-            if (!name) {
-                continue;
-            }
-
-            len = strlen(name);
-            if (!strncmp(s + n, name, len) &&
-                (s[n + len] == ',' || s[n + len] == ')')) {
-                if (!(bit & allowed)) {
-                    return -EINVAL;
-                }
-                result |= bit;
-                n += len + (s[n + len] == ',');
-                break;
-            }
-        }
-
-        if (!bit) {
-            return -EINVAL;
-        }
-    }
-
-    *res_flags = result;
-    if (res_mask) {
-        *res_mask = UINT32_MAX;
-    }
-    return n;
+    return parse_flags(s, bit_to_string, ')', NULL, NULL,
+                       res_flags, allowed, res_mask);
 }
 
 static void
@@ -358,6 +251,8 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
                                           .optional = true },
         [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32,
                                                  .optional = true },
+        [OVS_USERSPACE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC,
+                                                 .optional = true },
     };
     struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
     const struct nlattr *userdata_attr;
@@ -429,6 +324,10 @@ format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
         }
     }
 
+    if (a[OVS_USERSPACE_ATTR_ACTIONS]) {
+        ds_put_cstr(ds, ",actions");
+    }
+
     tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT];
     if (tunnel_out_port_attr) {
         ds_put_format(ds, ",tunnel_out_port=%"PRIu32,
@@ -580,9 +479,19 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
 
         gnh = format_udp_tnl_push_header(ds, ip);
 
-        ds_put_format(ds, "geneve(%svni=0x%"PRIx32")",
+        ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
                       gnh->oam ? "oam," : "",
+                      gnh->critical ? "crit," : "",
                       ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
+        if (gnh->opt_len) {
+            ds_put_cstr(ds, ",options(");
+            format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
+                               ds, false);
+            ds_put_char(ds, ')');
+        }
+
+        ds_put_char(ds, ')');
     } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
         const struct gre_base_hdr *greh;
         ovs_16aligned_be32 *options;
@@ -763,6 +672,7 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
     int n = -1;
     void *user_data = NULL;
     size_t user_data_size = 0;
+    bool include_actions = false;
 
     if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) {
         return -EINVAL;
@@ -801,9 +711,9 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
             cookie.slow_path.unused = 0;
             cookie.slow_path.reason = 0;
 
-            res = parse_flags(&s[n], slow_path_reason_to_string,
-                              &cookie.slow_path.reason,
-                              SLOW_PATH_REASON_MASK, NULL);
+            res = parse_odp_flags(&s[n], slow_path_reason_to_string,
+                                  &cookie.slow_path.reason,
+                                  SLOW_PATH_REASON_MASK, NULL);
             if (res < 0 || s[n + res] != ')') {
                 return res;
             }
@@ -849,14 +759,24 @@ parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
         }
     }
 
+    {
+        int n1 = -1;
+        if (ovs_scan(&s[n], ",actions%n", &n1)) {
+            n += n1;
+            include_actions = true;
+        }
+    }
+
     {
         int n1 = -1;
         if (ovs_scan(&s[n], ",tunnel_out_port=%"SCNi32")%n",
                      &tunnel_out_port, &n1)) {
-            odp_put_userspace_action(pid, user_data, user_data_size, tunnel_out_port, actions);
+            odp_put_userspace_action(pid, user_data, user_data_size,
+                                     tunnel_out_port, include_actions, actions);
             return n + n1;
         } else if (s[n] == ')') {
-            odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, actions);
+            odp_put_userspace_action(pid, user_data, user_data_size,
+                                     ODPP_NONE, include_actions, actions);
             return n + 1;
         }
     }
@@ -938,17 +858,41 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
             struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
 
             memset(gnh, 0, sizeof *gnh);
+            header_len = sizeof *eth + sizeof *ip +
+                         sizeof *udp + sizeof *gnh;
+
             if (ovs_scan_len(s, &n, "oam,")) {
                 gnh->oam = 1;
             }
-            if (!ovs_scan_len(s, &n, "vni=0x%"SCNx32"))", &vni)) {
+            if (ovs_scan_len(s, &n, "crit,")) {
+                gnh->critical = 1;
+            }
+            if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
+                return -EINVAL;
+            }
+            if (ovs_scan_len(s, &n, ",options(")) {
+                struct geneve_scan options;
+                int len;
+
+                memset(&options, 0, sizeof options);
+                len = scan_geneve(s + n, &options, NULL);
+                if (!len) {
+                    return -EINVAL;
+                }
+
+                memcpy(gnh->options, options.d, options.len);
+                gnh->opt_len = options.len / 4;
+                header_len += options.len;
+
+                n += len;
+            }
+            if (!ovs_scan_len(s, &n, "))")) {
                 return -EINVAL;
             }
+
             gnh->proto_type = htons(ETH_TYPE_TEB);
             put_16aligned_be32(&gnh->vni, htonl(vni << 8));
             tnl_type = OVS_VPORT_TYPE_GENEVE;
-            header_len = sizeof *eth + sizeof *ip +
-                         sizeof *udp + sizeof *gnh;
         } else {
             return -EINVAL;
         }
@@ -1317,45 +1261,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;
-}
-
 static enum odp_key_fitness
 odp_tun_key_from_attr__(const struct nlattr *attr,
-                        const struct nlattr *flow_attrs OVS_UNUSED,
-                        size_t flow_attr_len OVS_UNUSED,
-                        const struct flow_tnl *src_tun OVS_UNUSED,
-                        struct flow_tnl *tun)
+                        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;
@@ -1424,15 +1333,14 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
 
             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. */
@@ -1453,13 +1361,14 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
 enum odp_key_fitness
 odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
 {
+    memset(tun, 0, sizeof *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,
-                const struct flow_tnl *tun_flow_key OVS_UNUSED,
-                const struct ofpbuf *key_buf OVS_UNUSED)
+                const struct flow_tnl *tun_flow_key,
+                const struct ofpbuf *key_buf)
 {
     size_t tun_key_ofs;
 
@@ -1503,6 +1412,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);
 }
 
@@ -1773,14 +1689,13 @@ format_tun_flags(struct ds *ds, const char *name, uint16_t key,
     bool mask_empty = mask && !*mask;
 
     if (verbose || !mask_empty) {
-        bool mask_full = !mask || (*mask & FLOW_TNL_F_MASK) == FLOW_TNL_F_MASK;
-
         ds_put_cstr(ds, name);
         ds_put_char(ds, '(');
-        if (!mask_full) { /* Partially masked. */
-            format_flags_masked(ds, NULL, flow_tun_flag_to_string, key, *mask);
+        if (mask) {
+            format_flags_masked(ds, NULL, flow_tun_flag_to_string, key,
+                                *mask & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK);
         } else { /* Fully masked. */
-            format_flags(ds, flow_tun_flag_to_string, key, ',');
+            format_flags(ds, flow_tun_flag_to_string, key, '|');
         }
         ds_put_cstr(ds, "),");
     }
@@ -1901,21 +1816,10 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr,
 #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
 
 static void
-format_odp_tun_geneve(const struct nlattr *attr,
-                      const struct nlattr *mask_attr, struct ds *ds,
-                      bool verbose)
+format_geneve_opts(const struct geneve_opt *opt,
+                   const struct geneve_opt *mask, int opts_len,
+                   struct ds *ds, bool verbose)
 {
-    int opts_len = nl_attr_get_size(attr);
-    const struct geneve_opt *opt = nl_attr_get(attr);
-    const struct geneve_opt *mask = mask_attr ?
-                                    nl_attr_get(mask_attr) : NULL;
-
-    if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
-        ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
-                      nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
-        return;
-    }
-
     while (opts_len > 0) {
         unsigned int len;
         uint8_t data_len, data_len_mask;
@@ -1965,6 +1869,25 @@ format_odp_tun_geneve(const struct nlattr *attr,
     };
 }
 
+static void
+format_odp_tun_geneve(const struct nlattr *attr,
+                      const struct nlattr *mask_attr, struct ds *ds,
+                      bool verbose)
+{
+    int opts_len = nl_attr_get_size(attr);
+    const struct geneve_opt *opt = nl_attr_get(attr);
+    const struct geneve_opt *mask = mask_attr ?
+                                    nl_attr_get(mask_attr) : NULL;
+
+    if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
+        ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
+                      nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
+        return;
+    }
+
+    format_geneve_opts(opt, mask, opts_len, ds, verbose);
+}
+
 static void
 format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
                     struct ds *ds, bool verbose)
@@ -2251,10 +2174,11 @@ format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
         if (!is_exact) {
             format_flags_masked(ds, NULL, packet_tcp_flag_to_string,
                                 ntohs(nl_attr_get_be16(a)),
-                                ntohs(nl_attr_get_be16(ma)));
+                                TCP_FLAGS(nl_attr_get_be16(ma)),
+                                TCP_FLAGS(OVS_BE16_MAX));
         } else {
             format_flags(ds, packet_tcp_flag_to_string,
-                         ntohs(nl_attr_get_be16(a)), ',');
+                         ntohs(nl_attr_get_be16(a)), '|');
         }
         break;
 
@@ -2654,8 +2578,8 @@ scan_tun_flags(const char *s, uint16_t *key, uint16_t *mask)
     uint32_t flags, fmask;
     int n;
 
-    n = parse_flags(s, flow_tun_flag_to_string, &flags,
-                    FLOW_TNL_F_MASK, mask ? &fmask : NULL);
+    n = parse_odp_flags(s, flow_tun_flag_to_string, &flags,
+                        FLOW_TNL_F_MASK, mask ? &fmask : NULL);
     if (n >= 0 && s[n] == ')') {
         *key = flags;
         if (mask) {
@@ -2672,8 +2596,8 @@ scan_tcp_flags(const char *s, ovs_be16 *key, ovs_be16 *mask)
     uint32_t flags, fmask;
     int n;
 
-    n = parse_flags(s, packet_tcp_flag_to_string, &flags,
-                    TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
+    n = parse_odp_flags(s, packet_tcp_flag_to_string, &flags,
+                        TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
     if (n >= 0) {
         *key = htons(flags);
         if (mask) {
@@ -2903,11 +2827,6 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
     return 0;
 }
 
-struct geneve_scan {
-    struct geneve_opt d[63];
-    int len;
-};
-
 static int
 scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
 {
@@ -3446,7 +3365,7 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
 
     nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
 
-    if (parms->recirc) {
+    if (parms->support.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);
     }
@@ -3521,7 +3440,7 @@ odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
 
         n = flow_count_mpls_labels(flow, NULL);
         if (export_mask) {
-            n = MIN(n, parms->max_mpls_depth);
+            n = MIN(n, parms->support.max_mpls_depth);
         }
         mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
                                             n * sizeof *mpls_key);
@@ -3646,7 +3565,7 @@ odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
         1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
         1u << OVS_KEY_ATTR_IN_PORT;
 
-    *md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+    pkt_metadata_init(md, ODPP_NONE);
 
     NL_ATTR_FOR_EACH (nla, left, key, key_len) {
         uint16_t type = nl_attr_type(nla);
@@ -4349,6 +4268,7 @@ size_t
 odp_put_userspace_action(uint32_t pid,
                          const void *userdata, size_t userdata_size,
                          odp_port_t tunnel_out_port,
+                         bool include_actions,
                          struct ofpbuf *odp_actions)
 {
     size_t userdata_ofs;
@@ -4379,6 +4299,9 @@ odp_put_userspace_action(uint32_t pid,
         nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,
                             tunnel_out_port);
     }
+    if (include_actions) {
+        nl_msg_put_flag(odp_actions, OVS_USERSPACE_ATTR_ACTIONS);
+    }
     nl_msg_end_nested(odp_actions, offset);
 
     return userdata_ofs;