Skip to content

Commit

Permalink
pcap-file: Add nanosecond resolution pcap support.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
putnopvut authored and blp committed Oct 6, 2018
1 parent 2b2532d commit b6e840a
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 61 deletions.
12 changes: 5 additions & 7 deletions lib/netdev-dummy.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
102 changes: 71 additions & 31 deletions lib/pcap-file.c
Expand Up @@ -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 */
Expand All @@ -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"
Expand All @@ -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. */
Expand All @@ -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;
Expand All @@ -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));
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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 {
Expand Down
14 changes: 9 additions & 5 deletions lib/pcap-file.h
Expand Up @@ -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. */

Expand Down
4 changes: 2 additions & 2 deletions tests/test-conntrack.c
Expand Up @@ -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");
Expand Down Expand Up @@ -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[] = {
Expand Down
11 changes: 4 additions & 7 deletions tests/test-flows.c
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit b6e840a

Please sign in to comment.