summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Michelson <mmichels@redhat.com>2018-10-05 12:52:40 -0400
committerBen Pfaff <blp@ovn.org>2018-10-05 17:35:07 -0700
commitb6e840aed03e3f6d1aa726b482140d895f60f90f (patch)
tree1893a7a3b24917591591874441db5136c4e7b946
parent2b2532dd4cec6175973aeb34e6e445d59812c1ab (diff)
downloadopenvswitch-b6e840aed03e3f6d1aa726b482140d895f60f90f.tar.gz
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 <mmichels@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
-rw-r--r--lib/netdev-dummy.c12
-rw-r--r--lib/pcap-file.c102
-rw-r--r--lib/pcap-file.h14
-rw-r--r--tests/test-conntrack.c4
-rw-r--r--tests/test-flows.c11
-rw-r--r--utilities/ovs-ofctl.c20
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);
}