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

Reply via email to