diff --git a/src/bridge.c b/src/bridge.c index 970b753d..653f9192 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -210,8 +210,8 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) do_bridge_bidirectional(options, tcpedit); } - if (gettimeofday(&stats.end_time, NULL) < 0) - errx(-1, "gettimeofday() failed: %s", strerror(errno)); + if (get_current_time(&stats.end_time) < 0) + errx(-1, "get_current_time() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index 169ca65d..be447673 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -39,7 +39,18 @@ timesdiv_float(struct timespec *tvs, float div) } void -init_timestamp(timestamp_t *ctx) +init_timestamp(struct timespec *timestamp) { - timerclear(ctx); + timesclear(timestamp); +} + +int get_current_time(struct timespec *ts){ + #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L + return clock_gettime(CLOCK_MONOTONIC, ts); + #else + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; + #endif } diff --git a/src/common/timer.h b/src/common/timer.h index db5bc174..71baade3 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -112,6 +112,35 @@ void timesdiv_float(struct timespec *tvs, float 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) \ @@ -150,4 +179,7 @@ void timesdiv_float(struct timespec *tvs, float div); typedef struct timeval timestamp_t; -void init_timestamp(timestamp_t *ctx); +void init_timestamp(struct timespec *timestamp); +int get_current_time(struct timespec *ts); + +#endif /* _TIMER_H_ */ diff --git a/src/common/utils.c b/src/common/utils.c index 6aed5b32..764a37e1 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -211,7 +211,7 @@ our_safe_pcap_next_ex(pcap_t *pcap, 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; @@ -221,8 +221,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; @@ -251,18 +251,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", @@ -294,8 +289,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]; @@ -307,7 +301,7 @@ 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 c758f472..a858099a 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -29,11 +29,11 @@ typedef struct { COUNTER bytes_sent; COUNTER pkts_sent; COUNTER failed; - struct timeval start_time; - struct timeval time_delta; - struct timeval end_time; - struct timeval pkt_ts_delta; - struct timeval last_print; + struct timespec start_time; + struct timespec time_delta; + struct timespec end_time; + struct timespec pkt_ts_delta; + struct timespec last_print; COUNTER flow_non_flow_packets; COUNTER flows; COUNTER flows_unique; @@ -44,7 +44,7 @@ typedef struct { int read_hexstring(const char *l2string, u_char *hex, int hexlen); void packet_stats(const tcpreplay_stats_t *stats); -int format_date_time(struct timeval *when, char *buf, size_t len); +int format_date_time(struct timespec *when, char *buf, size_t len); uint32_t tcpr_random(uint32_t *seed); void restore_stdin(void); diff --git a/src/defines.h.in b/src/defines.h.in index ee5d2425..fe776552 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -315,10 +315,9 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t typedef u_int32_t uint32_t #define MICROSEC_TO_SEC(x) (x / 1000000) #define NANOSEC_TO_SEC(x) ((u_int64_t)x / 1000000000) -#define TIMEVAL_TO_MILLISEC(x) (((x)->tv_sec * 1000) + ((x)->tv_usec / 1000)) -#define TIMEVAL_TO_MICROSEC(x) (((x)->tv_sec * 1000000) + (x)->tv_usec) -#define TIMEVAL_TO_NANOSEC(x) ((u_int64_t)((x)->tv_sec * 1000000000) + ((u_int64_t)(x)->tv_usec * 1000)) -#define TIMSTAMP_TO_MICROSEC(x) (TIMEVAL_TO_MICROSEC(x)) +#define TIMEVAL_TO_MILLISEC(x) (((x)->tv_sec * 1000) + ((x)->tv_usec / 1000)) +#define TIMEVAL_TO_MICROSEC(x) (((x)->tv_sec * 1000000) + (x)->tv_usec) +#define TIMEVAL_TO_NANOSEC(x) ((u_int64_t)((x)->tv_sec * 1000000000) + ((u_int64_t)(x)->tv_usec * 1000)) #define MILLISEC_TO_TIMEVAL(x, tv) \ do { \ @@ -360,6 +359,13 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t typedef u_int32_t uint32_t (a)->tv_nsec = (b)->tv_nsec; \ } while (0) +/* libpcap puts nanosec values in tv_usec when pcap file is read with nanosec precision*/ +#define TIMEVAL_AS_TIMESPEC_SET(a, b) \ + do { \ + (a)->tv_sec = (b)->tv_sec; \ + (a)->tv_nsec = (b)->tv_usec; \ + } while(0) + /* * Help suppress some compiler warnings * No problem if variable is actually used diff --git a/src/replay.c b/src/replay.c index c3966526..03b2abb6 100644 --- a/src/replay.c +++ b/src/replay.c @@ -117,7 +117,7 @@ replay_file(tcpreplay_t *ctx, int idx) /* read from pcap file if we haven't cached things yet */ if (!ctx->options->preload_pcap) { - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -133,10 +133,10 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); } } @@ -145,10 +145,10 @@ replay_file(tcpreplay_t *ctx, int idx) if (ctx->options->verbose) { /* in cache mode, we may not have opened the file */ if (pcap == NULL) - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); /* init tcpdump */ diff --git a/src/send_packets.c b/src/send_packets.c index e9e445d9..53fe0a52 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -56,21 +56,20 @@ extern tcpedit_t *tcpedit; #include "send_packets.h" #include "sleep.h" -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 tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, struct timespec *nap_this_time, struct timeval *now); -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); +#ifdef DEBUG +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 uint32_t get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter); #ifdef HAVE_NETMAP @@ -334,8 +333,8 @@ 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 timespec now, print_delta, last_pkt_ts, pkthdr_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -358,9 +357,9 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) bool now_is_now = true; bool read_next_packet = true; // used for LIBXDP batch packet processing with cached packets - gettimeofday(&now, NULL); - if (!timerisset(&stats->start_time)) { - TIMEVAL_SET(&stats->start_time, &now); + get_current_time(&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) @@ -369,9 +368,10 @@ 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 = TIMEVAL_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 +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 && read_next_packet && - (pktdata = get_next_packet(options, 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++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -443,25 +444,26 @@ 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(&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); } } if (!top_speed) { now_is_now = true; - gettimeofday(&now, NULL); + get_current_time(&now); } /* @@ -470,24 +472,17 @@ 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, - TIMEVAL_TO_MICROSEC(&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". * * 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. @@ -534,7 +529,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); @@ -549,14 +544,14 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #endif /* 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); } } } @@ -568,9 +563,9 @@ 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) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + 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 */ @@ -580,20 +575,20 @@ 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; - gettimeofday(&now, NULL); + get_current_time(&now); } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { now_is_now = true; - gettimeofday(&now, NULL); + get_current_time(&now); } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + get_current_time(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -605,7 +600,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 print_delta, now, last_pkt_ts; + struct timespec now, print_delta, last_pkt_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -625,9 +620,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); + get_current_time(&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) @@ -636,9 +631,10 @@ 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 = TIMEVAL_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; @@ -736,22 +732,24 @@ 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(&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 (!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); + get_current_time(&now); now_is_now = true; } @@ -761,15 +759,9 @@ 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, - TIMEVAL_TO_MICROSEC(&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". @@ -777,8 +769,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. @@ -803,21 +795,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); } } } @@ -837,9 +829,9 @@ 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) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + 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 */ @@ -848,21 +840,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); + get_current_time(&now); now_is_now = true; } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { - gettimeofday(&now, NULL); + get_current_time(&now); now_is_now = true; } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + get_current_time(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -991,20 +983,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); @@ -1028,11 +1014,18 @@ calc_sleep_time(tcpreplay_t *ctx, * 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); - dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); + 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); dbgx(3, "original packet delta/div: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); } @@ -1043,31 +1036,32 @@ calc_sleep_time(tcpreplay_t *ctx, * 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) - next_tx_us = (bits_sent * 1000) / bps * 1000; + if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) + 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); + 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); @@ -1075,40 +1069,38 @@ calc_sleep_time(tcpreplay_t *ctx, case speed_packetrate: /* - * 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; - COUNTER pph = ctx->options->speed.speed; - COUNTER pkts_sent = ctx->stats.pkts_sent; - COUNTER tx_us = now_us - start_us; - /* - * 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_us = (pkts_sent * 1000000) * (60 * 60) / pph; - else - next_tx_us = ((pkts_sent * 1000000) / pph) * (60 * 60); - - if (next_tx_us > tx_us) - NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &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); - } - - 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: @@ -1134,8 +1126,8 @@ calc_sleep_time(tcpreplay_t *ctx, } } -static void -tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, struct timeval *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 = @@ -1146,8 +1138,9 @@ 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 9bfd56ee..f8b85d9e 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -31,7 +31,7 @@ #include #include -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 05b2ef10..160a6956 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -48,36 +48,43 @@ ioport_sleep_init(void) #endif } -void -ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timeval *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 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); + for (i = 0; i < nap_for.tv_sec; i ++) { + 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--; - outb(ioport_sleep_value, 0x80); + /* process the nsec */ + nsec = nap->tv_nsec; + nsec --; /* fudge factor for all the above */ + while (nsec > 0) { + nsec --; + outb(ioport_sleep_value, 0x80); } #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 0606a0e4..fe0e8be7 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -28,6 +28,10 @@ #include #endif +#include +#include +#include +#include #include #include #include @@ -51,15 +55,23 @@ #endif /* HAVE_NETMAP */ static inline void -nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval *now, bool flush _U_) +nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, + struct timespec *now, bool flush _U_) { - nanosleep(nap, NULL); + 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) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - gettimeofday(now, NULL); + get_current_time(now); } /* @@ -69,37 +81,37 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval * Note: make sure "now" has recently been updated. */ static inline void -gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timeval *now, bool flush _U_) +gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, + struct timespec *now, bool flush _U_) { - struct timeval sleep_until, nap_for; + struct timeval now_ms, sleep_until, nap_for, last; + TIMESPEC_TO_TIMEVAL(&nap_for, nap); + gettimeofday(&now_ms, NULL); #ifdef HAVE_NETMAP - struct timeval last; uint32_t i = 0; - - TIMEVAL_SET(&last, now); + TIMEVAL_SET(&last, &now_ms); #endif /* HAVE_NETMAP */ - - TIMESPEC_TO_TIMEVAL(&nap_for, nap); - timeradd(now, &nap_for, &sleep_until); - + + timeradd(&now_ms, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timercmp(now, &last, !=)) { - TIMEVAL_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 (timercmp(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 - gettimeofday(now, NULL); + gettimeofday(&now_ms, NULL); } + get_current_time(now); } #ifdef HAVE_SELECT @@ -109,16 +121,19 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timeval *n * resolution which is pretty much useless for our needs. Keeping it here * for future reference */ -static inline void -select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval *now, 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; + 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)); @@ -127,8 +142,7 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval *n if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - - gettimeofday(now, NULL); + get_current_time(now_ns); } #endif /* HAVE_SELECT */ @@ -143,4 +157,7 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval *n /* 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 timeval *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/tcpbridge.c b/src/tcpbridge.c index be7dc19d..e37936b7 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -91,9 +91,9 @@ main(int argc, char *argv[]) } #endif - if (gettimeofday(&stats.start_time, NULL) < 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.c b/src/tcpreplay.c index 8e1b84c3..84bc84d9 100644 --- a/src/tcpreplay.c +++ b/src/tcpreplay.c @@ -216,7 +216,7 @@ main(int argc, char *argv[]) */ static void flow_stats(const tcpreplay_t *tcpr_ctx) { - struct timeval diff; + struct timespec diff; COUNTER diff_us; const tcpreplay_stats_t *stats = &tcpr_ctx->stats; const tcpreplay_opt_t *options = tcpr_ctx->options; @@ -228,8 +228,8 @@ static void flow_stats(const tcpreplay_t *tcpr_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 || !tcpr_ctx->iteration) return; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 2b992e00..8b4c4043 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 @@ -314,6 +316,7 @@ 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; @@ -915,24 +918,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; } @@ -1142,7 +1145,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); @@ -1166,16 +1169,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); + get_current_time(&ctx->stats.end_time); + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); + } } #ifdef HAVE_LIBXDP sendpacket_t *sp = ctx->intf1; @@ -1195,14 +1197,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); + get_current_time(&ctx->stats.end_time); if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } @@ -1384,35 +1383,17 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } -#ifdef HAVE_LIBXDP -void -delete_xsk_socket(struct xsk_socket *xsk) -{ - size_t desc_sz = sizeof(struct xdp_desc); - struct xdp_mmap_offsets off; - socklen_t optlen; - int err; - - if (!xsk) - return; - - optlen = sizeof(off); - err = getsockopt(xsk->fd, SOL_XDP, XDP_MMAP_OFFSETS, &off, &optlen); - if (!err) { - if (xsk->rx) { - munmap(xsk->rx->ring - off.rx.desc, off.rx.desc + xsk->config.rx_size * desc_sz); - } - if (xsk->tx) { - munmap(xsk->tx->ring - off.tx.desc, off.tx.desc + xsk->config.tx_size * desc_sz); +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); + } } - close(xsk->fd); -} - -void -free_umem_and_xsk(sendpacket_t *sp) -{ - xsk_umem__delete(sp->xsk_info->umem->umem); - delete_xsk_socket(sp->xsk_info->xsk); } -#endif /* HAVE_LIBXDP */ diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 1510443a..321c8f18 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -103,6 +103,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; @@ -264,13 +265,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 e05c7b32..a17dff73 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 3aaa46af..f567f810 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -27,9 +27,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; }; @@ -46,9 +46,9 @@ update_current_timestamp_trace_entry(COUNTER bytes_sent, COUNTER now_us, COUNTER 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; @@ -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 timeval *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; @@ -66,7 +66,7 @@ add_timestamp_trace_entry(COUNTER size, struct timeval *timestamp, COUNTER skip_ 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; } @@ -100,18 +100,10 @@ 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 timeval *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 */ 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