ovs-ofctl: Add bundle support and unit testing.
[cascardo/ovs.git] / lib / ofp-parse.c
index 6fc5af0..210feed 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);
@@ -452,7 +475,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
         if (!error) {
             enum ofperr err;
 
-            err = ofpacts_check(ofpbuf_data(&ofpacts), ofpbuf_size(&ofpacts), &fm->match.flow,
+            err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match.flow,
                                 OFPP_MAX, fm->table_id, 255, usable_protocols);
             if (!err && !usable_protocols) {
                 err = OFPERR_OFPBAC_MATCH_INCONSISTENT;
@@ -468,7 +491,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             return error;
         }
 
-        fm->ofpacts_len = ofpbuf_size(&ofpacts);
+        fm->ofpacts_len = ofpacts.size;
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
     } else {
         fm->ofpacts_len = 0;
@@ -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
@@ -883,10 +915,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)
 {
@@ -1151,8 +1187,69 @@ parse_bucket_str(struct ofputil_bucket *bucket, char *str_,
         ofpbuf_uninit(&ofpacts);
         return error;
     }
-    bucket->ofpacts = ofpbuf_data(&ofpacts);
-    bucket->ofpacts_len = ofpbuf_size(&ofpacts);
+    bucket->ofpacts = ofpacts.data;
+    bucket->ofpacts_len = ofpacts.size;
+
+    return NULL;
+}
+
+static char * OVS_WARN_UNUSED_RESULT
+parse_select_group_field(char *s, struct field_array *fa,
+                         enum ofputil_protocol *usable_protocols)
+{
+    char *save_ptr = NULL;
+    char *name;
+
+    for (name = strtok_r(s, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+        const struct mf_field *mf = mf_from_name(name);
+
+        if (mf) {
+            char *error;
+            const char *value_str;
+            union mf_value value;
+
+            if (bitmap_is_set(fa->used.bm, mf->id)) {
+                return xasprintf("%s: duplicate field", name);
+            }
+
+            value_str = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (value_str) {
+                error = mf_parse_value(mf, value_str, &value);
+                if (error) {
+                    return error;
+                }
+
+                /* The mask cannot be all-zeros */
+                if (is_all_zeros(&value, mf->n_bytes)) {
+                    return xasprintf("%s: values are wildcards here "
+                                     "and must not be all-zeros", s);
+                }
+
+                /* The values parsed are masks for fields used
+                 * by the selection method */
+                if (!mf_is_mask_valid(mf, &value)) {
+                    return xasprintf("%s: invalid mask for field %s",
+                                     value_str, mf->name);
+                }
+            } else {
+                memset(&value, 0xff, mf->n_bytes);
+            }
+
+            field_array_set(mf->id, &value, fa);
+
+            if (is_all_ones(&value, mf->n_bytes)) {
+                *usable_protocols &= mf->usable_protocols_exact;
+            } else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr
+                       || ip_is_cidr(value.be32)) {
+                *usable_protocols &= mf->usable_protocols_cidr;
+            } else {
+                *usable_protocols &= mf->usable_protocols_bitwise;
+            }
+        } else {
+            return xasprintf("%s: unknown field %s", s, name);
+        }
+    }
 
     return NULL;
 }
@@ -1272,7 +1369,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;
                 }
@@ -1295,7 +1392,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;
                 }
@@ -1327,6 +1424,42 @@ parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, uint16_t command,
         } else if (!strcmp(name, "bucket")) {
             error = xstrdup("bucket is not needed");
             goto out;
+        } else if (!strcmp(name, "selection_method")) {
+            if (!(fields & F_GROUP_TYPE)) {
+                error = xstrdup("selection method is not needed");
+                goto out;
+            }
+            if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) {
+                error = xasprintf("selection method is longer than %u"
+                                  " bytes long",
+                                  NTR_MAX_SELECTION_METHOD_LEN - 1);
+                goto out;
+            }
+            memset(gm->props.selection_method, '\0',
+                   NTR_MAX_SELECTION_METHOD_LEN);
+            strcpy(gm->props.selection_method, value);
+            *usable_protocols &= OFPUTIL_P_OF15_UP;
+        } else if (!strcmp(name, "selection_method_param")) {
+            if (!(fields & F_GROUP_TYPE)) {
+                error = xstrdup("selection method param is not needed");
+                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)) {
+                error = xstrdup("fields are not needed");
+                goto out;
+            }
+            error = parse_select_group_field(value, &gm->props.fields,
+                                             usable_protocols);
+            if (error) {
+                goto out;
+            }
+            *usable_protocols &= OFPUTIL_P_OF15_UP;
         } else {
             error = xasprintf("unknown keyword %s", name);
             goto out;