#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);
ignore(fwrite(&prh, sizeof prh, 1, file));
ignore(fwrite(buf->data, buf->size, 1, file));
}
+\f
+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;
+ }
+}
#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 *);
+\f
+/* 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 */
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
.
.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"
" 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();
}
}
+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[])
{
{ "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 },