2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-22 09:58:01 +00:00

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>
This commit is contained in:
Mark Michelson 2018-10-05 12:52:40 -04:00 committed by Ben Pfaff
parent 2b2532dd4c
commit b6e840aed0
6 changed files with 102 additions and 61 deletions

View File

@ -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) {

View File

@ -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 {

View File

@ -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. */

View File

@ -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[] = {

View File

@ -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);
}

View File

@ -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);
}