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/CHANGELOG b/docs/CHANGELOG index 99e0d31b..0042a65d 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,5 +1,6 @@ 09/03/2023 Version 4.5.0-beta1 - handle IPv6 fragment extension header (#832 #837) + - 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 af043fde..20953c5c 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -117,4 +117,9 @@ David Guti Bastian Triller - Linux SLL2 -Chuck Cottrill \ No newline at end of file +GithHub @plangarbalint + - eBPF + - nanosecond timers + +Chuck Cottrill + - handle IPv6 fragment extensions diff --git a/src/bridge.c b/src/bridge.c index 970b753d..69efa31c 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..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 @@ -39,7 +39,20 @@ 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 CLOCK_MONOTONIC || 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..b4cbfa34 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -112,6 +112,33 @@ 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 +177,5 @@ 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 *timestamp); diff --git a/src/common/utils.c b/src/common/utils.c index 6aed5b32..520d40d2 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,19 @@ 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 + (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); + (ssize_t)diff.tv_nsec / 1000); + } if (mb_sec >= 1) printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n", @@ -295,7 +296,7 @@ packet_stats(const tcpreplay_stats_t *stats) * @return: string containing date, or -1 on error */ int -format_date_time(struct timeval *when, char *buf, size_t len) +format_date_time(struct timespec *when, char *buf, size_t len) { struct tm *tm; char tmp[64]; @@ -306,8 +307,8 @@ format_date_time(struct timeval *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_usec); + strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%09u", tm); + return snprintf(buf, len, tmp, when->tv_nsec); } /** 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..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 @@ -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..c01206d6 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,7 +133,7 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - 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; } @@ -145,7 +145,7 @@ 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) { + 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; } diff --git a/src/send_packets.c b/src/send_packets.c index e9e445d9..788341b4 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -57,15 +57,15 @@ extern tcpedit_t *tcpedit; #include "sleep.h" static void calc_sleep_time(tcpreplay_t *ctx, - struct timeval *pkt_ts_delta, - struct timeval *time_delta, + struct timespec *pkt_time, + struct timespec *last, COUNTER len, sendpacket_t *sp, COUNTER counter, - timestamp_t *sent_timestamp, + 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); +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_opt_t *options, pcap_t *pcap, struct pcap_pkthdr *pkthdr, @@ -335,7 +335,7 @@ 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; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -358,9 +358,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 +369,9 @@ 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; @@ -387,6 +387,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) */ 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++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -443,25 +445,25 @@ 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); } /* @@ -477,7 +479,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* @@ -486,8 +488,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. @@ -534,7 +536,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 +551,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,7 +570,7 @@ 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; @@ -580,20 +582,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 +607,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 +627,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 +638,9 @@ 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 +738,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; } @@ -768,7 +772,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* @@ -777,8 +781,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 +807,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,7 +841,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; @@ -848,21 +852,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); } @@ -993,18 +997,18 @@ cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num) */ static void calc_sleep_time(tcpreplay_t *ctx, - struct timeval *pkt_ts_delta, - struct timeval *time_delta, + struct timespec *pkt_ts_delta, + struct timespec *time_delta, COUNTER len, sendpacket_t *sp, COUNTER counter, - timestamp_t *sent_timestamp, - COUNTER start_us, + 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,10 +1032,10 @@ 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); + timessub(pkt_ts_delta, time_delta, &nap_for); + 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 +1047,31 @@ 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 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; + 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); @@ -1078,12 +1082,12 @@ calc_sleep_time(tcpreplay_t *ctx, * 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) @@ -1092,16 +1096,16 @@ calc_sleep_time(tcpreplay_t *ctx, * 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); + update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, @@ -1135,7 +1139,7 @@ calc_sleep_time(tcpreplay_t *ctx, } static void -tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, struct timeval *now) +tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, struct timespec *now) { tcpreplay_opt_t *options = ctx->options; bool flush = diff --git a/src/sleep.c b/src/sleep.c index 05b2ef10..27e3e85f 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -49,32 +49,32 @@ ioport_sleep_init(void) } void -ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timeval *now _U_, bool flush _U_) +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); + 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 diff --git a/src/sleep.h b/src/sleep.h index 0606a0e4..763b8980 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #ifdef HAVE_SYS_EVENT #include @@ -51,15 +52,22 @@ #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_) { +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L + struct timespec sleep_until; + timeradd_timespec(now, nap, &sleep_until); + 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,36 +77,35 @@ 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, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timeval sleep_until, nap_for; + struct timespec sleep_until; #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, &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); + get_current_time(now); } } @@ -110,15 +117,17 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timeval *n * 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)); @@ -128,7 +137,7 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timeval *n ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - gettimeofday(now, NULL); + get_current_time(now_ns); } #endif /* HAVE_SELECT */ @@ -143,4 +152,4 @@ 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); 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..453b92d7 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -18,23 +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 "replay.h" #ifdef TCPREPLAY_EDIT #include "tcpreplay_edit_opts.h" @@ -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; @@ -915,24 +916,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; } @@ -1119,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 * @@ -1169,13 +1171,12 @@ tcpreplay_replay(tcpreplay_t *ctx) 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 (apply_loop_delay(ctx)) + get_current_time(&ctx->stats.end_time); - if (ctx->options->stats == 0) + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); + } } #ifdef HAVE_LIBXDP sendpacket_t *sp = ctx->intf1; @@ -1198,10 +1199,8 @@ tcpreplay_replay(tcpreplay_t *ctx) 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); - } + if (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,20 @@ 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); - } +bool +apply_loop_delay(tcpreplay_t *ctx) { + if (!ctx->abort && ctx->options->loopdelay_ms > 0) { + usleep(ctx->options->loopdelay_ms * 1000); + return true; } - 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); + 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; } -#endif /* HAVE_LIBXDP */ diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 1510443a..6cff96b7 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,8 +265,8 @@ 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 *); diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index e05c7b32..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->"; @@ -328,6 +329,20 @@ flag = { doc = ""; }; +flag = { + name = loopdelay-ns; + flags-cant = loopdelay-ms; + flags-must = loop; + 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..19d7548d 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; @@ -58,7 +58,7 @@ update_current_timestamp_trace_entry(COUNTER bytes_sent, COUNTER now_us, COUNTER } static inline void -add_timestamp_trace_entry(COUNTER size, struct timeval *timestamp, COUNTER skip_length) +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; } @@ -107,7 +107,7 @@ update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), COUNTER UNUSED(next_tx_us)) {} static inline void -add_timestamp_trace_entry(COUNTER UNUSED(size), struct timeval *UNUSED(timestamp), COUNTER UNUSED(skip_length)) +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), diff --git a/test/Makefile.am b/test/Makefile.am index 405438aa..34be770e 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 --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