ofp-util: Support for OpenFlow 1.3 meters.
[sliver-openvswitch.git] / lib / ofp-parse.c
index 970fd53..b0f4d93 100644 (file)
@@ -45,14 +45,14 @@ static void ofp_fatal(const char *flow, bool verbose, const char *format, ...)
     NO_RETURN;
 
 static uint8_t
-str_to_table_id(const char *str)
+str_to_u8(const char *str, const char *name)
 {
-    int table_id;
+    int value;
 
-    if (!str_to_int(str, 10, &table_id) || table_id < 0 || table_id > 255) {
-        ovs_fatal(0, "invalid table \"%s\"", str);
+    if (!str_to_int(str, 10, &value) || value < 0 || value > 255) {
+        ovs_fatal(0, "invalid %s \"%s\"", name, str);
     }
-    return table_id;
+    return value;
 }
 
 static uint16_t
@@ -135,7 +135,7 @@ parse_enqueue(char *arg, struct ofpbuf *ofpacts)
     }
 
     enqueue = ofpact_put_ENQUEUE(ofpacts);
-    enqueue->port = str_to_u32(port);
+    enqueue->port = u16_to_ofp(str_to_u32(port));
     enqueue->queue = str_to_u32(queue);
 }
 
@@ -152,7 +152,7 @@ parse_output(char *arg, struct ofpbuf *ofpacts)
         struct ofpact_output *output;
 
         output = ofpact_put_OUTPUT(ofpacts);
-        output->port = str_to_u32(arg);
+        output->port = u16_to_ofp(str_to_u32(arg));
         output->max_len = output->port == OFPP_CONTROLLER ? UINT16_MAX : 0;
     }
 }
@@ -418,7 +418,7 @@ parse_sample(struct ofpbuf *b, char *arg)
 }
 
 static void
-parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+parse_named_action(enum ofputil_action_code code,
                    char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_tunnel *tunnel;
@@ -579,7 +579,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
-        learn_parse(arg, flow, ofpacts);
+        learn_parse(arg, ofpacts);
         break;
 
     case OFPUTIL_NXAST_EXIT:
@@ -634,12 +634,12 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
 }
 
 static bool
-str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
+str_to_ofpact__(char *pos, char *act, char *arg,
                 struct ofpbuf *ofpacts, int n_actions)
 {
     int code = ofputil_action_code_from_name(act);
     if (code >= 0) {
-        parse_named_action(code, flow, arg, ofpacts);
+        parse_named_action(code, arg, ofpacts);
     } else if (!strcasecmp(act, "drop")) {
         if (n_actions) {
             ovs_fatal(0, "Drop actions must not be preceded by other "
@@ -650,7 +650,7 @@ str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
         }
         return false;
     } else {
-        uint16_t port;
+        ofp_port_t port;
         if (ofputil_port_from_string(act, &port)) {
             ofpact_put_OUTPUT(ofpacts)->port = port;
         } else {
@@ -662,7 +662,7 @@ str_to_ofpact__(const struct flow *flow, char *pos, char *act, char *arg,
 }
 
 static void
-str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
+str_to_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
     char *pos, *act, *arg;
     enum ofperr error;
@@ -671,7 +671,7 @@ str_to_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        if (!str_to_ofpact__(flow, pos, act, arg, ofpacts, n_actions)) {
+        if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) {
             break;
         }
         n_actions++;
@@ -705,6 +705,10 @@ parse_named_instruction(enum ovs_instruction_type type,
         ofpact_put_CLEAR_ACTIONS(ofpacts);
         break;
 
+    case OVSINST_OFPIT13_METER:
+        ofpact_put_METER(ofpacts)->meter_id = str_to_u32(arg);
+        break;
+
     case OVSINST_OFPIT11_WRITE_METADATA:
         parse_metadata(ofpacts, arg);
         break;
@@ -715,7 +719,7 @@ parse_named_instruction(enum ovs_instruction_type type,
         if (!table_s || !table_s[0]) {
             ovs_fatal(0, "instruction goto-table needs table id");
         }
-        ogt->table_id = str_to_table_id(table_s);
+        ogt->table_id = str_to_u8(table_s, "table");
         break;
     }
     }
@@ -729,7 +733,7 @@ parse_named_instruction(enum ovs_instruction_type type,
 }
 
 static void
-str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
+str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
 {
     char *pos, *inst, *arg;
     int type;
@@ -741,7 +745,7 @@ str_to_inst_ofpacts(const struct flow *flow, char *str, struct ofpbuf *ofpacts)
     while (ofputil_parse_key_value(&pos, &inst, &arg)) {
         type = ofpact_instruction_type_from_name(inst);
         if (type < 0) {
-            if (!str_to_ofpact__(flow, pos, inst, arg, ofpacts, n_actions)) {
+            if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
                 break;
             }
 
@@ -948,7 +952,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             }
 
             if (!strcmp(name, "table")) {
-                fm->table_id = str_to_table_id(value);
+                fm->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
                 if (!ofputil_port_from_string(name, &fm->out_port)) {
                     ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
@@ -1007,7 +1011,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         enum ofperr err;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts);
+        str_to_inst_ofpacts(act_str, &ofpacts);
         fm->ofpacts_len = ofpacts.size;
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
 
@@ -1025,6 +1029,198 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     free(string);
 }
 
+/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
+ * page) into 'mm' for sending the specified meter_mod 'command' to a switch.
+ */
+void
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+                        int command, bool verbose)
+{
+    enum {
+        F_METER = 1 << 0,
+        F_FLAGS = 1 << 1,
+        F_BANDS = 1 << 2,
+    } fields;
+    char *string = xstrdup(str_);
+    char *save_ptr = NULL;
+    char *band_str = NULL;
+    char *name;
+
+    switch (command) {
+    case -1:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_ADD:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    case OFPMC13_DELETE:
+        fields = F_METER;
+        break;
+
+    case OFPMC13_MODIFY:
+        fields = F_METER | F_FLAGS | F_BANDS;
+        break;
+
+    default:
+        NOT_REACHED();
+    }
+
+    mm->command = command;
+    mm->meter.meter_id = 0;
+    mm->meter.flags = 0;
+    if (fields & F_BANDS) {
+        band_str = strstr(string, "band");
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+        *band_str = '\0';
+
+        band_str = strchr(band_str + 1, '=');
+        if (!band_str) {
+            ofp_fatal(str_, verbose, "must specify bands");
+        }
+
+        band_str++;
+    }
+    for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+         name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+        if (fields & F_FLAGS && !strcmp(name, "kbps")) {
+            mm->meter.flags |= OFPMF13_KBPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
+            mm->meter.flags |= OFPMF13_PKTPS;
+        } else if (fields & F_FLAGS && !strcmp(name, "burst")) {
+            mm->meter.flags |= OFPMF13_BURST;
+        } else if (fields & F_FLAGS && !strcmp(name, "stats")) {
+            mm->meter.flags |= OFPMF13_STATS;
+        } else {
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "meter")) {
+                if (!strcmp(value, "all")) {
+                    mm->meter.meter_id = OFPM13_ALL;
+                } else if (!strcmp(value, "controller")) {
+                    mm->meter.meter_id = OFPM13_CONTROLLER;
+                } else if (!strcmp(value, "slowpath")) {
+                    mm->meter.meter_id = OFPM13_SLOWPATH;
+                } else {
+                    mm->meter.meter_id = str_to_u32(value);
+                    if (mm->meter.meter_id > OFPM13_MAX) {
+                        ofp_fatal(str_, verbose, "invalid value for %s", name);
+                    }
+                }
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+    }
+    if (fields & F_METER && !mm->meter.meter_id) {
+        ofp_fatal(str_, verbose, "must specify 'meter'");
+    }
+    if (fields & F_FLAGS && !mm->meter.flags) {
+        ofp_fatal(str_, verbose,
+                  "meter must specify either 'kbps' or 'pktps'");
+    }
+
+    if (fields & F_BANDS) {
+        struct ofpbuf bands;
+        uint16_t n_bands = 0;
+        struct ofputil_meter_band *band = NULL;
+        int i;
+
+        ofpbuf_init(&bands, 64);
+
+        for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
+             name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+
+            char *value;
+
+            value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+            if (!value) {
+                ofp_fatal(str_, verbose, "field %s missing value", name);
+            }
+
+            if (!strcmp(name, "type")) {
+                /* Start a new band */
+                band = ofpbuf_put_zeros(&bands, sizeof *band);
+                n_bands++;
+
+                if (!strcmp(value, "drop")) {
+                    band->type = OFPMBT13_DROP;
+                } else if (!strcmp(value, "dscp_remark")) {
+                    band->type = OFPMBT13_DSCP_REMARK;
+                } else {
+                    ofp_fatal(str_, verbose, "field %s unknown value %s", name,
+                              value);
+                }
+            } else if (!band || !band->type) {
+                ofp_fatal(str_, verbose,
+                          "band must start with the 'type' keyword");
+            } else if (!strcmp(name, "rate")) {
+                band->rate = str_to_u32(value);
+            } else if (!strcmp(name, "burst_size")) {
+                band->burst_size = str_to_u32(value);
+            } else if (!strcmp(name, "prec_level")) {
+                band->prec_level = str_to_u8(value, name);
+            } else {
+                ofp_fatal(str_, verbose, "unknown keyword %s", name);
+            }
+        }
+        /* validate bands */
+        if (!n_bands) {
+            ofp_fatal(str_, verbose, "meter must have bands");
+        }
+
+        mm->meter.n_bands = n_bands;
+        mm->meter.bands = ofpbuf_steal_data(&bands);
+
+        for (i = 0; i < n_bands; ++i) {
+            band = &mm->meter.bands[i];
+
+            if (!band->type) {
+                ofp_fatal(str_, verbose, "band must have 'type'");
+            }
+            if (band->type == OFPMBT13_DSCP_REMARK) {
+                if (!band->prec_level) {
+                    ofp_fatal(str_, verbose, "'dscp_remark' band must have"
+                              " 'prec_level'");
+                }
+            } else {
+                if (band->prec_level) {
+                    ofp_fatal(str_, verbose, "Only 'dscp_remark' band may have"
+                              " 'prec_level'");
+                }
+            }
+            if (!band->rate) {
+                ofp_fatal(str_, verbose, "band must have 'rate'");
+            }
+            if (mm->meter.flags & OFPMF13_BURST) {
+                if (!band->burst_size) {
+                    ofp_fatal(str_, verbose, "band must have 'burst_size' "
+                              "when 'burst' flag is set");
+                }
+            } else {
+                if (band->burst_size) {
+                    ofp_fatal(str_, verbose, "band may have 'burst_size' only "
+                              "when 'burst' flag is set");
+                }
+            }
+        }
+    } else {
+        mm->meter.n_bands = 0;
+        mm->meter.bands = NULL;
+    }
+
+    free(string);
+}
+
 /* Convert 'str_' (as described in the documentation for the "monitor" command
  * in the ovs-ofctl man page) into 'fmr'. */
 void
@@ -1074,9 +1270,9 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
             }
 
             if (!strcmp(name, "table")) {
-                fmr->table_id = str_to_table_id(value);
+                fmr->table_id = str_to_u8(value, name);
             } else if (!strcmp(name, "out_port")) {
-                fmr->out_port = atoi(value);
+                fmr->out_port = u16_to_ofp(atoi(value));
             } else if (mf_from_name(name)) {
                 parse_field(mf_from_name(name), value, &fmr->match);
             } else {
@@ -1096,7 +1292,7 @@ void
 parse_ofpacts(const char *s_, struct ofpbuf *ofpacts)
 {
     char *s = xstrdup(s_);
-    str_to_ofpacts(NULL, s, ofpacts);
+    str_to_ofpacts(s, ofpacts);
     free(s);
 }
 
@@ -1229,8 +1425,8 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
-    if (!flow->in_port) {
-        flow->in_port = OFPP_NONE;
+    if (!flow->in_port.ofp_port) {
+        flow->in_port.ofp_port = OFPP_NONE;
     }
 
 exit: