Merge branch 'mainstream'
[sliver-openvswitch.git] / lib / pcap-file.c
1 /*
2  * Copyright (c) 2009, 2010, 2012, 2013 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 "ofpbuf.h"
27 #include "vlog.h"
28
29 VLOG_DEFINE_THIS_MODULE(pcap);
30
31 struct pcap_hdr {
32     uint32_t magic_number;   /* magic number */
33     uint16_t version_major;  /* major version number */
34     uint16_t version_minor;  /* minor version number */
35     int32_t thiszone;        /* GMT to local correction */
36     uint32_t sigfigs;        /* accuracy of timestamps */
37     uint32_t snaplen;        /* max length of captured packets */
38     uint32_t network;        /* data link type */
39 };
40 BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24);
41
42 struct pcaprec_hdr {
43     uint32_t ts_sec;         /* timestamp seconds */
44     uint32_t ts_usec;        /* timestamp microseconds */
45     uint32_t incl_len;       /* number of octets of packet saved in file */
46     uint32_t orig_len;       /* actual length of packet */
47 };
48 BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16);
49
50 FILE *
51 pcap_open(const char *file_name, const char *mode)
52 {
53     struct stat s;
54     FILE *file;
55     int error;
56
57     ovs_assert(!strcmp(mode, "rb") ||
58                !strcmp(mode, "wb") ||
59                !strcmp(mode, "ab"));
60
61     file = fopen(file_name, mode);
62     if (file == NULL) {
63         VLOG_WARN("%s: failed to open pcap file for %s (%s)", file_name,
64                   (mode[0] == 'r' ? "reading"
65                    : mode[0] == 'w' ? "writing"
66                    : "appending"),
67                   ovs_strerror(errno));
68         return NULL;
69     }
70
71     switch (mode[0]) {
72     case 'r':
73         error = pcap_read_header(file);
74         if (error) {
75             errno = error;
76             fclose(file);
77             return NULL;
78         }
79         break;
80
81     case 'w':
82         pcap_write_header(file);
83         break;
84
85     case 'a':
86         if (!fstat(fileno(file), &s) && !s.st_size) {
87             pcap_write_header(file);
88         }
89         break;
90
91     default:
92         NOT_REACHED();
93     }
94     return file;
95 }
96
97 int
98 pcap_read_header(FILE *file)
99 {
100     struct pcap_hdr ph;
101     if (fread(&ph, sizeof ph, 1, file) != 1) {
102         int error = ferror(file) ? errno : EOF;
103         VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error));
104         return error;
105     }
106     if (ph.magic_number != 0xa1b2c3d4 && ph.magic_number != 0xd4c3b2a1) {
107         VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file "
108                   "(expected 0xa1b2c3d4 or 0xd4c3b2a1)", ph.magic_number);
109         return EPROTO;
110     }
111     return 0;
112 }
113
114 void
115 pcap_write_header(FILE *file)
116 {
117     /* The pcap reader is responsible for figuring out endianness based on the
118      * magic number, so the lack of htonX calls here is intentional. */
119     struct pcap_hdr ph;
120     ph.magic_number = 0xa1b2c3d4;
121     ph.version_major = 2;
122     ph.version_minor = 4;
123     ph.thiszone = 0;
124     ph.sigfigs = 0;
125     ph.snaplen = 1518;
126     ph.network = 1;             /* Ethernet */
127     ignore(fwrite(&ph, sizeof ph, 1, file));
128 }
129
130 int
131 pcap_read(FILE *file, struct ofpbuf **bufp)
132 {
133     struct pcaprec_hdr prh;
134     struct ofpbuf *buf;
135     void *data;
136     size_t len;
137
138     *bufp = NULL;
139
140     /* Read header. */
141     if (fread(&prh, sizeof prh, 1, file) != 1) {
142         if (ferror(file)) {
143             int error = errno;
144             VLOG_WARN("failed to read pcap record header: %s",
145                       ovs_retval_to_string(error));
146             return error;
147         } else {
148             return EOF;
149         }
150     }
151
152     /* Calculate length. */
153     len = prh.incl_len;
154     if (len > 0xffff) {
155         uint32_t swapped_len = uint32_byteswap(len);
156         if (swapped_len > 0xffff) {
157             VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32" "
158                       "reading pcap file",
159                       len, swapped_len);
160             return EPROTO;
161         }
162         len = swapped_len;
163     }
164
165     /* Read packet. */
166     buf = ofpbuf_new(len);
167     data = ofpbuf_put_uninit(buf, len);
168     if (fread(data, len, 1, file) != 1) {
169         int error = ferror(file) ? errno : EOF;
170         VLOG_WARN("failed to read pcap packet: %s",
171                   ovs_retval_to_string(error));
172         ofpbuf_delete(buf);
173         return error;
174     }
175     *bufp = buf;
176     return 0;
177 }
178
179 void
180 pcap_write(FILE *file, struct ofpbuf *buf)
181 {
182     struct pcaprec_hdr prh;
183     prh.ts_sec = 0;
184     prh.ts_usec = 0;
185     prh.incl_len = buf->size;
186     prh.orig_len = buf->size;
187     ignore(fwrite(&prh, sizeof prh, 1, file));
188     ignore(fwrite(buf->data, buf->size, 1, file));
189 }