From 3b0934ecbe7f5276e2e700fb04ea3fef1da40282 Mon Sep 17 00:00:00 2001 From: eplabal Date: Sat, 4 Mar 2023 18:31:14 +0100 Subject: [PATCH 01/11] rewrite do_sleep to handle sub sleep times by only nanosleeping --- src/bridge.c | 2 +- src/common/timer.c | 14 +++- src/common/timer.h | 34 ++++++++- src/common/utils.c | 20 ++--- src/common/utils.h | 12 +-- src/defines.h.in | 1 - src/send_packets.c | 168 ++++++++++++++++++++--------------------- src/signal_handler.c | 2 +- src/sleep.c | 25 +++--- src/sleep.h | 42 ++++++----- src/tcpbridge.c | 2 +- src/tcpreplay.c | 6 +- src/tcpreplay_api.c | 57 ++++++++------ src/tcpreplay_api.h | 11 ++- src/tcpreplay_opts.def | 13 ++++ src/timestamp_trace.h | 12 +-- test/Makefile.am | 8 +- 17 files changed, 258 insertions(+), 171 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index d6d20e40..cd1b3b76 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -238,7 +238,7 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) do_bridge_bidirectional(options, tcpedit); } - if (gettimeofday(&stats.end_time, NULL) < 0) + if (get_time_of_day(&stats.end_time) < 0) errx(-1, "gettimeofday() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index 0bd9bab8..6ed978ef 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -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_time_of_day(struct timespec *ts) { + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; +} + +int clock_get_time(struct timespec *ts){ + return clock_gettime(CLOCK_REALTIME, ts); +} diff --git a/src/common/timer.h b/src/common/timer.h index 97905573..5e48d34c 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -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) \ @@ -170,7 +199,8 @@ 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_time_of_day(struct timespec *ts); +int clock_get_time(struct timespec *ts); #endif /* _TIMER_H_ */ diff --git a/src/common/utils.c b/src/common/utils.c index a641939d..b7ed24ac 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -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; @@ -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; @@ -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", @@ -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]; @@ -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); } /** diff --git a/src/common/utils.h b/src/common/utils.h index 445b0f62..d9017cf5 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -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; @@ -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); diff --git a/src/defines.h.in b/src/defines.h.in index ea50e0ab..20e41a70 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -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 { \ diff --git a/src/send_packets.c b/src/send_packets.c index effe659e..a129d4d9 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -65,12 +65,12 @@ extern tcpedit_t *tcpedit; extern int debug; #endif -static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_time, - struct timeval *last, COUNTER len, - sendpacket_t *sp, COUNTER counter, timestamp_t *sent_timestamp, +static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, + struct timespec *last, COUNTER len, + sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, COUNTER start_us, COUNTER *skip_length); static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, - struct timespec *nap_this_time, struct timeval *now); + struct timespec *nap_this_time, struct timespec *now); static u_char *get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, @@ -315,9 +315,9 @@ static void increment_iteration(tcpreplay_t *ctx) */ void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) -{ - - struct timeval print_delta, now, last_pkt_ts; +{ + struct timeval last_pkt_ts; + struct timespec now, print_delta; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -339,9 +339,9 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - gettimeofday(&now, NULL); - if (!timerisset(&stats->start_time)) { - TIMEVAL_SET(&stats->start_time, &now); + ctx->timefunction.gettime(&now); + if (!timesisset(&stats->start_time)) { + TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { char buf[64]; if (format_date_time(&stats->start_time, buf, sizeof(buf)) > 0) @@ -352,7 +352,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) ctx->skip_packets = 0; timerclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) + + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -441,14 +441,14 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) struct timeval delta; timersub(&pkthdr.ts, &last_pkt_ts, &delta); - timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); } } if (!top_speed) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } /* @@ -459,7 +459,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) */ calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, pktlen, sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), &skip_length); + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* * Track the time of the "last packet sent". @@ -467,8 +467,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * A number of 3rd party tools generate bad timestamps which go backwards * in time. Hence, don't update the "last" unless pkthdr.ts > last */ - if (timercmp(&stats->time_delta, &stats->pkt_ts_delta, <)) - TIMEVAL_SET(&stats->time_delta, &stats->pkt_ts_delta); + if (timescmp(&stats->time_delta, &stats->pkt_ts_delta, <)) + TIMESPEC_SET(&stats->time_delta, &stats->pkt_ts_delta); /* * we know how long to sleep between sends, now do it. @@ -493,7 +493,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) /* * Mark the time when we sent the last packet */ - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); #ifdef TIMESTAMP_TRACE add_timestamp_trace_entry(pktlen, &stats->end_time, skip_length); @@ -504,14 +504,14 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) /* print stats during the run? */ if (options->stats > 0) { - if (! timerisset(&stats->last_print)) { - TIMEVAL_SET(&stats->last_print, &now); + if (! timesisset(&stats->last_print)) { + TIMESPEC_SET(&stats->last_print, &now); } else { - timersub(&now, &stats->last_print, &print_delta); + timessub(&now, &stats->last_print, &print_delta); if (print_delta.tv_sec >= options->stats) { - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); packet_stats(stats); - TIMEVAL_SET(&stats->last_print, &now); + TIMESPEC_SET(&stats->last_print, &now); } } } @@ -523,33 +523,32 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) } #endif /* stop sending based on the duration limit... */ - if ((end_us > 0 && (COUNTER)TIMEVAL_TO_MICROSEC(&now) > end_us) || + if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || /* ... or stop sending based on the limit -L? */ (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ - #ifdef HAVE_NETMAP /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -561,7 +560,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) void send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *pcap2, int cache_file_idx2) { - struct timeval print_delta, now, last_pkt_ts; + struct timeval last_pkt_ts; + struct timespec now, print_delta; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -581,9 +581,9 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - gettimeofday(&now, NULL); - if (!timerisset(&stats->start_time)) { - TIMEVAL_SET(&stats->start_time, &now); + ctx->timefunction.gettime(&now); + if (!timesisset(&stats->start_time)) { + TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { char buf[64]; if (format_date_time(&stats->start_time, buf, sizeof(buf)) > 0) @@ -594,7 +594,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; timerclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) + + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -714,16 +714,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * struct timeval delta; timersub(&pkthdr_ptr->ts, &last_pkt_ts, &delta); - timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); } - if (!timerisset(&stats->time_delta)) - TIMEVAL_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta); + if (!timesisset(&stats->time_delta)) + TIMESPEC_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta); } if (!top_speed) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } @@ -735,7 +735,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * */ calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, pktlen, sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), &skip_length); + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* * Track the time of the "last packet sent". @@ -743,8 +743,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * * A number of 3rd party tools generate bad timestamps which go backwards * in time. Hence, don't update the "last" unless pkthdr_ptr->ts > last */ - if (timercmp(&stats->time_delta, &stats->pkt_ts_delta, <)) - TIMEVAL_SET(&stats->time_delta, &stats->pkt_ts_delta); + if (timescmp(&stats->time_delta, &stats->pkt_ts_delta, <)) + TIMESPEC_SET(&stats->time_delta, &stats->pkt_ts_delta); /* * we know how long to sleep between sends, now do it. @@ -769,21 +769,21 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* * Mark the time when we sent the last packet */ - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); ++stats->pkts_sent; stats->bytes_sent += pktlen; /* print stats during the run? */ if (options->stats > 0) { - if (! timerisset(&stats->last_print)) { - TIMEVAL_SET(&stats->last_print, &now); + if (! timesisset(&stats->last_print)) { + TIMESPEC_SET(&stats->last_print, &now); } else { - timersub(&now, &stats->last_print, &print_delta); + timessub(&now, &stats->last_print, &print_delta); if (print_delta.tv_sec >= options->stats) { - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); packet_stats(stats); - TIMEVAL_SET(&stats->last_print, &now); + TIMESPEC_SET(&stats->last_print, &now); } } } @@ -803,7 +803,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } /* stop sending based on the duration limit... */ - if ((end_us > 0 && (COUNTER)TIMEVAL_TO_MICROSEC(&now) > end_us) || + if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || /* ... or stop sending based on the limit -L? */ (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; @@ -814,21 +814,21 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -965,14 +965,14 @@ cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num) * calculate the appropriate amount of time to sleep. Sleep time * will be in ctx->nap. */ -static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, - struct timeval *time_delta, COUNTER len, - sendpacket_t *sp, COUNTER counter, timestamp_t *sent_timestamp, - COUNTER start_us, COUNTER *skip_length) +static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, + struct timespec *time_delta, COUNTER len, + sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, + COUNTER start_ns, COUNTER *skip_length) { tcpreplay_opt_t *options = ctx->options; - struct timeval nap_for; - COUNTER now_us; + struct timespec nap_for; + COUNTER now_ns; timesclear(&ctx->nap); @@ -996,10 +996,10 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Replay packets a factor of the time they were originally sent. * Make sure the packet is not late. */ - if (timercmp(pkt_ts_delta, time_delta, >)) { + if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ - timersub(pkt_ts_delta, time_delta, &nap_for); - TIMEVAL_TO_TIMESPEC(&nap_for, &ctx->nap); + timessub(pkt_ts_delta, time_delta, &nap_for); + TIMESPEC_SET(&nap_for, &ctx->nap); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1013,32 +1013,32 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Ignore the time supplied by the capture file and send data at * a constant 'rate' (bytes per second). */ - now_us = TIMSTAMP_TO_MICROSEC(sent_timestamp); - if (now_us) { - COUNTER next_tx_us; + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); - COUNTER tx_us = now_us - start_us; + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); //PB: Ferencol miert bits sent? + COUNTER tx_ns = now_ns - start_ns; /* - * bits * 1000000 divided by bps = microseconds + * bits * 1000000000 divided by bps = nanosecond * * ensure there is no overflow in cases where bits_sent is very high */ if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) - next_tx_us = (bits_sent * 1000) / bps * 1000; + next_tx_ns = (bits_sent * 1000) / bps * 1000000; else - next_tx_us = (bits_sent * 1000000) / bps; + next_tx_ns = (bits_sent * 1000000000) / bps; - if (next_tx_us > tx_us) { - NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap); - } else if (tx_us > next_tx_us) { - tx_us = now_us - start_us; - *skip_length = ((tx_us - next_tx_us) * bps) / 8000000; + if (next_tx_ns > tx_ns) { + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); + } else if (tx_ns > next_tx_ns) { + tx_ns = now_ns - start_ns; + *skip_length = ((tx_ns - next_tx_ns) * bps) / 8000000000; } update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_us, tx_us, next_tx_us); + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet size=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, len, @@ -1050,12 +1050,12 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Ignore the time supplied by the capture file and send data at * a constant rate (packets per second). */ - now_us = TIMSTAMP_TO_MICROSEC(sent_timestamp); - if (now_us) { - COUNTER next_tx_us; + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; COUNTER pph = ctx->options->speed.speed; COUNTER pkts_sent = ctx->stats.pkts_sent; - COUNTER tx_us = now_us - start_us; + COUNTER tx_ns = now_ns - start_ns; /* * packets * 1000000 divided by pps = microseconds * packets per sec (pps) = packets per hour / (60 * 60) @@ -1064,17 +1064,17 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * When active, adjusted calculation may add a bit of jitter. */ if ((pkts_sent < COUNTER_OVERFLOW_RISK)) - next_tx_us = (pkts_sent * 1000000) * (60 * 60) / pph; + next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; else - next_tx_us = ((pkts_sent * 1000000) / pph) * (60 * 60); + next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); - if (next_tx_us > tx_us) - NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap); + if (next_tx_ns > tx_ns) + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); else ctx->skip_packets = options->speed.pps_multi; update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_us, tx_us, next_tx_us); + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, @@ -1105,7 +1105,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, } static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, - struct timespec *nap_this_time, struct timeval *now) + struct timespec *nap_this_time, struct timespec *now) { tcpreplay_opt_t *options = ctx->options; bool flush = diff --git a/src/signal_handler.c b/src/signal_handler.c index ddc43efb..17f546f9 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -35,7 +35,7 @@ #include "tcpreplay_api.h" #include "signal_handler.h" -struct timeval suspend_time; +struct timeval suspend_time; // PB: do we need to modify this part of the code? static struct timeval suspend_start; static struct timeval suspend_end; diff --git a/src/sleep.c b/src/sleep.c index 9042bc13..151e7a2c 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -55,32 +55,32 @@ ioport_sleep_init(void) void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, - struct timeval *now _U_, bool flush _U_) + struct timespec *now _U_, bool flush _U_) { #if defined HAVE_IOPORT_SLEEP__ - struct timeval nap_for; - u_int32_t usec; + struct timespec nap_for; + u_int32_t nsec; time_t i; - TIMESPEC_TO_TIMEVAL(&nap_for, nap); + TIMESPEC_SET(&nap_for, nap); /* * process the seconds, we do this in a loop so we don't have to * use slower 64bit integers or worry about integer overflows. */ for (i = 0; i < nap_for.tv_sec; i ++) { - usec = SEC_TO_MICROSEC(nap_for.tv_sec); + nsec = nap_for.tv_sec * 1000000000; while (usec > 0) { usec --; outb(ioport_sleep_value, 0x80); } } - /* process the usec */ - usec = nap->tv_nsec / 1000; - usec --; /* fudge factor for all the above */ - while (usec > 0) { - usec --; + /* process the nsec */ + nsec = nap->tv_nsec; + nsec --; /* fudge factor for all the above */ + while (nsec > 0) { + nsec --; outb(ioport_sleep_value, 0x80); } #else @@ -91,6 +91,7 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now); } diff --git a/src/sleep.h b/src/sleep.h index 5f67ca58..c3e92142 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,7 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush _U_) + struct timespec *now, bool flush _U_) { nanosleep(nap, NULL); #ifdef HAVE_NETMAP @@ -62,7 +63,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - gettimeofday(now, NULL); + clock_gettime(CLOCK_REALTIME, now); } @@ -74,36 +75,38 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, */ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timeval *now, bool flush _U_) + struct timespec *now, bool flush _U_) { - struct timeval sleep_until, nap_for; + struct timespec sleep_until, nap_for; + init_timestamp(&nap_for); #ifdef HAVE_NETMAP - struct timeval last; + struct timespec last; uint32_t i = 0; - TIMEVAL_SET(&last, now); + TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - TIMESPEC_TO_TIMEVAL(&nap_for, nap); - timeradd(now, &nap_for, &sleep_until); + timeradd_timespec(now, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timercmp(now, &last, !=)) { - TIMEVAL_SET(&last, now); + if (flush && timescmp(now, &last, !=)) { + TIMESPEC_SET(&last, now); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timercmp(now, &sleep_until, >=)) + if (timescmp(now, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now); } } @@ -115,16 +118,18 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, * for future reference */ static inline void -select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush _U_) +select_sleep(sendpacket_t *sp _U_, struct timespec *nap, + struct timespec *now_ns, bool flush _U_) { struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; #ifdef HAVE_NETMAP if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - TIMESPEC_TO_TIMEVAL(&timeout, nap); + TIMEVAL_TO_TIMESPEC(&timeout, nap); if (select(0, NULL, NULL, NULL, &timeout) < 0) warnx("select_sleep() returned early due to error: %s", strerror(errno)); @@ -133,8 +138,9 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now_ns); } #endif /* HAVE_SELECT */ @@ -150,6 +156,6 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, void ioport_sleep_init(void); void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush); + struct timespec *now, bool flush); #endif /* __SLEEP_H__ */ diff --git a/src/tcpbridge.c b/src/tcpbridge.c index b1e4f568..059b00f4 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -98,7 +98,7 @@ main(int argc, char *argv[]) } #endif - if (gettimeofday(&stats.start_time, NULL) < 0) { + if (get_time_of_day(&stats.start_time) < 0) { tcpedit_close(&tcpedit); err(-1, "gettimeofday() failed"); } diff --git a/src/tcpreplay.c b/src/tcpreplay.c index 144594b8..09d7b5c7 100644 --- a/src/tcpreplay.c +++ b/src/tcpreplay.c @@ -220,7 +220,7 @@ main(int argc, char *argv[]) */ static void flow_stats(const tcpreplay_t *ctx) { - struct timeval diff; + struct timespec diff; COUNTER diff_us; const tcpreplay_stats_t *stats = &ctx->stats; const tcpreplay_opt_t *options = ctx->options; @@ -232,8 +232,8 @@ static void flow_stats(const tcpreplay_t *ctx) COUNTER flows_sec = 0; u_int32_t flows_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 (!flows_total || !ctx->iteration) return; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index d429b91b..ebe7029b 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -31,9 +31,11 @@ #include #include #include +#include #include "tcpreplay_api.h" #include "send_packets.h" +#include "sleep.h" #include "replay.h" #ifdef TCPREPLAY_EDIT @@ -284,7 +286,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) if (HAVE_OPT(FLOW_EXPIRY)) { options->flow_expiry = OPT_VALUE_FLOW_EXPIRY; } - + ctx->timefunction.gettime = &get_time_of_day; if (HAVE_OPT(TIMER)) { if (strcmp(OPT_ARG(TIMER), "select") == 0) { #ifdef HAVE_SELECT @@ -305,6 +307,8 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; + ctx->timefunction.gettime = &clock_get_time; + options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); ret = -1; @@ -895,24 +899,24 @@ tcpreplay_get_failed(tcpreplay_t *ctx) } /** - * \brief returns a pointer to the timeval structure of when replay first started + * \brief returns a pointer to the timespec structure of when replay first started */ -const struct timeval * +const struct timespec * tcpreplay_get_start_time(tcpreplay_t *ctx) { assert(ctx); - TIMEVAL_SET(&ctx->static_stats.start_time, &ctx->stats.start_time); + TIMESPEC_SET(&ctx->static_stats.start_time, &ctx->stats.start_time); return &ctx->static_stats.start_time; } /** - * \brief returns a pointer to the timeval structure of when replay finished + * \brief returns a pointer to the timespec structure of when replay finished */ -const struct timeval * +const struct timespec * tcpreplay_get_end_time(tcpreplay_t *ctx) { assert(ctx); - TIMEVAL_SET(&ctx->static_stats.end_time, &ctx->stats.end_time); + TIMESPEC_SET(&ctx->static_stats.end_time, &ctx->stats.end_time); return &ctx->static_stats.end_time; } @@ -1122,7 +1126,7 @@ tcpreplay_replay(tcpreplay_t *ctx) tcpreplay_seterr(ctx, "invalid dualfile source count: %d", ctx->options->source_cnt); return -1; } - + init_timestamp(&ctx->stats.start_time); init_timestamp(&ctx->stats.time_delta); init_timestamp(&ctx->stats.end_time); @@ -1146,16 +1150,15 @@ tcpreplay_replay(tcpreplay_t *ctx) loop, total_loops, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) + if ((rcode = tcpr_replay_index(ctx)) < 0) { return rcode; + } if (ctx->options->loop > 0) { - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - gettimeofday(&ctx->stats.end_time, NULL); - } - - if (ctx->options->stats == 0) + apply_loop_delay(ctx); + ctx->timefunction.gettime(&ctx->stats.end_time); + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); + } } } } else { @@ -1168,14 +1171,11 @@ tcpreplay_replay(tcpreplay_t *ctx) printf("Loop " COUNTER_SPEC " (" COUNTER_SPEC " unique)...\n", loop, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) + if ((rcode = tcpr_replay_index(ctx)) < 0) { return rcode; - - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - gettimeofday(&ctx->stats.end_time, NULL); } - + apply_loop_delay(ctx); + ctx->timefunction.gettime(&ctx->stats.end_time); if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } @@ -1356,3 +1356,18 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } + +void apply_loop_delay(tcpreplay_t *ctx){ + if(ctx->options->accurate == accurate_nanosleep){ + if (!ctx->abort && ctx->options->loopdelay_ns > 0) { + struct timespec nap; + nap.tv_sec = 0; + nap.tv_nsec = ctx->options->loopdelay_ns; + nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); + } + }else{ + if (!ctx->abort && ctx->options->loopdelay_ms > 0) { + usleep(ctx->options->loopdelay_ms * 1000); + } + } +} diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 7c4e94e9..2acbda65 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -96,6 +96,10 @@ typedef struct { char *filename; } tcpreplay_source_t; +typedef struct time_function { + int (*gettime)(struct timespec*); +} time_function; + /* run-time options */ typedef struct tcpreplay_opt_s { /* input/output */ @@ -105,6 +109,7 @@ typedef struct tcpreplay_opt_s { tcpreplay_speed_t speed; COUNTER loop; u_int32_t loopdelay_ms; + u_int32_t loopdelay_ns; int stats; bool use_pkthdr_len; @@ -189,6 +194,7 @@ typedef struct tcpreplay_s { struct timespec nap; uint32_t skip_packets; bool first_time; + struct time_function timefunction; /* counter stats */ tcpreplay_stats_t stats; @@ -270,13 +276,14 @@ int tcpreplay_set_manual_callback(tcpreplay_t *ctx, tcpreplay_manual_callback); COUNTER tcpreplay_get_pkts_sent(tcpreplay_t *ctx); COUNTER tcpreplay_get_bytes_sent(tcpreplay_t *ctx); COUNTER tcpreplay_get_failed(tcpreplay_t *ctx); -const struct timeval *tcpreplay_get_start_time(tcpreplay_t *ctx); -const struct timeval *tcpreplay_get_end_time(tcpreplay_t *ctx); +const struct timespec *tcpreplay_get_start_time(tcpreplay_t *ctx); +const struct timespec *tcpreplay_get_end_time(tcpreplay_t *ctx); int tcpreplay_set_verbose(tcpreplay_t *, bool); int tcpreplay_set_tcpdump_args(tcpreplay_t *, char *); int tcpreplay_set_tcpdump(tcpreplay_t *, tcpdump_t *); +void apply_loop_delay(tcpreplay_t *ctx); /* * These functions are seen by the outside world, but nobody should ever use them * outside of internal tcpreplay API functions diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a0036f76..39006506 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -328,6 +328,19 @@ flag = { doc = ""; }; +flag = { + name = loopdelay-ns; + flags-must = loop, timer; + arg-type = number; + arg-range = "0->"; + descrip = "Delay between loops in nanoseconds"; + arg-default = 0; + doc = <<- EOText +By default, tcpreplay will use loop delay with microsecond accuracy (loopdelay-ms). +In order to use loop delay with nanosecond accuracy you need to use nano packet timing mode. +EOText; +}; + flag = { name = pktlen; max = 1; diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index 2d19336d..972d4196 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -29,9 +29,9 @@ struct timestamp_trace_entry { COUNTER skip_length; COUNTER size; COUNTER bytes_sent; - COUNTER now_us; - COUNTER tx_us; - COUNTER next_tx_us; + COUNTER now_ns; + COUNTER tx_ns; + COUNTER next_tx_ns; COUNTER sent_bits; struct timeval timestamp; }; @@ -60,7 +60,7 @@ static inline void update_current_timestamp_trace_entry(COUNTER bytes_sent, } static inline void add_timestamp_trace_entry(COUNTER size, - struct timeval *timestamp, COUNTER skip_length) + struct timespec *timestamp, COUNTER skip_length) { if (trace_num >= TRACE_MAX_ENTRIES) return; @@ -68,7 +68,7 @@ static inline void add_timestamp_trace_entry(COUNTER size, timestamp_trace_entry_array[trace_num].skip_length = skip_length; timestamp_trace_entry_array[trace_num].size = size; timestamp_trace_entry_array[trace_num].timestamp.tv_sec = timestamp->tv_sec; - timestamp_trace_entry_array[trace_num].timestamp.tv_usec = timestamp->tv_usec; + timestamp_trace_entry_array[trace_num].timestamp.tv_nsec = timestamp->tv_nsec; ++trace_num; } @@ -102,7 +102,7 @@ static inline void dump_timestamp_trace_array(const struct timeval *start, #else static inline void update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), COUNTER UNUSED(now_us), COUNTER UNUSED(tx_us), COUNTER UNUSED(next_tx_us)) { } -static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timeval *UNUSED(timestamp), +static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), COUNTER UNUSED(skip_length)) { } static inline void dump_timestamp_trace_array(const struct timeval *UNUSED(start), const struct timeval *UNUSED(stop), const COUNTER UNUSED(bps)) { } diff --git a/test/Makefile.am b/test/Makefile.am index 405438aa..d1925e19 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -196,7 +196,7 @@ tcprewrite: rewrite_portmap rewrite_range_portmap rewrite_endpoint \ rewrite_mac_seed_keep rewrite_l7fuzzing rewrite_sequence rewrite_fixcsum \ rewrite_fixlen_pad rewrite_fixlen_trunc rewrite_fixlen_del -tcpreplay: replay_basic replay_cache replay_pps replay_rate replay_top \ +tcpreplay: replay_basic replay_nano_timer replay_cache replay_pps replay_rate replay_top \ replay_config replay_multi replay_pps_multi replay_precache \ replay_stats replay_dualfile replay_maxsleep @@ -344,6 +344,12 @@ replay_basic: $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) -t $(TEST_PCAP) >> test.log 2>&1 if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi +replay_nano_timer: + $(PRINTF) "%s" "[tcpreplay] Nano timer test: " + $(PRINTF) "%s\n" "*** [tcpreplay] Nano timer test: " >> test.log + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --timer=nano --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 + if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi + replay_cache: $(PRINTF) "%s" "[tcpreplay] Cache test: " $(PRINTF) "%s\n" "*** [tcpreplay] Cache test: " >> test.log From 29976333f4b765f91ccfdf999cf1dbc336821b34 Mon Sep 17 00:00:00 2001 From: eplabal Date: Sun, 12 Mar 2023 14:35:53 +0100 Subject: [PATCH 02/11] Use clock_nanosleep with TIMER_ABSTIME, decide time function in use in compile time --- src/bridge.c | 4 ++-- src/common/timer.c | 18 +++++++++--------- src/common/timer.h | 3 +-- src/send_packets.c | 20 ++++++++++---------- src/sleep.c | 4 +--- src/sleep.h | 15 ++++++++++----- src/tcpbridge.c | 4 ++-- src/tcpreplay_api.c | 7 +++---- src/tcpreplay_api.h | 5 ----- src/timestamp_trace.h | 6 +++--- 10 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index cd1b3b76..75450bfa 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -238,8 +238,8 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) do_bridge_bidirectional(options, tcpedit); } - if (get_time_of_day(&stats.end_time) < 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); } diff --git a/src/common/timer.c b/src/common/timer.c index 6ed978ef..5b62e724 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -91,13 +91,13 @@ init_timestamp(struct timespec *timestamp) timesclear(timestamp); } -int get_time_of_day(struct timespec *ts) { - struct timeval tv; - int success = gettimeofday(&tv, NULL); - TIMEVAL_TO_TIMESPEC(&tv, ts); - return success; -} - -int clock_get_time(struct timespec *ts){ - return clock_gettime(CLOCK_REALTIME, ts); +int get_current_time(struct timespec *ts){ + #ifdef _POSIX_C_SOURCE >= 199309L + return clock_gettime(CLOCK_REALTIME, ts); + #else + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; + #endif } diff --git a/src/common/timer.h b/src/common/timer.h index 5e48d34c..c7a901f2 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -200,7 +200,6 @@ void timesdiv(struct timespec *tvs, COUNTER div); typedef struct timeval timestamp_t; void init_timestamp(struct timespec *timestamp); -int get_time_of_day(struct timespec *ts); -int clock_get_time(struct timespec *ts); +int get_current_time(struct timespec *ts); #endif /* _TIMER_H_ */ diff --git a/src/send_packets.c b/src/send_packets.c index a129d4d9..2314e029 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -339,7 +339,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); if (!timesisset(&stats->start_time)) { TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { @@ -448,7 +448,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) if (!top_speed) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } /* @@ -535,18 +535,18 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } } #endif /* HAVE_NETMAP */ if (!now_is_now) - ctx->timefunction.gettime(&now); + get_current_time(&now); TIMESPEC_SET(&stats->end_time, &now); @@ -581,7 +581,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); if (!timesisset(&stats->start_time)) { TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { @@ -723,7 +723,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } if (!top_speed) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } @@ -814,19 +814,19 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } } #endif /* HAVE_NETMAP */ if (!now_is_now) - ctx->timefunction.gettime(&now); + get_current_time(&now); TIMESPEC_SET(&stats->end_time, &now); diff --git a/src/sleep.c b/src/sleep.c index 151e7a2c..f666a8cd 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -91,7 +91,5 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - struct timeval now_ms; - gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now); + get_current_time(now); } diff --git a/src/sleep.h b/src/sleep.h index c3e92142..20eee331 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -57,13 +57,20 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - nanosleep(nap, NULL); + #ifdef _POSIX_C_SOURCE >= 200112L + struct timespec sleep_until; + timeradd_timespec(now, nap, &sleep_until); + clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); + #else + nanosleep(nap, NULL); + #endif + #ifdef HAVE_NETMAP if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - clock_gettime(CLOCK_REALTIME, now); + get_current_time(now); } @@ -138,9 +145,7 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - struct timeval now_ms; - gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now_ns); + get_current_time(now_ns); } #endif /* HAVE_SELECT */ diff --git a/src/tcpbridge.c b/src/tcpbridge.c index 059b00f4..42641cd4 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -98,9 +98,9 @@ main(int argc, char *argv[]) } #endif - if (get_time_of_day(&stats.start_time) < 0) { + if (get_current_time(&stats.start_time) < 0) { tcpedit_close(&tcpedit); - err(-1, "gettimeofday() failed"); + err(-1, "get_current_time() failed"); } /* process packets */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index ebe7029b..431aa61d 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -286,7 +286,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) if (HAVE_OPT(FLOW_EXPIRY)) { options->flow_expiry = OPT_VALUE_FLOW_EXPIRY; } - ctx->timefunction.gettime = &get_time_of_day; + if (HAVE_OPT(TIMER)) { if (strcmp(OPT_ARG(TIMER), "select") == 0) { #ifdef HAVE_SELECT @@ -307,7 +307,6 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; - ctx->timefunction.gettime = &clock_get_time; options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); @@ -1155,7 +1154,7 @@ tcpreplay_replay(tcpreplay_t *ctx) } if (ctx->options->loop > 0) { apply_loop_delay(ctx); - ctx->timefunction.gettime(&ctx->stats.end_time); + get_current_time(&ctx->stats.end_time); if (ctx->options->stats == 0) { packet_stats(&ctx->stats); } @@ -1175,7 +1174,7 @@ tcpreplay_replay(tcpreplay_t *ctx) return rcode; } apply_loop_delay(ctx); - ctx->timefunction.gettime(&ctx->stats.end_time); + get_current_time(&ctx->stats.end_time); if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 2acbda65..e26f65a4 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -96,10 +96,6 @@ typedef struct { char *filename; } tcpreplay_source_t; -typedef struct time_function { - int (*gettime)(struct timespec*); -} time_function; - /* run-time options */ typedef struct tcpreplay_opt_s { /* input/output */ @@ -194,7 +190,6 @@ typedef struct tcpreplay_s { struct timespec nap; uint32_t skip_packets; bool first_time; - struct time_function timefunction; /* counter stats */ tcpreplay_stats_t stats; diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index 972d4196..058bbd64 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -48,9 +48,9 @@ static inline void update_current_timestamp_trace_entry(COUNTER bytes_sent, return; if (!now_us) { - struct timeval now; - gettimeofday(&now, NULL); - now_us = TIMSTAMP_TO_MICROSEC(&now); + struct timespec now; + get_current_time(now); + now_us = TIMESPEC_TO_MICROSEC(&now); } timestamp_trace_entry_array[trace_num].bytes_sent = bytes_sent; From 00e9f83646dc2e85ad075d8f62dad096bddf529e Mon Sep 17 00:00:00 2001 From: bplangar Date: Sun, 23 Apr 2023 00:25:05 +0200 Subject: [PATCH 03/11] Read pcap files with nanosec precision, set nano_sleep as default sleep method --- src/common/timer.c | 2 +- src/defines.h.in | 7 +++++++ src/replay.c | 18 +++++++++--------- src/send_packets.c | 32 ++++++++++++++++---------------- src/sleep.h | 2 +- 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/common/timer.c b/src/common/timer.c index 5b62e724..23b6ee0e 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -92,7 +92,7 @@ init_timestamp(struct timespec *timestamp) } int get_current_time(struct timespec *ts){ - #ifdef _POSIX_C_SOURCE >= 199309L + #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L return clock_gettime(CLOCK_REALTIME, ts); #else struct timeval tv; diff --git a/src/defines.h.in b/src/defines.h.in index 20e41a70..d0267df5 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -358,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 diff --git a/src/replay.c b/src/replay.c index b3334ae6..e7cf3d76 100644 --- a/src/replay.c +++ b/src/replay.c @@ -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; } @@ -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); } } @@ -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 */ diff --git a/src/send_packets.c b/src/send_packets.c index 2314e029..dde51022 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -316,8 +316,7 @@ static void increment_iteration(tcpreplay_t *ctx) void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) { - struct timeval last_pkt_ts; - struct timespec now, print_delta; + struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -350,7 +349,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) } ctx->skip_packets = 0; - timerclear(&last_pkt_ts); + timesclear(&last_pkt_ts); if (options->limit_time > 0) end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); @@ -369,7 +368,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) */ while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { - + TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -429,20 +428,20 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * time stamping is expensive, but now is the * time to do it. */ - dbgx(4, "This packet time: " TIMEVAL_FORMAT, pkthdr.ts.tv_sec, - pkthdr.ts.tv_usec); + dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, + pkthdr_ts.tv_nsec); skip_length = 0; ctx->skip_packets = 0; if (options->speed.mode == speed_multiplier) { - if (!timerisset(&last_pkt_ts)) { - TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); - } else if (timercmp(&pkthdr.ts, &last_pkt_ts, >)) { - struct timeval delta; - - timersub(&pkthdr.ts, &last_pkt_ts, &delta); - timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); - TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); + if (!timesisset(&last_pkt_ts)) { + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); + } else if (timescmp(&pkthdr_ts, &last_pkt_ts, >)) { + struct timespec delta; + + timessub(&pkthdr_ts, &last_pkt_ts, &delta); + timeradd_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); } } @@ -999,7 +998,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); - TIMESPEC_SET(&nap_for, &ctx->nap); + TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1117,8 +1116,9 @@ static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)) + if (!timesisset(nap_this_time)){ return; + } /* do we need to limit the total time we sleep? */ if (timesisset(&(options->maxsleep)) && (timescmp(nap_this_time, &(options->maxsleep), >))) { diff --git a/src/sleep.h b/src/sleep.h index 20eee331..0ba9b878 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -57,9 +57,9 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - #ifdef _POSIX_C_SOURCE >= 200112L struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); + #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); From a044d72aa58c596aaa00ab95517d25d8b9c1f03a Mon Sep 17 00:00:00 2001 From: bplangar Date: Tue, 2 May 2023 21:12:35 +0200 Subject: [PATCH 04/11] fix gettimeofday_sleep function, add nap to now variable to get sleep_until --- src/sleep.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sleep.h b/src/sleep.h index 0ba9b878..dd22f320 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -84,8 +84,7 @@ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until, nap_for; - init_timestamp(&nap_for); + struct timespec sleep_until; #ifdef HAVE_NETMAP struct timespec last; uint32_t i = 0; @@ -93,8 +92,7 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - timeradd_timespec(now, &nap_for, &sleep_until); - + timeradd_timespec(now, nap, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP if (flush && timescmp(now, &last, !=)) { From 77a14488621028a38a536d04733105f9965730d6 Mon Sep 17 00:00:00 2001 From: bplangar Date: Tue, 2 May 2023 22:05:12 +0200 Subject: [PATCH 05/11] use CLOCK_MONOTONIC in clock_gettime function, modify gettimeofday_sleep to work with CLOCK_MONOTONIC --- src/common/timer.c | 2 +- src/sleep.h | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/common/timer.c b/src/common/timer.c index 23b6ee0e..18d589bb 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -93,7 +93,7 @@ init_timestamp(struct timespec *timestamp) int get_current_time(struct timespec *ts){ #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L - return clock_gettime(CLOCK_REALTIME, ts); + return clock_gettime(CLOCK_MONOTONIC, ts); #else struct timeval tv; int success = gettimeofday(&tv, NULL); diff --git a/src/sleep.h b/src/sleep.h index dd22f320..71ce39ab 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -84,35 +84,34 @@ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until; + struct timeval now_ms, sleep_until, nap_for, last; + TIMESPEC_TO_TIMEVAL(&nap_for, nap); + gettimeofday(&now_ms, NULL); #ifdef HAVE_NETMAP - struct timespec last; uint32_t i = 0; - - TIMESPEC_SET(&last, now); + TIMEVAL_SET(&last, &now_ms); #endif /* HAVE_NETMAP */ - - timeradd_timespec(now, nap, &sleep_until); + + timeradd(&now_ms, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timescmp(now, &last, !=)) { - TIMESPEC_SET(&last, now); + if (flush && timercmp(&now_ms, &last, !=)) { + TIMESPEC_SET(&last, &now_ms); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timescmp(now, &sleep_until, >=)) + if (timercmp(&now_ms, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - struct timeval now_ms; gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now); } + get_current_time(now); } #ifdef HAVE_SELECT From 6085e4f2a7dd77a8b96aff2912e2a43ef52e2760 Mon Sep 17 00:00:00 2001 From: bplangar Date: Wed, 3 May 2023 00:16:51 +0200 Subject: [PATCH 06/11] fix time related bug in send_dual_packets function --- src/send_packets.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/send_packets.c b/src/send_packets.c index dde51022..437a5bec 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -559,8 +559,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) void send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *pcap2, int cache_file_idx2) { - struct timeval last_pkt_ts; - struct timespec now, print_delta; + struct timespec now, print_delta, last_pkt_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -591,7 +590,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } ctx->skip_packets = 0; - timerclear(&last_pkt_ts); + timesclear(&last_pkt_ts); if (options->limit_time > 0) end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); @@ -707,14 +706,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; if (options->speed.mode == speed_multiplier) { - if (!timerisset(&last_pkt_ts)) { - TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); - } else if (timercmp(&pkthdr_ptr->ts, &last_pkt_ts, >)) { - struct timeval delta; - - timersub(&pkthdr_ptr->ts, &last_pkt_ts, &delta); - timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); - TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); + struct timespec pkthdr_ts; + TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &pkthdr_ts); + if (!timesisset(&last_pkt_ts)) { + TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &last_pkt_ts); + } else if (timescmp(&pkthdr_ts, &last_pkt_ts, >)) { + struct timespec delta; + + timessub(&pkthdr_ts, &last_pkt_ts, &delta); + timeradd_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); } if (!timesisset(&stats->time_delta)) @@ -998,6 +999,12 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); + // printf("pkt_ts_delta sec: %lu\n", pkt_ts_delta->tv_sec); + // printf("pkt_ts_delta nsec: %lu\n", pkt_ts_delta->tv_nsec); + // printf("time_delta sec: %lu\n", time_delta->tv_sec); + // printf("time_delta nsec: %lu\n", time_delta->tv_nsec); + // printf("nap_for sec: %lu\n", nap_for.tv_sec); + // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); From ba8c8079f184a649d7757b2868c44c015a1b2db6 Mon Sep 17 00:00:00 2001 From: bplangar Date: Thu, 11 May 2023 20:24:40 +0200 Subject: [PATCH 07/11] change CLOCK_REALTIME to CLOCK_MONOTONIC flag in clock_nanosleep --- src/sleep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sleep.h b/src/sleep.h index 71ce39ab..bf2f4f4e 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -60,7 +60,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L - clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); #endif From 6af43412b876a6abeaaf2799961b5dfac0deb2c1 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:17:08 -0800 Subject: [PATCH 08/11] Feature #796: nanosecond -changelog/credit --- docs/CHANGELOG | 1 + docs/CREDIT | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 331e4de3..fa49fc84 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 09/03/2023 Version 4.5.0-beta1 + - nanosecond timestamps (#796) - low PPS values run at full speed after several days (#779) - create DLT_LINUX_SLL2 plugin (#727) diff --git a/docs/CREDIT b/docs/CREDIT index 38e200f6..26583a35 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -116,3 +116,6 @@ David Guti Bastian Triller - Linux SLL2 + +GithHub @plangarbalint + - nanosecond timers \ No newline at end of file From f9e2f703c8bc82d7aecef69efb46fa3a6700f6c5 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:35:16 -0800 Subject: [PATCH 09/11] Feature #796: clang-format mods format commit with `git-clang-format HEAD~1` --- src/bridge.c | 2 +- src/common/timer.c | 20 ++--- src/common/timer.h | 36 +++++---- src/common/utils.c | 15 +++- src/replay.c | 16 ++-- src/send_packets.c | 166 +++++++++++++++++++++++------------------- src/sleep.c | 13 ++-- src/sleep.h | 35 ++++----- src/tcpreplay_api.c | 32 ++++---- src/timestamp_trace.h | 24 ++++-- 10 files changed, 191 insertions(+), 168 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index 653f9192..69efa31c 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -211,7 +211,7 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) } if (get_current_time(&stats.end_time) < 0) - errx(-1, "get_current_time() failed: %s", strerror(errno)); + errx(-1, "get_current_time() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index be447673..60e10bf2 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -44,13 +44,15 @@ init_timestamp(struct timespec *timestamp) 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; - #endif +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; +#endif } diff --git a/src/common/timer.h b/src/common/timer.h index 71baade3..0cf801fa 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -114,33 +114,31 @@ void timesdiv_float(struct timespec *tvs, float div); /* 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; \ - } \ +#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; \ - } \ +#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) \ diff --git a/src/common/utils.c b/src/common/utils.c index 764a37e1..8e69834e 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -253,10 +253,16 @@ packet_stats(const tcpreplay_stats_t *stats) 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_nsec / (10 * 1000000))); - } 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_nsec / 1000); + stats->pkts_sent, + stats->bytes_sent, + (ssize_t)diff.tv_sec, + (ssize_t)diff.tv_nsec / 1000); } if (mb_sec >= 1) @@ -289,7 +295,8 @@ 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 timespec *when, char *buf, size_t len) +int +format_date_time(struct timespec *when, char *buf, size_t len) { struct tm *tm; char tmp[64]; diff --git a/src/replay.c b/src/replay.c index 03b2abb6..c01206d6 100644 --- a/src/replay.c +++ b/src/replay.c @@ -133,10 +133,10 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - 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; - } + 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); } } @@ -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_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, 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 */ diff --git a/src/send_packets.c b/src/send_packets.c index 53fe0a52..e8d41fbc 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -60,16 +60,18 @@ extern tcpedit_t *tcpedit; extern int debug; #endif -static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, - struct timespec *last, COUNTER len, - sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, - COUNTER start_us, COUNTER *skip_length); -static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, - struct timespec *nap_this_time, struct timespec *now); -static u_char *get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, - struct pcap_pkthdr *pkthdr, - int file_idx, - packet_cache_t **prev_packet); +static void calc_sleep_time(tcpreplay_t *ctx, + struct timespec *pkt_time, + struct timespec *last, + COUNTER len, + sendpacket_t *sp, + COUNTER counter, + struct timespec *sent_timestamp, + COUNTER start_us, + COUNTER *skip_length); +static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, struct timespec *nap_this_time, struct timespec *now); +static u_char * +get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, packet_cache_t **prev_packet); static uint32_t get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter); #ifdef HAVE_NETMAP @@ -333,7 +335,7 @@ increment_iteration(tcpreplay_t *ctx) */ void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) -{ +{ struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; @@ -370,8 +372,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) ctx->skip_packets = 0; timesclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + - SEC_TO_MICROSEC(options->limit_time); + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -385,8 +386,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * Keep sending while we have packets or until * we've sent enough packets */ - while (!ctx->abort && - (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; @@ -444,8 +444,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * time stamping is expensive, but now is the * time to do it. */ - dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, - pkthdr_ts.tv_nsec); + dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, pkthdr_ts.tv_nsec); skip_length = 0; ctx->skip_packets = 0; @@ -472,9 +471,15 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * This also sets skip_length and skip_packets which will avoid * timestamping for a given number of packets. */ - calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, - pktlen, sp, packetnum, &stats->end_time, - TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + calc_sleep_time(ctx, + &stats->pkt_ts_delta, + &stats->time_delta, + pktlen, + sp, + packetnum, + &stats->end_time, + TIMESPEC_TO_NANOSEC(&stats->start_time), + &skip_length); /* * Track the time of the "last packet sent". * @@ -544,7 +549,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #endif /* print stats during the run? */ if (options->stats > 0) { - if (! timesisset(&stats->last_print)) { + if (!timesisset(&stats->last_print)) { TIMESPEC_SET(&stats->last_print, &now); } else { timessub(&now, &stats->last_print, &print_delta); @@ -564,8 +569,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #endif /* stop sending based on the duration limit... */ if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + /* ... or stop sending based on the limit -L? */ + (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ @@ -633,8 +638,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; timesclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + - SEC_TO_MICROSEC(options->limit_time); + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -759,9 +763,15 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * * This also sets skip_length and skip_packets which will avoid * timestamping for a given number of packets. */ - calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, - pktlen, sp, packetnum, &stats->end_time, - TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + calc_sleep_time(ctx, + &stats->pkt_ts_delta, + &stats->time_delta, + pktlen, + sp, + packetnum, + &stats->end_time, + TIMESPEC_TO_NANOSEC(&stats->start_time), + &skip_length); /* * Track the time of the "last packet sent". @@ -802,7 +812,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* print stats during the run? */ if (options->stats > 0) { - if (! timesisset(&stats->last_print)) { + if (!timesisset(&stats->last_print)) { TIMESPEC_SET(&stats->last_print, &now); } else { timessub(&now, &stats->last_print, &print_delta); @@ -830,8 +840,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* stop sending based on the duration limit... */ if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + /* ... or stop sending based on the limit -L? */ + (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ @@ -983,10 +993,16 @@ cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num) * calculate the appropriate amount of time to sleep. Sleep time * will be in ctx->nap. */ -static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, - struct timespec *time_delta, COUNTER len, - sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, - COUNTER start_ns, COUNTER *skip_length) +static void +calc_sleep_time(tcpreplay_t *ctx, + struct timespec *pkt_ts_delta, + struct timespec *time_delta, + COUNTER len, + sendpacket_t *sp, + COUNTER counter, + struct timespec *sent_timestamp, + COUNTER start_ns, + COUNTER *skip_length) { tcpreplay_opt_t *options = ctx->options; struct timespec nap_for; @@ -1024,8 +1040,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, // printf("nap_for sec: %lu\n", nap_for.tv_sec); // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); - dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, - ctx->nap.tv_sec, ctx->nap.tv_nsec); + dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); dbgx(3, "original packet delta/div: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); } @@ -1040,7 +1055,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (now_ns) { COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); //PB: Ferencol miert bits sent? + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); // PB: Ferencol miert bits sent? COUNTER tx_ns = now_ns - start_ns; /* @@ -1060,8 +1075,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, *skip_length = ((tx_ns - next_tx_ns) * bps) / 8000000000; } - update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_ns, tx_ns, next_tx_ns); + update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet size=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, len, ctx->nap.tv_sec, ctx->nap.tv_nsec); @@ -1069,38 +1083,40 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, case speed_packetrate: /* - * Ignore the time supplied by the capture file and send data at - * a constant rate (packets per second). - */ - now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); - if (now_ns) { - COUNTER next_tx_ns; - COUNTER pph = ctx->options->speed.speed; - COUNTER pkts_sent = ctx->stats.pkts_sent; - COUNTER tx_ns = now_ns - start_ns; - /* - * packets * 1000000 divided by pps = microseconds - * packets per sec (pps) = packets per hour / (60 * 60) - * - * Adjust for long running tests with high PPS to prevent overflow. - * When active, adjusted calculation may add a bit of jitter. - */ - if ((pkts_sent < COUNTER_OVERFLOW_RISK)) - next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; - else - next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); - - if (next_tx_ns > tx_ns) - NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); - else - ctx->skip_packets = options->speed.pps_multi; - - update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_ns, tx_ns, next_tx_ns); - } - - dbgx(3, "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, - ctx->stats.pkts_sent, ctx->nap.tv_sec, ctx->nap.tv_nsec); + * Ignore the time supplied by the capture file and send data at + * a constant rate (packets per second). + */ + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; + COUNTER pph = ctx->options->speed.speed; + COUNTER pkts_sent = ctx->stats.pkts_sent; + COUNTER tx_ns = now_ns - start_ns; + /* + * packets * 1000000 divided by pps = microseconds + * packets per sec (pps) = packets per hour / (60 * 60) + * + * Adjust for long running tests with high PPS to prevent overflow. + * When active, adjusted calculation may add a bit of jitter. + */ + if ((pkts_sent < COUNTER_OVERFLOW_RISK)) + next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; + else + next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); + + if (next_tx_ns > tx_ns) + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); + else + ctx->skip_packets = options->speed.pps_multi; + + update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_ns, tx_ns, next_tx_ns); + } + + dbgx(3, + "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, + ctx->stats.pkts_sent, + ctx->nap.tv_sec, + ctx->nap.tv_nsec); break; case speed_oneatatime: @@ -1126,8 +1142,8 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, } } -static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, - struct timespec *nap_this_time, struct timespec *now) +static void +tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, struct timespec *now) { tcpreplay_opt_t *options = ctx->options; bool flush = @@ -1138,7 +1154,7 @@ static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, #endif /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)){ + if (!timesisset(nap_this_time)) { return; } diff --git a/src/sleep.c b/src/sleep.c index 160a6956..16234a43 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -48,9 +48,8 @@ ioport_sleep_init(void) #endif } -void -ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, - struct timespec *now _U_, bool flush _U_) +void +ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timespec *now _U_, bool flush _U_) { #if defined HAVE_IOPORT_SLEEP__ struct timespec nap_for; @@ -63,7 +62,7 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, * process the seconds, we do this in a loop so we don't have to * use slower 64bit integers or worry about integer overflows. */ - for (i = 0; i < nap_for.tv_sec; i ++) { + for (i = 0; i < nap_for.tv_sec; i++) { nsec = nap_for.tv_sec * 1000000000; while (usec > 0) { usec--; @@ -73,10 +72,10 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, /* process the nsec */ nsec = nap->tv_nsec; - nsec --; /* fudge factor for all the above */ + nsec--; /* fudge factor for all the above */ while (nsec > 0) { - nsec --; - outb(ioport_sleep_value, 0x80); + nsec--; + outb(ioport_sleep_value, 0x80); } #else err(-1, "Platform does not support IO Port for timing"); diff --git a/src/sleep.h b/src/sleep.h index fe0e8be7..71365ab5 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -28,14 +28,11 @@ #include #endif -#include -#include -#include -#include #include #include #include #include +#include #include #ifdef HAVE_SYS_EVENT #include @@ -55,16 +52,15 @@ #endif /* HAVE_NETMAP */ static inline void -nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timespec *now, bool flush _U_) +nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until; - timeradd_timespec(now, nap, &sleep_until); - #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); - #else - nanosleep(nap, NULL); - #endif + struct timespec sleep_until; + timeradd_timespec(now, nap, &sleep_until); +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); +#else + nanosleep(nap, NULL); +#endif #ifdef HAVE_NETMAP if (flush) @@ -81,8 +77,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, * Note: make sure "now" has recently been updated. */ static inline void -gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timespec *now, bool flush _U_) +gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { struct timeval now_ms, sleep_until, nap_for, last; TIMESPEC_TO_TIMEVAL(&nap_for, nap); @@ -91,7 +86,7 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, uint32_t i = 0; TIMEVAL_SET(&last, &now_ms); #endif /* HAVE_NETMAP */ - + timeradd(&now_ms, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP @@ -121,9 +116,8 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, * resolution which is pretty much useless for our needs. Keeping it here * for future reference */ -static inline void -select_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timespec *now_ns, bool flush _U_) +static inline void +select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns, bool flush _U_) { struct timeval timeout; timeout.tv_sec = 0; @@ -157,7 +151,6 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, /* before calling port_sleep(), you have to call port_sleep_init() */ void ioport_sleep_init(void); -void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timespec *now, bool flush); +void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush); #endif /* __SLEEP_H__ */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 8b4c4043..319ae109 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -18,25 +18,23 @@ * along with the Tcpreplay Suite. If not, see . */ -#include "config.h" +#include "tcpreplay_api.h" #include "defines.h" +#include "config.h" #include "common.h" - +#include "replay.h" +#include "send_packets.h" +#include "sleep.h" #include +#include #include +#include #include #include #include #include -#include -#include -#include #include - -#include "tcpreplay_api.h" -#include "send_packets.h" -#include "sleep.h" -#include "replay.h" +#include #ifdef TCPREPLAY_EDIT #include "tcpreplay_edit_opts.h" @@ -1145,7 +1143,7 @@ tcpreplay_replay(tcpreplay_t *ctx) tcpreplay_seterr(ctx, "invalid dualfile source count: %d", ctx->options->source_cnt); return -1; } - + init_timestamp(&ctx->stats.start_time); init_timestamp(&ctx->stats.time_delta); init_timestamp(&ctx->stats.end_time); @@ -1383,17 +1381,19 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } -void apply_loop_delay(tcpreplay_t *ctx){ - if(ctx->options->accurate == accurate_nanosleep){ +void +apply_loop_delay(tcpreplay_t *ctx) +{ + if (ctx->options->accurate == accurate_nanosleep) { if (!ctx->abort && ctx->options->loopdelay_ns > 0) { struct timespec nap; nap.tv_sec = 0; nap.tv_nsec = ctx->options->loopdelay_ns; nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); } - }else{ + } else { if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - } + usleep(ctx->options->loopdelay_ms * 1000); + } } } diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index f567f810..19d7548d 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -57,8 +57,8 @@ update_current_timestamp_trace_entry(COUNTER bytes_sent, COUNTER now_us, COUNTER timestamp_trace_entry_array[trace_num].next_tx_us = next_tx_us; } -static inline void add_timestamp_trace_entry(COUNTER size, - struct timespec *timestamp, COUNTER skip_length) +static inline void +add_timestamp_trace_entry(COUNTER size, struct timespec *timestamp, COUNTER skip_length) { if (trace_num >= TRACE_MAX_ENTRIES) return; @@ -100,10 +100,18 @@ dump_timestamp_trace_array(const struct timeval *start, const struct timeval *st } } #else -static inline void update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), COUNTER UNUSED(now_us), - COUNTER UNUSED(tx_us), COUNTER UNUSED(next_tx_us)) { } -static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), - COUNTER UNUSED(skip_length)) { } -static inline void dump_timestamp_trace_array(const struct timeval *UNUSED(start), - const struct timeval *UNUSED(stop), const COUNTER UNUSED(bps)) { } +static inline void +update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), + COUNTER UNUSED(now_us), + COUNTER UNUSED(tx_us), + COUNTER UNUSED(next_tx_us)) +{} +static inline void +add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), COUNTER UNUSED(skip_length)) +{} +static inline void +dump_timestamp_trace_array(const struct timeval *UNUSED(start), + const struct timeval *UNUSED(stop), + const COUNTER UNUSED(bps)) +{} #endif /* TIMESTAMP_TRACE */ From dda4343d2fe6cb37fec6775a11ccb463d8cac895 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:56:20 -0800 Subject: [PATCH 10/11] Feature #796: fix netmap, get_current_time etc. * macOS was not reading nanosecond timer * get time called too often, causing overhead * fine tune gettimeofday_sleep() * adjusted overflow value to prevent issues with very long running instances * fix build/merge issues --- .clang-format | 15 ++++++++++++- docs/CREDIT | 1 + src/common/timer.c | 4 ++-- src/common/timer.h | 4 +--- src/common/utils.c | 4 ++-- src/defines.h.in | 2 +- src/send_packets.c | 31 +++++++++++--------------- src/signal_handler.c | 2 +- src/sleep.c | 6 ------ src/sleep.h | 27 +++++++++++------------ src/tcpreplay_api.c | 49 ++++++++++++++++++++++-------------------- src/tcpreplay_api.h | 1 - src/tcpreplay_opts.def | 4 +++- 13 files changed, 77 insertions(+), 73 deletions(-) diff --git a/.clang-format b/.clang-format index 0b3aa238..56e516cd 100644 --- a/.clang-format +++ b/.clang-format @@ -3,10 +3,12 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: None AlignOperands: Align +AlignTrailingComments: true AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Always +AllowShortEnumsOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: All @@ -16,7 +18,7 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: TopLevelDefinitions AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: ['__capability', '__output', '__ununsed', '_U_'] +AttributeMacros: ['__capability', '__output', '__unused', '_U_'] BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: None @@ -28,9 +30,11 @@ BraceWrapping: AfterEnum: false AfterFunction: true AfterNamespace: false + AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false + BeforeWhile: false IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: true @@ -69,7 +73,12 @@ IncludeCategories: Priority: 6 IndentGotoLabels: false IndentPPDirectives: None +IndentRequiresClause: false +IndentExternBlock: AfterExternBlock IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertTrailingCommas: None KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 1 NamespaceIndentation: None @@ -86,12 +95,16 @@ SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Never SpacesInConditionalStatement: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 8 diff --git a/docs/CREDIT b/docs/CREDIT index 26583a35..d21aee22 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -118,4 +118,5 @@ Bastian Triller - Linux SLL2 GithHub @plangarbalint + - eBPF - nanosecond timers \ No newline at end of file diff --git a/src/common/timer.c b/src/common/timer.c index 60e10bf2..95ebe5dc 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -22,7 +22,7 @@ #include "config.h" #include -/* Miscellaneous timeval routines */ +/* Miscellaneous timeval/timespec routines */ /* Divide tvs by div, storing the result in tvs */ void @@ -47,7 +47,7 @@ init_timestamp(struct timespec *timestamp) int get_current_time(struct timespec *ts) { -#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L +#if defined CLOCK_MONOTONIC || defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L return clock_gettime(CLOCK_MONOTONIC, ts); #else struct timeval tv; diff --git a/src/common/timer.h b/src/common/timer.h index 0cf801fa..b4cbfa34 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -178,6 +178,4 @@ void timesdiv_float(struct timespec *tvs, float div); typedef struct timeval timestamp_t; void init_timestamp(struct timespec *timestamp); -int get_current_time(struct timespec *ts); - -#endif /* _TIMER_H_ */ +int get_current_time(struct timespec *timestamp); diff --git a/src/common/utils.c b/src/common/utils.c index 8e69834e..520d40d2 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -307,8 +307,8 @@ format_date_time(struct timespec *when, char *buf, size_t len) if (!tm) return -1; - strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%06u", tm); - return snprintf(buf, len, tmp, when->tv_nsec / 1000); + strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%09u", tm); + return snprintf(buf, len, tmp, when->tv_nsec); } /** diff --git a/src/defines.h.in b/src/defines.h.in index fe776552..1975f053 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -102,7 +102,7 @@ char dummy[0]; #define COUNTER unsigned long #define COUNTER_SPEC "%lu" #endif -#define COUNTER_OVERFLOW_RISK (((COUNTER)~0) >> 20) +#define COUNTER_OVERFLOW_RISK (((COUNTER)~0) >> 23) #include #include diff --git a/src/send_packets.c b/src/send_packets.c index e8d41fbc..788341b4 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -56,10 +56,6 @@ extern tcpedit_t *tcpedit; #include "send_packets.h" #include "sleep.h" -#ifdef DEBUG -extern int debug; -#endif - static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, struct timespec *last, @@ -70,8 +66,11 @@ static void calc_sleep_time(tcpreplay_t *ctx, COUNTER start_us, COUNTER *skip_length); static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, struct timespec *nap_this_time, struct timespec *now); -static u_char * -get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, packet_cache_t **prev_packet); +static u_char *get_next_packet(tcpreplay_opt_t *options, + pcap_t *pcap, + struct pcap_pkthdr *pkthdr, + int file_idx, + packet_cache_t **prev_packet); static uint32_t get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter); #ifdef HAVE_NETMAP @@ -336,7 +335,7 @@ increment_iteration(tcpreplay_t *ctx) void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) { - struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; + struct timespec now, print_delta, last_pkt_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -386,7 +385,9 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * Keep sending while we have packets or until * we've sent enough packets */ - while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + while (!ctx->abort && read_next_packet && + (pktdata = get_next_packet(options, pcap, &pkthdr, idx, prev_packet)) != NULL) { + struct timespec pkthdr_ts; TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; @@ -480,6 +481,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) &stats->end_time, TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + /* * Track the time of the "last packet sent". * @@ -1033,12 +1035,6 @@ calc_sleep_time(tcpreplay_t *ctx, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); - // printf("pkt_ts_delta sec: %lu\n", pkt_ts_delta->tv_sec); - // printf("pkt_ts_delta nsec: %lu\n", pkt_ts_delta->tv_nsec); - // printf("time_delta sec: %lu\n", time_delta->tv_sec); - // printf("time_delta nsec: %lu\n", time_delta->tv_nsec); - // printf("nap_for sec: %lu\n", nap_for.tv_sec); - // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1055,7 +1051,7 @@ calc_sleep_time(tcpreplay_t *ctx, if (now_ns) { COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); // PB: Ferencol miert bits sent? + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); COUNTER tx_ns = now_ns - start_ns; /* @@ -1063,7 +1059,7 @@ calc_sleep_time(tcpreplay_t *ctx, * * ensure there is no overflow in cases where bits_sent is very high */ - if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) + if (bits_sent > COUNTER_OVERFLOW_RISK) next_tx_ns = (bits_sent * 1000) / bps * 1000000; else next_tx_ns = (bits_sent * 1000000000) / bps; @@ -1154,9 +1150,8 @@ tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, s #endif /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)) { + if (!timesisset(nap_this_time)) return; - } /* do we need to limit the total time we sleep? */ if (timesisset(&(options->maxsleep)) && (timescmp(nap_this_time, &(options->maxsleep), >))) { diff --git a/src/signal_handler.c b/src/signal_handler.c index f8b85d9e..9bfd56ee 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -31,7 +31,7 @@ #include #include -struct timeval suspend_time; // PB: do we need to modify this part of the code? +struct timeval suspend_time; static struct timeval suspend_start; static struct timeval suspend_end; diff --git a/src/sleep.c b/src/sleep.c index 16234a43..27e3e85f 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -80,10 +80,4 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timesp #else err(-1, "Platform does not support IO Port for timing"); #endif - -#ifdef HAVE_NETMAP - if (flush) - ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ -#endif /* HAVE_NETMAP */ - get_current_time(now); } diff --git a/src/sleep.h b/src/sleep.h index 71365ab5..763b8980 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -54,9 +54,9 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); -#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); @@ -77,36 +77,36 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespe * Note: make sure "now" has recently been updated. */ static inline void -gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) +gettimeofday_sleep(sendpacket_t *sp, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timeval now_ms, sleep_until, nap_for, last; - TIMESPEC_TO_TIMEVAL(&nap_for, nap); - gettimeofday(&now_ms, NULL); + struct timespec sleep_until; #ifdef HAVE_NETMAP + struct timespec last; uint32_t i = 0; - TIMEVAL_SET(&last, &now_ms); + + TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - timeradd(&now_ms, &nap_for, &sleep_until); + timeradd_timespec(now, nap, &sleep_until); + while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timercmp(&now_ms, &last, !=)) { - TIMESPEC_SET(&last, &now_ms); + if (flush && timescmp(now, &last, !=)) { + TIMESPEC_SET(&last, now); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timercmp(&now_ms, &sleep_until, >=)) + if (timescmp(now, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - gettimeofday(&now_ms, NULL); + get_current_time(now); } - get_current_time(now); } #ifdef HAVE_SELECT @@ -136,6 +136,7 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif + get_current_time(now_ns); } #endif /* HAVE_SELECT */ @@ -152,5 +153,3 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns void ioport_sleep_init(void); void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush); - -#endif /* __SLEEP_H__ */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 319ae109..453b92d7 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -166,6 +166,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->loop = OPT_VALUE_LOOP; options->loopdelay_ms = OPT_VALUE_LOOPDELAY_MS; + options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; if (HAVE_OPT(LIMIT)) options->limit_send = OPT_VALUE_LIMIT; @@ -314,7 +315,6 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; - options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); ret = -1; @@ -1120,6 +1120,7 @@ tcpreplay_prepare(tcpreplay_t *ctx) return ret; } +static bool apply_loop_delay(tcpreplay_t *ctx); /** * \brief sends the traffic out the interfaces * @@ -1167,12 +1168,12 @@ tcpreplay_replay(tcpreplay_t *ctx) loop, total_loops, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) { + if ((rcode = tcpr_replay_index(ctx)) < 0) return rcode; - } if (ctx->options->loop > 0) { - apply_loop_delay(ctx); - get_current_time(&ctx->stats.end_time); + if (apply_loop_delay(ctx)) + get_current_time(&ctx->stats.end_time); + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); } @@ -1195,11 +1196,12 @@ tcpreplay_replay(tcpreplay_t *ctx) printf("Loop " COUNTER_SPEC " (" COUNTER_SPEC " unique)...\n", loop, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) { + if ((rcode = tcpr_replay_index(ctx)) < 0) return rcode; - } - apply_loop_delay(ctx); - get_current_time(&ctx->stats.end_time); + + if (apply_loop_delay(ctx)) + get_current_time(&ctx->stats.end_time); + if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } @@ -1381,19 +1383,20 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } -void -apply_loop_delay(tcpreplay_t *ctx) -{ - if (ctx->options->accurate == accurate_nanosleep) { - if (!ctx->abort && ctx->options->loopdelay_ns > 0) { - struct timespec nap; - nap.tv_sec = 0; - nap.tv_nsec = ctx->options->loopdelay_ns; - nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); - } - } else { - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - } +bool +apply_loop_delay(tcpreplay_t *ctx) { + if (!ctx->abort && ctx->options->loopdelay_ms > 0) { + usleep(ctx->options->loopdelay_ms * 1000); + return true; + } + + if (!ctx->abort && ctx->options->loopdelay_ns > 0) { + struct timespec nap; + nap.tv_sec = 0; + nap.tv_nsec = ctx->options->loopdelay_ns; + nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); + return true; } + + return false; } diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 321c8f18..6cff96b7 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -272,7 +272,6 @@ int tcpreplay_set_verbose(tcpreplay_t *, bool); int tcpreplay_set_tcpdump_args(tcpreplay_t *, char *); int tcpreplay_set_tcpdump(tcpreplay_t *, tcpdump_t *); -void apply_loop_delay(tcpreplay_t *ctx); /* * These functions are seen by the outside world, but nobody should ever use them * outside of internal tcpreplay API functions diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a17dff73..e3536fde 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -320,6 +320,7 @@ flag = { flag = { name = loopdelay-ms; + flags-cant = loopdelay-ns; flags-must = loop; arg-type = number; arg-range = "0->"; @@ -330,7 +331,8 @@ flag = { flag = { name = loopdelay-ns; - flags-must = loop, timer; + flags-cant = loopdelay-ms; + flags-must = loop; arg-type = number; arg-range = "0->"; descrip = "Delay between loops in nanoseconds"; From 3f1a891044525a1c9443f3952ab532c58f88a17e Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 26 Dec 2023 13:22:15 -0800 Subject: [PATCH 11/11] Feature #796 - Nano timer test no longer requires --timer --- test/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile.am b/test/Makefile.am index d1925e19..34be770e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -347,7 +347,7 @@ replay_basic: replay_nano_timer: $(PRINTF) "%s" "[tcpreplay] Nano timer test: " $(PRINTF) "%s\n" "*** [tcpreplay] Nano timer test: " >> test.log - $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --timer=nano --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi replay_cache: