ofp-actions: Give ovs_instruction funcs names that start with that prefix.
[sliver-openvswitch.git] / lib / ofp-parse.c
index 2d2daa4..2c1fb5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,6 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#include "autopath.h"
 #include "bundle.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
@@ -46,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
@@ -136,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);
 }
 
@@ -153,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;
     }
 }
@@ -168,8 +167,7 @@ parse_resubmit(char *arg, struct ofpbuf *ofpacts)
 
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
-        resubmit->in_port = ofputil_port_from_string(in_port_s);
-        if (!resubmit->in_port) {
+        if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
             ovs_fatal(0, "%s: resubmit to unknown port", in_port_s);
         }
     } else {
@@ -280,22 +278,28 @@ parse_controller(struct ofpbuf *b, char *arg)
 }
 
 static void
-parse_dec_ttl(struct ofpbuf *b, char *arg)
+parse_noargs_dec_ttl(struct ofpbuf *b)
 {
     struct ofpact_cnt_ids *ids;
+    uint16_t id = 0;
 
     ids = ofpact_put_DEC_TTL(b);
+    ofpbuf_put(b, &id, sizeof id);
+    ids = b->l2;
+    ids->n_controllers++;
+    ofpact_update_len(b, &ids->ofpact);
+}
 
+static void
+parse_dec_ttl(struct ofpbuf *b, char *arg)
+{
     if (*arg == '\0') {
-        uint16_t id = 0;
-
-        ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL;
-        ofpbuf_put(b, &id, sizeof id);
-        ids = b->l2;
-        ids->n_controllers++;
+        parse_noargs_dec_ttl(b);
     } else {
+        struct ofpact_cnt_ids *ids;
         char *cntr;
 
+        ids = ofpact_put_DEC_TTL(b);
         ids->ofpact.compat = OFPUTIL_NXAST_DEC_TTL_CNT_IDS;
         for (cntr = strtok_r(arg, ", ", &arg); cntr != NULL;
              cntr = strtok_r(NULL, ", ", &arg)) {
@@ -309,9 +313,20 @@ parse_dec_ttl(struct ofpbuf *b, char *arg)
             ovs_fatal(0, "dec_ttl_cnt_ids: expected at least one controller "
                       "id.");
         }
+        ofpact_update_len(b, &ids->ofpact);
+    }
+}
 
+static void
+parse_set_mpls_ttl(struct ofpbuf *b, const char *arg)
+{
+    struct ofpact_mpls_ttl *mpls_ttl = ofpact_put_SET_MPLS_TTL(b);
+
+    if (*arg == '\0') {
+        ovs_fatal(0, "parse_set_mpls_ttl: expected ttl.");
     }
-    ofpact_update_len(b, &ids->ofpact);
+
+    mpls_ttl->ttl = atoi(arg);
 }
 
 static void
@@ -357,11 +372,58 @@ set_field_parse(const char *arg, struct ofpbuf *ofpacts)
 }
 
 static void
-parse_named_action(enum ofputil_action_code code, const struct flow *flow,
+parse_metadata(struct ofpbuf *b, char *arg)
+{
+    struct ofpact_metadata *om;
+    char *mask = strchr(arg, '/');
+
+    om = ofpact_put_WRITE_METADATA(b);
+
+    if (mask) {
+        *mask = '\0';
+        om->mask = htonll(str_to_u64(mask + 1));
+    } else {
+        om->mask = htonll(UINT64_MAX);
+    }
+
+    om->metadata = htonll(str_to_u64(arg));
+}
+
+static void
+parse_sample(struct ofpbuf *b, char *arg)
+{
+    struct ofpact_sample *os = ofpact_put_SAMPLE(b);
+    char *key, *value;
+
+    while (ofputil_parse_key_value(&arg, &key, &value)) {
+        if (!strcmp(key, "probability")) {
+            os->probability = str_to_u16(value, "probability");
+            if (os->probability == 0) {
+                ovs_fatal(0, "invalid probability value \"%s\"", value);
+            }
+        } else if (!strcmp(key, "collector_set_id")) {
+            os->collector_set_id = str_to_u32(value);
+        } else if (!strcmp(key, "obs_domain_id")) {
+            os->obs_domain_id = str_to_u32(value);
+        } else if (!strcmp(key, "obs_point_id")) {
+            os->obs_point_id = str_to_u32(value);
+        } else {
+            ovs_fatal(0, "invalid key \"%s\" in \"sample\" argument",
+                      key);
+        }
+    }
+    if (os->probability == 0) {
+        ovs_fatal(0, "non-zero \"probability\" must be specified on sample");
+    }
+}
+
+static void
+parse_named_action(enum ofputil_action_code code,
                    char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_tunnel *tunnel;
     uint16_t vid;
+    uint16_t ethertype;
     ovs_be32 ip;
     uint8_t pcp;
     uint8_t tos;
@@ -398,9 +460,24 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         break;
 
     case OFPUTIL_OFPAT10_STRIP_VLAN:
+    case OFPUTIL_OFPAT11_POP_VLAN:
         ofpact_put_STRIP_VLAN(ofpacts);
         break;
 
+    case OFPUTIL_OFPAT11_PUSH_VLAN:
+        ethertype = str_to_u16(arg, "ethertype");
+        if (ethertype != ETH_TYPE_VLAN_8021Q) {
+            /* XXX ETH_TYPE_VLAN_8021AD case isn't supported */
+            ovs_fatal(0, "%s: not a valid VLAN ethertype", arg);
+        }
+        ofpact_put_PUSH_VLAN(ofpacts);
+        break;
+
+    case OFPUTIL_OFPAT11_SET_QUEUE:
+        ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
+        break;
+
+
     case OFPUTIL_OFPAT10_SET_DL_SRC:
     case OFPUTIL_OFPAT11_SET_DL_SRC:
         str_to_mac(arg, ofpact_put_SET_ETH_SRC(ofpacts)->mac);
@@ -432,6 +509,9 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         ofpact_put_SET_IPV4_DSCP(ofpacts)->dscp = tos;
         break;
 
+    case OFPUTIL_OFPAT11_DEC_NW_TTL:
+        NOT_REACHED();
+
     case OFPUTIL_OFPAT10_SET_TP_SRC:
     case OFPUTIL_OFPAT11_SET_TP_SRC:
         ofpact_put_SET_L4_SRC_PORT(ofpacts)->port = str_to_u32(arg);
@@ -457,6 +537,10 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         tunnel->tun_id = str_to_u64(arg);
         break;
 
+    case OFPUTIL_NXAST_WRITE_METADATA:
+        parse_metadata(ofpacts, arg);
+        break;
+
     case OFPUTIL_NXAST_SET_QUEUE:
         ofpact_put_SET_QUEUE(ofpacts)->queue_id = str_to_u32(arg);
         break;
@@ -481,10 +565,6 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         multipath_parse(ofpact_put_MULTIPATH(ofpacts), arg);
         break;
 
-    case OFPUTIL_NXAST_AUTOPATH__DEPRECATED:
-        autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
-        break;
-
     case OFPUTIL_NXAST_BUNDLE:
         bundle_parse(arg, ofpacts);
         break;
@@ -499,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:
@@ -510,6 +590,16 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         parse_dec_ttl(ofpacts, arg);
         break;
 
+    case OFPUTIL_NXAST_SET_MPLS_TTL:
+    case OFPUTIL_OFPAT11_SET_MPLS_TTL:
+        parse_set_mpls_ttl(ofpacts, arg);
+        break;
+
+    case OFPUTIL_OFPAT11_DEC_MPLS_TTL:
+    case OFPUTIL_NXAST_DEC_MPLS_TTL:
+        ofpact_put_DEC_MPLS_TTL(ofpacts);
+        break;
+
     case OFPUTIL_NXAST_FIN_TIMEOUT:
         parse_fin_timeout(ofpacts, arg);
         break;
@@ -517,38 +607,171 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
     case OFPUTIL_NXAST_CONTROLLER:
         parse_controller(ofpacts, arg);
         break;
+
+    case OFPUTIL_OFPAT11_PUSH_MPLS:
+    case OFPUTIL_NXAST_PUSH_MPLS:
+        ofpact_put_PUSH_MPLS(ofpacts)->ethertype =
+            htons(str_to_u16(arg, "push_mpls"));
+        break;
+
+    case OFPUTIL_OFPAT11_POP_MPLS:
+    case OFPUTIL_NXAST_POP_MPLS:
+        ofpact_put_POP_MPLS(ofpacts)->ethertype =
+            htons(str_to_u16(arg, "pop_mpls"));
+        break;
+
+    case OFPUTIL_NXAST_STACK_PUSH:
+        nxm_parse_stack_action(ofpact_put_STACK_PUSH(ofpacts), arg);
+        break;
+    case OFPUTIL_NXAST_STACK_POP:
+        nxm_parse_stack_action(ofpact_put_STACK_POP(ofpacts), arg);
+        break;
+
+    case OFPUTIL_NXAST_SAMPLE:
+        parse_sample(ofpacts, arg);
+        break;
     }
 }
 
+static bool
+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, arg, ofpacts);
+    } else if (!strcasecmp(act, "drop")) {
+        if (n_actions) {
+            ovs_fatal(0, "Drop actions must not be preceded by other "
+                      "actions");
+        } else if (ofputil_parse_key_value(&pos, &act, &arg)) {
+            ovs_fatal(0, "Drop actions must not be followed by other "
+                      "actions");
+        }
+        return false;
+    } else {
+        ofp_port_t port;
+        if (ofputil_port_from_string(act, &port)) {
+            ofpact_put_OUTPUT(ofpacts)->port = port;
+        } else {
+            ovs_fatal(0, "Unknown action: %s", act);
+        }
+    }
+
+    return true;
+}
+
 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;
     int n_actions;
 
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        int code = ofputil_action_code_from_name(act);
-        if (code >= 0) {
-            parse_named_action(code, flow, arg, ofpacts);
-        } else if (!strcasecmp(act, "drop")) {
-            if (n_actions) {
-                ovs_fatal(0, "Drop actions must not be preceded by other "
-                          "actions");
-            } else if (ofputil_parse_key_value(&pos, &act, &arg)) {
-                ovs_fatal(0, "Drop actions must not be followed by other "
-                          "actions");
-            }
+        if (!str_to_ofpact__(pos, act, arg, ofpacts, n_actions)) {
             break;
-        } else {
-            uint16_t port = ofputil_port_from_string(act);
-            if (port) {
-                ofpact_put_OUTPUT(ofpacts)->port = port;
-            } else {
-                ovs_fatal(0, "Unknown action: %s", act);
+        }
+        n_actions++;
+    }
+
+    error = ofpacts_verify(ofpacts->data, ofpacts->size);
+    if (error) {
+        ovs_fatal(0, "Incorrect action ordering");
+    }
+
+    ofpact_pad(ofpacts);
+}
+
+static void
+parse_named_instruction(enum ovs_instruction_type type,
+                        char *arg, struct ofpbuf *ofpacts)
+{
+    enum ofperr error;
+
+    switch (type) {
+    case OVSINST_OFPIT11_APPLY_ACTIONS:
+        NOT_REACHED();  /* This case is handled by str_to_inst_ofpacts() */
+        break;
+
+    case OVSINST_OFPIT11_WRITE_ACTIONS:
+        /* XXX */
+        ovs_fatal(0, "instruction write-actions is not supported yet");
+        break;
+
+    case OVSINST_OFPIT11_CLEAR_ACTIONS:
+        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;
+
+    case OVSINST_OFPIT11_GOTO_TABLE: {
+        struct ofpact_goto_table *ogt = ofpact_put_GOTO_TABLE(ofpacts);
+        char *table_s = strsep(&arg, ",");
+        if (!table_s || !table_s[0]) {
+            ovs_fatal(0, "instruction goto-table needs table id");
+        }
+        ogt->table_id = str_to_u8(table_s, "table");
+        break;
+    }
+    }
+
+    /* If write_metadata is specified as an action AND an instruction, ofpacts
+       could be invalid. */
+    error = ofpacts_verify(ofpacts->data, ofpacts->size);
+    if (error) {
+        ovs_fatal(0, "Incorrect instruction ordering");
+    }
+}
+
+static void
+str_to_inst_ofpacts(char *str, struct ofpbuf *ofpacts)
+{
+    char *pos, *inst, *arg;
+    int type;
+    const char *prev_inst = NULL;
+    int prev_type = -1;
+    int n_actions = 0;
+
+    pos = str;
+    while (ofputil_parse_key_value(&pos, &inst, &arg)) {
+        type = ovs_instruction_type_from_name(inst);
+        if (type < 0) {
+            if (!str_to_ofpact__(pos, inst, arg, ofpacts, n_actions)) {
+                break;
+            }
+
+            type = OVSINST_OFPIT11_APPLY_ACTIONS;
+            if (prev_type == type) {
+                n_actions++;
+                continue;
             }
+        } else if (type == OVSINST_OFPIT11_APPLY_ACTIONS) {
+            ovs_fatal(0, "%s isn't supported. Just write actions then "
+                      "it is interpreted as apply_actions", inst);
+        } else {
+            parse_named_instruction(type, arg, ofpacts);
+        }
+
+        if (type == prev_type) {
+            ovs_fatal(0, "instruction can be specified at most once: %s",
+                      inst);
         }
+        if (type <= prev_type) {
+            ovs_fatal(0, "Instruction %s must be specified before %s",
+                      inst, prev_inst);
+        }
+
+        prev_inst = inst;
+        prev_type = type;
         n_actions++;
     }
     ofpact_pad(ofpacts);
@@ -574,6 +797,9 @@ parse_protocol(const char *name, const struct protocol **p_out)
         { "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
         { "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
         { "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
+        { "rarp", ETH_TYPE_RARP, 0},
+        { "mpls", ETH_TYPE_MPLS, 0 },
+        { "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
     };
     const struct protocol *p;
 
@@ -682,7 +908,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     fm->idle_timeout = OFP_FLOW_PERMANENT;
     fm->hard_timeout = OFP_FLOW_PERMANENT;
     fm->buffer_id = UINT32_MAX;
-    fm->out_port = OFPP_NONE;
+    fm->out_port = OFPP_ANY;
     fm->flags = 0;
     if (fields & F_ACTIONS) {
         act_str = strstr(string, "action");
@@ -711,6 +937,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
             fm->flags |= OFPFF_SEND_FLOW_REM;
         } else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
             fm->flags |= OFPFF_CHECK_OVERLAP;
+        } else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
+            fm->flags |= OFPFF12_RESET_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
+            fm->flags |= OFPFF13_NO_PKT_COUNTS;
+        } else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
+            fm->flags |= OFPFF13_NO_BYT_COUNTS;
         } else {
             char *value;
 
@@ -720,10 +952,9 @@ 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")) {
-                fm->out_port = ofputil_port_from_string(name);
-                if (!fm->out_port) {
+                if (!ofputil_port_from_string(value, &fm->out_port)) {
                     ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
                               name);
                 }
@@ -757,7 +988,9 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                 parse_field(mf_from_name(name), value, &fm->match);
             } else if (!strcmp(name, "duration")
                        || !strcmp(name, "n_packets")
-                       || !strcmp(name, "n_bytes")) {
+                       || !strcmp(name, "n_bytes")
+                       || !strcmp(name, "idle_age")
+                       || !strcmp(name, "hard_age")) {
                 /* Ignore these, so that users can feed the output of
                  * "ovs-ofctl dump-flows" back into commands that parse
                  * flows. */
@@ -775,11 +1008,19 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     }
     if (fields & F_ACTIONS) {
         struct ofpbuf ofpacts;
+        enum ofperr err;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_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);
+
+        err = ofpacts_check(fm->ofpacts, fm->ofpacts_len, &fm->match.flow,
+                            OFPP_MAX);
+        if (err) {
+            exit(EXIT_FAILURE);
+        }
+
     } else {
         fm->ofpacts_len = 0;
         fm->ofpacts = NULL;
@@ -788,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
@@ -837,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 {
@@ -859,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);
 }
 
@@ -992,6 +1425,10 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
+    if (!flow->in_port.ofp_port) {
+        flow->in_port.ofp_port = OFPP_NONE;
+    }
+
 exit:
     free(copy);