LCOV - code coverage report
Current view: top level - lib - pcap-file.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 54 142 38.0 %
Date: 2016-09-14 01:02:56 Functions: 6 12 50.0 %
Branches: 25 96 26.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2009, 2010, 2012, 2013, 2014, 2015 Nicira, Inc.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
       5                 :            :  * you may not use this file except in compliance with the License.
       6                 :            :  * You may obtain a copy of the License at:
       7                 :            :  *
       8                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
       9                 :            :  *
      10                 :            :  * Unless required by applicable law or agreed to in writing, software
      11                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      12                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      13                 :            :  * See the License for the specific language governing permissions and
      14                 :            :  * limitations under the License.
      15                 :            :  */
      16                 :            : 
      17                 :            : #include <config.h>
      18                 :            : #include "pcap-file.h"
      19                 :            : #include <errno.h>
      20                 :            : #include <inttypes.h>
      21                 :            : #include <stdlib.h>
      22                 :            : #include <string.h>
      23                 :            : #include <sys/stat.h>
      24                 :            : #include "byte-order.h"
      25                 :            : #include "compiler.h"
      26                 :            : #include "dp-packet.h"
      27                 :            : #include "flow.h"
      28                 :            : #include "openvswitch/hmap.h"
      29                 :            : #include "packets.h"
      30                 :            : #include "timeval.h"
      31                 :            : #include "unaligned.h"
      32                 :            : #include "util.h"
      33                 :            : #include "openvswitch/vlog.h"
      34                 :            : 
      35                 :      10242 : VLOG_DEFINE_THIS_MODULE(pcap);
      36                 :            : 
      37                 :            : struct pcap_hdr {
      38                 :            :     uint32_t magic_number;   /* magic number */
      39                 :            :     uint16_t version_major;  /* major version number */
      40                 :            :     uint16_t version_minor;  /* minor version number */
      41                 :            :     int32_t thiszone;        /* GMT to local correction */
      42                 :            :     uint32_t sigfigs;        /* accuracy of timestamps */
      43                 :            :     uint32_t snaplen;        /* max length of captured packets */
      44                 :            :     uint32_t network;        /* data link type */
      45                 :            : };
      46                 :            : BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24);
      47                 :            : 
      48                 :            : struct pcaprec_hdr {
      49                 :            :     uint32_t ts_sec;         /* timestamp seconds */
      50                 :            :     uint32_t ts_usec;        /* timestamp microseconds */
      51                 :            :     uint32_t incl_len;       /* number of octets of packet saved in file */
      52                 :            :     uint32_t orig_len;       /* actual length of packet */
      53                 :            : };
      54                 :            : BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16);
      55                 :            : 
      56                 :            : FILE *
      57                 :       3235 : ovs_pcap_open(const char *file_name, const char *mode)
      58                 :            : {
      59                 :            :     struct stat s;
      60                 :            :     FILE *file;
      61                 :            :     int error;
      62                 :            : 
      63 [ +  + ][ -  + ]:       3235 :     ovs_assert(!strcmp(mode, "rb") ||
         [ +  + ][ -  + ]
      64                 :            :                !strcmp(mode, "wb") ||
      65                 :            :                !strcmp(mode, "ab"));
      66                 :            : 
      67                 :       3235 :     file = fopen(file_name, mode);
      68         [ -  + ]:       3235 :     if (file == NULL) {
      69 [ #  # ][ #  # ]:          0 :         VLOG_WARN("%s: failed to open pcap file for %s (%s)", file_name,
                 [ #  # ]
      70                 :            :                   (mode[0] == 'r' ? "reading"
      71                 :            :                    : mode[0] == 'w' ? "writing"
      72                 :            :                    : "appending"),
      73                 :            :                   ovs_strerror(errno));
      74                 :          0 :         return NULL;
      75                 :            :     }
      76                 :            : 
      77   [ +  -  +  - ]:       3235 :     switch (mode[0]) {
      78                 :            :     case 'r':
      79                 :        310 :         error = ovs_pcap_read_header(file);
      80         [ -  + ]:        310 :         if (error) {
      81                 :          0 :             errno = error;
      82                 :          0 :             fclose(file);
      83                 :          0 :             return NULL;
      84                 :            :         }
      85                 :        310 :         break;
      86                 :            : 
      87                 :            :     case 'w':
      88                 :          0 :         ovs_pcap_write_header(file);
      89                 :          0 :         break;
      90                 :            : 
      91                 :            :     case 'a':
      92 [ +  - ][ +  + ]:       2925 :         if (!fstat(fileno(file), &s) && !s.st_size) {
      93                 :        540 :             ovs_pcap_write_header(file);
      94                 :            :         }
      95                 :       2925 :         break;
      96                 :            : 
      97                 :            :     default:
      98                 :          0 :         OVS_NOT_REACHED();
      99                 :            :     }
     100                 :       3235 :     return file;
     101                 :            : }
     102                 :            : 
     103                 :            : int
     104                 :        311 : ovs_pcap_read_header(FILE *file)
     105                 :            : {
     106                 :            :     struct pcap_hdr ph;
     107         [ -  + ]:        311 :     if (fread(&ph, sizeof ph, 1, file) != 1) {
     108         [ #  # ]:          0 :         int error = ferror(file) ? errno : EOF;
     109         [ #  # ]:          0 :         VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error));
     110                 :          0 :         return error;
     111                 :            :     }
     112 [ +  + ][ -  + ]:        311 :     if (ph.magic_number != 0xa1b2c3d4 && ph.magic_number != 0xd4c3b2a1) {
     113         [ #  # ]:          0 :         VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file "
     114                 :            :                   "(expected 0xa1b2c3d4 or 0xd4c3b2a1)", ph.magic_number);
     115                 :          0 :         return EPROTO;
     116                 :            :     }
     117                 :        311 :     return 0;
     118                 :            : }
     119                 :            : 
     120                 :            : void
     121                 :        540 : ovs_pcap_write_header(FILE *file)
     122                 :            : {
     123                 :            :     /* The pcap reader is responsible for figuring out endianness based on the
     124                 :            :      * magic number, so the lack of htonX calls here is intentional. */
     125                 :            :     struct pcap_hdr ph;
     126                 :        540 :     ph.magic_number = 0xa1b2c3d4;
     127                 :        540 :     ph.version_major = 2;
     128                 :        540 :     ph.version_minor = 4;
     129                 :        540 :     ph.thiszone = 0;
     130                 :        540 :     ph.sigfigs = 0;
     131                 :        540 :     ph.snaplen = 1518;
     132                 :        540 :     ph.network = 1;             /* Ethernet */
     133                 :        540 :     ignore(fwrite(&ph, sizeof ph, 1, file));
     134                 :        540 :     fflush(file);
     135                 :        540 : }
     136                 :            : 
     137                 :            : int
     138                 :       2140 : ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when)
     139                 :            : {
     140                 :            :     struct pcaprec_hdr prh;
     141                 :            :     struct dp_packet *buf;
     142                 :            :     void *data;
     143                 :            :     size_t len;
     144                 :            :     bool swap;
     145                 :            : 
     146                 :       2140 :     *bufp = NULL;
     147                 :            : 
     148                 :            :     /* Read header. */
     149         [ +  + ]:       2140 :     if (fread(&prh, sizeof prh, 1, file) != 1) {
     150         [ -  + ]:        310 :         if (ferror(file)) {
     151                 :          0 :             int error = errno;
     152         [ #  # ]:          0 :             VLOG_WARN("failed to read pcap record header: %s",
     153                 :            :                       ovs_retval_to_string(error));
     154                 :          0 :             return error;
     155                 :            :         } else {
     156                 :        310 :             return EOF;
     157                 :            :         }
     158                 :            :     }
     159                 :            : 
     160                 :            :     /* Calculate length. */
     161                 :       1830 :     len = prh.incl_len;
     162                 :       1830 :     swap = len > 0xffff;
     163         [ +  + ]:       1830 :     if (swap) {
     164                 :        247 :         len = uint32_byteswap(len);
     165         [ -  + ]:        247 :         if (len > 0xffff) {
     166         [ #  # ]:          0 :             VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32
     167                 :            :                       "reading pcap file",
     168                 :            :                       len, uint32_byteswap(len));
     169                 :          0 :             return EPROTO;
     170                 :            :         }
     171                 :            :     }
     172                 :            : 
     173                 :            :     /* Calculate time. */
     174         [ -  + ]:       1830 :     if (when) {
     175         [ #  # ]:          0 :         uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec;
     176         [ #  # ]:          0 :         uint32_t ts_usec = swap ? uint32_byteswap(prh.ts_usec) : prh.ts_usec;
     177                 :          0 :         *when = ts_sec * 1000LL + ts_usec / 1000;
     178                 :            :     }
     179                 :            : 
     180                 :            :     /* Read packet. */
     181                 :       1830 :     buf = dp_packet_new(len);
     182                 :       1830 :     data = dp_packet_put_uninit(buf, len);
     183         [ -  + ]:       1830 :     if (fread(data, len, 1, file) != 1) {
     184         [ #  # ]:          0 :         int error = ferror(file) ? errno : EOF;
     185         [ #  # ]:          0 :         VLOG_WARN("failed to read pcap packet: %s",
     186                 :            :                   ovs_retval_to_string(error));
     187                 :          0 :         dp_packet_delete(buf);
     188                 :          0 :         return error;
     189                 :            :     }
     190                 :       1830 :     *bufp = buf;
     191                 :       2140 :     return 0;
     192                 :            : }
     193                 :            : 
     194                 :            : void
     195                 :      17020 : ovs_pcap_write(FILE *file, struct dp_packet *buf)
     196                 :            : {
     197                 :            :     struct pcaprec_hdr prh;
     198                 :            :     struct timeval tv;
     199                 :            : 
     200                 :      17020 :     xgettimeofday(&tv);
     201                 :      17020 :     prh.ts_sec = tv.tv_sec;
     202                 :      17020 :     prh.ts_usec = tv.tv_usec;
     203                 :      17020 :     prh.incl_len = dp_packet_size(buf);
     204                 :      17020 :     prh.orig_len = dp_packet_size(buf);
     205                 :      17020 :     ignore(fwrite(&prh, sizeof prh, 1, file));
     206                 :      17020 :     ignore(fwrite(dp_packet_data(buf), dp_packet_size(buf), 1, file));
     207                 :      17020 :     fflush(file);
     208                 :      17020 : }
     209                 :            : 
     210                 :            : struct tcp_key {
     211                 :            :     ovs_be32 nw_src, nw_dst;
     212                 :            :     ovs_be16 tp_src, tp_dst;
     213                 :            : };
     214                 :            : 
     215                 :            : struct tcp_stream {
     216                 :            :     struct hmap_node hmap_node;
     217                 :            :     struct tcp_key key;
     218                 :            :     uint32_t seq_no;
     219                 :            :     struct dp_packet payload;
     220                 :            : };
     221                 :            : 
     222                 :            : struct tcp_reader {
     223                 :            :     struct hmap streams;
     224                 :            : };
     225                 :            : 
     226                 :            : static void
     227                 :          0 : tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream)
     228                 :            : {
     229                 :          0 :     hmap_remove(&r->streams, &stream->hmap_node);
     230                 :          0 :     dp_packet_uninit(&stream->payload);
     231                 :          0 :     free(stream);
     232                 :          0 : }
     233                 :            : 
     234                 :            : /* Returns a new data structure for extracting TCP stream data from an
     235                 :            :  * Ethernet packet capture */
     236                 :            : struct tcp_reader *
     237                 :          0 : tcp_reader_open(void)
     238                 :            : {
     239                 :            :     struct tcp_reader *r;
     240                 :            : 
     241                 :          0 :     r = xmalloc(sizeof *r);
     242                 :          0 :     hmap_init(&r->streams);
     243                 :          0 :     return r;
     244                 :            : }
     245                 :            : 
     246                 :            : /* Closes and frees 'r'. */
     247                 :            : void
     248                 :          0 : tcp_reader_close(struct tcp_reader *r)
     249                 :            : {
     250                 :            :     struct tcp_stream *stream, *next_stream;
     251                 :            : 
     252 [ #  # ][ #  # ]:          0 :     HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) {
                 [ #  # ]
     253                 :          0 :         tcp_stream_destroy(r, stream);
     254                 :            :     }
     255                 :          0 :     hmap_destroy(&r->streams);
     256                 :          0 :     free(r);
     257                 :          0 : }
     258                 :            : 
     259                 :            : static struct tcp_stream *
     260                 :          0 : tcp_stream_lookup(struct tcp_reader *r,
     261                 :            :                   const struct tcp_key *key, uint32_t hash)
     262                 :            : {
     263                 :            :     struct tcp_stream *stream;
     264                 :            : 
     265 [ #  # ][ #  # ]:          0 :     HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) {
     266         [ #  # ]:          0 :         if (!memcmp(&stream->key, key, sizeof *key)) {
     267                 :          0 :             return stream;
     268                 :            :         }
     269                 :            :     }
     270                 :          0 :     return NULL;
     271                 :            : }
     272                 :            : 
     273                 :            : static struct tcp_stream *
     274                 :          0 : tcp_stream_new(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash)
     275                 :            : {
     276                 :            :     struct tcp_stream *stream;
     277                 :            : 
     278                 :          0 :     stream = xmalloc(sizeof *stream);
     279                 :          0 :     hmap_insert(&r->streams, &stream->hmap_node, hash);
     280                 :          0 :     memcpy(&stream->key, key, sizeof *key);
     281                 :          0 :     stream->seq_no = 0;
     282                 :          0 :     dp_packet_init(&stream->payload, 2048);
     283                 :          0 :     return stream;
     284                 :            : }
     285                 :            : 
     286                 :            : /* Processes 'packet' through TCP reader 'r'.  The caller must have already
     287                 :            :  * extracted the packet's headers into 'flow', using flow_extract().
     288                 :            :  *
     289                 :            :  * If 'packet' is a TCP packet, then the reader attempts to reconstruct the
     290                 :            :  * data stream.  If successful, it returns an dp_packet that represents the data
     291                 :            :  * stream so far.  The caller may examine the data in the dp_packet and pull off
     292                 :            :  * any data that it has fully processed.  The remaining data that the caller
     293                 :            :  * does not pull off will be presented again in future calls if more data
     294                 :            :  * arrives in the stream.
     295                 :            :  *
     296                 :            :  * Returns null if 'packet' doesn't add new data to a TCP stream. */
     297                 :            : struct dp_packet *
     298                 :          0 : tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
     299                 :            :                const struct dp_packet *packet)
     300                 :            : {
     301                 :            :     struct tcp_stream *stream;
     302                 :            :     struct tcp_header *tcp;
     303                 :            :     struct dp_packet *payload;
     304                 :            :     unsigned int l7_length;
     305                 :            :     struct tcp_key key;
     306                 :            :     uint32_t hash;
     307                 :            :     uint32_t seq;
     308                 :            :     uint8_t flags;
     309                 :          0 :     const char *l7 = dp_packet_get_tcp_payload(packet);
     310                 :            : 
     311         [ #  # ]:          0 :     if (flow->dl_type != htons(ETH_TYPE_IP)
     312         [ #  # ]:          0 :         || flow->nw_proto != IPPROTO_TCP
     313         [ #  # ]:          0 :         || !l7) {
     314                 :          0 :         return NULL;
     315                 :            :     }
     316                 :          0 :     tcp = dp_packet_l4(packet);
     317                 :          0 :     flags = TCP_FLAGS(tcp->tcp_ctl);
     318                 :          0 :     l7_length = (char *) dp_packet_tail(packet) - l7;
     319                 :          0 :     seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
     320                 :            : 
     321                 :            :     /* Construct key. */
     322                 :          0 :     memset(&key, 0, sizeof key);
     323                 :          0 :     key.nw_src = flow->nw_src;
     324                 :          0 :     key.nw_dst = flow->nw_dst;
     325                 :          0 :     key.tp_src = flow->tp_src;
     326                 :          0 :     key.tp_dst = flow->tp_dst;
     327                 :          0 :     hash = hash_bytes(&key, sizeof key, 0);
     328                 :            : 
     329                 :            :     /* Find existing stream or start a new one for a SYN or if there's data. */
     330                 :          0 :     stream = tcp_stream_lookup(r, &key, hash);
     331         [ #  # ]:          0 :     if (!stream) {
     332 [ #  # ][ #  # ]:          0 :         if (flags & TCP_SYN || l7_length) {
     333                 :          0 :             stream = tcp_stream_new(r, &key, hash);
     334         [ #  # ]:          0 :             stream->seq_no = flags & TCP_SYN ? seq + 1 : seq;
     335                 :            :         } else {
     336                 :          0 :             return NULL;
     337                 :            :         }
     338                 :            :     }
     339                 :            : 
     340                 :          0 :     payload = &stream->payload;
     341 [ #  # ][ #  # ]:          0 :     if (flags & TCP_SYN || !stream->seq_no) {
     342                 :          0 :         dp_packet_clear(payload);
     343                 :          0 :         stream->seq_no = seq + 1;
     344                 :          0 :         return NULL;
     345         [ #  # ]:          0 :     } else if (flags & (TCP_FIN | TCP_RST)) {
     346                 :          0 :         tcp_stream_destroy(r, stream);
     347                 :          0 :         return NULL;
     348         [ #  # ]:          0 :     } else if (seq == stream->seq_no) {
     349                 :            :         /* Shift all of the existing payload to the very beginning of the
     350                 :            :          * allocated space, so that we reuse allocated space instead of
     351                 :            :          * continually expanding it. */
     352                 :          0 :         dp_packet_shift(payload, (char *) dp_packet_base(payload) - (char *) dp_packet_data(payload));
     353                 :            : 
     354                 :          0 :         dp_packet_put(payload, l7, l7_length);
     355                 :          0 :         stream->seq_no += l7_length;
     356                 :          0 :         return payload;
     357                 :            :     } else {
     358                 :          0 :         return NULL;
     359                 :            :     }
     360                 :            : }

Generated by: LCOV version 1.12