Implement OpenFlow 1.4+ OFPTC_EVICTION.
[cascardo/ovs.git] / lib / ofp-parse.c
index 8fce546..9e88d6d 100644 (file)
@@ -258,6 +258,29 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
 
     *usable_protocols = OFPUTIL_P_ANY;
 
+    if (command == -2) {
+        size_t len;
+
+        string += strspn(string, " \t\r\n");   /* Skip white space. */
+        len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
+
+        if (!strncmp(string, "add", len)) {
+            command = OFPFC_ADD;
+        } else if (!strncmp(string, "delete", len)) {
+            command = OFPFC_DELETE;
+        } else if (!strncmp(string, "delete_strict", len)) {
+            command = OFPFC_DELETE_STRICT;
+        } else if (!strncmp(string, "modify", len)) {
+            command = OFPFC_MODIFY;
+        } else if (!strncmp(string, "modify_strict", len)) {
+            command = OFPFC_MODIFY_STRICT;
+        } else {
+            len = 0;
+            command = OFPFC_ADD;
+        }
+        string += len;
+    }
+
     switch (command) {
     case -1:
         fields = F_OUT_PORT;
@@ -353,7 +376,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
                 if (fm->table_id != 0xff) {
                     *usable_protocols &= OFPUTIL_P_TID;
                 }
-            } else if (!strcmp(name, "out_port")) {
+            } else if (fields & F_OUT_PORT && !strcmp(name, "out_port")) {
                 if (!ofputil_port_from_string(value, &fm->out_port)) {
                     error = xasprintf("%s is not a valid OpenFlow port",
                                       value);
@@ -486,6 +509,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
  * constant for 'command'.  To parse syntax for an OFPST_FLOW or
  * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'.
  *
+ * If 'command' is given as -2, 'str_' may begin with a command name ("add",
+ * "modify", "delete", "modify_strict", or "delete_strict").  A missing command
+ * name is treated as "add".
+ *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
@@ -818,14 +845,19 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
 /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
  * (one of OFPFC_*) into 'fm'.
  *
+ * If 'command' is given as -2, 'string' may begin with a command name ("add",
+ * "modify", "delete", "modify_strict", or "delete_strict").  A missing command
+ * name is treated as "add".
+ *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
-                       uint16_t command,
+                       int command,
                        enum ofputil_protocol *usable_protocols)
 {
     char *error = parse_ofp_str(fm, command, string, usable_protocols);
+
     if (!error) {
         /* Normalize a copy of the match.  This ensures that non-normalized
          * flows get logged but doesn't affect what gets sent to the switch, so
@@ -837,20 +869,20 @@ parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
     return error;
 }
 
-/* Convert 'table_id' and 'flow_miss_handling' (as described for the
- * "mod-table" command in the ovs-ofctl man page) into 'tm' for sending the
- * specified table_mod 'command' to a switch.
+/* Convert 'table_id' and 'setting' (as described for the "mod-table" command
+ * in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a
+ * switch.
+ *
+ * Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
+ * '*usable_versions'.
  *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
 parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
-                    const char *flow_miss_handling,
-                    enum ofputil_protocol *usable_protocols)
+                    const char *setting, uint32_t *usable_versions)
 {
-    /* Table mod requires at least OF 1.1. */
-    *usable_protocols = OFPUTIL_P_OF11_UP;
-
+    *usable_versions = 0;
     if (!strcasecmp(table_id, "all")) {
         tm->table_id = OFPTT_ALL;
     } else {
@@ -860,18 +892,38 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
         }
     }
 
-    if (strcmp(flow_miss_handling, "controller") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_CONTROLLER;
-    } else if (strcmp(flow_miss_handling, "continue") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_CONTINUE;
-    } else if (strcmp(flow_miss_handling, "drop") == 0) {
-        tm->miss_config = OFPUTIL_TABLE_MISS_DROP;
+    tm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
+    tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
+    tm->eviction_flags = UINT32_MAX;
+
+    /* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod.
+     * Only OpenFlow 1.4+ can configure eviction via table_mod.
+     *
+     * (OpenFlow 1.4+ can also configure vacancy events via table_mod, but OVS
+     * doesn't support those yet and they're also logically a per-OpenFlow
+     * session setting so it wouldn't make sense to support them here anyway.)
+     */
+    if (!strcmp(setting, "controller")) {
+        tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "continue")) {
+        tm->miss = OFPUTIL_TABLE_MISS_CONTINUE;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "drop")) {
+        tm->miss = OFPUTIL_TABLE_MISS_DROP;
+        *usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
+    } else if (!strcmp(setting, "evict")) {
+        tm->eviction = OFPUTIL_TABLE_EVICTION_ON;
+        *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
+    } else if (!strcmp(setting, "noevict")) {
+        tm->eviction = OFPUTIL_TABLE_EVICTION_OFF;
+        *usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
     } else {
-        return xasprintf("invalid flow_miss_handling %s", flow_miss_handling);
+        return xasprintf("invalid table_mod setting %s", setting);
     }
 
     if (tm->table_id == 0xfe
-        && tm->miss_config == OFPUTIL_TABLE_MISS_CONTINUE) {
+        && tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) {
         return xstrdup("last table's flow miss handling can not be continue");
     }
 
@@ -883,10 +935,14 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
  * type (one of OFPFC_*).  Stores each flow_mod in '*fm', an array allocated
  * on the caller's behalf, and the number of flow_mods in '*n_fms'.
  *
+ * If 'command' is given as -2, each line may start with a command name
+ * ("add", "modify", "delete", "modify_strict", or "delete_strict").  A missing
+ * command name is treated as "add".
+ *
  * Returns NULL if successful, otherwise a malloc()'d string describing the
  * error.  The caller is responsible for freeing the returned string. */
 char * OVS_WARN_UNUSED_RESULT
-parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
+parse_ofp_flow_mod_file(const char *file_name, int command,
                         struct ofputil_flow_mod **fms, size_t *n_fms,
                         enum ofputil_protocol *usable_protocols)
 {
@@ -1333,7 +1389,7 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
             } else if (!strcmp(value, "last")) {
                 gm->command_bucket_id = OFPG15_BUCKET_LAST;
             } else {
-                char *error = str_to_u32(value, &gm->command_bucket_id);
+                error = str_to_u32(value, &gm->command_bucket_id);
                 if (error) {
                     goto out;
                 }
@@ -1356,7 +1412,7 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
             if(!strcmp(value, "all")) {
                 gm->group_id = OFPG_ALL;
             } else {
-                char *error = str_to_u32(value, &gm->group_id);
+                error = str_to_u32(value, &gm->group_id);
                 if (error) {
                     goto out;
                 }
@@ -1409,6 +1465,9 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
                 goto out;
             }
             error = str_to_u64(value, &gm->props.selection_method_param);
+            if (error) {
+                goto out;
+            }
             *usable_protocols &= OFPUTIL_P_OF15_UP;
         } else if (!strcmp(name, "fields")) {
             if (!(fields & F_GROUP_TYPE)) {
@@ -1545,3 +1604,36 @@ parse_ofp_group_mod_file(const char *file_name, uint16_t command,
     }
     return NULL;
 }
+
+char * OVS_WARN_UNUSED_RESULT
+parse_ofp_geneve_table_mod_str(struct ofputil_geneve_table_mod *gtm,
+                               uint16_t command, const char *s,
+                               enum ofputil_protocol *usable_protocols)
+{
+    *usable_protocols = OFPUTIL_P_NXM_OXM_ANY;
+
+    gtm->command = command;
+    list_init(&gtm->mappings);
+
+    while (*s) {
+        struct ofputil_geneve_map *map = xmalloc(sizeof *map);
+        int n;
+
+        if (*s == ',') {
+            s++;
+        }
+
+        list_push_back(&gtm->mappings, &map->list_node);
+
+        if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n",
+                      &map->option_class, &map->option_type, &map->option_len,
+                      &map->index, &n)) {
+            ofputil_uninit_geneve_table(&gtm->mappings);
+            return xstrdup("invalid geneve mapping");
+        }
+
+        s += n;
+    }
+
+    return NULL;
+}