]> git.proxmox.com Git - mirror_ovs.git/blame - lib/pcap-file.c
ovsdb-idl: Fix memleak when reinserting tracked orphan rows.
[mirror_ovs.git] / lib / pcap-file.c
CommitLineData
064af421 1/*
f9ef2270 2 * Copyright (c) 2009, 2010, 2012, 2013, 2014, 2015 Nicira, Inc.
064af421 3 *
a14bc59f
BP
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:
064af421 7 *
a14bc59f
BP
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.
064af421
BP
15 */
16
17#include <config.h>
2c78a3e6 18#include "pcap-file.h"
064af421
BP
19#include <errno.h>
20#include <inttypes.h>
b6cdfbb4 21#include <stdlib.h>
064af421 22#include <string.h>
b6cdfbb4 23#include <sys/stat.h>
4cf5406b 24#include "byte-order.h"
064af421 25#include "compiler.h"
cf62fa4c 26#include "dp-packet.h"
f3dd1419 27#include "flow.h"
ee89ea7b 28#include "openvswitch/hmap.h"
f3dd1419 29#include "packets.h"
a797eab3 30#include "timeval.h"
f3dd1419 31#include "unaligned.h"
ee89ea7b 32#include "util.h"
e6211adc 33#include "openvswitch/vlog.h"
064af421 34
d98e6007 35VLOG_DEFINE_THIS_MODULE(pcap);
5136ce49 36
b6e840ae
MM
37enum ts_resolution {
38 PCAP_USEC,
39 PCAP_NSEC,
40};
41
597177a2 42enum network_type {
7f8d02a9 43 PCAP_ETHERNET = 1,
597177a2
BP
44 PCAP_LINUX_SLL = 0x71
45};
46
b6e840ae
MM
47struct pcap_file {
48 FILE *file;
49 enum ts_resolution resolution;
597177a2 50 enum network_type network;
b6e840ae
MM
51};
52
064af421
BP
53struct 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 */
381328fe
BP
61};
62BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24);
064af421
BP
63
64struct pcaprec_hdr {
65 uint32_t ts_sec; /* timestamp seconds */
b6e840ae 66 uint32_t ts_subsec; /* timestamp subseconds */
064af421
BP
67 uint32_t incl_len; /* number of octets of packet saved in file */
68 uint32_t orig_len; /* actual length of packet */
381328fe
BP
69};
70BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16);
064af421 71
b6e840ae 72struct pcap_file *
50aa0364 73ovs_pcap_open(const char *file_name, const char *mode)
064af421 74{
b6cdfbb4 75 struct stat s;
b6e840ae 76 struct pcap_file *p_file;
b6cdfbb4 77 int error;
064af421 78
b6cdfbb4
BP
79 ovs_assert(!strcmp(mode, "rb") ||
80 !strcmp(mode, "wb") ||
81 !strcmp(mode, "ab"));
064af421 82
b6e840ae
MM
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) {
b6cdfbb4
BP
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));
064af421
BP
92 return NULL;
93 }
94
b6cdfbb4
BP
95 switch (mode[0]) {
96 case 'r':
b6e840ae 97 error = ovs_pcap_read_header(p_file);
b6cdfbb4
BP
98 if (error) {
99 errno = error;
b6e840ae 100 ovs_pcap_close(p_file);
064af421
BP
101 return NULL;
102 }
b6cdfbb4
BP
103 break;
104
105 case 'w':
b6e840ae 106 ovs_pcap_write_header(p_file);
b6cdfbb4
BP
107 break;
108
109 case 'a':
b6e840ae
MM
110 if (!fstat(fileno(p_file->file), &s) && !s.st_size) {
111 ovs_pcap_write_header(p_file);
b6cdfbb4
BP
112 }
113 break;
114
115 default:
428b2edd 116 OVS_NOT_REACHED();
064af421 117 }
b6e840ae
MM
118
119 return p_file;
120}
121
122struct pcap_file *
123ovs_pcap_stdout(void)
124{
125 struct pcap_file *p_file = xmalloc(sizeof *p_file);
126 p_file->file = stdout;
127 return p_file;
064af421
BP
128}
129
130int
b6e840ae 131ovs_pcap_read_header(struct pcap_file *p_file)
064af421
BP
132{
133 struct pcap_hdr ph;
b6e840ae
MM
134 if (fread(&ph, sizeof ph, 1, p_file->file) != 1) {
135 int error = ferror(p_file->file) ? errno : EOF;
c18ea70d 136 VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error));
064af421
BP
137 return error;
138 }
597177a2 139 bool byte_swap;
b6e840ae 140 if (ph.magic_number == 0xa1b2c3d4 || ph.magic_number == 0xd4c3b2a1) {
597177a2 141 byte_swap = ph.magic_number == 0xd4c3b2a1;
b6e840ae
MM
142 p_file->resolution = PCAP_USEC;
143 } else if (ph.magic_number == 0xa1b23c4d ||
144 ph.magic_number == 0x4d3cb2a1) {
597177a2 145 byte_swap = ph.magic_number == 0x4d3cb2a1;
b6e840ae
MM
146 p_file->resolution = PCAP_NSEC;
147 } else {
064af421 148 VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file "
b6e840ae
MM
149 "(expected 0xa1b2c3d4, 0xa1b23c4d, 0xd4c3b2a1, "
150 "or 0x4d3cb2a1)", ph.magic_number);
064af421
BP
151 return EPROTO;
152 }
597177a2
BP
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) {
9a609f63
BP
156 VLOG_WARN("unknown network type %u reading pcap file",
157 (unsigned int) p_file->network);
597177a2
BP
158 return EPROTO;
159 }
064af421
BP
160 return 0;
161}
162
163void
b6e840ae 164ovs_pcap_write_header(struct pcap_file *p_file)
064af421
BP
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 */
b6e840ae
MM
176 ignore(fwrite(&ph, sizeof ph, 1, p_file->file));
177 fflush(p_file->file);
064af421
BP
178}
179
180int
b6e840ae
MM
181ovs_pcap_read(struct pcap_file *p_file, struct dp_packet **bufp,
182 long long int *when)
064af421
BP
183{
184 struct pcaprec_hdr prh;
cf62fa4c 185 struct dp_packet *buf;
064af421
BP
186 void *data;
187 size_t len;
a797eab3 188 bool swap;
064af421
BP
189
190 *bufp = NULL;
191
192 /* Read header. */
b6e840ae
MM
193 if (fread(&prh, sizeof prh, 1, p_file->file) != 1) {
194 if (ferror(p_file->file)) {
f6cdbd3e
BP
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 }
064af421
BP
202 }
203
204 /* Calculate length. */
205 len = prh.incl_len;
a797eab3
BP
206 swap = len > 0xffff;
207 if (swap) {
208 len = uint32_byteswap(len);
209 if (len > 0xffff) {
2f7662a1 210 VLOG_WARN("bad packet length %"PRIuSIZE" or %"PRIu32" "
064af421 211 "reading pcap file",
a797eab3 212 len, uint32_byteswap(len));
064af421
BP
213 return EPROTO;
214 }
a797eab3
BP
215 }
216
217 /* Calculate time. */
218 if (when) {
219 uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec;
b6e840ae
MM
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;
064af421
BP
225 }
226
2482b0b0 227 /* Read packet. Packet type is Ethernet */
cf62fa4c
PS
228 buf = dp_packet_new(len);
229 data = dp_packet_put_uninit(buf, len);
b6e840ae
MM
230 if (fread(data, len, 1, p_file->file) != 1) {
231 int error = ferror(p_file->file) ? errno : EOF;
064af421 232 VLOG_WARN("failed to read pcap packet: %s",
c18ea70d 233 ovs_retval_to_string(error));
cf62fa4c 234 dp_packet_delete(buf);
064af421
BP
235 return error;
236 }
597177a2
BP
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
064af421
BP
275 *bufp = buf;
276 return 0;
277}
278
279void
b6e840ae 280ovs_pcap_write(struct pcap_file *p_file, struct dp_packet *buf)
064af421
BP
281{
282 struct pcaprec_hdr prh;
a797eab3
BP
283 struct timeval tv;
284
940ac2ce 285 ovs_assert(dp_packet_is_eth(buf));
2482b0b0 286
a797eab3
BP
287 xgettimeofday(&tv);
288 prh.ts_sec = tv.tv_sec;
b6e840ae 289 prh.ts_subsec = tv.tv_usec;
cf62fa4c
PS
290 prh.incl_len = dp_packet_size(buf);
291 prh.orig_len = dp_packet_size(buf);
b6e840ae
MM
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
297void
298ovs_pcap_close(struct pcap_file *p_file)
299{
300 if (p_file->file != stdout) {
301 fclose(p_file->file);
302 }
303 free(p_file);
064af421 304}
f3dd1419
BP
305\f
306struct tcp_key {
307 ovs_be32 nw_src, nw_dst;
308 ovs_be16 tp_src, tp_dst;
309};
310
311struct tcp_stream {
312 struct hmap_node hmap_node;
313 struct tcp_key key;
314 uint32_t seq_no;
cf62fa4c 315 struct dp_packet payload;
f3dd1419
BP
316};
317
318struct tcp_reader {
319 struct hmap streams;
320};
321
322static void
323tcp_stream_destroy(struct tcp_reader *r, struct tcp_stream *stream)
324{
325 hmap_remove(&r->streams, &stream->hmap_node);
cf62fa4c 326 dp_packet_uninit(&stream->payload);
f3dd1419
BP
327 free(stream);
328}
329
330/* Returns a new data structure for extracting TCP stream data from an
331 * Ethernet packet capture */
332struct tcp_reader *
333tcp_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'. */
343void
344tcp_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
355static struct tcp_stream *
276e2864
BP
356tcp_stream_lookup(struct tcp_reader *r,
357 const struct tcp_key *key, uint32_t hash)
f3dd1419
BP
358{
359 struct tcp_stream *stream;
f3dd1419
BP
360
361 HMAP_FOR_EACH_WITH_HASH (stream, hmap_node, hash, &r->streams) {
276e2864 362 if (!memcmp(&stream->key, key, sizeof *key)) {
f3dd1419
BP
363 return stream;
364 }
365 }
276e2864
BP
366 return NULL;
367}
368
369static struct tcp_stream *
370tcp_stream_new(struct tcp_reader *r, const struct tcp_key *key, uint32_t hash)
371{
372 struct tcp_stream *stream;
f3dd1419
BP
373
374 stream = xmalloc(sizeof *stream);
375 hmap_insert(&r->streams, &stream->hmap_node, hash);
276e2864 376 memcpy(&stream->key, key, sizeof *key);
f3dd1419 377 stream->seq_no = 0;
cf62fa4c 378 dp_packet_init(&stream->payload, 2048);
f3dd1419
BP
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
cf62fa4c
PS
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
f3dd1419
BP
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. */
cf62fa4c 393struct dp_packet *
f3dd1419 394tcp_reader_run(struct tcp_reader *r, const struct flow *flow,
cf62fa4c 395 const struct dp_packet *packet)
f3dd1419
BP
396{
397 struct tcp_stream *stream;
398 struct tcp_header *tcp;
cf62fa4c 399 struct dp_packet *payload;
276e2864
BP
400 unsigned int l7_length;
401 struct tcp_key key;
402 uint32_t hash;
f3dd1419
BP
403 uint32_t seq;
404 uint8_t flags;
cf62fa4c 405 const char *l7 = dp_packet_get_tcp_payload(packet);
f3dd1419
BP
406
407 if (flow->dl_type != htons(ETH_TYPE_IP)
408 || flow->nw_proto != IPPROTO_TCP
5a51b2cd 409 || !l7) {
f3dd1419
BP
410 return NULL;
411 }
cf62fa4c 412 tcp = dp_packet_l4(packet);
f3dd1419 413 flags = TCP_FLAGS(tcp->tcp_ctl);
cf62fa4c 414 l7_length = (char *) dp_packet_tail(packet) - l7;
f3dd1419 415 seq = ntohl(get_16aligned_be32(&tcp->tcp_seq));
276e2864
BP
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) {
cf62fa4c 438 dp_packet_clear(payload);
f3dd1419
BP
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) {
f3dd1419
BP
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. */
cf62fa4c 448 dp_packet_shift(payload, (char *) dp_packet_base(payload) - (char *) dp_packet_data(payload));
f3dd1419 449
cf62fa4c 450 dp_packet_put(payload, l7, l7_length);
276e2864 451 stream->seq_no += l7_length;
f3dd1419
BP
452 return payload;
453 } else {
454 return NULL;
455 }
456}