+/* "parse-pcap PCAP": read packets from PCAP and print their flows. */
+static void
+ofctl_parse_pcap(int argc OVS_UNUSED, char *argv[])
+{
+ FILE *pcap;
+
+ pcap = ovs_pcap_open(argv[1], "rb");
+ if (!pcap) {
+ ovs_fatal(errno, "%s: open failed", argv[1]);
+ }
+
+ for (;;) {
+ struct ofpbuf *packet;
+ struct flow flow;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+ int error;
+
+ error = ovs_pcap_read(pcap, &packet, NULL);
+ if (error == EOF) {
+ break;
+ } else if (error) {
+ ovs_fatal(error, "%s: read failed", argv[1]);
+ }
+
+ flow_extract(packet, &md, &flow);
+ flow_print(stdout, &flow);
+ putchar('\n');
+ ofpbuf_delete(packet);
+ }
+}
+
+/* "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;
+ 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));
+ 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);
+ 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));
+ 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(ofpbuf_data(&nxm), 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, 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));
+ } else {
+ printf("--\n");
+ }
+ }
+ free(nxm_s);
+ ofpbuf_uninit(&nxm);
+
+ /* Convert to and from OpenFlow 1.0. */
+ ofputil_match_to_ofp10_match(&match, &of10_raw);
+ ofputil_match_from_ofp10_match(&of10_raw, &of10_match);
+ printf("OF1.0: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of10_raw.dl_vlan),
+ (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN)) != 0,
+ of10_raw.dl_vlan_pcp,
+ (of10_raw.wildcards & htonl(OFPFW10_DL_VLAN_PCP)) != 0,
+ ntohs(of10_match.flow.vlan_tci),
+ ntohs(of10_match.wc.masks.vlan_tci));
+
+ /* Convert to and from OpenFlow 1.1. */
+ ofputil_match_to_ofp11_match(&match, &of11_raw);
+ ofputil_match_from_ofp11_match(&of11_raw, &of11_match);
+ printf("OF1.1: %04"PRIx16"/%d,%02"PRIx8"/%d -> %04"PRIx16"/%04"PRIx16"\n",
+ ntohs(of11_raw.dl_vlan),
+ (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN)) != 0,
+ of11_raw.dl_vlan_pcp,
+ (of11_raw.wildcards & htonl(OFPFW11_DL_VLAN_PCP)) != 0,
+ ntohs(of11_match.flow.vlan_tci),
+ ntohs(of11_match.wc.masks.vlan_tci));
+}
+