Thanks. I pushed these patches to master.
On Fri, Dec 20, 2013 at 02:56:17PM -0800, Alex Wang wrote: > Patch 2/5 - 4/5 all look good to me, > > Tested with some sample pcap, works good (including --timestamp), > > > On Fri, Nov 22, 2013 at 1:37 PM, Ben Pfaff <b...@nicira.com> wrote: > > > Based on the number of people who ask about Wireshark support for OpenFlow, > > this is likely to be widely useful. > > > > Signed-off-by: Ben Pfaff <b...@nicira.com> > > --- > > lib/pcap-file.c | 138 > > ++++++++++++++++++++++++++++++++++++++++++++++ > > lib/pcap-file.h | 9 +++ > > utilities/ovs-ofctl.8.in | 18 +++++- > > utilities/ovs-ofctl.c | 93 ++++++++++++++++++++++++++++++- > > 4 files changed, 255 insertions(+), 3 deletions(-) > > > > diff --git a/lib/pcap-file.c b/lib/pcap-file.c > > index f13fe19..0b24f28 100644 > > --- a/lib/pcap-file.c > > +++ b/lib/pcap-file.c > > @@ -23,8 +23,12 @@ > > #include <sys/stat.h> > > #include "byte-order.h" > > #include "compiler.h" > > +#include "flow.h" > > +#include "hmap.h" > > #include "ofpbuf.h" > > +#include "packets.h" > > #include "timeval.h" > > +#include "unaligned.h" > > #include "vlog.h" > > > > VLOG_DEFINE_THIS_MODULE(pcap); > > @@ -198,3 +202,137 @@ pcap_write(FILE *file, struct ofpbuf *buf) > > ignore(fwrite(&prh, sizeof prh, 1, file)); > > ignore(fwrite(buf->data, buf->size, 1, file)); > > } > > + > > +struct tcp_key { > > + ovs_be32 nw_src, nw_dst; > > + ovs_be16 tp_src, tp_dst; > > +}; > > + > > +struct tcp_stream { > > + struct hmap_node hmap_node; > > + struct tcp_key key; > > + uint32_t seq_no; > > + struct ofpbuf payload; > > +}; > > + > > +struct tcp_reader { > > + struct hmap streams; > > +}; > > + > > +static void > > +tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream) > > +{ > > + hmap_remove(&r->streams, &stream->hmap_node); > > + ofpbuf_uninit(&stream->payload); > > + free(stream); > > +} > > + > > +/* Returns a new data structure for extracting TCP stream data from an > > + * Ethernet packet capture */ > > +struct tcp_reader * > > +tcp_reader_open(void) > > +{ > > + struct tcp_reader *r; > > + > > + r = xmalloc(sizeof *r); > > + hmap_init(&r->streams); > > + return r; > > +} > > + > > +/* Closes and frees 'r'. */ > > +void > > +tcp_reader_close(struct tcp_reader *r) > > +{ > > + struct tcp_stream *stream, *next_stream; > > + > > + HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) { > > + tcp_stream_destroy(r, stream); > > + } > > + hmap_destroy(&r->streams); > > + free(r); > > +} > > + > > +static struct tcp_stream * > > +tcp_stream_lookup(struct tcp_reader *r, const struct flow *flow) > > +{ > > + struct tcp_stream *stream; > > + struct tcp_key key; > > + uint32_t hash; > > + > > + memset(&key, 0, sizeof key); > > + key.nw_src = flow->nw_src; > > + key.nw_dst = flow->nw_dst; > > + key.tp_src = flow->tp_src; > > + key.tp_dst = flow->tp_dst; > > + hash = hash_bytes(&key, sizeof key, 0); > > + > > + HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) { > > + if (!memcmp(&stream->key, &key, sizeof key)) { > > + return stream; > > + } > > + } > > + > > + stream = xmalloc(sizeof *stream); > > + hmap_insert(&r->streams, &stream->hmap_node, hash); > > + memcpy(&stream->key, &key, sizeof key); > > + stream->seq_no = 0; > > + ofpbuf_init(&stream->payload, 2048); > > + return stream; > > +} > > + > > +/* Processes 'packet' through TCP reader 'r'. The caller must have > > already > > + * extracted the packet's headers into 'flow', using flow_extract(). > > + * > > + * If 'packet' is a TCP packet, then the reader attempts to reconstruct > > the > > + * data stream. If successful, it returns an ofpbuf that represents the > > data > > + * stream so far. The caller may examine the data in the ofpbuf and pull > > off > > + * any data that it has fully processed. The remaining data that the > > caller > > + * does not pull off will be presented again in future calls if more data > > + * arrives in the stream. > > + * > > + * Returns null if 'packet' doesn't add new data to a TCP stream. */ > > +struct ofpbuf * > > +tcp_reader_run(struct tcp_reader *r, const struct flow *flow, > > + const struct ofpbuf *packet) > > +{ > > + struct tcp_stream *stream; > > + struct tcp_header *tcp; > > + struct ofpbuf *payload; > > + uint32_t seq; > > + uint8_t flags; > > + > > + if (flow->dl_type != htons(ETH_TYPE_IP) > > + || flow->nw_proto != IPPROTO_TCP > > + || !packet->l7) { > > + return NULL; > > + } > > + > > + stream = tcp_stream_lookup(r, flow); > > + payload = &stream->payload; > > + > > + tcp = packet->l4; > > + flags = TCP_FLAGS(tcp->tcp_ctl); > > + seq = ntohl(get_16aligned_be32(&tcp->tcp_seq)); > > + if (flags & TCP_SYN) { > > + ofpbuf_clear(payload); > > + stream->seq_no = seq + 1; > > + return NULL; > > + } else if (flags & (TCP_FIN | TCP_RST)) { > > + tcp_stream_destroy(r, stream); > > + return NULL; > > + } else if (seq == stream->seq_no) { > > + size_t length; > > + > > + /* Shift all of the existing payload to the very beginning of the > > + * allocated space, so that we reuse allocated space instead of > > + * continually expanding it. */ > > + ofpbuf_shift(payload, (char *) payload->base - (char *) > > payload->data); > > + > > + length = (char *) ofpbuf_end(packet) - (char *) packet->l7; > > + ofpbuf_put(payload, packet->l7, length); > > + stream->seq_no += length; > > + return payload; > > + } else { > > + return NULL; > > + } > > +} > > diff --git a/lib/pcap-file.h b/lib/pcap-file.h > > index 7148b18..ef491e5 100644 > > --- a/lib/pcap-file.h > > +++ b/lib/pcap-file.h > > @@ -19,12 +19,21 @@ > > > > #include <stdio.h> > > > > +struct flow; > > struct ofpbuf; > > > > +/* PCAP file reading and writing. */ > > FILE *pcap_open(const char *file_name, const char *mode); > > int pcap_read_header(FILE *); > > void pcap_write_header(FILE *); > > int pcap_read(FILE *, struct ofpbuf **, long long int *when); > > void pcap_write(FILE *, struct ofpbuf *); > > + > > +/* Extracting TCP stream data from an Ethernet packet capture. */ > > + > > +struct tcp_reader *tcp_reader_open(void); > > +void tcp_reader_close(struct tcp_reader *); > > +struct ofpbuf *tcp_reader_run(struct tcp_reader *, const struct flow *, > > + const struct ofpbuf *); > > > > #endif /* pcap-file.h */ > > diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in > > index e5e488a..823ea7f 100644 > > --- a/utilities/ovs-ofctl.8.in > > +++ b/utilities/ovs-ofctl.8.in > > @@ -487,6 +487,21 @@ series of OpenFlow messages in the binary format used > > on an OpenFlow > > connection, and prints them to the console. This can be useful for > > printing OpenFlow messages captured from a TCP stream. > > . > > +.IP "\fBofp\-parse\-pcap\fR \fIfile\fR [\fIport\fR...]" > > +Reads \fIfile\fR, which must be in the PCAP format used by network > > +capture tools such as \fBtcpdump\fR or \fBwireshark\fR, extracts all > > +the TCP streams for OpenFlow connections, and prints the OpenFlow > > +messages in those connections in human-readable format on > > +\fBstdout\fR. > > +.IP > > +OpenFlow connections are distinguished by TCP port number. > > +Non-OpenFlow packets are ignored. By default, data on TCP ports 6633 > > +and 6653 are considered to be OpenFlow. Specify one or more > > +\fIport\fR arguments to override the default. > > +.IP > > +This command cannot usefully print SSL encrypted traffic. It does not > > +understand IPv6. > > +. > > .SS "Flow Syntax" > > .PP > > Some \fBovs\-ofctl\fR commands accept an argument that describes a flow or > > @@ -2003,7 +2018,8 @@ affects the \fBmonitor\fR command. > > . > > .IP "\fB\-\-timestamp\fR" > > Print a timestamp before each received packet. This option only > > -affects the \fBmonitor\fR and \fBsnoop\fR commands. > > +affects the \fBmonitor\fR, \fBsnoop\fR, and \fBofp\-parse\-pcap\fR > > +commands. > > . > > .IP "\fB\-m\fR" > > .IQ "\fB\-\-more\fR" > > diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c > > index d7c2a12..4500591 100644 > > --- a/utilities/ovs-ofctl.c > > +++ b/utilities/ovs-ofctl.c > > @@ -325,7 +325,8 @@ usage(void) > > " benchmark TARGET N COUNT bandwidth of COUNT N-byte > > echos\n" > > "SWITCH or TARGET is an active OpenFlow connection method.\n" > > "\nOther commands:\n" > > - " ofp-parse FILE print messages read from > > FILE\n", > > + " ofp-parse FILE print messages read from FILE\n" > > + " ofp-parse-pcap PCAP print OpenFlow read from > > PCAP\n", > > program_name, program_name); > > vconn_usage(true, false, false); > > daemon_usage(); > > @@ -1825,6 +1826,92 @@ ofctl_ofp_parse(int argc OVS_UNUSED, char *argv[]) > > } > > } > > > > +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 = 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; > > + > > + error = pcap_read(file, &packet, &when); > > + if (error) { > > + break; > > + } > > + flow_extract(packet, 0, 0, NULL, NULL, &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 (payload->size >= sizeof(struct ofp_header)) { > > + const struct ofp_header *oh; > > + int length; > > + > > + /* Align OpenFlow on 8-byte boundary for safe access. > > */ > > + ofpbuf_shift(payload, -((intptr_t) payload->data & > > 7)); > > + > > + oh = payload->data; > > + length = ntohs(oh->length); > > + if (payload->size < 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, payload->data, length, verbosity + > > 1); > > + ofpbuf_pull(payload, length); > > + } > > + } > > + } > > + ofpbuf_delete(packet); > > + } > > + tcp_reader_close(reader); > > +} > > + > > static void > > ofctl_ping(int argc, char *argv[]) > > { > > @@ -3365,11 +3452,13 @@ static const struct command all_commands[] = { > > { "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 }, > > > > + { "ofp-parse", 1, 1, ofctl_ofp_parse }, > > + { "ofp-parse-pcap", 1, INT_MAX, ofctl_ofp_parse_pcap }, > > + > > { "add-group", 1, 2, ofctl_add_group }, > > { "add-groups", 1, 2, ofctl_add_groups }, > > { "mod-group", 1, 2, ofctl_mod_group }, > > -- > > 1.7.10.4 > > > > _______________________________________________ > > dev mailing list > > dev@openvswitch.org > > http://openvswitch.org/mailman/listinfo/dev > > _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev