From b6e840aed03e3f6d1aa726b482140d895f60f90f Mon Sep 17 00:00:00 2001 From: Mark Michelson Date: Fri, 5 Oct 2018 12:52:40 -0400 Subject: [PATCH] pcap-file: Add nanosecond resolution pcap support. PCAP header magic numbers are different for microsecond and nanosecond resolution timestamps. This patch adds support for understanding the difference and reporting the time correctly with ovs_pcap_read(). When writing pcap files, OVS will always use microsecond resolution, so no new calculations were added to those functions. Signed-off-by: Mark Michelson Signed-off-by: Ben Pfaff --- lib/netdev-dummy.c | 12 ++--- lib/pcap-file.c | 102 ++++++++++++++++++++++++++++------------- lib/pcap-file.h | 14 ++++-- tests/test-conntrack.c | 4 +- tests/test-flows.c | 11 ++--- utilities/ovs-ofctl.c | 20 ++++---- 6 files changed, 102 insertions(+), 61 deletions(-) diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 2cf05634eae..6d0b2e2d83a 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -118,7 +118,7 @@ struct netdev_dummy { struct dummy_packet_conn conn OVS_GUARDED; - FILE *tx_pcap, *rxq_pcap OVS_GUARDED; + struct pcap_file *tx_pcap, *rxq_pcap OVS_GUARDED; struct in_addr address, netmask; struct in6_addr ipv6, ipv6_mask; @@ -719,10 +719,10 @@ netdev_dummy_destruct(struct netdev *netdev_) ovs_mutex_lock(&netdev->mutex); if (netdev->rxq_pcap) { - fclose(netdev->rxq_pcap); + ovs_pcap_close(netdev->rxq_pcap); } if (netdev->tx_pcap && netdev->tx_pcap != netdev->rxq_pcap) { - fclose(netdev->tx_pcap); + ovs_pcap_close(netdev->tx_pcap); } dummy_packet_conn_close(&netdev->conn); netdev->conn.type = NONE; @@ -859,10 +859,10 @@ netdev_dummy_set_config(struct netdev *netdev_, const struct smap *args, dummy_packet_conn_set_config(&netdev->conn, args); if (netdev->rxq_pcap) { - fclose(netdev->rxq_pcap); + ovs_pcap_close(netdev->rxq_pcap); } if (netdev->tx_pcap && netdev->tx_pcap != netdev->rxq_pcap) { - fclose(netdev->tx_pcap); + ovs_pcap_close(netdev->tx_pcap); } netdev->rxq_pcap = netdev->tx_pcap = NULL; pcap = smap_get(args, "pcap"); @@ -1144,7 +1144,6 @@ netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED, dp_packet_use_const(&dp, buffer, size); ovs_pcap_write(dev->tx_pcap, &dp); - fflush(dev->tx_pcap); } ovs_mutex_unlock(&dev->mutex); @@ -1529,7 +1528,6 @@ netdev_dummy_queue_packet(struct netdev_dummy *dummy, struct dp_packet *packet, if (dummy->rxq_pcap) { ovs_pcap_write(dummy->rxq_pcap, packet); - fflush(dummy->rxq_pcap); } prev = NULL; LIST_FOR_EACH (rx, node, &dummy->rxes) { diff --git a/lib/pcap-file.c b/lib/pcap-file.c index ea5cfa3b274..81a094c2a01 100644 --- a/lib/pcap-file.c +++ b/lib/pcap-file.c @@ -34,6 +34,16 @@ VLOG_DEFINE_THIS_MODULE(pcap); +enum ts_resolution { + PCAP_USEC, + PCAP_NSEC, +}; + +struct pcap_file { + FILE *file; + enum ts_resolution resolution; +}; + struct pcap_hdr { uint32_t magic_number; /* magic number */ uint16_t version_major; /* major version number */ @@ -47,25 +57,27 @@ BUILD_ASSERT_DECL(sizeof(struct pcap_hdr) == 24); struct pcaprec_hdr { uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ + uint32_t ts_subsec; /* timestamp subseconds */ uint32_t incl_len; /* number of octets of packet saved in file */ uint32_t orig_len; /* actual length of packet */ }; BUILD_ASSERT_DECL(sizeof(struct pcaprec_hdr) == 16); -FILE * +struct pcap_file * ovs_pcap_open(const char *file_name, const char *mode) { struct stat s; - FILE *file; + struct pcap_file *p_file; int error; ovs_assert(!strcmp(mode, "rb") || !strcmp(mode, "wb") || !strcmp(mode, "ab")); - file = fopen(file_name, mode); - if (file == NULL) { + p_file = xmalloc(sizeof *p_file); + p_file->file = fopen(file_name, mode); + p_file->resolution = PCAP_USEC; + if (p_file->file == NULL) { VLOG_WARN("%s: failed to open pcap file for %s (%s)", file_name, (mode[0] == 'r' ? "reading" : mode[0] == 'w' ? "writing" @@ -76,49 +88,64 @@ ovs_pcap_open(const char *file_name, const char *mode) switch (mode[0]) { case 'r': - error = ovs_pcap_read_header(file); + error = ovs_pcap_read_header(p_file); if (error) { errno = error; - fclose(file); + ovs_pcap_close(p_file); return NULL; } break; case 'w': - ovs_pcap_write_header(file); + ovs_pcap_write_header(p_file); break; case 'a': - if (!fstat(fileno(file), &s) && !s.st_size) { - ovs_pcap_write_header(file); + if (!fstat(fileno(p_file->file), &s) && !s.st_size) { + ovs_pcap_write_header(p_file); } break; default: OVS_NOT_REACHED(); } - return file; + + return p_file; +} + +struct pcap_file * +ovs_pcap_stdout(void) +{ + struct pcap_file *p_file = xmalloc(sizeof *p_file); + p_file->file = stdout; + return p_file; } int -ovs_pcap_read_header(FILE *file) +ovs_pcap_read_header(struct pcap_file *p_file) { struct pcap_hdr ph; - if (fread(&ph, sizeof ph, 1, file) != 1) { - int error = ferror(file) ? errno : EOF; + if (fread(&ph, sizeof ph, 1, p_file->file) != 1) { + int error = ferror(p_file->file) ? errno : EOF; VLOG_WARN("failed to read pcap header: %s", ovs_retval_to_string(error)); return error; } - if (ph.magic_number != 0xa1b2c3d4 && ph.magic_number != 0xd4c3b2a1) { + if (ph.magic_number == 0xa1b2c3d4 || ph.magic_number == 0xd4c3b2a1) { + p_file->resolution = PCAP_USEC; + } else if (ph.magic_number == 0xa1b23c4d || + ph.magic_number == 0x4d3cb2a1) { + p_file->resolution = PCAP_NSEC; + } else { VLOG_WARN("bad magic 0x%08"PRIx32" reading pcap file " - "(expected 0xa1b2c3d4 or 0xd4c3b2a1)", ph.magic_number); + "(expected 0xa1b2c3d4, 0xa1b23c4d, 0xd4c3b2a1, " + "or 0x4d3cb2a1)", ph.magic_number); return EPROTO; } return 0; } void -ovs_pcap_write_header(FILE *file) +ovs_pcap_write_header(struct pcap_file *p_file) { /* The pcap reader is responsible for figuring out endianness based on the * magic number, so the lack of htonX calls here is intentional. */ @@ -130,12 +157,13 @@ ovs_pcap_write_header(FILE *file) ph.sigfigs = 0; ph.snaplen = 1518; ph.network = 1; /* Ethernet */ - ignore(fwrite(&ph, sizeof ph, 1, file)); - fflush(file); + ignore(fwrite(&ph, sizeof ph, 1, p_file->file)); + fflush(p_file->file); } int -ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when) +ovs_pcap_read(struct pcap_file *p_file, struct dp_packet **bufp, + long long int *when) { struct pcaprec_hdr prh; struct dp_packet *buf; @@ -146,8 +174,8 @@ ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when) *bufp = NULL; /* Read header. */ - if (fread(&prh, sizeof prh, 1, file) != 1) { - if (ferror(file)) { + if (fread(&prh, sizeof prh, 1, p_file->file) != 1) { + if (ferror(p_file->file)) { int error = errno; VLOG_WARN("failed to read pcap record header: %s", ovs_retval_to_string(error)); @@ -173,15 +201,18 @@ ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when) /* Calculate time. */ if (when) { uint32_t ts_sec = swap ? uint32_byteswap(prh.ts_sec) : prh.ts_sec; - uint32_t ts_usec = swap ? uint32_byteswap(prh.ts_usec) : prh.ts_usec; - *when = ts_sec * 1000LL + ts_usec / 1000; + uint32_t ts_subsec = swap ? uint32_byteswap(prh.ts_subsec) + : prh.ts_subsec; + ts_subsec = p_file->resolution == PCAP_USEC ? ts_subsec / 1000 + : ts_subsec / 1000000; + *when = ts_sec * 1000LL + ts_subsec; } /* Read packet. Packet type is Ethernet */ buf = dp_packet_new(len); data = dp_packet_put_uninit(buf, len); - if (fread(data, len, 1, file) != 1) { - int error = ferror(file) ? errno : EOF; + if (fread(data, len, 1, p_file->file) != 1) { + int error = ferror(p_file->file) ? errno : EOF; VLOG_WARN("failed to read pcap packet: %s", ovs_retval_to_string(error)); dp_packet_delete(buf); @@ -192,7 +223,7 @@ ovs_pcap_read(FILE *file, struct dp_packet **bufp, long long int *when) } void -ovs_pcap_write(FILE *file, struct dp_packet *buf) +ovs_pcap_write(struct pcap_file *p_file, struct dp_packet *buf) { struct pcaprec_hdr prh; struct timeval tv; @@ -201,12 +232,21 @@ ovs_pcap_write(FILE *file, struct dp_packet *buf) xgettimeofday(&tv); prh.ts_sec = tv.tv_sec; - prh.ts_usec = tv.tv_usec; + prh.ts_subsec = tv.tv_usec; prh.incl_len = dp_packet_size(buf); prh.orig_len = dp_packet_size(buf); - ignore(fwrite(&prh, sizeof prh, 1, file)); - ignore(fwrite(dp_packet_data(buf), dp_packet_size(buf), 1, file)); - fflush(file); + ignore(fwrite(&prh, sizeof prh, 1, p_file->file)); + ignore(fwrite(dp_packet_data(buf), dp_packet_size(buf), 1, p_file->file)); + fflush(p_file->file); +} + +void +ovs_pcap_close(struct pcap_file *p_file) +{ + if (p_file->file != stdout) { + fclose(p_file->file); + } + free(p_file); } struct tcp_key { diff --git a/lib/pcap-file.h b/lib/pcap-file.h index ddc40022d5b..a5033ba7dc5 100644 --- a/lib/pcap-file.h +++ b/lib/pcap-file.h @@ -21,13 +21,17 @@ struct flow; struct dp_packet; +struct pcap_file; /* PCAP file reading and writing. */ -FILE *ovs_pcap_open(const char *file_name, const char *mode); -int ovs_pcap_read_header(FILE *); -void ovs_pcap_write_header(FILE *); -int ovs_pcap_read(FILE *, struct dp_packet **, long long int *when); -void ovs_pcap_write(FILE *, struct dp_packet *); +struct pcap_file *ovs_pcap_open(const char *file_name, const char *mode); +struct pcap_file *ovs_pcap_stdout(void); +int ovs_pcap_read_header(struct pcap_file *); +void ovs_pcap_write_header(struct pcap_file *); +int ovs_pcap_read(struct pcap_file *, struct dp_packet **, + long long int *when); +void ovs_pcap_write(struct pcap_file *, struct dp_packet *); +void ovs_pcap_close(struct pcap_file *); /* Extracting TCP stream data from an Ethernet packet capture. */ diff --git a/tests/test-conntrack.c b/tests/test-conntrack.c index 12017ea83b4..24d0bb442e5 100644 --- a/tests/test-conntrack.c +++ b/tests/test-conntrack.c @@ -191,7 +191,7 @@ static void test_pcap(struct ovs_cmdl_context *ctx) { size_t total_count, batch_size_; - FILE *pcap; + struct pcap_file *pcap; int err = 0; pcap = ovs_pcap_open(ctx->argv[1], "rb"); @@ -245,7 +245,7 @@ test_pcap(struct ovs_cmdl_context *ctx) dp_packet_delete_batch(batch, true); } conntrack_destroy(&ct); - fclose(pcap); + ovs_pcap_close(pcap); } static const struct ovs_cmdl_command commands[] = { diff --git a/tests/test-flows.c b/tests/test-flows.c index 871b729ebf6..7adbe5dd992 100644 --- a/tests/test-flows.c +++ b/tests/test-flows.c @@ -37,7 +37,8 @@ static void test_flows_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) { struct ofp10_match expected_match; - FILE *flows, *pcap; + FILE *flows; + struct pcap_file *pcap; int retval; int n = 0, errors = 0; @@ -47,16 +48,11 @@ test_flows_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) if (!flows) { ovs_fatal(errno, "failed to open %s", argv[1]); } - pcap = fopen(argv[2], "rb"); + pcap = ovs_pcap_open(argv[2], "rb"); if (!pcap) { ovs_fatal(errno, "failed to open %s", argv[2]); } - retval = ovs_pcap_read_header(pcap); - if (retval) { - ovs_fatal(retval > 0 ? retval : 0, "reading pcap header failed"); - } - while (fread(&expected_match, sizeof expected_match, 1, flows)) { struct dp_packet *packet; struct ofp10_match extracted_match; @@ -97,6 +93,7 @@ test_flows_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED) dp_packet_delete(packet); } + ovs_pcap_close(pcap); printf("checked %d packets, %d errors\n", n, errors); exit(errors != 0); } diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c index 82bc0cad686..aa8f6971cbe 100644 --- a/utilities/ovs-ofctl.c +++ b/utilities/ovs-ofctl.c @@ -2743,12 +2743,12 @@ static void ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) { struct tcp_reader *reader; - FILE *file; + struct pcap_file *p_file; int error; bool first; - file = ovs_pcap_open(ctx->argv[1], "rb"); - if (!file) { + p_file = ovs_pcap_open(ctx->argv[1], "rb"); + if (!p_file) { ovs_fatal(errno, "%s: open failed", ctx->argv[1]); } @@ -2759,7 +2759,7 @@ ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) long long int when; struct flow flow; - error = ovs_pcap_read(file, &packet, &when); + error = ovs_pcap_read(p_file, &packet, &when); if (error) { break; } @@ -2809,7 +2809,7 @@ ofctl_ofp_parse_pcap(struct ovs_cmdl_context *ctx) dp_packet_delete(packet); } tcp_reader_close(reader); - fclose(file); + ovs_pcap_close(p_file); } static void @@ -4456,7 +4456,7 @@ ofctl_parse_pcap(struct ovs_cmdl_context *ctx) int error = 0; for (int i = 1; i < ctx->argc; i++) { const char *filename = ctx->argv[i]; - FILE *pcap = ovs_pcap_open(filename, "rb"); + struct pcap_file *pcap = ovs_pcap_open(filename, "rb"); if (!pcap) { error = errno; ovs_error(error, "%s: open failed", filename); @@ -4482,7 +4482,7 @@ ofctl_parse_pcap(struct ovs_cmdl_context *ctx) putchar('\n'); dp_packet_delete(packet); } - fclose(pcap); + ovs_pcap_close(pcap); } exit(error); } @@ -4783,8 +4783,10 @@ ofctl_compose_packet(struct ovs_cmdl_context *ctx) free(l7); if (print_pcap) { - ovs_pcap_write_header(stdout); - ovs_pcap_write(stdout, &p); + struct pcap_file *p_file = ovs_pcap_stdout(); + ovs_pcap_write_header(p_file); + ovs_pcap_write(p_file, &p); + ovs_pcap_close(p_file); } else { ovs_hex_dump(stdout, dp_packet_data(&p), dp_packet_size(&p), 0, false); }