nicira-ext: Add Nicira actions NXAST_STACK_PUSH and NXAST_STACK_POP.
[sliver-openvswitch.git] / lib / ofp-parse.c
index 922e296..e8abc9f 100644 (file)
@@ -22,7 +22,6 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#include "autopath.h"
 #include "bundle.h"
 #include "byte-order.h"
 #include "dynamic-string.h"
@@ -169,7 +168,7 @@ parse_resubmit(char *arg, struct ofpbuf *ofpacts)
     in_port_s = strsep(&arg, ",");
     if (in_port_s && in_port_s[0]) {
         if (!ofputil_port_from_string(in_port_s, &resubmit->in_port)) {
-            resubmit->in_port = str_to_u32(in_port_s);
+            ovs_fatal(0, "%s: resubmit to unknown port", in_port_s);
         }
     } else {
         resubmit->in_port = OFPP_IN_PORT;
@@ -278,12 +277,125 @@ parse_controller(struct ofpbuf *b, char *arg)
     }
 }
 
+static void
+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') {
+        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)) {
+            uint16_t id = atoi(cntr);
+
+            ofpbuf_put(b, &id, sizeof id);
+            ids = b->l2;
+            ids->n_controllers++;
+        }
+        if (!ids->n_controllers) {
+            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.");
+    }
+
+    mpls_ttl->ttl = atoi(arg);
+}
+
+static void
+set_field_parse(const char *arg, struct ofpbuf *ofpacts)
+{
+    char *orig = xstrdup(arg);
+    struct ofpact_reg_load *load = ofpact_put_REG_LOAD(ofpacts);
+    char *value;
+    char *delim;
+    char *key;
+    const struct mf_field *mf;
+    const char *error;
+    union mf_value mf_value;
+
+    value = orig;
+    delim = strstr(orig, "->");
+    if (!delim) {
+        ovs_fatal(0, "%s: missing `->'", orig);
+    }
+    if (strlen(delim) <= strlen("->")) {
+        ovs_fatal(0, "%s: missing field name following `->'", orig);
+    }
+
+    key = delim + strlen("->");
+    mf = mf_from_name(key);
+    if (!mf) {
+        ovs_fatal(0, "%s is not valid oxm field name", key);
+    }
+    if (!mf->writable) {
+        ovs_fatal(0, "%s is not allowed to set", key);
+    }
+
+    delim[0] = '\0';
+    error = mf_parse_value(mf, value, &mf_value);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    if (!mf_is_value_valid(mf, &mf_value)) {
+        ovs_fatal(0, "%s is not valid valid for field %s", value, key);
+    }
+    ofpact_set_field_init(load, mf, &mf_value);
+    free(orig);
+}
+
+static void
+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_named_action(enum ofputil_action_code code, const struct flow *flow,
                    char *arg, struct ofpbuf *ofpacts)
 {
     struct ofpact_tunnel *tunnel;
     uint16_t vid;
+    uint16_t ethertype;
     ovs_be32 ip;
     uint8_t pcp;
     uint8_t tos;
@@ -315,10 +427,29 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         ofpact_put_SET_VLAN_PCP(ofpacts)->vlan_pcp = pcp;
         break;
 
+    case OFPUTIL_OFPAT12_SET_FIELD:
+        set_field_parse(arg, ofpacts);
+        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);
@@ -350,6 +481,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);
@@ -375,6 +509,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;
@@ -399,10 +537,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:
-        autopath_parse(ofpact_put_AUTOPATH(ofpacts), arg);
-        break;
-
     case OFPUTIL_NXAST_BUNDLE:
         bundle_parse(arg, ofpacts);
         break;
@@ -413,6 +547,7 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
 
     case OFPUTIL_NXAST_RESUBMIT_TABLE:
     case OFPUTIL_NXAST_OUTPUT_REG:
+    case OFPUTIL_NXAST_DEC_TTL_CNT_IDS:
         NOT_REACHED();
 
     case OFPUTIL_NXAST_LEARN:
@@ -424,7 +559,17 @@ parse_named_action(enum ofputil_action_code code, const struct flow *flow,
         break;
 
     case OFPUTIL_NXAST_DEC_TTL:
-        ofpact_put_DEC_TTL(ofpacts);
+        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:
@@ -434,38 +579,162 @@ 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;
+    }
+}
+
+static bool
+str_to_ofpact__(const struct flow *flow, 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);
+    } 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 {
+        uint16_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)
 {
     char *pos, *act, *arg;
+    enum ofperr error;
     int n_actions;
 
     pos = str;
     n_actions = 0;
     while (ofputil_parse_key_value(&pos, &act, &arg)) {
-        uint16_t port;
-        int code;
-
-        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__(flow, pos, act, arg, ofpacts, n_actions)) {
             break;
-        } else if (ofputil_port_from_string(act, &port)) {
-            ofpact_put_OUTPUT(ofpacts)->port = port;
+        }
+        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_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_table_id(table_s);
+        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(const struct flow *flow, 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 = ofpact_instruction_type_from_name(inst);
+        if (type < 0) {
+            if (!str_to_ofpact__(flow, 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 {
-            ovs_fatal(0, "Unknown action: %s", act);
+            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);
@@ -491,6 +760,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;
 
@@ -518,7 +790,7 @@ ofp_fatal(const char *flow, bool verbose, const char *format, ...)
 }
 
 static void
-parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
+parse_field(const struct mf_field *mf, const char *s, struct match *match)
 {
     union mf_value value, mask;
     char *error;
@@ -528,7 +800,7 @@ parse_field(const struct mf_field *mf, const char *s, struct cls_rule *rule)
         ovs_fatal(0, "%s", error);
     }
 
-    mf_set(mf, &value, &mask, rule);
+    mf_set(mf, &value, &mask, match);
 }
 
 /* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
@@ -584,7 +856,8 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         NOT_REACHED();
     }
 
-    cls_rule_init_catchall(&fm->cr, OFP_DEFAULT_PRIORITY);
+    match_init_catchall(&fm->match);
+    fm->priority = OFP_DEFAULT_PRIORITY;
     fm->cookie = htonll(0);
     fm->cookie_mask = htonll(0);
     if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
@@ -598,7 +871,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");
@@ -619,14 +892,20 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         const struct protocol *p;
 
         if (parse_protocol(name, &p)) {
-            cls_rule_set_dl_type(&fm->cr, htons(p->dl_type));
+            match_set_dl_type(&fm->match, htons(p->dl_type));
             if (p->nw_proto) {
-                cls_rule_set_nw_proto(&fm->cr, p->nw_proto);
+                match_set_nw_proto(&fm->match, p->nw_proto);
             }
         } else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
             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;
 
@@ -638,9 +917,12 @@ 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);
             } else if (!strcmp(name, "out_port")) {
-                fm->out_port = atoi(value);
+                if (!ofputil_port_from_string(name, &fm->out_port)) {
+                    ofp_fatal(str_, verbose, "%s is not a valid OpenFlow port",
+                              name);
+                }
             } else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
-                fm->cr.priority = str_to_u16(value, name);
+                fm->priority = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
                 fm->idle_timeout = str_to_u16(value, name);
             } else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
@@ -666,10 +948,12 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
                     fm->new_cookie = htonll(str_to_u64(value));
                 }
             } else if (mf_from_name(name)) {
-                parse_field(mf_from_name(name), value, &fm->cr);
+                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. */
@@ -689,7 +973,7 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
         struct ofpbuf ofpacts;
 
         ofpbuf_init(&ofpacts, 32);
-        str_to_ofpacts(&fm->cr.flow, act_str, &ofpacts);
+        str_to_inst_ofpacts(&fm->match.flow, act_str, &ofpacts);
         fm->ofpacts_len = ofpacts.size;
         fm->ofpacts = ofpbuf_steal_data(&ofpacts);
     } else {
@@ -700,6 +984,68 @@ parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
     free(string);
 }
 
+/* Convert 'str_' (as described in the documentation for the "monitor" command
+ * in the ovs-ofctl man page) into 'fmr'. */
+void
+parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
+                           const char *str_)
+{
+    static uint32_t id;
+
+    char *string = xstrdup(str_);
+    char *save_ptr = NULL;
+    char *name;
+
+    fmr->id = 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) {
+                ovs_fatal(0, "%s: field %s missing value", str_, name);
+            }
+
+            if (!strcmp(name, "table")) {
+                fmr->table_id = str_to_table_id(value);
+            } else if (!strcmp(name, "out_port")) {
+                fmr->out_port = atoi(value);
+            } else if (mf_from_name(name)) {
+                parse_field(mf_from_name(name), value, &fmr->match);
+            } else {
+                ovs_fatal(0, "%s: unknown keyword %s", str_, name);
+            }
+        }
+    }
+    free(string);
+}
+
 /* Parses 's' as a set of OpenFlow actions and appends the actions to
  * 'actions'.
  *
@@ -719,15 +1065,15 @@ void
 parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
                        uint16_t command, bool verbose)
 {
-    struct cls_rule rule_copy;
+    struct match match_copy;
 
     parse_ofp_str(fm, command, string, verbose);
 
-    /* Normalize a copy of the rule.  This ensures that non-normalized flows
+    /* 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 that the
      * switch can do whatever it likes with the flow. */
-    rule_copy = fm->cr;
-    ofputil_normalize_rule(&rule_copy);
+    match_copy = fm->match;
+    ofputil_normalize_match(&match_copy);
 }
 
 void
@@ -769,7 +1115,7 @@ parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
     fsr->aggregate = aggregate;
     fsr->cookie = fm.cookie;
     fsr->cookie_mask = fm.cookie_mask;
-    fsr->match = fm.cr;
+    fsr->match = fm.match;
     fsr->out_port = fm.out_port;
     fsr->table_id = fm.table_id;
 }
@@ -842,6 +1188,10 @@ parse_ofp_exact_flow(struct flow *flow, const char *s)
         }
     }
 
+    if (!flow->in_port) {
+        flow->in_port = OFPP_NONE;
+    }
+
 exit:
     free(copy);