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