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 all 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 @@ -210,8 +210,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
15 changes: 13 additions & 2 deletions src/common/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,18 @@
}

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

int get_current_time(struct timespec *ts){

Check warning on line 47 in src/common/timer.c

View workflow job for this annotation

GitHub Actions / cpp-linter

src/common/timer.c:47:39 [readability-identifier-length]

parameter name 'ts' is too short, expected at least 3 characters
#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
}
34 changes: 33 additions & 1 deletion src/common/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,35 @@
} 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 @@ -150,4 +179,7 @@

typedef struct timeval timestamp_t;

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

Check warning on line 183 in src/common/timer.h

View workflow job for this annotation

GitHub Actions / cpp-linter

src/common/timer.h:183:39 [readability-identifier-length]

parameter name 'ts' is too short, expected at least 3 characters

#endif /* _TIMER_H_ */
26 changes: 10 additions & 16 deletions src/common/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
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 @@ -221,8 +221,8 @@
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 @@ -251,18 +251,13 @@
pkts_sec_100ths = pkts_sec_X100 % 100;
}

if (diff_us >= 1000 * 1000)
if (diff_us >= 1000 * 1000) {

Check warning on line 254 in src/common/utils.c

View workflow job for this annotation

GitHub Actions / cpp-linter

src/common/utils.c:254:5 [bugprone-branch-clone]

if with identical then and else branches
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 Down Expand Up @@ -294,8 +289,7 @@
* @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 @@ -307,7 +301,7 @@
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 @@ -29,11 +29,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 @@ -44,7 +44,7 @@ typedef struct {

int read_hexstring(const char *l2string, u_char *hex, 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
14 changes: 10 additions & 4 deletions src/defines.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,9 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t typedef u_int32_t uint32_t
#define MICROSEC_TO_SEC(x) (x / 1000000)
#define NANOSEC_TO_SEC(x) ((u_int64_t)x / 1000000000)

#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 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 MILLISEC_TO_TIMEVAL(x, tv) \
do { \
Expand Down Expand Up @@ -360,6 +359,13 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t 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 @@ -117,7 +117,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 @@ -133,10 +133,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 @@ -145,10 +145,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
Loading