[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