X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fpcap-file.c;h=191e690e637815801269054bc286e9580a5cbbab;hb=28c5588e8e1a8d091c5d2275232c35f2968a97fa;hp=700cdea677151d59846869880a27f238010c21e7;hpb=b6cdfbb4a37884151809ec2090058c8caebf60aa;p=sliver-openvswitch.git diff --git a/lib/pcap-file.c b/lib/pcap-file.c index 700cdea67..191e690e6 100644 --- a/lib/pcap-file.c +++ b/lib/pcap-file.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2010, 2012, 2013 Nicira, Inc. + * Copyright (c) 2009, 2010, 2012, 2013, 2014 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,8 +21,14 @@ #include #include #include +#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); @@ -47,7 +53,7 @@ struct pcaprec_hdr { BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16); FILE * -pcap_open(const char *file_name, const char *mode) +ovs_pcap_open(const char *file_name, const char *mode) { struct stat s; FILE *file; @@ -69,7 +75,7 @@ pcap_open(const char *file_name, const char *mode) switch (mode[0]) { case 'r': - error = pcap_read_header(file); + error = ovs_pcap_read_header(file); if (error) { errno = error; fclose(file); @@ -78,23 +84,23 @@ pcap_open(const char *file_name, const char *mode) break; case 'w': - pcap_write_header(file); + ovs_pcap_write_header(file); break; case 'a': if (!fstat(fileno(file), &s) && !s.st_size) { - pcap_write_header(file); + ovs_pcap_write_header(file); } break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return file; } int -pcap_read_header(FILE *file) +ovs_pcap_read_header(FILE *file) { struct pcap_hdr ph; if (fread(&ph, sizeof ph, 1, file) != 1) { @@ -111,7 +117,7 @@ pcap_read_header(FILE *file) } void -pcap_write_header(FILE *file) +ovs_pcap_write_header(FILE *file) { /* The pcap reader is responsible for figuring out endianness based on the * magic number, so the lack of htonX calls here is intentional. */ @@ -127,12 +133,13 @@ pcap_write_header(FILE *file) } int -pcap_read(FILE *file, struct ofpbuf **bufp) +ovs_pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when) { struct pcaprec_hdr prh; struct ofpbuf *buf; void *data; size_t len; + bool swap; *bufp = NULL; @@ -150,18 +157,22 @@ pcap_read(FILE *file, struct ofpbuf **bufp) /* Calculate length. */ len = prh.incl_len; - if (len > 0xffff) { - uint32_t swapped_len = (((len & 0xff000000) >> 24) | - ((len & 0x00ff0000) >> 8) | - ((len & 0x0000ff00) << 8) | - ((len & 0x000000ff) << 24)); - if (swapped_len > 0xffff) { - VLOG_WARN("bad packet length %zu or %"PRIu32" " + swap = len > 0xffff; + if (swap) { + len = uint32_byteswap(len); + if (len > 0xffff) { + VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32 "reading pcap file", - len, swapped_len); + len, uint32_byteswap(len)); return EPROTO; } - len = swapped_len; + } + + /* Calculate time. */ + if (when) { + uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec; + uint32_t ts_usec = swap ? uint32_byteswap(prh.ts_usec) : prh.ts_usec; + *when = ts_sec * 1000LL + ts_usec / 1000; } /* Read packet. */ @@ -179,13 +190,168 @@ pcap_read(FILE *file, struct ofpbuf **bufp) } void -pcap_write(FILE *file, struct ofpbuf *buf) +ovs_pcap_write(FILE *file, struct ofpbuf *buf) { struct pcaprec_hdr prh; - prh.ts_sec = 0; - prh.ts_usec = 0; - prh.incl_len = buf->size; - prh.orig_len = buf->size; + struct timeval tv; + + xgettimeofday(&tv); + prh.ts_sec = tv.tv_sec; + prh.ts_usec = tv.tv_usec; + prh.incl_len = ofpbuf_size(buf); + prh.orig_len = ofpbuf_size(buf); ignore(fwrite(&prh, sizeof prh, 1, file)); - ignore(fwrite(buf->data, buf->size, 1, file)); + ignore(fwrite(ofpbuf_data(buf), ofpbuf_size(buf), 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 tcp_key *key, uint32_t hash) +{ + struct tcp_stream *stream; + + HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) { + if (!memcmp(&stream->key, key, sizeof *key)) { + return stream; + } + } + return NULL; +} + +static struct tcp_stream * +tcp_stream_new(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash) +{ + struct tcp_stream *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; + unsigned int l7_length; + struct tcp_key key; + uint32_t hash; + uint32_t seq; + uint8_t flags; + const char *l7 = ofpbuf_get_tcp_payload(packet); + + if (flow->dl_type != htons(ETH_TYPE_IP) + || flow->nw_proto != IPPROTO_TCP + || !l7) { + return NULL; + } + tcp = ofpbuf_l4(packet); + flags = TCP_FLAGS(tcp->tcp_ctl); + l7_length = (char *) ofpbuf_tail(packet) - l7; + seq = ntohl(get_16aligned_be32(&tcp->tcp_seq)); + + /* Construct key. */ + 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); + + /* Find existing stream or start a new one for a SYN or if there's data. */ + stream = tcp_stream_lookup(r, &key, hash); + if (!stream) { + if (flags & TCP_SYN || l7_length) { + stream = tcp_stream_new(r, &key, hash); + stream->seq_no = flags & TCP_SYN ? seq + 1 : seq; + } else { + return NULL; + } + } + + payload = &stream->payload; + if (flags & TCP_SYN || !stream->seq_no) { + 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) { + /* 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 *) ofpbuf_base(payload) - (char *) ofpbuf_data(payload)); + + ofpbuf_put(payload, l7, l7_length); + stream->seq_no += l7_length; + return payload; + } else { + return NULL; + } }