[ovs-dev] [PATCH v2 2/2] pcap-file: Add nanosecond resolution pcap support.

Mark Michelson mmichels at redhat.com
Fri Oct 5 16:52:40 UTC 2018


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 <mmichels at redhat.com>
---
 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 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);
     }
-- 
2.14.4



More information about the dev mailing list