datapath: More flexible kernel/userspace tunneling attribute.
[cascardo/ovs.git] / lib / odp-util.c
index 2a23f1f..c4f2be8 100644 (file)
@@ -95,7 +95,7 @@ ovs_key_attr_to_string(enum ovs_key_attr attr)
     case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
     case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
     case OVS_KEY_ATTR_TUN_ID: return "tun_id";
-    case OVS_KEY_ATTR_IPV4_TUNNEL: return "ipv4_tunnel";
+    case OVS_KEY_ATTR_TUNNEL: return "tunnel";
     case OVS_KEY_ATTR_IN_PORT: return "in_port";
     case OVS_KEY_ATTR_ETHERNET: return "eth";
     case OVS_KEY_ATTR_VLAN: return "vlan";
@@ -617,7 +617,7 @@ odp_flow_key_attr_len(uint16_t type)
     case OVS_KEY_ATTR_PRIORITY: return 4;
     case OVS_KEY_ATTR_SKB_MARK: return 4;
     case OVS_KEY_ATTR_TUN_ID: return 8;
-    case OVS_KEY_ATTR_IPV4_TUNNEL: return sizeof(struct ovs_key_ipv4_tunnel);
+    case OVS_KEY_ATTR_TUNNEL: return -2;
     case OVS_KEY_ATTR_IN_PORT: return 4;
     case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
     case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
@@ -672,19 +672,109 @@ ovs_frag_type_to_string(enum ovs_frag_type type)
     }
 }
 
-static const char *
-odp_tun_flag_to_string(uint32_t flags)
+static int
+tunnel_key_attr_len(int type)
 {
-    switch (flags) {
-    case OVS_TNL_F_DONT_FRAGMENT:
-        return "df";
-    case OVS_TNL_F_CSUM:
-        return "csum";
-    case OVS_TNL_F_KEY:
-        return "key";
-    default:
-        return NULL;
+    switch (type) {
+    case OVS_TUNNEL_KEY_ATTR_ID: return 8;
+    case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: return 4;
+    case OVS_TUNNEL_KEY_ATTR_IPV4_DST: return 4;
+    case OVS_TUNNEL_KEY_ATTR_TOS: return 1;
+    case OVS_TUNNEL_KEY_ATTR_TTL: return 1;
+    case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: return 0;
+    case OVS_TUNNEL_KEY_ATTR_CSUM: return 0;
+    case __OVS_TUNNEL_KEY_ATTR_MAX:
+        return -1;
     }
+    return -1;
+}
+
+static enum odp_key_fitness
+tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
+{
+    unsigned int left;
+    const struct nlattr *a;
+    bool ttl = false;
+    bool unknown = false;
+
+    NL_NESTED_FOR_EACH(a, left, attr) {
+        uint16_t type = nl_attr_type(a);
+        size_t len = nl_attr_get_size(a);
+        int expected_len = tunnel_key_attr_len(type);
+
+        if (len != expected_len && expected_len >= 0) {
+            return ODP_FIT_ERROR;
+        }
+
+        switch (type) {
+        case OVS_TUNNEL_KEY_ATTR_ID:
+            tun->tun_id = nl_attr_get_be64(a);
+            tun->flags |= FLOW_TNL_F_KEY;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
+            tun->ip_src = nl_attr_get_be32(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
+            tun->ip_dst = nl_attr_get_be32(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_TOS:
+            tun->ip_tos = nl_attr_get_u8(a);
+            break;
+        case OVS_TUNNEL_KEY_ATTR_TTL:
+            tun->ip_ttl = nl_attr_get_u8(a);
+            ttl = true;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
+            tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
+            break;
+        case OVS_TUNNEL_KEY_ATTR_CSUM:
+            tun->flags |= FLOW_TNL_F_CSUM;
+            break;
+        default:
+            /* Allow this to show up as unexpected, if there are unknown
+             * tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
+            unknown = true;
+            break;
+        }
+    }
+
+    if (!ttl) {
+        return ODP_FIT_ERROR;
+    }
+    if (unknown) {
+            return ODP_FIT_TOO_MUCH;
+    }
+    return ODP_FIT_PERFECT;
+}
+
+static void
+tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
+{
+    size_t tun_key_ofs;
+
+    tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
+
+    if (tun_key->flags & FLOW_TNL_F_KEY) {
+        nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
+    }
+    if (tun_key->ip_src) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src);
+    }
+    if (tun_key->ip_dst) {
+        nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
+    }
+    if (tun_key->ip_tos) {
+        nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
+    }
+    nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl);
+    if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
+        nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
+    }
+    if (tun_key->flags & FLOW_TNL_F_CSUM) {
+        nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
+    }
+
+    nl_msg_end_nested(a, tun_key_ofs);
 }
 
 static void
@@ -699,7 +789,7 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
     const struct ovs_key_icmpv6 *icmpv6_key;
     const struct ovs_key_arp *arp_key;
     const struct ovs_key_nd *nd_key;
-    const struct ovs_key_ipv4_tunnel *ipv4_tun_key;
+    struct flow_tnl tun_key;
     enum ovs_key_attr attr = nl_attr_type(a);
     int expected_len;
 
@@ -734,18 +824,23 @@ format_odp_key_attr(const struct nlattr *a, struct ds *ds)
         ds_put_format(ds, "(%#"PRIx64")", ntohll(nl_attr_get_be64(a)));
         break;
 
-    case OVS_KEY_ATTR_IPV4_TUNNEL:
-        ipv4_tun_key = nl_attr_get(a);
-        ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
-                      "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
-                      ntohll(ipv4_tun_key->tun_id),
-                      IP_ARGS(&ipv4_tun_key->ipv4_src),
-                      IP_ARGS(&ipv4_tun_key->ipv4_dst),
-                      ipv4_tun_key->ipv4_tos, ipv4_tun_key->ipv4_ttl);
-
-        format_flags(ds, odp_tun_flag_to_string,
-                     ipv4_tun_key->tun_flags, ',');
-        ds_put_format(ds, "))");
+    case OVS_KEY_ATTR_TUNNEL:
+        memset(&tun_key, 0, sizeof tun_key);
+        if (tun_key_from_attr(a, &tun_key) == ODP_FIT_ERROR) {
+            ds_put_format(ds, "(error)");
+        } else {
+            ds_put_format(ds, "(tun_id=0x%"PRIx64",src="IP_FMT",dst="IP_FMT","
+                          "tos=0x%"PRIx8",ttl=%"PRIu8",flags(",
+                          ntohll(tun_key.tun_id),
+                          IP_ARGS(&tun_key.ip_src),
+                          IP_ARGS(&tun_key.ip_dst),
+                          tun_key.ip_tos, tun_key.ip_ttl);
+
+            format_flags(ds, flow_tun_flag_to_string,
+                         (uint32_t) tun_key.flags, ',');
+            ds_put_format(ds, "))");
+        }
+
         break;
 
     case OVS_KEY_ATTR_IN_PORT:
@@ -974,23 +1069,24 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
     {
         char tun_id_s[32];
         int tos, ttl;
-        struct ovs_key_ipv4_tunnel tun_key;
+        struct flow_tnl tun_key;
         int n = -1;
 
-        if (sscanf(s, "ipv4_tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
+        if (sscanf(s, "tunnel(tun_id=%31[x0123456789abcdefABCDEF],"
                    "src="IP_SCAN_FMT",dst="IP_SCAN_FMT
                    ",tos=%i,ttl=%i,flags%n", tun_id_s,
-                    IP_SCAN_ARGS(&tun_key.ipv4_src),
-                    IP_SCAN_ARGS(&tun_key.ipv4_dst), &tos, &ttl,
+                    IP_SCAN_ARGS(&tun_key.ip_src),
+                    IP_SCAN_ARGS(&tun_key.ip_dst), &tos, &ttl,
                     &n) > 0 && n > 0) {
             int res;
+            uint32_t flags;
 
             tun_key.tun_id = htonll(strtoull(tun_id_s, NULL, 0));
-            tun_key.ipv4_tos = tos;
-            tun_key.ipv4_ttl = ttl;
+            tun_key.ip_tos = tos;
+            tun_key.ip_ttl = ttl;
+            res = parse_flags(&s[n], flow_tun_flag_to_string, &flags);
+            tun_key.flags = (uint16_t) flags;
 
-            res = parse_flags(&s[n], odp_tun_flag_to_string,
-                              &tun_key.tun_flags);
             if (res < 0) {
                 return res;
             }
@@ -999,10 +1095,7 @@ parse_odp_key_attr(const char *s, const struct simap *port_names,
                 return -EINVAL;
             }
             n++;
-
-            memset(&tun_key.pad, 0, sizeof tun_key.pad);
-            nl_msg_put_unspec(key, OVS_KEY_ATTR_IPV4_TUNNEL, &tun_key,
-                              sizeof tun_key);
+            tun_key_to_attr(key, &tun_key);
             return n;
         }
     }