diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 2cf05634e..6d0b2e2d8 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 ea5cfa3b2..81a094c2a 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 ddc40022d..a5033ba7d 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 12017ea83..24d0bb442 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 871b729eb..7adbe5dd9 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 82bc0cad6..aa8f6971c 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); }