Implement new "learn" action.
[sliver-openvswitch.git] / lib / ofp-util.c
index bcb1b9e..00d1af7 100644 (file)
@@ -25,6 +25,7 @@
 #include "byte-order.h"
 #include "classifier.h"
 #include "dynamic-string.h"
+#include "learn.h"
 #include "multipath.h"
 #include "nx-match.h"
 #include "ofp-errors.h"
@@ -1825,10 +1826,7 @@ make_add_simple_flow(const struct cls_rule *rule,
         struct ofpbuf *buffer;
 
         buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao);
-        oao = ofpbuf_put_zeros(buffer, sizeof *oao);
-        oao->type = htons(OFPAT_OUTPUT);
-        oao->len = htons(sizeof *oao);
-        oao->port = htons(out_port);
+        ofputil_put_OFPAT_OUTPUT(buffer)->port = htons(out_port);
         return buffer;
     } else {
         return make_add_flow(rule, buffer_id, idle_timeout, 0);
@@ -2135,6 +2133,10 @@ validate_actions(const union ofp_action *actions, size_t n_actions,
                 (const struct nx_action_resubmit *) a);
             break;
 
+        case OFPUTIL_NXAST_LEARN:
+            error = learn_check((const struct nx_action_learn *) a, flow);
+            break;
+
         case OFPUTIL_OFPAT_STRIP_VLAN:
         case OFPUTIL_OFPAT_SET_NW_SRC:
         case OFPUTIL_OFPAT_SET_NW_DST:
@@ -2187,26 +2189,16 @@ ofputil_decode_ofpat_action(const union ofp_action *a)
     enum ofp_action_type type = ntohs(a->type);
 
     switch (type) {
-#define OFPAT_ACTION(ENUM, TYPE)                            \
+#define OFPAT_ACTION(ENUM, STRUCT, NAME)                    \
         case ENUM: {                                        \
             static const struct ofputil_action action = {   \
-                OFPUTIL_##ENUM, sizeof(TYPE), sizeof(TYPE)  \
+                OFPUTIL_##ENUM,                             \
+                sizeof(struct STRUCT),                      \
+                sizeof(struct STRUCT)                       \
             };                                              \
             return &action;                                 \
         }
-        OFPAT_ACTION(OFPAT_OUTPUT,       struct ofp_action_output);
-        OFPAT_ACTION(OFPAT_SET_VLAN_VID, struct ofp_action_vlan_vid);
-        OFPAT_ACTION(OFPAT_SET_VLAN_PCP, struct ofp_action_vlan_pcp);
-        OFPAT_ACTION(OFPAT_STRIP_VLAN,   struct ofp_action_header);
-        OFPAT_ACTION(OFPAT_SET_DL_SRC,   struct ofp_action_dl_addr);
-        OFPAT_ACTION(OFPAT_SET_DL_DST,   struct ofp_action_dl_addr);
-        OFPAT_ACTION(OFPAT_SET_NW_SRC,   struct ofp_action_nw_addr);
-        OFPAT_ACTION(OFPAT_SET_NW_DST,   struct ofp_action_nw_addr);
-        OFPAT_ACTION(OFPAT_SET_NW_TOS,   struct ofp_action_nw_tos);
-        OFPAT_ACTION(OFPAT_SET_TP_SRC,   struct ofp_action_tp_port);
-        OFPAT_ACTION(OFPAT_SET_TP_DST,   struct ofp_action_tp_port);
-        OFPAT_ACTION(OFPAT_ENQUEUE,      struct ofp_action_enqueue);
-#undef OFPAT_ACTION
+#include "ofp-util.def"
 
     case OFPAT_VENDOR:
     default:
@@ -2221,30 +2213,16 @@ ofputil_decode_nxast_action(const union ofp_action *a)
     enum nx_action_subtype subtype = ntohs(nah->subtype);
 
     switch (subtype) {
-#define NXAST_ACTION(ENUM, TYPE, EXTENSIBLE)                \
-        case ENUM: {                                        \
-            static const struct ofputil_action action = {   \
-                OFPUTIL_##ENUM,                             \
-                sizeof(TYPE),                               \
-                EXTENSIBLE ? UINT_MAX : sizeof(TYPE)        \
-            };                                              \
-            return &action;                                 \
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
+        case ENUM: {                                            \
+            static const struct ofputil_action action = {       \
+                OFPUTIL_##ENUM,                                 \
+                sizeof(struct STRUCT),                          \
+                EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT)   \
+            };                                                  \
+            return &action;                                     \
         }
-        NXAST_ACTION(NXAST_RESUBMIT,     struct nx_action_resubmit,     false);
-        NXAST_ACTION(NXAST_SET_TUNNEL,   struct nx_action_set_tunnel,   false);
-        NXAST_ACTION(NXAST_SET_QUEUE,    struct nx_action_set_queue,    false);
-        NXAST_ACTION(NXAST_POP_QUEUE,    struct nx_action_pop_queue,    false);
-        NXAST_ACTION(NXAST_REG_MOVE,     struct nx_action_reg_move,     false);
-        NXAST_ACTION(NXAST_REG_LOAD,     struct nx_action_reg_load,     false);
-        NXAST_ACTION(NXAST_NOTE,         struct nx_action_note,         true);
-        NXAST_ACTION(NXAST_SET_TUNNEL64, struct nx_action_set_tunnel64, false);
-        NXAST_ACTION(NXAST_MULTIPATH,    struct nx_action_multipath,    false);
-        NXAST_ACTION(NXAST_AUTOPATH,     struct nx_action_autopath,     false);
-        NXAST_ACTION(NXAST_BUNDLE,       struct nx_action_bundle,       true);
-        NXAST_ACTION(NXAST_BUNDLE_LOAD,  struct nx_action_bundle,       true);
-        NXAST_ACTION(NXAST_RESUBMIT_TABLE, struct nx_action_resubmit,   false);
-        NXAST_ACTION(NXAST_OUTPUT_REG,   struct nx_action_output_reg,   false);
-#undef NXAST_ACTION
+#include "ofp-util.def"
 
     case NXAST_SNAT__OBSOLETE:
     case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
@@ -2308,6 +2286,84 @@ ofputil_decode_action_unsafe(const union ofp_action *a)
     return action->code;
 }
 
+/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
+ * 'name' is "output" then the return value is OFPUTIL_OFPAT_OUTPUT), or -1 if
+ * 'name' is not the name of any action.
+ *
+ * ofp-util.def lists the mapping from names to action. */
+int
+ofputil_action_code_from_name(const char *name)
+{
+    static const char *names[OFPUTIL_N_ACTIONS] = {
+#define OFPAT_ACTION(ENUM, STRUCT, NAME)             NAME,
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
+#include "ofp-util.def"
+    };
+
+    const char **p;
+
+    for (p = names; p < &names[ARRAY_SIZE(names)]; p++) {
+        if (*p && !strcasecmp(name, *p)) {
+            return p - names;
+        }
+    }
+    return -1;
+}
+
+/* Appends an action of the type specified by 'code' to 'buf' and returns the
+ * action.  Initializes the parts of 'action' that identify it as having type
+ * <ENUM> and length 'sizeof *action' and zeros the rest.  For actions that
+ * have variable length, the length used and cleared is that of struct
+ * <STRUCT>.  */
+void *
+ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
+{
+    switch (code) {
+#define OFPAT_ACTION(ENUM, STRUCT, NAME)                    \
+    case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)        \
+    case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
+#include "ofp-util.def"
+    }
+    NOT_REACHED();
+}
+
+#define OFPAT_ACTION(ENUM, STRUCT, NAME)                        \
+    void                                                        \
+    ofputil_init_##ENUM(struct STRUCT *s)                       \
+    {                                                           \
+        memset(s, 0, sizeof *s);                                \
+        s->type = htons(ENUM);                                  \
+        s->len = htons(sizeof *s);                              \
+    }                                                           \
+                                                                \
+    struct STRUCT *                                             \
+    ofputil_put_##ENUM(struct ofpbuf *buf)                      \
+    {                                                           \
+        struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s);   \
+        ofputil_init_##ENUM(s);                                 \
+        return s;                                               \
+    }
+#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME)            \
+    void                                                        \
+    ofputil_init_##ENUM(struct STRUCT *s)                       \
+    {                                                           \
+        memset(s, 0, sizeof *s);                                \
+        s->type = htons(OFPAT_VENDOR);                          \
+        s->len = htons(sizeof *s);                              \
+        s->vendor = htonl(NX_VENDOR_ID);                        \
+        s->subtype = htons(ENUM);                               \
+    }                                                           \
+                                                                \
+    struct STRUCT *                                             \
+    ofputil_put_##ENUM(struct ofpbuf *buf)                      \
+    {                                                           \
+        struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s);   \
+        ofputil_init_##ENUM(s);                                 \
+        return s;                                               \
+    }
+#include "ofp-util.def"
+
 /* Returns true if 'action' outputs to 'port', false otherwise. */
 bool
 action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
@@ -2684,3 +2740,72 @@ ofputil_actions_clone(const union ofp_action *actions, size_t n)
 {
     return n ? xmemdup(actions, n * sizeof *actions) : NULL;
 }
+
+/* Parses a key or a key-value pair from '*stringp'.
+ *
+ * On success: Stores the key into '*keyp'.  Stores the value, if present, into
+ * '*valuep', otherwise an empty string.  Advances '*stringp' past the end of
+ * the key-value pair, preparing it for another call.  '*keyp' and '*valuep'
+ * are substrings of '*stringp' created by replacing some of its bytes by null
+ * terminators.  Returns true.
+ *
+ * If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to
+ * NULL and returns false. */
+bool
+ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
+{
+    char *pos, *key, *value;
+    size_t key_len;
+
+    pos = *stringp;
+    pos += strspn(pos, ", \t\r\n");
+    if (*pos == '\0') {
+        *keyp = *valuep = NULL;
+        return false;
+    }
+
+    key = pos;
+    key_len = strcspn(pos, ":=(, \t\r\n");
+    if (key[key_len] == ':' || key[key_len] == '=') {
+        /* The value can be separated by a colon. */
+        size_t value_len;
+
+        value = key + key_len + 1;
+        value_len = strcspn(value, ", \t\r\n");
+        pos = value + value_len + (value[value_len] != '\0');
+        value[value_len] = '\0';
+    } else if (key[key_len] == '(') {
+        /* The value can be surrounded by balanced parentheses.  The outermost
+         * set of parentheses is removed. */
+        int level = 1;
+        size_t value_len;
+
+        value = key + key_len + 1;
+        for (value_len = 0; level > 0; value_len++) {
+            switch (value[value_len]) {
+            case '\0':
+                ovs_fatal(0, "unbalanced parentheses in argument to %s", key);
+
+            case '(':
+                level++;
+                break;
+
+            case ')':
+                level--;
+                break;
+            }
+        }
+        value[value_len - 1] = '\0';
+        pos = value + value_len;
+    } else {
+        /* There might be no value at all. */
+        value = key + key_len;  /* Will become the empty string below. */
+        pos = key + key_len + (key[key_len] != '\0');
+    }
+    key[key_len] = '\0';
+
+    *stringp = pos;
+    *keyp = key;
+    *valuep = value;
+    return true;
+}