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

Reply via email to