|
@@ -2,7 +2,7 @@
|
|
|
|
|
|
/*
|
|
|
* Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
|
|
|
- * Copyright (c) 2013-2022 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
|
|
|
+ * Copyright (c) 2013-2024 Fred Klassen <tcpreplay at appneta dot com> - AppNeta
|
|
|
*
|
|
|
* The Tcpreplay Suite of tools is free software: you can redistribute it
|
|
|
* and/or modify it under the terms of the GNU General Public License as
|
|
@@ -57,17 +57,20 @@ 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 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 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,
|
|
|
+ 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
|
|
@@ -227,9 +230,11 @@ update_flow_stats(tcpreplay_t *ctx,
|
|
|
sendpacket_t *sp,
|
|
|
const struct pcap_pkthdr *pkthdr,
|
|
|
const u_char *pktdata,
|
|
|
- int datalink)
|
|
|
+ int datalink,
|
|
|
+ COUNTER packetnum)
|
|
|
{
|
|
|
- flow_entry_type_t res = flow_decode(ctx->flow_hash_table, pkthdr, pktdata, datalink, ctx->options->flow_expiry);
|
|
|
+ flow_entry_type_t res =
|
|
|
+ flow_decode(ctx->flow_hash_table, pkthdr, pktdata, datalink, ctx->options->flow_expiry, packetnum);
|
|
|
|
|
|
switch (res) {
|
|
|
case FLOW_ENTRY_NEW:
|
|
@@ -296,14 +301,17 @@ preload_pcap_file(tcpreplay_t *ctx, int idx)
|
|
|
if (close(1) == -1)
|
|
|
warnx("unable to close stdin: %s", strerror(errno));
|
|
|
|
|
|
- if ((pcap = pcap_open_offline(path, ebuf)) == NULL)
|
|
|
+ if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL)
|
|
|
errx(-1, "Error opening pcap file: %s", ebuf);
|
|
|
|
|
|
dlt = pcap_datalink(pcap);
|
|
|
+ COUNTER packetnum = 0;
|
|
|
/* loop through the pcap. get_next_packet() builds the cache for us! */
|
|
|
- while ((pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) {
|
|
|
- if (options->flow_stats)
|
|
|
- update_flow_stats(ctx, NULL, &pkthdr, pktdata, dlt);
|
|
|
+ while ((pktdata = get_next_packet(options, pcap, &pkthdr, idx, prev_packet)) != NULL) {
|
|
|
+ if (options->flow_stats) {
|
|
|
+ ++packetnum;
|
|
|
+ update_flow_stats(ctx, NULL, &pkthdr, pktdata, dlt, packetnum);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* mark this file as cached */
|
|
@@ -332,7 +340,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;
|
|
@@ -353,10 +361,11 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
|
|
|
bool top_speed = (options->speed.mode == speed_topspeed ||
|
|
|
(options->speed.mode == speed_mbpsrate && options->speed.speed == 0));
|
|
|
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)
|
|
@@ -365,9 +374,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;
|
|
|
|
|
@@ -381,10 +390,24 @@ 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;
|
|
|
+ PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr.ts, &pkthdr_ts);
|
|
|
now_is_now = false;
|
|
|
packetnum++;
|
|
|
#if defined TCPREPLAY || defined TCPREPLAY_EDIT
|
|
|
+ /* look for include or exclude LIST match */
|
|
|
+ if (options->list != NULL) {
|
|
|
+ bool rule_set = check_list(options->list, packetnum);
|
|
|
+ if ((rule_set && options->is_exclude) || (!rule_set && !options->is_exclude)) {
|
|
|
+ dbgx(2, "packet " COUNTER_SPEC " not sent due to %s rule",
|
|
|
+ packetnum,
|
|
|
+ options->is_exclude ? "exclude" : "include");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* do we use the snaplen (caplen) or the "actual" packet len? */
|
|
|
pktlen = options->use_pkthdr_len ? (COUNTER)pkthdr.len : (COUNTER)pkthdr.caplen;
|
|
|
#elif TCPBRIDGE
|
|
@@ -422,7 +445,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
|
|
|
|
|
|
/* update flow stats */
|
|
|
if (options->flow_stats && !preload)
|
|
|
- update_flow_stats(ctx, options->cache_packets ? sp : NULL, &pkthdr, pktdata, datalink);
|
|
|
+ update_flow_stats(ctx, options->cache_packets ? sp : NULL, &pkthdr, pktdata, datalink, packetnum);
|
|
|
|
|
|
/*
|
|
|
* this accelerator improves performance by avoiding expensive
|
|
@@ -438,25 +461,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);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -472,7 +495,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);
|
|
|
|
|
|
/*
|
|
@@ -481,14 +504,24 @@ 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.
|
|
|
*/
|
|
|
- if (!top_speed)
|
|
|
+ if (!top_speed) {
|
|
|
+#ifndef HAVE_LIBXDP
|
|
|
tcpr_sleep(ctx, sp, &ctx->nap, &now);
|
|
|
+#else
|
|
|
+ if (sp->handle_type != SP_TYPE_LIBXDP) {
|
|
|
+ tcpr_sleep(ctx, sp, &ctx->nap, &now);
|
|
|
+ } else if (sp->batch_size == 1) {
|
|
|
+ // In case of LIBXDP packet processing waiting only makes sense when batch size is one
|
|
|
+ tcpr_sleep(ctx, sp, &ctx->nap, &now);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#ifdef ENABLE_VERBOSE
|
|
@@ -497,6 +530,18 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx)
|
|
|
tcpdump_print(options->tcpdump, &pkthdr, pktdata);
|
|
|
#endif
|
|
|
|
|
|
+#ifdef HAVE_LIBXDP
|
|
|
+ if (sp->handle_type == SP_TYPE_LIBXDP) {
|
|
|
+ /* Reserve frames for the batc h*/
|
|
|
+ while (xsk_ring_prod__reserve(&(sp->xsk_info->tx), sp->batch_size, &sp->tx_idx) < sp->batch_size) {
|
|
|
+ complete_tx_only(sp);
|
|
|
+ }
|
|
|
+ /* The first packet is already in memory */
|
|
|
+ prepare_first_element_of_batch(ctx, &packetnum, pktdata, pkthdr.len);
|
|
|
+ /* Read more packets and prepare batch */
|
|
|
+ prepare_remaining_elements_of_batch(ctx, &packetnum, &read_next_packet, pcap, &idx, pkthdr, prev_packet);
|
|
|
+ }
|
|
|
+#endif
|
|
|
dbgx(2, "Sending packet #" COUNTER_SPEC, packetnum);
|
|
|
/* write packet out on network */
|
|
|
if (sendpacket(sp, pktdata, pktlen, &pkthdr) < (int)pktlen) {
|
|
@@ -507,25 +552,29 @@ 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);
|
|
|
#endif
|
|
|
|
|
|
stats->pkts_sent++;
|
|
|
+#ifndef HAVE_LIBXDP
|
|
|
stats->bytes_sent += pktlen;
|
|
|
-
|
|
|
+#else
|
|
|
+ if (sp->handle_type != SP_TYPE_LIBXDP)
|
|
|
+ stats->bytes_sent += pktlen;
|
|
|
+#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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -537,7 +586,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;
|
|
@@ -549,20 +598,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);
|
|
|
}
|
|
@@ -574,7 +623,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;
|
|
@@ -594,9 +643,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)
|
|
@@ -605,9 +654,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;
|
|
|
|
|
@@ -619,8 +668,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
|
|
|
prev_packet2 = NULL;
|
|
|
}
|
|
|
|
|
|
- pktdata1 = get_next_packet(ctx, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
|
|
|
- pktdata2 = get_next_packet(ctx, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);
|
|
|
+ pktdata1 = get_next_packet(options, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
|
|
|
+ pktdata2 = get_next_packet(options, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);
|
|
|
|
|
|
/* MAIN LOOP
|
|
|
* Keep sending while we have packets or until
|
|
@@ -629,6 +678,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
|
|
|
while (!ctx->abort && !(pktdata1 == NULL && pktdata2 == NULL)) {
|
|
|
now_is_now = false;
|
|
|
packetnum++;
|
|
|
+ /* look for include or exclude LIST match */
|
|
|
+ if (options->list != NULL) {
|
|
|
+ bool rule_set = check_list(options->list, packetnum);
|
|
|
+ if ((rule_set && options->is_exclude) || (!rule_set && !options->is_exclude)) {
|
|
|
+ dbgx(2, "packet " COUNTER_SPEC " not sent due to %s rule",
|
|
|
+ packetnum,
|
|
|
+ options->is_exclude ? "exclude" : "include");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
/* figure out which pcap file we need to process next
|
|
|
* when get_next_packet() returns null for pktdata, the pkthdr
|
|
@@ -684,7 +743,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
|
|
|
|
|
|
/* update flow stats */
|
|
|
if (options->flow_stats && !options->file_cache[cache_file_idx].cached)
|
|
|
- update_flow_stats(ctx, sp, pkthdr_ptr, pktdata, datalink);
|
|
|
+ update_flow_stats(ctx, sp, pkthdr_ptr, pktdata, datalink, packetnum);
|
|
|
|
|
|
/*
|
|
|
* this accelerator improves performance by avoiding expensive
|
|
@@ -705,22 +764,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;
|
|
|
+ PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr_ptr->ts, &pkthdr_ts);
|
|
|
+ if (!timesisset(&last_pkt_ts)) {
|
|
|
+ PCAP_TIMEVAL_TO_TIMESPEC_SET(&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;
|
|
|
}
|
|
|
|
|
@@ -737,7 +798,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);
|
|
|
|
|
|
/*
|
|
@@ -746,8 +807,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.
|
|
@@ -772,21 +833,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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -800,13 +861,13 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
|
|
|
|
|
|
/* get the next packet for this file handle depending on which we last used */
|
|
|
if (sp == ctx->intf2) {
|
|
|
- pktdata2 = get_next_packet(ctx, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);
|
|
|
+ pktdata2 = get_next_packet(options, pcap2, &pkthdr2, cache_file_idx2, prev_packet2);
|
|
|
} else {
|
|
|
- pktdata1 = get_next_packet(ctx, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
|
|
|
+ pktdata1 = get_next_packet(options, pcap1, &pkthdr1, cache_file_idx1, prev_packet1);
|
|
|
}
|
|
|
|
|
|
/* 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;
|
|
@@ -817,21 +878,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);
|
|
|
}
|
|
@@ -845,9 +906,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *
|
|
|
* will be updated as new entries are added (or retrieved) from the cache list.
|
|
|
*/
|
|
|
u_char *
|
|
|
-get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int idx, packet_cache_t **prev_packet)
|
|
|
+get_next_packet(tcpreplay_opt_t *options, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int idx, packet_cache_t **prev_packet)
|
|
|
{
|
|
|
- tcpreplay_opt_t *options = ctx->options;
|
|
|
u_char *pktdata = NULL;
|
|
|
uint32_t pktlen;
|
|
|
|
|
@@ -963,18 +1023,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);
|
|
|
|
|
@@ -998,10 +1058,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);
|
|
@@ -1013,31 +1073,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 && bps > 500000)
|
|
|
- next_tx_us = (bits_sent * 1000) / bps * 1000;
|
|
|
+ if (bits_sent > COUNTER_OVERFLOW_RISK)
|
|
|
+ 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);
|
|
@@ -1048,12 +1108,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)
|
|
@@ -1062,16 +1122,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,
|
|
@@ -1105,7 +1165,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 =
|
|
@@ -1215,3 +1275,78 @@ get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter)
|
|
|
|
|
|
return (uint32_t)send;
|
|
|
}
|
|
|
+
|
|
|
+#ifdef HAVE_LIBXDP
|
|
|
+void
|
|
|
+check_packet_fits_in_umem_frame(sendpacket_t *sp, int packet_len)
|
|
|
+{
|
|
|
+ if (packet_len > sp->frame_size) {
|
|
|
+ fprintf(stderr,
|
|
|
+ "ERROR: packet size cannot be larger than the size of an UMEM frame! Packet size: %i Frame size: %i\n",
|
|
|
+ packet_len,
|
|
|
+ sp->frame_size);
|
|
|
+ free_umem_and_xsk(sp);
|
|
|
+ exit(-1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+fill_umem_with_data_and_set_xdp_desc(sendpacket_t *sp, int tx_idx, COUNTER umem_index, u_char *pktdata, int len)
|
|
|
+{
|
|
|
+ check_packet_fits_in_umem_frame(sp, len);
|
|
|
+ COUNTER umem_index_mod = (umem_index % sp->batch_size) * sp->frame_size; // packets are sent in batch, after each
|
|
|
+ // batch umem memory is reusable
|
|
|
+ gen_eth_frame(sp->umem_info, umem_index_mod, pktdata, len);
|
|
|
+ struct xdp_desc *xdp_desc = xsk_ring_prod__tx_desc(&(sp->xsk_info->tx), tx_idx);
|
|
|
+ xdp_desc->addr = (COUNTER)(umem_index_mod);
|
|
|
+ xdp_desc->len = len;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+prepare_first_element_of_batch(tcpreplay_t *ctx, COUNTER *packetnum, u_char *pktdata, u_int32_t len)
|
|
|
+{
|
|
|
+ sendpacket_t *sp = ctx->intf1;
|
|
|
+ tcpreplay_stats_t *stats = &ctx->stats;
|
|
|
+ fill_umem_with_data_and_set_xdp_desc(sp, sp->tx_idx, *packetnum - 1, pktdata, len);
|
|
|
+ sp->bytes_sent += len;
|
|
|
+ stats->bytes_sent += len;
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+prepare_remaining_elements_of_batch(tcpreplay_t *ctx,
|
|
|
+ COUNTER *packetnum,
|
|
|
+ bool *read_next_packet,
|
|
|
+ pcap_t *pcap,
|
|
|
+ int *idx,
|
|
|
+ struct pcap_pkthdr pkthdr,
|
|
|
+ packet_cache_t **prev_packet)
|
|
|
+{
|
|
|
+ sendpacket_t *sp = ctx->intf1;
|
|
|
+ tcpreplay_stats_t *stats = &ctx->stats;
|
|
|
+ int datalink = ctx->options->file_cache[*idx].dlt;
|
|
|
+ bool preload = ctx->options->file_cache[*idx].cached;
|
|
|
+ u_char *pktdata = NULL;
|
|
|
+ unsigned int pckt_count = 1;
|
|
|
+ while (!ctx->abort && (pckt_count < sp->batch_size) &&
|
|
|
+ (pktdata = get_next_packet(ctx->options, pcap, &pkthdr, *idx, prev_packet)) != NULL) {
|
|
|
+ fill_umem_with_data_and_set_xdp_desc(sp, sp->tx_idx + pckt_count, *packetnum, pktdata, pkthdr.len);
|
|
|
+ ++pckt_count;
|
|
|
+ ++*packetnum;
|
|
|
+ stats->bytes_sent += pkthdr.len;
|
|
|
+ sp->bytes_sent += pkthdr.len;
|
|
|
+ stats->pkts_sent++;
|
|
|
+ if (ctx->options->flow_stats && !preload) {
|
|
|
+ update_flow_stats(ctx, ctx->options->cache_packets ? sp : NULL, &pkthdr, pktdata, datalink, *packetnum);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pckt_count < sp->batch_size) {
|
|
|
+ // No more packets to read, it is essential for cached packet processing
|
|
|
+ *read_next_packet = false;
|
|
|
+ }
|
|
|
+ sp->pckt_count = pckt_count;
|
|
|
+ dbgx(2,
|
|
|
+ "Sending packets with LIBXDP in batch, packet numbers from " COUNTER_SPEC " to " COUNTER_SPEC "\n",
|
|
|
+ *packetnum - pckt_count + 1,
|
|
|
+ *packetnum);
|
|
|
+}
|
|
|
+#endif /* HAVE_LIBXDP */
|