X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fpcap-file.c;h=191e690e637815801269054bc286e9580a5cbbab;hb=HEAD;hp=99dff237d17f5757a752f2df7e61114bb254bb12;hpb=a797eab3d57910765fd1515600feb4ffe20b31ce;p=sliver-openvswitch.git diff --git a/lib/pcap-file.c b/lib/pcap-file.c index 99dff237d..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. @@ -23,8 +23,12 @@ #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); @@ -49,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; @@ -71,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); @@ -80,12 +84,12 @@ 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; @@ -96,7 +100,7 @@ pcap_open(const char *file_name, const char *mode) } int -pcap_read_header(FILE *file) +ovs_pcap_read_header(FILE *file) { struct pcap_hdr ph; if (fread(&ph, sizeof ph, 1, file) != 1) { @@ -113,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. */ @@ -129,7 +133,7 @@ pcap_write_header(FILE *file) } int -pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when) +ovs_pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when) { struct pcaprec_hdr prh; struct ofpbuf *buf; @@ -186,7 +190,7 @@ pcap_read(FILE *file, struct ofpbuf **bufp, long long int *when) } void -pcap_write(FILE *file, struct ofpbuf *buf) +ovs_pcap_write(FILE *file, struct ofpbuf *buf) { struct pcaprec_hdr prh; struct timeval tv; @@ -194,8 +198,160 @@ pcap_write(FILE *file, struct ofpbuf *buf) xgettimeofday(&tv); prh.ts_sec = tv.tv_sec; prh.ts_usec = tv.tv_usec; - prh.incl_len = buf->size; - prh.orig_len = buf->size; + 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; + } }