]> git.proxmox.com Git - mirror_ovs.git/blobdiff - lib/ofp-util.c
other-config: Add tc-policy switch to control tc flower flag
[mirror_ovs.git] / lib / ofp-util.c
index 62cc3228cdc1166c4230cba96a8a4177d384fe70..da171cdf6fe5f675015123dda4054721deee0bff 100644 (file)
@@ -33,6 +33,7 @@
 #include "id-pool.h"
 #include "openflow/netronome-ext.h"
 #include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
 #include "openvswitch/meta-flow.h"
 #include "openvswitch/ofp-actions.h"
 #include "openvswitch/ofp-errors.h"
@@ -158,6 +159,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch,
     /* Initialize match->wc. */
     memset(&match->flow, 0, sizeof match->flow);
     ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
+    memset(&match->tun_md, 0, sizeof match->tun_md);
 
     /* Initialize most of match->flow. */
     match->flow.nw_src = ofmatch->nw_src;
@@ -4199,6 +4201,7 @@ ofputil_packet_in_private_destroy(struct ofputil_packet_in_private *pin)
 enum ofperr
 ofputil_decode_packet_out(struct ofputil_packet_out *po,
                           const struct ofp_header *oh,
+                          const struct tun_table *tun_table,
                           struct ofpbuf *ofpacts)
 {
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
@@ -4211,7 +4214,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po,
         const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
 
         po->buffer_id = ntohl(opo->buffer_id);
-        error = oxm_pull_match_loose(&b, true, NULL, &po->flow_metadata);
+        error = oxm_pull_match_loose(&b, true, tun_table, &po->flow_metadata);
         if (error) {
             return error;
         }
@@ -5179,20 +5182,22 @@ ofputil_decode_port_mod(const struct ofp_header *oh,
 
     struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
     enum ofpraw raw = ofpraw_pull_assert(&b);
+
+    enum ofperr error;
     if (raw == OFPRAW_OFPT10_PORT_MOD) {
-        return ofputil_decode_ofp10_port_mod(b.data, pm);
+        error = ofputil_decode_ofp10_port_mod(b.data, pm);
     } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
-        return ofputil_decode_ofp11_port_mod(b.data, pm);
+        error = ofputil_decode_ofp11_port_mod(b.data, pm);
     } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
-        return ofputil_decode_ofp14_port_mod(&b, loose, pm);
+        error = ofputil_decode_ofp14_port_mod(&b, loose, pm);
     } else if (raw == OFPRAW_OFPT16_PORT_MOD) {
-        return ofputil_decode_ofp16_port_mod(&b, loose, pm);
+        error = ofputil_decode_ofp16_port_mod(&b, loose, pm);
     } else {
-        return OFPERR_OFPBRC_BAD_TYPE;
+        error = OFPERR_OFPBRC_BAD_TYPE;
     }
 
     pm->config &= pm->mask;
-    return 0;
+    return error;
 }
 
 /* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
@@ -7269,7 +7274,8 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
 
 /* Stores the port number represented by 's' into '*portp'.  's' may be an
  * integer or, for reserved ports, the standard OpenFlow name for the port
- * (e.g. "LOCAL").
+ * (e.g. "LOCAL").  If 'port_map' is nonnull, also accepts names in it (quoted
+ * or unquoted).
  *
  * Returns true if successful, false if 's' is not a valid OpenFlow port number
  * or name.  The caller should issue an error message in this case, because
@@ -7281,7 +7287,9 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port)
  * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit
  * range as described in include/openflow/openflow-1.1.h. */
 bool
-ofputil_port_from_string(const char *s, ofp_port_t *portp)
+ofputil_port_from_string(const char *s,
+                         const struct ofputil_port_map *port_map,
+                         ofp_port_t *portp)
 {
     unsigned int port32; /* int is at least 32 bits wide. */
 
@@ -7300,7 +7308,8 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
         } else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
             char name[OFP10_MAX_PORT_NAME_LEN];
 
-            ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
+            ofputil_port_to_string(u16_to_ofp(port32), NULL,
+                                   name, sizeof name);
             VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
                            "for compatibility with OpenFlow 1.1 and later",
                            name, port32);
@@ -7333,20 +7342,89 @@ ofputil_port_from_string(const char *s, ofp_port_t *portp)
                 return true;
             }
         }
+
+        ofp_port_t ofp_port = OFPP_NONE;
+        if (s[0] != '"') {
+            ofp_port = ofputil_port_map_get_number(port_map, s);
+        } else {
+            size_t length = strlen(s);
+            char *name = NULL;
+            if (length > 1
+                && s[length - 1] == '"'
+                && json_string_unescape(s + 1, length - 2, &name)) {
+                ofp_port = ofputil_port_map_get_number(port_map, name);
+            }
+            free(name);
+        }
+        if (ofp_port != OFPP_NONE) {
+            *portp = ofp_port;
+            return true;
+        }
+
         return false;
     }
 }
 
+const char *
+ofputil_port_get_reserved_name(ofp_port_t port)
+{
+    switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: return #NAME;
+        OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+    default:
+        return NULL;
+    }
+}
+
+/* A port name doesn't need to be quoted if it is alphanumeric and starts with
+ * a letter. */
+static bool
+port_name_needs_quotes(const char *port_name)
+{
+    if (!isalpha((unsigned char) port_name[0])) {
+        return true;
+    }
+
+    for (const char *p = port_name + 1; *p; p++) {
+        if (!isalnum((unsigned char) *p)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+static void
+put_port_name(const char *port_name, struct ds *s)
+{
+    if (port_name_needs_quotes(port_name)) {
+        json_string_escape(port_name, s);
+    } else {
+        ds_put_cstr(s, port_name);
+    }
+}
+
 /* Appends to 's' a string representation of the OpenFlow port number 'port'.
  * Most ports' string representation is just the port number, but for special
  * ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
 void
-ofputil_format_port(ofp_port_t port, struct ds *s)
+ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *port_map,
+                    struct ds *s)
 {
-    char name[OFP10_MAX_PORT_NAME_LEN];
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ds_put_cstr(s, reserved_name);
+        return;
+    }
 
-    ofputil_port_to_string(port, name, sizeof name);
-    ds_put_cstr(s, name);
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        put_port_name(port_name, s);
+        return;
+    }
+
+    ds_put_format(s, "%"PRIu32, port);
 }
 
 /* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
@@ -7355,22 +7433,149 @@ ofputil_format_port(ofp_port_t port, struct ds *s)
  * by name, e.g. "LOCAL". */
 void
 ofputil_port_to_string(ofp_port_t port,
-                       char namebuf[OFP10_MAX_PORT_NAME_LEN], size_t bufsize)
+                       const struct ofputil_port_map *port_map,
+                       char *namebuf, size_t bufsize)
 {
-    switch (port) {
-#define OFPUTIL_NAMED_PORT(NAME)                        \
-        case OFPP_##NAME:                               \
-            ovs_strlcpy(namebuf, #NAME, bufsize);       \
-            break;
-        OFPUTIL_NAMED_PORTS
-#undef OFPUTIL_NAMED_PORT
+    const char *reserved_name = ofputil_port_get_reserved_name(port);
+    if (reserved_name) {
+        ovs_strlcpy(namebuf, reserved_name, bufsize);
+        return;
+    }
 
-    default:
-        snprintf(namebuf, bufsize, "%"PRIu32, port);
-        break;
+    const char *port_name = ofputil_port_map_get_name(port_map, port);
+    if (port_name) {
+        struct ds s = DS_EMPTY_INITIALIZER;
+        put_port_name(port_name, &s);
+        ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
+        ds_destroy(&s);
+        return;
     }
+
+    snprintf(namebuf, bufsize, "%"PRIu32, port);
+}
+\f
+/* ofputil_port_map.  */
+struct ofputil_port_map_node {
+    struct hmap_node name_node;
+    struct hmap_node number_node;
+    ofp_port_t ofp_port;        /* Port number. */
+    char *name;                 /* Port name. */
+
+    /* OpenFlow doesn't require port names to be unique, although that's the
+     * only sensible way.  However, even in Open vSwitch it's possible for two
+     * ports to appear to have the same name if their names are longer than the
+     * maximum length supported by a given version of OpenFlow.  So, we guard
+     * against duplicate names to avoid giving unexpected results in this
+     * corner case.
+     *
+     * OpenFlow does require port numbers to be unique.  We check for duplicate
+     * ports numbers just in case a switch has a bug. */
+    bool duplicate;
+};
+
+void
+ofputil_port_map_init(struct ofputil_port_map *map)
+{
+    hmap_init(&map->by_name);
+    hmap_init(&map->by_number);
 }
 
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_name(const struct ofputil_port_map *map,
+                              const char *name)
+{
+    struct ofputil_port_map_node *node;
+
+    HMAP_FOR_EACH_WITH_HASH (node, name_node, hash_string(name, 0),
+                             &map->by_name) {
+        if (!strcmp(name, node->name)) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_number(const struct ofputil_port_map *map,
+                                ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node;
+
+    HMAP_FOR_EACH_IN_BUCKET (node, number_node, hash_ofp_port(ofp_port),
+                             &map->by_number) {
+        if (node->ofp_port == ofp_port) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+void
+ofputil_port_map_put(struct ofputil_port_map *map,
+                     ofp_port_t ofp_port, const char *name)
+{
+    struct ofputil_port_map_node *node;
+
+    /* Look for duplicate name. */
+    node = ofputil_port_map_find_by_name(map, name);
+    if (node) {
+        if (node->ofp_port != ofp_port) {
+            node->duplicate = true;
+        }
+        return;
+    }
+
+    /* Look for duplicate number. */
+    node = ofputil_port_map_find_by_number(map, ofp_port);
+    if (node) {
+        node->duplicate = true;
+        return;
+    }
+
+    /* Add new node. */
+    node = xmalloc(sizeof *node);
+    hmap_insert(&map->by_number, &node->number_node, hash_ofp_port(ofp_port));
+    hmap_insert(&map->by_name, &node->name_node, hash_string(name, 0));
+    node->ofp_port = ofp_port;
+    node->name = xstrdup(name);
+    node->duplicate = false;
+}
+
+const char *
+ofputil_port_map_get_name(const struct ofputil_port_map *map,
+                          ofp_port_t ofp_port)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_number(map, ofp_port) : NULL;
+    return node && !node->duplicate ? node->name : NULL;
+}
+
+ofp_port_t
+ofputil_port_map_get_number(const struct ofputil_port_map *map,
+                            const char *name)
+{
+    struct ofputil_port_map_node *node
+        = map ? ofputil_port_map_find_by_name(map, name) : NULL;
+    return node && !node->duplicate ? node->ofp_port : OFPP_NONE;
+}
+
+void
+ofputil_port_map_destroy(struct ofputil_port_map *map)
+{
+    if (map) {
+        struct ofputil_port_map_node *node, *next;
+
+        HMAP_FOR_EACH_SAFE (node, next, name_node, &map->by_name) {
+            hmap_remove(&map->by_name, &node->name_node);
+            hmap_remove(&map->by_number, &node->number_node);
+            free(node->name);
+            free(node);
+        }
+        hmap_destroy(&map->by_name);
+        hmap_destroy(&map->by_number);
+    }
+}
+\f
 /* Stores the group id represented by 's' into '*group_idp'.  's' may be an
  * integer or, for reserved group IDs, the standard OpenFlow name for the group
  * (either "ANY" or "ALL").
@@ -7546,13 +7751,15 @@ ofputil_normalize_match__(struct match *match, bool may_log)
     /* Log any changes. */
     if (!flow_wildcards_equal(&wc, &match->wc)) {
         bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl);
-        char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL;
+        char *pre = (log
+                     ? match_to_string(match, NULL, OFP_DEFAULT_PRIORITY)
+                     : NULL);
 
         match->wc = wc;
         match_zero_wildcarded_fields(match);
 
         if (log) {
-            char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+            char *post = match_to_string(match, NULL, OFP_DEFAULT_PRIORITY);
             VLOG_INFO("normalization changed ofp_match, details:");
             VLOG_INFO(" pre: %s", pre);
             VLOG_INFO("post: %s", post);
@@ -10751,8 +10958,6 @@ struct ofpbuf *
 ofputil_encode_get_async_reply(const struct ofp_header *oh,
                                const struct ofputil_async_cfg *ac)
 {
-    struct ofpbuf *buf;
-
     enum ofpraw raw = (oh->version < OFP14_VERSION
                        ? OFPRAW_OFPT13_GET_ASYNC_REPLY
                        : OFPRAW_OFPT14_GET_ASYNC_REPLY);
@@ -10761,8 +10966,6 @@ ofputil_encode_get_async_reply(const struct ofp_header *oh,
                                raw == OFPRAW_OFPT14_GET_ASYNC_REPLY,
                                oh->version, UINT32_MAX);
     return reply;
-
-    return buf;
 }
 
 /* Encodes and returns a message, in a format appropriate for OpenFlow version