Didn't review closely,

I think this feature is very useful in debugging controller - ovs
communication,

The approach is reasonable and I cannot come up by other method
that does not need header synthesis.  Maybe others will have better idea.

Thanks,
Alex Wang,


On Fri, Nov 22, 2013 at 1:37 PM, Ben Pfaff <b...@nicira.com> wrote:

> Occasionally I get asked for the ability to capture all OpenFlow traffic
> in a raw format, to allow for analysis and debugging.  OVS has a few
> existing tools for this but none of them really fit the bill.  For example,
> turning up the log level for the "vconn" module will dump all traffic but
> only in a pre-parsed format, and "ovs-ofctl snoop" can only print a single
> connection at a time (also pre-parsed).  One can instead run tcpdump to
> capture all traffic, but then you run into the issue that you need the SSL
> keys to decrypt it, which is inconvenient.
>
> This commit proposes another approach: write all OpenFlow traffic to a
> .pcap file in binary format.  The really odd part of this approach is that
> OVS doesn't actually have the headers that should go into the .pcap file
> (because OVS uses the host network stack) so we have to synthesize
> plausible ones.  While that's easy enough (it's about 100 lines of code in
> this commit), it's weird enough that it seems worth doing an RFC patch for
> it.
>
> This commit is pretty preliminary in that it doesn't have a proper approach
> for configuring and enabling the feature.  Instead, it just writes
> everything that goes over a TCP stream socket to a file in the current
> directory called capture.pcap.
>
> Feedback appreciated!
> ---
>  lib/pcap-file.c       |  106
> +++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/pcap-file.h       |   15 ++++++-
>  lib/stream-fd.c       |   13 ++++++
>  lib/stream-provider.h |    1 +
>  lib/stream-tcp.c      |   14 +++++++
>  5 files changed, 148 insertions(+), 1 deletion(-)
>
> diff --git a/lib/pcap-file.c b/lib/pcap-file.c
> index 0b24f28..d19b5a9 100644
> --- a/lib/pcap-file.c
> +++ b/lib/pcap-file.c
> @@ -23,6 +23,7 @@
>  #include <sys/stat.h>
>  #include "byte-order.h"
>  #include "compiler.h"
> +#include "csum.h"
>  #include "flow.h"
>  #include "hmap.h"
>  #include "ofpbuf.h"
> @@ -336,3 +337,108 @@ tcp_reader_run(struct tcp_reader *r, const struct
> flow *flow,
>          return NULL;
>      }
>  }
> +
> +static void
> +put_tcp_packet(struct pcap_tcp *conn, int src, uint16_t tcp_flags,
> +               const void *data, size_t n)
> +{
> +    int dst = !src;
> +
> +    do {
> +        uint8_t macs[2][ETH_ADDR_LEN] = {
> +            { 0x00, 0x23, 0x20, 0xaa, 0xaa, 0xaa },
> +            { 0x00, 0x23, 0x20, 0xbb, 0xbb, 0xbb },
> +        };
> +
> +        struct eth_header *eth;
> +        struct ip_header *ip;
> +        struct tcp_header *tcp;
> +
> +        uint64_t stub[65536];
> +        struct ofpbuf packet;
> +
> +        size_t chunk = MIN(n, 64000);
> +
> +        ofpbuf_use_stack(&packet, stub, sizeof stub);
> +        ofpbuf_reserve(&packet, 2);
> +
> +        /* Ethernet header. */
> +        eth = ofpbuf_put_uninit(&packet, sizeof *eth);
> +        memcpy(eth->eth_dst, macs[dst], ETH_ADDR_LEN);
> +        memcpy(eth->eth_src, macs[src], ETH_ADDR_LEN);
> +        eth->eth_type = htons(ETH_TYPE_IP);
> +
> +        /* IP header. */
> +        ip = ofpbuf_put_uninit(&packet, sizeof *ip);
> +        ip->ip_ihl_ver = IP_IHL_VER(sizeof *ip / 4, IP_VERSION);
> +        ip->ip_tos = 0;
> +        ip->ip_tot_len = htons(sizeof *ip + sizeof *tcp + chunk);
> +        ip->ip_id = htons(0);
> +        ip->ip_frag_off = htons(0);
> +        ip->ip_ttl = 255;
> +        ip->ip_proto = IPPROTO_TCP;
> +        ip->ip_csum = htons(0);
> +        put_16aligned_be32(&ip->ip_src, conn->hosts[src]);
> +        put_16aligned_be32(&ip->ip_dst, conn->hosts[dst]);
> +        ip->ip_csum = csum(ip, sizeof *ip);
> +
> +        /* TCP header. */
> +        tcp = ofpbuf_put_uninit(&packet, sizeof *tcp);
> +        tcp->tcp_src = conn->ports[src];
> +        tcp->tcp_dst = conn->ports[dst];
> +        put_16aligned_be32(&tcp->tcp_seq, htonl(conn->seqnos[src]));
> +        put_16aligned_be32(&tcp->tcp_ack,
> +                           htonl(tcp_flags & TCP_ACK ? conn->seqnos[dst]
> : 0));
> +        tcp->tcp_ctl = TCP_CTL(tcp_flags, sizeof *tcp / 4);
> +        tcp->tcp_winsz = OVS_BE16_MAX;
> +        tcp->tcp_csum = htons(0);
> +        tcp->tcp_urg = htons(0);
> +
> +        conn->seqnos[src] += tcp_flags & (TCP_SYN | TCP_FIN) ? 1 : chunk;
> +
> +        ofpbuf_put(&packet, data, chunk);
> +
> +        pcap_write(conn->file, &packet);
> +
> +        ofpbuf_uninit(&packet);
> +
> +        data = ((const uint8_t *) data) + chunk;
> +        n -= chunk;
> +    } while (n > 0);
> +}
> +
> +void
> +pcap_tcp_open(struct pcap_tcp *conn, FILE *file,
> +              ovs_be32 hosts[2], ovs_be16 ports[2])
> +{
> +    conn->file = file;
> +    conn->hosts[0] = hosts[0];
> +    conn->hosts[1] = hosts[1];
> +    conn->ports[0] = ports[0];
> +    conn->ports[1] = ports[1];
> +    conn->seqnos[0] = random_uint32();
> +    conn->seqnos[1] = random_uint32();
> +
> +    put_tcp_packet(conn, 0, TCP_SYN, NULL, 0);
> +    put_tcp_packet(conn, 1, TCP_SYN | TCP_ACK, NULL, 0);
> +    put_tcp_packet(conn, 0, TCP_ACK, NULL, 0);
> +}
> +
> +void
> +pcap_tcp_close(struct pcap_tcp *conn)
> +{
> +    if (conn) {
> +        put_tcp_packet(conn, 0, TCP_FIN, NULL, 0);
> +        put_tcp_packet(conn, 1, TCP_FIN | TCP_ACK, NULL, 0);
> +        put_tcp_packet(conn, 0, TCP_ACK, NULL, 0);
> +    }
> +}
> +
> +void
> +pcap_tcp_send(struct pcap_tcp *conn, int src,
> +              const void *data, size_t n)
> +{
> +    if (n > 0) {
> +        put_tcp_packet(conn, src, TCP_ACK | TCP_PSH, data, n);
> +    }
> +}
> diff --git a/lib/pcap-file.h b/lib/pcap-file.h
> index ef491e5..43a9dc5 100644
> --- a/lib/pcap-file.h
> +++ b/lib/pcap-file.h
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2009 Nicira, Inc.
> + * Copyright (c) 2009, 2013 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -18,6 +18,7 @@
>  #define PCAP_FILE_H 1
>
>  #include <stdio.h>
> +#include "openvswitch/types.h"
>
>  struct flow;
>  struct ofpbuf;
> @@ -35,5 +36,17 @@ 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 *);
> +
> +struct pcap_tcp {
> +    FILE *file;
> +    ovs_be32 hosts[2];
> +    ovs_be16 ports[2];
> +    uint32_t seqnos[2];
> +};
> +
> +void pcap_tcp_open(struct pcap_tcp *, FILE *,
> +                   ovs_be32 hosts[2], ovs_be16 ports[2]);
> +void pcap_tcp_close(struct pcap_tcp *);
> +void pcap_tcp_send(struct pcap_tcp *, int src, const void *, size_t);
>
>  #endif /* pcap-file.h */
> diff --git a/lib/stream-fd.c b/lib/stream-fd.c
> index 1171f32..76da945 100644
> --- a/lib/stream-fd.c
> +++ b/lib/stream-fd.c
> @@ -24,6 +24,7 @@
>  #include <sys/types.h>
>  #include <unistd.h>
>  #include "fatal-signal.h"
> +#include "pcap-file.h"
>  #include "poll-loop.h"
>  #include "socket-util.h"
>  #include "util.h"
> @@ -77,6 +78,10 @@ static void
>  fd_close(struct stream *stream)
>  {
>      struct stream_fd *s = stream_fd_cast(stream);
> +    if (stream->conn) {
> +        pcap_tcp_close(stream->conn);
> +        free(stream->conn);
> +    }
>      close(s->fd);
>      free(s);
>  }
> @@ -95,6 +100,10 @@ fd_recv(struct stream *stream, void *buffer, size_t n)
>      ssize_t retval;
>
>      retval = read(s->fd, buffer, n);
> +    if (retval > 0 && stream->conn) {
> +        pcap_tcp_send(stream->conn, 1, buffer, retval);
> +        fflush(stream->conn->file);
> +    }
>      return retval >= 0 ? retval : -errno;
>  }
>
> @@ -105,6 +114,10 @@ fd_send(struct stream *stream, const void *buffer,
> size_t n)
>      ssize_t retval;
>
>      retval = write(s->fd, buffer, n);
> +    if (retval > 0 && stream->conn) {
> +        pcap_tcp_send(stream->conn, 0, buffer, retval);
> +        fflush(stream->conn->file);
> +    }
>      return (retval > 0 ? retval
>              : retval == 0 ? -EAGAIN
>              : -errno);
> diff --git a/lib/stream-provider.h b/lib/stream-provider.h
> index 43c63e8..1eaa669 100644
> --- a/lib/stream-provider.h
> +++ b/lib/stream-provider.h
> @@ -33,6 +33,7 @@ struct stream {
>      ovs_be16 remote_port;
>      ovs_be32 local_ip;
>      ovs_be16 local_port;
> +    struct pcap_tcp *conn;
>      char *name;
>  };
>
> diff --git a/lib/stream-tcp.c b/lib/stream-tcp.c
> index a4cdf45..9a9ed8f 100644
> --- a/lib/stream-tcp.c
> +++ b/lib/stream-tcp.c
> @@ -26,6 +26,7 @@
>  #include <sys/socket.h>
>  #include <unistd.h>
>  #include "packets.h"
> +#include "pcap-file.h"
>  #include "socket-util.h"
>  #include "util.h"
>  #include "stream-provider.h"
> @@ -36,6 +37,8 @@ VLOG_DEFINE_THIS_MODULE(stream_tcp);
>
>  /* Active TCP. */
>
> +static FILE *capture;
> +
>  static int
>  new_tcp_stream(const char *name, int fd, int connect_status,
>                 const struct sockaddr_in *remote, struct stream **streamp)
> @@ -65,6 +68,17 @@ new_tcp_stream(const char *name, int fd, int
> connect_status,
>          stream_set_remote_port(stream, remote->sin_port);
>          stream_set_local_ip(stream, local.sin_addr.s_addr);
>          stream_set_local_port(stream, local.sin_port);
> +        if (!capture) {
> +            capture = pcap_open("capture.pcap", "wb");
> +        }
> +        if (capture) {
> +            ovs_be32 hosts[2] = { local.sin_addr.s_addr,
> +                                  remote->sin_addr.s_addr };
> +            ovs_be16 ports[2] = { local.sin_port, remote->sin_port };
> +
> +            stream->conn = xmalloc(sizeof *stream->conn);
> +            pcap_tcp_open(stream->conn, capture, hosts, ports);
> +        }
>      }
>      return retval;
>  }
> --
> 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