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 : : }
|