+static void
+print_differences(const void *a_, size_t a_len,
+ const void *b_, size_t b_len)
+{
+ const uint8_t *a = a_;
+ const uint8_t *b = b_;
+ size_t i;
+
+ for (i = 0; i < MIN(a_len, b_len); i++) {
+ if (a[i] != b[i]) {
+ printf("%2zu: %02"PRIx8" -> %02"PRIx8"\n", i, a[i], b[i]);
+ }
+ }
+ for (i = a_len; i < b_len; i++) {
+ printf("%2zu: (none) -> %02"PRIx8"\n", i, b[i]);
+ }
+ for (i = b_len; i < a_len; i++) {
+ printf("%2zu: %02"PRIx8" -> (none)\n", i, a[i]);
+ }
+}
+
+/* "parse-ofp10-actions": reads a series of OpenFlow 1.0 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+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)) {
+ struct ofpbuf of10_out;
+ struct ofpbuf of10_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of10_in, 0);
+ if (ofpbuf_put_hex(&of10_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of10_in.size;
+ error = ofpacts_pull_openflow10(&of10_in, of10_in.size, &ofpacts);
+ if (error) {
+ printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of10_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp10 actions and print differences from input. */
+ ofpbuf_init(&of10_out, 0);
+ ofpacts_put_openflow10(ofpacts.data, ofpacts.size, &of10_out);
+
+ print_differences(of10_in.data, of10_in.size,
+ of10_out.data, of10_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of10_in);
+ ofpbuf_uninit(&of10_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
+ * bytes from stdin, converts them to cls_rules, prints them as strings on
+ * stdout, and then converts them back to hex bytes and prints any differences
+ * from the input. */
+static void
+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)) {
+ struct ofpbuf match_in;
+ struct ofp11_match match_out;
+ struct cls_rule rule;
+ enum ofperr error;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&match_in, 0);
+ if (ofpbuf_put_hex(&match_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+ if (match_in.size != sizeof(struct ofp11_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_in.size, sizeof(struct ofp11_match));
+ }
+
+ /* Convert to cls_rule. */
+ error = ofputil_cls_rule_from_ofp11_match(match_in.data,
+ OFP_DEFAULT_PRIORITY, &rule);
+ if (error) {
+ printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&match_in);
+ continue;
+ }
+
+ /* Print cls_rule. */
+ cls_rule_print(&rule);
+
+ /* Convert back to ofp11_match and print differences from input. */
+ ofputil_cls_rule_to_ofp11_match(&rule, &match_out);
+
+ print_differences(match_in.data, match_in.size,
+ &match_out, sizeof match_out);
+ putchar('\n');
+
+ ofpbuf_uninit(&match_in);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp11-actions": reads a series of OpenFlow 1.1 action specifications
+ * as hex bytes from stdin, converts them to ofpacts, prints them as strings
+ * on stdout, and then converts them back to hex bytes and prints any
+ * differences from the input. */
+static void
+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)) {
+ struct ofpbuf of11_out;
+ struct ofpbuf of11_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of11_in, 0);
+ if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of11_in.size;
+ error = ofpacts_pull_openflow11_actions(&of11_in, of11_in.size,
+ &ofpacts);
+ if (error) {
+ printf("bad OF1.1 actions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of11_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp11 actions and print differences from input. */
+ ofpbuf_init(&of11_out, 0);
+ ofpacts_put_openflow11_actions(ofpacts.data, ofpacts.size, &of11_out);
+
+ print_differences(of11_in.data, of11_in.size,
+ of11_out.data, of11_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ ofpbuf_uninit(&of11_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "parse-ofp11-instructions": reads a series of OpenFlow 1.1 instruction
+ * specifications as hex bytes from stdin, converts them to ofpacts, prints
+ * them as strings on stdout, and then converts them back to hex bytes and
+ * prints any differences from the input. */
+static void
+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)) {
+ struct ofpbuf of11_out;
+ struct ofpbuf of11_in;
+ struct ofpbuf ofpacts;
+ enum ofperr error;
+ size_t size;
+ struct ds s;
+
+ /* Parse hex bytes. */
+ ofpbuf_init(&of11_in, 0);
+ if (ofpbuf_put_hex(&of11_in, ds_cstr(&in), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+
+ /* Convert to ofpacts. */
+ ofpbuf_init(&ofpacts, 0);
+ size = of11_in.size;
+ error = ofpacts_pull_openflow11_instructions(&of11_in, of11_in.size,
+ &ofpacts);
+ if (error) {
+ printf("bad OF1.1 instructions: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ continue;
+ }
+ ofpbuf_push_uninit(&of11_in, size);
+
+ /* Print cls_rule. */
+ ds_init(&s);
+ ofpacts_format(ofpacts.data, ofpacts.size, &s);
+ puts(ds_cstr(&s));
+ ds_destroy(&s);
+
+ /* Convert back to ofp11 instructions and print differences from
+ * input. */
+ ofpbuf_init(&of11_out, 0);
+ ofpacts_put_openflow11_instructions(ofpacts.data, ofpacts.size,
+ &of11_out);
+
+ print_differences(of11_in.data, of11_in.size,
+ of11_out.data, of11_out.size);
+ putchar('\n');
+
+ ofpbuf_uninit(&ofpacts);
+ ofpbuf_uninit(&of11_in);
+ ofpbuf_uninit(&of11_out);
+ }
+ ds_destroy(&in);
+}
+
+/* "print-error ENUM": Prints the type and code of ENUM for every OpenFlow
+ * version. */
+static void
+ofctl_print_error(int argc OVS_UNUSED, char *argv[])
+{
+ enum ofperr error;
+ int version;
+
+ error = ofperr_from_name(argv[1]);
+ if (!error) {
+ ovs_fatal(0, "unknown error \"%s\"", argv[1]);
+ }
+
+ for (version = 0; version <= UINT8_MAX; version++) {
+ const struct ofperr_domain *domain;
+
+ domain = ofperr_domain_from_version(version);
+ if (!domain) {
+ continue;
+ }
+
+ printf("%s: %d,%d\n",
+ ofperr_domain_get_name(domain),
+ ofperr_get_type(error, domain),
+ ofperr_get_code(error, domain));
+ }
+}
+
+/* "ofp-print HEXSTRING [VERBOSITY]": Converts the hex digits in HEXSTRING into
+ * binary data, interpreting them as an OpenFlow message, and prints the
+ * OpenFlow message on stdout, at VERBOSITY (level 2 by default). */
+static void
+ofctl_ofp_print(int argc, char *argv[])
+{
+ struct ofpbuf packet;
+
+ ofpbuf_init(&packet, strlen(argv[1]) / 2);
+ if (ofpbuf_put_hex(&packet, argv[1], NULL)[0] != '\0') {
+ ovs_fatal(0, "trailing garbage following hex bytes");
+ }
+ ofp_print(stdout, packet.data, packet.size, argc > 2 ? atoi(argv[2]) : 2);
+ ofpbuf_uninit(&packet);
+}
+
+static const struct command all_commands[] = {
+ { "show", 1, 1, ofctl_show },
+ { "monitor", 1, 3, ofctl_monitor },
+ { "snoop", 1, 1, ofctl_snoop },
+ { "dump-desc", 1, 1, ofctl_dump_desc },
+ { "dump-tables", 1, 1, ofctl_dump_tables },
+ { "dump-flows", 1, 2, ofctl_dump_flows },
+ { "dump-aggregate", 1, 2, ofctl_dump_aggregate },
+ { "queue-stats", 1, 3, ofctl_queue_stats },
+ { "add-flow", 2, 2, ofctl_add_flow },
+ { "add-flows", 2, 2, ofctl_add_flows },
+ { "mod-flows", 2, 2, ofctl_mod_flows },
+ { "del-flows", 1, 2, ofctl_del_flows },
+ { "replace-flows", 2, 2, ofctl_replace_flows },
+ { "diff-flows", 2, 2, ofctl_diff_flows },
+ { "packet-out", 4, INT_MAX, ofctl_packet_out },
+ { "dump-ports", 1, 2, ofctl_dump_ports },
+ { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
+ { "mod-port", 3, 3, ofctl_mod_port },
+ { "get-frags", 1, 1, ofctl_get_frags },
+ { "set-frags", 2, 2, ofctl_set_frags },
+ { "probe", 1, 1, ofctl_probe },
+ { "ping", 1, 2, ofctl_ping },
+ { "benchmark", 3, 3, ofctl_benchmark },
+ { "help", 0, INT_MAX, ofctl_help },
+
+ /* Undocumented commands for testing. */
+ { "parse-flow", 1, 1, ofctl_parse_flow },
+ { "parse-flows", 1, 1, ofctl_parse_flows },
+ { "parse-nx-match", 0, 0, ofctl_parse_nxm },
+ { "parse-nxm", 0, 0, ofctl_parse_nxm },
+ { "parse-oxm", 0, 0, ofctl_parse_oxm },
+ { "parse-ofp10-actions", 0, 0, ofctl_parse_ofp10_actions },
+ { "parse-ofp11-match", 0, 0, ofctl_parse_ofp11_match },
+ { "parse-ofp11-actions", 0, 0, ofctl_parse_ofp11_actions },
+ { "parse-ofp11-instructions", 0, 0, ofctl_parse_ofp11_instructions },
+ { "print-error", 1, 1, ofctl_print_error },
+ { "ofp-print", 1, 2, ofctl_ofp_print },
+