+static bool
+is_openflow_port(ovs_be16 port_, char *ports[])
+{
+ uint16_t port = ntohs(port_);
+ if (ports[0]) {
+ int i;
+
+ for (i = 0; ports[i]; i++) {
+ if (port == atoi(ports[i])) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return port == OFP_PORT || port == OFP_OLD_PORT;
+ }
+}
+
+static void
+ofctl_ofp_parse_pcap(int argc OVS_UNUSED, char *argv[])
+{
+ struct tcp_reader *reader;
+ FILE *file;
+ int error;
+ bool first;
+
+ file = ovs_pcap_open(argv[1], "rb");
+ if (!file) {
+ ovs_fatal(errno, "%s: open failed", argv[1]);
+ }
+
+ reader = tcp_reader_open();
+ first = true;
+ for (;;) {
+ struct ofpbuf *packet;
+ long long int when;
+ struct flow flow;
+ const struct pkt_metadata md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+
+ error = ovs_pcap_read(file, &packet, &when);
+ if (error) {
+ break;
+ }
+ flow_extract(packet, &md, &flow);
+ if (flow.dl_type == htons(ETH_TYPE_IP)
+ && flow.nw_proto == IPPROTO_TCP
+ && (is_openflow_port(flow.tp_src, argv + 2) ||
+ is_openflow_port(flow.tp_dst, argv + 2))) {
+ struct ofpbuf *payload = tcp_reader_run(reader, &flow, packet);
+ if (payload) {
+ while (ofpbuf_size(payload) >= sizeof(struct ofp_header)) {
+ const struct ofp_header *oh;
+ void *data = ofpbuf_data(payload);
+ int length;
+
+ /* Align OpenFlow on 8-byte boundary for safe access. */
+ ofpbuf_shift(payload, -((intptr_t) data & 7));
+
+ oh = ofpbuf_data(payload);
+ length = ntohs(oh->length);
+ if (ofpbuf_size(payload) < length) {
+ break;
+ }
+
+ if (!first) {
+ putchar('\n');
+ }
+ first = false;
+
+ if (timestamp) {
+ char *s = xastrftime_msec("%H:%M:%S.### ", when, true);
+ fputs(s, stdout);
+ free(s);
+ }
+
+ printf(IP_FMT".%"PRIu16" > "IP_FMT".%"PRIu16":\n",
+ IP_ARGS(flow.nw_src), ntohs(flow.tp_src),
+ IP_ARGS(flow.nw_dst), ntohs(flow.tp_dst));
+ ofp_print(stdout, ofpbuf_data(payload), length, verbosity + 1);
+ ofpbuf_pull(payload, length);
+ }
+ }
+ }
+ ofpbuf_delete(packet);
+ }
+ tcp_reader_close(reader);
+}
+