+ }
+ 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-ofp10-match": reads a series of ofp10_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.
+ *
+ * The input hex bytes may contain "x"s to represent "don't-cares", bytes whose
+ * values are ignored in the input and will be set to zero when OVS converts
+ * them back to hex bytes. ovs-ofctl actually sets "x"s to random bits when
+ * it does the conversion to hex, to ensure that in fact they are ignored. */
+static void
+ofctl_parse_ofp10_match(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ struct ds expout;
+ struct ds in;
+
+ ds_init(&in);
+ ds_init(&expout);
+ while (!ds_get_preprocessed_line(&in, stdin)) {
+ struct ofpbuf match_in, match_expout;
+ struct ofp10_match match_out;
+ struct ofp10_match match_normal;
+ struct match match;
+ char *p;
+
+ /* Parse hex bytes to use for expected output. */
+ ds_clear(&expout);
+ ds_put_cstr(&expout, ds_cstr(&in));
+ for (p = ds_cstr(&expout); *p; p++) {
+ if (*p == 'x') {
+ *p = '0';
+ }
+ }
+ ofpbuf_init(&match_expout, 0);
+ if (ofpbuf_put_hex(&match_expout, ds_cstr(&expout), NULL)[0] != '\0') {
+ ovs_fatal(0, "Trailing garbage in hex data");
+ }
+ if (match_expout.size != sizeof(struct ofp10_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_expout.size, sizeof(struct ofp10_match));
+ }
+
+ /* Parse hex bytes for input. */
+ for (p = ds_cstr(&in); *p; p++) {
+ if (*p == 'x') {
+ *p = "0123456789abcdef"[random_uint32() & 0xf];
+ }
+ }
+ 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 ofp10_match)) {
+ ovs_fatal(0, "Input is %zu bytes, expected %zu",
+ match_in.size, sizeof(struct ofp10_match));
+ }
+
+ /* Convert to cls_rule and print. */
+ ofputil_match_from_ofp10_match(match_in.data, &match);
+ match_print(&match);
+
+ /* Convert back to ofp10_match and print differences from input. */
+ ofputil_match_to_ofp10_match(&match, &match_out);
+ print_differences("", match_expout.data, match_expout.size,
+ &match_out, sizeof match_out);
+
+ /* Normalize, then convert and compare again. */
+ ofputil_normalize_match(&match);
+ ofputil_match_to_ofp10_match(&match, &match_normal);
+ print_differences("normal: ", &match_out, sizeof match_out,
+ &match_normal, sizeof match_normal);
+ putchar('\n');
+
+ ofpbuf_uninit(&match_in);
+ ofpbuf_uninit(&match_expout);
+ }
+ ds_destroy(&in);
+ ds_destroy(&expout);
+}
+
+/* "parse-ofp11-match": reads a series of ofp11_match specifications as hex
+ * bytes from stdin, converts them to "struct match"es, 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 match match;
+ 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 match. */
+ error = ofputil_match_from_ofp11_match(match_in.data, &match);
+ if (error) {
+ printf("bad ofp11_match: %s\n\n", ofperr_get_name(error));
+ ofpbuf_uninit(&match_in);
+ continue;
+ }
+
+ /* Print match. */
+ match_print(&match);
+
+ /* Convert back to ofp11_match and print differences from input. */
+ ofputil_match_to_ofp11_match(&match, &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);
+}
+
+/* "check-vlan VLAN_TCI VLAN_TCI_MASK": converts the specified vlan_tci and
+ * mask values to and from various formats and prints the results. */
+static void
+ofctl_check_vlan(int argc OVS_UNUSED, char *argv[])
+{
+ struct match match;
+
+ char *string_s;
+ struct ofputil_flow_mod fm;
+
+ struct ofpbuf nxm;
+ struct match nxm_match;
+ int nxm_match_len;
+ char *nxm_s;
+
+ struct ofp10_match of10_raw;
+ struct match of10_match;
+
+ struct ofp11_match of11_raw;
+ struct match of11_match;
+
+ enum ofperr error;
+
+ match_init_catchall(&match);
+ match.flow.vlan_tci = htons(strtoul(argv[1], NULL, 16));
+ match.wc.masks.vlan_tci = htons(strtoul(argv[2], NULL, 16));
+
+ /* Convert to and from string. */
+ string_s = match_to_string(&match, OFP_DEFAULT_PRIORITY);
+ printf("%s -> ", string_s);
+ fflush(stdout);
+ parse_ofp_str(&fm, -1, string_s, false);
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(fm.match.flow.vlan_tci),
+ ntohs(fm.match.wc.masks.vlan_tci));
+ free(string_s);
+
+ /* Convert to and from NXM. */
+ ofpbuf_init(&nxm, 0);
+ nxm_match_len = nx_put_match(&nxm, &match, htonll(0), htonll(0));
+ nxm_s = nx_match_to_string(nxm.data, nxm_match_len);
+ error = nx_pull_match(&nxm, nxm_match_len, &nxm_match, NULL, NULL);
+ printf("NXM: %s -> ", nxm_s);
+ if (error) {
+ printf("%s\n", ofperr_to_string(error));
+ } else {
+ printf("%04"PRIx16"/%04"PRIx16"\n",
+ ntohs(nxm_match.flow.vlan_tci),
+ ntohs(nxm_match.wc.masks.vlan_tci));
+ }
+ free(nxm_s);
+ ofpbuf_uninit(&nxm);
+
+ /* 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);
+ error = oxm_pull_match(&nxm, &nxm_match);
+ printf("OXM: %s -> ", nxm_s);
+ if (error) {
+ printf("%s\n", ofperr_to_string(error));
+ } else {
+ uint16_t vid = ntohs(nxm_match.flow.vlan_tci) &
+ (VLAN_VID_MASK | VLAN_CFI);
+ uint16_t mask = ntohs(nxm_match.wc.masks.vlan_tci) &
+ (VLAN_VID_MASK | VLAN_CFI);
+
+ printf("%04"PRIx16"/%04"PRIx16",", vid, mask);
+ if (vid && vlan_tci_to_pcp(nxm_match.wc.masks.vlan_tci)) {
+ printf("%02"PRIx8"\n", vlan_tci_to_pcp(nxm_match.flow.vlan_tci));