+ return error;
+}
+
+static char * WARN_UNUSED_RESULT
+parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
+ struct ofpbuf *bands, int command,
+ enum ofputil_protocol *usable_protocols)
+{
+ enum {
+ F_METER = 1 << 0,
+ F_FLAGS = 1 << 1,
+ F_BANDS = 1 << 2,
+ } fields;
+ char *save_ptr = NULL;
+ char *band_str = NULL;
+ char *name;
+
+ /* Meters require at least OF 1.3. */
+ *usable_protocols = OFPUTIL_P_OF13_UP;
+
+ 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:
+ OVS_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) {
+ return xstrdup("must specify bands");
+ }
+ *band_str = '\0';
+
+ band_str = strchr(band_str + 1, '=');
+ if (!band_str) {
+ return xstrdup("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) {
+ return xasprintf("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 {
+ char *error = str_to_u32(value, &mm->meter.meter_id);
+ if (error) {
+ return error;
+ }
+ if (mm->meter.meter_id > OFPM13_MAX) {
+ return xasprintf("invalid value for %s", name);
+ }
+ }
+ } else {
+ return xasprintf("unknown keyword %s", name);
+ }
+ }
+ }
+ if (fields & F_METER && !mm->meter.meter_id) {
+ return xstrdup("must specify 'meter'");
+ }
+ if (fields & F_FLAGS && !mm->meter.flags) {
+ return xstrdup("meter must specify either 'kbps' or 'pktps'");
+ }
+
+ if (fields & F_BANDS) {
+ uint16_t n_bands = 0;
+ struct ofputil_meter_band *band = NULL;
+ int i;
+
+ 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) {
+ return xasprintf("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 {
+ return xasprintf("field %s unknown value %s", name, value);
+ }
+ } else if (!band || !band->type) {
+ return xstrdup("band must start with the 'type' keyword");
+ } else if (!strcmp(name, "rate")) {
+ char *error = str_to_u32(value, &band->rate);
+ if (error) {
+ return error;
+ }
+ } else if (!strcmp(name, "burst_size")) {
+ char *error = str_to_u32(value, &band->burst_size);
+ if (error) {
+ return error;
+ }
+ } else if (!strcmp(name, "prec_level")) {
+ char *error = str_to_u8(value, name, &band->prec_level);
+ if (error) {
+ return error;
+ }
+ } else {
+ return xasprintf("unknown keyword %s", name);
+ }
+ }
+ /* validate bands */
+ if (!n_bands) {
+ return xstrdup("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) {
+ return xstrdup("band must have 'type'");
+ }
+ if (band->type == OFPMBT13_DSCP_REMARK) {
+ if (!band->prec_level) {
+ return xstrdup("'dscp_remark' band must have"
+ " 'prec_level'");
+ }
+ } else {
+ if (band->prec_level) {
+ return xstrdup("Only 'dscp_remark' band may have"
+ " 'prec_level'");
+ }
+ }
+ if (!band->rate) {
+ return xstrdup("band must have 'rate'");
+ }
+ if (mm->meter.flags & OFPMF13_BURST) {
+ if (!band->burst_size) {
+ return xstrdup("band must have 'burst_size' "
+ "when 'burst' flag is set");
+ }
+ } else {
+ if (band->burst_size) {
+ return xstrdup("band may have 'burst_size' only "
+ "when 'burst' flag is set");
+ }
+ }
+ }
+ } else {
+ mm->meter.n_bands = 0;
+ mm->meter.bands = NULL;
+ }
+
+ return NULL;
+}
+
+/* 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.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
+ int command, enum ofputil_protocol *usable_protocols)
+{
+ struct ofpbuf bands;
+ char *string;
+ char *error;
+
+ ofpbuf_init(&bands, 64);
+ string = xstrdup(str_);
+
+ error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
+ usable_protocols);
+
+ free(string);
+ ofpbuf_uninit(&bands);
+
+ return error;
+}
+
+static char * WARN_UNUSED_RESULT
+parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
+ const char *str_, char *string,
+ enum ofputil_protocol *usable_protocols)
+{
+ static atomic_uint32_t id = ATOMIC_VAR_INIT(0);
+ char *save_ptr = NULL;
+ char *name;
+
+ atomic_add(&id, 1, &fmr->id);
+
+ fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
+ | NXFMF_OWN | NXFMF_ACTIONS);
+ fmr->out_port = OFPP_NONE;
+ fmr->table_id = 0xff;
+ match_init_catchall(&fmr->match);
+
+ for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
+ name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
+ const struct protocol *p;
+
+ if (!strcmp(name, "!initial")) {
+ fmr->flags &= ~NXFMF_INITIAL;
+ } else if (!strcmp(name, "!add")) {
+ fmr->flags &= ~NXFMF_ADD;
+ } else if (!strcmp(name, "!delete")) {
+ fmr->flags &= ~NXFMF_DELETE;
+ } else if (!strcmp(name, "!modify")) {
+ fmr->flags &= ~NXFMF_MODIFY;
+ } else if (!strcmp(name, "!actions")) {
+ fmr->flags &= ~NXFMF_ACTIONS;
+ } else if (!strcmp(name, "!own")) {
+ fmr->flags &= ~NXFMF_OWN;
+ } else if (parse_protocol(name, &p)) {
+ match_set_dl_type(&fmr->match, htons(p->dl_type));
+ if (p->nw_proto) {
+ match_set_nw_proto(&fmr->match, p->nw_proto);
+ }
+ } else {
+ char *value;
+
+ value = strtok_r(NULL, ", \t\r\n", &save_ptr);
+ if (!value) {
+ return xasprintf("%s: field %s missing value", str_, name);
+ }
+
+ if (!strcmp(name, "table")) {
+ char *error = str_to_u8(value, "table", &fmr->table_id);
+ if (error) {
+ return error;
+ }
+ } else if (!strcmp(name, "out_port")) {
+ fmr->out_port = u16_to_ofp(atoi(value));
+ } else if (mf_from_name(name)) {
+ char *error;
+
+ error = parse_field(mf_from_name(name), value, &fmr->match,
+ usable_protocols);
+ if (error) {
+ return error;
+ }
+ } else {
+ return xasprintf("%s: unknown keyword %s", str_, name);
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Convert 'str_' (as described in the documentation for the "monitor" command
+ * in the ovs-ofctl man page) into 'fmr'.
+ *
+ * Returns NULL if successful, otherwise a malloc()'d string describing the
+ * error. The caller is responsible for freeing the returned string. */
+char * WARN_UNUSED_RESULT
+parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
+ const char *str_,
+ enum ofputil_protocol *usable_protocols)
+{
+ char *string = xstrdup(str_);
+ char *error = parse_flow_monitor_request__(fmr, str_, string,
+ usable_protocols);
+ free(string);
+ return error;