openflow-1.1+: OFPT_TABLE_MOD (part 1)
[sliver-openvswitch.git] / utilities / ovs-ofctl.c
index 28e3863..95bf1bf 100644 (file)
@@ -98,7 +98,7 @@ struct sort_criterion {
 static struct sort_criterion *criteria;
 static size_t n_criteria, allocated_criteria;
 
-static const struct command all_commands[];
+static const struct command *get_all_commands(void);
 
 static void usage(void) NO_RETURN;
 static void parse_options(int argc, char *argv[]);
@@ -113,7 +113,7 @@ main(int argc, char *argv[])
     set_program_name(argv[0]);
     parse_options(argc, argv);
     signal(SIGPIPE, SIG_IGN);
-    run_command(argc - optind, argv + optind, all_commands);
+    run_command(argc - optind, argv + optind, get_all_commands());
     return 0;
 }
 
@@ -281,6 +281,7 @@ usage(void)
            "  dump-desc SWITCH            print switch description\n"
            "  dump-tables SWITCH          print table stats\n"
            "  mod-port SWITCH IFACE ACT   modify port behavior\n"
+           "  mod-table SWITCH MOD        modify flow table behavior\n"
            "  get-frags SWITCH            print fragment handling behavior\n"
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
@@ -301,11 +302,20 @@ usage(void)
            "  monitor SWITCH [MISSLEN] [invalid_ttl] [watch:[...]]\n"
            "                              print packets received from SWITCH\n"
            "  snoop SWITCH                snoop on SWITCH and its controller\n"
+           "  add-group SWITCH GROUP      add group described by GROUP\n"
+           "  add-group SWITCH FILE       add group from FILE\n"
+           "  mod-group SWITCH GROUP      modify specific group\n"
+           "  del-groups SWITCH [GROUP]   delete matching GROUPs\n"
+           "  dump-group-features SWITCH  print group features\n"
+           "  dump-groups SWITCH          print group description\n"
+           "  dump-group-stats SWITCH [GROUP]  print group statistics\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
            "  benchmark TARGET N COUNT    bandwidth of COUNT N-byte echos\n"
-           "where SWITCH or TARGET is an active OpenFlow connection method.\n",
+           "SWITCH or TARGET is an active OpenFlow connection method.\n"
+           "\nOther commands:\n"
+           "  ofp-parse FILE              print messages read from FILE\n",
            program_name, program_name);
     vconn_usage(true, false, false);
     daemon_usage();
@@ -361,7 +371,7 @@ open_vconn_socket(const char *name, struct vconn **vconnp)
                        vconnp);
     if (error && error != ENOENT) {
         ovs_fatal(0, "%s: failed to open socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
     free(vconn_name);
 
@@ -412,7 +422,7 @@ open_vconn__(const char *name, enum open_target target,
     error = vconn_connect_block(*vconnp);
     if (error) {
         ovs_fatal(0, "%s: failed to connect to socket (%s)", name,
-                  strerror(error));
+                  ovs_strerror(error));
     }
 
     ofp_version = vconn_get_version(*vconnp);
@@ -866,9 +876,14 @@ prepare_dump_flows(int argc, char *argv[], bool aggregate,
     enum ofputil_protocol usable_protocols, protocol;
     struct ofputil_flow_stats_request fsr;
     struct vconn *vconn;
+    char *error;
 
-    parse_ofp_flow_stats_request_str(&fsr, aggregate, argc > 2 ? argv[2] : "");
-    usable_protocols = ofputil_flow_stats_request_usable_protocols(&fsr);
+    error = parse_ofp_flow_stats_request_str(&fsr, aggregate,
+                                             argc > 2 ? argv[2] : "",
+                                             &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
     protocol = open_vconn(argv[1], &vconn);
     protocol = set_protocol_for_flow_dump(vconn, protocol, usable_protocols);
@@ -1023,17 +1038,13 @@ ofctl_queue_stats(int argc, char *argv[])
 }
 
 static enum ofputil_protocol
-open_vconn_for_flow_mod(const char *remote,
-                        const struct ofputil_flow_mod *fms, size_t n_fms,
-                        struct vconn **vconnp)
+open_vconn_for_flow_mod(const char *remote, struct vconn **vconnp,
+                        enum ofputil_protocol usable_protocols)
 {
-    enum ofputil_protocol usable_protocols;
     enum ofputil_protocol cur_protocol;
     char *usable_s;
     int i;
 
-    /* Figure out what flow formats will work. */
-    usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
     if (!(usable_protocols & allowed_protocols)) {
         char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
         usable_s = ofputil_protocols_to_string(usable_protocols);
@@ -1065,13 +1076,13 @@ open_vconn_for_flow_mod(const char *remote,
 
 static void
 ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
-                 size_t n_fms)
+                 size_t n_fms, enum ofputil_protocol usable_protocols)
 {
     enum ofputil_protocol protocol;
     struct vconn *vconn;
     size_t i;
 
-    protocol = open_vconn_for_flow_mod(remote, fms, n_fms, &vconn);
+    protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
 
     for (i = 0; i < n_fms; i++) {
         struct ofputil_flow_mod *fm = &fms[i];
@@ -1085,23 +1096,37 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
 static void
 ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms);
-    ofctl_flow_mod__(argv[1], fms, n_fms);
+    error = parse_ofp_flow_mod_file(argv[2], command, &fms, &n_fms,
+                                    &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
     free(fms);
 }
 
 static void
 ofctl_flow_mod(int argc, char *argv[], uint16_t command)
 {
+    enum ofputil_protocol usable_protocols;
+
     if (argc > 2 && !strcmp(argv[2], "-")) {
         ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
-        parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command, false);
-        ofctl_flow_mod__(argv[1], &fm, 1);
+        char *error;
+
+        error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
+                                       &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
+        ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
     }
 }
 
@@ -1233,7 +1258,7 @@ ofctl_send(struct unixctl_conn *conn, int argc,
         error = vconn_send_block(vconn, msg);
         if (error) {
             ofpbuf_delete(msg);
-            ds_put_format(&reply, "%s\n", strerror(error));
+            ds_put_format(&reply, "%s\n", ovs_strerror(error));
             ok = false;
         } else {
             ds_put_cstr(&reply, "sent\n");
@@ -1270,7 +1295,7 @@ ofctl_barrier(struct unixctl_conn *conn, int argc OVS_UNUSED,
     error = vconn_send_block(aux->vconn, msg);
     if (error) {
         ofpbuf_delete(msg);
-        unixctl_command_reply_error(conn, strerror(error));
+        unixctl_command_reply_error(conn, ovs_strerror(error));
     } else {
         aux->conn = conn;
     }
@@ -1284,7 +1309,7 @@ ofctl_set_output_file(struct unixctl_conn *conn, int argc OVS_UNUSED,
 
     fd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
     if (fd < 0) {
-        unixctl_command_reply_error(conn, strerror(errno));
+        unixctl_command_reply_error(conn, ovs_strerror(errno));
         return;
     }
 
@@ -1423,6 +1448,7 @@ ofctl_monitor(int argc, char *argv[])
 {
     struct vconn *vconn;
     int i;
+    enum ofputil_protocol usable_protocols;
 
     open_vconn(argv[1], &vconn);
     for (i = 2; i < argc; i++) {
@@ -1439,8 +1465,13 @@ ofctl_monitor(int argc, char *argv[])
         } else if (!strncmp(arg, "watch:", 6)) {
             struct ofputil_flow_monitor_request fmr;
             struct ofpbuf *msg;
+            char *error;
 
-            parse_flow_monitor_request(&fmr, arg + 6);
+            error = parse_flow_monitor_request(&fmr, arg + 6,
+                                               &usable_protocols);
+            if (error) {
+                ovs_fatal(0, "%s", error);
+            }
 
             msg = ofpbuf_new(0);
             ofputil_append_flow_monitor_request(&fmr, msg);
@@ -1538,10 +1569,15 @@ ofctl_packet_out(int argc, char *argv[])
     struct ofputil_packet_out po;
     struct ofpbuf ofpacts;
     struct vconn *vconn;
+    char *error;
     int i;
+    enum ofputil_protocol usable_protocols; /* TODO: Use in proto selection */
 
     ofpbuf_init(&ofpacts, 64);
-    parse_ofpacts(argv[3], &ofpacts);
+    error = parse_ofpacts(argv[3], &ofpacts, &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
 
     po.buffer_id = UINT32_MAX;
     po.in_port = str_to_port_no(argv[1], argv[2]);
@@ -1628,6 +1664,42 @@ found:
     vconn_close(vconn);
 }
 
+static void
+ofctl_mod_table(int argc OVS_UNUSED, char *argv[])
+{
+    enum ofputil_protocol protocol, usable_protocols;
+    struct ofputil_table_mod tm;
+    struct vconn *vconn;
+    char *error;
+    int i;
+
+    error = parse_ofp_table_mod(&tm, argv[2], argv[3], &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
+    protocol = open_vconn(argv[1], &vconn);
+    if (!(protocol & usable_protocols)) {
+        for (i = 0; i < sizeof(enum ofputil_protocol) * CHAR_BIT; i++) {
+            enum ofputil_protocol f = 1 << i;
+            if (f != protocol
+                && f & usable_protocols
+                && try_set_protocol(vconn, f, &protocol)) {
+                protocol = f;
+                break;
+            }
+        }
+    }
+
+    if (!(protocol & usable_protocols)) {
+        char *usable_s = ofputil_protocols_to_string(usable_protocols);
+        ovs_fatal(0, "Switch does not support table mod message(%s)", usable_s);
+    }
+
+    transact_noreply(vconn, ofputil_encode_table_mod(&tm, protocol));
+    vconn_close(vconn);
+}
+
 static void
 ofctl_get_frags(int argc OVS_UNUSED, char *argv[])
 {
@@ -1673,6 +1745,56 @@ ofctl_set_frags(int argc OVS_UNUSED, char *argv[])
     vconn_close(vconn);
 }
 
+static void
+ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[])
+{
+    const char *filename = argv[1];
+    struct ofpbuf b;
+    FILE *file;
+
+    file = !strcmp(filename, "-") ? stdin : fopen(filename, "r");
+    if (file == NULL) {
+        ovs_fatal(errno, "%s: open", filename);
+    }
+
+    ofpbuf_init(&b, 65536);
+    for (;;) {
+        struct ofp_header *oh;
+        size_t length, tail_len;
+        void *tail;
+        size_t n;
+
+        ofpbuf_clear(&b);
+        oh = ofpbuf_put_uninit(&b, sizeof *oh);
+        n = fread(oh, 1, sizeof *oh, file);
+        if (n == 0) {
+            break;
+        } else if (n < sizeof *oh) {
+            ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+        }
+
+        length = ntohs(oh->length);
+        if (length < sizeof *oh) {
+            ovs_fatal(0, "%s: %zu-byte message is too short for OpenFlow",
+                      filename, length);
+        }
+
+        tail_len = length - sizeof *oh;
+        tail = ofpbuf_put_uninit(&b, tail_len);
+        n = fread(tail, 1, tail_len, file);
+        if (n < tail_len) {
+            ovs_fatal(0, "%s: unexpected end of file mid-message", filename);
+        }
+
+        ofp_print(stdout, b.data, b.size, verbosity + 2);
+    }
+    ofpbuf_uninit(&b);
+
+    if (file != stdin) {
+        fclose(file);
+    }
+}
+
 static void
 ofctl_ping(int argc, char *argv[])
 {
@@ -1764,6 +1886,153 @@ ofctl_benchmark(int argc OVS_UNUSED, char *argv[])
            count * message_size / (duration / 1000.0));
 }
 
+static void
+ofctl_group_mod__(const char *remote, struct ofputil_group_mod *gms,
+                 size_t n_gms)
+{
+    struct ofputil_group_mod *gm;
+    struct ofpbuf *request;
+
+    struct vconn *vconn;
+    size_t i;
+
+    open_vconn(remote, &vconn);
+
+    for (i = 0; i < n_gms; i++) {
+        gm = &gms[i];
+        request = ofputil_encode_group_mod(vconn_get_version(vconn), gm);
+        if (request) {
+            transact_noreply(vconn, request);
+        }
+    }
+
+    vconn_close(vconn);
+
+}
+
+
+static void
+ofctl_group_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
+{
+    struct ofputil_group_mod *gms = NULL;
+    enum ofputil_protocol usable_protocols;
+    size_t n_gms = 0;
+    char *error;
+
+    error = parse_ofp_group_mod_file(argv[2], command, &gms, &n_gms,
+                                     &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    ofctl_group_mod__(argv[1], gms, n_gms);
+    free(gms);
+}
+
+static void
+ofctl_group_mod(int argc, char *argv[], uint16_t command)
+{
+    if (argc > 2 && !strcmp(argv[2], "-")) {
+        ofctl_group_mod_file(argc, argv, command);
+    } else {
+        enum ofputil_protocol usable_protocols;
+        struct ofputil_group_mod gm;
+        char *error;
+
+        error = parse_ofp_group_mod_str(&gm, command, argc > 2 ? argv[2] : "",
+                                        &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
+        ofctl_group_mod__(argv[1], &gm, 1);
+    }
+}
+
+static void
+ofctl_add_group(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_add_groups(int argc, char *argv[])
+{
+    ofctl_group_mod_file(argc, argv, OFPGC11_ADD);
+}
+
+static void
+ofctl_mod_group(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_MODIFY);
+}
+
+static void
+ofctl_del_groups(int argc, char *argv[])
+{
+    ofctl_group_mod(argc, argv, OFPGC11_DELETE);
+}
+
+static void
+ofctl_dump_group_stats(int argc, char *argv[])
+{
+    enum ofputil_protocol usable_protocols;
+    struct ofputil_group_mod gm;
+    struct ofpbuf *request;
+    struct vconn *vconn;
+    uint32_t group_id;
+    char *error;
+
+    memset(&gm, 0, sizeof gm);
+
+    error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
+                                    argc > 2 ? argv[2] : "",
+                                    &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+
+    group_id = gm.group_id;
+
+    open_vconn(argv[1], &vconn);
+    request = ofputil_encode_group_stats_request(vconn_get_version(vconn),
+                                                 group_id);
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_desc(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+
+    request = ofputil_encode_group_desc_request(vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
+static void
+ofctl_dump_group_features(int argc OVS_UNUSED, char *argv[])
+{
+    struct ofpbuf *request;
+    struct vconn *vconn;
+
+    open_vconn(argv[1], &vconn);
+    request = ofputil_encode_group_features_request(vconn_get_version(vconn));
+    if (request) {
+        dump_stats_transaction(vconn, request);
+    }
+
+    vconn_close(vconn);
+}
+
 static void
 ofctl_help(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 {
@@ -1866,11 +2135,13 @@ fte_free_all(struct classifier *cls)
     struct cls_cursor cursor;
     struct fte *fte, *next;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     cls_cursor_init(&cursor, cls, NULL);
     CLS_CURSOR_FOR_EACH_SAFE (fte, next, rule, &cursor) {
         classifier_remove(cls, &fte->rule);
         fte_free(fte);
     }
+    ovs_rwlock_unlock(&cls->rwlock);
     classifier_destroy(cls);
 }
 
@@ -1889,7 +2160,9 @@ fte_insert(struct classifier *cls, const struct match *match,
     cls_rule_init(&fte->rule, match, priority);
     fte->versions[index] = version;
 
+    ovs_rwlock_wrlock(&cls->rwlock);
     old = fte_from_cls_rule(classifier_replace(cls, &fte->rule));
+    ovs_rwlock_unlock(&cls->rwlock);
     if (old) {
         fte_version_free(old->versions[index]);
         fte->versions[!index] = old->versions[!index];
@@ -1905,6 +2178,7 @@ static enum ofputil_protocol
 read_flows_from_file(const char *filename, struct classifier *cls, int index)
 {
     enum ofputil_protocol usable_protocols;
+    int line_number;
     struct ds s;
     FILE *file;
 
@@ -1915,22 +2189,28 @@ read_flows_from_file(const char *filename, struct classifier *cls, int index)
 
     ds_init(&s);
     usable_protocols = OFPUTIL_P_ANY;
-    while (!ds_get_preprocessed_line(&s, file)) {
+    line_number = 0;
+    while (!ds_get_preprocessed_line(&s, file, &line_number)) {
         struct fte_version *version;
         struct ofputil_flow_mod fm;
+        char *error;
+        enum ofputil_protocol usable;
 
-        parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), true);
+        error = parse_ofp_str(&fm, OFPFC_ADD, ds_cstr(&s), &usable);
+        if (error) {
+            ovs_fatal(0, "%s:%d: %s", filename, line_number, error);
+        }
+        usable_protocols &= usable;
 
         version = xmalloc(sizeof *version);
         version->cookie = fm.new_cookie;
         version->idle_timeout = fm.idle_timeout;
         version->hard_timeout = fm.hard_timeout;
-        version->flags = fm.flags & (OFPFF_SEND_FLOW_REM | OFPFF10_EMERG);
+        version->flags = fm.flags & (OFPUTIL_FF_SEND_FLOW_REM
+                                     | OFPUTIL_FF_EMERG);
         version->ofpacts = fm.ofpacts;
         version->ofpacts_len = fm.ofpacts_len;
 
-        usable_protocols &= ofputil_usable_protocols(&fm.match);
-
         fte_insert(cls, &fm.match, fm.priority, version, index);
     }
     ds_destroy(&s);
@@ -2050,6 +2330,7 @@ fte_make_flow_mod(const struct fte *fte, int index, uint16_t command,
     fm.cookie = htonll(0);
     fm.cookie_mask = htonll(0);
     fm.new_cookie = version->cookie;
+    fm.modify_cookie = true;
     fm.table_id = 0xff;
     fm.command = command;
     fm.idle_timeout = version->idle_timeout;
@@ -2092,6 +2373,7 @@ ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
     list_init(&requests);
 
     /* Delete flows that exist on the switch but not in the file. */
+    ovs_rwlock_rdlock(&cls.rwlock);
     cls_cursor_init(&cursor, &cls, NULL);
     CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
         struct fte_version *file_ver = fte->versions[FILE_IDX];
@@ -2115,6 +2397,7 @@ ofctl_replace_flows(int argc OVS_UNUSED, char *argv[])
             fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
         }
     }
+    ovs_rwlock_unlock(&cls.rwlock);
     transact_multiple_noreply(vconn, &requests);
     vconn_close(vconn);
 
@@ -2156,6 +2439,7 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
     ds_init(&a_s);
     ds_init(&b_s);
 
+    ovs_rwlock_rdlock(&cls.rwlock);
     cls_cursor_init(&cursor, &cls, NULL);
     CLS_CURSOR_FOR_EACH (fte, rule, &cursor) {
         struct fte_version *a = fte->versions[0];
@@ -2175,6 +2459,7 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
             }
         }
     }
+    ovs_rwlock_unlock(&cls.rwlock);
 
     ds_destroy(&a_s);
     ds_destroy(&b_s);
@@ -2189,14 +2474,13 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
 /* Undocumented commands for unit testing. */
 
 static void
-ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
+ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms,
+                    enum ofputil_protocol usable_protocols)
 {
-    enum ofputil_protocol usable_protocols;
     enum ofputil_protocol protocol = 0;
     char *usable_s;
     size_t i;
 
-    usable_protocols = ofputil_flow_mod_usable_protocols(fms, n_fms);
     usable_s = ofputil_protocols_to_string(usable_protocols);
     printf("usable protocols: %s\n", usable_s);
     free(usable_s);
@@ -2231,10 +2515,15 @@ ofctl_parse_flows__(struct ofputil_flow_mod *fms, size_t n_fms)
 static void
 ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod fm;
+    char *error;
 
-    parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, false);
-    ofctl_parse_flows__(&fm, 1);
+    error = parse_ofp_flow_mod_str(&fm, argv[1], OFPFC_ADD, &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    ofctl_parse_flows__(&fm, 1, usable_protocols);
 }
 
 /* "parse-flows FILENAME": reads the named file as a sequence of flows (like
@@ -2242,11 +2531,17 @@ ofctl_parse_flow(int argc OVS_UNUSED, char *argv[])
 static void
 ofctl_parse_flows(int argc OVS_UNUSED, char *argv[])
 {
+    enum ofputil_protocol usable_protocols;
     struct ofputil_flow_mod *fms = NULL;
     size_t n_fms = 0;
+    char *error;
 
-    parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms);
-    ofctl_parse_flows__(fms, n_fms);
+    error = parse_ofp_flow_mod_file(argv[1], OFPFC_ADD, &fms, &n_fms,
+                                    &usable_protocols);
+    if (error) {
+        ovs_fatal(0, "%s", error);
+    }
+    ofctl_parse_flows__(fms, n_fms, usable_protocols);
     free(fms);
 }
 
@@ -2297,7 +2592,7 @@ ofctl_parse_nxm__(bool oxm)
             ofpbuf_init(&nx_match, 0);
             if (oxm) {
                 match_len = oxm_put_match(&nx_match, &match);
-                out = oxm_match_to_string(nx_match.data, match_len);
+                out = oxm_match_to_string(&nx_match, match_len);
             } else {
                 match_len = nx_put_match(&nx_match, &match,
                                          cookie, cookie_mask);
@@ -2367,7 +2662,7 @@ ofctl_parse_ofp10_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of10_out;
         struct ofpbuf of10_in;
         struct ofpbuf ofpacts;
@@ -2431,7 +2726,7 @@ ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
 
     ds_init(&in);
     ds_init(&expout);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in, match_expout;
         struct ofp10_match match_out;
         struct ofp10_match match_normal;
@@ -2503,7 +2798,7 @@ ofctl_parse_ofp11_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf match_in;
         struct ofp11_match match_out;
         struct match match;
@@ -2552,7 +2847,7 @@ ofctl_parse_ofp11_actions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2610,7 +2905,7 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     struct ds in;
 
     ds_init(&in);
-    while (!ds_get_preprocessed_line(&in, stdin)) {
+    while (!ds_get_preprocessed_line(&in, stdin, NULL)) {
         struct ofpbuf of11_out;
         struct ofpbuf of11_in;
         struct ofpbuf ofpacts;
@@ -2637,9 +2932,15 @@ ofctl_parse_ofp11_instructions(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
         /* Convert to ofpacts. */
         ofpbuf_init(&ofpacts, 0);
         size = of11_in.size;
-        error = ofpacts_pull_openflow11_instructions(
-            &of11_in, of11_in.size, table_id ? atoi(table_id) : 0,
-            &ofpacts);
+        error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
+                                                     &ofpacts);
+        if (!error) {
+            /* Verify actions. */
+            struct flow flow;
+            memset(&flow, 0, sizeof flow);
+            error = ofpacts_check(ofpacts.data, ofpacts.size, &flow, OFPP_MAX,
+                                  table_id ? atoi(table_id) : 0);
+        }
         if (error) {
             printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
             ofpbuf_uninit(&ofpacts);
@@ -2693,6 +2994,9 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     struct match of11_match;
 
     enum ofperr error;
+    char *error_s;
+
+    enum ofputil_protocol usable_protocols; /* Unused for now. */
 
     match_init_catchall(&match);
     match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
@@ -2702,7 +3006,10 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
     printf("%s -> ", string_s);
     fflush(stdout);
-    parse_ofp_str(&fm, -1, string_s, false);
+    error_s = parse_ofp_str(&fm, -1, string_s, &usable_protocols);
+    if (error_s) {
+        ovs_fatal(0, "%s", error_s);
+    }
     printf("%04"PRIx16"/%04"PRIx16"\n",
            ntohs(fm.match.flow.vlan_tci),
            ntohs(fm.match.wc.masks.vlan_tci));
@@ -2727,7 +3034,7 @@ ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
     /* Convert to and from OXM. */
     ofpbuf_init(&nxm, 0);
     nxm_match_len = oxm_put_match(&nxm, &match);
-    nxm_s = oxm_match_to_string(nxm.data, nxm_match_len);
+    nxm_s = oxm_match_to_string(&nxm, nxm_match_len);
     error = oxm_pull_match(&nxm, &nxm_match);
     printf("OXM: %s -> ", nxm_s);
     if (error) {
@@ -2786,13 +3093,16 @@ ofctl_print_error(int argc OVS_UNUSED, char *argv[])
 
     for (version = 0; version <= UINT8_MAX; version++) {
         const char *name = ofperr_domain_get_name(version);
-        if (!name) {
-            continue;
+        if (name) {
+            int vendor = ofperr_get_vendor(error, version);
+            int type = ofperr_get_type(error, version);
+            int code = ofperr_get_code(error, version);
+
+            if (vendor != -1 || type != -1 || code != -1) {
+                printf("%s: vendor %#x, type %d, code %d\n",
+                       name, vendor, type, code);
+            }
         }
-        printf("%s: %d,%d\n",
-               ofperr_domain_get_name(version),
-               ofperr_get_type(error, version),
-               ofperr_get_code(error, version));
     }
 }
 
@@ -2879,11 +3189,21 @@ static const struct command all_commands[] = {
     { "dump-ports", 1, 2, ofctl_dump_ports },
     { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
     { "mod-port", 3, 3, ofctl_mod_port },
+    { "mod-table", 3, 3, ofctl_mod_table },
     { "get-frags", 1, 1, ofctl_get_frags },
     { "set-frags", 2, 2, ofctl_set_frags },
+    { "ofp-parse", 1, 1, ofctl_ofp_parse },
     { "probe", 1, 1, ofctl_probe },
     { "ping", 1, 2, ofctl_ping },
     { "benchmark", 3, 3, ofctl_benchmark },
+
+    { "add-group", 1, 2, ofctl_add_group },
+    { "add-groups", 1, 2, ofctl_add_groups },
+    { "mod-group", 1, 2, ofctl_mod_group },
+    { "del-groups", 1, 2, ofctl_del_groups },
+    { "dump-groups", 1, 1, ofctl_dump_group_desc },
+    { "dump-group-stats", 1, 2, ofctl_dump_group_stats },
+    { "dump-group-features", 1, 1, ofctl_dump_group_features },
     { "help", 0, INT_MAX, ofctl_help },
 
     /* Undocumented commands for testing. */
@@ -2905,3 +3225,8 @@ static const struct command all_commands[] = {
 
     { NULL, 0, 0, NULL },
 };
+
+static const struct command *get_all_commands(void)
+{
+    return all_commands;
+}