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