[ovs-dev] [tcpdump 4/5] ovs-ofctl: New command "ofp-parse-pcap" to dump OpenFlow from PCAP files.
Alex Wang
alexw at nicira.com
Fri Dec 20 22:56:17 UTC 2013
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 <blp at 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 <blp at 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 at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openvswitch.org/pipermail/ovs-dev/attachments/20131220/cff7b297/attachment-0003.html>
More information about the dev
mailing list