]> git.proxmox.com Git - mirror_ovs.git/blob - lib/pcap-file.c
pcap: Fix reading regular old Ethernet pcap files.
[mirror_ovs.git] / lib / pcap-file.c
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 VLOG_DEFINE_THIS_MODULE(pcap);
36
37 enum ts_resolution {
38 PCAP_USEC,
39 PCAP_NSEC,
40 };
41
42 enum network_type {
43 PCAP_ETHERNET = 1,
44 PCAP_LINUX_SLL = 0x71
45 };
46
47 struct pcap_file {
48 FILE *file;
49 enum ts_resolution resolution;
50 enum network_type network;
51 };
52
53 struct pcap_hdr {
54 uint32_t magic_number; /* magic number */
55 uint16_t version_major; /* major version number */
56 uint16_t version_minor; /* minor version number */
57 int32_t thiszone; /* GMT to local correction */
58 uint32_t sigfigs; /* accuracy of timestamps */
59 uint32_t snaplen; /* max length of captured packets */
60 uint32_t network; /* data link type */
61 };
62 BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24);
63
64 struct pcaprec_hdr {
65 uint32_t ts_sec; /* timestamp seconds */
66 uint32_t ts_subsec; /* timestamp subseconds */
67 uint32_t incl_len; /* number of octets of packet saved in file */
68 uint32_t orig_len; /* actual length of packet */
69 };
70 BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16);
71
72 struct pcap_file *
73 ovs_pcap_open(const char *file_name, const char *mode)
74 {
75 struct stat s;
76 struct pcap_file *p_file;
77 int error;
78
79 ovs_assert(!strcmp(mode, "rb") ||
80 !strcmp(mode, "wb") ||
81 !strcmp(mode, "ab"));
82
83 p_file = xmalloc(sizeof *p_file);
84 p_file->file = fopen(file_name, mode);
85 p_file->resolution = PCAP_USEC;
86 if (p_file->file == NULL) {
87 VLOG_WARN("%s: failed to open pcap file for %s (%s)", file_name,
88 (mode[0] == 'r' ? "reading"
89 : mode[0] == 'w' ? "writing"
90 : "appending"),
91 ovs_strerror(errno));
92 return NULL;
93 }
94
95 switch (mode[0]) {
96 case 'r':
97 error = ovs_pcap_read_header(p_file);
98 if (error) {
99 errno = error;
100 ovs_pcap_close(p_file);
101 return NULL;
102 }
103 break;
104
105 case 'w':
106 ovs_pcap_write_header(p_file);
107 break;
108
109 case 'a':
110 if (!fstat(fileno(p_file->file), &s) && !s.st_size) {
111 ovs_pcap_write_header(p_file);
112 }
113 break;
114
115 default:
116 OVS_NOT_REACHED();
117 }
118
119 return p_file;
120 }
121
122 struct pcap_file *
123 ovs_pcap_stdout(void)
124 {
125 struct pcap_file *p_file = xmalloc(sizeof *p_file);
126 p_file->file = stdout;
127 return p_file;
128 }
129
130 int
131 ovs_pcap_read_header(struct pcap_file *p_file)
132 {
133 struct pcap_hdr ph;
134 if (fread(&ph, sizeof ph, 1, p_file->file) != 1) {
135 int error = ferror(p_file->file) ? errno : EOF;
136 VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error));
137 return error;
138 }
139 bool byte_swap;
140 if (ph.magic_number == 0xa1b2c3d4 || ph.magic_number == 0xd4c3b2a1) {
141 byte_swap = ph.magic_number == 0xd4c3b2a1;
142 p_file->resolution = PCAP_USEC;
143 } else if (ph.magic_number == 0xa1b23c4d ||
144 ph.magic_number == 0x4d3cb2a1) {
145 byte_swap = ph.magic_number == 0x4d3cb2a1;
146 p_file->resolution = PCAP_NSEC;
147 } else {
148 VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file "
149 "(expected 0xa1b2c3d4, 0xa1b23c4d, 0xd4c3b2a1, "
150 "or 0x4d3cb2a1)", ph.magic_number);
151 return EPROTO;
152 }
153 p_file->network = byte_swap ? uint32_byteswap(ph.network) : ph.network;
154 if (p_file->network != PCAP_ETHERNET &&
155 p_file->network != PCAP_LINUX_SLL) {
156 VLOG_WARN("unknown network type %"PRIu16" reading pcap file",
157 p_file->network);
158 return EPROTO;
159 }
160 return 0;
161 }
162
163 void
164 ovs_pcap_write_header(struct pcap_file *p_file)
165 {
166 /* The pcap reader is responsible for figuring out endianness based on the
167 * magic number, so the lack of htonX calls here is intentional. */
168 struct pcap_hdr ph;
169 ph.magic_number = 0xa1b2c3d4;
170 ph.version_major = 2;
171 ph.version_minor = 4;
172 ph.thiszone = 0;
173 ph.sigfigs = 0;
174 ph.snaplen = 1518;
175 ph.network = 1; /* Ethernet */
176 ignore(fwrite(&ph, sizeof ph, 1, p_file->file));
177 fflush(p_file->file);
178 }
179
180 int
181 ovs_pcap_read(struct pcap_file *p_file, struct dp_packet **bufp,
182 long long int *when)
183 {
184 struct pcaprec_hdr prh;
185 struct dp_packet *buf;
186 void *data;
187 size_t len;
188 bool swap;
189
190 *bufp = NULL;
191
192 /* Read header. */
193 if (fread(&prh, sizeof prh, 1, p_file->file) != 1) {
194 if (ferror(p_file->file)) {
195 int error = errno;
196 VLOG_WARN("failed to read pcap record header: %s",
197 ovs_retval_to_string(error));
198 return error;
199 } else {
200 return EOF;
201 }
202 }
203
204 /* Calculate length. */
205 len = prh.incl_len;
206 swap = len > 0xffff;
207 if (swap) {
208 len = uint32_byteswap(len);
209 if (len > 0xffff) {
210 VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32" "
211 "reading pcap file",
212 len, uint32_byteswap(len));
213 return EPROTO;
214 }
215 }
216
217 /* Calculate time. */
218 if (when) {
219 uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec;
220 uint32_t ts_subsec = swap ? uint32_byteswap(prh.ts_subsec)
221 : prh.ts_subsec;
222 ts_subsec = p_file->resolution == PCAP_USEC ? ts_subsec / 1000
223 : ts_subsec / 1000000;
224 *when = ts_sec * 1000LL + ts_subsec;
225 }
226
227 /* Read packet. Packet type is Ethernet */
228 buf = dp_packet_new(len);
229 data = dp_packet_put_uninit(buf, len);
230 if (fread(data, len, 1, p_file->file) != 1) {
231 int error = ferror(p_file->file) ? errno : EOF;
232 VLOG_WARN("failed to read pcap packet: %s",
233 ovs_retval_to_string(error));
234 dp_packet_delete(buf);
235 return error;
236 }
237
238 if (p_file->network == PCAP_LINUX_SLL) {
239 /* This format doesn't include the destination Ethernet address, which
240 * is weird. */
241
242 struct sll_header {
243 ovs_be16 packet_type;
244 ovs_be16 arp_hrd;
245 ovs_be16 lla_len;
246 struct eth_addr dl_src;
247 ovs_be16 reserved;
248 ovs_be16 protocol;
249 };
250 const struct sll_header *sll;
251 if (len < sizeof *sll) {
252 VLOG_WARN("pcap packet too short for SLL header");
253 dp_packet_delete(buf);
254 return EPROTO;
255 }
256
257 /* Pull Linux SLL header. */
258 sll = dp_packet_pull(buf, sizeof *sll);
259 if (sll->lla_len != htons(6)) {
260 ovs_hex_dump(stdout, sll, sizeof *sll, 0, false);
261 VLOG_WARN("bad SLL header");
262 dp_packet_delete(buf);
263 return EPROTO;
264 }
265
266 /* Push Ethernet header. */
267 struct eth_header eth = {
268 /* eth_dst is all zeros because the format doesn't include it. */
269 .eth_src = sll->dl_src,
270 .eth_type = sll->protocol,
271 };
272 dp_packet_push(buf, &eth, sizeof eth);
273 }
274
275 *bufp = buf;
276 return 0;
277 }
278
279 void
280 ovs_pcap_write(struct pcap_file *p_file, struct dp_packet *buf)
281 {
282 struct pcaprec_hdr prh;
283 struct timeval tv;
284
285 ovs_assert(buf->packet_type == htonl(PT_ETH));
286
287 xgettimeofday(&tv);
288 prh.ts_sec = tv.tv_sec;
289 prh.ts_subsec = tv.tv_usec;
290 prh.incl_len = dp_packet_size(buf);
291 prh.orig_len = dp_packet_size(buf);
292 ignore(fwrite(&prh, sizeof prh, 1, p_file->file));
293 ignore(fwrite(dp_packet_data(buf), dp_packet_size(buf), 1, p_file->file));
294 fflush(p_file->file);
295 }
296
297 void
298 ovs_pcap_close(struct pcap_file *p_file)
299 {
300 if (p_file->file != stdout) {
301 fclose(p_file->file);
302 }
303 free(p_file);
304 }
305 \f
306 struct tcp_key {
307 ovs_be32 nw_src, nw_dst;
308 ovs_be16 tp_src, tp_dst;
309 };
310
311 struct tcp_stream {
312 struct hmap_node hmap_node;
313 struct tcp_key key;
314 uint32_t seq_no;
315 struct dp_packet payload;
316 };
317
318 struct tcp_reader {
319 struct hmap streams;
320 };
321
322 static void
323 tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream)
324 {
325 hmap_remove(&r->streams, &stream->hmap_node);
326 dp_packet_uninit(&stream->payload);
327 free(stream);
328 }
329
330 /* Returns a new data structure for extracting TCP stream data from an
331 * Ethernet packet capture */
332 struct tcp_reader *
333 tcp_reader_open(void)
334 {
335 struct tcp_reader *r;
336
337 r = xmalloc(sizeof *r);
338 hmap_init(&r->streams);
339 return r;
340 }
341
342 /* Closes and frees 'r'. */
343 void
344 tcp_reader_close(struct tcp_reader *r)
345 {
346 struct tcp_stream *stream, *next_stream;
347
348 HMAP_FOR_EACH_SAFE (stream, next_stream, hmap_node, &r->streams) {
349 tcp_stream_destroy(r, stream);
350 }
351 hmap_destroy(&r->streams);
352 free(r);
353 }
354
355 static struct tcp_stream *
356 tcp_stream_lookup(struct tcp_reader *r,
357 const struct tcp_key *key, uint32_t hash)
358 {
359 struct tcp_stream *stream;
360
361 HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) {
362 if (!memcmp(&stream->key, key, sizeof *key)) {
363 return stream;
364 }
365 }
366 return NULL;
367 }
368
369 static struct tcp_stream *
370 tcp_stream_new(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash)
371 {
372 struct tcp_stream *stream;
373
374 stream = xmalloc(sizeof *stream);
375 hmap_insert(&r->streams, &stream->hmap_node, hash);
376 memcpy(&stream->key, key, sizeof *key);
377 stream->seq_no = 0;
378 dp_packet_init(&stream->payload, 2048);
379 return stream;
380 }
381
382 /* Processes 'packet' through TCP reader 'r'. The caller must have already
383 * extracted the packet's headers into 'flow', using flow_extract().
384 *
385 * If 'packet' is a TCP packet, then the reader attempts to reconstruct the
386 * data stream. If successful, it returns an dp_packet that represents the data
387 * stream so far. The caller may examine the data in the dp_packet and pull off
388 * any data that it has fully processed. The remaining data that the caller
389 * does not pull off will be presented again in future calls if more data
390 * arrives in the stream.
391 *
392 * Returns null if 'packet' doesn't add new data to a TCP stream. */
393 struct dp_packet *
394 tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
395 const struct dp_packet *packet)
396 {
397 struct tcp_stream *stream;
398 struct tcp_header *tcp;
399 struct dp_packet *payload;
400 unsigned int l7_length;
401 struct tcp_key key;
402 uint32_t hash;
403 uint32_t seq;
404 uint8_t flags;
405 const char *l7 = dp_packet_get_tcp_payload(packet);
406
407 if (flow->dl_type != htons(ETH_TYPE_IP)
408 || flow->nw_proto != IPPROTO_TCP
409 || !l7) {
410 return NULL;
411 }
412 tcp = dp_packet_l4(packet);
413 flags = TCP_FLAGS(tcp->tcp_ctl);
414 l7_length = (char *) dp_packet_tail(packet) - l7;
415 seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
416
417 /* Construct key. */
418 memset(&key, 0, sizeof key);
419 key.nw_src = flow->nw_src;
420 key.nw_dst = flow->nw_dst;
421 key.tp_src = flow->tp_src;
422 key.tp_dst = flow->tp_dst;
423 hash = hash_bytes(&key, sizeof key, 0);
424
425 /* Find existing stream or start a new one for a SYN or if there's data. */
426 stream = tcp_stream_lookup(r, &key, hash);
427 if (!stream) {
428 if (flags & TCP_SYN || l7_length) {
429 stream = tcp_stream_new(r, &key, hash);
430 stream->seq_no = flags & TCP_SYN ? seq + 1 : seq;
431 } else {
432 return NULL;
433 }
434 }
435
436 payload = &stream->payload;
437 if (flags & TCP_SYN || !stream->seq_no) {
438 dp_packet_clear(payload);
439 stream->seq_no = seq + 1;
440 return NULL;
441 } else if (flags & (TCP_FIN | TCP_RST)) {
442 tcp_stream_destroy(r, stream);
443 return NULL;
444 } else if (seq == stream->seq_no) {
445 /* Shift all of the existing payload to the very beginning of the
446 * allocated space, so that we reuse allocated space instead of
447 * continually expanding it. */
448 dp_packet_shift(payload, (char *) dp_packet_base(payload) - (char *) dp_packet_data(payload));
449
450 dp_packet_put(payload, l7, l7_length);
451 stream->seq_no += l7_length;
452 return payload;
453 } else {
454 return NULL;
455 }
456 }