Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nanosec accurate packet processing #796

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit)
do_bridge_bidirectional(options, tcpedit);
}

if (gettimeofday(&stats.end_time, NULL) < 0)
errx(-1, "gettimeofday() failed: %s", strerror(errno));
if (get_current_time(&stats.end_time) < 0)
errx(-1, "get_current_time() failed: %s", strerror(errno));
packet_stats(&stats);
}

Expand Down
14 changes: 12 additions & 2 deletions src/common/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,18 @@ void timesdiv(struct timespec *tvs, COUNTER div)
}

void
init_timestamp(timestamp_t *ctx)
init_timestamp(struct timespec *timestamp)
{
timerclear(ctx);
timesclear(timestamp);
}

int get_current_time(struct timespec *ts){
#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L
return clock_gettime(CLOCK_MONOTONIC, ts);
#else
struct timeval tv;
int success = gettimeofday(&tv, NULL);
TIMEVAL_TO_TIMESPEC(&tv, ts);
return success;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice that this supports older OS's. How much testing has there been on this?

Copy link
Contributor Author

@plangarbalint plangarbalint May 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comprehensive test on this is definitely missing, I made some user tests with basic setups + all the tests pass when make test is executed. However there are many include guards, with if, elseif etc... branches, they all need to be tested separately. Just to mention one, it wasn't tested how it works with netmap

#endif
}
33 changes: 31 additions & 2 deletions src/common/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,35 @@ void timesdiv(struct timespec *tvs, COUNTER div);
} while (0)
#endif

/* add tvp and uvp and store in vvp */
#ifndef timeradd_timespec
#define timeradd_timespec(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \
if ((vvp)->tv_nsec >= 1000000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_nsec -= 1000000000; \
} \
} while (0)
#endif


/* add tvp and uvp and store in vvp */
#ifndef timeradd_timeval_timespec
#define timeradd_timeval_timespec(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_usec * 1000; \
if ((vvp)->tv_nsec >= 1000000000) { \
int seconds = (vvp)->tv_nsec % 1000000000; \
(vvp)->tv_sec += seconds; \
(vvp)->tv_nsec -= 1000000000 * seconds; \
} \
} while (0)
#endif


/* subtract uvp from tvp and store in vvp */
#ifndef timersub
#define timersub(tvp, uvp, vvp) \
Expand Down Expand Up @@ -170,7 +199,7 @@ void timesdiv(struct timespec *tvs, COUNTER div);

typedef struct timeval timestamp_t;

void init_timestamp(timestamp_t *ctx);

void init_timestamp(struct timespec *timestamp);
int get_current_time(struct timespec *ts);

#endif /* _TIMER_H_ */
20 changes: 10 additions & 10 deletions src/common/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ int _our_safe_pcap_next_ex(pcap_t *pcap, struct pcap_pkthdr **pkthdr,
void
packet_stats(const tcpreplay_stats_t *stats)
{
struct timeval diff;
struct timespec diff;
COUNTER diff_us;
COUNTER bytes_sec = 0;
u_int32_t bytes_sec_10ths = 0;
Expand All @@ -206,8 +206,8 @@ packet_stats(const tcpreplay_stats_t *stats)
COUNTER pkts_sec = 0;
u_int32_t pkts_sec_100ths = 0;

timersub(&stats->end_time, &stats->start_time, &diff);
diff_us = TIMEVAL_TO_MICROSEC(&diff);
timessub(&stats->end_time, &stats->start_time, &diff);
diff_us = TIMESPEC_TO_MICROSEC(&diff);

if (diff_us && stats->pkts_sent && stats->bytes_sent) {
COUNTER bytes_sec_X10;
Expand Down Expand Up @@ -236,13 +236,13 @@ packet_stats(const tcpreplay_stats_t *stats)
pkts_sec_100ths = pkts_sec_X100 % 100;
}

if (diff_us >= 1000 * 1000)
if (diff_us >= 1000 * 1000) {
printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n",
stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_usec / (10 * 1000)));
else
stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_nsec / (10 * 1000000)));
} else{
printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n",
stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_usec);

stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_nsec / 1000);
}

if (mb_sec >= 1)
printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n",
Expand All @@ -265,7 +265,7 @@ packet_stats(const tcpreplay_stats_t *stats)
* @param len: length of the buffer
* @return: string containing date, or -1 on error
*/
int format_date_time(struct timeval *when, char *buf, size_t len)
int format_date_time(struct timespec *when, char *buf, size_t len)
{
struct tm *tm;
char tmp[64];
Expand All @@ -277,7 +277,7 @@ int format_date_time(struct timeval *when, char *buf, size_t len)
return -1;

strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%06u", tm);
return snprintf(buf, len, tmp, when->tv_usec);
return snprintf(buf, len, tmp, when->tv_nsec / 1000);
}

/**
Expand Down
12 changes: 6 additions & 6 deletions src/common/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ typedef struct {
COUNTER bytes_sent;
COUNTER pkts_sent;
COUNTER failed;
struct timeval start_time;
struct timeval time_delta;
struct timeval end_time;
struct timeval pkt_ts_delta;
struct timeval last_print;
struct timespec start_time;
struct timespec time_delta;
struct timespec end_time;
struct timespec pkt_ts_delta;
struct timespec last_print;
COUNTER flow_non_flow_packets;
COUNTER flows;
COUNTER flows_unique;
Expand All @@ -47,7 +47,7 @@ typedef struct {

int read_hexstring(const char *l2string, u_char *hex, const int hexlen);
void packet_stats(const tcpreplay_stats_t *stats);
int format_date_time(struct timeval *when, char *buf, size_t len);
int format_date_time(struct timespec *when, char *buf, size_t len);
uint32_t tcpr_random(uint32_t *seed);
void restore_stdin(void);

Expand Down
8 changes: 7 additions & 1 deletion src/defines.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ typedef u_int32_t uint32_t
#define TIMEVAL_TO_MILLISEC(x) (((x)->tv_sec * 1000) + ((x)->tv_usec / 1000))
#define TIMEVAL_TO_MICROSEC(x) (((x)->tv_sec * 1000000) + (x)->tv_usec)
#define TIMEVAL_TO_NANOSEC(x) ((u_int64_t)((x)->tv_sec * 1000000000) + ((u_int64_t)(x)->tv_usec * 1000))
#define TIMSTAMP_TO_MICROSEC(x) (TIMEVAL_TO_MICROSEC(x))

#define MILLISEC_TO_TIMEVAL(x, tv) \
do { \
Expand Down Expand Up @@ -359,6 +358,13 @@ typedef u_int32_t uint32_t
(a)->tv_nsec = (b)->tv_nsec; \
} while(0)

/* libpcap puts nanosec values in tv_usec when pcap file is read with nanosec precision*/
#define TIMEVAL_AS_TIMESPEC_SET(a, b) \
do { \
(a)->tv_sec = (b)->tv_sec; \
(a)->tv_nsec = (b)->tv_usec; \
} while(0)

/*
* Help suppress some compiler warnings
* No problem if variable is actually used
Expand Down
18 changes: 9 additions & 9 deletions src/replay.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ replay_file(tcpreplay_t *ctx, int idx)

/* read from pcap file if we haven't cached things yet */
if (!ctx->options->preload_pcap) {
if ((pcap = pcap_open_offline(path, ebuf)) == NULL) {
if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
Expand All @@ -140,10 +140,10 @@ replay_file(tcpreplay_t *ctx, int idx)

} else {
if (!ctx->options->file_cache[idx].cached) {
if ((pcap = pcap_open_offline(path, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
ctx->options->file_cache[idx].dlt = pcap_datalink(pcap);
}
}
Expand All @@ -152,10 +152,10 @@ replay_file(tcpreplay_t *ctx, int idx)
if (ctx->options->verbose) {
/* in cache mode, we may not have opened the file */
if (pcap == NULL)
if ((pcap = pcap_open_offline(path, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}

ctx->options->file_cache[idx].dlt = pcap_datalink(pcap);
/* init tcpdump */
Expand Down
Loading