From 3b0934ecbe7f5276e2e700fb04ea3fef1da40282 Mon Sep 17 00:00:00 2001 From: eplabal Date: Sat, 4 Mar 2023 18:31:14 +0100 Subject: [PATCH 01/69] rewrite do_sleep to handle sub sleep times by only nanosleeping --- src/bridge.c | 2 +- src/common/timer.c | 14 +++- src/common/timer.h | 34 ++++++++- src/common/utils.c | 20 ++--- src/common/utils.h | 12 +-- src/defines.h.in | 1 - src/send_packets.c | 168 ++++++++++++++++++++--------------------- src/signal_handler.c | 2 +- src/sleep.c | 25 +++--- src/sleep.h | 42 ++++++----- src/tcpbridge.c | 2 +- src/tcpreplay.c | 6 +- src/tcpreplay_api.c | 57 ++++++++------ src/tcpreplay_api.h | 11 ++- src/tcpreplay_opts.def | 13 ++++ src/timestamp_trace.h | 12 +-- test/Makefile.am | 8 +- 17 files changed, 258 insertions(+), 171 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index d6d20e40..cd1b3b76 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -238,7 +238,7 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) do_bridge_bidirectional(options, tcpedit); } - if (gettimeofday(&stats.end_time, NULL) < 0) + if (get_time_of_day(&stats.end_time) < 0) errx(-1, "gettimeofday() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index 0bd9bab8..6ed978ef 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -86,8 +86,18 @@ void timesdiv(struct timespec *tvs, COUNTER div) } void -init_timestamp(timestamp_t *ctx) +init_timestamp(struct timespec *timestamp) { - timerclear(ctx); + timesclear(timestamp); } +int get_time_of_day(struct timespec *ts) { + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; +} + +int clock_get_time(struct timespec *ts){ + return clock_gettime(CLOCK_REALTIME, ts); +} diff --git a/src/common/timer.h b/src/common/timer.h index 97905573..5e48d34c 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -117,6 +117,35 @@ void timesdiv(struct timespec *tvs, COUNTER div); } while (0) #endif +/* add tvp and uvp and store in vvp */ +#ifndef timeradd_timespec +#define timeradd_timespec(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_nsec -= 1000000000; \ + } \ + } while (0) +#endif + + +/* add tvp and uvp and store in vvp */ +#ifndef timeradd_timeval_timespec +#define timeradd_timeval_timespec(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_usec * 1000; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + int seconds = (vvp)->tv_nsec % 1000000000; \ + (vvp)->tv_sec += seconds; \ + (vvp)->tv_nsec -= 1000000000 * seconds; \ + } \ + } while (0) +#endif + + /* subtract uvp from tvp and store in vvp */ #ifndef timersub #define timersub(tvp, uvp, vvp) \ @@ -170,7 +199,8 @@ void timesdiv(struct timespec *tvs, COUNTER div); typedef struct timeval timestamp_t; -void init_timestamp(timestamp_t *ctx); - +void init_timestamp(struct timespec *timestamp); +int get_time_of_day(struct timespec *ts); +int clock_get_time(struct timespec *ts); #endif /* _TIMER_H_ */ diff --git a/src/common/utils.c b/src/common/utils.c index a641939d..b7ed24ac 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -196,7 +196,7 @@ int _our_safe_pcap_next_ex(pcap_t *pcap, struct pcap_pkthdr **pkthdr, void packet_stats(const tcpreplay_stats_t *stats) { - struct timeval diff; + struct timespec diff; COUNTER diff_us; COUNTER bytes_sec = 0; u_int32_t bytes_sec_10ths = 0; @@ -206,8 +206,8 @@ packet_stats(const tcpreplay_stats_t *stats) COUNTER pkts_sec = 0; u_int32_t pkts_sec_100ths = 0; - timersub(&stats->end_time, &stats->start_time, &diff); - diff_us = TIMEVAL_TO_MICROSEC(&diff); + timessub(&stats->end_time, &stats->start_time, &diff); + diff_us = TIMESPEC_TO_MICROSEC(&diff); if (diff_us && stats->pkts_sent && stats->bytes_sent) { COUNTER bytes_sec_X10; @@ -236,13 +236,13 @@ packet_stats(const tcpreplay_stats_t *stats) pkts_sec_100ths = pkts_sec_X100 % 100; } - if (diff_us >= 1000 * 1000) + if (diff_us >= 1000 * 1000) { printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n", - stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_usec / (10 * 1000))); - else + stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_nsec / (10 * 1000000))); + } else{ printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n", - stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_usec); - + stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_nsec / 1000); + } if (mb_sec >= 1) printf("Rated: %llu.%1u Bps, %llu.%02u Mbps, %llu.%02u pps\n", @@ -265,7 +265,7 @@ packet_stats(const tcpreplay_stats_t *stats) * @param len: length of the buffer * @return: string containing date, or -1 on error */ -int format_date_time(struct timeval *when, char *buf, size_t len) +int format_date_time(struct timespec *when, char *buf, size_t len) { struct tm *tm; char tmp[64]; @@ -277,7 +277,7 @@ int format_date_time(struct timeval *when, char *buf, size_t len) return -1; strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%06u", tm); - return snprintf(buf, len, tmp, when->tv_usec); + return snprintf(buf, len, tmp, when->tv_nsec / 1000); } /** diff --git a/src/common/utils.h b/src/common/utils.h index 445b0f62..d9017cf5 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -31,11 +31,11 @@ typedef struct { COUNTER bytes_sent; COUNTER pkts_sent; COUNTER failed; - struct timeval start_time; - struct timeval time_delta; - struct timeval end_time; - struct timeval pkt_ts_delta; - struct timeval last_print; + struct timespec start_time; + struct timespec time_delta; + struct timespec end_time; + struct timespec pkt_ts_delta; + struct timespec last_print; COUNTER flow_non_flow_packets; COUNTER flows; COUNTER flows_unique; @@ -47,7 +47,7 @@ typedef struct { int read_hexstring(const char *l2string, u_char *hex, const int hexlen); void packet_stats(const tcpreplay_stats_t *stats); -int format_date_time(struct timeval *when, char *buf, size_t len); +int format_date_time(struct timespec *when, char *buf, size_t len); uint32_t tcpr_random(uint32_t *seed); void restore_stdin(void); diff --git a/src/defines.h.in b/src/defines.h.in index ea50e0ab..20e41a70 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -317,7 +317,6 @@ typedef u_int32_t uint32_t #define TIMEVAL_TO_MILLISEC(x) (((x)->tv_sec * 1000) + ((x)->tv_usec / 1000)) #define TIMEVAL_TO_MICROSEC(x) (((x)->tv_sec * 1000000) + (x)->tv_usec) #define TIMEVAL_TO_NANOSEC(x) ((u_int64_t)((x)->tv_sec * 1000000000) + ((u_int64_t)(x)->tv_usec * 1000)) -#define TIMSTAMP_TO_MICROSEC(x) (TIMEVAL_TO_MICROSEC(x)) #define MILLISEC_TO_TIMEVAL(x, tv) \ do { \ diff --git a/src/send_packets.c b/src/send_packets.c index effe659e..a129d4d9 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -65,12 +65,12 @@ extern tcpedit_t *tcpedit; extern int debug; #endif -static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_time, - struct timeval *last, COUNTER len, - sendpacket_t *sp, COUNTER counter, timestamp_t *sent_timestamp, +static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, + struct timespec *last, COUNTER len, + sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, COUNTER start_us, COUNTER *skip_length); static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, - struct timespec *nap_this_time, struct timeval *now); + struct timespec *nap_this_time, struct timespec *now); static u_char *get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, @@ -315,9 +315,9 @@ static void increment_iteration(tcpreplay_t *ctx) */ void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) -{ - - struct timeval print_delta, now, last_pkt_ts; +{ + struct timeval last_pkt_ts; + struct timespec now, print_delta; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -339,9 +339,9 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - gettimeofday(&now, NULL); - if (!timerisset(&stats->start_time)) { - TIMEVAL_SET(&stats->start_time, &now); + ctx->timefunction.gettime(&now); + if (!timesisset(&stats->start_time)) { + TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { char buf[64]; if (format_date_time(&stats->start_time, buf, sizeof(buf)) > 0) @@ -352,7 +352,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) ctx->skip_packets = 0; timerclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) + + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -441,14 +441,14 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) struct timeval delta; timersub(&pkthdr.ts, &last_pkt_ts, &delta); - timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); } } if (!top_speed) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } /* @@ -459,7 +459,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) */ calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, pktlen, sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), &skip_length); + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* * Track the time of the "last packet sent". @@ -467,8 +467,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * A number of 3rd party tools generate bad timestamps which go backwards * in time. Hence, don't update the "last" unless pkthdr.ts > last */ - if (timercmp(&stats->time_delta, &stats->pkt_ts_delta, <)) - TIMEVAL_SET(&stats->time_delta, &stats->pkt_ts_delta); + if (timescmp(&stats->time_delta, &stats->pkt_ts_delta, <)) + TIMESPEC_SET(&stats->time_delta, &stats->pkt_ts_delta); /* * we know how long to sleep between sends, now do it. @@ -493,7 +493,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) /* * Mark the time when we sent the last packet */ - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); #ifdef TIMESTAMP_TRACE add_timestamp_trace_entry(pktlen, &stats->end_time, skip_length); @@ -504,14 +504,14 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) /* print stats during the run? */ if (options->stats > 0) { - if (! timerisset(&stats->last_print)) { - TIMEVAL_SET(&stats->last_print, &now); + if (! timesisset(&stats->last_print)) { + TIMESPEC_SET(&stats->last_print, &now); } else { - timersub(&now, &stats->last_print, &print_delta); + timessub(&now, &stats->last_print, &print_delta); if (print_delta.tv_sec >= options->stats) { - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); packet_stats(stats); - TIMEVAL_SET(&stats->last_print, &now); + TIMESPEC_SET(&stats->last_print, &now); } } } @@ -523,33 +523,32 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) } #endif /* stop sending based on the duration limit... */ - if ((end_us > 0 && (COUNTER)TIMEVAL_TO_MICROSEC(&now) > end_us) || + if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || /* ... or stop sending based on the limit -L? */ (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ - #ifdef HAVE_NETMAP /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { now_is_now = true; - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -561,7 +560,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) void send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *pcap2, int cache_file_idx2) { - struct timeval print_delta, now, last_pkt_ts; + struct timeval last_pkt_ts; + struct timespec now, print_delta; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -581,9 +581,9 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - gettimeofday(&now, NULL); - if (!timerisset(&stats->start_time)) { - TIMEVAL_SET(&stats->start_time, &now); + ctx->timefunction.gettime(&now); + if (!timesisset(&stats->start_time)) { + TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { char buf[64]; if (format_date_time(&stats->start_time, buf, sizeof(buf)) > 0) @@ -594,7 +594,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; timerclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMEVAL_TO_MICROSEC(&stats->start_time) + + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -714,16 +714,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * struct timeval delta; timersub(&pkthdr_ptr->ts, &last_pkt_ts, &delta); - timeradd(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); } - if (!timerisset(&stats->time_delta)) - TIMEVAL_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta); + if (!timesisset(&stats->time_delta)) + TIMESPEC_SET(&stats->pkt_ts_delta, &stats->pkt_ts_delta); } if (!top_speed) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } @@ -735,7 +735,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * */ calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, pktlen, sp, packetnum, &stats->end_time, - TIMEVAL_TO_MICROSEC(&stats->start_time), &skip_length); + TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); /* * Track the time of the "last packet sent". @@ -743,8 +743,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * * A number of 3rd party tools generate bad timestamps which go backwards * in time. Hence, don't update the "last" unless pkthdr_ptr->ts > last */ - if (timercmp(&stats->time_delta, &stats->pkt_ts_delta, <)) - TIMEVAL_SET(&stats->time_delta, &stats->pkt_ts_delta); + if (timescmp(&stats->time_delta, &stats->pkt_ts_delta, <)) + TIMESPEC_SET(&stats->time_delta, &stats->pkt_ts_delta); /* * we know how long to sleep between sends, now do it. @@ -769,21 +769,21 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* * Mark the time when we sent the last packet */ - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); ++stats->pkts_sent; stats->bytes_sent += pktlen; /* print stats during the run? */ if (options->stats > 0) { - if (! timerisset(&stats->last_print)) { - TIMEVAL_SET(&stats->last_print, &now); + if (! timesisset(&stats->last_print)) { + TIMESPEC_SET(&stats->last_print, &now); } else { - timersub(&now, &stats->last_print, &print_delta); + timessub(&now, &stats->last_print, &print_delta); if (print_delta.tv_sec >= options->stats) { - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); packet_stats(stats); - TIMEVAL_SET(&stats->last_print, &now); + TIMESPEC_SET(&stats->last_print, &now); } } } @@ -803,7 +803,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } /* stop sending based on the duration limit... */ - if ((end_us > 0 && (COUNTER)TIMEVAL_TO_MICROSEC(&now) > end_us) || + if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || /* ... or stop sending based on the limit -L? */ (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; @@ -814,21 +814,21 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); now_is_now = true; } } #endif /* HAVE_NETMAP */ if (!now_is_now) - gettimeofday(&now, NULL); + ctx->timefunction.gettime(&now); - TIMEVAL_SET(&stats->end_time, &now); + TIMESPEC_SET(&stats->end_time, &now); increment_iteration(ctx); } @@ -965,14 +965,14 @@ cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num) * calculate the appropriate amount of time to sleep. Sleep time * will be in ctx->nap. */ -static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, - struct timeval *time_delta, COUNTER len, - sendpacket_t *sp, COUNTER counter, timestamp_t *sent_timestamp, - COUNTER start_us, COUNTER *skip_length) +static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, + struct timespec *time_delta, COUNTER len, + sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, + COUNTER start_ns, COUNTER *skip_length) { tcpreplay_opt_t *options = ctx->options; - struct timeval nap_for; - COUNTER now_us; + struct timespec nap_for; + COUNTER now_ns; timesclear(&ctx->nap); @@ -996,10 +996,10 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Replay packets a factor of the time they were originally sent. * Make sure the packet is not late. */ - if (timercmp(pkt_ts_delta, time_delta, >)) { + if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ - timersub(pkt_ts_delta, time_delta, &nap_for); - TIMEVAL_TO_TIMESPEC(&nap_for, &ctx->nap); + timessub(pkt_ts_delta, time_delta, &nap_for); + TIMESPEC_SET(&nap_for, &ctx->nap); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1013,32 +1013,32 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Ignore the time supplied by the capture file and send data at * a constant 'rate' (bytes per second). */ - now_us = TIMSTAMP_TO_MICROSEC(sent_timestamp); - if (now_us) { - COUNTER next_tx_us; + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); - COUNTER tx_us = now_us - start_us; + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); //PB: Ferencol miert bits sent? + COUNTER tx_ns = now_ns - start_ns; /* - * bits * 1000000 divided by bps = microseconds + * bits * 1000000000 divided by bps = nanosecond * * ensure there is no overflow in cases where bits_sent is very high */ if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) - next_tx_us = (bits_sent * 1000) / bps * 1000; + next_tx_ns = (bits_sent * 1000) / bps * 1000000; else - next_tx_us = (bits_sent * 1000000) / bps; + next_tx_ns = (bits_sent * 1000000000) / bps; - if (next_tx_us > tx_us) { - NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap); - } else if (tx_us > next_tx_us) { - tx_us = now_us - start_us; - *skip_length = ((tx_us - next_tx_us) * bps) / 8000000; + if (next_tx_ns > tx_ns) { + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); + } else if (tx_ns > next_tx_ns) { + tx_ns = now_ns - start_ns; + *skip_length = ((tx_ns - next_tx_ns) * bps) / 8000000000; } update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_us, tx_us, next_tx_us); + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet size=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, len, @@ -1050,12 +1050,12 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * Ignore the time supplied by the capture file and send data at * a constant rate (packets per second). */ - now_us = TIMSTAMP_TO_MICROSEC(sent_timestamp); - if (now_us) { - COUNTER next_tx_us; + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; COUNTER pph = ctx->options->speed.speed; COUNTER pkts_sent = ctx->stats.pkts_sent; - COUNTER tx_us = now_us - start_us; + COUNTER tx_ns = now_ns - start_ns; /* * packets * 1000000 divided by pps = microseconds * packets per sec (pps) = packets per hour / (60 * 60) @@ -1064,17 +1064,17 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, * When active, adjusted calculation may add a bit of jitter. */ if ((pkts_sent < COUNTER_OVERFLOW_RISK)) - next_tx_us = (pkts_sent * 1000000) * (60 * 60) / pph; + next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; else - next_tx_us = ((pkts_sent * 1000000) / pph) * (60 * 60); + next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); - if (next_tx_us > tx_us) - NANOSEC_TO_TIMESPEC((next_tx_us - tx_us) * 1000, &ctx->nap); + if (next_tx_ns > tx_ns) + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); else ctx->skip_packets = options->speed.pps_multi; update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_us, tx_us, next_tx_us); + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, @@ -1105,7 +1105,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_ts_delta, } static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, - struct timespec *nap_this_time, struct timeval *now) + struct timespec *nap_this_time, struct timespec *now) { tcpreplay_opt_t *options = ctx->options; bool flush = diff --git a/src/signal_handler.c b/src/signal_handler.c index ddc43efb..17f546f9 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -35,7 +35,7 @@ #include "tcpreplay_api.h" #include "signal_handler.h" -struct timeval suspend_time; +struct timeval suspend_time; // PB: do we need to modify this part of the code? static struct timeval suspend_start; static struct timeval suspend_end; diff --git a/src/sleep.c b/src/sleep.c index 9042bc13..151e7a2c 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -55,32 +55,32 @@ ioport_sleep_init(void) void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, - struct timeval *now _U_, bool flush _U_) + struct timespec *now _U_, bool flush _U_) { #if defined HAVE_IOPORT_SLEEP__ - struct timeval nap_for; - u_int32_t usec; + struct timespec nap_for; + u_int32_t nsec; time_t i; - TIMESPEC_TO_TIMEVAL(&nap_for, nap); + TIMESPEC_SET(&nap_for, nap); /* * process the seconds, we do this in a loop so we don't have to * use slower 64bit integers or worry about integer overflows. */ for (i = 0; i < nap_for.tv_sec; i ++) { - usec = SEC_TO_MICROSEC(nap_for.tv_sec); + nsec = nap_for.tv_sec * 1000000000; while (usec > 0) { usec --; outb(ioport_sleep_value, 0x80); } } - /* process the usec */ - usec = nap->tv_nsec / 1000; - usec --; /* fudge factor for all the above */ - while (usec > 0) { - usec --; + /* process the nsec */ + nsec = nap->tv_nsec; + nsec --; /* fudge factor for all the above */ + while (nsec > 0) { + nsec --; outb(ioport_sleep_value, 0x80); } #else @@ -91,6 +91,7 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now); } diff --git a/src/sleep.h b/src/sleep.h index 5f67ca58..c3e92142 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -54,7 +55,7 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush _U_) + struct timespec *now, bool flush _U_) { nanosleep(nap, NULL); #ifdef HAVE_NETMAP @@ -62,7 +63,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - gettimeofday(now, NULL); + clock_gettime(CLOCK_REALTIME, now); } @@ -74,36 +75,38 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, */ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timeval *now, bool flush _U_) + struct timespec *now, bool flush _U_) { - struct timeval sleep_until, nap_for; + struct timespec sleep_until, nap_for; + init_timestamp(&nap_for); #ifdef HAVE_NETMAP - struct timeval last; + struct timespec last; uint32_t i = 0; - TIMEVAL_SET(&last, now); + TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - TIMESPEC_TO_TIMEVAL(&nap_for, nap); - timeradd(now, &nap_for, &sleep_until); + timeradd_timespec(now, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timercmp(now, &last, !=)) { - TIMEVAL_SET(&last, now); + if (flush && timescmp(now, &last, !=)) { + TIMESPEC_SET(&last, now); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timercmp(now, &sleep_until, >=)) + if (timescmp(now, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now); } } @@ -115,16 +118,18 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, * for future reference */ static inline void -select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush _U_) +select_sleep(sendpacket_t *sp _U_, struct timespec *nap, + struct timespec *now_ns, bool flush _U_) { struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; #ifdef HAVE_NETMAP if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - TIMESPEC_TO_TIMEVAL(&timeout, nap); + TIMEVAL_TO_TIMESPEC(&timeout, nap); if (select(0, NULL, NULL, NULL, &timeout) < 0) warnx("select_sleep() returned early due to error: %s", strerror(errno)); @@ -133,8 +138,9 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - - gettimeofday(now, NULL); + struct timeval now_ms; + gettimeofday(&now_ms, NULL); + TIMEVAL_TO_TIMESPEC(&now_ms, now_ns); } #endif /* HAVE_SELECT */ @@ -150,6 +156,6 @@ select_sleep(sendpacket_t *sp _U_, const struct timespec *nap, void ioport_sleep_init(void); void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timeval *now, bool flush); + struct timespec *now, bool flush); #endif /* __SLEEP_H__ */ diff --git a/src/tcpbridge.c b/src/tcpbridge.c index b1e4f568..059b00f4 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -98,7 +98,7 @@ main(int argc, char *argv[]) } #endif - if (gettimeofday(&stats.start_time, NULL) < 0) { + if (get_time_of_day(&stats.start_time) < 0) { tcpedit_close(&tcpedit); err(-1, "gettimeofday() failed"); } diff --git a/src/tcpreplay.c b/src/tcpreplay.c index 144594b8..09d7b5c7 100644 --- a/src/tcpreplay.c +++ b/src/tcpreplay.c @@ -220,7 +220,7 @@ main(int argc, char *argv[]) */ static void flow_stats(const tcpreplay_t *ctx) { - struct timeval diff; + struct timespec diff; COUNTER diff_us; const tcpreplay_stats_t *stats = &ctx->stats; const tcpreplay_opt_t *options = ctx->options; @@ -232,8 +232,8 @@ static void flow_stats(const tcpreplay_t *ctx) COUNTER flows_sec = 0; u_int32_t flows_sec_100ths = 0; - timersub(&stats->end_time, &stats->start_time, &diff); - diff_us = TIMEVAL_TO_MICROSEC(&diff); + timessub(&stats->end_time, &stats->start_time, &diff); + diff_us = TIMESPEC_TO_MICROSEC(&diff); if (!flows_total || !ctx->iteration) return; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index d429b91b..ebe7029b 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -31,9 +31,11 @@ #include #include #include +#include #include "tcpreplay_api.h" #include "send_packets.h" +#include "sleep.h" #include "replay.h" #ifdef TCPREPLAY_EDIT @@ -284,7 +286,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) if (HAVE_OPT(FLOW_EXPIRY)) { options->flow_expiry = OPT_VALUE_FLOW_EXPIRY; } - + ctx->timefunction.gettime = &get_time_of_day; if (HAVE_OPT(TIMER)) { if (strcmp(OPT_ARG(TIMER), "select") == 0) { #ifdef HAVE_SELECT @@ -305,6 +307,8 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; + ctx->timefunction.gettime = &clock_get_time; + options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); ret = -1; @@ -895,24 +899,24 @@ tcpreplay_get_failed(tcpreplay_t *ctx) } /** - * \brief returns a pointer to the timeval structure of when replay first started + * \brief returns a pointer to the timespec structure of when replay first started */ -const struct timeval * +const struct timespec * tcpreplay_get_start_time(tcpreplay_t *ctx) { assert(ctx); - TIMEVAL_SET(&ctx->static_stats.start_time, &ctx->stats.start_time); + TIMESPEC_SET(&ctx->static_stats.start_time, &ctx->stats.start_time); return &ctx->static_stats.start_time; } /** - * \brief returns a pointer to the timeval structure of when replay finished + * \brief returns a pointer to the timespec structure of when replay finished */ -const struct timeval * +const struct timespec * tcpreplay_get_end_time(tcpreplay_t *ctx) { assert(ctx); - TIMEVAL_SET(&ctx->static_stats.end_time, &ctx->stats.end_time); + TIMESPEC_SET(&ctx->static_stats.end_time, &ctx->stats.end_time); return &ctx->static_stats.end_time; } @@ -1122,7 +1126,7 @@ tcpreplay_replay(tcpreplay_t *ctx) tcpreplay_seterr(ctx, "invalid dualfile source count: %d", ctx->options->source_cnt); return -1; } - + init_timestamp(&ctx->stats.start_time); init_timestamp(&ctx->stats.time_delta); init_timestamp(&ctx->stats.end_time); @@ -1146,16 +1150,15 @@ tcpreplay_replay(tcpreplay_t *ctx) loop, total_loops, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) + if ((rcode = tcpr_replay_index(ctx)) < 0) { return rcode; + } if (ctx->options->loop > 0) { - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - gettimeofday(&ctx->stats.end_time, NULL); - } - - if (ctx->options->stats == 0) + apply_loop_delay(ctx); + ctx->timefunction.gettime(&ctx->stats.end_time); + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); + } } } } else { @@ -1168,14 +1171,11 @@ tcpreplay_replay(tcpreplay_t *ctx) printf("Loop " COUNTER_SPEC " (" COUNTER_SPEC " unique)...\n", loop, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) + if ((rcode = tcpr_replay_index(ctx)) < 0) { return rcode; - - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - gettimeofday(&ctx->stats.end_time, NULL); } - + apply_loop_delay(ctx); + ctx->timefunction.gettime(&ctx->stats.end_time); if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } @@ -1356,3 +1356,18 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } + +void apply_loop_delay(tcpreplay_t *ctx){ + if(ctx->options->accurate == accurate_nanosleep){ + if (!ctx->abort && ctx->options->loopdelay_ns > 0) { + struct timespec nap; + nap.tv_sec = 0; + nap.tv_nsec = ctx->options->loopdelay_ns; + nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); + } + }else{ + if (!ctx->abort && ctx->options->loopdelay_ms > 0) { + usleep(ctx->options->loopdelay_ms * 1000); + } + } +} diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 7c4e94e9..2acbda65 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -96,6 +96,10 @@ typedef struct { char *filename; } tcpreplay_source_t; +typedef struct time_function { + int (*gettime)(struct timespec*); +} time_function; + /* run-time options */ typedef struct tcpreplay_opt_s { /* input/output */ @@ -105,6 +109,7 @@ typedef struct tcpreplay_opt_s { tcpreplay_speed_t speed; COUNTER loop; u_int32_t loopdelay_ms; + u_int32_t loopdelay_ns; int stats; bool use_pkthdr_len; @@ -189,6 +194,7 @@ typedef struct tcpreplay_s { struct timespec nap; uint32_t skip_packets; bool first_time; + struct time_function timefunction; /* counter stats */ tcpreplay_stats_t stats; @@ -270,13 +276,14 @@ int tcpreplay_set_manual_callback(tcpreplay_t *ctx, tcpreplay_manual_callback); COUNTER tcpreplay_get_pkts_sent(tcpreplay_t *ctx); COUNTER tcpreplay_get_bytes_sent(tcpreplay_t *ctx); COUNTER tcpreplay_get_failed(tcpreplay_t *ctx); -const struct timeval *tcpreplay_get_start_time(tcpreplay_t *ctx); -const struct timeval *tcpreplay_get_end_time(tcpreplay_t *ctx); +const struct timespec *tcpreplay_get_start_time(tcpreplay_t *ctx); +const struct timespec *tcpreplay_get_end_time(tcpreplay_t *ctx); int tcpreplay_set_verbose(tcpreplay_t *, bool); int tcpreplay_set_tcpdump_args(tcpreplay_t *, char *); int tcpreplay_set_tcpdump(tcpreplay_t *, tcpdump_t *); +void apply_loop_delay(tcpreplay_t *ctx); /* * These functions are seen by the outside world, but nobody should ever use them * outside of internal tcpreplay API functions diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a0036f76..39006506 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -328,6 +328,19 @@ flag = { doc = ""; }; +flag = { + name = loopdelay-ns; + flags-must = loop, timer; + arg-type = number; + arg-range = "0->"; + descrip = "Delay between loops in nanoseconds"; + arg-default = 0; + doc = <<- EOText +By default, tcpreplay will use loop delay with microsecond accuracy (loopdelay-ms). +In order to use loop delay with nanosecond accuracy you need to use nano packet timing mode. +EOText; +}; + flag = { name = pktlen; max = 1; diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index 2d19336d..972d4196 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -29,9 +29,9 @@ struct timestamp_trace_entry { COUNTER skip_length; COUNTER size; COUNTER bytes_sent; - COUNTER now_us; - COUNTER tx_us; - COUNTER next_tx_us; + COUNTER now_ns; + COUNTER tx_ns; + COUNTER next_tx_ns; COUNTER sent_bits; struct timeval timestamp; }; @@ -60,7 +60,7 @@ static inline void update_current_timestamp_trace_entry(COUNTER bytes_sent, } static inline void add_timestamp_trace_entry(COUNTER size, - struct timeval *timestamp, COUNTER skip_length) + struct timespec *timestamp, COUNTER skip_length) { if (trace_num >= TRACE_MAX_ENTRIES) return; @@ -68,7 +68,7 @@ static inline void add_timestamp_trace_entry(COUNTER size, timestamp_trace_entry_array[trace_num].skip_length = skip_length; timestamp_trace_entry_array[trace_num].size = size; timestamp_trace_entry_array[trace_num].timestamp.tv_sec = timestamp->tv_sec; - timestamp_trace_entry_array[trace_num].timestamp.tv_usec = timestamp->tv_usec; + timestamp_trace_entry_array[trace_num].timestamp.tv_nsec = timestamp->tv_nsec; ++trace_num; } @@ -102,7 +102,7 @@ static inline void dump_timestamp_trace_array(const struct timeval *start, #else static inline void update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), COUNTER UNUSED(now_us), COUNTER UNUSED(tx_us), COUNTER UNUSED(next_tx_us)) { } -static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timeval *UNUSED(timestamp), +static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), COUNTER UNUSED(skip_length)) { } static inline void dump_timestamp_trace_array(const struct timeval *UNUSED(start), const struct timeval *UNUSED(stop), const COUNTER UNUSED(bps)) { } diff --git a/test/Makefile.am b/test/Makefile.am index 405438aa..d1925e19 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -196,7 +196,7 @@ tcprewrite: rewrite_portmap rewrite_range_portmap rewrite_endpoint \ rewrite_mac_seed_keep rewrite_l7fuzzing rewrite_sequence rewrite_fixcsum \ rewrite_fixlen_pad rewrite_fixlen_trunc rewrite_fixlen_del -tcpreplay: replay_basic replay_cache replay_pps replay_rate replay_top \ +tcpreplay: replay_basic replay_nano_timer replay_cache replay_pps replay_rate replay_top \ replay_config replay_multi replay_pps_multi replay_precache \ replay_stats replay_dualfile replay_maxsleep @@ -344,6 +344,12 @@ replay_basic: $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) -t $(TEST_PCAP) >> test.log 2>&1 if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi +replay_nano_timer: + $(PRINTF) "%s" "[tcpreplay] Nano timer test: " + $(PRINTF) "%s\n" "*** [tcpreplay] Nano timer test: " >> test.log + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --timer=nano --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 + if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi + replay_cache: $(PRINTF) "%s" "[tcpreplay] Cache test: " $(PRINTF) "%s\n" "*** [tcpreplay] Cache test: " >> test.log From 29976333f4b765f91ccfdf999cf1dbc336821b34 Mon Sep 17 00:00:00 2001 From: eplabal Date: Sun, 12 Mar 2023 14:35:53 +0100 Subject: [PATCH 02/69] Use clock_nanosleep with TIMER_ABSTIME, decide time function in use in compile time --- src/bridge.c | 4 ++-- src/common/timer.c | 18 +++++++++--------- src/common/timer.h | 3 +-- src/send_packets.c | 20 ++++++++++---------- src/sleep.c | 4 +--- src/sleep.h | 15 ++++++++++----- src/tcpbridge.c | 4 ++-- src/tcpreplay_api.c | 7 +++---- src/tcpreplay_api.h | 5 ----- src/timestamp_trace.h | 6 +++--- 10 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index cd1b3b76..75450bfa 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -238,8 +238,8 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) do_bridge_bidirectional(options, tcpedit); } - if (get_time_of_day(&stats.end_time) < 0) - errx(-1, "gettimeofday() failed: %s", strerror(errno)); + if (get_current_time(&stats.end_time) < 0) + errx(-1, "get_current_time() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index 6ed978ef..5b62e724 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -91,13 +91,13 @@ init_timestamp(struct timespec *timestamp) timesclear(timestamp); } -int get_time_of_day(struct timespec *ts) { - struct timeval tv; - int success = gettimeofday(&tv, NULL); - TIMEVAL_TO_TIMESPEC(&tv, ts); - return success; -} - -int clock_get_time(struct timespec *ts){ - return clock_gettime(CLOCK_REALTIME, ts); +int get_current_time(struct timespec *ts){ + #ifdef _POSIX_C_SOURCE >= 199309L + return clock_gettime(CLOCK_REALTIME, ts); + #else + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; + #endif } diff --git a/src/common/timer.h b/src/common/timer.h index 5e48d34c..c7a901f2 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -200,7 +200,6 @@ void timesdiv(struct timespec *tvs, COUNTER div); typedef struct timeval timestamp_t; void init_timestamp(struct timespec *timestamp); -int get_time_of_day(struct timespec *ts); -int clock_get_time(struct timespec *ts); +int get_current_time(struct timespec *ts); #endif /* _TIMER_H_ */ diff --git a/src/send_packets.c b/src/send_packets.c index a129d4d9..2314e029 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -339,7 +339,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); if (!timesisset(&stats->start_time)) { TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { @@ -448,7 +448,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) if (!top_speed) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } /* @@ -535,18 +535,18 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); } } #endif /* HAVE_NETMAP */ if (!now_is_now) - ctx->timefunction.gettime(&now); + get_current_time(&now); TIMESPEC_SET(&stats->end_time, &now); @@ -581,7 +581,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * (options->speed.mode == speed_mbpsrate && options->speed.speed == 0)); bool now_is_now = true; - ctx->timefunction.gettime(&now); + get_current_time(&now); if (!timesisset(&stats->start_time)) { TIMESPEC_SET(&stats->start_time, &now); if (ctx->options->stats >= 0) { @@ -723,7 +723,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } if (!top_speed) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } @@ -814,19 +814,19 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* when completing test, wait until the last packet is sent */ if (options->netmap && (ctx->abort || options->loop == 1)) { while (ctx->intf1 && !netmap_tx_queues_empty(ctx->intf1)) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } while (ctx->intf2 && !netmap_tx_queues_empty(ctx->intf2)) { - ctx->timefunction.gettime(&now); + get_current_time(&now); now_is_now = true; } } #endif /* HAVE_NETMAP */ if (!now_is_now) - ctx->timefunction.gettime(&now); + get_current_time(&now); TIMESPEC_SET(&stats->end_time, &now); diff --git a/src/sleep.c b/src/sleep.c index 151e7a2c..f666a8cd 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -91,7 +91,5 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - struct timeval now_ms; - gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now); + get_current_time(now); } diff --git a/src/sleep.h b/src/sleep.h index c3e92142..20eee331 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -57,13 +57,20 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - nanosleep(nap, NULL); + #ifdef _POSIX_C_SOURCE >= 200112L + struct timespec sleep_until; + timeradd_timespec(now, nap, &sleep_until); + clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); + #else + nanosleep(nap, NULL); + #endif + #ifdef HAVE_NETMAP if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif /* HAVE_NETMAP */ - clock_gettime(CLOCK_REALTIME, now); + get_current_time(now); } @@ -138,9 +145,7 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif - struct timeval now_ms; - gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now_ns); + get_current_time(now_ns); } #endif /* HAVE_SELECT */ diff --git a/src/tcpbridge.c b/src/tcpbridge.c index 059b00f4..42641cd4 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -98,9 +98,9 @@ main(int argc, char *argv[]) } #endif - if (get_time_of_day(&stats.start_time) < 0) { + if (get_current_time(&stats.start_time) < 0) { tcpedit_close(&tcpedit); - err(-1, "gettimeofday() failed"); + err(-1, "get_current_time() failed"); } /* process packets */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index ebe7029b..431aa61d 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -286,7 +286,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) if (HAVE_OPT(FLOW_EXPIRY)) { options->flow_expiry = OPT_VALUE_FLOW_EXPIRY; } - ctx->timefunction.gettime = &get_time_of_day; + if (HAVE_OPT(TIMER)) { if (strcmp(OPT_ARG(TIMER), "select") == 0) { #ifdef HAVE_SELECT @@ -307,7 +307,6 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; - ctx->timefunction.gettime = &clock_get_time; options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); @@ -1155,7 +1154,7 @@ tcpreplay_replay(tcpreplay_t *ctx) } if (ctx->options->loop > 0) { apply_loop_delay(ctx); - ctx->timefunction.gettime(&ctx->stats.end_time); + get_current_time(&ctx->stats.end_time); if (ctx->options->stats == 0) { packet_stats(&ctx->stats); } @@ -1175,7 +1174,7 @@ tcpreplay_replay(tcpreplay_t *ctx) return rcode; } apply_loop_delay(ctx); - ctx->timefunction.gettime(&ctx->stats.end_time); + get_current_time(&ctx->stats.end_time); if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 2acbda65..e26f65a4 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -96,10 +96,6 @@ typedef struct { char *filename; } tcpreplay_source_t; -typedef struct time_function { - int (*gettime)(struct timespec*); -} time_function; - /* run-time options */ typedef struct tcpreplay_opt_s { /* input/output */ @@ -194,7 +190,6 @@ typedef struct tcpreplay_s { struct timespec nap; uint32_t skip_packets; bool first_time; - struct time_function timefunction; /* counter stats */ tcpreplay_stats_t stats; diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index 972d4196..058bbd64 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -48,9 +48,9 @@ static inline void update_current_timestamp_trace_entry(COUNTER bytes_sent, return; if (!now_us) { - struct timeval now; - gettimeofday(&now, NULL); - now_us = TIMSTAMP_TO_MICROSEC(&now); + struct timespec now; + get_current_time(now); + now_us = TIMESPEC_TO_MICROSEC(&now); } timestamp_trace_entry_array[trace_num].bytes_sent = bytes_sent; From 961bc4016e49613869d50890f0f28965aee0c31e Mon Sep 17 00:00:00 2001 From: eplabal Date: Wed, 5 Apr 2023 14:25:56 +0200 Subject: [PATCH 03/69] libxdp implementation --- configure.ac | 47 ++++++++- src/common/sendpacket.c | 207 ++++++++++++++++++++++++++++++++++++++-- src/common/sendpacket.h | 111 ++++++++++++++++++++- src/defines.h.in | 22 +++++ src/send_packets.c | 97 ++++++++++++++++--- src/send_packets.h | 6 +- src/tcpreplay_api.c | 45 ++++++++- src/tcpreplay_api.h | 10 +- src/tcpreplay_opts.def | 9 ++ 9 files changed, 525 insertions(+), 29 deletions(-) diff --git a/configure.ac b/configure.ac index d624d4d4..199171bc 100644 --- a/configure.ac +++ b/configure.ac @@ -550,6 +550,10 @@ AC_ARG_ENABLE(force-libdnet, AS_HELP_STRING([--enable-force-libdnet],[Force using libdnet for sending packets]), [ AC_DEFINE([FORCE_INJECT_LIBDNET], [1], [Force using libdnet for sending packets])]) +AC_ARG_ENABLE(force-libxdp, + AS_HELP_STRING([--enable-force-libxdp],[Force using libxdp for sending packets]), + [ AC_DEFINE([FORCE_INJECT_LIBXDP], [1], [Force using libxdp for sending packets])]) + AC_ARG_ENABLE(force-inject, AS_HELP_STRING([--enable-force-inject],[Force using libpcap's pcap_inject() for sending packets]), [ AC_DEFINE([FORCE_INJECT_PCAP_INJECT],[1], [Force using libpcap's pcap_inject() for sending packets])]) @@ -826,7 +830,13 @@ fi # libpcap can require libnl AC_SEARCH_LIBS([nl_handle_alloc], [nl], - [AC_MSG_NOTICE([Unable to find nl library - may be needed by libpcap])]) + [AC_MSG_NOTICE([Unable to find xdp library - may be needed by libpcap])]) + +AC_CHECK_LIB(bpf, bpf_object__open_file,, + [AC_MSG_NOTICE([Unable to find libbpf library ])]) + +AC_CHECK_LIB(xdp, xsk_umem__delete,, + [AC_MSG_NOTICE([Unable to find libxdp library ])]) ## ## If not automatically configured, @@ -1383,6 +1393,37 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ AC_MSG_RESULT(no) ]) +have_libxdp=no +dnl Check for LIBXDP AF_XDP socket support +AC_MSG_CHECKING(for LIBXDP XDP packet sending support) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +]], [[ + struct xsk_socket { + struct xsk_ring_cons *rx; + struct xsk_ring_prod *tx; + struct xsk_ctx *ctx; + struct xsk_socket_config config; + int fd; + }; + struct xsk_socket *xsk; + struct xsk_ring_cons *rxr = NULL; + struct xsk_ring_prod *txr = NULL; + xsk = (struct xsk_socket*)malloc(sizeof(struct xsk_socket)); + int queue_id = 0; + xsk_socket__create(xsk, "lo", queue_id, NULL, rxr, txr, NULL); + socket(AF_XDP, SOCK_RAW, 0); +]])],[ + AC_DEFINE([HAVE_LIBXDP], [1], + [Do we have LIBXDP AF_XDP socket support?]) + AC_MSG_RESULT(yes) + have_libxdp=yes +],[ + AC_MSG_RESULT(no) +]) + have_tx_ring=no dnl Check for older Linux TX_RING support AC_MSG_CHECKING(for TX_RING socket sending support) @@ -1404,6 +1445,9 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ AC_MSG_RESULT(no) ]) +AC_CHECK_HEADERS([bpf/libbpf.h]) +AC_CHECK_HEADERS([bpf/bpf.h]) +AC_CHECK_HEADERS([xdp/libxdp.h]) AC_CHECK_HEADERS([net/bpf.h], [have_bpf=yes], [have_bpf=no]) if test $have_bpf = yes ; then @@ -1923,6 +1967,7 @@ pcap_sendpacket: ${have_pcap_sendpacket} ** pcap_netmap ${have_pcap_netmap} Linux/BSD netmap: ${have_netmap} Tuntap device support: ${have_tuntap} +LIBXDP for AF_XDP socket: ${have_libxdp} * In order of preference; see configure --help to override ** Required for tcpbridge diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index e0e0dc43..d7c12e53 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -64,6 +64,7 @@ #undef HAVE_PCAP_INJECT #undef HAVE_PCAP_SENDPACKET #undef HAVE_BPF +#undef HAVE_LIBXDP #endif #ifdef FORCE_INJECT_PF_PACKET @@ -72,6 +73,7 @@ #undef HAVE_PCAP_INJECT #undef HAVE_PCAP_SENDPACKET #undef HAVE_BPF +#undef HAVE_LIBXDP #endif #ifdef FORCE_INJECT_LIBDNET @@ -80,6 +82,7 @@ #undef HAVE_PCAP_INJECT #undef HAVE_PCAP_SENDPACKET #undef HAVE_BPF +#undef HAVE_LIBXDP #endif #ifdef FORCE_INJECT_BPF @@ -88,6 +91,7 @@ #undef HAVE_PCAP_INJECT #undef HAVE_PCAP_SENDPACKET #undef HAVE_PF_PACKET +#undef HAVE_LIBXDP #endif #ifdef FORCE_INJECT_PCAP_INJECT @@ -96,6 +100,7 @@ #undef HAVE_PCAP_SENDPACKET #undef HAVE_BPF #undef HAVE_PF_PACKET +#undef HAVE_LIBXDP #endif #ifdef FORCE_INJECT_PCAP_SENDPACKET @@ -104,14 +109,24 @@ #undef HAVE_PCAP_INJECT #undef HAVE_BPF #undef HAVE_PF_PACKET +#undef HAVE_LIBXDP +#endif + +#ifdef FORCE_INJECT_LIBXDP +#undef HAVE_TX_RING +#undef HAVE_LIBDNET +#undef HAVE_PF_PACKET +#undef HAVE_PCAP_INJECT +#undef HAVE_PCAP_SENDPACKET +#undef HAVE_BPF #endif #if (defined HAVE_WINPCAP && defined HAVE_PCAP_INJECT) #undef HAVE_PCAP_INJECT /* configure returns true for some odd reason */ #endif -#if !defined HAVE_PCAP_INJECT && !defined HAVE_PCAP_SENDPACKET && !defined HAVE_LIBDNET && !defined HAVE_PF_PACKET && !defined HAVE_BPF && !defined TX_RING -#error You need pcap_inject() or pcap_sendpacket() from libpcap, libdnet, Linux's PF_PACKET/TX_RING or *BSD's BPF +#if !defined HAVE_PCAP_INJECT && !defined HAVE_PCAP_SENDPACKET && !defined HAVE_LIBDNET && !defined HAVE_PF_PACKET && !defined HAVE_BPF && !defined TX_RING && !defined HAVE_LIBXDP +#error You need pcap_inject() or pcap_sendpacket() from libpcap, libdnet, Linux's PF_PACKET/TX_RING/AF_XDP with libxdp or *BSD's BPF #endif @@ -211,7 +226,15 @@ static struct tcpr_ether_addr *sendpacket_get_hwaddr_pcap(sendpacket_t *) _U_; #undef INJECT_METHOD #define INJECT_METHOD "pcap_sendpacket()" #endif - +#ifdef HAVE_LIBXDP +#include +static sendpacket_t *sendpacket_open_xsk(const char *, char *) _U_; +static struct tcpr_ether_addr *sendpacket_get_hwaddr_libxdp(sendpacket_t *); +#endif +#if defined HAVE_LIBXDP && ! defined INJECT_METHOD +#undef INJECT_METHOD +#define INJECT_METHOD "xsk_ring_prod_submit()" +#endif static void sendpacket_seterr(sendpacket_t *sp, const char *fmt, ...); static sendpacket_t * sendpacket_open_khial(const char *, char *) _U_; static struct tcpr_ether_addr * sendpacket_get_hwaddr_khial(sendpacket_t *) _U_; @@ -237,7 +260,10 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr static const size_t buffer_payload_size = sizeof(buffer) + sizeof(struct pcap_pkthdr); assert(sp); + #ifndef HAVE_LIBXDP + // In case of XDP packet processing we are storing data in sp->packet_processing->xdp_descs assert(data); + #endif if (len == 0) return -1; @@ -444,7 +470,18 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr } #endif /* HAVE_NETMAP */ break; - + case SP_TYPE_LIBXDP: + #ifdef HAVE_LIBXDP + retcode = len; + xsk_ring_prod__submit(&(sp->xsk_info->tx), sp->pckt_count); //submit all packets at once + sp->xsk_info->ring_stats.tx_npkts += sp->pckt_count; + sp->xsk_info->outstanding_tx += sp->pckt_count; + while(sp->xsk_info->outstanding_tx != 0){ + complete_tx_only(sp); + } + sp->sent += sp->pckt_count; + #endif + break; default: errx(-1, "Unsupported sp->handle_type = %d", sp->handle_type); } /* end case */ @@ -458,8 +495,15 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr retcode, len); sp->trunc_packets ++; } else { + #ifndef HAVE_LIBXDP sp->bytes_sent += len; sp->sent ++; + #else + if(sp->handle_type != SP_TYPE_LIBXDP){ + sp->bytes_sent += len; + sp->sent ++; + } + #endif } return retcode; } @@ -542,6 +586,8 @@ sendpacket_open(const char *device, char *errbuf, tcpr_dir_t direction, sp = sendpacket_open_libdnet(device, errbuf); #elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) sp = sendpacket_open_pcap(device, errbuf); +#elif defined HAVE_LIBXDP + sp = sendpacket_open_xsk(device, errbuf); #else #error "No defined packet injection method for sendpacket_open()" #endif @@ -562,13 +608,13 @@ sendpacket_open(const char *device, char *errbuf, tcpr_dir_t direction, size_t sendpacket_getstat(sendpacket_t *sp, char *buf, size_t buf_size) { - size_t offset; + size_t offset = 0; assert(sp); assert(buf); memset(buf, 0, buf_size); - offset = snprintf(buf, buf_size, "Statistics for network device: %s\n" + snprintf(buf, buf_size, "Statistics for network device: %s\n" "\tSuccessful packets: " COUNTER_SPEC "\n" "\tFailed packets: " COUNTER_SPEC "\n" "\tTruncated packets: " COUNTER_SPEC "\n" @@ -658,7 +704,7 @@ sendpacket_close(sendpacket_t *sp) struct tcpr_ether_addr * sendpacket_get_hwaddr(sendpacket_t *sp) { - struct tcpr_ether_addr *addr; + struct tcpr_ether_addr *addr = NULL; assert(sp); /* if we already have our MAC address stored, just return it */ @@ -670,6 +716,8 @@ sendpacket_get_hwaddr(sendpacket_t *sp) } else { #if defined HAVE_PF_PACKET addr = sendpacket_get_hwaddr_pf(sp); +#elif defined HAVE_LIBXDP + addr = sendpacket_get_hwaddr_libxdp(sp); #elif defined HAVE_BPF addr = sendpacket_get_hwaddr_bpf(sp); #elif defined HAVE_LIBDNET @@ -1055,7 +1103,7 @@ sendpacket_get_hwaddr_pf(sendpacket_t *sp) } #endif /* HAVE_PF_PACKET */ -#if defined HAVE_BPF +#if 0 /** * Inner sendpacket_open() method for using BSD's BPF interface */ @@ -1244,7 +1292,7 @@ sendpacket_get_dlt(sendpacket_t *sp) /* always EN10MB */ ; } else { -#if defined HAVE_BPF +#if 0 int rcode; if ((rcode = ioctl(sp->handle.fd, BIOCGDLT, &dlt)) < 0) { @@ -1333,3 +1381,144 @@ sendpacket_abort(sendpacket_t *sp) sp->abort = true; } +#ifdef HAVE_LIBXDP +static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem, struct xsk_socket_config* cfg, int queue_id, const char *device) +{ + struct xsk_socket_info *xsk; + struct xsk_ring_cons *rxr = NULL; + struct xsk_ring_prod *txr; + int ret; + xsk = (struct xsk_socket_info*)safe_malloc(sizeof(struct xsk_socket_info)); + xsk->umem = umem; + ret = xsk_socket__create(&xsk->xsk, device, queue_id, umem->umem, rxr, &xsk->tx, cfg); + if (ret){ + return NULL; + } + + memset(&xsk->app_stats, 0, sizeof(xsk->app_stats)); + + return xsk; +} + +static sendpacket_t * sendpacket_open_xsk(const char *device, char *errbuf){ + sendpacket_t *sp; + + assert(device); + assert(errbuf); + + int nb_of_frames = 4096; + int frame_size = 4096; + int nb_of_completion_queue_desc = 4096; + int nb_of_fill_queue_desc = 4096; + struct xsk_umem_info* umem_info = create_umem_area(nb_of_frames, frame_size, nb_of_completion_queue_desc, nb_of_fill_queue_desc); + if(umem_info == NULL){ + return NULL; + } + + int nb_of_tx_queue_desc = 4096; + int nb_of_rx_queue_desc = 4096; + u_int32_t queue_id = 0; + struct xsk_socket_info* xsk_info = create_xsk_socket(umem_info, nb_of_tx_queue_desc, nb_of_rx_queue_desc, device, queue_id, errbuf); + if(xsk_info == NULL){ + return NULL; + } + + sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t)); + strlcpy(sp->device, device, sizeof(sp->device)); + sp->handle.fd = xsk_info->xsk->fd; + sp->handle_type = SP_TYPE_LIBXDP; + sp->xsk_info = xsk_info; + sp->umem_info = umem_info; + sp->frame_size = frame_size; + return sp; +} + +struct xsk_umem_info* create_umem_area(int nb_of_frames, int frame_size, int nb_of_completion_queue_descs, int nb_of_fill_queue_descs){ + int umem_size = nb_of_frames * frame_size; + struct xsk_umem_info *umem; + void* umem_area = NULL; + struct xsk_umem_config cfg = { + /* We recommend that you set the fill ring size >= HW RX ring size + + * AF_XDP RX ring size. Make sure you fill up the fill ring + * with buffers at regular intervals, and you will with this setting + * avoid allocation failures in the driver. These are usually quite + * expensive since drivers have not been written to assume that + * allocation failures are common. For regular sockets, kernel + * allocated memory is used that only runs out in OOM situations + * that should be rare. + */ + .fill_size = nb_of_fill_queue_descs * 2, + .comp_size = nb_of_completion_queue_descs, + .frame_size = frame_size, + .frame_headroom = 0, + .flags = XDP_UMEM_UNALIGNED_CHUNK_FLAG + }; + umem = (struct xsk_umem_info*)safe_malloc(sizeof(struct xsk_umem_info)); + if (posix_memalign(&umem_area, getpagesize(), /* PAGE_SIZE aligned */ + umem_size)) { + fprintf(stderr, "ERROR: Can't allocate buffer memory \"%s\"\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + int ret = xsk_umem__create(&umem->umem, umem_area, umem_size, &umem->fq, &umem->cq, &cfg); + umem->buffer = umem_area; + if(ret != 0){ + return NULL; + } + return umem; +} + +static struct xsk_socket_info* create_xsk_socket(struct xsk_umem_info* umem_info, int nb_of_tx_queue_desc, int nb_of_rx_queue_desc, const char *device, u_int32_t queue_id, char *errbuf){ + struct xsk_socket_info* xsk_info = (struct xsk_socket_info*)safe_malloc(sizeof(struct xsk_socket_info)); + struct xsk_socket_config* socket_config = (struct xsk_socket_config*)safe_malloc(sizeof(struct xsk_socket_config)); + + socket_config->rx_size = nb_of_rx_queue_desc; + socket_config->tx_size = nb_of_tx_queue_desc; + socket_config->libbpf_flags = XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD; + socket_config->bind_flags = 0; //XDP_FLAGS_SKB_MODE (1U << 1) or XDP_FLAGS_DRV_MODE (1U << 2) + xsk_info = xsk_configure_socket(umem_info, socket_config, queue_id, device); + + if(xsk_info == NULL){ + snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "AF_XDP socket configuration is not successful: %s", strerror(errno)); + return NULL; + } + return xsk_info; +} + +/** + * gets the hardware address via Linux's PF packet interface + */ +static struct tcpr_ether_addr * +sendpacket_get_hwaddr_libxdp(sendpacket_t *sp) +{ + struct ifreq ifr; + int fd; + + assert(sp); + + if (!sp->open) { + sendpacket_seterr(sp, "Unable to get hardware address on un-opened sendpacket handle"); + return NULL; + } + + + /* create dummy socket for ioctl */ + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + sendpacket_seterr(sp, "Unable to open dummy socket for get_hwaddr: %s", strerror(errno)); + return NULL; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, sp->device, sizeof(ifr.ifr_name)); + + if (ioctl(fd, SIOCGIFHWADDR, (int8_t *)&ifr) < 0) { + close(fd); + sendpacket_seterr(sp, "Error getting hardware address: %s", strerror(errno)); + return NULL; + } + + memcpy(&sp->ether, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN); + close(fd); + return(&sp->ether); +} +#endif /*HAVE_LIBXDP*/ diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index 248b87f2..f13762ef 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -69,7 +69,8 @@ typedef enum sendpacket_type_e { SP_TYPE_TX_RING, SP_TYPE_KHIAL, SP_TYPE_NETMAP, - SP_TYPE_TUNTAP + SP_TYPE_TUNTAP, + SP_TYPE_LIBXDP } sendpacket_type_t; /* these are the file_operations ioctls */ @@ -94,6 +95,70 @@ union sendpacket_handle { #define SENDPACKET_ERRBUF_SIZE 1024 #define MAX_IFNAMELEN 64 +#ifdef HAVE_LIBXDP +#include +#include +#include + +struct xsk_ring_stats { + unsigned long rx_npkts; + unsigned long tx_npkts; + unsigned long rx_dropped_npkts; + unsigned long rx_invalid_npkts; + unsigned long tx_invalid_npkts; + unsigned long rx_full_npkts; + unsigned long rx_fill_empty_npkts; + unsigned long tx_empty_npkts; + unsigned long prev_rx_npkts; + unsigned long prev_tx_npkts; + unsigned long prev_rx_dropped_npkts; + unsigned long prev_rx_invalid_npkts; + unsigned long prev_tx_invalid_npkts; + unsigned long prev_rx_full_npkts; + unsigned long prev_rx_fill_empty_npkts; + unsigned long prev_tx_empty_npkts; +}; +struct xsk_driver_stats { + unsigned long intrs; + unsigned long prev_intrs; +}; +struct xsk_app_stats { + unsigned long rx_empty_polls; + unsigned long fill_fail_polls; + unsigned long copy_tx_sendtos; + unsigned long tx_wakeup_sendtos; + unsigned long opt_polls; + unsigned long prev_rx_empty_polls; + unsigned long prev_fill_fail_polls; + unsigned long prev_copy_tx_sendtos; + unsigned long prev_tx_wakeup_sendtos; + unsigned long prev_opt_polls; +}; +struct xsk_umem_info { + struct xsk_ring_prod fq; + struct xsk_ring_cons cq; + struct xsk_umem *umem; + void *buffer; +}; +struct xsk_socket { + struct xsk_ring_cons *rx; + struct xsk_ring_prod *tx; + struct xsk_ctx *ctx; + struct xsk_socket_config config; + int fd; +}; +struct xsk_socket_info { + struct xsk_ring_cons rx; + struct xsk_ring_prod tx; + struct xsk_umem_info *umem; + struct xsk_socket *xsk; + struct xsk_ring_stats ring_stats; + struct xsk_app_stats app_stats; + struct xsk_driver_stats drv_stats; + u_int32_t outstanding_tx; +}; +#endif /*HAVE_LIBXDP*/ + struct sendpacket_s { tcpr_dir_t cache_dir; int open; @@ -144,12 +209,54 @@ struct sendpacket_s { #ifdef HAVE_TX_RING txring_t * tx_ring; #endif +#endif +#ifdef HAVE_LIBXDP + struct xsk_socket_info* xsk_info; + struct xsk_umem_info* umem_info; + unsigned int batch_size; + unsigned int pckt_count; + int frame_size; + int tx_idx; #endif bool abort; }; - typedef struct sendpacket_s sendpacket_t; +#ifdef HAVE_LIBXDP +struct xsk_umem_info* create_umem_area(int nb_of_frames, int frame_size, int nb_of_completion_queue_descs, int nb_of_fill_queue_descs); +static struct xsk_socket_info* create_xsk_socket(struct xsk_umem_info* umem, int nb_of_tx_queue_desc, int nb_of_rx_queue_desc, const char *device, u_int32_t queue_id, char *errbuf); +static inline void gen_eth_frame(struct xsk_umem_info *umem, u_int64_t addr, u_char* pkt_data, COUNTER pkt_size) +{ + memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, pkt_size); +} + +static inline void kick_tx(struct xsk_socket_info *xsk) +{ + int ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); + if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN || + errno == EBUSY || errno == ENETDOWN){ + return; + } + printf("%s\n", "Packet sending exited with error!"); + exit(ret); +} +static inline void complete_tx_only(sendpacket_t *sp) +{ + int completion_idx = 0; + if (sp->xsk_info->outstanding_tx == 0){ + return; + } + if (xsk_ring_prod__needs_wakeup(&(sp->xsk_info->tx))) { + sp->xsk_info->app_stats.tx_wakeup_sendtos++; + kick_tx(sp->xsk_info); + } + unsigned int rcvd = xsk_ring_cons__peek(&sp->xsk_info->umem->cq, sp->pckt_count, &(completion_idx)); + if (rcvd > 0) { + xsk_ring_cons__release(&sp->xsk_info->umem->cq, rcvd); + sp->xsk_info->outstanding_tx -= rcvd; + } +} +#endif /*HAVE_LIBXDP*/ int sendpacket(sendpacket_t *, const u_char *, size_t, struct pcap_pkthdr *); void sendpacket_close(sendpacket_t *); char *sendpacket_geterr(sendpacket_t *); diff --git a/src/defines.h.in b/src/defines.h.in index ea50e0ab..4ce62d3b 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -44,6 +44,28 @@ #include "tcpr.h" +#ifdef HAVE_BPF +#include +#define PCAP_DONT_INCLUDE_PCAP_BPF_H 1 +#endif + +#ifdef HAVE_LIBBPF +#undef HAVE_BPF +#include +#include +#define PCAP_DONT_INCLUDE_PCAP_BPF_H 1 + +struct bpf_program { +char dummy[0]; +}; + +#endif + +#ifdef HAVE_LIBXDP +#include +#endif + + #ifdef HAVE_BPF #include #define PCAP_DONT_INCLUDE_PCAP_BPF_H 1 diff --git a/src/send_packets.c b/src/send_packets.c index effe659e..044b2bd9 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -60,7 +60,6 @@ extern tcpedit_t *tcpedit; #include "send_packets.h" #include "sleep.h" - #ifdef DEBUG extern int debug; #endif @@ -71,7 +70,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timeval *pkt_time, 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, +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); @@ -283,7 +282,7 @@ preload_pcap_file(tcpreplay_t *ctx, int idx) dlt = pcap_datalink(pcap); /* loop through the pcap. get_next_packet() builds the cache for us! */ - while ((pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + while ((pktdata = get_next_packet(ctx->options, pcap, &pkthdr, idx, prev_packet)) != NULL) { packetnum++; if (options->flow_stats) update_flow_stats(ctx, NULL, &pkthdr, pktdata, dlt); @@ -338,6 +337,7 @@ 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)) { @@ -367,8 +367,8 @@ 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(ctx->options, pcap, &pkthdr, idx, prev_packet)) != NULL) { now_is_now = false; packetnum++; @@ -473,8 +473,18 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) /* * 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 @@ -483,6 +493,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 batch*/ + 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) { @@ -500,7 +522,13 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #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) { @@ -545,7 +573,6 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) } } #endif /* HAVE_NETMAP */ - if (!now_is_now) gettimeofday(&now, NULL); @@ -607,8 +634,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(ctx->options, pcap1, &pkthdr1, cache_file_idx1, prev_packet1); + pktdata2 = get_next_packet(ctx->options, pcap2, &pkthdr2, cache_file_idx2, prev_packet2); /* MAIN LOOP * Keep sending while we have packets or until @@ -797,9 +824,9 @@ 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(ctx->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(ctx->options, pcap1, &pkthdr1, cache_file_idx1, prev_packet1); } /* stop sending based on the duration limit... */ @@ -844,10 +871,9 @@ 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, +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; @@ -1211,3 +1237,48 @@ get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter) return(uint32_t)send; } +#ifdef HAVE_LIBXDP +static inline void fill_umem_with_data_and_set_xdp_desc(sendpacket_t* sp, int tx_idx, COUNTER umem_index, u_char* pktdata, int 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; +} + +static inline 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; +} + +static inline 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); + } + } + 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 %llu to %llu\n", packetnum - pckt_count +1, packetnum); +} +#endif /*HAVE_LIBXDP*/ diff --git a/src/send_packets.h b/src/send_packets.h index 316faa32..d6105742 100644 --- a/src/send_packets.h +++ b/src/send_packets.h @@ -28,5 +28,9 @@ void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx); void send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int idx1, pcap_t *pcap2, int idx2); void *cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num); void preload_pcap_file(tcpreplay_t *ctx, int idx); - +#ifdef HAVE_LIBXDP +static inline 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); +static inline void prepare_first_element_of_batch(tcpreplay_t *ctx, COUNTER* packetnum, u_char* pktdata, u_int32_t len); +static inline void fill_umem_with_data_and_set_xdp_desc(sendpacket_t* sp, int tx_idx, COUNTER umem_index, u_char* pktdata, int len); +#endif #endif diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index d429b91b..fe876f88 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -358,7 +358,9 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) ret = -1; goto out; } - +#ifdef HAVE_LIBXDP + ctx->intf1->batch_size = OPT_VALUE_BATCH_SIZE; +#endif #if defined HAVE_NETMAP ctx->intf1->netmap_delay = ctx->options->netmap_delay; #endif @@ -429,6 +431,15 @@ tcpreplay_close(tcpreplay_t *ctx) assert(ctx->options); options = ctx->options; +#ifdef HAVE_LIBXDP + if(ctx->intf1->handle_type == SP_TYPE_LIBXDP){ + free_umem_and_xsk(ctx->intf1); + if(ctx->intf2){ + free_umem_and_xsk(ctx->intf2); + } + } +#endif + safe_free(options->intf1_name); safe_free(options->intf2_name); sendpacket_close(ctx->intf1); @@ -1356,3 +1367,35 @@ 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); + } + } + 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 7c4e94e9..1c2254f4 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -33,7 +33,10 @@ #ifdef ENABLE_DMALLOC #include #endif - +#ifdef HAVE_LIBXDP +#include +#include +#endif #ifdef __cplusplus @@ -203,7 +206,6 @@ typedef struct tcpreplay_s { bool running; } tcpreplay_t; - /* * manual callback definition: * ctx = tcpreplay context @@ -286,6 +288,10 @@ int tcpreplay_set_tcpdump(tcpreplay_t *, tcpdump_t *); void __tcpreplay_seterr(tcpreplay_t *ctx, const char *func, const int line, const char *file, const char *fmt, ...); void tcpreplay_setwarn(tcpreplay_t *ctx, const char *fmt, ...); +#ifdef HAVE_LIBXDP +void delete_xsk_socket(struct xsk_socket *xsk); +void free_umem_and_xsk(sendpacket_t* sp); +#endif #ifdef __cplusplus } #endif diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a0036f76..9d3d5e57 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -663,6 +663,15 @@ EOVersion; doc = ""; }; +flag = { + name = batch-size; + arg-type = number; + arg-range = "1->4096"; + descrip = "The maximum number of packets that can be submitted to the Tx ring at once"; + arg-default = 25; + doc = ""; +}; + flag = { name = less-help; value = "h"; From 00e9f83646dc2e85ad075d8f62dad096bddf529e Mon Sep 17 00:00:00 2001 From: bplangar Date: Sun, 23 Apr 2023 00:25:05 +0200 Subject: [PATCH 04/69] Read pcap files with nanosec precision, set nano_sleep as default sleep method --- src/common/timer.c | 2 +- src/defines.h.in | 7 +++++++ src/replay.c | 18 +++++++++--------- src/send_packets.c | 32 ++++++++++++++++---------------- src/sleep.h | 2 +- 5 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/common/timer.c b/src/common/timer.c index 5b62e724..23b6ee0e 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -92,7 +92,7 @@ init_timestamp(struct timespec *timestamp) } int get_current_time(struct timespec *ts){ - #ifdef _POSIX_C_SOURCE >= 199309L + #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L return clock_gettime(CLOCK_REALTIME, ts); #else struct timeval tv; diff --git a/src/defines.h.in b/src/defines.h.in index 20e41a70..d0267df5 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -358,6 +358,13 @@ typedef u_int32_t uint32_t (a)->tv_nsec = (b)->tv_nsec; \ } while(0) +/* libpcap puts nanosec values in tv_usec when pcap file is read with nanosec precision*/ +#define TIMEVAL_AS_TIMESPEC_SET(a, b) \ + do { \ + (a)->tv_sec = (b)->tv_sec; \ + (a)->tv_nsec = (b)->tv_usec; \ + } while(0) + /* * Help suppress some compiler warnings * No problem if variable is actually used diff --git a/src/replay.c b/src/replay.c index b3334ae6..e7cf3d76 100644 --- a/src/replay.c +++ b/src/replay.c @@ -125,7 +125,7 @@ replay_file(tcpreplay_t *ctx, int idx) /* read from pcap file if we haven't cached things yet */ if (!ctx->options->preload_pcap) { - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -140,10 +140,10 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); } } @@ -152,10 +152,10 @@ replay_file(tcpreplay_t *ctx, int idx) if (ctx->options->verbose) { /* in cache mode, we may not have opened the file */ if (pcap == NULL) - if ((pcap = pcap_open_offline(path, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); /* init tcpdump */ diff --git a/src/send_packets.c b/src/send_packets.c index 2314e029..dde51022 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -316,8 +316,7 @@ static void increment_iteration(tcpreplay_t *ctx) void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) { - struct timeval last_pkt_ts; - struct timespec now, print_delta; + struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -350,7 +349,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) } ctx->skip_packets = 0; - timerclear(&last_pkt_ts); + timesclear(&last_pkt_ts); if (options->limit_time > 0) end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); @@ -369,7 +368,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) */ while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { - + TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -429,20 +428,20 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * time stamping is expensive, but now is the * time to do it. */ - dbgx(4, "This packet time: " TIMEVAL_FORMAT, pkthdr.ts.tv_sec, - pkthdr.ts.tv_usec); + dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, + pkthdr_ts.tv_nsec); skip_length = 0; ctx->skip_packets = 0; if (options->speed.mode == speed_multiplier) { - if (!timerisset(&last_pkt_ts)) { - TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); - } else if (timercmp(&pkthdr.ts, &last_pkt_ts, >)) { - struct timeval delta; - - timersub(&pkthdr.ts, &last_pkt_ts, &delta); - timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); - TIMEVAL_SET(&last_pkt_ts, &pkthdr.ts); + if (!timesisset(&last_pkt_ts)) { + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); + } else if (timescmp(&pkthdr_ts, &last_pkt_ts, >)) { + struct timespec delta; + + timessub(&pkthdr_ts, &last_pkt_ts, &delta); + timeradd_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); } } @@ -999,7 +998,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); - TIMESPEC_SET(&nap_for, &ctx->nap); + TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1117,8 +1116,9 @@ static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)) + if (!timesisset(nap_this_time)){ return; + } /* do we need to limit the total time we sleep? */ if (timesisset(&(options->maxsleep)) && (timescmp(nap_this_time, &(options->maxsleep), >))) { diff --git a/src/sleep.h b/src/sleep.h index 20eee331..0ba9b878 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -57,9 +57,9 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - #ifdef _POSIX_C_SOURCE >= 200112L struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); + #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); From a044d72aa58c596aaa00ab95517d25d8b9c1f03a Mon Sep 17 00:00:00 2001 From: bplangar Date: Tue, 2 May 2023 21:12:35 +0200 Subject: [PATCH 05/69] fix gettimeofday_sleep function, add nap to now variable to get sleep_until --- src/sleep.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sleep.h b/src/sleep.h index 0ba9b878..dd22f320 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -84,8 +84,7 @@ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until, nap_for; - init_timestamp(&nap_for); + struct timespec sleep_until; #ifdef HAVE_NETMAP struct timespec last; uint32_t i = 0; @@ -93,8 +92,7 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - timeradd_timespec(now, &nap_for, &sleep_until); - + timeradd_timespec(now, nap, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP if (flush && timescmp(now, &last, !=)) { From 77a14488621028a38a536d04733105f9965730d6 Mon Sep 17 00:00:00 2001 From: bplangar Date: Tue, 2 May 2023 22:05:12 +0200 Subject: [PATCH 06/69] use CLOCK_MONOTONIC in clock_gettime function, modify gettimeofday_sleep to work with CLOCK_MONOTONIC --- src/common/timer.c | 2 +- src/sleep.h | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/common/timer.c b/src/common/timer.c index 23b6ee0e..18d589bb 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -93,7 +93,7 @@ init_timestamp(struct timespec *timestamp) int get_current_time(struct timespec *ts){ #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L - return clock_gettime(CLOCK_REALTIME, ts); + return clock_gettime(CLOCK_MONOTONIC, ts); #else struct timeval tv; int success = gettimeofday(&tv, NULL); diff --git a/src/sleep.h b/src/sleep.h index dd22f320..71ce39ab 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -84,35 +84,34 @@ static inline void gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until; + struct timeval now_ms, sleep_until, nap_for, last; + TIMESPEC_TO_TIMEVAL(&nap_for, nap); + gettimeofday(&now_ms, NULL); #ifdef HAVE_NETMAP - struct timespec last; uint32_t i = 0; - - TIMESPEC_SET(&last, now); + TIMEVAL_SET(&last, &now_ms); #endif /* HAVE_NETMAP */ - - timeradd_timespec(now, nap, &sleep_until); + + timeradd(&now_ms, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timescmp(now, &last, !=)) { - TIMESPEC_SET(&last, now); + if (flush && timercmp(&now_ms, &last, !=)) { + TIMESPEC_SET(&last, &now_ms); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timescmp(now, &sleep_until, >=)) + if (timercmp(&now_ms, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - struct timeval now_ms; gettimeofday(&now_ms, NULL); - TIMEVAL_TO_TIMESPEC(&now_ms, now); } + get_current_time(now); } #ifdef HAVE_SELECT From 6085e4f2a7dd77a8b96aff2912e2a43ef52e2760 Mon Sep 17 00:00:00 2001 From: bplangar Date: Wed, 3 May 2023 00:16:51 +0200 Subject: [PATCH 07/69] fix time related bug in send_dual_packets function --- src/send_packets.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/send_packets.c b/src/send_packets.c index dde51022..437a5bec 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -559,8 +559,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) void send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t *pcap2, int cache_file_idx2) { - struct timeval last_pkt_ts; - struct timespec now, print_delta; + struct timespec now, print_delta, last_pkt_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -591,7 +590,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * } ctx->skip_packets = 0; - timerclear(&last_pkt_ts); + timesclear(&last_pkt_ts); if (options->limit_time > 0) end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); @@ -707,14 +706,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; if (options->speed.mode == speed_multiplier) { - if (!timerisset(&last_pkt_ts)) { - TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); - } else if (timercmp(&pkthdr_ptr->ts, &last_pkt_ts, >)) { - struct timeval delta; - - timersub(&pkthdr_ptr->ts, &last_pkt_ts, &delta); - timeradd_timeval_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); - TIMEVAL_SET(&last_pkt_ts, &pkthdr_ptr->ts); + struct timespec pkthdr_ts; + TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &pkthdr_ts); + if (!timesisset(&last_pkt_ts)) { + TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &last_pkt_ts); + } else if (timescmp(&pkthdr_ts, &last_pkt_ts, >)) { + struct timespec delta; + + timessub(&pkthdr_ts, &last_pkt_ts, &delta); + timeradd_timespec(&stats->pkt_ts_delta, &delta, &stats->pkt_ts_delta); + TIMESPEC_SET(&last_pkt_ts, &pkthdr_ts); } if (!timesisset(&stats->time_delta)) @@ -998,6 +999,12 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); + // printf("pkt_ts_delta sec: %lu\n", pkt_ts_delta->tv_sec); + // printf("pkt_ts_delta nsec: %lu\n", pkt_ts_delta->tv_nsec); + // printf("time_delta sec: %lu\n", time_delta->tv_sec); + // printf("time_delta nsec: %lu\n", time_delta->tv_nsec); + // printf("nap_for sec: %lu\n", nap_for.tv_sec); + // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); From d5229438cd7ce3b67f96a6a4a1ce8ee1e17aced7 Mon Sep 17 00:00:00 2001 From: bplangar Date: Mon, 8 May 2023 11:53:53 +0200 Subject: [PATCH 08/69] Fix loop feature for AF_XDP packet sending --- src/common/sendpacket.c | 1 + src/common/sendpacket.h | 1 + src/tcpreplay_api.c | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index d7c12e53..abb1acd0 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -1430,6 +1430,7 @@ static sendpacket_t * sendpacket_open_xsk(const char *device, char *errbuf){ sp->xsk_info = xsk_info; sp->umem_info = umem_info; sp->frame_size = frame_size; + sp->tx_size = nb_of_tx_queue_desc; return sp; } diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index f13762ef..c94fb181 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -217,6 +217,7 @@ struct sendpacket_s { unsigned int pckt_count; int frame_size; int tx_idx; + int tx_size; #endif bool abort; }; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index fe876f88..98942773 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -1168,6 +1168,14 @@ tcpreplay_replay(tcpreplay_t *ctx) if (ctx->options->stats == 0) packet_stats(&ctx->stats); } + + #ifdef HAVE_LIBXDP + sendpacket_t* sp = ctx->intf1; + if(sp->handle_type == SP_TYPE_LIBXDP){ + sp->xsk_info->tx.cached_prod = 0; + sp->xsk_info->tx.cached_cons = sp->tx_size; + } + #endif } } else { while (!ctx->abort) { /* loop forever unless user aborts */ From 5bdbad30f864893e6e18eda0380d647470f487de Mon Sep 17 00:00:00 2001 From: bplangar Date: Mon, 8 May 2023 21:04:32 +0200 Subject: [PATCH 09/69] Check packet fits in umem frame before copying --- src/send_packets.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/send_packets.c b/src/send_packets.c index 044b2bd9..8fc71879 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -1238,7 +1238,16 @@ 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); + } +} + static inline 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); From ba8c8079f184a649d7757b2868c44c015a1b2db6 Mon Sep 17 00:00:00 2001 From: bplangar Date: Thu, 11 May 2023 20:24:40 +0200 Subject: [PATCH 10/69] change CLOCK_REALTIME to CLOCK_MONOTONIC flag in clock_nanosleep --- src/sleep.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sleep.h b/src/sleep.h index 71ce39ab..bf2f4f4e 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -60,7 +60,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L - clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &sleep_until, NULL); + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); #endif From 5ac105dc4f2b16ad5f16311cc372855432b620a2 Mon Sep 17 00:00:00 2001 From: eplabal Date: Mon, 5 Jun 2023 15:17:16 +0200 Subject: [PATCH 11/69] Fix copy/paste error in configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 199171bc..344d6ecf 100644 --- a/configure.ac +++ b/configure.ac @@ -830,7 +830,7 @@ fi # libpcap can require libnl AC_SEARCH_LIBS([nl_handle_alloc], [nl], - [AC_MSG_NOTICE([Unable to find xdp library - may be needed by libpcap])]) + [AC_MSG_NOTICE([Unable to find nl library - may be needed by libpcap])]) AC_CHECK_LIB(bpf, bpf_object__open_file,, [AC_MSG_NOTICE([Unable to find libbpf library ])]) From ad31b98a03d34f87155f262fc91799b844dcc38b Mon Sep 17 00:00:00 2001 From: Bastian Triller Date: Fri, 20 May 2022 17:38:04 +0200 Subject: [PATCH 12/69] Add support for LINUX_SLL2 fixes #727 --- src/tcpedit/plugins/Makefile.am | 1 + src/tcpedit/plugins/dlt_linuxsll2/Makefile.am | 30 ++ src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c | 323 ++++++++++++++++++ src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h | 50 +++ .../plugins/dlt_linuxsll2/linuxsll2_opts.def | 1 + .../plugins/dlt_linuxsll2/linuxsll2_types.h | 72 ++++ src/tcpedit/plugins/dlt_plugins.c | 2 + src/tcpedit/plugins/dlt_stub.def | 1 + 8 files changed, 480 insertions(+) create mode 100644 src/tcpedit/plugins/dlt_linuxsll2/Makefile.am create mode 100644 src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c create mode 100644 src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h create mode 100644 src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_opts.def create mode 100644 src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h diff --git a/src/tcpedit/plugins/Makefile.am b/src/tcpedit/plugins/Makefile.am index c99e2741..9b6dd40c 100644 --- a/src/tcpedit/plugins/Makefile.am +++ b/src/tcpedit/plugins/Makefile.am @@ -42,6 +42,7 @@ include %reldir%/dlt_raw/Makefile.am include %reldir%/dlt_null/Makefile.am include %reldir%/dlt_loop/Makefile.am include %reldir%/dlt_linuxsll/Makefile.am +include %reldir%/dlt_linuxsll2/Makefile.am include %reldir%/dlt_ieee80211/Makefile.am include %reldir%/dlt_radiotap/Makefile.am include %reldir%/dlt_jnpr_ether/Makefile.am diff --git a/src/tcpedit/plugins/dlt_linuxsll2/Makefile.am b/src/tcpedit/plugins/dlt_linuxsll2/Makefile.am new file mode 100644 index 00000000..6ec1fbbc --- /dev/null +++ b/src/tcpedit/plugins/dlt_linuxsll2/Makefile.am @@ -0,0 +1,30 @@ +# $Id:$ +# START OF: dlt_linuxsll2 +# Note, if you add any files to your plugin, you will need to edit dlt_/Makefile.am +# add your .c files to libtcpedit_a_SOURCES +# add your .h files to noinst_HEADERS +# add any other files (like documentation, notes, etc) to EXTRA_DIST +# add your dependency information (see comment below) + +libtcpedit_a_SOURCES += %reldir%/linuxsll2.c + +noinst_HEADERS += \ + %reldir%/linuxsll2.h \ + %reldir%/linuxsll2_types.h + +EXTRA_DIST += %reldir%/linuxsll2_opts.def + +# dependencies for your plugin source code. Edit as necessary +linuxsll2.c: \ + $(TCPEDIT_PLUGINS_DEPS) \ + %reldir%/../../tcpedit_api.h \ + %reldir%/linuxsll2.h \ + %reldir%/linuxsll2_types.h + +# You probably don't want to touch anything below this line until the end of the plugin + +DLT_STUB_DEPS += %reldir%/linuxsll2_opts.def + +MOSTLYCLEANFILES += *~ + +# END OF: dlt_linuxsll2 diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c new file mode 100644 index 00000000..828cb041 --- /dev/null +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c @@ -0,0 +1,323 @@ +/* $Id$ */ + +/* + * Copyright (c) 2001-2010 Aaron Turner + * Copyright (c) 2013-2023 Fred Klassen - 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 + * published by the Free Software Foundation, either version 3 of the + * License, or with the authors permission any later version. + * + * The Tcpreplay Suite is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Tcpreplay Suite. If not, see . + */ + +#include +#include + +#include "tcpedit.h" +#include "common.h" +#include "tcpr.h" +#include "dlt_utils.h" +#include "tcpedit_stub.h" +#include "../ethernet.h" +#include "linuxsll2.h" + + +static char dlt_name[] = "linuxsll2"; +static char _U_ dlt_prefix[] = "linuxsll2"; +static uint16_t dlt_value = DLT_LINUX_SLL2; + +/* + * Function to register ourselves. This function is always called, regardless + * of what DLT types are being used, so it shouldn't be allocating extra buffers + * or anything like that (use the dlt_linuxsll2_init() function below for that). + * Tasks: + * - Create a new plugin struct + * - Fill out the provides/requires bit masks. Note: Only specify which fields are + * actually in the header. + * - Add the plugin to the context's plugin chain + * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN + */ +int +dlt_linuxsll2_register(tcpeditdlt_t *ctx) +{ + tcpeditdlt_plugin_t *plugin; + assert(ctx); + + /* create a new plugin structure */ + plugin = tcpedit_dlt_newplugin(); + + /* FIXME: set what we provide & require */ + plugin->provides += PLUGIN_MASK_PROTO + PLUGIN_MASK_SRCADDR; + plugin->requires += 0; + + + /* what is our DLT value? */ + plugin->dlt = dlt_value; + + /* set the prefix name of our plugin. This is also used as the prefix for our options */ + plugin->name = safe_strdup(dlt_prefix); + + /* + * Point to our functions, note, you need a function for EVERY method. + * Even if it is only an empty stub returning success. + */ + plugin->plugin_init = dlt_linuxsll2_init; + plugin->plugin_cleanup = dlt_linuxsll2_cleanup; + plugin->plugin_parse_opts = dlt_linuxsll2_parse_opts; + plugin->plugin_decode = dlt_linuxsll2_decode; + plugin->plugin_encode = dlt_linuxsll2_encode; + plugin->plugin_proto = dlt_linuxsll2_proto; + plugin->plugin_l2addr_type = dlt_linuxsll2_l2addr_type; + plugin->plugin_l2len = dlt_linuxsll2_l2len; + plugin->plugin_get_layer3 = dlt_linuxsll2_get_layer3; + plugin->plugin_merge_layer3 = dlt_linuxsll2_merge_layer3; + plugin->plugin_get_mac = dlt_linuxsll2_get_mac; + + /* add it to the available plugin list */ + return tcpedit_dlt_addplugin(ctx, plugin); +} + + +/* + * Initializer function. This function is called only once, if and only if + * this plugin will be utilized. Remember, if you need to keep track of any state, + * store it in your plugin->config, not a global! + * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN + */ +int +dlt_linuxsll2_init(tcpeditdlt_t *ctx) +{ + tcpeditdlt_plugin_t *plugin; + assert(ctx); + + if ((plugin = tcpedit_dlt_getplugin(ctx, dlt_value)) == NULL) { + tcpedit_seterr(ctx->tcpedit, "Unable to initialize unregistered plugin %s", dlt_name); + return TCPEDIT_ERROR; + } + + /* allocate memory for our decode extra data */ + ctx->decoded_extra_size = sizeof(linuxsll2_extra_t); + ctx->decoded_extra = safe_malloc(ctx->decoded_extra_size); + + /* allocate memory for our config data */ + plugin->config_size = sizeof(linuxsll2_config_t); + plugin->config = safe_malloc(plugin->config_size); + + return TCPEDIT_OK; /* success */ +} + +/* + * Since this is used in a library, we should manually clean up after ourselves + * Unless you allocated some memory in dlt_linuxsll2_init(), this is just an stub. + * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN + */ +int +dlt_linuxsll2_cleanup(tcpeditdlt_t *ctx) +{ + tcpeditdlt_plugin_t *plugin; + assert(ctx); + + if ((plugin = tcpedit_dlt_getplugin(ctx, dlt_value)) == NULL) { + tcpedit_seterr(ctx->tcpedit, "Unable to cleanup unregistered plugin %s", dlt_name); + return TCPEDIT_ERROR; + } + + /* FIXME: make this function do something if necessary */ + if (ctx->decoded_extra != NULL) { + safe_free(ctx->decoded_extra); + ctx->decoded_extra = NULL; + ctx->decoded_extra_size = 0; + } + + if (plugin->config != NULL) { + safe_free(plugin->config); + plugin->config = NULL; + plugin->config_size = 0; + } + + return TCPEDIT_OK; /* success */ +} + +/* + * This is where you should define all your AutoGen AutoOpts option parsing. + * Any user specified option should have it's bit turned on in the 'provides' + * bit mask. + * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN + */ +int +dlt_linuxsll2_parse_opts(tcpeditdlt_t *ctx) +{ + assert(ctx); + + /* nothing to parse */ + return TCPEDIT_OK; /* success */ +} + +/* + * Function to decode the layer 2 header in the packet. + * You need to fill out: + * - ctx->l2len + * - ctx->srcaddr + * - ctx->dstaddr + * - ctx->proto + * - ctx->decoded_extra + * Returns: TCPEDIT_ERROR | TCPEDIT_OK | TCPEDIT_WARN + */ +int +dlt_linuxsll2_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) +{ + int type; + linux_sll2_header_t *linux_sll2; + assert(ctx); + assert(packet); + + if (pktlen < (int)sizeof(linux_sll2_header_t)) + return TCPEDIT_ERROR; + + linux_sll2 = (linux_sll2_header_t *)packet; + ctx->proto = linux_sll2->proto; + ctx->l2len = sizeof(linux_sll2_header_t); + + + type = ntohs(linux_sll2->type); + if (type == ARPHRD_ETHER || type == ARPHRD_LOOPBACK) { /* ethernet or loopback */ + memcpy(&(ctx->srcaddr), linux_sll2->address, ETHER_ADDR_LEN); + } else { + tcpedit_seterr(ctx->tcpedit, "%s", "DLT_LINUX_SLL2 pcap's must contain only ethernet or loopback packets"); + return TCPEDIT_ERROR; + } + + return TCPEDIT_OK; /* success */ +} + +/* + * Function to encode the layer 2 header back into the packet. + * Returns: total packet len or TCPEDIT_ERROR + */ +int +dlt_linuxsll2_encode(tcpeditdlt_t *ctx, u_char *packet, _U_ int pktlen, + _U_ tcpr_dir_t dir) +{ + assert(ctx); + assert(packet); + + tcpedit_seterr(ctx->tcpedit, "%s", "DLT_LINUX_SLL2 plugin does not support packet encoding"); + return TCPEDIT_ERROR; +} + +/* + * Function returns the Layer 3 protocol type of the given packet, or TCPEDIT_ERROR on error + */ +int +dlt_linuxsll2_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) +{ + linux_sll2_header_t *linux_sll2; + assert(ctx); + assert(packet); + + if (pktlen < (int)sizeof(linux_sll2_header_t)) + return TCPEDIT_ERROR; + + linux_sll2 = (linux_sll2_header_t *)packet; + + return linux_sll2->proto; +} + +/* + * Function returns a pointer to the layer 3 protocol header or NULL on error + */ +u_char * +dlt_linuxsll2_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen) +{ + int l2len; + assert(ctx); + assert(packet); + + l2len = dlt_linuxsll2_l2len(ctx, packet, pktlen); + if (l2len == -1 || pktlen < l2len) + return NULL; + + return tcpedit_dlt_l3data_copy(ctx, packet, pktlen, l2len); +} + +/* + * function merges the packet (containing L2 and old L3) with the l3data buffer + * containing the new l3 data. Note, if L2 % 4 == 0, then they're pointing to the + * same buffer, otherwise there was a memcpy involved on strictly aligned architectures + * like SPARC + */ +u_char * +dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data) +{ + int l2len; + assert(ctx); + assert(packet); + assert(l3data); + + l2len = dlt_linuxsll2_l2len(ctx, packet, pktlen); + if (l2len == -1 || pktlen < l2len) + return NULL; + + return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, l3data, l2len); +} + +/* + * return the length of the L2 header of the current packet + */ +int +dlt_linuxsll2_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) +{ + assert(ctx); + assert(packet); + + if (pktlen < (int)sizeof(linux_sll2_header_t)) + return -1; + + return sizeof(linux_sll2_header_t); +} + +/* + * return a static pointer to the source/destination MAC address + * return NULL on error/address doesn't exist + */ +u_char * +dlt_linuxsll2_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char *packet, const int pktlen) +{ + assert(ctx); + assert(packet); + + if (pktlen < 14) + return NULL; + + /* FIXME: return a ptr to the source or dest mac address. */ + switch(mac) { + case SRC_MAC: + memcpy(ctx->srcmac, &packet[6], 8); /* linuxssl defines the src mac field to be 8 bytes, not 6 */ + return(ctx->srcmac); + break; + + case DST_MAC: + return(NULL); + break; + + default: + errx(-1, "Invalid tcpeditdlt_mac_type_t: %d", mac); + } + return(NULL); +} + +tcpeditdlt_l2addr_type_t +dlt_linuxsll2_l2addr_type(void) +{ + /* we only support ethernet packets */ + return ETHERNET; +} + diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h new file mode 100644 index 00000000..7c3b7add --- /dev/null +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h @@ -0,0 +1,50 @@ +/* $Id$ */ + +/* + * Copyright (c) 2001-2010 Aaron Turner + * Copyright (c) 2013-2023 Fred Klassen - 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 + * published by the Free Software Foundation, either version 3 of the + * License, or with the authors permission any later version. + * + * The Tcpreplay Suite is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Tcpreplay Suite. If not, see . + */ + +#ifndef _DLT_linuxsll2_H_ +#define _DLT_linuxsll2_H_ + +#include "plugins_types.h" +#include "linuxsll2_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +int dlt_linuxsll2_register(tcpeditdlt_t *ctx); +int dlt_linuxsll2_init(tcpeditdlt_t *ctx); +int dlt_linuxsll2_cleanup(tcpeditdlt_t *ctx); +int dlt_linuxsll2_parse_opts(tcpeditdlt_t *ctx); +int dlt_linuxsll2_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen); +int dlt_linuxsll2_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir); +int dlt_linuxsll2_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen); +u_char *dlt_linuxsll2_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen); +u_char *dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data); +tcpeditdlt_l2addr_type_t dlt_linuxsll2_l2addr_type(void); +int dlt_linuxsll2_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen); +u_char *dlt_linuxsll2_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char *packet, const int pktlen); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_opts.def b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_opts.def new file mode 100644 index 00000000..07c11b5f --- /dev/null +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_opts.def @@ -0,0 +1 @@ +/* no options for DLT_LINUX_SLL2 */ diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h new file mode 100644 index 00000000..06614329 --- /dev/null +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h @@ -0,0 +1,72 @@ +/* $Id$ */ + +/* + * Copyright (c) 2001-2010 Aaron Turner + * Copyright (c) 2013-2023 Fred Klassen - 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 + * published by the Free Software Foundation, either version 3 of the + * License, or with the authors permission any later version. + * + * The Tcpreplay Suite is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Tcpreplay Suite. If not, see . + */ + +#ifndef _DLT_linuxsll2_TYPES_H_ +#define _DLT_linuxsll2_TYPES_H_ + +#include "tcpedit_types.h" +#include "plugins_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * structure to hold any data parsed from the packet by the decoder. + * Example: Ethernet VLAN tag info + */ +typedef struct { + u_char packet[MAXPACKET]; +} linuxsll2_extra_t; + + +/* + * FIXME: structure to hold any data in the tcpeditdlt_plugin_t->config + * Things like: + * - Parsed user options + * - State between packets + * - Note, you should only use this for the encoder function, decoder functions should place + * "extra" data parsed from the packet in the tcpeditdlt_t->decoded_extra buffer since that + * is available to any encoder plugin. + */ +typedef struct { + /* dummy entry for SunPro compiler which doesn't like empty structs */ + int dummy; +} linuxsll2_config_t; + + +typedef struct { + u_int16_t proto; /* Ethernet protocol type */ + u_int16_t mbz; /* reserved */ + u_int32_t iindex; /* iface index */ + u_int16_t type; /* linux ARPHRD_* values for link-layer device type. See: + * http://www.gelato.unsw.edu.au/lxr/source/include/linux/if_arp.h + */ + u_int8_t source; /* values 0-4 determine where the packet came and where it's going */ + u_int8_t length; /* source address length */ + u_char address[8]; /* first 8 bytes of source address (may be truncated) */ +} linux_sll2_header_t; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/tcpedit/plugins/dlt_plugins.c b/src/tcpedit/plugins/dlt_plugins.c index 51585378..70885309 100644 --- a/src/tcpedit/plugins/dlt_plugins.c +++ b/src/tcpedit/plugins/dlt_plugins.c @@ -33,6 +33,7 @@ #include "dlt_ieee80211/ieee80211.h" #include "dlt_jnpr_ether/jnpr_ether.h" #include "dlt_linuxsll/linuxsll.h" +#include "dlt_linuxsll2/linuxsll2.h" #include "dlt_loop/loop.h" #include "dlt_null/null.h" #include "dlt_pppserial/pppserial.h" @@ -57,6 +58,7 @@ tcpedit_dlt_register(tcpeditdlt_t *ctx) retcode += dlt_null_register(ctx); retcode += dlt_loop_register(ctx); retcode += dlt_linuxsll_register(ctx); + retcode += dlt_linuxsll2_register(ctx); retcode += dlt_ieee80211_register(ctx); retcode += dlt_radiotap_register(ctx); retcode += dlt_jnpr_ether_register(ctx); diff --git a/src/tcpedit/plugins/dlt_stub.def b/src/tcpedit/plugins/dlt_stub.def index 1c948898..10af36f0 100644 --- a/src/tcpedit/plugins/dlt_stub.def +++ b/src/tcpedit/plugins/dlt_stub.def @@ -8,6 +8,7 @@ #include dlt_null/null_opts.def #include dlt_loop/loop_opts.def #include dlt_linuxsll/linuxsll_opts.def +#include dlt_linuxsll2/linuxsll2_opts.def #include dlt_ieee80211/ieee80211_opts.def #include dlt_radiotap/radiotap_opts.def #include dlt_jnpr_ether/jnpr_ether_opts.def From 5f8c78362b3b1e06f5adff2d4b140509c4799894 Mon Sep 17 00:00:00 2001 From: Martin Jansa Date: Sun, 3 Sep 2023 12:31:59 +0200 Subject: [PATCH 13/69] configure.ac: unify search dirs for pcap and add lib32 * add lib32 because when building lib32-tcpreplay it's impossible to set --with-libpcap so that it would find both include files as well as the library in lib32 directory * maybe it would be beneficial to split --with-libpcap into --with-libpcap-includedir --with-libpcap-libdir as this already searches in the --with-libpcap value with and without any "lib" prefix, but include files always expect "include" dir there * most of this code was added in: https://github.com/appneta/tcpreplay/commit/202b8e82f9fd3c84ce5804577caeb36a33baabe7#diff-49473dca262eeab3b4a43002adb08b4db31020d190caaad1594b47f1d5daa810R570 * then search for ${host_cpu} lib/${host_cpu} (without -${host_os} suffix) and ${build_arch}-${host_os} lib/${build_arch}-${host_os} was added, but only for search of dynamic library in: https://github.com/appneta/tcpreplay/commit/c3d5236563985a99f8bb02c3f1bd6950e3929047 * ${build_arch}-${host_os} lib/${build_arch}-${host_os} was later replaced with: lib/${MULTIARCH} ${MULTIARCH} and it was added to static library search as well but for dynamic library it was searching in reversed order: ${MULTIARCH} lib/${MULTIARCH} https://github.com/appneta/tcpreplay/commit/ed9e3a818bde04813144014561e62f018c9eb85f I don't think this reversed order was intentional, just unify all 4 cases to use the same directories in the same order Signed-off-by: Martin Jansa --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 387219de..26ba31a5 100644 --- a/configure.ac +++ b/configure.ac @@ -671,7 +671,7 @@ AC_ARG_WITH(libpcap, LPCAPINCDIR=${testdir} if test $dynamic_link = yes; then for ext in .dylib .so .tbd ; do - for dir in . lib lib64 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do + for dir in . lib lib64 lib32 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do sharefile=$(ls ${testdir}/$dir/libpcap${ext}* 2> /dev/null | sort | head -n1) if test -n "${sharefile}"; then LPCAP_LD_LIBRARY_PATH="$(dirname ${sharefile})" @@ -690,7 +690,7 @@ AC_ARG_WITH(libpcap, dnl If dynamic library not found, try static dnl for ext in ${libext} .a .A.tbd ; do - for dir in . lib lib64 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do + for dir in . lib lib64 lib32 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do staticfile=$(ls ${testdir}/$dir/libpcap${ext} 2> /dev/null | sort | head -n1) if test -n "${staticfile}"; then LPCAPLIB="${staticfile}" @@ -771,7 +771,7 @@ AC_ARG_WITH(libpcap, LPCAPINCDIR="${testdir}/include" if test $dynamic_link = yes; then for ext in .dylib .so .tbd; do - for dir in . lib lib64 ${host_cpu} lib/${host_cpu} ${host_cpu}-${host_os} lib/${host_cpu}-${host_os} ${MULTIARCH} lib/${MULTIARCH}; do + for dir in . lib lib64 lib32 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do sharefile=$(ls "${testdir}/$dir/libpcap${ext}" 2> /dev/null | sort | head -n1) if test -n "${sharefile}"; then LPCAPLIB="-L$(dirname ${sharefile}) -lpcap" @@ -790,7 +790,7 @@ AC_ARG_WITH(libpcap, dnl If dynamic library not found, try static dnl for ext in ${libext} .a .A.tbd ; do - for dir in . lib lib64 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do + for dir in . lib lib64 lib32 lib/${host_cpu}-${host_os} ${host_cpu}-${host_os} lib/${MULTIARCH} ${MULTIARCH}; do staticfile=$(ls "${testdir}/$dir/libpcap${ext}" 2> /dev/null | sort | head -n1) if test -n "${staticfile}"; then LPCAPLIB="${staticfile}" From 2f83213b1858647885fa53c34e601348bd5961ad Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 3 Sep 2023 09:05:05 -0700 Subject: [PATCH 14/69] 4.5.0-beta1 bump version --- configure.ac | 2 +- docs/CHANGELOG | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 387219de..b410e541 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl $Id$ AC_PREREQ([2.69]) dnl Set version info here! -AC_INIT([tcpreplay],[4.4.4],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/]) +AC_INIT([tcpreplay],[4.5.0-beta1],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/]) AC_CONFIG_SRCDIR([src/tcpreplay.c]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_AUX_DIR(config) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ca8129e8..571c4846 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,3 +1,5 @@ +09/03/2023 Version 4.5.0-beta1 + 06/04/2023 Version 4.4.4 - overflow check fix for parse_mpls (#795) - tcpreplay-edit: prevent L2 flooding of ipv6 unicast packets (#793) From 4def4c766c2516ed91eb6ed2d29b2de9cd0df66b Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 3 Sep 2023 14:02:46 -0700 Subject: [PATCH 15/69] Feature #727 - Linux SLL version 2 updates PR #728 cleanup and completion of SLL v2 implementation --- configure.ac | 9 ++ docs/CHANGELOG | 1 + docs/CREDIT | 3 + lib/sll.h | 110 +++++++++++------- src/common/dlt_names.c | 22 ++++ src/common/dlt_names.h | 4 + src/common/fakepcap.h | 4 + src/common/get.c | 10 +- src/defines.h.in | 1 + src/tcpedit/dlt.c | 15 ++- src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c | 31 ++--- src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h | 6 +- .../plugins/dlt_linuxsll2/linuxsll2_types.h | 2 +- src/tcpprep.c | 1 + src/tcprewrite_opts.def | 2 + 15 files changed, 154 insertions(+), 67 deletions(-) diff --git a/configure.ac b/configure.ac index b410e541..cdc1a9d3 100644 --- a/configure.ac +++ b/configure.ac @@ -1525,6 +1525,15 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "$LPCAPINC"]], [[ int foo; ],[AC_MSG_RESULT(no) ]) +AC_MSG_CHECKING(for DLT_LINUX_SLL2 in libpcap) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "$LPCAPINC"]], [[ int foo; + foo = DLT_LINUX_SLL2 + ]])],[ AC_DEFINE([HAVE_DLT_LINUX_SLL2], [1], + [Does pcap.h include a header with DLT_LINUX_SLL2?]) + AC_MSG_RESULT(yes) + ],[AC_MSG_RESULT(no) + ]) + AC_MSG_CHECKING(for DLT_C_HDLC in libpcap) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include "$LPCAPINC"]], [[ int foo; foo = DLT_C_HDLC ]])],[ AC_DEFINE([HAVE_DLT_C_HDLC], [1], diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 571c4846..f2d997fe 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 09/03/2023 Version 4.5.0-beta1 + - create DLT_LINUX_SLL2 plugin (#727) 06/04/2023 Version 4.4.4 - overflow check fix for parse_mpls (#795) diff --git a/docs/CREDIT b/docs/CREDIT index 467e076c..38e200f6 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -113,3 +113,6 @@ Florian Weimer David Guti - prevent L2 flooding of ipv6 unicast packets for tcpreplay-edit + +Bastian Triller + - Linux SLL2 diff --git a/lib/sll.h b/lib/sll.h index 4f278792..14966546 100644 --- a/lib/sll.h +++ b/lib/sll.h @@ -1,10 +1,10 @@ /*- * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 - * The Regents of the University of California. All rights reserved. + * The Regents of the University of California. All rights reserved. * * This code is derived from the Stanford/CMU enet packet filter, * (net/enet.c) distributed as part of 4.3BSD, and code contributed - * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence * Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without @@ -42,30 +42,30 @@ * For captures on Linux cooked sockets, we construct a fake header * that includes: * - * a 2-byte "packet type" which is one of: + * a 2-byte "packet type" which is one of: * - * LINUX_SLL_HOST packet was sent to us - * LINUX_SLL_BROADCAST packet was broadcast - * LINUX_SLL_MULTICAST packet was multicast - * LINUX_SLL_OTHERHOST packet was sent to somebody else - * LINUX_SLL_OUTGOING packet was sent *by* us; + * LINUX_SLL_HOST packet was sent to us + * LINUX_SLL_BROADCAST packet was broadcast + * LINUX_SLL_MULTICAST packet was multicast + * LINUX_SLL_OTHERHOST packet was sent to somebody else + * LINUX_SLL_OUTGOING packet was sent *by* us; * - * a 2-byte Ethernet protocol field; + * a 2-byte Ethernet protocol field; * - * a 2-byte link-layer type; + * a 2-byte link-layer type; * - * a 2-byte link-layer address length; + * a 2-byte link-layer address length; * - * an 8-byte source link-layer address, whose actual length is - * specified by the previous value. + * an 8-byte source link-layer address, whose actual length is + * specified by the previous value. * * All fields except for the link-layer address are in network byte order. * * DO NOT change the layout of this structure, or change any of the * LINUX_SLL_ values below. If you must change the link-layer header * for a "cooked" Linux capture, introduce a new DLT_ type (ask - * "tcpdump-workers@tcpdump.org" for one, so that you don't give it a - * value that collides with a value already being used), and use the + * "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it + * a value that collides with a value already being used), and use the * new header in captures of that type, so that programs that can * handle DLT_LINUX_SLL captures will continue to handle them correctly * without any change, and so that capture files with different headers @@ -77,54 +77,76 @@ #ifndef _SLL_H_ #define _SLL_H_ +#include + /* * A DLT_LINUX_SLL fake link-layer header. */ -#define SLL_HDR_LEN 16 /* total header length */ -#define SLL_ADDRLEN 8 /* length of address field */ +#define SLL_HDR_LEN 16 /* total header length */ +#define SLL_ADDRLEN 8 /* length of address field */ struct sll_header { - u_int16_t sll_pkttype; /* packet type */ - u_int16_t sll_hatype; /* link-layer address type */ - u_int16_t sll_halen; /* link-layer address length */ + u_int16_t sll_pkttype; /* packet type */ + u_int16_t sll_hatype; /* link-layer address type */ + u_int16_t sll_halen; /* link-layer address length */ u_int8_t sll_addr[SLL_ADDRLEN]; /* link-layer address */ - u_int16_t sll_protocol; /* protocol */ + u_int16_t sll_protocol; /* protocol */ +}; + +/* + * A DLT_LINUX_SLL2 fake link-layer header. + */ +#define SLL2_HDR_LEN 20 /* total header length */ + +struct sll2_header { + u_int16_t sll2_protocol; /* protocol */ + u_int16_t sll2_reserved_mbz; /* reserved - must be zero */ + u_int32_t sll2_if_index; /* 1-based interface index */ + u_int16_t sll2_hatype; /* link-layer address type */ + u_int8_t sll2_pkttype; /* packet type */ + u_int8_t sll2_halen; /* link-layer address length */ + u_int8_t sll2_addr[SLL_ADDRLEN]; /* link-layer address */ }; /* - * The LINUX_SLL_ values for "sll_pkttype"; these correspond to the - * PACKET_ values on Linux, but are defined here so that they're - * available even on systems other than Linux, and so that they - * don't change even if the PACKET_ values change. + * The LINUX_SLL_ values for "sll_pkttype" and LINUX_SLL2_ values for + * "sll2_pkttype"; these correspond to the PACKET_ values on Linux, + * which are defined by a header under include/uapi in the current + * kernel source, and are thus not going to change on Linux. We + * define them here so that they're available even on systems other + * than Linux. */ -#define LINUX_SLL_HOST 0 -#define LINUX_SLL_BROADCAST 1 -#define LINUX_SLL_MULTICAST 2 -#define LINUX_SLL_OTHERHOST 3 -#define LINUX_SLL_OUTGOING 4 +#define LINUX_SLL_HOST 0 +#define LINUX_SLL_BROADCAST 1 +#define LINUX_SLL_MULTICAST 2 +#define LINUX_SLL_OTHERHOST 3 +#define LINUX_SLL_OUTGOING 4 /* - * The LINUX_SLL_ values for "sll_protocol"; these correspond to the - * ETH_P_ values on Linux, but are defined here so that they're - * available even on systems other than Linux. We assume, for now, - * that the ETH_P_ values won't change in Linux; if they do, then: + * The LINUX_SLL_ values for "sll_protocol" and LINUX_SLL2_ values for + * "sll2_protocol"; these correspond to the ETH_P_ values on Linux, but + * are defined here so that they're available even on systems other than + * Linux. We assume, for now, that the ETH_P_ values won't change in + * Linux; if they do, then: * - * if we don't translate them in "pcap-linux.c", capture files - * won't necessarily be readable if captured on a system that - * defines ETH_P_ values that don't match these values; + * if we don't translate them in "pcap-linux.c", capture files + * won't necessarily be readable if captured on a system that + * defines ETH_P_ values that don't match these values; * - * if we do translate them in "pcap-linux.c", that makes life - * unpleasant for the BPF code generator, as the values you test - * for in the kernel aren't the values that you test for when - * reading a capture file, so the fixup code run on BPF programs - * handed to the kernel ends up having to do more work. + * if we do translate them in "pcap-linux.c", that makes life + * unpleasant for the BPF code generator, as the values you test + * for in the kernel aren't the values that you test for when + * reading a capture file, so the fixup code run on BPF programs + * handed to the kernel ends up having to do more work. * * Add other values here as necessary, for handling packet types that * might show up on non-Ethernet, non-802.x networks. (Not all the ones * in the Linux "if_ether.h" will, I suspect, actually show up in * captures.) */ -#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ -#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ +#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */ +#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */ +#define LINUX_SLL_P_CAN 0x000C /* CAN frames, with SocketCAN pseudo-headers */ +#define LINUX_SLL_P_CANFD 0x000D /* CAN FD frames, with SocketCAN pseudo-headers */ #endif diff --git a/src/common/dlt_names.c b/src/common/dlt_names.c index de694f20..d0b60ecf 100644 --- a/src/common/dlt_names.c +++ b/src/common/dlt_names.c @@ -506,6 +506,28 @@ char *dlt2name[] = { "Unknown", "Unknown", "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "Unknown", + "DLT_LINUX_SLL2", NULL }; diff --git a/src/common/dlt_names.h b/src/common/dlt_names.h index dfd43794..3f3534a3 100644 --- a/src/common/dlt_names.h +++ b/src/common/dlt_names.h @@ -186,6 +186,10 @@ extern const char *dlt2name[]; #define DLT_LINUX_SLL 113 #endif +#ifndef DLT_LINUX_SLL2 +#define DLT_LINUX_SLL2 276 +#endif + #ifndef DLT_LTALK #define DLT_LTALK 114 #endif diff --git a/src/common/fakepcap.h b/src/common/fakepcap.h index fd0efd90..cc269d4d 100644 --- a/src/common/fakepcap.h +++ b/src/common/fakepcap.h @@ -29,6 +29,10 @@ #define DLT_LINUX_SLL 113 #endif +#ifndef HAVE_DLT_LINUX_SLL2 +#define DLT_LINUX_SLL2 276 +#endif + #ifndef HAVE_DLT_C_HDLC #define DLT_C_HDLC 104 #endif diff --git a/src/common/get.c b/src/common/get.c index 2d911160..e77f358a 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -357,10 +357,18 @@ get_l2len_protocol(const u_char *pktdata, if (datalen < SLL_HDR_LEN) return -1; + *l2len = SLL_HDR_LEN; sll_hdr_t *sll_hdr = (sll_hdr_t *)pktdata; - *l2len = sizeof(*sll_hdr); *protocol = ntohs(sll_hdr->sll_protocol); break; + case DLT_LINUX_SLL2: + if (datalen < SLL2_HDR_LEN) + return -1; + + *l2len = SLL2_HDR_LEN; + sll2_hdr_t *sll2_hdr = (sll2_hdr_t *)pktdata; + *protocol = ntohs(sll2_hdr->sll2_protocol); + break; default: errx(-1, "Unable to process unsupported DLT type: %s (0x%x)", diff --git a/src/defines.h.in b/src/defines.h.in index 6b651398..364b215b 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -101,6 +101,7 @@ typedef struct tcpr_icmpv6_hdr icmpv6_hdr_t; typedef struct tcpr_ethernet_hdr eth_hdr_t; typedef struct tcpr_802_1q_hdr vlan_hdr_t; typedef struct sll_header sll_hdr_t; +typedef struct sll2_header sll2_hdr_t; typedef struct tcpr_arp_hdr arp_hdr_t; typedef struct tcpr_dnsv4_hdr dnsv4_hdr_t; diff --git a/src/tcpedit/dlt.c b/src/tcpedit/dlt.c index 56d64b1f..a1d26380 100644 --- a/src/tcpedit/dlt.c +++ b/src/tcpedit/dlt.c @@ -21,6 +21,7 @@ #include "dlt.h" #include "config.h" #include "tcpedit.h" +#include #include /** @@ -49,13 +50,13 @@ dlt2layer2len(tcpedit_t *tcpedit, int dlt) case DLT_EN10MB: len = 12; break; - /* - case DLT_VLAN: - len = 14; - break; - */ + case DLT_LINUX_SLL: - len = 16; + len = SLL_HDR_LEN; + break; + + case DLT_LINUX_SLL2: + len = SLL2_HDR_LEN; break; case DLT_PPP_SERIAL: @@ -103,6 +104,7 @@ dltrequires(tcpedit_t *tcpedit, int dlt) break; case DLT_LINUX_SLL: + case DLT_LINUX_SLL2: /* we have proto & SRC address */ req = TCPEDIT_DLT_DST; break; @@ -136,6 +138,7 @@ dlt2mtu(tcpedit_t *tcpedit, int dlt) break; case DLT_LINUX_SLL: + case DLT_LINUX_SLL2: mtu = 16436; break; diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c index 828cb041..2c581216 100644 --- a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.c @@ -59,7 +59,7 @@ dlt_linuxsll2_register(tcpeditdlt_t *ctx) plugin->requires += 0; - /* what is our DLT value? */ + /* what is our DLT value? */ plugin->dlt = dlt_value; /* set the prefix name of our plugin. This is also used as the prefix for our options */ @@ -85,7 +85,6 @@ dlt_linuxsll2_register(tcpeditdlt_t *ctx) return tcpedit_dlt_addplugin(ctx, plugin); } - /* * Initializer function. This function is called only once, if and only if * this plugin will be utilized. Remember, if you need to keep track of any state, @@ -104,8 +103,15 @@ dlt_linuxsll2_init(tcpeditdlt_t *ctx) } /* allocate memory for our decode extra data */ - ctx->decoded_extra_size = sizeof(linuxsll2_extra_t); - ctx->decoded_extra = safe_malloc(ctx->decoded_extra_size); + if (ctx->decoded_extra_size > 0) { + if (ctx->decoded_extra_size < sizeof(linuxsll2_extra_t)) { + ctx->decoded_extra_size = sizeof(linuxsll2_extra_t); + ctx->decoded_extra = safe_realloc(ctx->decoded_extra, ctx->decoded_extra_size); + } + } else { + ctx->decoded_extra_size = sizeof(linuxsll2_extra_t); + ctx->decoded_extra = safe_malloc(ctx->decoded_extra_size); + } /* allocate memory for our config data */ plugin->config_size = sizeof(linuxsll2_config_t); @@ -130,13 +136,14 @@ dlt_linuxsll2_cleanup(tcpeditdlt_t *ctx) return TCPEDIT_ERROR; } - /* FIXME: make this function do something if necessary */ if (ctx->decoded_extra != NULL) { safe_free(ctx->decoded_extra); ctx->decoded_extra = NULL; ctx->decoded_extra_size = 0; } + safe_free(plugin->name); + plugin->name = NULL; if (plugin->config != NULL) { safe_free(plugin->config); plugin->config = NULL; @@ -255,18 +262,18 @@ dlt_linuxsll2_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen) * like SPARC */ u_char * -dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data) +dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, int pktlen, u_char *ipv4_data, u_char *ipv6_data) { int l2len; assert(ctx); assert(packet); - assert(l3data); + assert(ipv4_data || ipv6_data); l2len = dlt_linuxsll2_l2len(ctx, packet, pktlen); if (l2len == -1 || pktlen < l2len) return NULL; - return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, l3data, l2len); + return tcpedit_dlt_l3data_merge(ctx, packet, pktlen, ipv4_data ?: ipv6_data, l2len); } /* @@ -301,17 +308,13 @@ dlt_linuxsll2_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char switch(mac) { case SRC_MAC: memcpy(ctx->srcmac, &packet[6], 8); /* linuxssl defines the src mac field to be 8 bytes, not 6 */ - return(ctx->srcmac); - break; - + return ctx->srcmac; case DST_MAC: - return(NULL); break; - default: errx(-1, "Invalid tcpeditdlt_mac_type_t: %d", mac); } - return(NULL); + return NULL; } tcpeditdlt_l2addr_type_t diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h index 7c3b7add..0a76988b 100644 --- a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2.h @@ -37,7 +37,11 @@ int dlt_linuxsll2_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktl int dlt_linuxsll2_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir); int dlt_linuxsll2_proto(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen); u_char *dlt_linuxsll2_get_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen); -u_char *dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, const int pktlen, u_char *l3data); +u_char *dlt_linuxsll2_merge_layer3(tcpeditdlt_t *ctx, + u_char *packet, + int pktlen, + u_char *ipv4_data, + u_char *ipv6_data); tcpeditdlt_l2addr_type_t dlt_linuxsll2_l2addr_type(void); int dlt_linuxsll2_l2len(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen); u_char *dlt_linuxsll2_get_mac(tcpeditdlt_t *ctx, tcpeditdlt_mac_type_t mac, const u_char *packet, const int pktlen); diff --git a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h index 06614329..d44a2cbb 100644 --- a/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h +++ b/src/tcpedit/plugins/dlt_linuxsll2/linuxsll2_types.h @@ -55,7 +55,7 @@ typedef struct { typedef struct { u_int16_t proto; /* Ethernet protocol type */ u_int16_t mbz; /* reserved */ - u_int32_t iindex; /* iface index */ + u_int32_t if_index; /* iface index */ u_int16_t type; /* linux ARPHRD_* values for link-layer device type. See: * http://www.gelato.unsw.edu.au/lxr/source/include/linux/if_arp.h */ diff --git a/src/tcpprep.c b/src/tcpprep.c index d17014c0..52fdc8aa 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -108,6 +108,7 @@ main(int argc, char *argv[]) switch (pcap_datalink(options->pcap)) { case DLT_EN10MB: case DLT_LINUX_SLL: + case DLT_LINUX_SLL2: case DLT_RAW: case DLT_C_HDLC: case DLT_JUNIPER_ETHER: diff --git a/src/tcprewrite_opts.def b/src/tcprewrite_opts.def index 42f34179..a3f40077 100644 --- a/src/tcprewrite_opts.def +++ b/src/tcprewrite_opts.def @@ -63,6 +63,8 @@ tcprewrite currently supports reading the following DLT types: @item @var{DLT_LINUX_SLL} aka Linux Cooked Socket @item +@var{DLT_LINUX_SLL2} aka Linux Cooked Socket v2 +@item @var{DLT_RAW} aka RAW IP @item @var{DLT_NULL} aka BSD Loopback From 54bf031bd07bebafa1461cb1c3be00d251ebadf4 Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 3 Sep 2023 16:02:59 -0700 Subject: [PATCH 16/69] Bug #779 - honour overflow for all pps values I'm not sure why PPS overflow protection was not implemented for lower rates. This fix makes the overflow protection always available. Also fix a compile warning. --- docs/CHANGELOG | 1 + src/common/sendpacket.c | 35 +---------------------------------- src/send_packets.c | 2 +- 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index f2d997fe..331e4de3 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 09/03/2023 Version 4.5.0-beta1 + - low PPS values run at full speed after several days (#779) - create DLT_LINUX_SLL2 plugin (#727) 06/04/2023 Version 4.4.4 diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 9dc378f6..b21b757c 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -1066,7 +1066,7 @@ sendpacket_open_bpf(const char *device, char *errbuf) { sendpacket_t *sp; char bpf_dev[16]; - int dev, mysocket, link_offset, link_type; + int dev, mysocket; struct ifreq ifr; struct bpf_version bv; u_int v; @@ -1134,43 +1134,10 @@ sendpacket_open_bpf(const char *device, char *errbuf) } #endif - /* assign link type and offset */ - switch (v) { - case DLT_SLIP: - link_offset = 0x10; - break; - case DLT_RAW: - link_offset = 0x0; - break; - case DLT_PPP: - link_offset = 0x04; - break; - case DLT_EN10MB: - default: /* default to Ethernet */ - link_offset = 0xe; - break; - } -#if _BSDI_VERSION - 0 > 199510 - switch (v) { - case DLT_SLIP: - v = DLT_SLIP_BSDOS; - link_offset = 0x10; - break; - case DLT_PPP: - v = DLT_PPP_BSDOS; - link_offset = 0x04; - break; - } -#endif - - link_type = v; - /* allocate our sp handle, and return it */ sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t)); strlcpy(sp->device, device, sizeof(sp->device)); sp->handle.fd = mysocket; - // sp->link_type = link_type; - // sp->link_offset = link_offset; sp->handle_type = SP_TYPE_BPF; return sp; diff --git a/src/send_packets.c b/src/send_packets.c index a09d7d5e..d3969e42 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -1025,7 +1025,7 @@ calc_sleep_time(tcpreplay_t *ctx, * * ensure there is no overflow in cases where bits_sent is very high */ - if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) + if (bits_sent > COUNTER_OVERFLOW_RISK) next_tx_us = (bits_sent * 1000) / bps * 1000; else next_tx_us = (bits_sent * 1000000) / bps; From 4f33fbf06eb4b24e8c8be4787d11586fa7e38291 Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 3 Sep 2023 20:13:31 -0700 Subject: [PATCH 17/69] Feature #822: fix compile issue --- configure.ac | 5 ++--- src/common/sendpacket.c | 11 ++++------- src/common/sendpacket.h | 22 ++++++++++++---------- src/send_packets.c | 10 +++++----- src/tcpedit/plugins/dlt_en10mb/en10mb.c | 4 ++-- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/configure.ac b/configure.ac index 97dc4310..12b6b31a 100644 --- a/configure.ac +++ b/configure.ac @@ -1424,12 +1424,11 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ struct xsk_socket_config config; int fd; }; - struct xsk_socket *xsk; + struct xsk_socket xsk; struct xsk_ring_cons *rxr = NULL; struct xsk_ring_prod *txr = NULL; - xsk = (struct xsk_socket*)malloc(sizeof(struct xsk_socket)); int queue_id = 0; - xsk_socket__create(xsk, "lo", queue_id, NULL, rxr, txr, NULL); + xsk_socket__create(&xsk, "lo", queue_id, NULL, rxr, txr, NULL); socket(AF_XDP, SOCK_RAW, 0); ]])],[ AC_DEFINE([HAVE_LIBXDP], [1], diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index a0d26608..3fae5c88 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -132,9 +132,6 @@ #ifdef HAVE_SYS_PARAM_H #include #endif -#ifdef HAVE_SYS_SYSCTL_H -#include -#endif #ifdef HAVE_NET_ROUTE_H #include #endif @@ -1353,8 +1350,8 @@ xsk_configure_socket(struct xsk_umem_info *umem, struct xsk_socket_config *cfg, { struct xsk_socket_info *xsk; struct xsk_ring_cons *rxr = NULL; - struct xsk_ring_prod *txr; int ret; + xsk = (struct xsk_socket_info *)safe_malloc(sizeof(struct xsk_socket_info)); xsk->umem = umem; ret = xsk_socket__create(&xsk->xsk, device, queue_id, umem->umem, rxr, &xsk->tx, cfg); @@ -1440,7 +1437,7 @@ create_umem_area(int nb_of_frames, int frame_size, int nb_of_completion_queue_de return umem; } -static struct xsk_socket_info * +struct xsk_socket_info * create_xsk_socket(struct xsk_umem_info *umem_info, int nb_of_tx_queue_desc, int nb_of_rx_queue_desc, @@ -1464,10 +1461,10 @@ create_xsk_socket(struct xsk_umem_info *umem_info, return xsk_info; } -/** +/* * gets the hardware address via Linux's PF packet interface */ -static struct tcpr_ether_addr * +static _U_ struct tcpr_ether_addr * sendpacket_get_hwaddr_libxdp(sendpacket_t *sp) { struct ifreq ifr; diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index b95b4934..73f2cddd 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -94,6 +94,7 @@ union sendpacket_handle { #ifdef HAVE_LIBXDP #include +#include #include #include @@ -212,7 +213,7 @@ struct sendpacket_s { unsigned int batch_size; unsigned int pckt_count; int frame_size; - int tx_idx; + unsigned int tx_idx; int tx_size; #endif bool abort; @@ -222,12 +223,12 @@ typedef struct sendpacket_s sendpacket_t; #ifdef HAVE_LIBXDP struct xsk_umem_info * create_umem_area(int nb_of_frames, int frame_size, int nb_of_completion_queue_descs, int nb_of_fill_queue_descs); -static struct xsk_socket_info *create_xsk_socket(struct xsk_umem_info *umem, - int nb_of_tx_queue_desc, - int nb_of_rx_queue_desc, - const char *device, - u_int32_t queue_id, - char *errbuf); +struct xsk_socket_info *create_xsk_socket(struct xsk_umem_info *umem, + int nb_of_tx_queue_desc, + int nb_of_rx_queue_desc, + const char *device, + u_int32_t queue_id, + char *errbuf); static inline void gen_eth_frame(struct xsk_umem_info *umem, u_int64_t addr, u_char *pkt_data, COUNTER pkt_size) { @@ -242,12 +243,13 @@ kick_tx(struct xsk_socket_info *xsk) return; } printf("%s\n", "Packet sending exited with error!"); - exit(ret); + exit (1); } + static inline void complete_tx_only(sendpacket_t *sp) { - int completion_idx = 0; + u_int32_t completion_idx = 0; if (sp->xsk_info->outstanding_tx == 0) { return; } @@ -255,7 +257,7 @@ complete_tx_only(sendpacket_t *sp) sp->xsk_info->app_stats.tx_wakeup_sendtos++; kick_tx(sp->xsk_info); } - unsigned int rcvd = xsk_ring_cons__peek(&sp->xsk_info->umem->cq, sp->pckt_count, &(completion_idx)); + unsigned int rcvd = xsk_ring_cons__peek(&sp->xsk_info->umem->cq, sp->pckt_count, &completion_idx); if (rcvd > 0) { xsk_ring_cons__release(&sp->xsk_info->umem->cq, rcvd); sp->xsk_info->outstanding_tx -= rcvd; diff --git a/src/send_packets.c b/src/send_packets.c index 69b8ce88..e9e445d9 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -386,7 +386,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * we've sent enough packets */ while (!ctx->abort && read_next_packet && - (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + (pktdata = get_next_packet(options, pcap, &pkthdr, idx, prev_packet)) != NULL) { now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -515,7 +515,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #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) { + 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 */ @@ -1315,8 +1315,8 @@ prepare_remaining_elements_of_batch(tcpreplay_t *ctx, } sp->pckt_count = pckt_count; dbgx(2, - "Sending packets with LIBXDP in batch, packet numbers from %llu to %llu\n", - packetnum - pckt_count + 1, - packetnum); + "Sending packets with LIBXDP in batch, packet numbers from " COUNTER_SPEC " to " COUNTER_SPEC "\n", + *packetnum - pckt_count + 1, + *packetnum); } #endif /* HAVE_LIBXDP */ diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb.c b/src/tcpedit/plugins/dlt_en10mb/en10mb.c index 0c24d8e5..957ce20d 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb.c +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb.c @@ -184,7 +184,7 @@ dlt_en10mb_parse_subsmac(tcpeditdlt_t *ctx, en10mb_config_t *config, const char { size_t input_len = strlen(input); size_t possible_entries_number = (input_len / (SUBSMAC_ENTRY_LEN + 1)) + 1; - int entry; + size_t entry; en10mb_sub_entry_t *entries = safe_malloc(possible_entries_number * sizeof(en10mb_sub_entry_t)); @@ -524,7 +524,7 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir) newl2len = TCPR_802_1Q_H; } - if (pktlen < newl2len || pktlen + newl2len - ctx->l2len > MAXPACKET) { + if ((uint32_t)pktlen < newl2len || pktlen + newl2len - ctx->l2len > MAXPACKET) { tcpedit_seterr(ctx->tcpedit, "Unable to process packet #" COUNTER_SPEC " since its new length is %d bytes.", ctx->tcpedit->runtime.packetnum, From dccbbc298c48956e3184519c415156e3f504d9a9 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 4 Sep 2023 17:31:24 +0000 Subject: [PATCH 18/69] Feature #822: rename --batch-size to --xdp-batch-size --- src/tcpreplay_api.c | 2 +- src/tcpreplay_opts.def | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 41b32c01..3c14286b 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -359,7 +359,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) goto out; } #ifdef HAVE_LIBXDP - ctx->intf1->batch_size = OPT_VALUE_BATCH_SIZE; + ctx->intf1->batch_size = OPT_VALUE_XDP_BATCH_SIZE; #endif #if defined HAVE_NETMAP ctx->intf1->netmap_delay = ctx->options->netmap_delay; diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index 605a6f45..e4a45ead 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -133,16 +133,6 @@ level for debugging output. Higher numbers increase verbosity. EOText; }; -flag = { - ifdef = HAVE_LIBXDP; - name = batch-size; - arg-type = number; - arg-range = "1->4096"; - descrip = "The maximum number of packets that can be submitted to the AF_XDP TX ring at once"; - arg-default = 25; - doc = "Higher values may improve performance at the cost of burstiness"; -}; - flag = { name = quiet; value = q; @@ -607,6 +597,16 @@ sending packets may cause equally long delays between printing statistics. EOText; }; +flag = { + ifdef = HAVE_LIBXDP; + name = xdp-batch-size; + arg-type = number; + arg-range = "1->4096"; + descrip = "The maximum number of packets that can be submitted to the AF_XDP TX ring at once"; + arg-default = 25; + doc = "Higher values may improve performance at the cost of accuracy"; +}; + flag = { name = version; value = V; From 6bcac917bfa5121aca2f387b906ec6a9b4e4c533 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 4 Sep 2023 14:06:57 -0700 Subject: [PATCH 19/69] Feature #822 - add --xdp option, cleanup, document --- INSTALL | 303 ---------------------------------------- docs/INSTALL | 28 ++++ src/common/sendpacket.c | 63 +++++---- src/tcpreplay_api.c | 9 ++ src/tcpreplay_api.h | 4 + src/tcpreplay_opts.def | 14 ++ 6 files changed, 93 insertions(+), 328 deletions(-) delete mode 100644 INSTALL diff --git a/INSTALL b/INSTALL deleted file mode 100644 index ae077a6a..00000000 --- a/INSTALL +++ /dev/null @@ -1,303 +0,0 @@ -Installation Instructions -************************* - -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2014 Free Software Foundation, Inc. - -This file is free documentation; the Free Software Foundation gives -unlimited permission to copy, distribute and modify it. - - -Advanced Installation -===================== -Visit http://tcpreplay.appneta.com/wiki/installation.html - - -Basic Installation -================== - - ./configure - make - sudo make install - -Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following -more-detailed instructions are generic; see the `README' file for -instructions specific to this package. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, and a -file `config.log' containing compiler output (useful mainly for -debugging `configure'). - - It can also use an optional file (typically called `config.cache' -and enabled with `--cache-file=config.cache' or simply `-C') that saves -the results of its tests to speed up reconfiguring. Caching is -disabled by default to prevent problems with accidental use of stale -cache files. - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If you are using the cache, and at -some point `config.cache' contains results you don't want to keep, you -may remove or edit it. - - The file `configure.ac' (or `configure.in') is used to create -`configure' by a program called `autoconf'. You need `configure.ac' if -you want to change it or regenerate `configure' using a newer version -of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. - - Running `configure' might take a while. While running, it prints - some messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - - -How to make Tcpreplay go fast -============================= - -1) netmap - ------ -This feature will detect netmap capable network drivers on Linux and -BSD systems. If detected, the network driver is bypassed for the -execution duration of tcpreplay and tcpreplay-edit, and network buffers -will be written to directly. This will allow you to achieve full 10GigE -line rates on commodity 10GigE network adapters, similar to rates -achieved by commercial network traffic generators. - -Note that bypassing the network driver will disrupt other applications -connected through the test interface. Use caution when testing on the -same interface you ssh'ed into. - -Ensure that you have supported NICs installed. Most Intel and nForce -(nVidia) adapters will work. Some virtual adapters are supported. - -FreeBSD 10 and higher already contains netmap capabilities and should -be detected automatically by "configure". But first you must enable -netmap on the system by adding 'device netmap' to your kernel config -and rebuilding the kernel. When complete, /dev/netmap will be -available. - -For Linux, download latest netmap sources from http://info.iet.unipi.it/~luigi/netmap/ -or run 'git clone https://code.google.com/p/netmap/'. You will also need to have -kernel sources installed so the build system can patch the sources and build -netmap-enabled drivers. If kernel sources are in /a/b/c/linux-A.B.C/ , then you -should do: - - cd netmap/LINUX - make KSRC=/a/b/c/linux-A.B.C/ # builds the kernel modules - make KSRC=/a/b/c/linux-A.B.C/ apps # builds sample applications - -You can omit KSRC if your kernel sources are in a standard place. - -Once you load the netmap.lin.ko module on your Linux machine, /dev/netmap -will be available. You will also need to replace your existing network drivers -(beyond the scope of this document). - -Building netmap-aware Tcpreplay suite is relatively straight forward. For -FreeBSD, build normally. For Linux, if you extracted netmap into /usr/src/ you -can also build normally. Otherwise you will have to specify the netmap source -directory, for example: - - ./configure --with-netmap=/home/fklassen/git/netmap - make - sudo make install - - -Compilers and Options -===================== - -Some systems require unusual options for compilation or linking that the -`configure' script does not know about. Run `./configure --help' for -details on some of the pertinent environment variables. - - You can give `configure' initial values for configuration parameters -by setting variables in the command line or in the environment. Here -is an example: - - ./configure CC=c99 CFLAGS=-g LIBS=-lposix - - *Note Defining Variables::, for more details. - - -Compiling For Multiple Architectures -==================================== - -You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you can use GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - With a non-GNU `make', it is safer to compile the package for one -architecture at a time in the source code directory. After you have -installed the package for one architecture, use `make distclean' before -reconfiguring for another architecture. - - -Installation Names -================== - -By default, `make install' installs the package's commands under -`/usr/local/bin', include files under `/usr/local/include', etc. You -can specify an installation prefix other than `/usr/local' by giving -`configure' the option `--prefix=PREFIX'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -pass the option `--exec-prefix=PREFIX' to `configure', the package uses -PREFIX as the prefix for installing programs and libraries. -Documentation and other data files still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=DIR' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - - -Optional Features -================= - -Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - - -Specifying the System Type -========================== - -There may be some features `configure' cannot figure out automatically, -but needs to determine by the type of machine the package will run on. -Usually, assuming the package is built to be run on the _same_ -architectures, `configure' can figure that out, but if it prints a -message saying it cannot guess the machine type, give it the -`--build=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name which has the form: - - CPU-COMPANY-SYSTEM - -where SYSTEM can have one of these forms: - - OS KERNEL-OS - - See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the machine type. - - If you are _building_ compiler tools for cross-compiling, you should -use the option `--target=TYPE' to select the type of system they will -produce code for. - - If you want to _use_ a cross compiler, that generates code for a -platform different from the build platform, you should specify the -"host" platform (i.e., that on which the generated programs will -eventually be run) with `--host=TYPE'. - - -Sharing Defaults -================ - -If you want to set default values for `configure' scripts to share, you -can create a site shell script called `config.site' that gives default -values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - - -Defining Variables -================== - -Variables not defined in a site shell script can be set in the -environment passed to `configure'. However, some packages may run -configure again during the build, and the customized values of these -variables may be lost. In order to avoid this problem, you should set -them in the `configure' command line, using `VAR=value'. For example: - - ./configure CC=/usr/local2/bin/gcc - -causes the specified `gcc' to be used as the C compiler (unless it is -overridden in the site shell script). - -Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: - - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash - - -`configure' Invocation -====================== - -`configure' recognizes the following options to control how it operates. - -`--help' -`-h' - Print a summary of the options to `configure', and exit. - -`--version' -`-V' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`--cache-file=FILE' - Enable the cache: use and save the results of the tests in FILE, - traditionally `config.cache'. FILE defaults to `/dev/null' to - disable caching. - -`--config-cache' -`-C' - Alias for `--cache-file=config.cache'. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`configure' also accepts some other, not widely useful, options. Run -`configure --help' for more details. - diff --git a/docs/INSTALL b/docs/INSTALL index ae077a6a..1a74f66a 100644 --- a/docs/INSTALL +++ b/docs/INSTALL @@ -128,6 +128,34 @@ directory, for example: make sudo make install +2) AF_XDF + ------ + +This feature will detect AF_XDP capable network drivers on Linux. If detected, +the `--xdp` option becomes available, allowing eBPF enabled adapters to be +written to directly. + +This feature requires `libxdp-dev` and `libbpf-dev` packages to be installed. +For example: + + $ ./configure | tail + Linux/BSD netmap: no + Tuntap device support: yes + LIBXDP for AF_XDP socket: yes + $ make + $ sudo make install + $ tcpreplay -i eth0 --xdp test/test.pcap + +If you want to compile a version that only uses AF_XDP, use the `--enable-force-libxdp` +configure option, e.g. + + $ ./configure --enable-force-libxdp | tail + Linux/BSD netmap: no + Tuntap device support: yes + LIBXDP for AF_XDP socket: yes + $ make + $ sudo make install + $ tcpreplay -i eth0 test/test.pcap Compilers and Options ===================== diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 3fae5c88..6b060aad 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -572,16 +572,21 @@ sendpacket_open(const char *device, sp = (sendpacket_t *)sendpacket_open_netmap(device, errbuf, arg); else #endif +#ifdef HAVE_LIBXDP + if (sendpacket_type == SP_TYPE_LIBXDP) + sp = sendpacket_open_xsk(device, errbuf); + else +#endif #if defined HAVE_PF_PACKET sp = sendpacket_open_pf(device, errbuf); #elif defined HAVE_BPF - sp = sendpacket_open_bpf(device, errbuf); + sp = sendpacket_open_bpf(device, errbuf); #elif defined HAVE_LIBDNET - sp = sendpacket_open_libdnet(device, errbuf); + sp = sendpacket_open_libdnet(device, errbuf); #elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) - sp = sendpacket_open_pcap(device, errbuf); + sp = sendpacket_open_pcap(device, errbuf); #elif defined HAVE_LIBXDP - sp = sendpacket_open_xsk(device, errbuf); + sp = sendpacket_open_xsk(device, errbuf); #else #error "No defined packet injection method for sendpacket_open()" #endif @@ -693,8 +698,10 @@ sendpacket_close(sendpacket_t *sp) #endif break; case SP_TYPE_LIBXDP: -#if defined HAVE_LIBXDP +#ifdef HAVE_LIBXDP close(sp->handle.fd); + safe_free(sp->xsk_info); + safe_free(sp->umem_info); #endif break; case SP_TYPE_NONE: @@ -1253,30 +1260,36 @@ sendpacket_get_dlt(sendpacket_t *sp) { int dlt = DLT_EN10MB; - if (sp->handle_type == SP_TYPE_KHIAL || sp->handle_type == SP_TYPE_NETMAP || sp->handle_type == SP_TYPE_TUNTAP) { - /* always EN10MB */ - } else { -#if defined HAVE_BPF - int rcode; + switch (sp->handle_type) { + case SP_TYPE_KHIAL: + case SP_TYPE_NETMAP: + case SP_TYPE_TUNTAP: + case SP_TYPE_LIBXDP: + /* always EN10MB */ + return dlt; + default: + ; + } - if ((rcode = ioctl(sp->handle.fd, BIOCGDLT, &dlt)) < 0) { - warnx("Unable to get DLT value for BPF device (%s): %s", sp->device, strerror(errno)); - return (-1); - } +#if defined HAVE_BPF + if ((ioctl(sp->handle.fd, BIOCGDLT, &dlt)) < 0) { + warnx("Unable to get DLT value for BPF device (%s): %s", sp->device, strerror(errno)); + return (-1); + } #elif defined HAVE_PF_PACKET || defined HAVE_LIBDNET - /* use libpcap to get dlt */ - pcap_t *pcap; - char errbuf[PCAP_ERRBUF_SIZE]; - if ((pcap = pcap_open_live(sp->device, 65535, 0, 0, errbuf)) == NULL) { - warnx("Unable to get DLT value for %s: %s", sp->device, errbuf); - return (-1); - } - dlt = pcap_datalink(pcap); - pcap_close(pcap); + /* use libpcap to get dlt */ + pcap_t *pcap; + char errbuf[PCAP_ERRBUF_SIZE]; + if ((pcap = pcap_open_live(sp->device, 65535, 0, 0, errbuf)) == NULL) { + warnx("Unable to get DLT value for %s: %s", sp->device, errbuf); + return (-1); + } + dlt = pcap_datalink(pcap); + pcap_close(pcap); #elif defined HAVE_PCAP_SENDPACKET || defined HAVE_PCAP_INJECT - dlt = pcap_datalink(sp->handle.pcap); + dlt = pcap_datalink(sp->handle.pcap); #endif - } + return dlt; } diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 3c14286b..2b992e00 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -265,6 +265,15 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) #endif } + if (HAVE_OPT(XDP)) { +#ifdef HAVE_LIBXDP + options->xdp = 1; + ctx->sp_type = SP_TYPE_LIBXDP; +#else + err(-1, "--xdp feature was not compiled in. See INSTALL."); +#endif + } + if (HAVE_OPT(UNIQUE_IP)) options->unique_ip = 1; diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index aa46e311..1510443a 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -148,6 +148,10 @@ typedef struct tcpreplay_opt_s { int netmap_delay; #endif +#ifdef HAVE_LIBXDP + int xdp; +#endif + /* print flow statistic */ bool flow_stats; int flow_expiry; diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index e4a45ead..e05c7b32 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -528,6 +528,20 @@ are fully up before netmap transmit. Requires netmap option. Default is 10 secon EOText; }; +flag = { + ifdef = HAVE_LIBXDP; + name = xdp; + descrip = "Write packets directly to AF_XDP enabled network adapter"; + doc = <<- EOText +This feature will detect AF_XDP capable network drivers on Linux systems +that have 'libxdp-dev' and 'libbpf-dev' installed. If detected, the network +stack is bypassed and packets are sent directly to an eBPF enabled driver directly. +This will allow you to achieve full line rates on commodity network adapters, similar to rates +achieved by commercial network traffic generators. +EOText; +}; + + flag = { name = no-flow-stats; descrip = "Suppress printing and tracking flow count, rates and expirations"; From dd324f5be09fa151c2ea6637c4b3ed64d691a86e Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 29 Nov 2023 18:08:05 -0500 Subject: [PATCH 20/69] Handle IPv6 fragment extension header only return NULL when reached end of packet (no data) --- src/common/get.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/common/get.c b/src/common/get.c index 2d911160..c9ab3f6b 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -593,9 +593,25 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const u_char *end_ptr) break; /* - * Can't handle. Unparsable IPv6 fragment/encrypted data + * handle (unparsable) IPv6 fragment data */ case TCPR_IPV6_NH_FRAGMENT: + // next points to l4 data + dbgx(3, "Go deeper due to fragment extension header 0x%02X", proto); + exthdr = get_ipv6_next(next, end_ptr); + if (exthdr > end_ptr) { + next = NULL; + done = true; + break; + } + proto = exthdr->ip_nh; + next = exthdr; + // done = true; + break; + + /* + * Can't handle. Unparsable IPv6 encrypted data + */ case TCPR_IPV6_NH_ESP: next = NULL; done = true; From 58f935e64344274499bba1ff86317e50dc25a8bc Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Tue, 12 Dec 2023 14:10:23 -0500 Subject: [PATCH 21/69] check for NULL ptr --- src/common/get.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/get.c b/src/common/get.c index c9ab3f6b..2437cfa5 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -599,7 +599,7 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const u_char *end_ptr) // next points to l4 data dbgx(3, "Go deeper due to fragment extension header 0x%02X", proto); exthdr = get_ipv6_next(next, end_ptr); - if (exthdr > end_ptr) { + if ((NULL == exthdr) || (exthdr > end_ptr)) { next = NULL; done = true; break; From c4ee66de6313d9d9169b1e2f1ca44b9b9c925dad Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Thu, 14 Dec 2023 16:58:22 -0500 Subject: [PATCH 22/69] resolve warning --- src/common/get.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/get.c b/src/common/get.c index 2437cfa5..b3bf3661 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -599,7 +599,7 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const u_char *end_ptr) // next points to l4 data dbgx(3, "Go deeper due to fragment extension header 0x%02X", proto); exthdr = get_ipv6_next(next, end_ptr); - if ((NULL == exthdr) || (exthdr > end_ptr)) { + if ((NULL == exthdr) || ((u_char *)exthdr > end_ptr)) { next = NULL; done = true; break; From a591bf795055c4f8ef73f26d764cfb94435c9e49 Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Fri, 15 Dec 2023 20:51:22 -0500 Subject: [PATCH 23/69] revise comparison to match rest of code --- src/common/get.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/get.c b/src/common/get.c index b3bf3661..95ca15b8 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -599,7 +599,7 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const u_char *end_ptr) // next points to l4 data dbgx(3, "Go deeper due to fragment extension header 0x%02X", proto); exthdr = get_ipv6_next(next, end_ptr); - if ((NULL == exthdr) || ((u_char *)exthdr > end_ptr)) { + if ((exthdr == NULL) || ((u_char *)exthdr > end_ptr)) { next = NULL; done = true; break; From 6af43412b876a6abeaaf2799961b5dfac0deb2c1 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:17:08 -0800 Subject: [PATCH 24/69] Feature #796: nanosecond -changelog/credit --- docs/CHANGELOG | 1 + docs/CREDIT | 3 +++ 2 files changed, 4 insertions(+) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 331e4de3..fa49fc84 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 09/03/2023 Version 4.5.0-beta1 + - nanosecond timestamps (#796) - low PPS values run at full speed after several days (#779) - create DLT_LINUX_SLL2 plugin (#727) diff --git a/docs/CREDIT b/docs/CREDIT index 38e200f6..26583a35 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -116,3 +116,6 @@ David Guti Bastian Triller - Linux SLL2 + +GithHub @plangarbalint + - nanosecond timers \ No newline at end of file From f9e2f703c8bc82d7aecef69efb46fa3a6700f6c5 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:35:16 -0800 Subject: [PATCH 25/69] Feature #796: clang-format mods format commit with `git-clang-format HEAD~1` --- src/bridge.c | 2 +- src/common/timer.c | 20 ++--- src/common/timer.h | 36 +++++---- src/common/utils.c | 15 +++- src/replay.c | 16 ++-- src/send_packets.c | 166 +++++++++++++++++++++++------------------- src/sleep.c | 13 ++-- src/sleep.h | 35 ++++----- src/tcpreplay_api.c | 32 ++++---- src/timestamp_trace.h | 24 ++++-- 10 files changed, 191 insertions(+), 168 deletions(-) diff --git a/src/bridge.c b/src/bridge.c index 653f9192..69efa31c 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -211,7 +211,7 @@ do_bridge(tcpbridge_opt_t *options, tcpedit_t *tcpedit) } if (get_current_time(&stats.end_time) < 0) - errx(-1, "get_current_time() failed: %s", strerror(errno)); + errx(-1, "get_current_time() failed: %s", strerror(errno)); packet_stats(&stats); } diff --git a/src/common/timer.c b/src/common/timer.c index be447673..60e10bf2 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -44,13 +44,15 @@ init_timestamp(struct timespec *timestamp) timesclear(timestamp); } -int get_current_time(struct timespec *ts){ - #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L - return clock_gettime(CLOCK_MONOTONIC, ts); - #else - struct timeval tv; - int success = gettimeofday(&tv, NULL); - TIMEVAL_TO_TIMESPEC(&tv, ts); - return success; - #endif +int +get_current_time(struct timespec *ts) +{ +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L + return clock_gettime(CLOCK_MONOTONIC, ts); +#else + struct timeval tv; + int success = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, ts); + return success; +#endif } diff --git a/src/common/timer.h b/src/common/timer.h index 71baade3..0cf801fa 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -114,33 +114,31 @@ void timesdiv_float(struct timespec *tvs, float div); /* add tvp and uvp and store in vvp */ #ifndef timeradd_timespec -#define timeradd_timespec(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ - (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \ - if ((vvp)->tv_nsec >= 1000000000) { \ - (vvp)->tv_sec++; \ - (vvp)->tv_nsec -= 1000000000; \ - } \ +#define timeradd_timespec(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_nsec; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_nsec -= 1000000000; \ + } \ } while (0) #endif - /* add tvp and uvp and store in vvp */ #ifndef timeradd_timeval_timespec -#define timeradd_timeval_timespec(tvp, uvp, vvp) \ - do { \ - (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ - (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_usec * 1000; \ - if ((vvp)->tv_nsec >= 1000000000) { \ - int seconds = (vvp)->tv_nsec % 1000000000; \ - (vvp)->tv_sec += seconds; \ - (vvp)->tv_nsec -= 1000000000 * seconds; \ - } \ +#define timeradd_timeval_timespec(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_nsec = (tvp)->tv_nsec + (uvp)->tv_usec * 1000; \ + if ((vvp)->tv_nsec >= 1000000000) { \ + int seconds = (vvp)->tv_nsec % 1000000000; \ + (vvp)->tv_sec += seconds; \ + (vvp)->tv_nsec -= 1000000000 * seconds; \ + } \ } while (0) #endif - /* subtract uvp from tvp and store in vvp */ #ifndef timersub #define timersub(tvp, uvp, vvp) \ diff --git a/src/common/utils.c b/src/common/utils.c index 764a37e1..8e69834e 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -253,10 +253,16 @@ packet_stats(const tcpreplay_stats_t *stats) if (diff_us >= 1000 * 1000) { printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%02zd seconds\n", - stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)(diff.tv_nsec / (10 * 1000000))); - } else{ + stats->pkts_sent, + stats->bytes_sent, + (ssize_t)diff.tv_sec, + (ssize_t)(diff.tv_nsec / (10 * 1000000))); + } else { printf("Actual: " COUNTER_SPEC " packets (" COUNTER_SPEC " bytes) sent in %zd.%06zd seconds\n", - stats->pkts_sent, stats->bytes_sent, (ssize_t)diff.tv_sec, (ssize_t)diff.tv_nsec / 1000); + stats->pkts_sent, + stats->bytes_sent, + (ssize_t)diff.tv_sec, + (ssize_t)diff.tv_nsec / 1000); } if (mb_sec >= 1) @@ -289,7 +295,8 @@ packet_stats(const tcpreplay_stats_t *stats) * @param len: length of the buffer * @return: string containing date, or -1 on error */ -int format_date_time(struct timespec *when, char *buf, size_t len) +int +format_date_time(struct timespec *when, char *buf, size_t len) { struct tm *tm; char tmp[64]; diff --git a/src/replay.c b/src/replay.c index 03b2abb6..c01206d6 100644 --- a/src/replay.c +++ b/src/replay.c @@ -133,10 +133,10 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); } } @@ -145,10 +145,10 @@ replay_file(tcpreplay_t *ctx, int idx) if (ctx->options->verbose) { /* in cache mode, we may not have opened the file */ if (pcap == NULL) - if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { - tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); - return -1; - } + if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); + return -1; + } ctx->options->file_cache[idx].dlt = pcap_datalink(pcap); /* init tcpdump */ diff --git a/src/send_packets.c b/src/send_packets.c index 53fe0a52..e8d41fbc 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -60,16 +60,18 @@ extern tcpedit_t *tcpedit; extern int debug; #endif -static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, - struct timespec *last, COUNTER len, - sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, - COUNTER start_us, COUNTER *skip_length); -static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, - struct timespec *nap_this_time, struct timespec *now); -static u_char *get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, - struct pcap_pkthdr *pkthdr, - int file_idx, - packet_cache_t **prev_packet); +static void calc_sleep_time(tcpreplay_t *ctx, + struct timespec *pkt_time, + struct timespec *last, + COUNTER len, + sendpacket_t *sp, + COUNTER counter, + struct timespec *sent_timestamp, + COUNTER start_us, + COUNTER *skip_length); +static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, struct timespec *nap_this_time, struct timespec *now); +static u_char * +get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, packet_cache_t **prev_packet); static uint32_t get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter); #ifdef HAVE_NETMAP @@ -333,7 +335,7 @@ increment_iteration(tcpreplay_t *ctx) */ void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) -{ +{ struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; @@ -370,8 +372,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) ctx->skip_packets = 0; timesclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + - SEC_TO_MICROSEC(options->limit_time); + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -385,8 +386,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * Keep sending while we have packets or until * we've sent enough packets */ - while (!ctx->abort && - (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; @@ -444,8 +444,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * time stamping is expensive, but now is the * time to do it. */ - dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, - pkthdr_ts.tv_nsec); + dbgx(4, "This packet time: " TIMESPEC_FORMAT, pkthdr_ts.tv_sec, pkthdr_ts.tv_nsec); skip_length = 0; ctx->skip_packets = 0; @@ -472,9 +471,15 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * This also sets skip_length and skip_packets which will avoid * timestamping for a given number of packets. */ - calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, - pktlen, sp, packetnum, &stats->end_time, - TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + calc_sleep_time(ctx, + &stats->pkt_ts_delta, + &stats->time_delta, + pktlen, + sp, + packetnum, + &stats->end_time, + TIMESPEC_TO_NANOSEC(&stats->start_time), + &skip_length); /* * Track the time of the "last packet sent". * @@ -544,7 +549,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #endif /* print stats during the run? */ if (options->stats > 0) { - if (! timesisset(&stats->last_print)) { + if (!timesisset(&stats->last_print)) { TIMESPEC_SET(&stats->last_print, &now); } else { timessub(&now, &stats->last_print, &print_delta); @@ -564,8 +569,8 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) #endif /* stop sending based on the duration limit... */ if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + /* ... or stop sending based on the limit -L? */ + (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ @@ -633,8 +638,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * ctx->skip_packets = 0; timesclear(&last_pkt_ts); if (options->limit_time > 0) - end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + - SEC_TO_MICROSEC(options->limit_time); + end_us = TIMESPEC_TO_MICROSEC(&stats->start_time) + SEC_TO_MICROSEC(options->limit_time); else end_us = 0; @@ -759,9 +763,15 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * * This also sets skip_length and skip_packets which will avoid * timestamping for a given number of packets. */ - calc_sleep_time(ctx, &stats->pkt_ts_delta, &stats->time_delta, - pktlen, sp, packetnum, &stats->end_time, - TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + calc_sleep_time(ctx, + &stats->pkt_ts_delta, + &stats->time_delta, + pktlen, + sp, + packetnum, + &stats->end_time, + TIMESPEC_TO_NANOSEC(&stats->start_time), + &skip_length); /* * Track the time of the "last packet sent". @@ -802,7 +812,7 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* print stats during the run? */ if (options->stats > 0) { - if (! timesisset(&stats->last_print)) { + if (!timesisset(&stats->last_print)) { TIMESPEC_SET(&stats->last_print, &now); } else { timessub(&now, &stats->last_print, &print_delta); @@ -830,8 +840,8 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * /* stop sending based on the duration limit... */ if ((end_us > 0 && (COUNTER)TIMESPEC_TO_MICROSEC(&now) > end_us) || - /* ... or stop sending based on the limit -L? */ - (limit_send > 0 && stats->pkts_sent >= limit_send)) { + /* ... or stop sending based on the limit -L? */ + (limit_send > 0 && stats->pkts_sent >= limit_send)) { ctx->abort = true; } } /* while */ @@ -983,10 +993,16 @@ cache_mode(tcpreplay_t *ctx, char *cachedata, COUNTER packet_num) * calculate the appropriate amount of time to sleep. Sleep time * will be in ctx->nap. */ -static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, - struct timespec *time_delta, COUNTER len, - sendpacket_t *sp, COUNTER counter, struct timespec *sent_timestamp, - COUNTER start_ns, COUNTER *skip_length) +static void +calc_sleep_time(tcpreplay_t *ctx, + struct timespec *pkt_ts_delta, + struct timespec *time_delta, + COUNTER len, + sendpacket_t *sp, + COUNTER counter, + struct timespec *sent_timestamp, + COUNTER start_ns, + COUNTER *skip_length) { tcpreplay_opt_t *options = ctx->options; struct timespec nap_for; @@ -1024,8 +1040,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, // printf("nap_for sec: %lu\n", nap_for.tv_sec); // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); - dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, - ctx->nap.tv_sec, ctx->nap.tv_nsec); + dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); dbgx(3, "original packet delta/div: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); } @@ -1040,7 +1055,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, if (now_ns) { COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); //PB: Ferencol miert bits sent? + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); // PB: Ferencol miert bits sent? COUNTER tx_ns = now_ns - start_ns; /* @@ -1060,8 +1075,7 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, *skip_length = ((tx_ns - next_tx_ns) * bps) / 8000000000; } - update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_ns, tx_ns, next_tx_ns); + update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_ns, tx_ns, next_tx_ns); } dbgx(3, "packet size=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, len, ctx->nap.tv_sec, ctx->nap.tv_nsec); @@ -1069,38 +1083,40 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, case speed_packetrate: /* - * Ignore the time supplied by the capture file and send data at - * a constant rate (packets per second). - */ - now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); - if (now_ns) { - COUNTER next_tx_ns; - COUNTER pph = ctx->options->speed.speed; - COUNTER pkts_sent = ctx->stats.pkts_sent; - COUNTER tx_ns = now_ns - start_ns; - /* - * packets * 1000000 divided by pps = microseconds - * packets per sec (pps) = packets per hour / (60 * 60) - * - * Adjust for long running tests with high PPS to prevent overflow. - * When active, adjusted calculation may add a bit of jitter. - */ - if ((pkts_sent < COUNTER_OVERFLOW_RISK)) - next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; - else - next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); - - if (next_tx_ns > tx_ns) - NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); - else - ctx->skip_packets = options->speed.pps_multi; - - update_current_timestamp_trace_entry(ctx->stats.bytes_sent + - (COUNTER)len, now_ns, tx_ns, next_tx_ns); - } - - dbgx(3, "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, - ctx->stats.pkts_sent, ctx->nap.tv_sec, ctx->nap.tv_nsec); + * Ignore the time supplied by the capture file and send data at + * a constant rate (packets per second). + */ + now_ns = TIMESPEC_TO_NANOSEC(sent_timestamp); + if (now_ns) { + COUNTER next_tx_ns; + COUNTER pph = ctx->options->speed.speed; + COUNTER pkts_sent = ctx->stats.pkts_sent; + COUNTER tx_ns = now_ns - start_ns; + /* + * packets * 1000000 divided by pps = microseconds + * packets per sec (pps) = packets per hour / (60 * 60) + * + * Adjust for long running tests with high PPS to prevent overflow. + * When active, adjusted calculation may add a bit of jitter. + */ + if ((pkts_sent < COUNTER_OVERFLOW_RISK)) + next_tx_ns = (pkts_sent * 1000000000) * (60 * 60) / pph; + else + next_tx_ns = ((pkts_sent * 1000000) / pph * 1000) * (60 * 60); + + if (next_tx_ns > tx_ns) + NANOSEC_TO_TIMESPEC(next_tx_ns - tx_ns, &ctx->nap); + else + ctx->skip_packets = options->speed.pps_multi; + + update_current_timestamp_trace_entry(ctx->stats.bytes_sent + (COUNTER)len, now_ns, tx_ns, next_tx_ns); + } + + dbgx(3, + "packet count=" COUNTER_SPEC "\t\tnap=" TIMESPEC_FORMAT, + ctx->stats.pkts_sent, + ctx->nap.tv_sec, + ctx->nap.tv_nsec); break; case speed_oneatatime: @@ -1126,8 +1142,8 @@ static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_ts_delta, } } -static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, - struct timespec *nap_this_time, struct timespec *now) +static void +tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, struct timespec *now) { tcpreplay_opt_t *options = ctx->options; bool flush = @@ -1138,7 +1154,7 @@ static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, #endif /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)){ + if (!timesisset(nap_this_time)) { return; } diff --git a/src/sleep.c b/src/sleep.c index 160a6956..16234a43 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -48,9 +48,8 @@ ioport_sleep_init(void) #endif } -void -ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, - struct timespec *now _U_, bool flush _U_) +void +ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timespec *now _U_, bool flush _U_) { #if defined HAVE_IOPORT_SLEEP__ struct timespec nap_for; @@ -63,7 +62,7 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, * process the seconds, we do this in a loop so we don't have to * use slower 64bit integers or worry about integer overflows. */ - for (i = 0; i < nap_for.tv_sec; i ++) { + for (i = 0; i < nap_for.tv_sec; i++) { nsec = nap_for.tv_sec * 1000000000; while (usec > 0) { usec--; @@ -73,10 +72,10 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, /* process the nsec */ nsec = nap->tv_nsec; - nsec --; /* fudge factor for all the above */ + nsec--; /* fudge factor for all the above */ while (nsec > 0) { - nsec --; - outb(ioport_sleep_value, 0x80); + nsec--; + outb(ioport_sleep_value, 0x80); } #else err(-1, "Platform does not support IO Port for timing"); diff --git a/src/sleep.h b/src/sleep.h index fe0e8be7..71365ab5 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -28,14 +28,11 @@ #include #endif -#include -#include -#include -#include #include #include #include #include +#include #include #ifdef HAVE_SYS_EVENT #include @@ -55,16 +52,15 @@ #endif /* HAVE_NETMAP */ static inline void -nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timespec *now, bool flush _U_) +nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timespec sleep_until; - timeradd_timespec(now, nap, &sleep_until); - #if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); - #else - nanosleep(nap, NULL); - #endif + struct timespec sleep_until; + timeradd_timespec(now, nap, &sleep_until); +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); +#else + nanosleep(nap, NULL); +#endif #ifdef HAVE_NETMAP if (flush) @@ -81,8 +77,7 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, * Note: make sure "now" has recently been updated. */ static inline void -gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timespec *now, bool flush _U_) +gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) { struct timeval now_ms, sleep_until, nap_for, last; TIMESPEC_TO_TIMEVAL(&nap_for, nap); @@ -91,7 +86,7 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, uint32_t i = 0; TIMEVAL_SET(&last, &now_ms); #endif /* HAVE_NETMAP */ - + timeradd(&now_ms, &nap_for, &sleep_until); while (!sp->abort) { #ifdef HAVE_NETMAP @@ -121,9 +116,8 @@ gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, * resolution which is pretty much useless for our needs. Keeping it here * for future reference */ -static inline void -select_sleep(sendpacket_t *sp _U_, struct timespec *nap, - struct timespec *now_ns, bool flush _U_) +static inline void +select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns, bool flush _U_) { struct timeval timeout; timeout.tv_sec = 0; @@ -157,7 +151,6 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, /* before calling port_sleep(), you have to call port_sleep_init() */ void ioport_sleep_init(void); -void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, - struct timespec *now, bool flush); +void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush); #endif /* __SLEEP_H__ */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 8b4c4043..319ae109 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -18,25 +18,23 @@ * along with the Tcpreplay Suite. If not, see . */ -#include "config.h" +#include "tcpreplay_api.h" #include "defines.h" +#include "config.h" #include "common.h" - +#include "replay.h" +#include "send_packets.h" +#include "sleep.h" #include +#include #include +#include #include #include #include #include -#include -#include -#include #include - -#include "tcpreplay_api.h" -#include "send_packets.h" -#include "sleep.h" -#include "replay.h" +#include #ifdef TCPREPLAY_EDIT #include "tcpreplay_edit_opts.h" @@ -1145,7 +1143,7 @@ tcpreplay_replay(tcpreplay_t *ctx) tcpreplay_seterr(ctx, "invalid dualfile source count: %d", ctx->options->source_cnt); return -1; } - + init_timestamp(&ctx->stats.start_time); init_timestamp(&ctx->stats.time_delta); init_timestamp(&ctx->stats.end_time); @@ -1383,17 +1381,19 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } -void apply_loop_delay(tcpreplay_t *ctx){ - if(ctx->options->accurate == accurate_nanosleep){ +void +apply_loop_delay(tcpreplay_t *ctx) +{ + if (ctx->options->accurate == accurate_nanosleep) { if (!ctx->abort && ctx->options->loopdelay_ns > 0) { struct timespec nap; nap.tv_sec = 0; nap.tv_nsec = ctx->options->loopdelay_ns; nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); } - }else{ + } else { if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - } + usleep(ctx->options->loopdelay_ms * 1000); + } } } diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index f567f810..19d7548d 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -57,8 +57,8 @@ update_current_timestamp_trace_entry(COUNTER bytes_sent, COUNTER now_us, COUNTER timestamp_trace_entry_array[trace_num].next_tx_us = next_tx_us; } -static inline void add_timestamp_trace_entry(COUNTER size, - struct timespec *timestamp, COUNTER skip_length) +static inline void +add_timestamp_trace_entry(COUNTER size, struct timespec *timestamp, COUNTER skip_length) { if (trace_num >= TRACE_MAX_ENTRIES) return; @@ -100,10 +100,18 @@ dump_timestamp_trace_array(const struct timeval *start, const struct timeval *st } } #else -static inline void update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), COUNTER UNUSED(now_us), - COUNTER UNUSED(tx_us), COUNTER UNUSED(next_tx_us)) { } -static inline void add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), - COUNTER UNUSED(skip_length)) { } -static inline void dump_timestamp_trace_array(const struct timeval *UNUSED(start), - const struct timeval *UNUSED(stop), const COUNTER UNUSED(bps)) { } +static inline void +update_current_timestamp_trace_entry(COUNTER UNUSED(bytes_sent), + COUNTER UNUSED(now_us), + COUNTER UNUSED(tx_us), + COUNTER UNUSED(next_tx_us)) +{} +static inline void +add_timestamp_trace_entry(COUNTER UNUSED(size), struct timespec *UNUSED(timestamp), COUNTER UNUSED(skip_length)) +{} +static inline void +dump_timestamp_trace_array(const struct timeval *UNUSED(start), + const struct timeval *UNUSED(stop), + const COUNTER UNUSED(bps)) +{} #endif /* TIMESTAMP_TRACE */ From dda4343d2fe6cb37fec6775a11ccb463d8cac895 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 25 Dec 2023 09:56:20 -0800 Subject: [PATCH 26/69] Feature #796: fix netmap, get_current_time etc. * macOS was not reading nanosecond timer * get time called too often, causing overhead * fine tune gettimeofday_sleep() * adjusted overflow value to prevent issues with very long running instances * fix build/merge issues --- .clang-format | 15 ++++++++++++- docs/CREDIT | 1 + src/common/timer.c | 4 ++-- src/common/timer.h | 4 +--- src/common/utils.c | 4 ++-- src/defines.h.in | 2 +- src/send_packets.c | 31 +++++++++++--------------- src/signal_handler.c | 2 +- src/sleep.c | 6 ------ src/sleep.h | 27 +++++++++++------------ src/tcpreplay_api.c | 49 ++++++++++++++++++++++-------------------- src/tcpreplay_api.h | 1 - src/tcpreplay_opts.def | 4 +++- 13 files changed, 77 insertions(+), 73 deletions(-) diff --git a/.clang-format b/.clang-format index 0b3aa238..56e516cd 100644 --- a/.clang-format +++ b/.clang-format @@ -3,10 +3,12 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: None AlignOperands: Align +AlignTrailingComments: true AllowAllArgumentsOnNextLine: false AllowAllConstructorInitializersOnNextLine: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: Always +AllowShortEnumsOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None AllowShortLambdasOnASingleLine: All @@ -16,7 +18,7 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: TopLevelDefinitions AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes -AttributeMacros: ['__capability', '__output', '__ununsed', '_U_'] +AttributeMacros: ['__capability', '__output', '__unused', '_U_'] BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: None @@ -28,9 +30,11 @@ BraceWrapping: AfterEnum: false AfterFunction: true AfterNamespace: false + AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false + BeforeWhile: false IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: true @@ -69,7 +73,12 @@ IncludeCategories: Priority: 6 IndentGotoLabels: false IndentPPDirectives: None +IndentRequiresClause: false +IndentExternBlock: AfterExternBlock IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertTrailingCommas: None KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 1 NamespaceIndentation: None @@ -86,12 +95,16 @@ SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Never SpacesInConditionalStatement: false SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 SpacesInParentheses: false SpacesInSquareBrackets: false TabWidth: 8 diff --git a/docs/CREDIT b/docs/CREDIT index 26583a35..d21aee22 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -118,4 +118,5 @@ Bastian Triller - Linux SLL2 GithHub @plangarbalint + - eBPF - nanosecond timers \ No newline at end of file diff --git a/src/common/timer.c b/src/common/timer.c index 60e10bf2..95ebe5dc 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -22,7 +22,7 @@ #include "config.h" #include -/* Miscellaneous timeval routines */ +/* Miscellaneous timeval/timespec routines */ /* Divide tvs by div, storing the result in tvs */ void @@ -47,7 +47,7 @@ init_timestamp(struct timespec *timestamp) int get_current_time(struct timespec *ts) { -#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L +#if defined CLOCK_MONOTONIC || defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L return clock_gettime(CLOCK_MONOTONIC, ts); #else struct timeval tv; diff --git a/src/common/timer.h b/src/common/timer.h index 0cf801fa..b4cbfa34 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -178,6 +178,4 @@ void timesdiv_float(struct timespec *tvs, float div); typedef struct timeval timestamp_t; void init_timestamp(struct timespec *timestamp); -int get_current_time(struct timespec *ts); - -#endif /* _TIMER_H_ */ +int get_current_time(struct timespec *timestamp); diff --git a/src/common/utils.c b/src/common/utils.c index 8e69834e..520d40d2 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -307,8 +307,8 @@ format_date_time(struct timespec *when, char *buf, size_t len) if (!tm) return -1; - strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%06u", tm); - return snprintf(buf, len, tmp, when->tv_nsec / 1000); + strftime(tmp, sizeof tmp, "%Y-%m-%d %H:%M:%S.%%09u", tm); + return snprintf(buf, len, tmp, when->tv_nsec); } /** diff --git a/src/defines.h.in b/src/defines.h.in index fe776552..1975f053 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -102,7 +102,7 @@ char dummy[0]; #define COUNTER unsigned long #define COUNTER_SPEC "%lu" #endif -#define COUNTER_OVERFLOW_RISK (((COUNTER)~0) >> 20) +#define COUNTER_OVERFLOW_RISK (((COUNTER)~0) >> 23) #include #include diff --git a/src/send_packets.c b/src/send_packets.c index e8d41fbc..788341b4 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -56,10 +56,6 @@ extern tcpedit_t *tcpedit; #include "send_packets.h" #include "sleep.h" -#ifdef DEBUG -extern int debug; -#endif - static void calc_sleep_time(tcpreplay_t *ctx, struct timespec *pkt_time, struct timespec *last, @@ -70,8 +66,11 @@ static void calc_sleep_time(tcpreplay_t *ctx, COUNTER start_us, COUNTER *skip_length); static void tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp _U_, struct timespec *nap_this_time, struct timespec *now); -static u_char * -get_next_packet(tcpreplay_t *ctx, pcap_t *pcap, struct pcap_pkthdr *pkthdr, int file_idx, packet_cache_t **prev_packet); +static u_char *get_next_packet(tcpreplay_opt_t *options, + pcap_t *pcap, + struct pcap_pkthdr *pkthdr, + int file_idx, + packet_cache_t **prev_packet); static uint32_t get_user_count(tcpreplay_t *ctx, sendpacket_t *sp, COUNTER counter); #ifdef HAVE_NETMAP @@ -336,7 +335,7 @@ increment_iteration(tcpreplay_t *ctx) void send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) { - struct timespec now, print_delta, last_pkt_ts, pkthdr_ts; + struct timespec now, print_delta, last_pkt_ts; tcpreplay_opt_t *options = ctx->options; tcpreplay_stats_t *stats = &ctx->stats; COUNTER packetnum = 0; @@ -386,7 +385,9 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) * Keep sending while we have packets or until * we've sent enough packets */ - while (!ctx->abort && (pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { + while (!ctx->abort && read_next_packet && + (pktdata = get_next_packet(options, pcap, &pkthdr, idx, prev_packet)) != NULL) { + struct timespec pkthdr_ts; TIMEVAL_AS_TIMESPEC_SET(&pkthdr_ts, &pkthdr.ts); // libpcap puts nanosec values in tv_usec now_is_now = false; packetnum++; @@ -480,6 +481,7 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) &stats->end_time, TIMESPEC_TO_NANOSEC(&stats->start_time), &skip_length); + /* * Track the time of the "last packet sent". * @@ -1033,12 +1035,6 @@ calc_sleep_time(tcpreplay_t *ctx, if (timescmp(pkt_ts_delta, time_delta, >)) { /* pkt_time_delta has increased, so handle normally */ timessub(pkt_ts_delta, time_delta, &nap_for); - // printf("pkt_ts_delta sec: %lu\n", pkt_ts_delta->tv_sec); - // printf("pkt_ts_delta nsec: %lu\n", pkt_ts_delta->tv_nsec); - // printf("time_delta sec: %lu\n", time_delta->tv_sec); - // printf("time_delta nsec: %lu\n", time_delta->tv_nsec); - // printf("nap_for sec: %lu\n", nap_for.tv_sec); - // printf("nap_for nsec: %lu\n", nap_for.tv_nsec); TIMESPEC_SET(&ctx->nap, &nap_for); dbgx(3, "original packet delta time: " TIMESPEC_FORMAT, ctx->nap.tv_sec, ctx->nap.tv_nsec); timesdiv_float(&ctx->nap, options->speed.multiplier); @@ -1055,7 +1051,7 @@ calc_sleep_time(tcpreplay_t *ctx, if (now_ns) { COUNTER next_tx_ns; COUNTER bps = options->speed.speed; - COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); // PB: Ferencol miert bits sent? + COUNTER bits_sent = ((ctx->stats.bytes_sent + len) * 8); COUNTER tx_ns = now_ns - start_ns; /* @@ -1063,7 +1059,7 @@ calc_sleep_time(tcpreplay_t *ctx, * * ensure there is no overflow in cases where bits_sent is very high */ - if (bits_sent > COUNTER_OVERFLOW_RISK && bps > 500000) + if (bits_sent > COUNTER_OVERFLOW_RISK) next_tx_ns = (bits_sent * 1000) / bps * 1000000; else next_tx_ns = (bits_sent * 1000000000) / bps; @@ -1154,9 +1150,8 @@ tcpr_sleep(tcpreplay_t *ctx, sendpacket_t *sp, struct timespec *nap_this_time, s #endif /* don't sleep if nap = {0, 0} */ - if (!timesisset(nap_this_time)) { + if (!timesisset(nap_this_time)) return; - } /* do we need to limit the total time we sleep? */ if (timesisset(&(options->maxsleep)) && (timescmp(nap_this_time, &(options->maxsleep), >))) { diff --git a/src/signal_handler.c b/src/signal_handler.c index f8b85d9e..9bfd56ee 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -31,7 +31,7 @@ #include #include -struct timeval suspend_time; // PB: do we need to modify this part of the code? +struct timeval suspend_time; static struct timeval suspend_start; static struct timeval suspend_end; diff --git a/src/sleep.c b/src/sleep.c index 16234a43..27e3e85f 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -80,10 +80,4 @@ ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap _U_, struct timesp #else err(-1, "Platform does not support IO Port for timing"); #endif - -#ifdef HAVE_NETMAP - if (flush) - ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ -#endif /* HAVE_NETMAP */ - get_current_time(now); } diff --git a/src/sleep.h b/src/sleep.h index 71365ab5..763b8980 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -54,9 +54,9 @@ static inline void nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush _U_) { +#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L struct timespec sleep_until; timeradd_timespec(now, nap, &sleep_until); -#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &sleep_until, NULL); #else nanosleep(nap, NULL); @@ -77,36 +77,36 @@ nanosleep_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespe * Note: make sure "now" has recently been updated. */ static inline void -gettimeofday_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now, bool flush _U_) +gettimeofday_sleep(sendpacket_t *sp, struct timespec *nap, struct timespec *now, bool flush _U_) { - struct timeval now_ms, sleep_until, nap_for, last; - TIMESPEC_TO_TIMEVAL(&nap_for, nap); - gettimeofday(&now_ms, NULL); + struct timespec sleep_until; #ifdef HAVE_NETMAP + struct timespec last; uint32_t i = 0; - TIMEVAL_SET(&last, &now_ms); + + TIMESPEC_SET(&last, now); #endif /* HAVE_NETMAP */ - timeradd(&now_ms, &nap_for, &sleep_until); + timeradd_timespec(now, nap, &sleep_until); + while (!sp->abort) { #ifdef HAVE_NETMAP - if (flush && timercmp(&now_ms, &last, !=)) { - TIMESPEC_SET(&last, &now_ms); + if (flush && timescmp(now, &last, !=)) { + TIMESPEC_SET(&last, now); if ((++i & 0xf) == 0) /* flush TX buffer every 16 usec */ ioctl(sp->handle.fd, NIOCTXSYNC, NULL); } #endif /* HAVE_NETMAP */ - if (timercmp(&now_ms, &sleep_until, >=)) + if (timescmp(now, &sleep_until, >=)) break; #ifdef HAVE_SCHED_H /* yield the CPU so other apps remain responsive */ sched_yield(); #endif - gettimeofday(&now_ms, NULL); + get_current_time(now); } - get_current_time(now); } #ifdef HAVE_SELECT @@ -136,6 +136,7 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns if (flush) ioctl(sp->handle.fd, NIOCTXSYNC, NULL); /* flush TX buffer */ #endif + get_current_time(now_ns); } #endif /* HAVE_SELECT */ @@ -152,5 +153,3 @@ select_sleep(sendpacket_t *sp _U_, struct timespec *nap, struct timespec *now_ns void ioport_sleep_init(void); void ioport_sleep(sendpacket_t *sp _U_, const struct timespec *nap, struct timespec *now, bool flush); - -#endif /* __SLEEP_H__ */ diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 319ae109..453b92d7 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -166,6 +166,7 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->loop = OPT_VALUE_LOOP; options->loopdelay_ms = OPT_VALUE_LOOPDELAY_MS; + options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; if (HAVE_OPT(LIMIT)) options->limit_send = OPT_VALUE_LIMIT; @@ -314,7 +315,6 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->accurate = accurate_gtod; } else if (strcmp(OPT_ARG(TIMER), "nano") == 0) { options->accurate = accurate_nanosleep; - options->loopdelay_ns = OPT_VALUE_LOOPDELAY_NS; } else if (strcmp(OPT_ARG(TIMER), "abstime") == 0) { tcpreplay_seterr(ctx, "%s", "abstime is deprecated"); ret = -1; @@ -1120,6 +1120,7 @@ tcpreplay_prepare(tcpreplay_t *ctx) return ret; } +static bool apply_loop_delay(tcpreplay_t *ctx); /** * \brief sends the traffic out the interfaces * @@ -1167,12 +1168,12 @@ tcpreplay_replay(tcpreplay_t *ctx) loop, total_loops, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) { + if ((rcode = tcpr_replay_index(ctx)) < 0) return rcode; - } if (ctx->options->loop > 0) { - apply_loop_delay(ctx); - get_current_time(&ctx->stats.end_time); + if (apply_loop_delay(ctx)) + get_current_time(&ctx->stats.end_time); + if (ctx->options->stats == 0) { packet_stats(&ctx->stats); } @@ -1195,11 +1196,12 @@ tcpreplay_replay(tcpreplay_t *ctx) printf("Loop " COUNTER_SPEC " (" COUNTER_SPEC " unique)...\n", loop, ctx->unique_iteration); } - if ((rcode = tcpr_replay_index(ctx)) < 0) { + if ((rcode = tcpr_replay_index(ctx)) < 0) return rcode; - } - apply_loop_delay(ctx); - get_current_time(&ctx->stats.end_time); + + if (apply_loop_delay(ctx)) + get_current_time(&ctx->stats.end_time); + if (ctx->options->stats == 0 && !ctx->abort) packet_stats(&ctx->stats); } @@ -1381,19 +1383,20 @@ int tcpreplay_get_flow_expiry(tcpreplay_t *ctx) return ctx->options->flow_expiry; } -void -apply_loop_delay(tcpreplay_t *ctx) -{ - if (ctx->options->accurate == accurate_nanosleep) { - if (!ctx->abort && ctx->options->loopdelay_ns > 0) { - struct timespec nap; - nap.tv_sec = 0; - nap.tv_nsec = ctx->options->loopdelay_ns; - nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); - } - } else { - if (!ctx->abort && ctx->options->loopdelay_ms > 0) { - usleep(ctx->options->loopdelay_ms * 1000); - } +bool +apply_loop_delay(tcpreplay_t *ctx) { + if (!ctx->abort && ctx->options->loopdelay_ms > 0) { + usleep(ctx->options->loopdelay_ms * 1000); + return true; + } + + if (!ctx->abort && ctx->options->loopdelay_ns > 0) { + struct timespec nap; + nap.tv_sec = 0; + nap.tv_nsec = ctx->options->loopdelay_ns; + nanosleep_sleep(NULL, &nap, &ctx->stats.end_time, NULL); + return true; } + + return false; } diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 321c8f18..6cff96b7 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -272,7 +272,6 @@ int tcpreplay_set_verbose(tcpreplay_t *, bool); int tcpreplay_set_tcpdump_args(tcpreplay_t *, char *); int tcpreplay_set_tcpdump(tcpreplay_t *, tcpdump_t *); -void apply_loop_delay(tcpreplay_t *ctx); /* * These functions are seen by the outside world, but nobody should ever use them * outside of internal tcpreplay API functions diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a17dff73..e3536fde 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -320,6 +320,7 @@ flag = { flag = { name = loopdelay-ms; + flags-cant = loopdelay-ns; flags-must = loop; arg-type = number; arg-range = "0->"; @@ -330,7 +331,8 @@ flag = { flag = { name = loopdelay-ns; - flags-must = loop, timer; + flags-cant = loopdelay-ms; + flags-must = loop; arg-type = number; arg-range = "0->"; descrip = "Delay between loops in nanoseconds"; From 6b5131d654664b0c4b35a0ead50b29983a020b31 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 26 Dec 2023 13:09:26 -0800 Subject: [PATCH 27/69] Bug #837 - update CHANGELOG and CREDIT downstream PR #832 from Chuck Cottrill --- docs/CHANGELOG | 1 + docs/CREDIT | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 331e4de3..99e0d31b 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 09/03/2023 Version 4.5.0-beta1 + - handle IPv6 fragment extension header (#832 #837) - low PPS values run at full speed after several days (#779) - create DLT_LINUX_SLL2 plugin (#727) diff --git a/docs/CREDIT b/docs/CREDIT index 38e200f6..af043fde 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -116,3 +116,5 @@ David Guti Bastian Triller - Linux SLL2 + +Chuck Cottrill \ No newline at end of file From 3f1a891044525a1c9443f3952ab532c58f88a17e Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 26 Dec 2023 13:22:15 -0800 Subject: [PATCH 28/69] Feature #796 - Nano timer test no longer requires --timer --- test/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Makefile.am b/test/Makefile.am index d1925e19..34be770e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -347,7 +347,7 @@ replay_basic: replay_nano_timer: $(PRINTF) "%s" "[tcpreplay] Nano timer test: " $(PRINTF) "%s\n" "*** [tcpreplay] Nano timer test: " >> test.log - $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --timer=nano --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --loop=2 --loopdelay-ns=125000000 -t $(TEST_PCAP) >> test.log 2>&1 if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi replay_cache: From 8b16b9116b6b4b05d8f4b9786d435d77b2cf10a1 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Fri, 29 Dec 2023 10:59:51 -0800 Subject: [PATCH 29/69] Feature #839 - add pull request template --- .github/PULL_REQUEST_TEMPLATE.md | 20 ++++++++++++++++++++ docs/CHANGELOG | 2 ++ docs/CREDIT | 4 ++++ 3 files changed, 26 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..5c94caeb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +## Standards checklist: + + + +- [ ] The PR title is descriptive. +- [ ] The PR doesn't replicate another PR which is already open. +- [ ] I have read the contribution guide and followed all the instructions. +- [ ] The code follows the code style guide detailed in the wiki. +- [ ] The code is mine or it's from somewhere with an MIT-compatible license. +- [ ] The code is efficient, to the best of my ability, and does not waste computer resources. +- [ ] The code is stable and I have tested it myself, to the best of my abilities. +- [ ] If the code introduces new aliases, I provide a valid use case for all plugin users down below. + +## Changes: + +- [...] + +## Other comments: + +... diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 0042a65d..8aaa393b 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,5 +1,7 @@ 09/03/2023 Version 4.5.0-beta1 + - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) + - configure.ac: unify search dirs for pcap and add lib32 (#819) - 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 20953c5c..2eacb2c4 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -123,3 +123,7 @@ GithHub @plangarbalint Chuck Cottrill - handle IPv6 fragment extensions + + +Martin 'JaMa' Jansa + - configure.ac: unify search dirs for pcap and add lib32 From a0b6d847f468217b8fd675f788e6eca74a21a90c Mon Sep 17 00:00:00 2001 From: Gabriel Ganne Date: Sun, 21 Jan 2024 09:16:38 +0100 Subject: [PATCH 30/69] add check for empty cidr This causes tcprewrite to exit with an error instead of crashing. Fixes: #824 Signed-off-by: Gabriel Ganne --- src/common/cidr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/cidr.c b/src/common/cidr.c index 687fd04b..9afbfecb 100644 --- a/src/common/cidr.c +++ b/src/common/cidr.c @@ -249,6 +249,10 @@ parse_cidr(tcpr_cidr_t **cidrdata, char *cidrin, char *delim) char *network; char *token = NULL; + if (cidrin == NULL) { + errx(-1, "%s", "Unable to parse empty CIDR"); + } + mask_cidr6(&cidrin, delim); /* first iteration of input using strtok */ From 52ed63329b37ae83cb86504db2c9deb6a91e2fe9 Mon Sep 17 00:00:00 2001 From: Gabriel Ganne Date: Sun, 21 Jan 2024 08:59:10 +0100 Subject: [PATCH 31/69] ipv6 - add check for extension header length Fixes #827 Signed-off-by: Gabriel Ganne --- src/common/get.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/common/get.c b/src/common/get.c index 2d911160..18a9d7bd 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -663,6 +663,10 @@ get_ipv6_next(struct tcpr_ipv6_ext_hdr_base *exthdr, const u_char *end_ptr) case TCPR_IPV6_NH_HBH: case TCPR_IPV6_NH_AH: extlen = IPV6_EXTLEN_TO_BYTES(exthdr->ip_len); + if (extlen == 0) { + dbg(3, "Malformed IPv6 extension header..."); + return NULL; + } dbgx(3, "Looks like we're an ext header (0x%hhx). Jumping %u bytes" " to the next", From d6ac779170bc6ed8ba6900401fd858811287a4a2 Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 31 Jan 2024 17:16:43 -0500 Subject: [PATCH 32/69] add fixhdrlen option to turn on/off changing packet header length --- src/tcpedit/edit_packet.c | 9 +++++++-- src/tcpedit/parse_args.c | 8 ++++++++ src/tcpedit/tcpedit.c | 21 ++++++++++++++------- src/tcpedit/tcpedit_api.c | 11 +++++++++++ src/tcpedit/tcpedit_api.h | 1 + src/tcpedit/tcpedit_opts.def | 14 ++++++++++++++ src/tcpedit/tcpedit_types.h | 3 +++ src/tcpreplay_api.h | 3 +++ 8 files changed, 61 insertions(+), 9 deletions(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index 401fdd73..c4b54819 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -371,13 +371,17 @@ int fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr, int ip_len = (int)ntohs(ip_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len); - if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) + if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) { + ip_hdr->ip_len = htons(ip_len_want); return -1; + } if ((htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0 && ip_len != ip_len_want) { +// here is problem? ip_hdr->ip_len = htons(ip_len_want); return 1; +// return 0; } return 0; @@ -389,8 +393,9 @@ int fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr, int ip_len = ntohs((uint16_t)ip6_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len - sizeof(*ip6_hdr)); - if (pkthdr->caplen < l2len + sizeof(*ip6_hdr)) + if (pkthdr->caplen < l2len + sizeof(*ip6_hdr)) { return -1; + } if (ip_len != ip_len_want) { ip6_hdr->ip_len = htons((uint16_t)ip_len_want); diff --git a/src/tcpedit/parse_args.c b/src/tcpedit/parse_args.c index ab012b3b..233b3582 100644 --- a/src/tcpedit/parse_args.c +++ b/src/tcpedit/parse_args.c @@ -102,6 +102,14 @@ tcpedit_post_args(tcpedit_t *tcpedit) { if (HAVE_OPT(FIXCSUM)) tcpedit->fixcsum = true; + /* --fixhdrlen */ + if (HAVE_OPT(FIXHDRLEN)) { + tcpedit->fixhdrlen = true; + } + else { + tcpedit->fixhdrlen = false; + } + /* --efcs */ if (HAVE_OPT(EFCS)) tcpedit->efcs = true; diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index d5463959..206a1b65 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -326,7 +326,6 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, } } - /* do we need to spoof the src/dst IP address in IPv4 or ARP? */ if (tcpedit->seed) { /* IPv4 Packets */ @@ -356,12 +355,20 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, } } - /* ensure IP header length is correct */ - if (ip_hdr != NULL) { - needtorecalc |= fix_ipv4_length(*pkthdr, ip_hdr, l2len); - needtorecalc = 1; - } else if (ip6_hdr != NULL) { - needtorecalc |= fix_ipv6_length(*pkthdr, ip6_hdr, l2len); + /* fixhdrlen option ensure IP header length is correct */ + /* do we need to fix checksums? -- must always do this last! */ + if (tcpedit->fixhdrlen) { + int changed = 0; + if (ip_hdr != NULL) { + changed = fix_ipv4_length(*pkthdr, ip_hdr, l2len); + } else if (ip6_hdr != NULL) { + changed |= fix_ipv6_length(*pkthdr, ip6_hdr, l2len); + } + /* did the packet change? then needtorecalc checksum */ + if (changed > 0) { + needtorecalc += changed; + // needtorecalc = 1; + } } /* do we need to fix checksums? -- must always do this last! */ diff --git a/src/tcpedit/tcpedit_api.c b/src/tcpedit/tcpedit_api.c index a9509762..05fca102 100644 --- a/src/tcpedit/tcpedit_api.c +++ b/src/tcpedit/tcpedit_api.c @@ -140,6 +140,17 @@ tcpedit_set_fixcsum(tcpedit_t *tcpedit, bool value) return TCPEDIT_OK; } +/** + * \brief should we fix(?) header length? + */ +int +tcpedit_set_fixhdrlen(tcpedit_t *tcpedit, bool value) +{ + assert(tcpedit); + tcpedit->fixhdrlen = value; + return TCPEDIT_OK; +} + /** * \brief should we remove the EFCS from the frame? */ diff --git a/src/tcpedit/tcpedit_api.h b/src/tcpedit/tcpedit_api.h index 1a3a1ec7..8c92e1f5 100644 --- a/src/tcpedit/tcpedit_api.h +++ b/src/tcpedit/tcpedit_api.h @@ -42,6 +42,7 @@ int tcpedit_set_encoder_dltplugin_byname(tcpedit_t *, const char *); int tcpedit_set_skip_broadcast(tcpedit_t *, bool); int tcpedit_set_fixlen(tcpedit_t *, tcpedit_fixlen); int tcpedit_set_fixcsum(tcpedit_t *, bool); +int tcpedit_set_fixhdrlen(tcpedit_t *, bool); int tcpedit_set_efcs(tcpedit_t *, bool); int tcpedit_set_ttl_mode(tcpedit_t *, tcpedit_ttl_mode); int tcpedit_set_ttl_value(tcpedit_t *, uint8_t); diff --git a/src/tcpedit/tcpedit_opts.def b/src/tcpedit/tcpedit_opts.def index 3f8ee287..2597202c 100644 --- a/src/tcpedit/tcpedit_opts.def +++ b/src/tcpedit/tcpedit_opts.def @@ -170,6 +170,20 @@ fixed. Automatically enabled for packets modified with @samp{--seed}, EOText; }; +// CHUCK +flag = { + name = fixhdrlen; + // value = C; + descrip = "fix IP/TCP header len"; + doc = <<- EOText +By default, tcpreplay will send packets with the original packet length, +However, you may want the packet length revised to minimum packet size. +Using this option, tcpreplay will rewrite (fix) the packet length, +and recalculate checksums. +Caution: undesired packet changes may occur when this option is specified. +EOText; +}; + flag = { name = mtu; value = m; diff --git a/src/tcpedit/tcpedit_types.h b/src/tcpedit/tcpedit_types.h index 19beb3cc..028ac4cb 100644 --- a/src/tcpedit/tcpedit_types.h +++ b/src/tcpedit/tcpedit_types.h @@ -161,6 +161,9 @@ typedef struct { uint32_t fuzz_seed; uint32_t fuzz_factor; + + /* fix header length */ + bool fixhdrlen; } tcpedit_t; diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index 7c4e94e9..e3412038 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -190,6 +190,9 @@ typedef struct tcpreplay_s { uint32_t skip_packets; bool first_time; + /* fix header length */ + bool fixhdrlen; + /* counter stats */ tcpreplay_stats_t stats; tcpreplay_stats_t static_stats; /* stats returned by tcpreplay_get_stats() */ From 69698236e1322e6546576337b120006e2ef84bb1 Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 31 Jan 2024 18:04:54 -0500 Subject: [PATCH 33/69] resolve merge conflict --- src/tcpedit/edit_packet.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index c4b54819..7de29fda 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -371,17 +371,15 @@ int fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr, int ip_len = (int)ntohs(ip_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len); + pkthdr->caplen, l2len, sizeof(*ip_hdr)); if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) { ip_hdr->ip_len = htons(ip_len_want); return -1; } - if ((htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0 && - ip_len != ip_len_want) { -// here is problem? + if ((htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0 && ip_len != ip_len_want) { ip_hdr->ip_len = htons(ip_len_want); return 1; -// return 0; } return 0; From 5811d31224134e1e76233dfde2031fa069ab5d6e Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 31 Jan 2024 18:10:08 -0500 Subject: [PATCH 34/69] resolve merge conflict --- src/tcpedit/tcpedit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index 206a1b65..b6707248 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -360,14 +360,14 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, if (tcpedit->fixhdrlen) { int changed = 0; if (ip_hdr != NULL) { - changed = fix_ipv4_length(*pkthdr, ip_hdr, l2len); + fix_ipv4_length(*pkthdr, ip_hdr, l2len); + changed = 1; } else if (ip6_hdr != NULL) { - changed |= fix_ipv6_length(*pkthdr, ip6_hdr, l2len); + changed = fix_ipv6_length(*pkthdr, ip6_hdr, l2len); } /* did the packet change? then needtorecalc checksum */ if (changed > 0) { - needtorecalc += changed; - // needtorecalc = 1; + needtorecalc |= changed; } } From fa37e0a00bb40104803f45b31696dedd20f1f2aa Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 31 Jan 2024 18:30:53 -0500 Subject: [PATCH 35/69] remove stray debug line --- src/tcpedit/edit_packet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index 7de29fda..95282eb5 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -371,7 +371,6 @@ int fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr, int ip_len = (int)ntohs(ip_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len); - pkthdr->caplen, l2len, sizeof(*ip_hdr)); if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) { ip_hdr->ip_len = htons(ip_len_want); return -1; From 303cd76b90338030d08e8888bcb8ccfd947971dd Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Wed, 31 Jan 2024 22:28:49 -0500 Subject: [PATCH 36/69] remove comment --- src/tcpedit/tcpedit_opts.def | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tcpedit/tcpedit_opts.def b/src/tcpedit/tcpedit_opts.def index 2597202c..c2fdd340 100644 --- a/src/tcpedit/tcpedit_opts.def +++ b/src/tcpedit/tcpedit_opts.def @@ -170,7 +170,6 @@ fixed. Automatically enabled for packets modified with @samp{--seed}, EOText; }; -// CHUCK flag = { name = fixhdrlen; // value = C; From 35aebe8076c6e4c28ac76465746e2a2504c8f6f3 Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Thu, 8 Feb 2024 22:05:25 -0500 Subject: [PATCH 37/69] changes to address PR comment s --- src/tcpedit/edit_packet.c | 3 +-- src/tcpedit/parse_args.c | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index 68634d68..caefbbcc 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -381,9 +381,8 @@ fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr, size_t l2len) int ip_len = ntohs((uint16_t)ip6_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len - sizeof(*ip6_hdr)); - if (pkthdr->caplen < l2len + sizeof(*ip6_hdr)) { + if (pkthdr->caplen < l2len + sizeof(*ip6_hdr)) return -1; - } if (ip_len != ip_len_want) { ip6_hdr->ip_len = htons((uint16_t)ip_len_want); diff --git a/src/tcpedit/parse_args.c b/src/tcpedit/parse_args.c index b18cb410..99a6583a 100644 --- a/src/tcpedit/parse_args.c +++ b/src/tcpedit/parse_args.c @@ -96,12 +96,10 @@ tcpedit_post_args(tcpedit_t *tcpedit) tcpedit->fixcsum = true; /* --fixhdrlen */ - if (HAVE_OPT(FIXHDRLEN)) { + if (HAVE_OPT(FIXHDRLEN)) tcpedit->fixhdrlen = true; - } - else { + else tcpedit->fixhdrlen = false; - } /* --efcs */ if (HAVE_OPT(EFCS)) From 4bbfd0c9737b257ed2b49385546aee7a54334eba Mon Sep 17 00:00:00 2001 From: Charles Cottrill Date: Tue, 20 Feb 2024 11:14:36 -0500 Subject: [PATCH 38/69] changes from PR review --- src/tcpedit/edit_packet.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index caefbbcc..1025ff90 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -362,10 +362,8 @@ fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr, size_t l2len) int ip_len = (int)ntohs(ip_hdr->ip_len); int ip_len_want = (int)(pkthdr->len - l2len); - if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) { - ip_hdr->ip_len = htons(ip_len_want); + if (pkthdr->caplen < l2len + sizeof(*ip_hdr)) return -1; - } if ((htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0 && ip_len != ip_len_want) { ip_hdr->ip_len = htons(ip_len_want); From 718049269ee3ec8bfa012ee48ad5b5a7be7dfb92 Mon Sep 17 00:00:00 2001 From: Denis Ovsienko Date: Wed, 21 Feb 2024 12:20:22 +0000 Subject: [PATCH 39/69] Fix the Autoconf test for inet_addr(). --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 387219de..052938ff 100644 --- a/configure.ac +++ b/configure.ac @@ -627,7 +627,7 @@ AC_CHECK_FUNC(inet_addr, inet_addr=yes, inet_addr=no) -if test x$inet_addr = no ; then +if test x$inet_addr = xno ; then AC_MSG_ERROR([We need inet_addr. See bug 26]) fi From 1819eb7f17f6dd87b2b8871bbeb7b580cad62088 Mon Sep 17 00:00:00 2001 From: Denis Ovsienko Date: Wed, 21 Feb 2024 12:21:44 +0000 Subject: [PATCH 40/69] Fix some build issues on Haiku. Link with two additional libraries if required. Do not include , which does not exist, and which the source does not require to compile. These changes allow tcpreplay to build on Haiku hrev57588 as follows: ./configure --with-libpcap=/path/to/libpcap/ --enable-static-link However, since Haiku ports do not have GNU AutoGen, the master branch would need some files either copied from the 4.4.4 release tarball or generated on a Linux host. Even after the build completes, tcpreplay is still not entirely functional because libpcap on Haiku does not implement pcap_inject() and tcpreplay fails to handle Haiku interfaces correctly: > ./src/tcpreplay --listnics Available network interfaces: /dev/net/ipro1000/1 vale:/dev/net/ipro1000/1 netmap:/dev/net/ipro1000/1 /dev/net/ipro1000/0 vale:/dev/net/ipro1000/0 netmap:/dev/net/ipro1000/0 > ./src/tcpreplay -i /dev/net/ipro1000/1 icmp-echoreply.pcap Fatal Error: failed to open device /dev/net/ipro1000/1: error opening khial device: Device/File/Resource busy So proper Haiku support would require some more work. --- configure.ac | 35 ++++++++++++++++++++++++++++++++--- src/common/sendpacket.h | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 052938ff..c8198d69 100644 --- a/configure.ac +++ b/configure.ac @@ -605,23 +605,49 @@ else fi dnl Check for inet_aton and inet_pton +dnl On Haiku these functions are in libnetwork. AC_CHECK_FUNC(inet_aton, AC_DEFINE([HAVE_INET_ATON], [1], [Do we have inet_aton?]) inet_aton=yes, - inet_aton=no) + [ + AC_CHECK_LIB(network, inet_aton, + AC_DEFINE([HAVE_INET_ATON], [1], [Do we have inet_aton?]) + use_libnetwork=yes, + [inet_aton=no] + ) + ] +) AC_CHECK_FUNC(inet_pton, AC_DEFINE([HAVE_INET_PTON], [1], [Do we have inet_pton?]) inet_pton=yes, - inet_pton=no) + [ + AC_CHECK_LIB(network, inet_pton, + AC_DEFINE([HAVE_INET_PTON], [1], [Do we have inet_pton?]) + use_libnetwork=yes, + [inet_pton=no] + ) + ] +) AC_CHECK_FUNC(inet_ntop, AC_DEFINE([HAVE_INET_NTOP], [1], [Do we have inet_ntop?]) inet_ntop=yes, - inet_ntop=no) + [ + AC_CHECK_LIB(network, inet_ntop, + AC_DEFINE([HAVE_INET_NTOP], [1], [Do we have inet_ntop?]) + use_libnetwork=yes, + [inet_ntop=no] + ) + ] +) if test "$inet_ntop" = "no" -a "$inet_pton" = "no" ; then AC_MSG_ERROR([We need either inet_ntop or inet_pton]) fi +if test "$use_libnetwork" = "yes" ; then + LIBS="-lnetwork $LIBS" +fi + AC_CHECK_FUNC(inet_addr, AC_DEFINE([HAVE_INET_ADDR], [1], [Do we have inet_addr?]) inet_addr=yes, @@ -631,6 +657,9 @@ if test x$inet_addr = xno ; then AC_MSG_ERROR([We need inet_addr. See bug 26]) fi +dnl On Haiku fts_*() functions are in libbsd. +AC_CHECK_FUNC(fts_read,,[AC_CHECK_LIB(bsd, fts_read)]) + dnl ##################################################### dnl Checks for tuntap device support dnl ##################################################### diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index e1dd6a42..44ff1a2c 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -26,7 +26,7 @@ #ifdef __NetBSD__ #include -#else +#elif ! defined(__HAIKU__) #include #endif From 42f7bbc1ce4913fe2c0bc76293c5445d31690f5d Mon Sep 17 00:00:00 2001 From: Chen Qi Date: Thu, 7 Mar 2024 21:02:07 -0800 Subject: [PATCH 41/69] configure.ac: do not run conftest in case of cross compilation It'll give us nothing but error like below: ./conftest: cannot execute binary file: Exec format error ... ./configure: line 23950: test: -eq: unary operator expected The version check only has effect on Apple systems. We'd better avoid error like above when cross compilation. Also, in case of cross compilation, instead of having the above Exec format error and resulting in unaligned_cv_fail to yes, set it directly to yes. Signed-off-by: Chen Qi --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 387219de..15201601 100644 --- a/configure.ac +++ b/configure.ac @@ -928,7 +928,7 @@ cat >conftest.c </dev/null 2>&1 -if test -x conftest ; then +if test -x conftest -a "$cross_compiling" != "yes"; then full_libpcap_version=$(LD_LIBRARY_PATH="$LPCAP_LD_LIBRARY_PATH" ./conftest) libpcap_version=$(echo "$full_libpcap_version" | ${CUT} -d' ' -f3) pcap_version_ok=yes @@ -1709,7 +1709,7 @@ case "$host_os" in EOF ${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS \ conftest.c $LIBS >/dev/null 2>&1 - if test ! -x conftest ; then + if test ! -x conftest -o "$cross_compiling" = "yes" ; then dnl failed to compile for some reason unaligned_cv_fail=yes else From 62bc10d4f1d2c9e2833ef2898fb0170e9300a9dd Mon Sep 17 00:00:00 2001 From: Marsman1996 Date: Tue, 2 Apr 2024 17:29:21 +0800 Subject: [PATCH 42/69] dlt_jnpr_ether_cleanup: check config before cleanup --- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c index c53ec297..9642a2c2 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c @@ -164,8 +164,9 @@ dlt_jnpr_ether_cleanup(tcpeditdlt_t *ctx) jnpr_ether_config_t *config; config = (jnpr_ether_config_t *)ctx->encoder->config; - if (config->subctx != NULL) + if (config != NULL && config->subctx != NULL) { tcpedit_dlt_cleanup(config->subctx); + } safe_free(plugin->config); plugin->config = NULL; plugin->config_size = 0; From d00951bca056cd50c3e85d1ce8a66ae68be15710 Mon Sep 17 00:00:00 2001 From: Gabriel Ganne Date: Sun, 19 May 2024 09:46:41 +0200 Subject: [PATCH 43/69] Fix recursive tcpedit cleanup Assume a single tcpedit struct and return the previously allocated context. This fixes an issue with the Juniper Encapsulated Ethernet DLT plugin which has an exception in the way the plugins works with regard to the extra buffer in question: tcpreplay works with the assumption that there only ever is a single link layer plugin which is mostly true except here: Juniper has a special call to tcpedit_dlt_copy_decoder_state() which causes the ctx and subctx to share a reference to the decoded_extra buffer, and a double free. Fixes: #813 #850 --- src/tcpedit/plugins/dlt_plugins.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tcpedit/plugins/dlt_plugins.c b/src/tcpedit/plugins/dlt_plugins.c index 51585378..1dc52ca1 100644 --- a/src/tcpedit/plugins/dlt_plugins.c +++ b/src/tcpedit/plugins/dlt_plugins.c @@ -94,6 +94,12 @@ const char *tcpeditdlt_bit_info[] = {"Missing required Layer 3 protocol.", * Public functions ********************************************************************/ +/* + * Ensure init/cleanup are called only once + * Assume a single tcpedit struct and return the previously allocated context. + */ +static int tcpedit_dlt_is_initialized = 0; + /** * initialize our plugin library. Pass the DLT of the source pcap handle. * Actions: @@ -115,6 +121,9 @@ tcpedit_dlt_init(tcpedit_t *tcpedit, const int srcdlt) assert(tcpedit); assert(srcdlt >= 0); + if (tcpedit_dlt_is_initialized++ > 0) + return tcpedit->dlt_ctx; + ctx = (tcpeditdlt_t *)safe_malloc(sizeof(tcpeditdlt_t)); /* do we need a side buffer for L3 data? */ @@ -443,6 +452,9 @@ tcpedit_dlt_cleanup(tcpeditdlt_t *ctx) { tcpeditdlt_plugin_t *plugin; + if (--tcpedit_dlt_is_initialized <= 0) + return; + assert(ctx); plugin = ctx->plugins; From 70e28a6f0490df0f25f415a05c52525521fc6bab Mon Sep 17 00:00:00 2001 From: Gabriel Ganne Date: Sun, 19 May 2024 10:04:18 +0200 Subject: [PATCH 44/69] autotools - AC_HELP_STRING is obsolete in 2.70 Autoconf flagged AC_HELP_STRING as obsolete [1] Replace with AS_HELP_STRING [2] which was introduced in 2.69 which is the current PREREQ. [1] https://www.gnu.org/software/autoconf//manual/autoconf-2.69/html_node/Obsolete-Macros.html#Obsolete-Macros [2] https://www.gnu.org/software/autoconf//manual/autoconf-2.69/html_node/Pretty-Help-Strings.html#AS%5fHELP%5fSTRING --- libopts/m4/libopts.m4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libopts/m4/libopts.m4 b/libopts/m4/libopts.m4 index 23738cab..3c271b1e 100644 --- a/libopts/m4/libopts.m4 +++ b/libopts/m4/libopts.m4 @@ -478,7 +478,7 @@ AC_DEFUN([LIBOPTS_CHECK_COMMON],[ m4_pushdef([AO_Libopts_Dir], [ifelse($1, , [libopts], [$1])]) AC_ARG_ENABLE([local-libopts], - AC_HELP_STRING([--enable-local-libopts], + AS_HELP_STRING([--enable-local-libopts], [Use the supplied libopts tearoff code]),[ if test x$enableval = xyes ; then AC_MSG_NOTICE([Using supplied libopts tearoff]) @@ -488,14 +488,14 @@ AC_DEFUN([LIBOPTS_CHECK_COMMON],[ fi]) AC_ARG_ENABLE([libopts-install], - AC_HELP_STRING([--enable-libopts-install], + AS_HELP_STRING([--enable-libopts-install], [Install libopts with client installation])) AM_CONDITIONAL([INSTALL_LIBOPTS],[test "X${enable_libopts_install}" = Xyes]) [if test -z "${NEED_LIBOPTS_DIR}" ; then] AC_MSG_CHECKING([whether autoopts-config can be found]) AC_ARG_WITH([autoopts-config], - AC_HELP_STRING([--with-autoopts-config], + AS_HELP_STRING([--with-autoopts-config], [specify the config-info script]), [lo_cv_with_autoopts_config=${with_autoopts_config}], AC_CACHE_CHECK([whether autoopts-config is specified], From 5b5644356693f5c68dd4295e86f24f1d0a515d60 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 11:46:10 -0700 Subject: [PATCH 45/69] Bug #827 PR# 842: add check for IPv6 extension header length --- docs/CHANGELOG | 3 ++- src/common/get.c | 29 +++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 8aaa393b..71483a8a 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ -09/03/2023 Version 4.5.0-beta1 +06/01/2024 Version 4.5.0-beta1 + - add check for IPv6 extension header length (#827 #842) - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) - configure.ac: unify search dirs for pcap and add lib32 (#819) diff --git a/src/common/get.c b/src/common/get.c index 0adbb1ea..1de06780 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -41,8 +41,8 @@ extern const char pcap_version[]; static void *get_ipv6_next(struct tcpr_ipv6_ext_hdr_base *exthdr, const u_char *end_ptr); /** - * Depending on what version of libpcap/WinPcap there are different ways to get - * the version of the libpcap/WinPcap library. This presents a unified way to + * Depending on what version of libpcap there are different ways to get + * the version of the libpcap library. This presents a unified way to * get that information. */ const char * @@ -196,8 +196,15 @@ parse_metadata(const u_char *pktdata, uint32_t *vlan_offset) { bool done = false; - int res = 0; - while (!done && res == 0) { + assert(next_protocol); + assert(l2len); + assert(l2offset); + assert(vlan_offset); + + if (!pktdata || !datalen) + errx(-1, "parse_metadata: invalid L2 parameters: pktdata=0x%p len=%d", pktdata, datalen); + + while (!done) { switch (*next_protocol) { case ETHERTYPE_VLAN: case ETHERTYPE_Q_IN_Q: @@ -205,18 +212,22 @@ parse_metadata(const u_char *pktdata, if (*vlan_offset == 0) *vlan_offset = *l2len; - res = parse_vlan(pktdata, datalen, next_protocol, l2len); + if (parse_vlan(pktdata, datalen, next_protocol, l2len)) + return -1; + break; case ETHERTYPE_MPLS: case ETHERTYPE_MPLS_MULTI: - res = parse_mpls(pktdata, datalen, next_protocol, l2len, l2offset); + if (parse_mpls(pktdata, datalen, next_protocol, l2len, l2offset)) + return -1; + break; default: done = true; } } - return res; + return 0; } /* @@ -629,9 +640,11 @@ get_layer4_v6(const ipv6_hdr_t *ip6_hdr, const u_char *end_ptr) * no further processing, either TCP, UDP, ICMP, etc... */ default: - if (proto != ip6_hdr->ip_nh) { + if (proto != ip6_hdr->ip_nh && next) { dbgx(3, "Returning byte offset of this ext header: %u", IPV6_EXTLEN_TO_BYTES(next->ip_len)); next = (void *)((u_char *)next + IPV6_EXTLEN_TO_BYTES(next->ip_len)); + if ((u_char*)next > end_ptr) + return NULL; } else { dbgx(3, "%s", "Returning end of IPv6 Header"); } From ef3df29f410e191e20214535ebeb8660266511a3 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 12:26:44 -0700 Subject: [PATCH 46/69] Bug #824 PR# 843: add check empty CIDR --- .github/workflows/c-linter.yml | 5 ++++- docs/CHANGELOG | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/c-linter.yml b/.github/workflows/c-linter.yml index 30d9293f..e0395c6e 100644 --- a/.github/workflows/c-linter.yml +++ b/.github/workflows/c-linter.yml @@ -27,5 +27,8 @@ jobs: if: steps.linter.outputs.checks-failed > 0 # for testing... # run: echo "Some files failed the linting checks!" + # for development... + run: exit 0 # exit the job with a success status # for actual deployment... - run: exit 1 + # run: exit 1 # exit the job with a failure status + diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 71483a8a..97d6d706 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,5 +1,6 @@ 06/01/2024 Version 4.5.0-beta1 - add check for IPv6 extension header length (#827 #842) + - add check for empty CIDR (#824 #843) - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) - configure.ac: unify search dirs for pcap and add lib32 (#819) From 859728baeda9d2e5771475d2c43ddf6c73296d81 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 13:31:41 -0700 Subject: [PATCH 47/69] Bug #844 PR# 846: --fixhdrlen option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added to control action on packet length changes Verification ``` ~/git/tcpreplay/build Bug_703_844_PR_846_optionally_fix_pkt_hdr_len* ⇡ ❯ src/tcprewrite --cachefile=pcaps/pcap.cache \ --infile=pcaps/cap-original-packet-3.pcap \ --outfile=pcaps/cap-4.5.0-packet-out.pcap \ --endpoints=10.200.1.1:10.200.1.2 Warning in ../../src/tcprewrite.c:post_args() line 231: pcaps/cap-original-packet-3.pcap was captured using a snaplen of 1514 bytes. This may mean you have truncated packets. ~/git/tcpreplay/build Bug_703_844_PR_846_optionally_fix_pkt_hdr_len* ⇡ ❯ diff pcaps/cap-4.4.0-packet-3.pcap pcaps/cap-4.5.0-packet-out.pcap ~/git/tcpreplay/build Bug_703_844_PR_846_optionally_fix_pkt_hdr_len* ⇡ ❯ ``` --- docs/CHANGELOG | 2 ++ docs/CREDIT | 3 ++- src/tcpedit/parse_args.c | 2 -- src/tcpedit/tcpedit.c | 3 +-- src/tcpedit/tcpedit_opts.def | 5 ++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 97d6d706..a05c2db2 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,6 @@ 06/01/2024 Version 4.5.0-beta1 + - --fixhdrlen option added to control action on packet length changes (#846) + - incorrect checksum for certain IPv4 packets - fixed by #846 (#844) - add check for IPv6 extension header length (#827 #842) - add check for empty CIDR (#824 #843) - GitHub template for pull requests (#839) diff --git a/docs/CREDIT b/docs/CREDIT index 2eacb2c4..f148e592 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -123,7 +123,8 @@ GithHub @plangarbalint Chuck Cottrill - handle IPv6 fragment extensions - + - --fixhdrlen option + - bug fixes Martin 'JaMa' Jansa - configure.ac: unify search dirs for pcap and add lib32 diff --git a/src/tcpedit/parse_args.c b/src/tcpedit/parse_args.c index 99a6583a..958d1711 100644 --- a/src/tcpedit/parse_args.c +++ b/src/tcpedit/parse_args.c @@ -98,8 +98,6 @@ tcpedit_post_args(tcpedit_t *tcpedit) /* --fixhdrlen */ if (HAVE_OPT(FIXHDRLEN)) tcpedit->fixhdrlen = true; - else - tcpedit->fixhdrlen = false; /* --efcs */ if (HAVE_OPT(EFCS)) diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index b1af7b05..160ec75f 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -321,8 +321,7 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, u_char **pktdata /* ensure IP header length is correct */ int changed = 0; if (ip_hdr != NULL) { - fix_ipv4_length(*pkthdr, ip_hdr, l2len); - changed = 1; + changed = fix_ipv4_length(*pkthdr, ip_hdr, l2len); } else if (ip6_hdr != NULL) { changed = fix_ipv6_length(*pkthdr, ip6_hdr, l2len); } diff --git a/src/tcpedit/tcpedit_opts.def b/src/tcpedit/tcpedit_opts.def index c2fdd340..a01523b1 100644 --- a/src/tcpedit/tcpedit_opts.def +++ b/src/tcpedit/tcpedit_opts.def @@ -172,13 +172,12 @@ EOText; flag = { name = fixhdrlen; - // value = C; - descrip = "fix IP/TCP header len"; + descrip = "Alter IP/TCP header len to match packet length"; doc = <<- EOText By default, tcpreplay will send packets with the original packet length, However, you may want the packet length revised to minimum packet size. Using this option, tcpreplay will rewrite (fix) the packet length, -and recalculate checksums. +and recalculate checksums when packet length changes. Caution: undesired packet changes may occur when this option is specified. EOText; }; From 4789deed99e5c78f4a3b9048d2799b7afb438ca5 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 16:49:08 -0700 Subject: [PATCH 48/69] Bug #844 PR# 846: reject invalid header length packets Triggers when attempting a checksum update but packet length does not match header length. Example: ``` tcprewrite -i test.pcap -o test2.rewrite_1ttl --ttl=58 Warning: skipping packet 11 because caplen 122 minus L2 length 22 does not equal IPv4 header length 255. Consider option '--fixhdrlen'. Warning: skipping packet 12 because caplen 114 minus L2 length 14 does not equal IPv4 header length 84. Consider option '--fixhdrlen'. Warning: skipping packet 14 because caplen 60 minus L2 length 14 does not equal IPv4 header length 28. Consider option '--fixhdrlen'. Warning: skipping packet 15 because caplen 60 minus L2 length 14 does not equal IPv4 header length 28. Consider option '--fixhdrlen'. Warning: skipping packet 16 because caplen 60 minus L2 length 14 does not equal IPv4 header length 28. Consider option '--fixhdrlen'. ``` --- src/tcpedit/edit_packet.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index 1025ff90..43ff04a8 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -78,12 +78,13 @@ fix_ipv4_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv4_hdr_t *i /* calc the L4 checksum if we have the whole packet && not a frag or first frag */ if (pkthdr->caplen == pkthdr->len && (htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0) { if (ip_len != (int)(pkthdr->caplen - l2len)) { - tcpedit_seterr(tcpedit, - "caplen minus L2 length %u does IPv4 header length %u: pkt=" COUNTER_SPEC, - pkthdr->caplen - l2len, - ip_len, - tcpedit->runtime.packetnum); - return TCPEDIT_ERROR; + tcpedit_setwarn(tcpedit, + "skipping packet " COUNTER_SPEC " because caplen %u minus L2 length %u does not equal IPv4 header length %u. Consider option '--fixhdrlen'.", + tcpedit->runtime.packetnum, + pkthdr->caplen, + l2len, + ip_len); + return TCPEDIT_WARN; } ret1 = do_checksum(tcpedit, (u_char *)ip_hdr, From 079cf31dcab6207b2ed93140e5e647542fd09815 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 19:34:01 -0700 Subject: [PATCH 49/69] Bug #844 PR# 846: backward compatibility for older libpcap Required to run tests on older bigendian machine. Also fix a usec to nsec conversion bug. --- configure.ac | 24 ++++++++++++++++++++++++ lib/sll.h | 3 ++- src/defines.h.in | 2 +- src/replay.c | 26 ++++++++++++++++++-------- test/Makefile.am | 42 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 86 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 13df91e0..70df1540 100644 --- a/configure.ac +++ b/configure.ac @@ -1194,6 +1194,30 @@ if test $have_pcap_snapshot = yes ; then [Does libpcap have pcap_snapshot?]) fi +have_pcap_open_offline_with_tstamp_precision=no +dnl Check to see if we've got pcap_open_offline_with_tstamp_precision() +AC_MSG_CHECKING(for pcap_open_offline_with_tstamp_precision support) +AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include +#include +#include +#include "$LPCAPINC" +]],[[ + pcap_t *pcap; + char ebuf[PCAP_ERRBUF_SIZE]; + pcap = pcap_open_offline_with_tstamp_precision("fake.pcap", PCAP_TSTAMP_PRECISION_NANO, &ebuf[0]); +]])],[ + have_pcap_open_offline_with_tstamp_precision=yes + AC_MSG_RESULT(yes) +], [ + have_pcap_open_offline_with_tstamp_precision=no + AC_MSG_RESULT(no) +]) + +if test $have_pcap_open_offline_with_tstamp_precision = yes ; then + AC_DEFINE([HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION], [1], [Does libpcap have pcap_open_offline_with_tstamp_precision?]) +fi + # Tcpbridge requires libpcap and pcap_sendpacket() enable_tcpbridge=no diff --git a/lib/sll.h b/lib/sll.h index 14966546..78e97fef 100644 --- a/lib/sll.h +++ b/lib/sll.h @@ -77,7 +77,8 @@ #ifndef _SLL_H_ #define _SLL_H_ -#include +//#include +#include /* * A DLT_LINUX_SLL fake link-layer header. diff --git a/src/defines.h.in b/src/defines.h.in index 1975f053..1a26132c 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -363,7 +363,7 @@ typedef u_int8_t uint8_t typedef u_int16_t uint16_t typedef u_int32_t uint32_t #define TIMEVAL_AS_TIMESPEC_SET(a, b) \ do { \ (a)->tv_sec = (b)->tv_sec; \ - (a)->tv_nsec = (b)->tv_usec; \ + (a)->tv_nsec = (b)->tv_usec * 1000; \ } while(0) /* diff --git a/src/replay.c b/src/replay.c index c01206d6..74b0ab68 100644 --- a/src/replay.c +++ b/src/replay.c @@ -98,6 +98,16 @@ tcpr_replay_index(tcpreplay_t *ctx) return rcode; } +static pcap_t * +tcpr_pcap_open(const char *path, char *ebuf) +{ +#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION + return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf); +#else + return pcap_open_offline(path, ebuf); +#endif +} + /** * \brief replay a pcap file out interface(s) * @@ -117,7 +127,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_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -133,7 +143,7 @@ replay_file(tcpreplay_t *ctx, int idx) } else { if (!ctx->options->file_cache[idx].cached) { - if ((pcap = pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -145,7 +155,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_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf)) == NULL) { + if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -214,26 +224,26 @@ replay_two_files(tcpreplay_t *ctx, int idx1, int idx2) /* read from first pcap file if we haven't cached things yet */ if (!ctx->options->preload_pcap) { - if ((pcap1 = pcap_open_offline(path1, ebuf)) == NULL) { + if ((pcap1 = tcpr_pcap_open(path1, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } ctx->options->file_cache[idx1].dlt = pcap_datalink(pcap1); - if ((pcap2 = pcap_open_offline(path2, ebuf)) == NULL) { + if ((pcap2 = tcpr_pcap_open(path2, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } ctx->options->file_cache[idx2].dlt = pcap_datalink(pcap2); } else { if (!ctx->options->file_cache[idx1].cached) { - if ((pcap1 = pcap_open_offline(path1, ebuf)) == NULL) { + if ((pcap1 = tcpr_pcap_open(path1, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } ctx->options->file_cache[idx1].dlt = pcap_datalink(pcap1); } if (!ctx->options->file_cache[idx2].cached) { - if ((pcap2 = pcap_open_offline(path2, ebuf)) == NULL) { + if ((pcap2 = tcpr_pcap_open(path2, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } @@ -293,7 +303,7 @@ replay_two_files(tcpreplay_t *ctx, int idx1, int idx2) if (ctx->options->verbose) { /* in cache mode, we may not have opened the file */ if (pcap1 == NULL) { - if ((pcap1 = pcap_open_offline(path1, ebuf)) == NULL) { + if ((pcap1 = tcpr_pcap_open(path1, ebuf)) == NULL) { tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf); return -1; } diff --git a/test/Makefile.am b/test/Makefile.am index 34be770e..5abaf63e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -125,6 +125,9 @@ standard_bigendian: $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_1ttl --ttl=58 $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_2ttl --ttl=+58 $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_3ttl --ttl=-58 + $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_1ttl-hdrfix --ttl=59 --fixhdrlen + $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_2ttl-hdrfix --ttl=+59 --fixhdrlen + $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_3ttl-hdrfix --ttl=-59 --fixhdrlen $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_mtutrunc --mtu-trunc --mtu=300 $(TCPREWRITE) -i $(TEST_PCAP) -o test.rewrite_l7fuzzing \ --fuzz-seed=42 --fuzz-factor=2 @@ -175,6 +178,9 @@ standard_littleendian: $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_1ttl --ttl=58 $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_2ttl --ttl=+58 $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_3ttl --ttl=-58 + $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_1ttl-hdrfix --ttl=59 --fixhdrlen + $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_2ttl-hdrfix --ttl=+59 --fixhdrlen + $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_3ttl-hdrfix --ttl=-59 --fixhdrlen $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_mtutrunc --mtu-trunc --mtu=300 $(TCPREWRITE) -i $(TEST_PCAP) -o test2.rewrite_l7fuzzing \ --fuzz-seed=42 --fuzz-factor=2 @@ -191,7 +197,8 @@ tcpprep: auto_router auto_bridge auto_client auto_server auto_first cidr regex \ tcprewrite: rewrite_portmap rewrite_range_portmap rewrite_endpoint \ rewrite_pnat rewrite_trunc rewrite_pad rewrite_seed rewrite_mac \ rewrite_layer2 rewrite_config rewrite_skip rewrite_dltuser rewrite_dlthdlc \ - rewrite_vlan802.1ad rewrite_vlandel rewrite_efcs rewrite_1ttl rewrite_2ttl rewrite_3ttl \ + rewrite_vlan802.1ad rewrite_vlandel rewrite_efcs \ + rewrite_1ttl rewrite_2ttl rewrite_3ttl rewrite_1ttl-hdrfix rewrite_2ttl-hdrfix rewrite_3ttl-hdrfix \ rewrite_tos rewrite_mtutrunc rewrite_enet_subsmac rewrite_mac_seed \ rewrite_mac_seed_keep rewrite_l7fuzzing rewrite_sequence rewrite_fixcsum \ rewrite_fixlen_pad rewrite_fixlen_trunc rewrite_fixlen_del @@ -638,6 +645,39 @@ else endif if [ $? ] ; then $(PRINTF) "\t\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi +rewrite_1ttl-hdrfix: + $(PRINTF) "%s" "[tcprewrite] Force TTL with header fix: " + $(PRINTF) "%s\n" "*** [tcprewrite] Force TTL header fix: " >> test.log + $(TCPREWRITE) $(ENABLE_DEBUG) -i $(TEST_PCAP) -o test.$@1 --ttl=59 --fixhdrlen >> test.log 2>&1 +if WORDS_BIGENDIAN + diff $(srcdir)/test.$@ test.$@1 >> test.log 2>&1 +else + diff $(srcdir)/test2.$@ test.$@1 >> test.log 2>&1 +endif + if [ $? ] ; then $(PRINTF) "\t%s\n" "FAILED"; else $(PRINTF) "\t%s\n" "OK"; fi + +rewrite_2ttl-hdrfix: + $(PRINTF) "%s" "[tcprewrite] Increase TTL with header fix:" + $(PRINTF) "%s\n" "*** [tcprewrite] Increase TTL with header fix: " >> test.log + $(TCPREWRITE) $(ENABLE_DEBUG) -i $(TEST_PCAP) -o test.$@1 --ttl=+59 --fixhdrlen >> test.log 2>&1 +if WORDS_BIGENDIAN + diff $(srcdir)/test.$@ test.$@1 >> test.log 2>&1 +else + diff $(srcdir)/test2.$@ test.$@1 >> test.log 2>&1 +endif + if [ $? ] ; then $(PRINTF) "\t%s\n" "FAILED"; else $(PRINTF) "\t%s\n" "OK"; fi + +rewrite_3ttl-hdrfix: + $(PRINTF) "%s" "[tcprewrite] Reduce TTL with header fix: " + $(PRINTF) "%s\n" "*** [tcprewrite] Reduce TTL with header fix: " >> test.log + $(TCPREWRITE) $(ENABLE_DEBUG) -i $(TEST_PCAP) -o test.$@1 --ttl=-59 --fixhdrlen >> test.log 2>&1 +if WORDS_BIGENDIAN + diff $(srcdir)/test.$@ test.$@1 >> test.log 2>&1 +else + diff $(srcdir)/test2.$@ test.$@1 >> test.log 2>&1 +endif + if [ $? ] ; then $(PRINTF) "\t%s\n" "FAILED"; else $(PRINTF) "\t%s\n" "OK"; fi + rewrite_tos: $(PRINTF) "%s" "[tcprewrite] TOS test: " $(PRINTF) "%s\n" "*** [tcprewrite] TOS test: " >> test.log From a2f7fb0d150dc445259a21033542b1b8b247d6a4 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 1 Jun 2024 20:54:39 -0700 Subject: [PATCH 50/69] Bug #844 PR# 846: updated tests include tests for --fixhdrlen option --- test/Makefile.am | 12 ++++++++---- test/test.rewrite_1ttl | Bin 71888 -> 71888 bytes test/test.rewrite_1ttl-hdrfix | Bin 0 -> 71888 bytes test/test.rewrite_2ttl | Bin 71888 -> 71888 bytes test/test.rewrite_2ttl-hdrfix | Bin 0 -> 71888 bytes test/test.rewrite_3ttl | Bin 71888 -> 71888 bytes test/test.rewrite_3ttl-hdrfix | Bin 0 -> 71888 bytes test/test.rewrite_config | Bin 72600 -> 72600 bytes test/test.rewrite_dlthdlc | Bin 69780 -> 69780 bytes test/test.rewrite_dltuser | Bin 69780 -> 69780 bytes test/test.rewrite_efcs | Bin 71172 -> 71172 bytes test/test.rewrite_endpoint | Bin 71888 -> 71888 bytes test/test.rewrite_enet_subsmac | Bin 71888 -> 71888 bytes test/test.rewrite_fixcsum | Bin 71888 -> 71888 bytes test/test.rewrite_fixlen_del | Bin 71888 -> 71888 bytes test/test.rewrite_fixlen_pad | Bin 71888 -> 71888 bytes test/test.rewrite_fixlen_trunc | Bin 71888 -> 71888 bytes test/test.rewrite_l7fuzzing | Bin 65905 -> 65905 bytes test/test.rewrite_layer2 | Bin 71560 -> 71560 bytes test/test.rewrite_mac | Bin 71888 -> 71888 bytes test/test.rewrite_mac_seed | Bin 71888 -> 71888 bytes test/test.rewrite_mac_seed_keep | Bin 71888 -> 71888 bytes test/test.rewrite_mtutrunc | Bin 30758 -> 30758 bytes test/test.rewrite_pad | Bin 71888 -> 71888 bytes test/test.rewrite_pnat | Bin 71888 -> 71888 bytes test/test.rewrite_portmap | Bin 71888 -> 71888 bytes test/test.rewrite_range_portmap | Bin 71888 -> 71888 bytes test/test.rewrite_seed | Bin 71888 -> 71888 bytes test/test.rewrite_sequence | Bin 71888 -> 71888 bytes test/test.rewrite_skip | Bin 70358 -> 70358 bytes test/test.rewrite_tos | Bin 71888 -> 71888 bytes test/test.rewrite_trunc | Bin 71888 -> 71888 bytes test/test.rewrite_vlan802.1ad | Bin 72600 -> 72600 bytes test/test.rewrite_vlandel | Bin 71888 -> 71888 bytes test/test2.rewrite_1ttl | Bin 71888 -> 71888 bytes test/test2.rewrite_1ttl-hdrfix | Bin 0 -> 71888 bytes test/test2.rewrite_2ttl | Bin 71888 -> 71888 bytes test/test2.rewrite_2ttl-hdrfix | Bin 0 -> 71888 bytes test/test2.rewrite_3ttl | Bin 71888 -> 71888 bytes test/test2.rewrite_3ttl-hdrfix | Bin 0 -> 71888 bytes test/test2.rewrite_config | Bin 72600 -> 72600 bytes test/test2.rewrite_dlthdlc | Bin 69780 -> 69780 bytes test/test2.rewrite_dltuser | Bin 69780 -> 69780 bytes test/test2.rewrite_efcs | Bin 71172 -> 71172 bytes test/test2.rewrite_endpoint | Bin 71888 -> 71888 bytes test/test2.rewrite_enet_subsmac | Bin 71888 -> 71888 bytes test/test2.rewrite_fixcsum | Bin 71888 -> 71888 bytes test/test2.rewrite_fixlen_del | Bin 71888 -> 71888 bytes test/test2.rewrite_fixlen_pad | Bin 71888 -> 71888 bytes test/test2.rewrite_fixlen_trunc | Bin 71888 -> 71888 bytes test/test2.rewrite_l7fuzzing | Bin 65905 -> 65905 bytes test/test2.rewrite_layer2 | Bin 71560 -> 71560 bytes test/test2.rewrite_mac | Bin 71888 -> 71888 bytes test/test2.rewrite_mac_seed | Bin 71888 -> 71888 bytes test/test2.rewrite_mac_seed_keep | Bin 71888 -> 71888 bytes test/test2.rewrite_mtutrunc | Bin 30758 -> 30758 bytes test/test2.rewrite_pad | Bin 71888 -> 71888 bytes test/test2.rewrite_pnat | Bin 71888 -> 71888 bytes test/test2.rewrite_portmap | Bin 71888 -> 71888 bytes test/test2.rewrite_range_portmap | Bin 71888 -> 71888 bytes test/test2.rewrite_seed | Bin 71888 -> 71888 bytes test/test2.rewrite_sequence | Bin 71888 -> 71888 bytes test/test2.rewrite_skip | Bin 70358 -> 70358 bytes test/test2.rewrite_tos | Bin 71888 -> 71888 bytes test/test2.rewrite_trunc | Bin 71888 -> 71888 bytes test/test2.rewrite_vlan802.1ad | Bin 72600 -> 72600 bytes test/test2.rewrite_vlandel | Bin 71888 -> 71888 bytes 67 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 test/test.rewrite_1ttl-hdrfix create mode 100644 test/test.rewrite_2ttl-hdrfix create mode 100644 test/test.rewrite_3ttl-hdrfix create mode 100644 test/test2.rewrite_1ttl-hdrfix create mode 100644 test/test2.rewrite_2ttl-hdrfix create mode 100644 test/test2.rewrite_3ttl-hdrfix diff --git a/test/Makefile.am b/test/Makefile.am index 5abaf63e..19e85798 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -25,8 +25,10 @@ EXTRA_DIST = test.pcap test.auto_bridge test.auto_client test.auto_router \ test.rewrite_pnat test.rewrite_pad test.rewrite_trunc \ test.rewrite_mac test.rewrite_layer2 test.rewrite_config \ test.rewrite_skip test.rewrite_dltuser test.rewrite_dlthdlc \ - test.rewrite_vlan802.1ad test.rewrite_vlandel test.rewrite_efcs test.rewrite_1ttl \ - test.rewrite_2ttl test.rewrite_3ttl test.rewrite_enet_subsmac \ + test.rewrite_vlan802.1ad test.rewrite_vlandel test.rewrite_efcs \ + test.rewrite_1ttl test.rewrite_2ttl test.rewrite_3ttl \ + test.rewrite_1ttl-hdrfix test.rewrite_2ttl-hdrfix test.rewrite_3ttl-hdrfix \ + test.rewrite_enet_subsmac \ test.rewrite_mtutrunc test.rewrite_mac_seed test.rewrite_range_portmap \ test.rewrite_mac_seed_keep test.rewrite_l7fuzzing test.rewrite_sequence test.rewrite_fixcsum \ test.rewrite_fixlen_pad test.rewrite_fixlen_trunc test.rewrite_fixlen_del \ @@ -34,9 +36,11 @@ EXTRA_DIST = test.pcap test.auto_bridge test.auto_client test.auto_router \ test2.rewrite_pnat test2.rewrite_pad test2.rewrite_trunc \ test2.rewrite_mac test2.rewrite_layer2 test2.rewrite_config \ test2.rewrite_skip test2.rewrite_dltuser test2.rewrite_dlthdlc \ - test2.rewrite_vlan802.1ad test2.rewrite_vlandel test2.rewrite_efcs test2.rewrite_1ttl \ + test2.rewrite_vlan802.1ad test2.rewrite_vlandel test2.rewrite_efcs \ + test2.rewrite_1ttl test2.rewrite_2ttl test2.rewrite_3ttl \ + test2.rewrite_1ttl-hdrfix test2.rewrite_2ttl-hdrfix test2.rewrite_3ttl-hdrfix \ test2.rewrite_mtutrunc test2.rewrite_enet_subsmac \ - test2.rewrite_2ttl test2.rewrite_3ttl test.rewrite_tos test2.rewrite_tos \ + test.rewrite_tos test2.rewrite_tos \ test2.rewrite_enet_subsmac test2.rewrite_mac_seed \ test2.rewrite_range_portmap test2.rewrite_mac_seed_keep \ test2.rewrite_l7fuzzing test2.rewrite_sequence test2.rewrite_fixcsum \ diff --git a/test/test.rewrite_1ttl b/test/test.rewrite_1ttl index 6783585457d4a3340c247516c884c9df95ed1a56..be666d94b3bc35bb92aaad932def7c278148579b 100644 GIT binary patch delta 88 zcmcbxk>$chmJKb;oc|dD85pb>m!F#4%UsDB0^zMZF`1v`I;Tu3NRCOrdh&Y~XBbZp Qlc(Oij&=JwRz@XZ0MZT`5C8xG delta 88 zcmcbxk>$chmJKb;oGA=}3=CF`Ic$@AnJbZa{4Ccw^-@7{OkWpHe$V2Jho|1Wj&=Jw IRz@XZ0Dxc`g8%>k diff --git a/test/test.rewrite_1ttl-hdrfix b/test/test.rewrite_1ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..efb430a4f223b25fcf7c9f8e23f67eb0a1df43cd GIT binary patch literal 71888 zcmeFZbyQW|*FSpbknZLH64G(#M!LJC;n3YB-QA#ciBbY0NDETZDJ38RB7(Gn%_;6Oz8$zrI^{o84sKXLnO$$=`2N36airTrvEiv|#q zx-EYt22naIFdjMJ|Bc6>&;P+Afcg{B z$K@B|Kk%r2%j2_uf^oje0_;((OF=0FsVEzM-uR{Pi25tQlkC83E5dVQk zjawd{{)NZafE;&klEVOc@%*02nUCR4$}iz8vyhO5J>bPfXoalH@C-c znuH93Lj@rtg5ZAs1~`TT?g0+qtmpPg)bcstr~hr?%=&aG2;$5_ViJgv1HplBfbnqy z3?(ZNOong{$mrA{ICNSVW(N+oHC_?$6bPhM@S$q-|LosSPM$UbXzsV=mIr_nfS=pJ z{09dBbr=^QP~ZPP2WV#(0QK8G`dhC{II6&UJ?R?O>+`fP*)C;3>YtlA+khuPb?`Sg zqB9^6JR(vf90CHuZ)QaRb`2381iEb>I%^v6Qv_DvKqjyb1(`_eSl_mf9SV~${7Lc7 zslmq&z%^W}yncH#=7J9Q+ofXo^2`0hHt>8gG+Xa4?;2p;pKp_8Ofw4?r=(|7u(m(A01%KG6?5X~JR0_b5Z{>K$ z@e-g3j4q)3#iqlf4KTwHx%K}M5pW=6L4QV+lhFoqfaSHd)j7D>xiHb?T|7L6z#fj~ z9=294p6o8}Hkjx-9#D5SNgJp$a7D$%-_Fs|oP(F06U?MyZfWQ2>EdB44Av0_D>!>X z9l<~ySWOG859Z=z=Mx4)o!NA>gu(9KLR{>E?A+{p_rP*cO9vMYZcZ*vPCjm4u&kXs z)Y`?DgNL1uoevXT($W&@3J~&y`g(HMdOA6>n!CC>+F6==+POG$_`+&feE(YOOs%FMxhdnWJ;h)vnt*~ZJ<1}X$&#mj>g>U?_x@9#IrI9s|{**V(?fo=TlTv@H4 z){f?$(BCiLw>5Y7fC3CDXsNLY2=EHBaj|-NTC)k|=l zxB&e7F3!$SOBmZiU2_?1522i6wmxct^GeYbEk&jfU>(*eyU03RtU_&2;3rKk~@Py zNMB&(uZT$RAK1VUA2asxb8>>!lrYh?pzhvKcOkH(E39RJHuA7T zxWG(0&UU`{z)mh!rmj$TM_4r%o4X|-04-%nEqPOAH91phNo`3$XJM*=iLTG4?&1S= zhgz{o`3ZrwV2TXpX6NMO<>chz;RM8^&nD{*T+y~Q_hj?{n}aQF?JOODr(Jy5!3tnk zKpCO#z#kVIcXKBXu!}X=(-sN_l;0ibGCdb}M=RhdDMxc>pbtGff1a_!Q1vyXwb+zh zek!UE*w)k2RfvP*KS$CZKYZP-JpR^&&Ms`0u%=_8tGk=qIGGE9|EPiWGcf+xwEbKG zHhv!)!T|TT>M3gH;qseyVxmg}`U~(1wPFQx@`Dw<903_~ae_H{gt+*GxH-XcD%voH zbrMAAlRVc0W|yH6d3>j1o?Bovi%xHtl&S)#qFcF zeE+)obC6-8YrC5}djNv|{V^e+C0@=BP%D`Ja#=zE`qc4&$^ZxAO{1=p%STz9YsZ_o zSS9S~H+(c`G$Df9Y4W1mD~p;svzu0ID_o8riJg7WB(pD1#}+BLik}(2{6@UKkZ)7y zXYCy|+1(d?{C1z3V{PDVTl2x;+?>m5Uidmvw~O4Cq$^j;Q=+7+F>@TFP9MjoqGLuo zePC4Gs?08-qdS6k7{pOL^2-N8r!;#Pw;l^y23R?MicFYR>eFO}pi&8GDWk`l7;TVw zd<`+-;Piy7Y=3yX++fd0^PU^+4&R$bGYY9Q`v;#`ERU=~qtikAuU5b2=bfktcf43H zEwx^1dP48sC`lEPyN=GD)1M`NUlfdZmT_3*UCChc0WOETXYnNRk&aB9)8M>+ z+{9v@y}%jkBomziT+Mn|DdF`t+?zO?ZAn{({vpOG>5x2Sf{UsTP77ykZZmK!ZY61N zs8;ZUym~2Vy-~j~esSIa!IYUkQD6zDUs3!g)VtOPP75 zSUqxXzc9})8f$W1B9-b+hCijyThG_N4DLGj1$T2QeBSN&v<*ZXA6235`jU>)b~`vP zuWTdhOw^ci3)8~pYnI-|-HIg(fCKm;_- z6E?*N&BiE{hIe6KJ2^fhC3@`sQUMp~C__Yq4z9poHtL~ax3A{`ng@H@cvS3~6;m16 zH8xR4aKFWq$%)T5a~3BzRG5lGPh0(qtlCZWSye=t{Zn%)TZ1&UUy96b4cZ1g>XN)e zB?JvWuI*ygs4b+HbZ&S=u<6LDH*k{wy=gqd!%`SB(rEiSo_Gm*+;$jK&!f!eBhF0D znaIz-TB!3<*0`WCx>@v@k@wDV)uZ}v1i3Zlv(w6^4lOK_Uwt-BZ>)Np-+c0@CvEsz z5ryaF0Gm)nfH1Xym)C!_{I<82pYhL@{~7HsUlIYlCjNXi3c0oX{c*B%G+&j@6TW{} zJ+LlP%3E8e#i*UZ-mB^+4E8Im4_t25yJ%5GuEY_&do zplQ~&M`ov#E!)zuYeEBg|H7o#g@s9euYHDgdR?07mq1}PP;k3Jlb`N;Z@(D!bi>&CgW~Bh7rvZU7K>xT^tIQ zPF8e4PCSV0H6C?w+`OpegyNw4(TVZRuaAAdCtUGf0BWYhsqDv?V#_5VtgaMn7^dTM_#VNQcC=QW-+sdqBcIo zv}s74O`EOHD92cP5wecKtX_tECLU|5%>l}V2L>8N?RO*(tTO}@A&(bPCpKFi_Lg}!jCFQeeWP@X z;Ef1{{{fLzKKHbne>Xpe4llUNe7A`Xeoq&yDeyjOUOZ`GYwfkZTc2z$UPd^L)u#{& zZ~NRID&>dxm+0N<)^{2r&--J@$X-UH26`inKYEJ3k@ih2-GLSA{o=4m{ho)s5FK^? z2b51Db9noe>S3N3CC z>ZzKFy_|`Q%eRibyRvy&i%BG#PpL{z4_BhY1MuXSSFa;$FP+6S1%;w=g=Xq8%6#J< zeE7U?E8>J1K6+8zi)F}Gv>lhVmD-od!&(6oOYeRh&2T%1VcMnL(e|~s;E&NK zF}b{=m5NohX=N^FXZ0wQ$>(>mmZ-WZL<{sUMOxo{56*3q!+6c#D^frjOA_*hjxnoU z`9)FtWIG0(<3hv+AuoINVIWI~rlK@95x7cRWk#tnhvLxF?G70xb$dQf}L(xk+m7 z0(1v^w;)_^W4Cr_t^ot_qDCl01vl3Ms>F5E5zG~+XKFE)6g>CFnvE<{I#Hl&kN!F# zAa| z+Y8%3(cPMY+6fUCr?JeIHw%e$b7gS(2{$!5vOgZ|31*HJu$(O#AJUzNsZmh4-pI!lVp2 zOtL$YRo!cx&LwC!P+~@|FM3_ChC^|~s2!<-M)62;XJg<-z(TJS7kWyBv_Nku4dJrG zN_(DU3+!|5e3~rQ#J}N*>rA=0^R7SRkYJ*;&vilujPEIiG63h)oE0_k4EgH~tFS3Z ziZXYL7m;Z~Xv6}oO?yH;58asvF(^wSLEE!`Lbi#b!Ys>}b)}Xur7MK?4CQq=1D?cU zWa|=jR*z8oSOyN>d2nA7w~Ikr$OK6K^&~5ApqyJst77{3hn=D&qloQ36hm-{j>DwR zj?oX>Lq*RtPVA2|<*(R$BoPV5Xr5sqdZ3*U2yYmOHkXmYk6Op-4zLE`{$~04f6XHP zI@tt4vMFGe-%9w;lTGf_-Im%RVETIWlHvEsrVucbfc!*^&-gEViQTO)VfeEzsU5oY zB_yEi2%A6q5TRCH1pM*MxcGGw#;I`<-z2Q!EgoO7zd)5k|3U;Nm_$ zkseCQ{?xTET*fr4u!yN|#=;;|FzmCpnqGuwpKL_nz8-COIs1BaCf4S=Bmo7B(&n&x z=?`PZf#*_RV}s3-pA=lh^zA{y=j1E-ia^3=gqxpf20tl&!mcW=8F5=Tb!_^C3pe7h z_So>4`#%3mlG(G}A;jvFsm8HDu_sV+h_2t|2 z7VO3zYKNgYvny%s3ozS2IY(#}FmZ_@x~qohd1cg?1YesOC>EoaRoPzV-|KD6&<=fd zlW#Y_5P*Ns)A%I*TRu26#8N*@+;VF`^NXmxJe65E*B5-pO_r4VO5z(dUxt}oNssP7 zA^Xg`QP%XW@l6dcVs8aKiRQN^H!OG=w;I<}9BjSeC#7fIRk)w2?%H=$h;DF%M1)z8 z_!`$81?{z-An3A1XF0N*8Y^X%aj-1au57GHS|~Y5eKWYh_TJg7ro$hM+Ug=S{6N}o zaOXn1#)YlN_?^pD!J?>myY8c9?fy8cge=ZZ0W01%^mFII2FJeVoQ({nbz4pXWNIJX zyd&>26Cl2BaqTr#@_@1m9b0KPbqYuc_Ho_BVl=JnW%=GM^@$#PobH2rELK%oKFlI^ zF?}skx|WpXI>mHHXnvfKEXaj5A#+13>p>_L`BHf`lHD-oV$cST`+S&7>Krr8&IfKNs)D^HS{1C{9Hxk zE81yqNA`CRbjBG);Glg_kJ3j7k2vIumJ?P3j{N@VkRwmIs)(?aCe-%KmxAaun@2lm z&yi(hG2#of6e;A=wGp^kvm+wMAV?y!Yx|q3{bbu6+J%TIzlR^!E0}POlfF&c&wiH5 z(4<@M3LY;drO;GH&&Ln0tnd-3!%WJ^5{pZpyad-o(0YLFmTfT{By2a12KRsLil;`| z;H|zuabq6QV6;g^>V<;#zVCg?R%>QLC+fp~je`-&(VFv&Nh!4r3-pODF@EMj@^c(| zJ31}(E=g6@2ZkMI;Xl|BX={oje2m>uP&S;%rq`QYN`s8JbfYylzsRx*8P6a8s9^Ow zdgvw=)Oi(dsyNMWjx8~`aKy|5UA2~_BDP0WHKqT2@i>3NIZ)Onen{pXZN#S;bh-1% zo2N9%L(|rQr0wh>Vvi3B5*du**ZbShM!a+{;$ETZgdkKmy1XYg^&9+Tx<(})(^nNi z>zwDTqs3^sA4wTsz-S`<;h2T6+h-A(I}h^oLh}gzxF>R=G1aj6gF5F!)$&3u?Oh%= z?Jpx&9eDhMXMChzKd4M&;RI_Cp;l;=I(g&BtSYq|nBfv#l`QFxEs_z1ly14}HlYQo zD4kzN+n)^0%f{awI${8=Gtwp!{AisA`;HD9roFSU!6IJNQp2Bh5x7KO@yy~Azu?S3rw0`%JQn3?eE{CRTUY#q3N&> zSZWGAusPP_Y=RpOzbm}lN|5Aa#xzy@Itx=09YdclJc}v*sWK_S@?Edf)l&+YE_%g9 zM^%Tnaz}eD#4E$wIMkN~@f+(s8H24FAJx6460YkS39O4xY-8p!<+V2QC#z;Dsr#RA z;x+o&Y1)+xl>}4jK#QkD4|U7=sPk+0zL%AKVqcE-iMZG;OkypZWHZbvq~n;5(XOc) z%wj1eYN712!P4+#*`of<^1u0aUxGmcv;6yffA=Mg?l52S{ExupAHD<&6a^px^9!K- zB@_y}|9}+)62@&(hamz5Zq^0F3*zvKz%KzNGr)h?fe01g=Z3REH+L|1GPi?T*+3DT zp`NhIu*JFv5Z1jS;I4lm@DjLD7}p5(7lEzMFak1Rge-q0VEZ=$6u$_Ry#@%F!^&S` zYQQ2zcQA~A+E*C&Fd`scpbg^K`}ifHAutw!r~a!JyvF$LCIS$m8SZrT&lbJb1D-O0 zl??HoY0ZvmYKLmZ#i(U7Sdf|tSr`nbpifu?y4(Gt**f@U;spb;9E{HGVT*^H_w)#) z?o5Mm3acoBzXHFxx{ZJrA;P1gS2LsnD++-45D-v)Qk!}T{M3b&pLBlG1gK%6J_3P} zh5w5FSLz`3r$-O{?kNBi&qDBSZi35hsj0)kA;JSxeug@+rX~Oiim>tzZ6f$@x9JR^ zq^OPqchO1q+xiFAG;BR&30D3Ax%_{KOagadDh2}mHRk?Dzzm4m*+4ykASX8`I}e!2 z(azb+m(9l6>)x+$v=A7GaR2)8AHg#_D(=jrz=fN$sO&BqP|&7Ez{fuJ_KrHj*_ z!+3uLtACFdV{N|sCB%Q|DnN+;&8|{(rooI=#{o%o0s3^S1lT0UAP@o}to$7re)WHd zOahWZ>jr`T7C-(O_0;x)vVyqHI-1)#{}HKF zhC17L0zpw8AOiVsflpY-)5GIe@Er*HJG%I=skz(P*a4yb-@4~-*HxgNwk}p4LSUf# zG0`op+1#OSUO)($&CW^)%xP|IZeaznu(ITY9fG_NO968$AT<8-02XGnpq>E1Un>We z)}|ItqFh!GOW3Ve7J?9SPCkAMK3<^BP(EM{17Zo~=eM@x;p61Dv|{7pKD53&<5Kd0k+j@b2sTcgm`oC4e1{TnPTtW~* zUI8I4UUN<%ZitX2mj#cI1wS935WlsxwU8C31=?*kk3-!>Vngn-G2Q^y5KwR`=J46J0AU39y%tc!WPsKO5bDg)+?_0KV;Zl{cggSi};_T;A(( z-*{K*e}g(vUw<8>IF%9qswZ=(lwu9~ISV!YlO_ zxT#t#sD6y;-1Gy!=v(ATb;X;$UC9Vz@75wK9m}fI_g5UAp9#3G(WAedD3!AQp7)X) z5wm(TfuAFuplnh682&KC^g%TCx>gI~yTwg0+LFg{(R6earw1$>3>)(Ja7?Rp_a({n z)Cjh!vu1vDKeWFiFq=T26U$UV9r(uZS*0AUvD^#dy`E(3Z4NPyqX)uuy$Pyxf)sUl z(IXk`8Ce>l(E2PJbRBs$5fi~Ib+i_cPL|`)@&0n8*r!(W>s`(#JQ2~oFP`V)Z%BcM z29Yw)S>+DNkI1F>YJ3U8X#z-aqH)Q%Whv-f*!Czu>&Zr!QKRwoeSP}!2oz$Z#iDLM zLVNa@sB`*6NTM!B9nM#Mn8iY05GDTTzF6_t`(Qs^fA|_{GozmC#~{67%^vOkckg4+ zfT3k*5`N7|8D`C*mM4a~n;=!pc6g>|M;fM;v0?6TOSR)0i$_p-d}cfSO7zCyqb9|^ zhtrjWGU<&&wR+6u2#z9P(TeCVS=&mpqY2gS7N-&hjUugR*;9neUmT4g6z+tmQ4-89 z`o?h=En3gu{508%6B4h(-xP4U>6(c=z5^wn(AOf8L0BFn=u@;^s*@!pr`LYc_&tw^ z+fZi1NY>Y;Blr_>LT$b^$9&nXndhMMU zoZh|4$QYi|)g|y|XF)}z7hCA~Vz#gCG&sbYC*;rtIozYaso_j&k@P7~q67H>yNO_L z^Cbld4h_+}$3@xXkG`E>4ihK+fbh9Tc1j@!z|ld1>bWi_hVsJ2xnma zsM_#cj|V*}WH@C`OT(^+c~VNR*oK|qrPtFu9aevRJM#!UBsDt?P**5fH(Y#>cto)E zj-qNvl&)oDto;MJW%*)mtVnF9kqd2+2XfxYXo?d`5vo>`cUq)794TGa@N4H}+{QQq z>a#2ZN+4*b7_oh-*ks>haP*#PhVnYRezG5wS4AKj=h4OrzgRE&@h-C5QwHP3iPf;G z=U<7I#T|FVu)`I3mlisV6uu_z8GpbGGPBV4ronXF^?9AQ%vv#q_Pv_;6hCkOS)RsK z?W+`jbC1W4YY04AIq8j<{wLDkUW9z{xfZ;cla77ZX zU$?fYcz5V}*njq<$q8}t_()Hg1l~x9sJtl}-aSVNfk3eFbGIN;q+}{f>Oovp3d>Su z>CFm%o08(S{L+0mYSz8Ik@g?HJRbV&pK~_vTkZ*#({>xsm}y(eNJ=^;5%Ds`V{9De ztf;K+FD>$MxAlH{HEFG5AB3;2ntZ06J4D@+@KE4K=`Nv})mnv~c;WO`qFB+MEpZc% zJ*T_Y^Ul2oCr$Jdn^i0Cu`9=$?^n&UJ)L@A;xcR{)T(tg@tA$xa(iY=9@lEF#}C!t zd)|QS3@2B*K@#pVaw6N1nwxtWLq*~lJ02B@MO~Bc)dRcSz$Nda-D7&e;L-NEaJ9Uw z-j9g&8^P@K2o!KCv=^`l_NWhFO*GLtFNj#V9)50pz}(uy(E7hySzXZzcO>`RAd3w)_N<>1(COC$`34B>e(tS1xOV?|#x4Fv9lk zXQu;Vr;J~MEf1@hQtf_}>$QfIiBGd8N67kQZ@()lvQMaIDy}c1zo*w$i}&4F%loCS zmw7ka>o24VVW<7|3=W8uUqunw_+pc6YL%*0~N3inlAw;k#XiKft6Ru1M;Yu`~F@PLKhk}!yQ<2;a}ND#`db19)J8Sc*ceI z;pyt6^yE3q%Q8=iLx>S3nOoa;8D7@daQI_p%-7VXB*+?vO6PX3-YvxhmRd->yAtXMTo zSl%YNw&c5D+aW_~dpwF-L+LRfeCcC!_|K*Ng{n@5%x#)=Wxou9*Lo`3L_`WXwzqm8Z2 zeEx{>GpFdeIVJciUeI+=r;4-IgRx`sNd`lc9QT;wlh7^gKgWNB93=GJ(G zva*Wto^OXt+4O02JD6SaK^hlf-m z6V^s>R~C0TNPIwIa&I>}hUJrJJ4zcTE`gigO5Yi?uacufwQKEEqu~SVaFWOeTwR2R zf*$=dX5ma9f4tYQbaHe)A2lL}lmu2(ls9r4X17VsuKLxnca4b$_gvaEpMUMV@~e+| zTd#v_Dl8m>>BggZx%Pv&IRl+Wfa<0xbb~wi-yvI5BQHg8 z1mi6ED5s0=CmK(%OV14Pz)IN8?s=Er^Q`55C#Ke{&ahI%l<8hw%2F8|@wnG--Q&Oo z2IX4eM5OW0Xwhgl6S$^>_gl*D4v&@HYxvy5En~d0wf1oLT1XNkzM89ThmXXPmwH2u z_eSs86tV^l+ct{5tDrumv#RE->w1nTe;S+mapsI!#B@@iR7(|2JdK@!J&v+3f!TI>zFlKPi`~J4X>17M^=}6}Y^Ke=O%R2=IR)UkdSGp5$?vJM(n_L59B873w zzof)p)dhI6cBR*k40;L2lTTya%}Poe-P`?`f8=~zqUhJM^-&Lzi1%LZa_g+H4EtS< z^^Gq1nIt^1m+=UG2h?B^o$Zm+)mE_ppXlP1Po(&MMsQ70G;Z-V`_!jWnS_}wdLPH{ zrOYi|`|3uJPIUb$!(Dqt7?4*623Re>) zF)USg6HaM-iB}?!F57JN4L0qr-MII&g468xL^(#^_8Pz9?^H|pFE0n42cBMd3J6)o zFID{*_4M3ROm`YiRzPDicHoI(6n)MXkTv%~P=hIKwTtHzJ@wc~-n*yMz+Mk8KWBI> zDO1;_-5djFMdhvKfQ+YuO>Ua9sMx%FBudNremZeE9IKO^tKU;*MEiEW_uBHfSyv8o zr*`DfBhqQ*g?l8)fp3gwTcr6fxQInMWM(Hia2g9Ie_H+rKs@K)axh~oVV2+h1QuX} zS^u9ovam(0t{zz6v9$iD;B=u7E7 z@f(?@Ps=sXefPdxLyi0Ci#5riGmV!+FRqSqHj~L%rzx9MJ6{$B4k;~=7ZfogzBr!T zJjm9)kS+W+9ZGu>&QOE*E;3#IDJH$qX%w++lE3^>SgRyu9m(F*bdjIjxkPTL;U^-h zF%?ZHHX*7HXLuDptBL2GeXp%}GUaQl3#a>Vp(JJv`tYsIZNv>;cnfjxf{eM}26pTru^`uvLAB2U5{J;^(P1lz=cf$7a%#X)0qQnQ%c`zXun zGO5nuMMR=~-M;dYI#-ygqFXC>Ih>1jxOmuA$zVn_v zAb%Mk*L9w<71fEU_);|?-;CQsR#=WUS+0>J!>HV7BEwR+T=3Ng4F*{BoB= zR(xZ169tbm!sPCm&~x2qd0M$|YY|7O?_yea8bpf1V-a`3^E17yeS2rXHT(^;(TC!U zbocN5?rvNS@<=Y&55?0;y*}*ZB$d34zWDKNwxTKk6P?!_h)4NO#wlrAlhDORJPUgc z6?o$ET25w#LL_@=(By2N5^)+Tg8AiBT6}1oSj=0LaOKA_@dHeA)}j-rhP3YZggfO! z5tps3?6M>U()ZTg>I)V-j=6*mc%P_cssvM!XAar%>}eN7t=kKYDQWDRdy!oe zjW7gDzs#jrG(6XOU&wTRgtV6nxhrOtP`J08?!Msma%_I7yV_a5R1~d5 zc5?eW#O|ON^Wp;J6Q{n<@5)A>A|c^0@GDE4QN_guUg2;$HP!Jkxz{j}n>J`ZdH*!A zk^9=bE6yLbpy6?iRGi%j@pfE5GkQg;Tc&~lapF34BE^ukU>93zguupWRhBukR+76)i%x{XzY+hXRV& zEozEok{EykDbNrex4Teq%CzUO9ER?zCu%bJ&ZHyo2>-FaIX+p$5X;>z%bX zp=@7UmLD_dRC`2V#*y@ZHCaRANfHYs+#p}G?bC*n{P#kFzLOAh>w@Xj=1=KiCits;z@&g$!@ri zX636xY2JzVzDO$Dq3<}^XlC&kCyp`|P2+V6u1vl`1n9o-@|$#V1o#Lk2S_&U?$jky z31_oxJdL)wJaH1sOWtDixYHOU|yPzGFqz;#dIC)%%t+vh)iN2mf z^9nRCM9AtQtEZ$kfizh9AOrfVpy@eEZl~qz8S6la-lCHgZhZB&Zo3f{c8(SwMWWM| zhT*GlE^4rt4?`m_g!&zk6m+-t&3FFF@u0xh!*&NgErC%3^b4&B{U#Zw!kaO;%TxPL zn>QcvYO!ylv&J%XcHw7I)@z4mmnyQq5qNR(P4ihKksGr$3+dB6S8In*ws2%rS^dKP zJHnwn?kHOZm>!lH<4s-ipVmr8G9yRiEcdV&0-mi8mc`!N^pCgXog0wAyI+v}iCsU2 zkHcm(#i{?meUluG~9mgT~kurk@$Z1NiO>ZkB56O{e2nK56yr&_EXoTF@TWINMoJkIGs zz~N=JR(O>s*labLUiZEbe=xXE|X83Bzu3j?5 zop;VvPa^To2$BoSuz7u_bhq#`On?=85WDH*wUBflU%^UN9U8VLMnIM|r#0YH-po*z zI4;{|quz<-7ckOe2{RXP&2noql(V5gJZQkNYCe3iaMlX(Mv3Z>|Iwg~r&TwLJP=od z<)mKTj^sc)Jou2pp~|2z6F}}M8;`CZ=k$tZeK6-3SC`_e^`fwfu=~TlywJm1=V-Bu zmb6W7iDg~B!Xe`B94OTL@8>Ln^+-3jxx_`5Z z%LnCA2h+|_S?uIH|M=3@cLVQce%SAYmERo)*3?JRP*#Fz)TVm!q5Qb9&tx4Tr#2`Z~o5o-zL}UjtK02IZT# z!KA1_R?N39E3|i{9bcl7iatr^VVPbrkaQ@dBI^`*MTDAJ zg0t{RurjQV=BkGFtdWI~i{2@^F2{G&T{X>UB*9qW11-*!7W zxM{nWjxG&MesWy&`61;{$S1V5>4oe{`81EXZ;~b0yqbFQ+!J{#I#Ay3jaHP%w?G9i zvFvaU$B_Nb79Tf~8P%4BJv!&;6w;#?+oeXNt0#PTh8JXZNKo1`A^RdV;gjvgai8Ia zMV%;gU@rItME5o~TFL!V<6Esu^@aE|K1~X=!*SDKF-qK^K@9RSi*hm6lDSC(NUSPj z`Y_z25iyPl*2MYimcE#W9^k7pGqUw%J-OM3;OYlGq_!%~U;FK~LTZ#8O0VBVVUe%eqCr)pxUDv${J(}h zC#yON#PD9`d(6}XPj2zf%~z4h4|fa4RL=L=5IELX;o--0w$vgAgR~4n^iLaOUipVP zw(StS#C-`Qccio!!?agfc#0}dQ_$q)12tXCW91ccBX;V>cNr?3G(2~NlAc+6GXMXAW>S*eKt$Frb1#8JE_q@PCc6TUw-b4hyCCSR%3 zY#g*_LC>(o&~5t`;%;Dl%pt6HSZzgu$^3pL5!K#2c#fIEv@*S3l6Hh;lYi}ORrJbM z9@K?!hjJmE)MK$VX-ewjsGDrNGJJ-tFB(@9KJ5)5FG&%ejxlGf{8oD1-39IH8FUR3 z1`+&%fasbPULlT_ev>I-JXC_-fv?K3_nCTVB73ND9cx-AXlMgEo4l=Ts8lzm$Z;{&^xBVZ|-r(k>AX3W}W>`xh+0m)dpd|d2MWY%Zy zga>Bq%?+5xW{ha!Q~LPztEei6p(VNRCUpcB0^5&<2jI;}fwqpT z%G?lkMhaP|@c{mGV~=I5TCJ{0B^KCi*Ns(!>LX-?v8930?KVj59g|6?QEZs`t! zaVjYn339Gi6!MFz9C6gNY>v}`6LV7B0j{#dIkD&)gXdW*{Z8P(tf0QTm}Y3MuOuh$ zuf49o^nXw^zg!SzCK<1+9Fb*|+Aybbw0bd`azuxHGWEup)m$<{%1UtuODm63~jOL0Ng<;q}B6W3_mw zBSAh|@SQs#v*b)3qfVJEOiehXGx6q2r!g6aW_^*BN_K)Miq}tvh&=>w6>P6csB4rl zpBXfW&#myXGwZ{JzF>Vm#olu)edO_-`lSj-G)t%VifOB^GwEIZsDvakTTeBT#JNTi z+L{Y?i{3Z_kl3<#HKv7d-=K#za&=(pCK+x@>S?y)Fs^Ujn-DchG@jfjiLlJ-0H=%3 zjz#m`a>OML)J(#}AF7of*o-9&rNz@l;#J*yPx>Ptt=aQc%DR|ZnrHN-um>iO%=+8< z9#C@$yXzxp0GH`xYsJ{BwFa>a_4SDGgl3(oy-_o9)(gx zFr0j)OK?Z{=G(eys5cwAM>SGkLS89TCkLeze}w!UC85=!;_0t6{wyDm1R;wVQUiwt zh{c>SUGxhxaM8`fV@pq?A^ zpcP6UlAZ=*FUJUn`kb=o;(%-KsWzEPzmv9Azwo!D`hZ4$CZ~~S-(EH_#o#k|#*yGY zt9*oLoJ~BtLA;;j(w^~v^GsHalgF^5foba4#U#3X`=cP1b#l+rF!FX?>ZPt+bH_|x zKi&Dc$>V;a`@KjPM;MgP4xXMe)h`fG`0fZ*uGb`Ipb)lOY2G8MgnSl%)2FU71zwjR z?vs&GufN3HmAvcneDCv*M`!%z8>Gi8k0u#vmwg!TeiR%k`y?(v;zQ*UU4sCv!r}(i ziOO9SX%AV(^R|@S+F^`J@NiR>3rOM{ZhhunmC1`$6{M#%H)V3C1+lyI zZU7xFVLrOie)r48;5pIHC&wO!iAOptoobNf*Hj7PuPf>7ldGF22r@O*bs?R1_@vpd zwmU~W>=^{LrWZ7$nS*EMH^TdNgtGfA!n5aX4=A3kvm~RPc;O*v?)|j<*qHytEP?@M z`F*+mK8u)Wh0P+;s(znEi2NRrf+HRR5PyEj{MS@CiD^_AV(2Uk5ta#dDGO2`Q?Ueg zI{5h=co47`0sNo80~di^`|T^{NDv$eHt_Rb(ECMU=t)JtqsxKRXH@oTUQ<`~KzH^J57nj@D)6I}EF+cU*Swv~w%GNigt%#8dM>{_= z8Pn%*$Hx`!D!r(cTaorvPIR;&>939C-Mzq@Sg$eX?~dn}?>sNWFP~)@F{4W%;3HDV zEO86<%`^%RtHg0EB5#*D5E~H<@3xEss+UmASs?G z*)-m~Xg;+)tY6J}Q2LT_mDB$7aR~89TxY1xE39$i)*)$le1Q%keVrsuLxJz%n-awG zbT^}BURm^Yy|WrwB*gq3K`9Hw{WFoC!}R{<9~2grYWbU@aYFs8Jsp-l`&70MhQa$) zvgW+qiEN(5=dKMF3W9t+gX)0aX|xfcuP2z$waI%rEWdh{JDGW&zpV?u^GMlzavaIe zIBQb|+G=g4!dq+f>Ya8vmsNH*@U2b%D#H^2N)t?P#nO&Jn^C@=i|60>yTd3#l<1|n z(P_xwF|f3Ks(dI=AUNI^qg@$#4_ub`2!gVLkp>7~gv;nok&#RclC{G3g^4!GOjMY2 z+Qv4$k}qMCqTJh_WTm`JCCx^5aY})j5O;&11ilZg&!nsooh48v!b0P*NSXiTX$?f`zjD%LD0q zUrw_~6R-CS7OH3apYp~fwx@TVtA#gdwWffak05yd{Xs?RpO802nyVxr+Nx&P*U27D zQj-q^&z^*u2|&l@$)(3B7qRRO=Ml^6g5WE<+cK(CPbI4bZJ2@^q)0y%t9rP5=P2g0 zYvaD>Z}14FTue6Rp%uC~ilmBYDZJZ3N>IJa!zD)`%Mpg!tD!%+E483Da&~S(wI_$L z$y6{#ii;~|=1ZclQARG9``rm{x=^~(XDO0A)D^Wqr3hJoi5Q8e##Y^vZ6*ili(k2i zMoo1k6o<+WKl&m6Ss@aSn6uTUw3l(g>1$w?ou;so5Hr5mCwArPx!HkQ zQ8;l3G~-VMu59>JWU`pErp=l+BI{_IAo9noI8S1S^c7b|rtXULhCa&tOsrIJ7nw05 zvgz}aB`dmX1u^4s6}z1;114AQZrut-hzAt2)2kF?A zt=iYzgw@=(VKXx%?i&^xagFtbCJJsn0I@$FVa3`-{0z z+4M0x`15eXZTWm7exl|5VyVM}WrZCUf9ZRUL5j@Xy)S%R#Qgn=q?IW=GTt>k53SvE z%U`lpZa9`*?DeMY$}~ROdE#wnyxeR{HQe`cv;2aVl_7EXj{nwz%hfv( zHZ&>Fu2_V-u{^beNI?9z94?hSHw1O6SM}jkYmqJGT2q&dR-x=-vS0fxq%$V20&utr zw%a7RG(G(UU04b_o}+uOCzIv|C#a`2wpNb3Pw3e46QAh!$deN(U%|k-p?o7WP5xbV z<|z0$H^`X9GQ-)|`@X|Y#%dzAKrBUOe+Ye6^lY!pI@6v{M_j;(_-xS|9s*NKG!0H% zdUukljL*296ic;l2VO#C9ia(1%ZD_jZw6lP8I-Q2HZ4ReqBl;>%&YP*?LNbB=N&T7 z^wHFREB!{gV5hTwW%spO_yZQ9D)(8uM@KK-sJu>?jrr*Z1LFWEahqTLxAf=x-60@Y z`tv2=SAI)>1~RNU`r!y!ZquJ(?&V+8pMU081CA@O6u3+Fw*1vC3LKjQ1mLJ{bIxHz z5D*CJV4yM}&@FKZT*bc-|1F6O?#L3j?Ka`~FKmNho29@UmUa)!(ZXCJuw{sBof`Hi z;Bd!^@Q8}8IexlBSi-a_xnl(|c@qIh!xGK`evh&MWFT`ED1YS_lBy3wev|sUqy6c| z#{l#(*o(n^;Mw28iy|=O-w=U-B|N|tQ2v4l&rW*#sv$V^kBGo_A^ACfM!Zd3_ORlx zvvP!5y0}BxJngK2w0U+*4-au|Q*KjUQ%+N0mxaG=C}PfT?P2BoGY|Pc)1Cjn-A)AH z^7kD=fRx(bb}sn8Z-)>~*d7$Hy)gc16A4Uo1#31HV9OF~K=5o@cFvZ-HVv}wcEDy2 z-2buJ1Gf<5kIf!{Ctw-0Y%1oSmOySZEx#Z?KR+8c#9UB-4Pwb{#mjANNed*+{?~ho z0NVMT5n@Mdp+spQ{`JjBusra*`Ke=+i7 zV=B=_dFA_3->7qA3-6YWmgvrWD;`wj^8M;7jwx)nv!chjz>L5Lam-g(Oin@NNcJ;bz4ZHSRClhE{T zH|Pgs(mkMpouf8z z9;?o>2mAhWJk}3|2hsJ>Yd7$kJwyg`?j1S-ejNsUi%7Fr5=-~1#c)Ndh)rhD=gm#laMzbZiRLW@inTg7$|uPFu77PHzsJ9!0Yi`rXsVV(?#)jVI&`?HER< z*HPY-EbjQ0rS`;N6dJn@nNfRayZT^l&x^d4Z$QpW)+tGKdk~eNh!p9O&jRE2zUhfV zztHqCZqLw`H2glQYR(=94$E2|Cn;Mx!#EA6y_fLCxWx_SnQh3Xnmu$$2Aiq>Zg=zB zpkjNU)=U|B%#C@WaG`I;ME*Lg>XU^hD8JT_=pO{B$^olFY;W(7r;x4*A^5-B@nYK> zMki!iU3S5A*k7-()Kds(7>r5#EV(yLL6Kv{ovZpJd+m{l(U^^1y34BAVxXl5HqDLg zhV1-h{7`UREB+6Z<;D^Dc#6eB%B02*_7v1zDSY0_g>VHQ70*kUs>`kvsblIb#BoBu z8BU(6>W_J!1nBMCo`25>dStRXGTwQensDLyD6KEt0yFE$_%qTHK|C|E5))B`!;aXl zl+?8FSFXzM`0fy-BCD0O!_kB#iq;3s4hB)s>IuV-pXrgbhc=hiYl(honi|-CH8Hvo z`of7b2lt?5ioTMnV>D~KgC9j2(R^(+mt?x&`OuIlGIrf10=XY)GYdj{iXqu@N);mB zE3a?t9O`^I6dx)nvdOSD4k)OsdS!O{&A;|CJj6~DO7OO=&-~P8TibASg%B}Mkba1f zpHrS*L;gNoA6Yy>HX{h7iyg*1-#R|Grju~**0SFyYVe8$Cc*~S7hHX zmMKsjo;cSaVlbiWuD6_L z`ZD=WWrUI5-Y1KwZl{eTV#8AbTqxSmcS5vYD6^0hTLYMpiZTwLJ)`aI^DFEND{Yyn zNAr&t;vD$DlNvCyQ*DV+XX}ujosD@Qy4r9wP}XW+m93XTc0OBiZFHS5)1edIv5FAb z=iOc+u(+yok-KjLQPjfGSX+pmwOvi5`J|R`d_Fd&h5Je*=jcIkW^FkhwXzM%;(1D) zCrh@$|HIxt21%lBTcdDw*|yPT+qP}nwrzBmZM(W`+vu`wqsw)x*V=2JZ=G}2j`#a@ zZ``;)GNK|XV`fI?%rTxZpE<|GuYxQ9nVm5vhwcfiETMOo#!_(wJPB1)GCRFdN%5jy zv}$k+P#KrtEc%t1Z#{rLg6IeWO!kwhSFQI~le6B%+f5 zB#1FPj0bnEKP*74c|Bour|Tp+^NoBNGvdX9AdViG&s7G001AqIHWGdx)^38zin0$2 z42gaOQk@RgSEz)}(J%a%3%wAvoOWi6MY{S?yr0s7sx09YwgvYWBNfX=2+tz8kAzMh zLSuPmVYgWg@;F&U0WLEgq#%bLv-0>AI6@<<2N63BcLtc`>wrhYeWSJD_8(jV1Qz`l zm;Co*Jpj`b$8Y4P!ufr?`HlYn)zh{H`TM{Ws`T&U4fkgSvGLD{&^G^pO921ilIXv4 z$r|KeT+-(7cP=q~QH95huyQ0`6%RoUtjse*sDSfSZ|ww45K`CrDo;d?by2jjJnvt(5sk_7;y%1z}gS2;*LH74?|Goz7vu`w*pRw4pwyS(Ro-*NhAEws%svlz@* z&4DQRGumPO#FZa7HAh#Xq9F1bHPkipSfJdnMarUMBTa=%u}-to-UqSkcWgc5%<^AW z8h4IJzK1X|hJ7PsNz%OZ)+fG6a*wo;BA&6JnQ_+l!V#UnL)YIGMJu~&0_4^jg25c| zn{QScWe`#JBU^KhbEhvwJ-W_FF7mV=A z%cT8T2TrYuwYMsNsN&#!E;rZ5k0}}UGEhUlHEtpVBeOdH7E%o~O*FhlvegA>huwAH zV@)CQ)gahKM5PSj5+b-motMjtfsrY(Zqx5#nN)(?DoG0dIS`UUwJz-GC(kif;XoM;w%+O0EsuuifixY^Mv-jrIn432N>e zH*uMAy_J}3K{h$8vLVnyYxgFtHLXn%L8f4DJ1l42u6CkFf^-%bicSn<2YM0TWT>3e z)OA9XY%RE0$}!s9$my3^rGO;+D-%_70~;WwED&2o*fYRe4_eC}bQ z!lL*@tF{f;HR{d&Y57Za0*RQl7@`}CoFLKhVOu%#mhSlVMK^4YbW~#En-)u)>ncGB zP|Cm~f8b*13>1BcG(2v`qzITJZ@Qs-a~~#3r9R@??MS3F|4ijvDktt>MUFEzMgI%z zge#Pyna+dfE@_E zY>QUwq5d`&;gZ}GnI13fFvQ`&f)@uQR%w>fm77Nyq)ygV5gHy%0n8E6IYMS9lc5JW z4pkVj4q%VqJ-!N?u3Z9>u$xZ$;~QMW7@(hS@CM1O#>4POYO0Rzw9w*>l*8^Xm-v- zHCUn<9NJC`CP|T21PfX?zn3D3n3=<9U+AuucSm>2evSYt_={kb%aACn13HNBbF^`< z#?q;eU63ykrk0GN2;Whg!1)S-SU^o>?PUFrS-E_H6^GHJ*UP8uY$V0yqW9cnSExO; z`Gbb>?1fm=#zksM_vkxrw$@8{!&wE?cgO>P7$zyi?wa_?c9f57HilDTe;eB9%;6tH zTdCWxQKVqJM~`3Vl_8FC}|^!Bwa=Bo&kYStA`m2p8(e&L{?ljXyP|O@I0$S^@^ct* z9;g~gK`=p&$x$%pYvfRFV9pK*NJ*vE#FmvH`46g}4#vV=XP8yVedUJL^_(p-Wu_CSz{`IkKV0&EKz^V&d$z|HzUtxn(}13 zwge7l+bn{c?~h$kSBiXZ>zt0ce;sSfZD|={yn} z*%ciw>=r~|wBQf!soEc3W1L^&w8XkffP=-P zP&&FKGh^f?BdX$D(p1l-gKg5dVvf%4>-!cVbaGf%jBZsDFy;Zx!H?JeT*vi&YBm-4_psIR5 z#!OziI6CE^w;w55N!VR^d*}G5mrV_3 zY{PnNPP8|sK0FmnLCC!+QtC?PlY?zQz@ixQZ6_nYn~!~Z%^DF5F;DnRDA~s~j!Gra zHWZE($nKl><*I#gUu9%;6-q!~sAE>lJ~A{{D=y|trDlfCNJ#4zXpGGrVM~6Ohb1=7 z2kI;ml&8R&Ska`4l^%Yz+yKHMl@SPT^>*W_h=jsfiH&NZ3nY8Q2*}PuE4X9xAnr;( z{ENl+253)fnB=>OJUu>3sONQ-*{7o7%UOQj7D65MwMPN1-p;bv=1Yo8OL);mXT_e@ zGsh~UKE(tv^{V7zw8LzZY*Vx@w(f(QMnD+q?1r+WLp!Ys{(`@usKMKDexu7W*UV{0j2FU z#Nu+m9m(-iUSQS^?+XztN)bkhanTCt91nfB3a$ALO2Lwz>9fz(g1M6A8wf5{sN?aitfU}_n;10*y9L*h&sGmQQo z%(8N7Ds?k_g`|cvUqWQl@f`Zg;?zDPq<3mgS0CdXR&I7|yr!~!Y$mOa)ioP@>WFUaFn}-b)3|gyhA<&Tz>@ox= zF(Xrm)g(nwNmk?=_?BB`!Nz?KPy=$$45`uWeo}o%LeaZV3_ifE4A6XghHyPE>0nC0Dx96DIgF9CLt?YOF zhq(F;`AvBXfPW!OqmTFBbFNJX5TXli7SRK5?W&rg@Wr2-CQkoR$H0#^zrn?y(M8|U&)8W z+!zFd3$*Mq+>octA7713tc~wfQs3CSvTlr$bX2j*VK}}y7J;qAFE1>xXQqlGFDuHZ zNo<3#^8^+*&XV*9nTlLBE)PY|B+4TWflSZ0mKhjxMmW<+W>~nCiPtWit9QjX8}`Mr z7H3y;9H0p@XH^^&mT3g(gekr{7x1LKEM9mkGbNbrF8Y1%)yv0H&SB2yYkodC7(>et z-xjVV4~uZJe~tb5;~=*dIviB8gF?NX*neNR=0uGEqAsaB)dY$r%5kM8OY%*)4pChvwdA~cF0dQLJm!%qjm3-m?1oR3;dEY*? z5+ou?`}NQHC@eL((y*VE?Q8;#V?p!_DxZzBnX(x)mTWlvInj^7+{dgeCR^2DV)!$` zKjoaoAJxD? zV*8j|z-c?YGX3_Ttf|jKE%r#+@d^CTE53;!Rt%l?}(7O>}l^<-Ak-uBf57-Jj&^~GfS;gu>CGzorslbyV(Bg!n63bqmtW1 z@z=4jO|&&lmtJyoTynNOs$11lCW{X9naq$}kw2eu=gjC25jUyNS{0qR(WoJgsX6Hg zIWTlcqt3fwqbv{}%6Fb>2Gy>UF9TP`HP*1{97bu>%9@&nomBJ#c_!R;4`$ok{gASo zu=asRh~z{eK&>KAjZg9SWyjI0pm~Sq)#zur=>X45UmKv9vQtmvT2QrX#Cm18-?A~*PtZGM|!t>bgtrZWP5@z;`u@JIJY6cF*t0SN$+kllfx!j`<#zyn1oNI7cy5m7_7f%YmcypPOQnm0{i75^a1n^n>! zShbe{zV(Yni1^wr1^mf0kkpGst_K^yX0>{hA*S?$=KK0+KC+=5n}#^l!aQuM>)J-f z1|yE2nBMUw#kYCupP3rVH+XCYQKx~S9ekA?r(}&00b{wdDc>Y;#LIY4&2=zjIwuNI zDN`~g(r}X2q@>qxZb#E-=Td{~ZR)r(hrSoc8a_&NlMm$3zLOSA97GfQH@cmy!4L1w zMmW)X;3(%g($%>U~;~XF4e$Xz%os9cz^@Cno$Th_@^y zR-Qo4D!35s#j0y_Y78)p;fCD=Ud#pzI_UD4@RI1j#v zKPH%<1rt)`4Td)^_DWaC(9!yZj=5nGd@B>WJu=5H#-;t7e zZ;^R;3`*77ZN3G2WSv+p(ij6#C>u(^@Q4W(E?q!%p;twuhGxgvMG2gtA2o_bXpqgS zEQ8D|?~g-ot9UXR?;e9=Z{(3$3G&L7&T90DFHn<&AgYt+eobx3pz(KEI>L-}cQ;O0t$lA)k<|R3QT! zry)XLiP;n-yk4R#Bu0hMrFOWEsJ>lt0)}m*S0V6= zJvvE|0YcAmXzVxAVQIn{vrwaK9vCr2g;+CiFENE#kAJj-y z^muxNIjjKwUPHr{hlH_9Rd!iQ0z#0nS)phJD6b0UqYjB*0=`hr4g_dke6;;^oy;Gk zIj~asmhNd});bWCN2YT5R71<#<-TKNCJgxcG8TB{JM%B+R`z$4pb(mKE#byF5^0Na zN(SInzb{{Ash8sU#g+Qi5t-v84t%>Vyevv6O)AEcHGFoQ z&`*;}&v;p&bxOQl4)P)95K;#iE8KW6OJy|`Kr<&>((1!c89Pr!$)NEgU&pT*Ig>i0 z?&F%0D+VbmZUV9Iq)3S5dlxp5h6yO|#Q#`pG@<|cguc6=wr|L46MYHavCCV!)0w6n z?VB|5Se(n-&hf@YLb?59;S#M&crM;DI2KGr^V9UG_r9*x+udmwx`4YR?OZO)jOCkD z->KVAURM4eyFOp-g3B@%Si+X|B9HWmyn?zdi=AKxD)T$V+-O7GA9TW$=0(#b>o>^@N zJkA!h=3MiO-&!Dt3JjKX5k-s(_zqB{v~CFgsg2M8cYi75M-iEt2?A4@xn$L{ep2Ec zlmtLQCRsGtbY!ZDpbgi4tA9tzxrD)JlV{rQ)j*We4ZgBv7UbaI#nRLIQuhk3_|BBHz07`D|DucBDyLG=D?INnQlI;iMrIDt^~j)jV$ z7YnW--bbk~m*C^E_M-XjO5v@As>yAlqCTO=lu{qZU5CLjq9t`P8}}wFf)1s^xl&Dr znbM|sfJydaeeL*1ck4!tj$#LDz%qw9Qy4iK@;@#RIM8q3vKmB=B#Z&$r8S8Yf;p9^ zjB+a+QAi3UwlNG{6;fNYLIPfxD1X3$^%$e}E7BgWwdqa$EWzSG1IB{a<1UjjLXbk+ z5PAb5oh-QBsaKtq2ZPPz!@V^Vtqlz_p;3mbO`Khto@OKAlW@9oWx7s0vAEjDN9?&0 zwI{0|E_F{+ir_#Y1s%vcDo+22WTW~%@?>Qf8d}`oyni0$g(=J1?nn>$ez)3*ME}Km z9nQJ^g%JBSvmgpE*V@c$jqYsXCZ|HtTJy+S-|^k`?uC{#WFhZT!<{D;I0a=E5$S+% zy0@`)5LGh|?GAet6oY~~@s79q^G*@wKnoZh|GIf@r>iaICx=sR;6QGh!>U1z)aA#U zsp$`75He26yi$5ZUA1rZct3vq$M} z>?vV8>VF~s!#}HtVzA%Huk?ov=Wq1?ue*qwvfnDgcKd%*5x?Eg{*g;S|KO7FzjMh= z*4ri-a6@~1A+!t8VR|!ZEPIfd zFjkezLg{4Wd-cvtImlW4i{VsJHYMhzHyFmR+2#Mxf`t1cfT?d zT@8JPPLr<26gLu6+vRWZzmhMfY<#fJBy2A0DntMm+5tj30>oqY{ zFnpOl82-ZLkDc!BR?r%nq%vT3qpuXoorC+me z3YlF+O%2T6o+~3_XWpd*>&7`b&oeH)_Djr~6^ha~C#Mep!7xgbyFldgCoTZ5!xycj zb!g6Z9I@URiv>-JK79p~dd5Qqk?jhtGvEXrU0V9 zG@ZgknoEENKz#3DnWzo|s0U{uuZ5AuNedWJaspcv%a)u-DU`gE%(tz2cJMI?0*Q*9 zFW(yU8^euOX!s)OgiPof1jMPq;UBbIB{4bGUCr?(i81}b8~Czi9@Od8=7Kc#?&?T@ zwp~~tpgB{&fKi1L{R%`JOE59L&)ga;{KY258QG=veK((D`}@x*iU6cDaj3?et1`8Z zT|q=#PW6=1L@`k}KE+o+ajC4lmS0#~ALlps_2oUJaJutY)m+y=KL`UV>3J_<|H{<{Bx@pRe9q zw=6xP>weuJW4<4ZID~eJb)6o^MB4=xo|C>JJs6MYrW4e@MjvxQqW8XuV&7o7Qn=X>aVuqL6)8k4;?vtnL-B;x)8v9~%0%q%x?DC4o>hL@t z#RU1x*eu4TgtfFun7ndqHV21gK`B{e$!(b(5@?ur_JhTzc~+Jl672Na84x(^=Pi$DhdA8i#0V9 z*&!WQc;zAg2^Z{cWc1JWc!}p@WE(+R^=OV&{&rRmfYFgF`{6Wkv<82*^T<@Vk*&OT z$};8aD^I6__Hvc|iSzVT^=$5CXWgp<-ZCSa7w;PJcf5&->uRs*mjO^^BlzJfHxmdw zEZ`l@!fA0>z%`+js7!gZpM~0X3Lg_5^AC;IK-*K-)%a{q=7%G-nN;GMnmzDT-8@!* zatQ{*KXZvD{cq&YA^D#bsYiuRF1fMzZ1nw~6)9}_|G*`Gu#-dB-?`*b;V&+k{nt(T zziv0Uj*@Ji%%%}hkYa@s3*yYXeBgAyO1i`moC1s_jO<=tJg#9aHiLXOS3~~=k|TXY zh&nm>jy>KQP1ZR5rp4vfa**G2W?;E5fs=?dVj3;N?TM$G63XW?)?EUG3)qNZ0wTvLmVSLE80 zM~P>%!MbZ|hn~j;kN3y2szKHUOX!V^SGGo$o<|d(XjAI(0<){@_Xm?vHl;_jL>^v9 z%05X<3|WVqX~G2*%6NIs3w%000EcnZ>#x)>=kC{xe6al<3Mk2#Ev7C!=`vG?_0|Ht z94W$ebH}e%EL}MxXFEUB(0hC>Wd#^+0>1osHz&UPxM~B?K9|TQhIy*y1VkwhUy%Be z0q_CH4nj63d2--Q3vRiLq>;POz+c~YE!;x^Qy+R;A(x*YOjf>9UmsSAP?3uncbzdG z+=If55{h0jMn1D+>oryM$RO=MC3RG$HxY_B5<&Qc!c3yBi~{q8QpuYrV=KtNfvgDs zX_Dzp@drQ)r*l+=9J^I2nK;S1-$VkL4j-zE=m_Wx+SQ=d+B1d`PA|E+YDO8SrgCi= zR8d9PRE-leCP+{BE+Xrjg^A{xbOgT7aJ9BO^BBeB7QvXy{ybGf&{UaVh|l}{S=6f} zE<;_NX9a^v%9la#8g)~K-e}wl$C)`>Xd4(Uf?o~bvx#Jq*#)2*Feb4gj)TfULMz{s zVvb0!(!J2QfZA94IW+n;br!^FJst~P2n+PKuwy)(>X%CT2Ngv3YAZo}XplE^uAHWM zxjUM3z8Z!IKMhu`$kC_NAE+r5J0AYRXD*R`gKuF*WMC`J-BB^YMghj;K6M5nI-)`> zG0t};P%Mb%o)5p(CTs3G%wm-%Z61anhM%p-h_nL{4a89~OJnQd092de=xP9mMwVKD zO@SccJg%B?ss9)a2|((zsnN-Kz6dVO^kbyWn43Tdhcy~8>#(#ju+0&uj&E=r-irDfS(a6 zpmlW5a2ZJKWcN9cX{~G?NzKrBZUKe;1@YthR%jLRF-Bxh5{E1NCFmUZwI7*2z+QFXl`W8r4d=p10N0T3K9D zUcq3(v_gRMlo*JCnlYA@yRE)P$J|Dxmf>jVZgp1)RRPCWHU?!XW>bF0jpn$43|e)M z>w@OH+X;~)S3Gj_xd$YfLqgGZ%I~y3Y^hUIJDV0NX&zYKD@+;%9IZ(d&&U4dgIP1~ zMuTg{Ghe^Q^U3hZa5zLVGBl%}1-g#Jc~!McstLa9cbueQHRK-E-BMoA)$N{r^|MzX zb;d3>Z*pI9;WV$K^$V{*3D%W#689kPDr?b0X1-cZlm zVmAjLW6H*n{HOCk6ud12ok98)eE=gTew~mfZb%GlQ-yb8tq7%&m}I&{VriaSbd%G+ zDpI(CpY7klzaRg-gBQ>>@Q;~D=d-^f|J(B)$p5)&Ci1f({r~4Kf(yz@D>qMhVFT1I z0fK1Ji!PaUY4}8~ALRh5QoV4&2zZFNVUq$#9&M@h+3bFC99-gQ5ZJm}t}U_E!2%}O zlAi23v&*{5w6_!PAY>aqs?HY!##1B7xT(oc$6;tB+%xxm|31VpHa{tFdx_|hN zRCJ22u635Jm|ZFqW488=;Wp%x4Fzp>81?#f!2t?P!l(@pA9YJJL7+F_k_co_#tZKX z(5~B8jz(OIEg9Wbqtu(jQtH~&VU6z{`{tQ+P(wg97x+PHX&3!6Lmg{Nc@lt?e07~O zL>dox)(Lo(R6<5LFMCF$p>pg|Lg_Z(be>sf`4v!%Sn-2ZoDRSc8;t?$i6g#y67q(9 z{76>|MPKM=+lOBhP7C7bBf`+o#sN-RA7E-l**T5iZIq0Kh>Uu!yPipqWH2OR5F{L% zRiV@m8tL{U3_6vKEw0ts@{{?_`T9gU_J65yLKUlSJ2|*BcQk|hB30a7A|>72FMM=E zL1fJPF#QPq;FRSfX@1}Z_$19)$ath8>CnlA1;^UYj#zEFLl^yV(BYp<((sEYruX|y z0Kt%5;u}GAr(oALqoGu8BU%1$~NFHQ(10vsQ3jmi$m1 zj@K=>qH9?YBAjqs7aEej98Eux$G!&^@alc1Z$J&7XRP#pDOX@V{P~S9z??$ukG-tH z06@6E?YRG8!p9Z+_W>v9e{NK21>pWImJL2faR284hoJdC9B?515X&KdOY_fYe;sg$ zm;avTf4ahGlK^(ciYSs$TEm9I95Ie!TC+FC!QiQ8C>XzWgVzVWU`;#Dwi81ZV;8N> zlDdHg5`gopaP(~zzi?DHxPPl8v9On+lO3gm4h6s`aO7{*-kI!mA$K2`O~a4@sYh+& zJq!cVvnU;grHWs&sXdYk?nY@>Ldc4VQ@`hLb2pz_ZJ?P_&P=v&;(bt>UzwUUs59ABjxzNreZr)CO zj|u{bC3vMOxbAh4xf=^|y@;Z*f<^mS5pI z;h0Qay`;51zdtO{LM~sUH#1pyZL?efg#ycGb9*SQ!q;M$Bz=Eu@%G*0jVG-!)}MKy z2VxChDsB{(`l*d@Eq*L}bf1Ogn5SZrx!YC_Vr>yx!juvD&=$ODlr%O0(TaosXC1{9 z?0q`S3A+VgvLTSu@7R{qYwVEYhkv0Xt7IegB=MY9U)|~UBMSs{r}xiQ3pjw2vwc>E z&-rB*6Uue8FOUnv3e9R&bmW9cqd%P1x;r0Iq$cfsO4~5!oF%c$cU@E^x?O4GIl|#& z4u0~8Ce%ofNUml=oFv>^2a+aWr`#?2pftee*2t$5vvi`gC+5*OJ(U}WmyQE5h3clu z_^NNhK2MJ*1DpgPO>+)$Q%~z0aKy7P(3e?5pTz1=f&4;{E=8H(@CG0NbBWv?bej8s2>>jiWy^m*pQLn=*EZ|z{Uzgqbp zD_Ss=9hhFH(G6I|T`RUt^!0?JLu#kjX{dJ^)yI52nL&wW?cE*+4+4`bH?*FigPmNZ zyTSrd7M`JSxZ2rqU)!_#3;8#bo^x=4VP%#rPhrwaxD*==vgdex)KgYiZqjwy?UxIc z354yta_#TRQkcwyK%$tiR+>ua^ogGzvf*Ru5mGF*JpALl@hToacn-&(SHQTbC0|L4 zaRW_5jLzo*E{z#k@+IYQNQsWWFKPCjt2C*Y8S7-BX`EcV24n!K zWq0NEkF8C^`P38R2_2R%5x7-V;6ZVi(CheR>ZOdeG_-%ZzpfU)l|n{sA#1#u{xFm) zFBy&>1p_nMEL*F;%(L8$#&4YQ1!aUfGZ#jH)nOX1&S>tGGJB{iPr^c@-RY0wa9(@J z3@BNbxv^HZ#erYL6Tt-9|^(fzf>z$yFncX40eH*K zqx(HONtHEuF`Fmm^PG<}s+c>8oB{Y1>Dgcpa}L$`HhP=U_eZyuc3Fl3h{L`E=hzWI|@W z+S-TKhv@`yi&NP|UzY?{lQ4my=o0bl3kF3KtZ)MHD}iHb!(z+S#2$XW^$@g}KMq++Ba)2M8dJcH*CtgGm)KYdHkFoUV$8@{+Iw|kaZZq| z*eWb@nL;9kIh!Q#!GpbW%chC8GUcyfoCG}c5uaUN#sTxvi*ri(>TW7F@sNSe1mG~O z4dIy*%uk~)^3k?^NFogv+9yEYhsxO@Yq@?^|9*DRediF4{864f?BsQO?lL@8IW-l0 zxphA;m{24x~gIr&3FFDb}Nr1oZ@Ws64mY z3?uYewm=78!c7ya-3174YDCjS_Oy`8Y2*7A87ri2@ih*=wE3j^l8AVqwuAkvA1|2$ zlUH9VD`SN=uiiI!)e|%EP?x*~s?1=1v)G^_4Gs``ei7yXCGjPR*Ya4B9+t;V2%AzA zYtVs)uSK=^ouK%I3`&>;UdW`?I2bs~vq?itOpb~38Id)3 zevm*M5^(InV+JEhhTk}<)KFPe!otyU;-WJnvMqNxY<65Eq{{bilK0<^hAIS~@XtmH zqK7#R{Sk{t<2YbWE!=ay*W%)R+;x+O3d$*Y?L1p$%Exj3ip9-+{hSv=c>EL|s?XAQ z5~u{J>j-??R6??evjG;N=w_Isz`{PNAVb^nM4Ex-K@--6G2gjiISqSyOwOp@H~|s~ zQXt9fQM}SzC%+g|Mf9QT3*h2!q0A$4Hz2qMkq_2_5%zL}e6`p9(vZZa*lcT{egMu9 ztBM%>AP40s`iY_m3y`E_mZ7C12?TLL-&(fN(}J}TGmF9OyoPJh{ccEovPEv8dk5#; ziFOpoMf+!bNJ2XH5>~(l;l@Z~mt|Nrar?;L*lQz`W+%5M0J9U;h+){Y6mrbl|I$mIzd(bU z+{pe_jLBkKIy;G1eWjRO_^gstD}-g2YGO zkk4J?F?kQ%tk`KfSzEvhPuZssTgPV2Qyhh9QPNC5B#|2;-}-r!e0b7bM}Nh#mAgZm z4hu>cycFq2zl{5=*@k_V6`c=BsAJ&h9ljmUXeC5FgN5E{Y-L3V;N^0>hhAHR$DY0k zmy}dif5R(`YbN7%oYT*2u<;0>fC`~9se~``@#y6oGx?W9EYdl|qk)=4`R$fXzj zHXa%lW#%{VjeIL855r+FcVPn5_wIr$%D(pISG-(^x&sQ*nxyn%d1 z{N#ks$A6ob2H+b-{T(r4=y$|F41~l0*q)5#99DmEk`|2iAc2eCq$XAA?v;dU??2`ycr| zmrf}4XY@b)7_5aqM`3yX?Z*JDq4*j3AJm$s>F@Ww9Psb={pY7L@)xuLBM zjh&6@|MMIe%&qlJjcGsM8r7eFQE=LG)6uZ~+mM0z|5b(zN@mWVd+3<{wTF(L?$exs zgOQbk`CohJ{`-hJy#b4fp#ilK8_Vwzbp}IbI=cUp5p`gzp8vn{UVJH5cjfb!Rq#9S zwSoUD?-_$q5&oX{*5Ln~_lHEkv($R<-&y+S@3r7F`s(kz&$IZiy#F2f&%C!z`FuUj zKOX-LUjV5QsDDS~@Bd#Te&+qI!rvqQmG@4r=1xw=4*y@;+kI{aG}Cu*{J+_m;h)l; z@qc$_pf_NpXJw`~(l_}l?dkuI(mwGM2LGu$B&PXyDw!<)UG_jl|5fn1X81FeK4s6v zXUcQuLy72oqq)k-_Xc<%^qUJ@E#!?2mQ`WnsmIvTbq&-31q_sGy;Y z=#pm$w8PEz2kXR%)c&f!u7@6NrIX}3TcM>VlZ3oZ`V2(0oj)dy=-y&`jEx;aY=})S z`N56$!X%mb2zV$ZSW!GnecYrt(hN-(!{ZB`0Eia3ljC!C^1L&+MHL6fQ_)Q(5GwBi z<*F9wVn_)5>K1RCidcn5vIT$2w31&KNN~DKVzs=-@rFyaD>XMq?-qy9ra{hwZ z)!5CL&kHa^hn;|JWP6}B>y(IKyu#vmJVg=;6C%l!D~#q!=^Mk%puEC0OQU6SUExU8 z51miqBU=kj1`2AprAAx3{R(0Hqk3^MbxAF{ZU?GF$4; zY}k+(13hP*Q79i=vISYwP`8#q?h(jB#!U_WZe*U&&~1MgxJ%de%$p+95;NjPN{rP( zGSZP!sw`>KnVo2{u1#EYOB~=$~7)E+*%;jJ5&#yJ&B+5%x^ak6G|@__&R?!eYoUthUV5u9lLVCJRi=UjMFD#N)g|~3TI6h$(i$GnURk& zJ7kK`OKU;^GNdlWKoren*JQuPmis)6nx&jTLJa5F@#bpSme4Hz z>5ozvMGvqzFpI<{hJh+;Gh_xuT`{`$FRTXUvY{3AmLnUt&sT<1Y6&!n*VE-$2%U4S zZtBGpE(qLfVHM@B(Niq#=?JnHZ5GA`F^IJTZ?!3L$mWO@%M;8xS7>QULg83xD)&Bq zzhSAt9VxF>4Og4)s`$f$5#3wH2P8KaolZ2O!^vD>iJ~=k`ME}4t?v9sj?gHfKzN$i z@|Dlqq^Lp?EldbBP{Q!eejBz=a^H~dyAd0pNuibKxRT}xzgW?6Qp!r4mflBR>N3U&!zL^mpVhbbT3piTZ0)1hdCK=&C#qE8n;5K7HDSa=waq zrfh?c8p(mE;n>J`z>TsU8=xQ;wqtmHknqRr2N}Gb9;;;n2%DTSx&SVscMH(21`b_p_tuL+H-*O37E)%&0{D`gpc3o< zcx{IOs8XQ6CJ#NCih9y;kSUxc#5GJNCTGo>0}q0``^W);dW0{a;x!EmhCI&aE0v<` z+gC7v-L)122m7*1*_>ePE+l5dT-aBnC6|o>- zFt^7RkG13QEh8=JvH3v@7w@Zqe&>7^)NLOvs*2s0>N~VbGlNYu=>!Y@MF!s^kr+Adbg^Mq`y-&sM`_;|mg{@bR2udjCHXLNGFM!}9=I0o0RIlDq&~hUf zfkN-s{;R=eJD&IvR7PfsEY`*yU+s@~`nm?oJJmoBMo}k>D-UI!$~ZUA%4U&$Jbhda zqqdup`@0-8i(iR1I&zfx42qG{XU^IQ)lT}IeY=pJ67{(!Iig*cbQfDU}_iE~^X_q-I z=aJGOBhtJ;#%Imapq4mqQR&{ag3+jbpDm+Je7?+(3%){xtJ}_px6DsOmp)ZgcUF|7 z+r`i~2bQ@J$*Z~aEq4C6l4+sP?9-MP1J~!*XkpVzfp0<~)imUDa^sJe^f7g|xv$^8 z-{qM5^@nZH^M&9}{0sS2|5=clQvOE%1ghWi@i+SayT`m0b^W&>l@$9g`S| z{{O%wt$%RI{NK3*b^R|cVfguXE)i7l`^u*fbVuuVM<6Lm3T>sOl%N4uS(D*>CmC7f z;NdQ_DSH@se70-bM~_Xb2rdRPAm4aD#3**Yi`#zfgi}>(5d$3v@qxxLZkM3NMq-(A zGGgS$-U#J{;dcP2-!pK2srG_))vFcj^W^P`W)vvbs-UIxBA zRY{9$(u;<+oI&pCenG7I7r(%fcm(>P{E-uvSTwgKN|^7}K3yXGexD)uqnS8%D12nK zJ@z@CNIt(&SYcod=DDCXIK8iPM$<0?vTnPd_RAw9aF9lY%8N$}aR^aRf}D70NmWK! zYmtCFWo?eF7-9)N3Nq++!2-soWXLk_5bja#64e6QO6*PAM(UQNmIrbaQVDh#Tb3hr zzXlgE7q#Oyq390QziogiL53jXmk5#6w<7#se0^n9T-%m);qDOJ-QC^Y-QC@t;1Jy1 z-Q6uX!QCx51b2ddDHZY@s;>u)j&M{3gF+!iS~C-Q3hQO}l~*)NA^ppnw>z!azM0WX=Fm=-?FhQYAat zoy*6p?BQRy*fKXv*bPh3ZPhG(BmFu|zeIlwk`YsIVS|!K=7Rm3<#}n!PYMX}TD%HD{<9YcbIDz26NKwdX+ zBg40yoB>D3yW~N*u+vB~22C~W1~LARy=zh$f{k?vqH4ip*wMN(I!VPpa4n}8rR=;C z@Im;7*12j8D&P}V8gU*ITT?_7Mc9 z^6bjsUf%o^Ki#Rc_-O=2i_s0f$S!u-E*m0UPra+l@+qB*wYlDLDtGyXr(NDJ&t4W` z3~lHNM5<}qu}@@V`HYaC%VIA1q_o$d%Xatel0If{mIL7kq0U~q)+$_YX@wzMEc+Hm z^Z$C%^RX;OVtmct<(3j>G@^b7JE3@w9+U7XmEG8;JqBC799ooTTt)bxr@OqGEYdCm z!Fzg<|7zzKYPRuOSc1=O(feLhP^0Zs@;CD5e0ELyxA~*azmR|7pU96B@-O5s`CJhA zkGjCW@<&HhF4k`!@pLFgy*nzjH*2CN^>==($o)#vv}vNA-EDrZ-rlzZ4wNvrh^$s^ z`rCR)n!Yu{F9zNm0jI~&z*BpGtELHDtbp~(B`Of>&ez5m5kg!om;y#ET8=sM9EJ-P zT`fcE^{bBU_~t3gF3VyMVYJHP~W5F z1}TnCPjte~){cVF-fH1T_<5TZ<1{v^#NpF0(ULQ2FtDlfFX%RH4GpUMVQe$KxDus za4y9!drDAf{2EMg{rWA}IRC6L7S*g0Ccvy>(+t%bg?I&cc}$7?JqI(pV#;SwsBfMZ zVK)x8*A%H39q5Rv&46uHFt2a{1p7;`7wU~k{BIn|F>M+Rs~Dt@o{Ac9_JmC0?^z}N zW2^gFx}j{!r(V4!MXukH*TtZ0ihb{Hz31GIsYK7uGJ%jMcfwE=LiiwD%ckLBY}v+n z&eDq>tW7#_I6>1HCmzc;z3IB!+HdZHEuLdyg9;QDzZwQzL@)h35@gyHSV#5Jk9|Ud zU$;DJ#@cE2S0qgKtRwwsc-iM$HQqS#ZPK&(oXrko*8GSX+^Y=50FBdb zk44T1b>;kvuZGP`k;QDP{}=HuNa?5&YkD8P2$WLwD4^XK-y~h3Q_2ID|2yP$mc`VMzdq2=P|h71&qg?d{Pt_ zJX3wuqRpjq=h5JK(J)NWxEuN#No)`%e2uLizuOkGJ`$h==l* zlZDTo*PA9H_uP-Pp;5a=N-{MOqgB1K8FJ=B=&D+KG6%O|Ur5Z-KZavMXsuh|Z&#oW z6JHZo5EfF1o@umU`;&rC1TbzXWE45#Vq<*3Xxp6167ks2idNjuk4zo{{4!*Y=11RB z*NMEIG-UQZJM@KGJafW<1R(+Gh5KqRY!goz621NXw*n3)w(Pdtmep;I8EV%t05WikwDjk1(fnoBFvn2rQKeJw(9tPE-xSkw-)o zsb2`6iC9>%R(UT7ul))W6&SN#9MV0brP)ZP&-@;O-??E+bF{Vp>2)k=_vX2w*XA-v z7b(PGz`_MeSyv@oRx$7j$n*0C5RXO2kStSVih>`@%Tf5kf4Yfdgz~L&H<<>$HS*O{NOSn+UMiGS^|&iFD}?=9R%v%L7y>CO_TQe!*lCs)7G zMi}b{bRQ-%lXbMb6Z8nH>b-+F7G_h@v~NEj=TX@a;rKX3I?%{&DJ!vh$%Xs4K1{ff zH1q9lrq)y>(hu+VW^M^S%JlPy9}gxOIk}}Y(E?Db9C#0DPDtkNKsvorJLuO(qr!N< z_rF~;L34Wly^0w9t6+*r_J67fBHBMHg8%7%QxQd<3*G;YOJe2z;u67satRUbzqo{- zo;WihJMIIuGzr}52Xls;^2k?g9-26pT4`B6Q3rQNw>yA3NM z=^+$U&X9$$Yv;VTSR_tCqgUP#$fb%_-Ca|$t7ccP0a|ISDov&Yeg1@WE`pjCCMo#$ z^!C~?Mec{uM8(eQ=`iu=Yd%W_a_Wa??#Iy5;9Ub`DBQ!C!DQhI@N}&kZBvx*C=^rH0y(C zea=&n%fBV*J}zH!CU`Y215Unx))f)Qx{q`o<#d|7=#X(5^Nhz$l{8*PvZQctYenL3 zMvbII4xN+Ys9xz2-sjCevJb}Y)R>UPp}X6V?j6U5&Se~|X&k{SWg;gxw=@W%J+IT& zqw1rmKm?9i{xrZAtWaz$jA|3{K@(%V5(~!~=$Il`UBl4EU(v2nGIODXgq1tkrsmtw z9p&TY^XYPiT0R<#WMFXC_J0=bYbfbW1}XNvKMzG8D=GkaRh>ufMiH?WL~&IfkP1i} zRnmDDKSHy>qc7U-Z`GkP#^fo%QHD^Wuu>vKr1M3*r_q`u<#3{Mzb+aX0*Zpi?*tpj ziD-y}qGFq0BjOBw`fLL!(W|ncw}H>9u#HljdbRhxVNPk8Afy{8=+M#afxu&o&v3|w z4VxyP>s+6lxK(BKX_h#9s>$fFuY^Kx)M;EM8!;x9Qs&DN&nT#ZQaSCHX-}TM z?fOjNgv`PkJ^-Goew|3>)y-E1KxLbNNLAjVI{WQ zuW!)!9&ZKmU(bs(f%P3mpGnE_st@hT(we@%-N({#&6+|1q)^Ekdd9O3?KRB9WiTBQ zDl>;(9@!)9)S#V?1&K9jQd_^%7C!xY$Y4T%`~}ZgP3wyZPW*zvP-6I?@XghvnU1aN zB1WLJQTZ8>TWebLr|Cs!E{0?FzAN=A%MuYY@)f!sriv_se^f?Rbx{S@dQGzrw6Ika2g@r4aOSA%akN^rIlt3Ev0Ypj{oJX&g zvAHpz(6D$80@(cVE`NU;0qSiFr%U2`RBJMU*7zq0!Y01hOLS;y8?Vx499U2E(31yx{| zs28*(D*g6m+o0WjD`P)*7vUum;8D2t>DPO=c@n5>2%{t%GD(1BZ)JAN6fJ?qg~X4p z0UmRk;g6iJ@AC+>{H!;jPSTY{aih0fz{xgNLri*rmvsVmv|r=OVe|?yMe(7MQgjl# zFqcCg?l|rD-5}XCIUU5Yc4gf!d;0kGh*=_%XpGRgdqTcgNHi8 z10p+%_}K!O?{X^f)n2&vd;#x7K2?Z?{h%B~QSj~wA z^5D(;R+!a4Xv3^EJ&#)-Rpd?`-ue5pp6^nE;8 zP|uh)6E2{_WMSif==}lm`!@%^`{+&N=;Dw`!q>J*JC|Ob&K3kM)7G`hv-_9zGqscH zYUNez{L}7CsU(G-&|9CjV!5aS3^2w(3{pmtsSbEH32~|*p5S=i&E^Os(v#FL<&#fC z%+H2CeBC(PI+s9j&lj8Hy|+;-0xZ6AlUs3uz*lc9Sx1Y`GsI;5`mh+1>`I>b)_7vK zazVr(+x`%O>-o>^^=HY0DdV^SX8ow(`W5wYLGrggk^QE|aN)kF9vAkt>`UsMJSQ6~ z$esn8@aQMPy`V3HxO?AYRQICCpd;ad*LJ@G&>qt0ap|IUrOny*sYRz+^fFb+W*8w= zbBe59Dd?s+yBlE25MdayCy=teyMjDj36fQh@eLN}5j;fN6NyLRZp;`#pM!vkw*~1Z z&obXK{0_(0?5A<`T!`JIlO6Y()c2bw^z`H$yy#zDy-gyA$@=U=E{iFaDj|O+X{qNP zwqqpIOhWcinWuZN9|v}p1R$VwB1L>}#I$X`TfFOZyyYmA0^82%$ueM&2Rb{ozBi78 zba(A-bt5R}`~r!J5{v11e;of!qV9;L6PiD(ar)3%thci0ewHo%q+%ozF<`tR3iP$2 z^8qpW!qEjq>A_kq*DW#!+>5df|2vi&vv7gva-`Vi_X7Jw&-;NM+%xVVL9K+dap~3ZLt@X6P;mAsyYv7p%8K*;6*#`33#Y@u#&iSb4d$x6{E{W z_+&Vpw(<1G5<3HTZCp0f3KqP4c^iz$XdB&eUK2c-g#`*|_znfmf@rfIwO8#V`L&|7 zskoS@KgG!fu{y&m1Kuota?OrLa)AS&AE>|4Y{-8ki8c&n8w-JgP^U7vjIva|dY;A= z3F!rf2cJu(G$0kgyCoX!lx&gKkLtan>VK~ObK%P=E?WQK9xCp4@ZSO-7#;@1uyY}o z(}8mFHud(TDhjK%t+YNT5>SEpLGcdKr&n{;uGTSrK8zPa1?bjjN~Jz+$9M(lj_$w% z1Ta8eOY@1*s5+Co&Dk>vjFUZEgqWZ*Ouf)QSd8lP$68_r8sWDP^}!Gw55nO03-YQB zw16F~u%TDlmeTxBPa5tyB$+*I3(G*XXXMe8K-Kmaq#E7(MF}U?`xEyK*E+jv=Ni_aO{xXPvuObZ8{vdzmUmeT-p#OiZMbP^GQ4uUf z|431GNTpd%Zvg^EFz6+5 z_4M@c#6N;I0iCiKYl9klQ>49r>0iC%$90Fe?3Z6Htl#!H_@%@yWpKe#_4$hYdYQg*;>;8{LU|?f2D2OkjifuCzkm0 z#69mLcHM_{1Y{e5o;do)sIFPdUB|~;AwCcNO#~IRBw_auU+nkYdNqYID=LMoen*Gf zuP>S;)7e~Lo*F7m8clT?NlO9T*_iuQM7dOxD-egs%b>eCAJg+SLnbXcMp zP`U=L2SgE#1CJbWHqiU5YYm9>(NI@tacMTu@*y*gX^ky4O_An|^crk4exXQkribMr zf;Dy=1T{_b+>bwGC77=eato$Xq1XpDw$d0MK#se#G<{L=0obk4BpYZ-3~26*0kLVj zRf}uuTEAehyFh3l=vorSm9!8DJy$rKf;s~cDXY24arPbJm&wG96hj0A31}Z5uv1IJ zI95PM+N&k47I;@o`g%5wY0W6xYWOq~sgSLxpuH*kzSxUfDN~g@IV(Y_l@{0Hi{SAW z(ELtZwEzM0i`_lc5mnJtOrtW8}(ADQ@duHPZ>qW-rCIg;3Q8yKj-$l(r5= z0BNl=SxwG2^2P;(fp~Z6t{mJ%b6Mi|YC}!)EL-WmXF-f0>)|&BcyLD?vF?hMjgVzf zS|oF{LS)odB2MjbOX+g`WFqBDFX@>z#IGFivo%u@^&dn`Y2rt*J{O9U}TX|qL(AB}KjSO}DS5F6WyhPf8e zc8y@MiNOur663;({rZNh1h-De+hB#tB2oDBOk$@}`Kahx#L&*b>bnIj*Ecdaxu|LQ zgWYNjk; zlX|@250Uk@KCZ2>k0)}GuNWd+yR-{C(f#KWTsdW$AgoacPK(nLYKjhVz%E`+7O`;H z2-sh7M+R=W1u@{)`%Hgw)){Aa>+#$;@phMiX=iv**YFh`T#Py2Q%ncn)ezq7{F+NT z)Si*}E!`QaulLBYo*a7y?bO(V)Hv8z2O=zs3L}=Ko-o*A9O zeT=}YfIQ+()iDZ9m0p@Y=yht`H{xDq6-ml^B$=L?Wy4_%cKszcBDt(Y%~sn5B4vKb zY=N2%c8 zS~i6HCr0D#L!lHNV7!P4R+vTUAUZf;XT@(6iQau#-P^5NzCv(FBo&zm3{of z%Cc5lY)F3Y@4F>oB?$Rw+-`HQ5fP-eY6#Lc69W-O7{H?iw6slS5lJW@Z`UspQVrc~ zdhtNoaO=p$FEO7gK2|*K%6AHpI_zG*`n#Yb>EN zxRujztSvvR^8uG?(O0(_gYY)ydL%Fv^Kay@q5nUSU-2*G=ldt}Gn4)c`B6Xp4gO;- z;;;Ns1T6U&=8d+lhf0#pwa$Fkoa(4z)@Oke2g9H0`n#`7{i|Gh2f}dQ9n4p~An|i0sth>)`=4 zUnM+DM3_W{WNl_HuOqmYfVpP| zQWf&l3r+B{f`gv)>TuaVMZkOmRj(tyI-ehI0@SdwO_v~A)hK~-#XKg3SI?kRtdHMafgvAb>3s6@rNY@=gIc67$2HJZ>p?J~yq z^RC_CvLUeBG8t=xn>HLk#7PcA1VRMEMU-a|1g!Xk6m@TC>yJO22^_QVrl;&Z@UHpw zY3xIBEI7Ib!<)M9dot6s*_jj_OggcWO;W8x{6U87&ZMvka~#m@afHuWUYV$3WC8d+ zRtvA~snT%zPwxrb=Wg}4+xMS?xizVdUk+qSvvkcO!PGN0B|yygC_9sFvYa2RNiNV{ z2vg7^U?>%XVjw9h6!#Taa^|W@@_rQjD0tB7YtoH2 zf-A^65q4witm6~kfd#l2DlKIyf_Rc;l}GD3+$+YW2e+(FC^vYDV#4TfkHvUT4*He} z5t6l3@dXz4WG#wMb6^j;T;0(rE2&~Szxp`TTmhBGZyg>Zo@8(z5n^N}eZ&O7c%(+L z4&;0>&q#Yd`j(Vzg<5o0#mqoeZsD&581W>DstmH_i))|Z6NZEM3%rax{Iu`W#I-uu zb0iD)O3w661rL|*R?^n;72n^(qqp~WVVrp)7z{Li6TpAnsX*3Bzhf+x$gP^IRXhpt zmoGxZzQQ-T8Mf+mGhWlY=MDBUFBRkWeU7~U^g|G>+1Vk(rUTisrDD1pdjuvhs)n8Nx6-?KsnJyv(##JO?hu zNg$Zs9$$^3C6341f8iSQWSVt=DPRSjHY)OD* zv{M{@NeanU_1yhF1)Y7w1hTyEaVP*vvR7!MIqB%Zl4F_J432>tJ)KYV4%-Jx`8rtM zzfq`(jy>Gtd+P0hb11Bp_g1tG{~n5yfR4UBB1IZy_oI7(>eg!ZzX8K1}e%h z+kIJ7|De!f8OeI;8xM?%9v+WVkyE;E)xxdJvm(>PbMMhZml&OgZSjQ2m+A~+`hs0d1x|4l_4iT@v5qVyM+@cxraI41waC4RU6`LNGg{)sYXj99~K>Y1D6Ivno<^2Rg-!KYTK)F0}0;U2!RBhE4&ci&GvDk z1uD7^APs%JLU+XX}!fLhh@uf`U%J&2)Ufv5-NS$28?`N#7ZXoEq zYprKi(x3rkWpE`?O;X25+`otNfi7khI(``l%w_@`H!>Gn5xRz5huS!xhKoA~Iy??Z zMXZPPj5LdBE1vW;#b0YrW$Qdj;E}W>%^qW|RU+?;Ww0Vj*0U1@bTDE=olhePIYCLP z5MYC;U_8{Vaq1E=5eTB0P(<+(iYcPj!iIMp=k?MuC{3%)zV~YPHe`H!T}1&?KWz^+ zPZ^^qG5%PJMwvmh~~wf6n_g=xmgtSqfj*#qV3 zMJT0RyHD5SeoWZR?`1?f?5yS9_asr0ZLIo-y~D;dK9^5qoW_p>6wl~NmI(B#c~(a= zMwl7rlC^!Cw_d6CzMFiJRA5Wq{OxN+il~Z>PMr?w?CV`DjNW$+@NNDomMs=P$>B>l z2^mSc;2GDGUZAI(X*~_j!jl#Rw&mk6C&S9s?aACSa^eRJ?Ble4eI5(=59quQ z^hH*^%`<32kb-OUlc)su1P^W+&|z|ei#E?9#(|%V;Y3Yjd3xbRNB)M!dZ=0(N`YDS zaP-U{A38x41-u{#G47-c6R&O3E0x}v76+< z-zUjy8AE^BDO0qdHSX=f{~T$b_X55PCvKMuNNtSwq!0 z`UmcTtna7Wb(u8Zu;AgL2jt!pUOGMhl!sZXxE!Kn&h5 z4>B^>XnR=R1RHkCy(sJU?7LBrcxfagCvnP3U9!V;Ra1W`FTE{CK znO}FU|D_+LMi>d6@8)5N$4~O5_>24>qC|>2~)E&^d+CP}R%)gpuPJMHH*n z34tP`IK#o-)jpKCMk>z^mGz5|V>C^)zKS>f!dscsM|k6_*{FIX&ir(*YA<=)7w!+w zwz<#?5QMDLZ^mBpJ~J>c)fTBH<`^Oy1{~q`X?ab-^Xrhz+P8){v&@56!7R&A%&_2r zrpbqQpq1(x96y%X|9mHD4sLR*yy?2J+Vf*VlLt}8X>?eZcjAuq$hxKd4~{8+6) z3hA0vTpt{Kz_TagnbSJDL=n4)7Fl#_-+ff&3!LCr-(B%Zi9m`{gJDa*L@{8$VMhp2 zHKHQXM>E0P*+g(SYv>mzL4Q|Xw&fZNA3%I3tz$KVnrSrZ;<%lfZcO`SuWWwc;$HMl z+`Z<8-TJxSbB9Lb4zw=ev+km8V2i2I1UD1*KzgE#2-zf}X+xn%N~z!R-NFO9Ta= z+>2L>)~Zyp`+dJM7^{WOR`fmvb2zAI{@_tNszEB_7aB5E-RI=H9vbqI$we3GFQxf1 zgxfw$p&|0|P)o4malurHHT_x>5B2DDI`TE5`MAdjKcu07MXtoDGRvLDa9;DZ9^qlM zZ1N6b`)cXsiiB;jdQf^DJ8HL#LGv_+^Z;uC@|r?RcRpgmeg_WRw|s*p22awwT>B*@ zf=VMd`xhw`SC5lTZ_jLx9J=PF74N{KI1`WXsObE$?po;9{+ul3sUPZnF#Z_Vr7n}b zHfP#U?2{mMfj%nlIhE_2H;ZnQP%IKNx*n0;)TrQHiiYV8fnqtWfRkDkMW;)*{ltpS znvQ0|^Y7Hm+^J5ugyR;dJzO8&@Q*H!tqb#zV7OBAAkY=rt8QS|<)i9Z-cTVImLaLf zo;-EEFxbu4%qDc{2CuCRB|!EEX{>>Krc#n+<;|n5Q3Q$0asjm9d#E%t7(j*}Hw=U* z$=Mc>D1aS6%-jI#K2KfrWc>ZHPtvgQruGXj+>r^b%SZDX3KO_LUGEc39)rPIA+${# z_Sat+sK9mrU_XfTS%V)|c_X?^N9zT>@qM-h$Xn9bQZ98Hp^5RK{YB}P)(G%ipZiss zz-c8WnxQfkbVSoDbjBAxTRE_WzXj+T4ZaMI3KpiU6PI&n!HmOSh)SSrs3*y!@$14j zkB46w1gecnGbj*gyaDGy(&VKa|B7*>cJ>=>t1r-#^jedQh6>E#k}*aaL`gv`(ZbZy zqAn~hEiL}C3S$1avRyBon$n*fUYdeE!KZfijIsdJWv-bEs>+nk$Z8(uAf~lMt-rfB zBr2{QR%~#3aphHdacQX8M~V>@Wk_kz>Lp1VhKfh98lD{NluC9?nHtdr&01h@QeF8_ z%W41sYz4mNUqNkIC@!i*H{WzBAk2Hw=)ExznsT&nC&L+ zslXMhLtT{(&?H#P1SiX>U$iL7s>Jd|6$E&#RQ|A6B|XPi8}PEPqT26|Ry7nQ9U0=g zQTzrYi!eJlb4BIt@XLN|V8tk(RRp?`_1lptg+NFEwt^$JW&8FRD^7iMF5gDZ?~G(ec)Ub7Tw$CnRO zag{|o6V(qZgdZ2wk1?d5Vd30>GtgBm&{N(7drVvl=|xDiSAj*VeoBt&nWJnUYl z+^Jga#=x&+Y<~2+BlgfV2HXGsG>=AwQ5|bfm{v-{6Xf(>gi{ykraZ#9aT-OVS&m}X z@~CFXR$i-z?&S)Uf;=;>j7%j?@v*&w@lJpPigMjZ+JIui)6a^bYiR03hv>T5g69tu zi~_!(ysX8tv+$~&!LexEj!K+;u6Cht>AZP>IBMo}g9q0?#nOR@)*hIQG}TEqXf!MZ z!xjzj2jK~HAjU}J_5n!kP!YmWl-ri)(RQL4i=TcNb){!T+pt!rRRham8U`t zEN1w@)><*-sw5@d*P){I4K7D_RLSP%j09oVXSXT@snqNNPSNv`OE_Z35puv<+=t9? zW-byca+8K^7BVtVXa^6>!z!d z@b?am1f<`s$9yCLDQd>YD6(?#rhPfI<2zCZh^qT?hXKLpgGLW^g(ZF=d4=K`Uj!dZ zmnagzCfaV<%&j3ut@uz_7cgJpV9Aa#;OlV68fB9lEYOHLXjE3B6~qlr>cQuhjL0|E z)NRLtjN)O71?;8V^~tmCvoHEAASex&4Wuj2_pod&{do$gS$~^dN?@z}ioQlU2&L=> zqcKrb{I)wJo&el-lI%zNv*x?gFOkM26EHIPZ$YC*YU6>tQ1f|R%MiTCJ8ic=)?$u- za)T=N8w<9w@`1#@#2KH}W3tj!!X|g*#))Qz+H-FesHx27qIVY@Hl+VjqtGOC*g`Ot z%jDuYnLixFzZsQ1*yB18kwq`LAoMY|dJLV^*cg+?3+A(Ydwq!k({p`DJ;wxoNb@3r>-1pt{SZ@1%7g?lylJ8~ zR}t%=oT85_(o3_%uBF>{iqZtG)IFN>lAYRWqKYS`{C2^XD-N(JWoiC^S)lpEuDNJ} zNsKM(hvI7qwehDCVS+8B19wg8Z$Q86jh8GNxd>iyEtAW%DZ)NJj(*2kIw$QSSvii4 zlR~yPDNQEwykDhUV0ToG*U&%^G!(W3iuwJxYb;f5xB}jG*z`u$CQj|r@NIx!U3%JG zbW?_yh^}W?auXD*GLi}E&{*63I3X64E!VXgAQ-JbiiT-u8c&Oh3c8BU^_YgZ@;s*! z45t1D4dycbp~FI}2zo~NIFoZhHqBUOz}~UX8o|1oK*0LoIgQtTBB#LW>HQHG^7TCi zSS>JQI%MrXkpHuy>ED?hQ6T?8{&~^=sUjqY{$!*ubpAILaar}Ri2vPz8Nf2U@+V^S z^&dm2KT!b+uYZi0LdN4t2mBFX<1`Wfj`(Svr2EI5=I=4y!u@-88eaVqFSXihG$XB(G0Uf;uc5OGt*pI?S=SpL)X zM^weooQr_ypJqQMtW1V1e_T)O?OaUlTxeB19Zb0h{?je#zh00rwKI3I;38mVVPk@W z)}U20bh5K_{yf3P!pOqR!oc=99^pU3ru`Gj$=-&Gz`@nX#?qLEz}C=%_S1cdmxFZT^2--`Xe-o(Jn#mL6R z#Q67{K0nRQ)Y!$+-j0jF+SJs6*3ib%%@htA4*HvdoH!gb7WC&|08Uau>0N}Bft!%EMeDa0&aqCVME0iOJLc&LebA$kBn9`i7efL__m zUIv`74}@Pr={i9E!9cw(@f^~S{-^)}uNa{cfPff~e|8#~G9V8WU@&fMv9%30R>8Fi5JKNdW3v z0lia*Fbx1mSO9}!cnBo`)Ei*%gP7O@5S$J`6}wUAxu&i`Jfix%Qt1sm?IfI{0ostX zju7hVba)g~lIYZ^Y=*#w83J_up6QrO0W1hVZzlkNym++FZ+rXZF$G^UHN_Uw0pS=dk&)5f`@wXjg8%@q_;nKVN2msu4+V?F`K!W&N1no?_e_ZmviRY05{LLg2 z903455fVC;u}1!pPk$?sya1{?LF~Ie44PhWoIbFPKA2YnwqPNW!G0k`A!rkS947T7 z#4(WQ=^;;qeG;I^kt778;i!s4(($Ykj7kwI1ga9aH@r^3oMAd*9r4^h08D|epxrshD~GY_)T%%_zug;8*Smuv2Z;7Uv4Oe^5g6hLN@&Pv$gN0} z5#W%pAgw{MLxBV<_LCDOmPt2}bs+s7#4uuF4AYRHAzng`MWILZ4i_F|Cqe!u%tYQE z2P|1%q%wy*CpKrNM0`S1p2;GCMVvUgW@6m{!I?xuK>Z_RKYM>*pLrj9pYTQ-I@?IV zvFJ&8mqKaGN(%D;@qpz3+%%0zP_-~aX|pU}1&K9QOQ<@(MX6RXtPF+4HG^|9=!>Lu zadkoRJeCEz#iM2Iu}cTiW-^{!>Wuo__}|F?!1aG1|Jq;3FaA&Duc!SNmk?e26ZucX zk5i99?%ZMFLeYXo!p$i#Gch1A%rV_DYCrcfV9rNkj;JuOGoGd$r6Z)VcR%9!jDq|~~mvfhe zRhXCAtMC{3X})~}*Bp=;kQXje%&XVm>l!A2F~)54pdqw|s|zi@pIr5Eqv}k1 z!|jaZttiju6lRuUR&P|gO)}$$*h_PK%5sr|I-)2|z4eROLcOHirNk~_k!fc%)v>Nf zNlnqHl&Dm!6hG}|t2r;tF6kES)O-nm7aWly*C7`-kDUU`K2ly)-YkVK{UXcDbzehQ zVO@-!-znn$-LCW^EvHA%KS##Cbj!2*=o!bC{!Qi0_PO+4999Fo5tame9d-(XXNc9& zo(GqHWc^xESa(1`xY#h+Fm)$5zI~Rba8WE;3?a&LSZ9x9&vw{$xR4B-jF3!P_LuBM zhLUW131tbl47be47EMc8OPi@=!@gFD=0!`F zU5FN^X0e8>T%ufM&XV~+-F3@oOL;41-N6d@dQFRMi?5-% z5xRNn&}XGyVt1l%hi~*7F9clJDtslb7OtOjlM^bJ8rN3FRMrK*@6cV!3B9?-G~)CM z^Ii+eIOU*awsPjYxH-CcyK~TOc;q+fz;U`%KDtco;msf2`%*6Vj=DQubNAVqjhX%D zW$U#UY!{XnVH>#~N&l-CNO9%fjotfsuqDHocq>VHVCmmG{o1IP1_Urj=*lzYN%$_%T8vPGl2haSe z{=1OUFXeQ|G+ZeHGU0L@Nmqi1RApbHNN-TwNn@Zr`*HitiRfq9X2Vy6b*S*};~cpkc_*QzI8<7l7FXl2yUL2QfydEv9eNK9j5<^eyo#qSqJ_$KQNHqA zJ+;QKo6JkrIzX>)EU>Q`$odpU75X5=_jF_VTvuL$UUR$I@HsRan-A?o<4P3QLyqU-dj zlN9TnK`p}Vh2MGFx=c$)rzW}iRf<&ZRGwAXF6K6y4VRznW-qY4yB=817T2}A9^d#z zd)8HfRAm><7ENCyUm73zYra*SJf*zY>Q>9yI-ZY!%z?Z?5W}}Dw=XQ>&he@!#V%2w zio=iK_q=Ccy3@k3!Kp@P4et^=hJl zDrdZC8)qy{o3++6<^0k$YSBq^pv_)8MRzH37k(fznPB8PR{oAqx-#KuwKyO zePlefZG~We*@5V?Gbj2HdBoktJ*9Kg=5o`pet@Fhrk+vDrEmMo7QIO-nr^deo@^e-=%*ia4xtiycxF0|Jg8P<#OYzRj+&A z?d*p5BX~&A^n3h$EP^mtY=|dPY5%8TIbeUe5d6H{5YA3%3#E_k9Sy zQhZqauD@5FDSb)AOicU$1xdSoHD;z=|Gaht#u7@h0Du=M0O0>wGxqzl+SvaD0B~gh z0Dc(&09BpFj;jWoLQ5DO}57;YFOP})Hj$>g7KK@@@Gaor6y8I!BooVU63 z)5J*EXY$|Q-eyglKa$G9K_NdUU^C(j*={bnghYs{oVOeO!@pD2YDc?sKe~QjK(N>1 zG$}hN$p9g#fC(NGhePUlAAn6|>joe$;h#C3PSyH2oo7$`-WVMH4uhaAB-^VgoZ|tQF0yzMq*Je z7V-2d5AzV4`7_~gMZ(Af0TdR|DofQ6p);gn>xYC&pnSy(Ir^-(D%U^gjO>U@NV+urQTkgI6>)Dz+->=VGZ|9Yr zAFksUcoh+4L_`2U8#t06rJ%akm=E1v{CU^&?%e&z;PK%c%GaIF&p$dqQdCZ)M#v!W z@7w(M|52zvg!kt*j~)M?z|mp#C!wL%@%L^1CsFWU!0`%!9|`Va#Z-svDT5cGcx8Gt`@xtU$AikE>~x zK-VEteATSP$Que8ak0wK>?&I*Q=8z*2u}3{UJaw4N4T;zi~&5km0x~P$~AGZ zYKQVe{_M)AP`R9|EGXi13XLvS8r=<=Dp zm;$e6((qzv!D*4S#A`r9%7UAd!={99#S)_~Iyfxt=$ZHRhqHcMUk47$zQ8&x8l)z$ z6{!9kW;55=VvJVnz-mtMP@thPwEIh~Eix-)Dg`_j03I+6H7AS4`ANHcf63(&C1u(6U+)-x;Pj^*&+ilM_L=&@f=!Qki5J8g zCylC6?5zp5C}%6zY}hv~-D}VB9;8popSwvHB z#Kz+%k$!aT{>$2CX+zhvb&=AtvgDMY=DE4ekNXe*97TFH1mtZh-GZdAK9RNLeCOU( zSF7K9tM#+PjT!fPMC?K#dhO*!W6zDfT-&URee@km^v1fboI^Ryl7f;eBgX93&Os-} z*KFRtheG~R_{OZAES~lIg~_sZGTuA^-YazitC?n*qP;06^$=2yFO&X8-$Wk&eUoR^SVSQ|3Vy&V5J_O)hN381@~L~_cUVROEtCGa41Pi04gVj=i^%dG zu^m2DjGI$3^SH4Z&v-v79Fo%1mYl$kKh82A#7XHJcf;o>Z)x^DmHHt~N+EMh%bE=N zB)t);)~eNNnL=sMYM89lIIyfVUej+@s?@>tE2OtU?PB(wm3ogj11=g*8t+mQq)eHD zVZUPqo0X=bTnu?jPS6FMXpH-Yh8-YJ#?aG_yq8_F@*RnkMk|(huHZ&T23;VI4E9^5 z$h*;zc8L!N>qz|xOUy5Kq2o_P$FE)K_(?C+iAqW1SuL8npVZ@)aBC`ZDH5*7!Lk~z za!GiKm6(E;)y!2cMr&cIqOiEyU@C=ea4WvVC~0z`qEJ;BTd2@#Z(nF+(}kwz=g2e~ zl~yK?nr*YlG~@~;brgM-5-hAU@%DQP-hS7?vKqegDYQ4AXSSHD@j-_Yn{AdZCdLqL zv6?EQja3yfR#Qx=soZR=sy54PrQi%xG%Pz=SH#6anhn<%F@*(Lercr2n>OuY z<2ZyCrXgz7wTTFs&r2@L+%6*>qYshJDOP0E0aoDQencvWd3 z@OcU40A>|_kyEW!X_Z=s8Yk(>@k|_C-KW&(RQ9e0l}*~~Q$7#B0=^PR8<%@wbU*kv zJH(I#s`*sQ+IQ#Rlq_#1jV?wOBTV~iVj?4v?=Peb--abdhdB5PiqiWE4`-b}R2LMe z-(k=HO?uK_cvQ%B$t`qa&)=jXUu`MB8~$XW7=a`al#%8-;O29d>lN+aUdj6|J6R$W4S{dv-Sa# z@z{XINxsK$)G7uLwQ=r@+AN3&p{PX$jPK+{tj9=VaQ@KIGp!ZaW9<`e@K`SGu_aqT zVl{5`C{K_0P+PcRoR5HUUJ50F*+VeSpPmlA_ch!60pZyu%3-#tgV}~IP=p0-7~EX5 znZDe`_pgC{;J|9Y;xGgnT-(_X4)+io9^!B){}ph6ID}VNaF|xULj&L-#xMX5^W1`i|6S5jywNQ<1WbahBS9Qk4Okq8K!a(J%OQB!2sGYe2&Ik&uj_-)&cS0I zH0pFOQXz%Kcs>G#2YQ4+Vnc<=YmT|nR8?*Dww4>LC8jEyxT+eLDiK`=4VbO@kQFFU>b;n+|;eAYfjLf)&Vt|qrOQ7}eQB5$-~ z8PhY8e6+(oUYHN(HiR&+z?`5D-Ygp|25ZhkA2=g0@v<$13fku4K5|ZAjB!6m1xSxy z9C$vWz7C@ZpXvwaRzLcEepyHYd!8|5_6b;r&$0;h`8?_Kdw2KQ@*4Ds=g~);_%!Zp zJnnOJCWGaP0^n1my2FSkKZZ}$RzV(pai6g4SHiLEnyHNJQ+Hz7S=Nk&eaarmaJ2U# z$Zo{V$v>;wULaV#&S3?b*O|MGQsN&nHi}*94SpeWDdmI0eXUiqtBqz0#D>|&Vm6!L zD=#&*;^GpkXK__kak*Ixj?cHD7L0dRi z1dSiLDM_AK`ue*a5^xNbE`W52lRX=NtwABnF;niQ)oE(0nGAP#cM* zSU?+hmjYd-lEz0{vnm?8qn<5g&=!j|q3}+e@ zjM&N<0i2$-%RgfwF@j_!E?5VQb`R_98h!xU3E2H+BDTBi3C`|eq{EY!;UN9c)hMgJsfNm3NFI=83Von#9dy+YiuD4H6}Xo^iuW_5u6 z={+XyM5zvOCt84qAOZdzf+DV-N{e7D5{pOvg5?AMh#tT{o*s7oiI(D#UdV!_#Df30 z%Yvo5g)R63qF7JqUUF#x=m8e+^k6Jl<_A&*Jlz$FElBtyBlX}W?CA@dMxOzCnw}Mb zPc1NGqen^O^J&6Syj}^`8kkHIUoS^eb?t&4vq|Hdl{~Wq_qz3N#;mV)f>}`&Q;zNA z%vF$*ErcUscj{($$^&O9A))8jXuOC_#k|jwyz*$UGv*d@KymEMyqfy zXEPWZ4t#`dSo+=(_--qcXt}h3kuJXTEJ&Y$o0AQ>Z7&fUzU2G@7$vOw`Pfd*FD|7+ zD$Dpo`hdx`57Q#O-6CGodl{A?T1w zBL@a>r!c10z`OC7N|b>SiKmQxMDiHc32(c#RURCPfVpv;!`v7NG$k1hl5~W)Wp4DG z@C>Wp%_g**ehkUnub-DKiq-FCSIMwu2K*8^{I(eYKe8?X{JwPye%>2-2g{CuThtbR9(AE%!kzuj)t?|=J$ Btl$chmJKb;jQ=OMGB$chmJKb;j46{_nHxD%7y=m>{xdFToy^a2ol`Fr$g^hpvT*Wy7H2#>_2zY~ L+t;x&DhUGsN`e}E diff --git a/test/test.rewrite_2ttl-hdrfix b/test/test.rewrite_2ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..9a3257d28cc75e946c3c9440c12255a46893d5c9 GIT binary patch literal 71888 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� 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 P_2zY~+t;x&DhUGsst6f` diff --git a/test/test.rewrite_3ttl-hdrfix b/test/test.rewrite_3ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..4c0b63096f542b5056540829560141efcc20f935 GIT binary patch literal 71888 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 literal 0 HcmV?d00001 diff --git a/test/test.rewrite_config b/test/test.rewrite_config index 164e13b334450415cd24459b4a0c526e287b6c2e..8c92c6e8a77151c4911f72c56d0a5faee1f896bc 100644 GIT binary patch delta 78 zcmbQSon^*$mJJ)28UIh-$lS^pGTEHvIj2l&AOiy;>a6ZCo*o)+^Kn*1M#lQh QFWDMI8QnH#$>{wD0J+x}G5`Po delta 78 zcmbQSon^*$mJJ)28B->2WNrnL<}A-S^-=>F7#NxK7EM-Xb;rfqe4JI0k+E*`OST44 LM%T?*GJ5|3mG>5; diff --git a/test/test.rewrite_dlthdlc b/test/test.rewrite_dlthdlc index 3f2215101c35d3cbed5fa6b7fb623a3f2eb77bf8..61abbd45a88e50329927c0cf529ecde36c2a6e73 100644 GIT binary patch delta 85 zcmbQTkY&n3mJNK&jQ=O|GnX@lOm1gB%_)-_$iTqJq`zhIeHQh}M_7blB6`?GzBATu QzRoHj%ILQFh18m_09yDPAOHXW delta 85 zcmbQTkY&n3mJNK&j46}(nahD>JM(Eyz0^Pk21X{mMU(Hds82q^B1AysJ7eAE>#PEz MjINtsNUiw_05e1yk^lez diff --git a/test/test.rewrite_dltuser b/test/test.rewrite_dltuser index 7b4fc8aa8d8442f79327f3a2556e039529540815..7074f81d8b7cbfec835bbc6e3198d48910c57d0a 100644 GIT binary patch delta 85 zcmbQTkY&n3mJNK&jQ=O|GnX@lOm1gB%_)-_$iTqJq`zhIeHQh}M_7blB6`?GzBATu QzRoHj%ILQFh18m_09yDPAOHXW delta 85 zcmbQTkY&n3mJNK&j46}(nahD>JM(Eyz0^Pk21X{mMU(Hds82q^B1AysJ7eAE>#PEz MjINtsNUiw_05e1yk^lez diff --git a/test/test.rewrite_efcs b/test/test.rewrite_efcs index 470dc3778a6f2a17df152481bd64ea6cb4a03793..b78682c8a9ce31dc14d2a90fcc8194401bdf0020 100644 GIT binary patch delta 5371 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 F{{fV;s7wF= delta 5405 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 diff --git a/test/test.rewrite_endpoint b/test/test.rewrite_endpoint index 5121e5cfc9dc0469c2d7104c20c62f41de4972d9..058ce28a5c3fe7364bf355b91b6d339ed3fbeb56 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJR delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X`_^vUm8obm9~C$D3D$9QG)T($&J M#`&B7NniQ{02-|uHvj+t diff --git a/test/test.rewrite_enet_subsmac b/test/test.rewrite_enet_subsmac index 4f49673d987789fc1fc6eae2f4a006d0fb5b92c4..43080acd11d48abf9d2270f096e9c1039b0fdb8a 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)-n@==`#M%e GC1C(&v=#;c delta 72 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|H?L#ezK)eqNf-b| CFBN(K diff --git a/test/test.rewrite_fixlen_del b/test/test.rewrite_fixlen_del index 7c40ec511d3a539f04dfedbd52a0c10d5c11bc5c..274a5b4510142cb8bbde48f8989a7cedc144737a 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj2I0*p2Zo))5GMcPhQ9Rj;M1& delta 74 zcmey^#PYF;WkU-yW6I=K=0+gN&T^kqFEx;XfssjX(d73m&Ukq0lh?7nW31aem+i_g E0CD#iUjP6A diff --git a/test/test.rewrite_layer2 b/test/test.rewrite_layer2 index be034dc6c17518ab2ad26cb3d904f9cff8177bad..8f2efc9718d12896bf0ea638ad559d6ce2f37beb 100644 GIT binary patch delta 86 zcmeBJ&(g7;WkU@!hG^AFB*ZL=TI|=6Y69 UM#lQhQ`r(k8QnI2lb-Y&0B~#>YybcN delta 86 zcmeBJ&(g7;WkU@!W6I=O=0+g-pZPwgUTPo%10$2(qRI8F>XZ3cW$=k?u4ffxWUSjf Pl`TP((RK4T=}Er$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJ4U$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj?s4WT($&J MM%T^%q%Zve03vr9!2kdN diff --git a/test/test.rewrite_range_portmap b/test/test.rewrite_range_portmap index c9a1c82ad341b232a9e6ef442e0969ce5facce6a..e66130a582b6b7d8671520487d13c42665e31980 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJH|_! Q=dvY;GPZ60Cw=J;08sxNBLDyZ delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj`8B=xoiod MjIEphNniQ{04sYNl>h($ diff --git a/test/test.rewrite_seed b/test/test.rewrite_seed index 6204f570f188f1c1daf02859ef280bf0eeab3584..a92db11e6280f40aaf8a660a84aae23fab1902dd 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJByfN7dlqLHj~|n#K6xGMJH}m` Q=dvY;GG=Z5Cw=J;08{oH6951J delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X|Svy$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJ4W-( QbJ-F^8QnJjlfLu^07sx3NB{r; delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj?rxMT($&J MM%T^%q%Zve03sY3x&QzG diff --git a/test/test.rewrite_skip b/test/test.rewrite_skip index d4e348cbdd70f0e8a16b3fca086efd2c38406779..13632b78df642b2b2dcd32adc18262d9847098f4 100644 GIT binary patch delta 81 zcmcb%l;zq|mJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJH}m` Q=dvY;GG=Z5Cw=1^084`#;{X5v delta 81 zcmcb%l;zq|mJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj&bMaxoiod MjG3GNN#FPe044t$RR910 diff --git a/test/test.rewrite_tos b/test/test.rewrite_tos index fb56b6761562223d88f198f02c8187c4db2d6222..999b51b578370bf6b8a98db06c04f58ea36a413f 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq~A38J&QApr-#W?pS+Is9b^6G QxoiodjBcC%NniQ{05zH#`Tzg` delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mg30e$obm9~C$D3D$5^*{E?a^q MqwD5>(wF`K01y@$YybcN diff --git a/test/test.rewrite_trunc b/test/test.rewrite_trunc index 7c40ec511d3a539f04dfedbd52a0c10d5c11bc5c..274a5b4510142cb8bbde48f8989a7cedc144737a 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj;>a6ZCo*o)+^Kn*1M#lQh QFWDMI8QnH#$>{wD0J+x}G5`Po delta 78 zcmbQSon^*$mJJ)28B->2WNrnL<}A-S^-=>F7#NxK7EM-Xb;rfqe4JI0k+E*`OST44 LM%T?*GJ5|3mG>5; diff --git a/test/test.rewrite_vlandel b/test/test.rewrite_vlandel index 7c40ec511d3a539f04dfedbd52a0c10d5c11bc5c..274a5b4510142cb8bbde48f8989a7cedc144737a 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)K6xGMJI4CW QbJ-F^8QnJjlfLu^082<4kN^Mx delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|PhQ9Rj$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iQI5^lkIx_bkpZ-d9YXTJu`g?Q2;X Gm4yMmp&0=H delta 72 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk1}mno3n#y4amK?_YhKH`eJv}avM>Oi C92j)~ diff --git a/test/test2.rewrite_1ttl-hdrfix b/test/test2.rewrite_1ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..3fefe3afb70ab7591e00b08ae997563b06c6b238 GIT binary patch literal 71888 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 S<@FmVe!PAb{Pwz4zyAR@f2`pE literal 0 HcmV?d00001 diff --git a/test/test2.rewrite_2ttl b/test/test2.rewrite_2ttl index a96794836161a47ed0f9baa809faa3b530925c51..4b196073f89c70ff9ea55ad6f4ea372a9e02ac49 100644 GIT binary patch delta 72 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iQID^mX&(_bkpZ-WN=sTJu`g?Q2;X Gm4yMmrx^hN delta 72 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk25Y7-3n#y4amK?_YhKH`eJv}avM>Oi CA{cc5 diff --git a/test/test2.rewrite_2ttl-hdrfix b/test/test2.rewrite_2ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..2e6570f043c0a787b12d269e2e6bf72499fad831 GIT binary patch literal 71888 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 literal 0 HcmV?d00001 diff --git a/test/test2.rewrite_3ttl b/test/test2.rewrite_3ttl index e820d35ea622feb3f6f48e73c3e8a3f07efe8f2d..d38cf60a79478c23fcc27ab963d3171bb36d9d4f 100644 GIT binary patch delta 72 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)*1VQ=`&w2; GWnlnjpB4lF delta 72 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|HLqpezLu3ySr`CD C8Wnf| diff --git a/test/test2.rewrite_3ttl-hdrfix b/test/test2.rewrite_3ttl-hdrfix new file mode 100644 index 0000000000000000000000000000000000000000..73d078b1af8c04b6a51aad6278adb888e0c2baf9 GIT binary patch literal 71888 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 I^r(IR1A~UTr2qf` literal 0 HcmV?d00001 diff --git a/test/test2.rewrite_config b/test/test2.rewrite_config index a6a834fec847df5bdecbd90fc57380a5cb316e1b..238ee7e585c89bfbc4aa06b9a19980fb456e7d7a 100644 GIT binary patch delta 78 zcmbQSon^*$mJJ)28UIh-$lS^pGTEHvIj2l&AOiy;>a6ZCo*o)+^Kn*1M#lQh QFWDMI8QnH#$>{wD0J+x}G5`Po delta 78 zcmbQSon^*$mJJ)28B->2WNrnL<}A-S^-=>F7#NxK7EM-Xb;rfqe4JI0k+E*`OST44 LM%T?*GJ5|3mG>5; diff --git a/test/test2.rewrite_dlthdlc b/test/test2.rewrite_dlthdlc index 0c04724758839413682089448ab3895accae36be..01c351cb75b3f339a9af1c1bd792ae3fa11f9270 100644 GIT binary patch delta 85 zcmbQTkY&n3mJNK&jQ=O|GnX@lOm1gB%_)-_$iTqJq`zhIeHOLJhgpPSB6`?GzBATu QzRoHj%ILQFh18m_09v3M9smFU delta 85 zcmbQTkY&n3mJNK&j46}(nahD>JM(Eyz0^Pk21X{mMU(Hds7*f1B1}N!J7eAE>#PEz MjINtsNUiw_05a?vkN^Mx diff --git a/test/test2.rewrite_dltuser b/test/test2.rewrite_dltuser index 0b605a59f898bf07b6dde82bc14b4f4e4835e073..ce15642fa6655012b2dd099e2bfa0a981b6cbab5 100644 GIT binary patch delta 85 zcmbQTkY&n3mJNK&jQ=O|GnX@lOm1gB%_)-_$iTqJq`zhIeHOLJhgpPSB6`?GzBATu QzRoHj%ILQFh18m_09v3M9smFU delta 85 zcmbQTkY&n3mJNK&j46}(nahD>JM(Eyz0^Pk21X{mMU(Hds7*f1B1}N!J7eAE>#PEz MjINtsNUiw_05a?vkN^Mx diff --git a/test/test2.rewrite_efcs b/test/test2.rewrite_efcs index fd74a0a24fd698b2c61e5ea6be647d815d66d432..3b5cec1b2139f2b6ce22e6038b764ac80dbb586b 100644 GIT binary patch delta 5391 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 delta 5425 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 diff --git a/test/test2.rewrite_endpoint b/test/test2.rewrite_endpoint index ba365c740e34b348ed1c6248eed878b3d674aa2c..58d7b43f4c548c1d8a3d08f8b5c49708125bc11b 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJ%8Q QbJ-F^85eB+Cw=J;06-iY!~g&Q delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X`_^vUm8obm9~Ca-0E&v<3?T($&J M#`&B7NniQ{02-JZHUIzs diff --git a/test/test2.rewrite_enet_subsmac b/test/test2.rewrite_enet_subsmac index 987bfa6ddccd290f6ef0b4cfe1d885abf32fc5a9..f3fc64f49ac6db43023952ac38a46c1f4219ac0f 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)*1VQ=`&w2; GWnlnjpB4lF delta 72 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|HLqpezLu3ySr`CD C8Wnf| diff --git a/test/test2.rewrite_fixlen_del b/test/test2.rewrite_fixlen_del index cae178213c8422f8ad463fd1c78171a9b6dca504..50f1291cabd6dc823030b141a5a217067257484f 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O2I0*p2Zo))5GMcOi_@% delta 74 zcmey^#PYF;WkU-yW6I=K=0+gN&T^kqFEx;XfssjX(d73m&Ukoglh?AoXRO;im+i_g E0CDLUUH||9 diff --git a/test/test2.rewrite_layer2 b/test/test2.rewrite_layer2 index c961f9e1619e6cfbab2ab65c7359b75231e7e246..885d6b737efe74fffa93d4892e66c5cbb4e72e34 100644 GIT binary patch delta 86 zcmeBJ&(g7;WkU@!gr!FRLs}L=TI|=6Y69 UM#lQhQ`r(k8QnI2lb-Y&0B{l+Y5)KL delta 86 zcmeBJ&(g7;WkU@!W6I=O=0+g-pZPwgUTPo%10$2(qRI8FYLj_cW$}q@u4ffxWUSjf Pl`TP((RK4T=}Er$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Udq%s> QbJ-F^8QnJjlfLu^07vE$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&Wzf Q=dvY;GPZ60Cw=J;08r{2A^-pY delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJ#Q$*edlqLHj}MclHhC@Ud&WJR Q=dvY;GG=Z5Cw=J;090oj7XSbN delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{n^ON7RIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Udq(rk QbJ-F^8QnJjlfLu^07r`(M*si- delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq~A38J&QApr-#W?o4l6wJ!Adm QxoiodjBcC%NniQ{05ydg`2YX_ delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mg30e$obm9~Ca-0E&sev4E?a^q MqwD5>(wF`K01yEhYXATM diff --git a/test/test2.rewrite_trunc b/test/test2.rewrite_trunc index cae178213c8422f8ad463fd1c78171a9b6dca504..50f1291cabd6dc823030b141a5a217067257484f 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O;>a6ZCo*o)+^Kn*1M#lQh QFWDMI8QnH#$>{wD0J+x}G5`Po delta 78 zcmbQSon^*$mJJ)28B->2WNrnL<}A-S^-=>F7#NxK7EM-Xb;rfqe4JI0k+E*`OST44 LM%T?*GJ5|3mG>5; diff --git a/test/test2.rewrite_vlandel b/test/test2.rewrite_vlandel index cae178213c8422f8ad463fd1c78171a9b6dca504..50f1291cabd6dc823030b141a5a217067257484f 100644 GIT binary patch delta 81 zcmcbxk>$chmJKb;jQ=OMGB+}YOlD`f&nc4{$iTqJq`zhIdlqLHPY;u)HhC@Ud&c_B QbJ-F^8QnJjlfLu^0829)j{pDw delta 81 zcmcbxk>$chmJKb;j46{_nHzy5JIj4ez0^Pk21X{mMU&sNIOE}|O Date: Sat, 1 Jun 2024 21:12:02 -0700 Subject: [PATCH 51/69] Bug #844 PR# 846: print error log on CI failure --- .github/workflows/github-actions-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-ci.yml b/.github/workflows/github-actions-ci.yml index 21b5e248..692bbf4e 100644 --- a/.github/workflows/github-actions-ci.yml +++ b/.github/workflows/github-actions-ci.yml @@ -28,5 +28,5 @@ jobs: - name: List files in the repository run: ls ${{ github.workspace }} - name: tests - run: sudo make test + run: sudo make test || cat test/test.log - run: echo "This test's status is ${{ job.status }}." From c7d7ff02b0459db11f3c9111c584f1f51ed83550 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 2 Jun 2024 11:35:47 -0700 Subject: [PATCH 52/69] Bug #863: CI build failures make test fail Also fix intermittent test failure in TOS test. It seems there is a memory alignment issue. --- .github/workflows/github-actions-ci.yml | 2 +- src/tcpedit/tcpedit.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/github-actions-ci.yml b/.github/workflows/github-actions-ci.yml index 692bbf4e..8a9882d5 100644 --- a/.github/workflows/github-actions-ci.yml +++ b/.github/workflows/github-actions-ci.yml @@ -28,5 +28,5 @@ jobs: - name: List files in the repository run: ls ${{ github.workspace }} - name: tests - run: sudo make test || cat test/test.log + run: sudo make test || (cat test/test.log; false) - run: echo "This test's status is ${{ job.status }}." diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index 160ec75f..f846d8b6 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -185,8 +185,10 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, u_char **pktdata volatile uint16_t oldval = *((uint16_t *)ip_hdr); volatile uint16_t newval; - ip_hdr->ip_tos = tcpedit->tos; newval = *((uint16_t *)ip_hdr); + newval = htons((ntohs(newval) & 0xff00) | (tcpedit->tos & 0xff)); + *((uint16_t *)ip_hdr) = newval; + static uint32_t cnt; csum_replace2(&ip_hdr->ip_sum, oldval, newval); } From 1f648d6b38f4f3e61481773f6f27ef7a014b687e Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 2 Jun 2024 13:19:18 -0700 Subject: [PATCH 53/69] Bug #863: implement tcpr_pcap_open() universally Add macros that can properly convert between PCAP timestamps and timespec/timeval. We still use pcap_open_offline() for tcprewrite.c, otherwise tests fail because timestamps get written inconsistently across libpcap versions. Also fix multiplier calculation error. --- configure.ac | 5 +++++ docs/CHANGELOG | 1 + src/common/fakepcapnav.c | 3 ++- src/common/tcpdump.c | 2 +- src/common/utils.c | 11 +++++++++++ src/common/utils.h | 1 + src/defines.h.in | 22 ++++++++++++++++------ src/replay.c | 10 ---------- src/send_packets.c | 8 ++++---- src/tcpprep.c | 2 +- 10 files changed, 42 insertions(+), 23 deletions(-) diff --git a/configure.ac b/configure.ac index 70df1540..ce474445 100644 --- a/configure.ac +++ b/configure.ac @@ -1216,6 +1216,11 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ if test $have_pcap_open_offline_with_tstamp_precision = yes ; then AC_DEFINE([HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION], [1], [Does libpcap have pcap_open_offline_with_tstamp_precision?]) + AC_DEFINE([PCAP_TSTAMP_US_TO_NS_MULTIPLIER], [1], [Multiplier for conversion from PCAP usec to nsec]) + AC_DEFINE([PCAP_TSTAMP_US_TO_US_DIVISOR], [1000], [Divisor for conversion from PCAP usec to usec]) +else + AC_DEFINE([PCAP_TSTAMP_US_TO_NS_MULTIPLIER], [1000], [Multiplier for conversion from PCAP usec to nsec]) + AC_DEFINE([PCAP_TSTAMP_US_TO_US_DIVISOR], [1], [Divisor for conversion from PCAP usec to usec]) fi diff --git a/docs/CHANGELOG b/docs/CHANGELOG index a05c2db2..ceea729e 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/01/2024 Version 4.5.0-beta1 + - fix nansecond timestamp regression bug (#863) - --fixhdrlen option added to control action on packet length changes (#846) - incorrect checksum for certain IPv4 packets - fixed by #846 (#844) - add check for IPv6 extension header length (#827 #842) diff --git a/src/common/fakepcapnav.c b/src/common/fakepcapnav.c index 057c9710..ae9a29ec 100644 --- a/src/common/fakepcapnav.c +++ b/src/common/fakepcapnav.c @@ -26,6 +26,7 @@ #include "defines.h" #include "config.h" #include "common.h" +#include "utils.h" #include #ifndef HAVE_PCAPNAV @@ -52,7 +53,7 @@ pcapnav_open_offline(const char *filename) err(-1, "malloc() error: unable to malloc pcapnav_t"); } - pcapnav->pcap = pcap_open_offline(filename, errbuf); + pcapnav->pcap = tcpr_pcap_open(filename, errbuf); if (pcapnav->pcap == NULL) { errx(-1, "Error opening pcap file %s: %s", filename, errbuf); } diff --git a/src/common/tcpdump.c b/src/common/tcpdump.c index 637af839..398024cd 100644 --- a/src/common/tcpdump.c +++ b/src/common/tcpdump.c @@ -75,7 +75,7 @@ tcpdump_print(tcpdump_t *tcpdump, struct pcap_pkthdr *pkthdr, const u_char *data /* convert header to file-format packet header */ actual_pkthdr.ts.ts_sec = (uint32_t)pkthdr->ts.tv_sec; - actual_pkthdr.ts.ts_usec = (uint32_t)pkthdr->ts.tv_sec; + actual_pkthdr.ts.ts_usec = (uint32_t)(pkthdr->ts.tv_usec / PCAP_TSTAMP_US_TO_US_DIVISOR); actual_pkthdr.caplen = pkthdr->caplen; actual_pkthdr.len = pkthdr->len; diff --git a/src/common/utils.c b/src/common/utils.c index 520d40d2..fa6b5738 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -97,6 +97,17 @@ our_safe_strdup(const char *str, const char *funcname, int line, const char *fil return newstr; } +pcap_t* +tcpr_pcap_open(const char *path, char *ebuf) +{ +#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION + return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf); +#else + return pcap_open_offline(path, ebuf); +#endif +} + + /** * calls free and sets to NULL. */ diff --git a/src/common/utils.h b/src/common/utils.h index a858099a..4239ead9 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -47,6 +47,7 @@ void packet_stats(const tcpreplay_stats_t *stats); int format_date_time(struct timespec *when, char *buf, size_t len); uint32_t tcpr_random(uint32_t *seed); void restore_stdin(void); +pcap_t* tcpr_pcap_open(const char *path, char *ebuf); /* our "safe" implimentations of functions which allocate memory */ #define safe_malloc(x) our_safe_malloc(x, __FUNCTION__, __LINE__, __FILE__) diff --git a/src/defines.h.in b/src/defines.h.in index 1a26132c..86214943 100644 --- a/src/defines.h.in +++ b/src/defines.h.in @@ -359,14 +359,24 @@ 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 * 1000; \ +/* libpcap that supports it, puts nanosecond values in tv_usec when pcap file is read with nanosec precision, + * and so tv_usec is directly copied to tv_nsec. + * But older versions do that do not support nanosecond precision need to multiply tv_usec by 1000 to convert + * to tv_nsec. + */ +#define PCAP_TIMEVAL_TO_TIMESPEC_SET(a, b) \ + do { \ + (b)->tv_sec = (a)->tv_sec; \ + (b)->tv_nsec = (a)->tv_usec * PCAP_TSTAMP_US_TO_NS_MULTIPLIER; \ + } while(0) + +#define PCAP_TIMEVAL_TO_TIMEVAL_SET(a, b) \ + do { \ + (b)->tv_sec = (a)->tv_sec; \ + (b)->tv_usec = (a)->tv_usec / PCAP_TSTAMP_US_TO_US_DIVISOR; \ } 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 74b0ab68..59f84303 100644 --- a/src/replay.c +++ b/src/replay.c @@ -98,16 +98,6 @@ tcpr_replay_index(tcpreplay_t *ctx) return rcode; } -static pcap_t * -tcpr_pcap_open(const char *path, char *ebuf) -{ -#ifdef HAVE_PCAP_OPEN_OFFLINE_WITH_TSTAMP_PRECISION - return pcap_open_offline_with_tstamp_precision(path, PCAP_TSTAMP_PRECISION_NANO, ebuf); -#else - return pcap_open_offline(path, ebuf); -#endif -} - /** * \brief replay a pcap file out interface(s) * diff --git a/src/send_packets.c b/src/send_packets.c index 788341b4..fdb2e37b 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -299,7 +299,7 @@ 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); @@ -388,7 +388,7 @@ 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 + PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr.ts, &pkthdr_ts); now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT @@ -739,9 +739,9 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * if (options->speed.mode == speed_multiplier) { struct timespec pkthdr_ts; - TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &pkthdr_ts); + PCAP_TIMEVAL_TO_TIMESPEC_SET(&pkthdr_ptr->ts, &pkthdr_ts); if (!timesisset(&last_pkt_ts)) { - TIMEVAL_TO_TIMESPEC(&pkthdr_ptr->ts, &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; diff --git a/src/tcpprep.c b/src/tcpprep.c index 52fdc8aa..83824766 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -91,7 +91,7 @@ main(int argc, char *argv[]) readpcap: /* open the pcap file */ - if ((options->pcap = pcap_open_offline(OPT_ARG(PCAP), errbuf)) == NULL) { + if ((options->pcap = tcpr_pcap_open(OPT_ARG(PCAP), errbuf)) == NULL) { close(out_file); tcpprep_close(tcpprep); errx(-1, "Error opening libpcap: %s", errbuf); From b73ac4f2778881e1fe40ea648caa3349275ae45b Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 2 Jun 2024 18:52:22 -0700 Subject: [PATCH 54/69] 4.5.0-beta1 contributions --- docs/CHANGELOG | 10 ++++++++-- docs/CREDIT | 12 +++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ceea729e..b5700959 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,12 +1,18 @@ -06/01/2024 Version 4.5.0-beta1 +06/02/2024 Version 4.5.0-beta1 - fix nansecond timestamp regression bug (#863) + - autotools - AC_HELP_STRING is obsolete in 2.70 (#856) + - configure.ac: do not run conftest in case of cross compilation (#849) + - Haiku support (#847) - --fixhdrlen option added to control action on packet length changes (#846) - incorrect checksum for certain IPv4 packets - fixed by #846 (#844) - add check for IPv6 extension header length (#827 #842) - - add check for empty CIDR (#824 #843) - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) + - Infinite loop in tcprewrite at get.c (#827 #842) + - add check for empty CIDR (#824 #843) + - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) + - dlt_jnpr_ether_cleanup: check config before cleanup (#812 #851) - 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 f148e592..f35d208f 100644 --- a/docs/CREDIT +++ b/docs/CREDIT @@ -118,7 +118,7 @@ Bastian Triller - Linux SLL2 GithHub @plangarbalint - - eBPF + - eBPF (AF_XDF) - nanosecond timers Chuck Cottrill @@ -128,3 +128,13 @@ Chuck Cottrill Martin 'JaMa' Jansa - configure.ac: unify search dirs for pcap and add lib32 + +Marsman + - dlt_jnpr_ether_cleanup: check config before cleanup + +Chen Qi + - configure.ac: do not run conftest in case of cross compilation + +Denis Ovsienko + - Haiku support + From 4ea1f43f012ee594d1ff2994818c30d1b70b8d98 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 2 Jun 2024 20:29:22 -0700 Subject: [PATCH 55/69] 4.5.0-beta1 tcpedit_dlt_cleanup changelog update --- docs/CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index b5700959..c918319a 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -12,6 +12,7 @@ - add check for empty CIDR (#824 #843) - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) + - double free in tcpedit_dlt_cleanup in tcprewrite (#813 #855) - dlt_jnpr_ether_cleanup: check config before cleanup (#812 #851) - nanosecond timestamps (#796) - low PPS values run at full speed after several days (#779) From 84dee5ed840d3ee6aa395d4bd8cc5dcb06484ebe Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 3 Jun 2024 19:20:03 -0700 Subject: [PATCH 56/69] Bug #813: back out PR #855 Double free was fixed in PRs #711 and #851. This fix applied after these PRs introduces memory leaks. --- docs/CHANGELOG | 9 +++++---- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c | 4 +--- src/tcpedit/plugins/dlt_plugins.c | 12 ------------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index c918319a..8923c088 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -12,7 +12,7 @@ - add check for empty CIDR (#824 #843) - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) - - double free in tcpedit_dlt_cleanup in tcprewrite (#813 #855) + - CVE-2023-4256 double free in tcprewrite DLT_JUNIPER_ETHER (#813 #851) - dlt_jnpr_ether_cleanup: check config before cleanup (#812 #851) - nanosecond timestamps (#796) - low PPS values run at full speed after several days (#779) @@ -53,15 +53,16 @@ - build failures Debian/kfreebsd (#706) - bus error when building on armhf (#705) - typo fixes (#704) - - heap buffer overflow in tcpreplay (#703) - - double free in Juniper DLT (#702) + - CVE-2022-27418 heap buffer overflow in tcpreplay (#703) + - CVE-2022-27416 double free in Juniper DLT (#702) 01/31/2022 Version 4.4.0 - remove obsolete FORCE_ALIGN support to fix macOS 11 compile (#695) - add a security policy document (#689) + - CVE-2021-45386 CVE-2021-45387 two reachable assertions in add_tree_ipv4() and add_tree_ipv6() (#687 #678) - ability to specify directory of pcap files (#682) - incorrect PPS rate for long-running sessions (#679) - - option --skipbroadcast not working (#677) + - option --skipbroadcast not working (#677 #678) - revert #630 to fix --multiplier issues (#674) - gcc 9.3 compiler warnings (#670) - installed netmap not automatically detected (#669) diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c index 9642a2c2..904709c3 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c @@ -64,9 +64,7 @@ dlt_jnpr_ether_register(tcpeditdlt_t *ctx) plugin = tcpedit_dlt_newplugin(); plugin->provides += PLUGIN_MASK_PROTO + PLUGIN_MASK_SRCADDR + PLUGIN_MASK_DSTADDR; - plugin-> - requires - = 0; + plugin->requires = 0; /* what is our DLT value? */ plugin->dlt = dlt_value; diff --git a/src/tcpedit/plugins/dlt_plugins.c b/src/tcpedit/plugins/dlt_plugins.c index 5c2741c2..70885309 100644 --- a/src/tcpedit/plugins/dlt_plugins.c +++ b/src/tcpedit/plugins/dlt_plugins.c @@ -96,12 +96,6 @@ const char *tcpeditdlt_bit_info[] = {"Missing required Layer 3 protocol.", * Public functions ********************************************************************/ -/* - * Ensure init/cleanup are called only once - * Assume a single tcpedit struct and return the previously allocated context. - */ -static int tcpedit_dlt_is_initialized = 0; - /** * initialize our plugin library. Pass the DLT of the source pcap handle. * Actions: @@ -123,9 +117,6 @@ tcpedit_dlt_init(tcpedit_t *tcpedit, const int srcdlt) assert(tcpedit); assert(srcdlt >= 0); - if (tcpedit_dlt_is_initialized++ > 0) - return tcpedit->dlt_ctx; - ctx = (tcpeditdlt_t *)safe_malloc(sizeof(tcpeditdlt_t)); /* do we need a side buffer for L3 data? */ @@ -454,9 +445,6 @@ tcpedit_dlt_cleanup(tcpeditdlt_t *ctx) { tcpeditdlt_plugin_t *plugin; - if (--tcpedit_dlt_is_initialized <= 0) - return; - assert(ctx); plugin = ctx->plugins; From 14b7e85b1fc1f8170e11ee00214c02b67546ba12 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Mon, 3 Jun 2024 21:37:51 -0700 Subject: [PATCH 57/69] 4.5.0-beta1 - update CHANGELOG --- docs/CHANGELOG | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 8923c088..7d8680b3 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,4 @@ -06/02/2024 Version 4.5.0-beta1 +06/03/2024 Version 4.5.0-beta1 - fix nansecond timestamp regression bug (#863) - autotools - AC_HELP_STRING is obsolete in 2.70 (#856) - configure.ac: do not run conftest in case of cross compilation (#849) @@ -9,7 +9,7 @@ - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) - Infinite loop in tcprewrite at get.c (#827 #842) - - add check for empty CIDR (#824 #843) + - CVE-2023-43279 add check for empty CIDR (#824 #843) - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) - CVE-2023-4256 double free in tcprewrite DLT_JUNIPER_ETHER (#813 #851) @@ -21,7 +21,7 @@ 06/04/2023 Version 4.4.4 - overflow check fix for parse_mpls (#795) - tcpreplay-edit: prevent L2 flooding of ipv6 unicast packets (#793) - - CVE-2023-27786 bugs caused by strtok_r (#782 #784 #785 #786 #787 #788) + - CVE-2023-27784 CVE-2023-27785 CVE-2023-27786 CVE-2023-27787 CVE-2023-27788 CVE-2023-27789 bugs caused by strtok_r (#782 #784 #785 #786 #787 #788) - CVE-2023-27783 reachable assert in tcpedit_dlt_cleanup (#780) - add CI and C/C++ Linter and CodeQL (#773) - reachable assert in fast_edit_packet (#772) @@ -101,7 +101,7 @@ - CVE-2018-20553 Correct L2 header length calculations so that IP header offset is correct (#584) - Correct L2 header length to correct IP header offset (#583) - Fix warnings from gcc version 10 (#580) - - Heap Buffer Overflow in randomize_iparp (#579) + - CVE-2020-23273 Heap Buffer Overflow in randomize_iparp (#579) - Use after free in get_ipv6_next (#578) - CVE-2020-12740 Heap Buffer Overflow in git_ipv6_next (#576) - Call pcap_freecode() on pcap_compile() (#572) @@ -109,7 +109,7 @@ - Fix divide by zero in fuzzing (#570) - Unique IP repeats at very high iteration counts (#566) - Fails to compile on FreeBSD amd64 13.0 (#558) - - Heap Buffer Overflow in do_checksum (#556) (#577) + - CVE-2020-18976 Heap Buffer Overflow in do_checksum (#556) (#577) - Attempt to correct corrupt pcap files, if possible (#557) - Fix GCC v10 warnings (#555) - Remove some duplicated SOURCES entries (#551) From 804cacbea35bd0989fa290d4dad9fa65efc71346 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 4 Jun 2024 20:45:23 -0700 Subject: [PATCH 58/69] Bug #867 - run regfree() on close --- docs/CHANGELOG | 1 + src/tcpprep.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 7d8680b3..87dd4964 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/03/2024 Version 4.5.0-beta1 + - memory leak in tcpprep when using RegEx (#867) - fix nansecond timestamp regression bug (#863) - autotools - AC_HELP_STRING is obsolete in 2.70 (#856) - configure.ac: do not run conftest in case of cross compilation (#849) diff --git a/src/tcpprep.c b/src/tcpprep.c index 83824766..6a5f7830 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -195,6 +195,8 @@ main(int argc, char *argv[]) /* close cache file */ close(out_file); + regfree(&tcpprep->options->preg); + tcpprep_close(tcpprep); restore_stdin(); From 635c1e1a8ade80a688da09961119723170338990 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 4 Jun 2024 21:16:44 -0700 Subject: [PATCH 59/69] Bug #869 - free option->xX on exit --- docs/CHANGELOG | 1 + src/tcpprep.c | 2 -- src/tcpprep_api.c | 8 ++++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 87dd4964..85bd728f 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/03/2024 Version 4.5.0-beta1 + - memory leak in tcpprep when using include/exclude (#869) - memory leak in tcpprep when using RegEx (#867) - fix nansecond timestamp regression bug (#863) - autotools - AC_HELP_STRING is obsolete in 2.70 (#856) diff --git a/src/tcpprep.c b/src/tcpprep.c index 6a5f7830..83824766 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -195,8 +195,6 @@ main(int argc, char *argv[]) /* close cache file */ close(out_file); - regfree(&tcpprep->options->preg); - tcpprep_close(tcpprep); restore_stdin(); diff --git a/src/tcpprep_api.c b/src/tcpprep_api.c index 2a9709eb..25f9a851 100644 --- a/src/tcpprep_api.c +++ b/src/tcpprep_api.c @@ -91,6 +91,14 @@ tcpprep_close(tcpprep_t *ctx) cidr = cidr_nxt; } + if (options->xX.list) + free_list(options->xX.list); + + if (options->xX.cidr) + safe_free(options->xX.cidr); + + regfree(&options->preg); + safe_free(options); safe_free(ctx->outfile); From a1a47354d222fadee81e831dbb7bdf2f0cc8fe61 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Tue, 4 Jun 2024 21:17:55 -0700 Subject: [PATCH 60/69] Bug #869 - GitHub Actions CI - enable ASAN --- .github/workflows/github-actions-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-actions-ci.yml b/.github/workflows/github-actions-ci.yml index 8a9882d5..1b81f614 100644 --- a/.github/workflows/github-actions-ci.yml +++ b/.github/workflows/github-actions-ci.yml @@ -18,7 +18,7 @@ jobs: - name: Create configure script run: ./autogen.sh - name: configure - run: ./configure --with-testnic=eth0 --disable-local-libopts + run: ./configure --with-testnic=eth0 --disable-local-libopts --enable-asan - name: make run: make - name: make dist From 8f26a9799a913a88fbf7ca9deea240cc003147e2 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 09:29:24 -0700 Subject: [PATCH 61/69] Bug #811 - add check for invalid jnpr header length --- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c index 904709c3..f5cd73b0 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c @@ -302,6 +302,13 @@ dlt_jnpr_ether_proto(tcpeditdlt_t *ctx, const u_char *packet, int pktlen) memcpy(&jnpr_hdr_len, &packet[JUNIPER_ETHER_EXTLEN_OFFSET], 2); jnpr_hdr_len = ntohs(jnpr_hdr_len) + JUNIPER_ETHER_HEADER_LEN; + if (jnpr_hdr_len > pktlen) { + tcpedit_seterr(ctx->tcpedit, + "Juniper header length %d invalid: it is greater than packet length %d", + jnpr_hdr_len, pktlen); + return TCPEDIT_ERROR; + } + ethernet = packet + jnpr_hdr_len; /* let the en10mb plugin do the rest of the work */ From 70a7843f82bfa00ee56209dfdd12bf80869e8195 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 12:53:55 -0700 Subject: [PATCH 62/69] Bug #792 avoid assertion and other fixes SLL (Linux cooked packets v1) caused a crash due to an overly aggressive assert. While here, fixed an issue where resultant packets were corrupt (wrong size, incorrect protocol) --- docs/CHANGELOG | 4 +++- src/tcpedit/plugins/dlt_en10mb/en10mb.c | 14 +++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 85bd728f..ac33081c 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -16,7 +16,9 @@ - configure.ac: unify search dirs for pcap and add lib32 (#819) - CVE-2023-4256 double free in tcprewrite DLT_JUNIPER_ETHER (#813 #851) - dlt_jnpr_ether_cleanup: check config before cleanup (#812 #851) - - nanosecond timestamps (#796) + - SEGV on invalid Juniper Ethernet header length (#811) + - nanosecond timestamps support (#796) + - Linux cooked packet fatal error (#792) - low PPS values run at full speed after several days (#779) - create DLT_LINUX_SLL2 plugin (#727) diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb.c b/src/tcpedit/plugins/dlt_en10mb/en10mb.c index 957ce20d..3fbca624 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb.c +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb.c @@ -519,9 +519,9 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir) } /* newl2len for some other DLT -> ethernet */ - else if (config->vlan == TCPEDIT_VLAN_ADD) { - /* if add a vlan then 18, */ - newl2len = TCPR_802_1Q_H; + else { + newl2len = config->vlan == TCPEDIT_VLAN_ADD ? TCPR_802_1Q_H : TCPR_802_3_H; + oldl2len = ctx->l2len; } if ((uint32_t)pktlen < newl2len || pktlen + newl2len - ctx->l2len > MAXPACKET) { @@ -555,7 +555,6 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir) /* update the total packet length */ pktlen += (int)(newl2len - oldl2len); - ctx->l2len += (int)(newl2len - oldl2len); /* set the src & dst address as the first 12 bytes */ eth = (struct tcpr_ethernet_hdr *)(packet + ctx->l2offset); @@ -665,6 +664,11 @@ dlt_en10mb_encode(tcpeditdlt_t *ctx, u_char *packet, int pktlen, tcpr_dir_t dir) } } + if (newl2len == TCPR_802_3_H) { + /* all we need for 802.3 is the proto */ + eth->ether_type = ctx->proto; + } + if (config->vlan == TCPEDIT_VLAN_ADD || (config->vlan == TCPEDIT_VLAN_OFF && extra->vlan)) { vlan_hdr = (struct tcpr_802_1q_hdr *)(packet + extra->vlan_offset); if (config->vlan == TCPEDIT_VLAN_ADD) { @@ -812,7 +816,7 @@ dlt_en10mb_merge_layer3(tcpeditdlt_t *ctx, u_char *packet, int pktlen, u_char *i if (l2len == -1 || pktlen < l2len) return NULL; - assert(ctx->decoded_extra_size == sizeof(*extra)); + assert(ctx->decoded_extra_size >= sizeof(*extra)); extra = (en10mb_extra_t *)ctx->decoded_extra; eth = (struct tcpr_ethernet_hdr *)(packet + ctx->l2offset); assert(eth); From d6f7c5a826bcf506f89abaf4376221d087946567 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 15:33:10 -0700 Subject: [PATCH 63/69] Bug #844 tap: ignore TUNSETIFF EBUSY errors This ioctl error suggests that the tap interface has already been set. Backed out #411 and #651 as they only partially address the issue and introduced new bugs. --- docs/CHANGELOG | 6 +++++- src/common/sendpacket.c | 20 +++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ac33081c..8485b8aa 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -10,10 +10,13 @@ - add check for IPv6 extension header length (#827 #842) - GitHub template for pull requests (#839) - handle IPv6 fragment extension header (#832 #837) + - Linux tap interfaces fail intermittently (#828) - Infinite loop in tcprewrite at get.c (#827 #842) + - SLL incorrect size and protocal when converted to Ethernet (#826) - CVE-2023-43279 add check for empty CIDR (#824 #843) - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) + - tcpreplay-edit recomputes IPv4 checksums unnecessarily (#815 #846) - CVE-2023-4256 double free in tcprewrite DLT_JUNIPER_ETHER (#813 #851) - dlt_jnpr_ether_cleanup: check config before cleanup (#812 #851) - SEGV on invalid Juniper Ethernet header length (#811) @@ -25,7 +28,8 @@ 06/04/2023 Version 4.4.4 - overflow check fix for parse_mpls (#795) - tcpreplay-edit: prevent L2 flooding of ipv6 unicast packets (#793) - - CVE-2023-27784 CVE-2023-27785 CVE-2023-27786 CVE-2023-27787 CVE-2023-27788 CVE-2023-27789 bugs caused by strtok_r (#782 #784 #785 #786 #787 #788) + - CVE-2023-27784 CVE-2023-27785 CVE-2023-27786 CVE-2023-27787 CVE-2023-27788 CVE-2023-27789 + bugs caused by strtok_r (#782 #784 #785 #786 #787 #788) - CVE-2023-27783 reachable assert in tcpedit_dlt_cleanup (#780) - add CI and C/C++ Linter and CodeQL (#773) - reachable assert in fast_edit_packet (#772) diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 6b060aad..11e3a761 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -524,10 +524,6 @@ sendpacket_open(const char *device, sendpacket_type_t sendpacket_type _U_, void *arg _U_) { -#ifdef HAVE_TUNTAP - char sys_dev_dir[128]; - bool device_exists; -#endif sendpacket_t *sp; struct stat sdata; @@ -536,11 +532,6 @@ sendpacket_open(const char *device, errbuf[0] = '\0'; -#ifdef HAVE_TUNTAP - snprintf(sys_dev_dir, sizeof(sys_dev_dir), "/sys/class/net/%s/", device); - device_exists = access(sys_dev_dir, R_OK) == 0; -#endif - /* khial is universal */ if (stat(device, &sdata) == 0) { if (((sdata.st_mode & S_IFMT) == S_IFCHR)) { @@ -563,7 +554,7 @@ sendpacket_open(const char *device, } } #ifdef HAVE_TUNTAP - } else if (strncmp(device, "tap", 3) == 0 && !device_exists) { + } else if (strncmp(device, "tap", 3) == 0) { sp = sendpacket_open_tuntap(device, errbuf); #endif } else { @@ -895,9 +886,12 @@ sendpacket_open_tuntap(const char *device, char *errbuf) strncpy(ifr.ifr_name, device, sizeof(ifr.ifr_name) - 1); if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) < 0) { - snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Unable to create tuntap interface: %s", device); - close(tapfd); - return NULL; + // ignore EBUSY - it just means that the tunnel has already been opened + if (errno != EBUSY) { + snprintf(errbuf, SENDPACKET_ERRBUF_SIZE, "Unable to create tuntap interface: %s errno=%d", device, errno); + close(tapfd); + return NULL; + } } #elif defined(HAVE_FREEBSD) if (*device == '/') { From bda0cadcd94cf1ecad73dcd3c91454799c661724 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 18:24:38 -0700 Subject: [PATCH 64/69] v4.5.0-beta1 --- docs/CHANGELOG | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 8485b8aa..0dddb539 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,7 +1,7 @@ -06/03/2024 Version 4.5.0-beta1 +06/08/2024 Version 4.5.0-beta1 - memory leak in tcpprep when using include/exclude (#869) - memory leak in tcpprep when using RegEx (#867) - - fix nansecond timestamp regression bug (#863) + - fix nanosecond timestamp regression bug (#863) - autotools - AC_HELP_STRING is obsolete in 2.70 (#856) - configure.ac: do not run conftest in case of cross compilation (#849) - Haiku support (#847) @@ -12,7 +12,7 @@ - handle IPv6 fragment extension header (#832 #837) - Linux tap interfaces fail intermittently (#828) - Infinite loop in tcprewrite at get.c (#827 #842) - - SLL incorrect size and protocal when converted to Ethernet (#826) + - SLL incorrect size and protocol when converted to Ethernet (#826) - CVE-2023-43279 add check for empty CIDR (#824 #843) - AF_XDF socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) From 555a92f5f7ea45e8a25565446a8fca7d59294aea Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 19:46:22 -0700 Subject: [PATCH 65/69] 4.5.0-beta2 update Copyright to 2024 Also update version with AF_XDP info --- configure.ac | 2 +- docs/CHANGELOG | 2 +- lib/strlcpy.h | 2 +- src/bridge.c | 2 +- src/bridge.h | 2 +- src/common.h | 2 +- src/common/cache.c | 2 +- src/common/cache.h | 2 +- src/common/cidr.c | 2 +- src/common/cidr.h | 2 +- src/common/err.c | 2 +- src/common/err.h | 2 +- src/common/fakepcap.c | 2 +- src/common/fakepcap.h | 2 +- src/common/fakepcapnav.c | 2 +- src/common/fakepcapnav.h | 2 +- src/common/flows.c | 2 +- src/common/flows.h | 2 +- src/common/get.c | 2 +- src/common/get.h | 2 +- src/common/interface.c | 2 +- src/common/interface.h | 2 +- src/common/list.c | 2 +- src/common/list.h | 2 +- src/common/mac.c | 2 +- src/common/mac.h | 2 +- src/common/pcap_dlt.h | 2 +- src/common/sendpacket.c | 2 +- src/common/sendpacket.h | 2 +- src/common/services.c | 2 +- src/common/services.h | 2 +- src/common/tcpdump.c | 2 +- src/common/tcpdump.h | 2 +- src/common/timer.c | 2 +- src/common/timer.h | 2 +- src/common/utils.c | 2 +- src/common/utils.h | 2 +- src/common/xX.c | 2 +- src/common/xX.h | 2 +- src/fragroute/fragroute.c | 2 +- src/fragroute/fragroute.h | 2 +- src/replay.c | 2 +- src/replay.h | 2 +- src/send_packets.c | 2 +- src/send_packets.h | 2 +- src/signal_handler.c | 2 +- src/signal_handler.h | 2 +- src/sleep.c | 2 +- src/sleep.h | 2 +- src/tcpbridge.c | 2 +- src/tcpbridge.h | 2 +- src/tcpbridge_opts.def | 6 +++--- src/tcpcapinfo.c | 2 +- src/tcpcapinfo_opts.def | 4 ++-- src/tcpedit/checksum.c | 2 +- src/tcpedit/checksum.h | 2 +- src/tcpedit/dlt.c | 2 +- src/tcpedit/dlt.h | 2 +- src/tcpedit/edit_packet.c | 2 +- src/tcpedit/edit_packet.h | 2 +- src/tcpedit/parse_args.c | 2 +- src/tcpedit/parse_args.h | 2 +- src/tcpedit/plugins.h | 2 +- src/tcpedit/plugins/dlt_en10mb/en10mb.c | 2 +- src/tcpedit/plugins/dlt_en10mb/en10mb.h | 2 +- src/tcpedit/plugins/dlt_en10mb/en10mb_api.c | 2 +- src/tcpedit/plugins/dlt_en10mb/en10mb_api.h | 2 +- src/tcpedit/plugins/dlt_en10mb/en10mb_types.h | 2 +- src/tcpedit/plugins/dlt_hdlc/hdlc.c | 2 +- src/tcpedit/plugins/dlt_hdlc/hdlc.h | 2 +- src/tcpedit/plugins/dlt_hdlc/hdlc_api.c | 2 +- src/tcpedit/plugins/dlt_hdlc/hdlc_api.h | 2 +- src/tcpedit/plugins/dlt_hdlc/hdlc_types.h | 2 +- src/tcpedit/plugins/dlt_ieee80211/ieee80211.c | 2 +- src/tcpedit/plugins/dlt_ieee80211/ieee80211.h | 2 +- src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.c | 2 +- src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.h | 2 +- src/tcpedit/plugins/dlt_ieee80211/ieee80211_types.h | 2 +- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c | 2 +- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.h | 2 +- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.c | 2 +- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.h | 2 +- src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_types.h | 2 +- src/tcpedit/plugins/dlt_linuxsll/linuxsll.c | 2 +- src/tcpedit/plugins/dlt_linuxsll/linuxsll.h | 2 +- src/tcpedit/plugins/dlt_loop/loop.c | 2 +- src/tcpedit/plugins/dlt_loop/loop.h | 2 +- src/tcpedit/plugins/dlt_null/null.c | 2 +- src/tcpedit/plugins/dlt_null/null.h | 2 +- src/tcpedit/plugins/dlt_plugins.c | 2 +- src/tcpedit/plugins/dlt_pppserial/pppserial.c | 2 +- src/tcpedit/plugins/dlt_pppserial/pppserial.h | 2 +- src/tcpedit/plugins/dlt_pppserial/pppserial_api.c | 2 +- src/tcpedit/plugins/dlt_pppserial/pppserial_api.h | 2 +- src/tcpedit/plugins/dlt_pppserial/pppserial_types.h | 2 +- src/tcpedit/plugins/dlt_radiotap/radiotap.c | 2 +- src/tcpedit/plugins/dlt_radiotap/radiotap.h | 2 +- src/tcpedit/plugins/dlt_raw/raw.c | 2 +- src/tcpedit/plugins/dlt_raw/raw.h | 2 +- src/tcpedit/plugins/dlt_template/plugin.h | 2 +- src/tcpedit/plugins/dlt_template/plugin_api.h | 2 +- src/tcpedit/plugins/dlt_template/plugin_types.h | 2 +- src/tcpedit/plugins/dlt_user/user.c | 2 +- src/tcpedit/plugins/dlt_user/user.h | 2 +- src/tcpedit/plugins/dlt_user/user_api.c | 2 +- src/tcpedit/plugins/dlt_user/user_api.h | 2 +- src/tcpedit/plugins/dlt_utils.c | 2 +- src/tcpedit/plugins/dlt_utils.h | 2 +- src/tcpedit/plugins/ethernet.c | 2 +- src/tcpedit/plugins/ethernet.h | 2 +- src/tcpedit/plugins_api.h | 2 +- src/tcpedit/plugins_types.h | 2 +- src/tcpedit/portmap.c | 2 +- src/tcpedit/portmap.h | 2 +- src/tcpedit/tcpedit.c | 2 +- src/tcpedit/tcpedit.h | 2 +- src/tcpedit/tcpedit_api.c | 2 +- src/tcpedit/tcpedit_api.h | 2 +- src/tcpedit/tcpedit_opts.def | 2 +- src/tcpedit/tcpedit_stub.def | 2 +- src/tcpedit/tcpedit_types.h | 2 +- src/tcpprep.c | 2 +- src/tcpprep.h | 2 +- src/tcpprep_api.c | 2 +- src/tcpprep_api.h | 2 +- src/tcpprep_opts.def | 6 +++--- src/tcpr.h | 2 +- src/tcpreplay.c | 2 +- src/tcpreplay.h | 2 +- src/tcpreplay_api.c | 2 +- src/tcpreplay_api.h | 2 +- src/tcpreplay_opts.def | 11 ++++++++--- src/tcprewrite.c | 2 +- src/tcprewrite.h | 2 +- src/tcprewrite_opts.def | 6 +++--- src/timestamp_trace.h | 2 +- src/tree.c | 2 +- src/tree.h | 2 +- 138 files changed, 152 insertions(+), 147 deletions(-) diff --git a/configure.ac b/configure.ac index 8a962aa8..ded65384 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ dnl $Id$ AC_PREREQ([2.69]) dnl Set version info here! -AC_INIT([tcpreplay],[4.5.0-beta1],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/]) +AC_INIT([tcpreplay],[4.5.0-beta2],[https://github.com/appneta/tcpreplay/issues],[tcpreplay],[http://tcpreplay.sourceforge.net/]) AC_CONFIG_SRCDIR([src/tcpreplay.c]) AC_CONFIG_HEADERS([src/config.h]) AC_CONFIG_AUX_DIR(config) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 0dddb539..ba0e734e 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,4 @@ -06/08/2024 Version 4.5.0-beta1 +06/08/2024 Version 4.5.0-beta2 - memory leak in tcpprep when using include/exclude (#869) - memory leak in tcpprep when using RegEx (#867) - fix nanosecond timestamp regression bug (#863) diff --git a/lib/strlcpy.h b/lib/strlcpy.h index 45a900c9..a3cc65d4 100644 --- a/lib/strlcpy.h +++ b/lib/strlcpy.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/bridge.c b/src/bridge.c index 69efa31c..d136832b 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/bridge.h b/src/bridge.h index bb778368..d5651014 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common.h b/src/common.h index dd57f08d..fd207c2a 100644 --- a/src/common.h +++ b/src/common.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/cache.c b/src/common/cache.c index d268463d..353879e7 100644 --- a/src/common/cache.c +++ b/src/common/cache.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/cache.h b/src/common/cache.h index f064ebdc..f38e70b5 100644 --- a/src/common/cache.h +++ b/src/common/cache.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/cidr.c b/src/common/cidr.c index 9afbfecb..d4fc302b 100644 --- a/src/common/cidr.c +++ b/src/common/cidr.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/cidr.h b/src/common/cidr.h index e2fb475c..52d70654 100644 --- a/src/common/cidr.h +++ b/src/common/cidr.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/err.c b/src/common/err.c index 6f16ff7e..0333f1ce 100644 --- a/src/common/err.c +++ b/src/common/err.c @@ -7,7 +7,7 @@ * * Copyright (c) 2001-2010 Aaron Turner. * - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * * Copyright (c) 2000 Dug Song * diff --git a/src/common/err.h b/src/common/err.h index 57afb835..657040c1 100644 --- a/src/common/err.h +++ b/src/common/err.h @@ -7,7 +7,7 @@ * * Copyright (c) 2001-2010 Aaron Turner. * - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * * Copyright (c) 2000 Dug Song * diff --git a/src/common/fakepcap.c b/src/common/fakepcap.c index cf69b450..88a9fe54 100644 --- a/src/common/fakepcap.c +++ b/src/common/fakepcap.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/fakepcap.h b/src/common/fakepcap.h index cc269d4d..0f35c9c9 100644 --- a/src/common/fakepcap.h +++ b/src/common/fakepcap.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/fakepcapnav.c b/src/common/fakepcapnav.c index ae9a29ec..2c103291 100644 --- a/src/common/fakepcapnav.c +++ b/src/common/fakepcapnav.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/fakepcapnav.h b/src/common/fakepcapnav.h index a1f86250..e86445ba 100644 --- a/src/common/fakepcapnav.h +++ b/src/common/fakepcapnav.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/flows.c b/src/common/flows.c index 28a4615a..5a674377 100644 --- a/src/common/flows.c +++ b/src/common/flows.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/flows.h b/src/common/flows.h index 14a02f75..cf9f6863 100644 --- a/src/common/flows.h +++ b/src/common/flows.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/get.c b/src/common/get.c index 1de06780..418b5d5b 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/get.h b/src/common/get.h index 181abd46..0d4697a0 100644 --- a/src/common/get.h +++ b/src/common/get.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/interface.c b/src/common/interface.c index 006f7133..b94f6e3d 100644 --- a/src/common/interface.c +++ b/src/common/interface.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/interface.h b/src/common/interface.h index 446b51a3..b2839048 100644 --- a/src/common/interface.h +++ b/src/common/interface.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/list.c b/src/common/list.c index ec41dd12..5075fd5a 100644 --- a/src/common/list.c +++ b/src/common/list.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/list.h b/src/common/list.h index 9eaa425f..7eb3b5ae 100644 --- a/src/common/list.h +++ b/src/common/list.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/mac.c b/src/common/mac.c index 45e45fac..88e675a0 100644 --- a/src/common/mac.c +++ b/src/common/mac.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/mac.h b/src/common/mac.h index 50116a8e..275671cb 100644 --- a/src/common/mac.h +++ b/src/common/mac.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/pcap_dlt.h b/src/common/pcap_dlt.h index 2d582782..767854b6 100644 --- a/src/common/pcap_dlt.h +++ b/src/common/pcap_dlt.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 11e3a761..7d966dd6 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index 9e54efcc..a39072ee 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/services.c b/src/common/services.c index 76b92599..06a08695 100644 --- a/src/common/services.c +++ b/src/common/services.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/services.h b/src/common/services.h index a632df23..022d9208 100644 --- a/src/common/services.h +++ b/src/common/services.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/tcpdump.c b/src/common/tcpdump.c index 398024cd..4e53a08e 100644 --- a/src/common/tcpdump.c +++ b/src/common/tcpdump.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/tcpdump.h b/src/common/tcpdump.h index e5dcec12..0dcbeba8 100644 --- a/src/common/tcpdump.h +++ b/src/common/tcpdump.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/timer.c b/src/common/timer.c index 95ebe5dc..305da90d 100755 --- a/src/common/timer.c +++ b/src/common/timer.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/timer.h b/src/common/timer.h index b4cbfa34..f84ce887 100755 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/utils.c b/src/common/utils.c index fa6b5738..affb2e1e 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/utils.h b/src/common/utils.h index 4239ead9..9938912b 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/xX.c b/src/common/xX.c index 35528832..be98943e 100644 --- a/src/common/xX.c +++ b/src/common/xX.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/common/xX.h b/src/common/xX.h index b8a714e2..fa258100 100644 --- a/src/common/xX.h +++ b/src/common/xX.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/fragroute/fragroute.c b/src/fragroute/fragroute.c index b7edd2b0..6812c602 100644 --- a/src/fragroute/fragroute.c +++ b/src/fragroute/fragroute.c @@ -3,7 +3,7 @@ * * Copyright (c) 2001 Dug Song * Copyright (c) 2007-2008 Aaron Turner. - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * $Id$ */ diff --git a/src/fragroute/fragroute.h b/src/fragroute/fragroute.h index 2931ad65..f7460a19 100644 --- a/src/fragroute/fragroute.h +++ b/src/fragroute/fragroute.h @@ -3,7 +3,7 @@ /* * Copyright (c) 2001 Dug Song * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/replay.c b/src/replay.c index 59f84303..db472467 100644 --- a/src/replay.c +++ b/src/replay.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/replay.h b/src/replay.h index 4a4becdf..3f8c4f59 100644 --- a/src/replay.h +++ b/src/replay.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/send_packets.c b/src/send_packets.c index fdb2e37b..dab78115 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/send_packets.h b/src/send_packets.h index 7756c287..2c3335d7 100644 --- a/src/send_packets.h +++ b/src/send_packets.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/signal_handler.c b/src/signal_handler.c index 9bfd56ee..e3eb0264 100644 --- a/src/signal_handler.c +++ b/src/signal_handler.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/signal_handler.h b/src/signal_handler.h index db1ef91c..a66a1ca9 100644 --- a/src/signal_handler.h +++ b/src/signal_handler.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/sleep.c b/src/sleep.c index 27e3e85f..d187377f 100644 --- a/src/sleep.c +++ b/src/sleep.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/sleep.h b/src/sleep.h index 763b8980..5ccb9f77 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpbridge.c b/src/tcpbridge.c index e37936b7..346f3dfb 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpbridge.h b/src/tcpbridge.h index 717bf218..7d1d7ad7 100644 --- a/src/tcpbridge.h +++ b/src/tcpbridge.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpbridge_opts.def b/src/tcpbridge_opts.def index a1f811ea..0dce6ca3 100644 --- a/src/tcpbridge_opts.def +++ b/src/tcpbridge_opts.def @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 @@ -28,7 +28,7 @@ copyright = { eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; author = <<- EOText -Copyright 2013-2022 Fred Klassen - AppNeta +Copyright 2013-2024 Fred Klassen - AppNeta Copyright 2000-2012 Aaron Turner @@ -366,7 +366,7 @@ flag = { fprintf(stderr, " (debug)"); #endif fprintf(stderr, "\n"); - fprintf(stderr, "Copyright 2013-2022 by Fred Klassen - AppNeta\n"); + fprintf(stderr, "Copyright 2013-2024 by Fred Klassen - AppNeta\n"); fprintf(stderr, "Copyright 2000-2012 by Aaron Turner \n"); fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n"); #ifdef HAVE_LIBDNET diff --git a/src/tcpcapinfo.c b/src/tcpcapinfo.c index 4182a68c..3985b2ae 100644 --- a/src/tcpcapinfo.c +++ b/src/tcpcapinfo.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2012 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpcapinfo_opts.def b/src/tcpcapinfo_opts.def index 978dbb2a..06a84511 100644 --- a/src/tcpcapinfo_opts.def +++ b/src/tcpcapinfo_opts.def @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 @@ -102,7 +102,7 @@ flag = { fprintf(stderr, " (debug)"); #endif fprintf(stderr, "\n"); - fprintf(stderr, "Copyright 2013-2022 by Fred Klassen - AppNeta\n"); + fprintf(stderr, "Copyright 2013-2024 by Fred Klassen - AppNeta\n"); fprintf(stderr, "Copyright 2000-2010 by Aaron Turner \n"); fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n"); exit(0); diff --git a/src/tcpedit/checksum.c b/src/tcpedit/checksum.c index 07abe5da..52d9cb65 100644 --- a/src/tcpedit/checksum.c +++ b/src/tcpedit/checksum.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/checksum.h b/src/tcpedit/checksum.h index aa5d1381..6eb7bf9f 100644 --- a/src/tcpedit/checksum.h +++ b/src/tcpedit/checksum.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/dlt.c b/src/tcpedit/dlt.c index a1d26380..6d457fdd 100644 --- a/src/tcpedit/dlt.c +++ b/src/tcpedit/dlt.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/dlt.h b/src/tcpedit/dlt.h index 18bc969a..a29cd7a6 100644 --- a/src/tcpedit/dlt.h +++ b/src/tcpedit/dlt.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/edit_packet.c b/src/tcpedit/edit_packet.c index 43ff04a8..94ac903f 100644 --- a/src/tcpedit/edit_packet.c +++ b/src/tcpedit/edit_packet.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/edit_packet.h b/src/tcpedit/edit_packet.h index d221cc8b..a507f795 100644 --- a/src/tcpedit/edit_packet.h +++ b/src/tcpedit/edit_packet.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/parse_args.c b/src/tcpedit/parse_args.c index 958d1711..02694b9a 100644 --- a/src/tcpedit/parse_args.c +++ b/src/tcpedit/parse_args.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/parse_args.h b/src/tcpedit/parse_args.h index c6ecd81b..639247d1 100644 --- a/src/tcpedit/parse_args.h +++ b/src/tcpedit/parse_args.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins.h b/src/tcpedit/plugins.h index 02eedb0b..2d3b8807 100644 --- a/src/tcpedit/plugins.h +++ b/src/tcpedit/plugins.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb.c b/src/tcpedit/plugins/dlt_en10mb/en10mb.c index 3fbca624..8a8b2330 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb.c +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb.h b/src/tcpedit/plugins/dlt_en10mb/en10mb.h index efc1e550..cb5b35e3 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb.h +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb_api.c b/src/tcpedit/plugins/dlt_en10mb/en10mb_api.c index 6fa31a86..a90b332d 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb_api.c +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb_api.h b/src/tcpedit/plugins/dlt_en10mb/en10mb_api.h index 20940e23..2b1ad3a5 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb_api.h +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_en10mb/en10mb_types.h b/src/tcpedit/plugins/dlt_en10mb/en10mb_types.h index 77cedace..d1f9a275 100644 --- a/src/tcpedit/plugins/dlt_en10mb/en10mb_types.h +++ b/src/tcpedit/plugins/dlt_en10mb/en10mb_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_hdlc/hdlc.c b/src/tcpedit/plugins/dlt_hdlc/hdlc.c index d2cbcda1..41c9349c 100644 --- a/src/tcpedit/plugins/dlt_hdlc/hdlc.c +++ b/src/tcpedit/plugins/dlt_hdlc/hdlc.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_hdlc/hdlc.h b/src/tcpedit/plugins/dlt_hdlc/hdlc.h index 379d1728..f6a6fbbf 100644 --- a/src/tcpedit/plugins/dlt_hdlc/hdlc.h +++ b/src/tcpedit/plugins/dlt_hdlc/hdlc.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_hdlc/hdlc_api.c b/src/tcpedit/plugins/dlt_hdlc/hdlc_api.c index 516dcfac..34c8a110 100644 --- a/src/tcpedit/plugins/dlt_hdlc/hdlc_api.c +++ b/src/tcpedit/plugins/dlt_hdlc/hdlc_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_hdlc/hdlc_api.h b/src/tcpedit/plugins/dlt_hdlc/hdlc_api.h index c5851cb4..fd419f2a 100644 --- a/src/tcpedit/plugins/dlt_hdlc/hdlc_api.h +++ b/src/tcpedit/plugins/dlt_hdlc/hdlc_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_hdlc/hdlc_types.h b/src/tcpedit/plugins/dlt_hdlc/hdlc_types.h index 60d2c519..c1930455 100644 --- a/src/tcpedit/plugins/dlt_hdlc/hdlc_types.h +++ b/src/tcpedit/plugins/dlt_hdlc/hdlc_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_ieee80211/ieee80211.c b/src/tcpedit/plugins/dlt_ieee80211/ieee80211.c index 092bddf9..c82cfc75 100644 --- a/src/tcpedit/plugins/dlt_ieee80211/ieee80211.c +++ b/src/tcpedit/plugins/dlt_ieee80211/ieee80211.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_ieee80211/ieee80211.h b/src/tcpedit/plugins/dlt_ieee80211/ieee80211.h index b983914e..d65183ae 100644 --- a/src/tcpedit/plugins/dlt_ieee80211/ieee80211.h +++ b/src/tcpedit/plugins/dlt_ieee80211/ieee80211.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.c b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.c index 12b23835..8b720731 100644 --- a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.c +++ b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.h b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.h index 77c6c23d..6f846e53 100644 --- a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.h +++ b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_hdr.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_types.h b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_types.h index 06f02dee..4b0b10d1 100644 --- a/src/tcpedit/plugins/dlt_ieee80211/ieee80211_types.h +++ b/src/tcpedit/plugins/dlt_ieee80211/ieee80211_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c index f5cd73b0..f96e4242 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2006-2007 Aaron Turner. - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.h b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.h index 4875350d..11bd372f 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.h +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.c b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.c index 393c4d25..b8693a12 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.c +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2009 Aaron Turner. - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.h b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.h index dbfaea8b..3bb60a2c 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.h +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_types.h b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_types.h index 95b466f7..5f1013cc 100644 --- a/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_types.h +++ b/src/tcpedit/plugins/dlt_jnpr_ether/jnpr_ether_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_linuxsll/linuxsll.c b/src/tcpedit/plugins/dlt_linuxsll/linuxsll.c index e6dfeb2f..ee4f2793 100644 --- a/src/tcpedit/plugins/dlt_linuxsll/linuxsll.c +++ b/src/tcpedit/plugins/dlt_linuxsll/linuxsll.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_linuxsll/linuxsll.h b/src/tcpedit/plugins/dlt_linuxsll/linuxsll.h index eb2b26e3..113a4e3e 100644 --- a/src/tcpedit/plugins/dlt_linuxsll/linuxsll.h +++ b/src/tcpedit/plugins/dlt_linuxsll/linuxsll.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_loop/loop.c b/src/tcpedit/plugins/dlt_loop/loop.c index 70b5fba0..144c0422 100644 --- a/src/tcpedit/plugins/dlt_loop/loop.c +++ b/src/tcpedit/plugins/dlt_loop/loop.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_loop/loop.h b/src/tcpedit/plugins/dlt_loop/loop.h index 5ffdcc9c..672067ca 100644 --- a/src/tcpedit/plugins/dlt_loop/loop.h +++ b/src/tcpedit/plugins/dlt_loop/loop.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_null/null.c b/src/tcpedit/plugins/dlt_null/null.c index cc682aa1..e00b07c1 100644 --- a/src/tcpedit/plugins/dlt_null/null.c +++ b/src/tcpedit/plugins/dlt_null/null.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_null/null.h b/src/tcpedit/plugins/dlt_null/null.h index fe348c51..ac231abd 100644 --- a/src/tcpedit/plugins/dlt_null/null.h +++ b/src/tcpedit/plugins/dlt_null/null.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_plugins.c b/src/tcpedit/plugins/dlt_plugins.c index 70885309..902ed52e 100644 --- a/src/tcpedit/plugins/dlt_plugins.c +++ b/src/tcpedit/plugins/dlt_plugins.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_pppserial/pppserial.c b/src/tcpedit/plugins/dlt_pppserial/pppserial.c index 1ac13562..41404ff6 100644 --- a/src/tcpedit/plugins/dlt_pppserial/pppserial.c +++ b/src/tcpedit/plugins/dlt_pppserial/pppserial.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2006-2007 Aaron Turner. - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/tcpedit/plugins/dlt_pppserial/pppserial.h b/src/tcpedit/plugins/dlt_pppserial/pppserial.h index 7bea1bed..7f72754a 100644 --- a/src/tcpedit/plugins/dlt_pppserial/pppserial.h +++ b/src/tcpedit/plugins/dlt_pppserial/pppserial.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_pppserial/pppserial_api.c b/src/tcpedit/plugins/dlt_pppserial/pppserial_api.c index a0921074..3d9ea0cf 100644 --- a/src/tcpedit/plugins/dlt_pppserial/pppserial_api.c +++ b/src/tcpedit/plugins/dlt_pppserial/pppserial_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2009 Aaron Turner. - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - AppNeta * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/src/tcpedit/plugins/dlt_pppserial/pppserial_api.h b/src/tcpedit/plugins/dlt_pppserial/pppserial_api.h index f37c4800..4185b092 100644 --- a/src/tcpedit/plugins/dlt_pppserial/pppserial_api.h +++ b/src/tcpedit/plugins/dlt_pppserial/pppserial_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_pppserial/pppserial_types.h b/src/tcpedit/plugins/dlt_pppserial/pppserial_types.h index 3b7b33e4..e72e143a 100644 --- a/src/tcpedit/plugins/dlt_pppserial/pppserial_types.h +++ b/src/tcpedit/plugins/dlt_pppserial/pppserial_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_radiotap/radiotap.c b/src/tcpedit/plugins/dlt_radiotap/radiotap.c index 9e875448..a02f1ba4 100644 --- a/src/tcpedit/plugins/dlt_radiotap/radiotap.c +++ b/src/tcpedit/plugins/dlt_radiotap/radiotap.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_radiotap/radiotap.h b/src/tcpedit/plugins/dlt_radiotap/radiotap.h index dfd061e9..e51f7207 100644 --- a/src/tcpedit/plugins/dlt_radiotap/radiotap.h +++ b/src/tcpedit/plugins/dlt_radiotap/radiotap.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_raw/raw.c b/src/tcpedit/plugins/dlt_raw/raw.c index cc801b1a..dcd2f362 100644 --- a/src/tcpedit/plugins/dlt_raw/raw.c +++ b/src/tcpedit/plugins/dlt_raw/raw.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_raw/raw.h b/src/tcpedit/plugins/dlt_raw/raw.h index 0a7e45d3..6ebde966 100644 --- a/src/tcpedit/plugins/dlt_raw/raw.h +++ b/src/tcpedit/plugins/dlt_raw/raw.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_template/plugin.h b/src/tcpedit/plugins/dlt_template/plugin.h index 1e69bce7..f715c8f6 100644 --- a/src/tcpedit/plugins/dlt_template/plugin.h +++ b/src/tcpedit/plugins/dlt_template/plugin.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_template/plugin_api.h b/src/tcpedit/plugins/dlt_template/plugin_api.h index bf6d4710..07683636 100644 --- a/src/tcpedit/plugins/dlt_template/plugin_api.h +++ b/src/tcpedit/plugins/dlt_template/plugin_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_template/plugin_types.h b/src/tcpedit/plugins/dlt_template/plugin_types.h index e0a6615f..276b8b07 100644 --- a/src/tcpedit/plugins/dlt_template/plugin_types.h +++ b/src/tcpedit/plugins/dlt_template/plugin_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_user/user.c b/src/tcpedit/plugins/dlt_user/user.c index b086670f..8f90994a 100644 --- a/src/tcpedit/plugins/dlt_user/user.c +++ b/src/tcpedit/plugins/dlt_user/user.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_user/user.h b/src/tcpedit/plugins/dlt_user/user.h index 94b5d18a..5515a10a 100644 --- a/src/tcpedit/plugins/dlt_user/user.h +++ b/src/tcpedit/plugins/dlt_user/user.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_user/user_api.c b/src/tcpedit/plugins/dlt_user/user_api.c index eea2544f..8eb1131c 100644 --- a/src/tcpedit/plugins/dlt_user/user_api.c +++ b/src/tcpedit/plugins/dlt_user/user_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_user/user_api.h b/src/tcpedit/plugins/dlt_user/user_api.h index f8df9d38..c686f13d 100644 --- a/src/tcpedit/plugins/dlt_user/user_api.h +++ b/src/tcpedit/plugins/dlt_user/user_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_utils.c b/src/tcpedit/plugins/dlt_utils.c index 29ebf1e0..67e13765 100644 --- a/src/tcpedit/plugins/dlt_utils.c +++ b/src/tcpedit/plugins/dlt_utils.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/dlt_utils.h b/src/tcpedit/plugins/dlt_utils.h index dcc88c48..27732106 100644 --- a/src/tcpedit/plugins/dlt_utils.h +++ b/src/tcpedit/plugins/dlt_utils.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/ethernet.c b/src/tcpedit/plugins/ethernet.c index bf85a2c8..347bb174 100644 --- a/src/tcpedit/plugins/ethernet.c +++ b/src/tcpedit/plugins/ethernet.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins/ethernet.h b/src/tcpedit/plugins/ethernet.h index db38cc2e..e4061505 100644 --- a/src/tcpedit/plugins/ethernet.h +++ b/src/tcpedit/plugins/ethernet.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins_api.h b/src/tcpedit/plugins_api.h index f22c07d9..dcd05c45 100644 --- a/src/tcpedit/plugins_api.h +++ b/src/tcpedit/plugins_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/plugins_types.h b/src/tcpedit/plugins_types.h index fae43f2c..d3758617 100644 --- a/src/tcpedit/plugins_types.h +++ b/src/tcpedit/plugins_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/portmap.c b/src/tcpedit/portmap.c index 5fe1779f..2e3d6af4 100644 --- a/src/tcpedit/portmap.c +++ b/src/tcpedit/portmap.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/portmap.h b/src/tcpedit/portmap.h index 05f90d52..8f9022e3 100644 --- a/src/tcpedit/portmap.h +++ b/src/tcpedit/portmap.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index f846d8b6..fed6277b 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit.h b/src/tcpedit/tcpedit.h index af6b1821..a7c44a39 100644 --- a/src/tcpedit/tcpedit.h +++ b/src/tcpedit/tcpedit.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit_api.c b/src/tcpedit/tcpedit_api.c index 3f23d6ac..6c7e0827 100644 --- a/src/tcpedit/tcpedit_api.c +++ b/src/tcpedit/tcpedit_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit_api.h b/src/tcpedit/tcpedit_api.h index 6547f262..4233a938 100644 --- a/src/tcpedit/tcpedit_api.h +++ b/src/tcpedit/tcpedit_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit_opts.def b/src/tcpedit/tcpedit_opts.def index a01523b1..5114bc3a 100644 --- a/src/tcpedit/tcpedit_opts.def +++ b/src/tcpedit/tcpedit_opts.def @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit_stub.def b/src/tcpedit/tcpedit_stub.def index 7b2000e9..999e834f 100644 --- a/src/tcpedit/tcpedit_stub.def +++ b/src/tcpedit/tcpedit_stub.def @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpedit/tcpedit_types.h b/src/tcpedit/tcpedit_types.h index 7085d737..68a3d5df 100644 --- a/src/tcpedit/tcpedit_types.h +++ b/src/tcpedit/tcpedit_types.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpprep.c b/src/tcpprep.c index 83824766..06e2a847 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpprep.h b/src/tcpprep.h index 0a8c4f35..c18a232b 100644 --- a/src/tcpprep.h +++ b/src/tcpprep.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpprep_api.c b/src/tcpprep_api.c index 25f9a851..81fea01d 100644 --- a/src/tcpprep_api.c +++ b/src/tcpprep_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpprep_api.h b/src/tcpprep_api.h index 279de749..92e1da78 100644 --- a/src/tcpprep_api.h +++ b/src/tcpprep_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpprep_opts.def b/src/tcpprep_opts.def index 14c0a37c..e4bc2349 100644 --- a/src/tcpprep_opts.def +++ b/src/tcpprep_opts.def @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 @@ -26,7 +26,7 @@ copyright = { eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; author = <<- EOText -Copyright 2013-2022 Fred Klassen - AppNeta +Copyright 2013-2024 Fred Klassen - AppNeta Copyright 2000-2012 Aaron Turner @@ -601,7 +601,7 @@ flag = { fprintf(stderr, " (debug)"); #endif fprintf(stderr, "\n"); - fprintf(stderr, "Copyright 2013-2022 by Fred Klassen - AppNeta\n"); + fprintf(stderr, "Copyright 2013-2024 by Fred Klassen - AppNeta\n"); fprintf(stderr, "Copyright 2000-2012 by Aaron Turner \n"); fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n"); fprintf(stderr, "Cache file supported: %s\n", CACHEVERSION); diff --git a/src/tcpr.h b/src/tcpr.h index 5554a296..4da521a0 100644 --- a/src/tcpr.h +++ b/src/tcpr.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpreplay.c b/src/tcpreplay.c index 84bc84d9..c33899a8 100644 --- a/src/tcpreplay.c +++ b/src/tcpreplay.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpreplay.h b/src/tcpreplay.h index ddfaea8c..1006843e 100644 --- a/src/tcpreplay.h +++ b/src/tcpreplay.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 453b92d7..3f5d125a 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index ca60eb81..c7154ed0 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index e3536fde..82dddc9f 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 @@ -27,7 +27,7 @@ copyright = { eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; author = <<- EOText -Copyright 2013-2022 Fred Klassen - AppNeta +Copyright 2013-2024 Fred Klassen - AppNeta Copyright 2000-2012 Aaron Turner @@ -650,7 +650,7 @@ flag = { fprintf(stderr, " (timestamp-trace)"); #endif fprintf(stderr, "\n"); - fprintf(stderr, "Copyright 2013-2022 by Fred Klassen - AppNeta\n"); + fprintf(stderr, "Copyright 2013-2024 by Fred Klassen - AppNeta\n"); fprintf(stderr, "Copyright 2000-2012 by Aaron Turner \n"); fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n"); fprintf(stderr, "Cache file supported: %s\n", CACHEVERSION); @@ -695,6 +695,11 @@ flag = { fprintf(stderr, "Optional injection method: netmap\n"); #else fprintf(stderr, "Not compiled with netmap\n"); +#endif +#ifdef HAVE_LIBXDP + fprintf(stderr, "Optional injection method: AF_XDP\n"); +#else + fprintf(stderr, "Not compiled with AF_XDP\n"); #endif exit(0); diff --git a/src/tcprewrite.c b/src/tcprewrite.c index c9aa52c8..3904ae52 100644 --- a/src/tcprewrite.c +++ b/src/tcprewrite.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcprewrite.h b/src/tcprewrite.h index b88eb860..bbbe5e00 100644 --- a/src/tcprewrite.h +++ b/src/tcprewrite.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tcprewrite_opts.def b/src/tcprewrite_opts.def index a3f40077..f52aac54 100644 --- a/src/tcprewrite_opts.def +++ b/src/tcprewrite_opts.def @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 @@ -26,7 +26,7 @@ copyright = { eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; author = <<- EOText -Copyright 2013-2022 Fred Klassen - AppNeta +Copyright 2013-2024 Fred Klassen - AppNeta Copyright 2000-2012 Aaron Turner @@ -251,7 +251,7 @@ flag = { fprintf(stderr, " (debug)"); #endif fprintf(stderr, "\n"); - fprintf(stderr, "Copyright 2013-2022 by Fred Klassen - AppNeta\n"); + fprintf(stderr, "Copyright 2013-2024 by Fred Klassen - AppNeta\n"); fprintf(stderr, "Copyright 2000-2012 by Aaron Turner \n"); fprintf(stderr, "The entire Tcpreplay Suite is licensed under the GPLv3\n"); fprintf(stderr, "Cache file supported: %s\n", CACHEVERSION); diff --git a/src/timestamp_trace.h b/src/timestamp_trace.h index 19d7548d..b6ff7652 100644 --- a/src/timestamp_trace.h +++ b/src/timestamp_trace.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tree.c b/src/tree.c index 41445816..8c85ad01 100644 --- a/src/tree.c +++ b/src/tree.c @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 diff --git a/src/tree.h b/src/tree.h index 9c6ff1b4..c4d104ac 100644 --- a/src/tree.h +++ b/src/tree.h @@ -2,7 +2,7 @@ /* * Copyright (c) 2001-2010 Aaron Turner - * Copyright (c) 2013-2022 Fred Klassen - AppNeta + * Copyright (c) 2013-2024 Fred Klassen - 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 From 7dd742594bacd439fd60d713001c996fceae8670 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sat, 8 Jun 2024 20:37:09 -0700 Subject: [PATCH 66/69] Bug #876 - add missing free_umem_and_xsk function Missed in merge. --- docs/CHANGELOG | 3 ++- docs/INSTALL | 1 + src/tcpedit/tcpedit.c | 1 - src/tcpreplay_api.c | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ba0e734e..6472d723 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/08/2024 Version 4.5.0-beta2 + - AF_XDP compile issue due to merge issue (#876) - memory leak in tcpprep when using include/exclude (#869) - memory leak in tcpprep when using RegEx (#867) - fix nanosecond timestamp regression bug (#863) @@ -14,7 +15,7 @@ - Infinite loop in tcprewrite at get.c (#827 #842) - SLL incorrect size and protocol when converted to Ethernet (#826) - CVE-2023-43279 add check for empty CIDR (#824 #843) - - AF_XDF socket extension (#822 #823) + - AF_XDP socket extension (#822 #823) - configure.ac: unify search dirs for pcap and add lib32 (#819) - tcpreplay-edit recomputes IPv4 checksums unnecessarily (#815 #846) - CVE-2023-4256 double free in tcprewrite DLT_JUNIPER_ETHER (#813 #851) diff --git a/docs/INSTALL b/docs/INSTALL index 1a74f66a..bb47ba92 100644 --- a/docs/INSTALL +++ b/docs/INSTALL @@ -138,6 +138,7 @@ written to directly. This feature requires `libxdp-dev` and `libbpf-dev` packages to be installed. For example: + $ sudo apt install -y libxdp-dev libbpf-dev $ ./configure | tail Linux/BSD netmap: no Tuntap device support: yes diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index fed6277b..07ed5742 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -188,7 +188,6 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, u_char **pktdata newval = *((uint16_t *)ip_hdr); newval = htons((ntohs(newval) & 0xff00) | (tcpedit->tos & 0xff)); *((uint16_t *)ip_hdr) = newval; - static uint32_t cnt; csum_replace2(&ip_hdr->ip_sum, oldval, newval); } diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index 3f5d125a..c2cb58d3 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -1400,3 +1400,37 @@ apply_loop_delay(tcpreplay_t *ctx) { return false; } + +#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); + } + } + 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*/ From 7ff711b49bd6de3d12069d1a4cf8b0e447b5422a Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 9 Jun 2024 13:28:29 -0700 Subject: [PATCH 67/69] Feature #878 - add -w / --suppress-warning option Suppress warning if desired. --- docs/CHANGELOG | 1 + src/common/err.c | 2 ++ src/common/err.h | 12 ++++++++---- src/tcpbridge.c | 3 ++- src/tcpbridge_opts.def | 12 +++++++++++- src/tcpprep.c | 3 +++ src/tcpprep_api.c | 3 +++ src/tcpprep_opts.def | 11 ++++++++++- src/tcpreplay_api.c | 3 +++ src/tcpreplay_opts.def | 11 ++++++++++- src/tcprewrite.c | 3 +++ src/tcprewrite_opts.def | 11 ++++++++++- 12 files changed, 66 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index 6472d723..d18e0870 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ 06/08/2024 Version 4.5.0-beta2 + - add -w (--suppress-warnings) option to suppress warning messages (#878) - AF_XDP compile issue due to merge issue (#876) - memory leak in tcpprep when using include/exclude (#869) - memory leak in tcpprep when using RegEx (#867) diff --git a/src/common/err.c b/src/common/err.c index 0333f1ce..606e1ecb 100644 --- a/src/common/err.c +++ b/src/common/err.c @@ -50,6 +50,8 @@ #include #include +int print_warnings = 1; + /** * writes a notice message to stderr. Always forces a newline */ diff --git a/src/common/err.h b/src/common/err.h index 657040c1..62424f44 100644 --- a/src/common/err.h +++ b/src/common/err.h @@ -50,6 +50,8 @@ #include "defines.h" #include +extern int print_warnings; + #ifdef DEBUG extern int debug; #endif @@ -84,12 +86,14 @@ void notice(const char *fmt, ...); fprintf(stderr, "DEBUG%d in %s:%s() line %d: " y "\n", x, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); \ } \ } while(0) - - -#define warn(x) fprintf(stderr, "Warning in %s:%s() line %d:\n%s\n", __FILE__, __FUNCTION__, __LINE__, x) +#define warn(x) \ + if (print_warnings) \ + fprintf(stderr, "Warning in %s:%s() line %d:\n%s\n", __FILE__, __FUNCTION__, __LINE__, x) -#define warnx(x, ...) fprintf(stderr, "Warning in %s:%s() line %d:\n" x "\n", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) +#define warnx(x, ...) \ + if (print_warnings) \ + fprintf(stderr, "Warning in %s:%s() line %d:\n" x "\n", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) #define err(x, y) do { \ fprintf(stderr, "\nFatal Error in %s:%s() line %d:\n%s\n", __FILE__, __FUNCTION__, __LINE__, y); \ diff --git a/src/tcpbridge.c b/src/tcpbridge.c index 346f3dfb..c8acb576 100644 --- a/src/tcpbridge.c +++ b/src/tcpbridge.c @@ -149,7 +149,8 @@ post_args(_U_ int argc, _U_ char *argv[]) if (HAVE_OPT(DBUG)) warn("not configured with --enable-debug. Debugging disabled."); #endif - + if (HAVE_OPT(SUPPRESS_WARNINGS)) + print_warnings = 0; #ifdef ENABLE_VERBOSE if (HAVE_OPT(VERBOSE)) options.verbose = 1; diff --git a/src/tcpbridge_opts.def b/src/tcpbridge_opts.def index 0dce6ca3..ee1f13f4 100644 --- a/src/tcpbridge_opts.def +++ b/src/tcpbridge_opts.def @@ -23,7 +23,7 @@ autogen definitions options; copyright = { - date = "2000-2022"; + date = "2000-2024"; owner = "Aaron Turner and Fred Klassen"; eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; @@ -411,3 +411,13 @@ flag = { EOHelp; doc = ""; }; + +flag = { + name = suppress-warnings; + value = w; + immediate; + descrip = "suppress printing warning messages"; + settable; + doc = ""; +}; + diff --git a/src/tcpprep.c b/src/tcpprep.c index 06e2a847..28cc2cac 100644 --- a/src/tcpprep.c +++ b/src/tcpprep.c @@ -125,6 +125,9 @@ main(int argc, char *argv[]) err(-1, "MAC mode splitting is only supported by DLT_EN10MB packet captures."); } + if (HAVE_OPT(SUPPRESS_WARNINGS)) + print_warnings = 0; + #ifdef ENABLE_VERBOSE if (HAVE_OPT(VERBOSE)) { tcpdump_open(&tcpprep->tcpdump, options->pcap); diff --git a/src/tcpprep_api.c b/src/tcpprep_api.c index 81fea01d..2c68e0b0 100644 --- a/src/tcpprep_api.c +++ b/src/tcpprep_api.c @@ -141,6 +141,9 @@ tcpprep_post_args(tcpprep_t *ctx, int argc, char *argv[]) debug = OPT_VALUE_DBUG; #endif + if (HAVE_OPT(SUPPRESS_WARNINGS)) + print_warnings = 0; + #ifdef ENABLE_VERBOSE if (HAVE_OPT(VERBOSE)) { ctx->options->verbose = 1; diff --git a/src/tcpprep_opts.def b/src/tcpprep_opts.def index e4bc2349..be2ef991 100644 --- a/src/tcpprep_opts.def +++ b/src/tcpprep_opts.def @@ -21,7 +21,7 @@ autogen definitions options; copyright = { - date = "2000-2022"; + date = "2000-2024"; owner = "Aaron Turner and Fred Klassen"; eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; @@ -644,3 +644,12 @@ flag = { EOHelp; }; + +flag = { + name = suppress-warnings; + value = w; + immediate; + descrip = "suppress printing warning messages"; + settable; + doc = ""; +}; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index c2cb58d3..c4a412a4 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -209,6 +209,9 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) options->maxsleep.tv_nsec = (OPT_VALUE_MAXSLEEP % 1000) * 1000 * 1000; } + if (HAVE_OPT(SUPPRESS_WARNINGS)) + print_warnings = 0; + #ifdef ENABLE_VERBOSE if (HAVE_OPT(VERBOSE)) options->verbose = 1; diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index 82dddc9f..c3ddfa9d 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -22,7 +22,7 @@ autogen definitions options; copyright = { - date = "2000-2022"; + date = "2000-2024"; owner = "Aaron Turner and Fred Klassen"; eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; @@ -719,3 +719,12 @@ flag = { EOHelp; doc = ""; }; + +flag = { + name = suppress-warnings; + value = w; + immediate; + descrip = "suppress printing warning messages"; + settable; + doc = ""; +}; diff --git a/src/tcprewrite.c b/src/tcprewrite.c index 3904ae52..d46e77d5 100644 --- a/src/tcprewrite.c +++ b/src/tcprewrite.c @@ -193,6 +193,9 @@ post_args(_U_ int argc, _U_ char *argv[]) warn("not configured with --enable-debug. Debugging disabled."); #endif + if (HAVE_OPT(SUPPRESS_WARNINGS)) + print_warnings = 0; + #ifdef ENABLE_VERBOSE if (HAVE_OPT(VERBOSE)) options.verbose = 1; diff --git a/src/tcprewrite_opts.def b/src/tcprewrite_opts.def index f52aac54..6ab38301 100644 --- a/src/tcprewrite_opts.def +++ b/src/tcprewrite_opts.def @@ -21,7 +21,7 @@ autogen definitions options; copyright = { - date = "2000-2022"; + date = "2000-2024"; owner = "Aaron Turner and Fred Klassen"; eaddr = "tcpreplay-users@lists.sourceforge.net"; type = gpl; @@ -300,3 +300,12 @@ flag = { EOHelp; doc = ""; }; + +flag = { + name = suppress-warnings; + value = w; + immediate; + descrip = "suppress printing warning messages"; + settable; + doc = ""; +}; From d7f5bfe94feab0c9b15f4af8ac0fab97c31e5b8b Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 9 Jun 2024 11:02:44 -0700 Subject: [PATCH 68/69] Bug #835 - improved flow_decode error messages Support packet numbers and warnings for all failing paths --- src/common/flows.c | 89 ++++++++++++++++++++++++++++++--------- src/common/flows.h | 3 +- src/common/get.c | 103 ++++++++++++++++++++++++++++++++++++--------- src/send_packets.c | 19 ++++++--- 4 files changed, 164 insertions(+), 50 deletions(-) diff --git a/src/common/flows.c b/src/common/flows.c index 5a674377..0fcb634f 100644 --- a/src/common/flows.c +++ b/src/common/flows.c @@ -157,8 +157,13 @@ static inline flow_entry_type_t hash_put_data(flow_hash_table_t *fht, const uint /* * Decode the packet, study it's flow status and report */ -flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *pkthdr, - const u_char *pktdata, const int datalink, const int expiry) +flow_entry_type_t +flow_decode(flow_hash_table_t *fht, + const struct pcap_pkthdr *pkthdr, + const u_char *pktdata, + const int datalink, + const int expiry, + COUNTER packetnum) { uint32_t pkt_len = pkthdr->caplen; const u_char *packet = pktdata; @@ -195,30 +200,52 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr * &vlan_offset); if (res == -1) { - warnx("Unable to process unsupported DLT type: %s (0x%x)", - pcap_datalink_val_to_description(datalink), datalink); + warnx("flow_decode failed to determine %s header length for packet " COUNTER_SPEC "", + pcap_datalink_val_to_description(datalink), + packetnum); return FLOW_ENTRY_INVALID; } if (ether_type == ETHERTYPE_IP) { - if (pkt_len < l2len + sizeof(ipv4_hdr_t)) - return FLOW_ENTRY_INVALID; + size_t required_len = sizeof(ipv4_hdr_t) + l2len; + if (pkt_len < required_len) { + warnx("flow_decode: packet " COUNTER_SPEC " needs at least %zd bytes for IPv4 header but only %d available", + packetnum, + required_len, + pkt_len); + return FLOW_ENTRY_INVALID; + } ip_hdr = (ipv4_hdr_t *)(packet + l2len); - if (ip_hdr->ip_v != 4) + if (ip_hdr->ip_v != 4) { + warnx("flow_decode: packet " COUNTER_SPEC " IPv4 header version should be 4 but instead is %u", + packetnum, + ip_hdr->ip_v); return FLOW_ENTRY_NON_IP; + } ip_len = ip_hdr->ip_hl * 4; protocol = ip_hdr->ip_p; entry.src_ip.in = ip_hdr->ip_src; entry.dst_ip.in = ip_hdr->ip_dst; } else if (ether_type == ETHERTYPE_IP6) { - if (pkt_len < l2len + sizeof(ipv6_hdr_t)) - return FLOW_ENTRY_INVALID; + size_t required_len = sizeof(ipv6_hdr_t) + l2len; + if (pkt_len < required_len) { + warnx("flow_decode: packet " COUNTER_SPEC " needs at least %zd bytes for IPv6 header but only %d available", + packetnum, + required_len, + pkt_len); + return FLOW_ENTRY_INVALID; + } - if ((packet[0] >> 4) != 6) + uint8_t ip6_version = packet[0] >> 4; + if (ip6_version != 6) { + warnx("flow_decode: packet " COUNTER_SPEC " IPv6 header version should be 6 but instead is %u", + packetnum, + ip6_version); return FLOW_ENTRY_NON_IP; + } ip6_hdr = (ipv6_hdr_t *)(packet + l2len); ip_len = sizeof(*ip6_hdr); @@ -238,30 +265,50 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr * entry.protocol = protocol; switch (protocol) { - case IPPROTO_UDP: - if (pkt_len < (l2len + ip_len + sizeof(udp_hdr_t))) + case IPPROTO_UDP: { + size_t required_len = sizeof(udp_hdr_t) + l2len + ip_len; + if (pkt_len < required_len) { + warnx("flow_decode: packet " COUNTER_SPEC " needs at least %zd bytes for UDP header but only %d available", + packetnum, + required_len, + pkt_len); return FLOW_ENTRY_INVALID; - udp_hdr = (udp_hdr_t*)(packet + ip_len + l2len); + } + udp_hdr = (udp_hdr_t *)(packet + ip_len + l2len); entry.src_port = udp_hdr->uh_sport; entry.dst_port = udp_hdr->uh_dport; break; - - case IPPROTO_TCP: - if (pkt_len < (l2len + ip_len + sizeof(tcp_hdr_t))) + } + case IPPROTO_TCP: { + size_t required_len = sizeof(tcp_hdr_t) + l2len + ip_len; + if (pkt_len < required_len) { + warnx("flow_decode: packet " COUNTER_SPEC " needs at least %zd bytes for TCP header but only %d available", + packetnum, + required_len, + pkt_len); return FLOW_ENTRY_INVALID; - tcp_hdr = (tcp_hdr_t*)(packet + ip_len + l2len); + } + tcp_hdr = (tcp_hdr_t *)(packet + ip_len + l2len); entry.src_port = tcp_hdr->th_sport; entry.dst_port = tcp_hdr->th_dport; break; - + } case IPPROTO_ICMP: - case IPPROTO_ICMPV6: - if (pkt_len < (l2len + ip_len + sizeof(icmpv4_hdr_t))) + case IPPROTO_ICMPV6: { + size_t required_len = sizeof(icmpv4_hdr_t) + l2len + ip_len; + if (pkt_len < required_len) { + warnx("flow_decode: packet " COUNTER_SPEC " needs at least %zd bytes for %s header but only %d available", + packetnum, + required_len, + (protocol == IPPROTO_ICMP) ? "ICMP" : "ICMPv6", + pkt_len); return FLOW_ENTRY_INVALID; - icmp_hdr = (icmpv4_hdr_t*)(packet + ip_len + l2len); + } + icmp_hdr = (icmpv4_hdr_t *)(packet + ip_len + l2len); entry.src_port = icmp_hdr->icmp_type; entry.dst_port = icmp_hdr->icmp_code; break; + } default: entry.src_port = 0; entry.dst_port = 0; diff --git a/src/common/flows.h b/src/common/flows.h index cf9f6863..73e487c4 100644 --- a/src/common/flows.h +++ b/src/common/flows.h @@ -38,4 +38,5 @@ flow_entry_type_t flow_decode(flow_hash_table_t *fht, const struct pcap_pkthdr *pkthdr, const u_char *pktdata, const int datalink, - const int expiry); + const int expiry, + COUNTER packetnum); diff --git a/src/common/get.c b/src/common/get.c index 418b5d5b..001b42ce 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -103,8 +103,12 @@ parse_mpls(const u_char *pktdata, uint32_t datalen, uint16_t *next_protocol, uin /* move over MPLS labels until we get to the last one */ while (!bos) { - if (pktdata + len + sizeof(*mpls_label) > end_ptr) + if (pktdata + len + sizeof(*mpls_label) > end_ptr) { + warnx("parse_mpls: Need at least %zu bytes for MPLS header but only %u available", + sizeof(*mpls_label) + len, + datalen); return -1; + } mpls_label = (struct tcpr_mpls_label *)(pktdata + len); len += sizeof(*mpls_label); @@ -117,8 +121,12 @@ parse_mpls(const u_char *pktdata, uint32_t datalen, uint16_t *next_protocol, uin } } - if ((u_char *)(mpls_label + 1) + 1 > end_ptr) + if ((u_char *)(mpls_label + 1) + 1 > end_ptr) { + warnx("parse_mpls: Need at least %zu bytes for MPLS label but only %u available", + sizeof(*mpls_label) + 1, + datalen); return -1; + } first_nibble = *((u_char *)(mpls_label + 1)) >> 4; switch (first_nibble) { @@ -132,8 +140,12 @@ parse_mpls(const u_char *pktdata, uint32_t datalen, uint16_t *next_protocol, uin /* EoMPLS - jump over PW Ethernet Control Word and handle * inner Ethernet header */ - if (pktdata + len + 4 + sizeof(*eth_hdr) > end_ptr) + if (pktdata + len + 4 + sizeof(*eth_hdr) > end_ptr) { + warnx("parse_mpls: Need at least %zu bytes for EoMPLS header but only %u available", + sizeof(*eth_hdr) + len + 4, + datalen); return -1; + } len += 4; *l2offset = len; @@ -142,7 +154,7 @@ parse_mpls(const u_char *pktdata, uint32_t datalen, uint16_t *next_protocol, uin *next_protocol = ntohs(eth_hdr->ether_type); break; default: - /* suspect Generic Associated Channel Header */ + warn("parse_mpls:suspect Generic Associated Channel Header"); return -1; } @@ -165,9 +177,10 @@ int parse_vlan(const u_char *pktdata, uint32_t datalen, uint16_t *next_protocol, uint32_t *l2len) { vlan_hdr_t *vlan_hdr; - if ((size_t)datalen < *l2len + sizeof(*vlan_hdr)) + if ((size_t)datalen < *l2len + sizeof(*vlan_hdr)) { + warnx("parse_vlan: Need at least %zu bytes for VLAN header but only %u available", sizeof(*vlan_hdr), datalen); return -1; - + } vlan_hdr = (vlan_hdr_t *)(pktdata + *l2len); *next_protocol = ntohs(vlan_hdr->vlan_tpid); *l2len += sizeof(vlan_hdr_t); @@ -277,19 +290,29 @@ get_l2len_protocol(const u_char *pktdata, *protocol = ETHERTYPE_IP6; break; case DLT_JUNIPER_ETHER: - if (datalen < 4) + if (datalen < 4) { + warnx("%s (0x%x): Need at least 4 bytes for DLT_JUNIPER_ETHER but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + datalen); return -1; + } if (memcmp(pktdata, JUNIPER_PCAP_MAGIC, 3) != 0) { - warnx("No Magic Number found during protocol lookup: %s (0x%x)", + warnx("%s (0x%x): No JUNIPER_PCAP_MAGIC Magic Number found during protocol lookup", pcap_datalink_val_to_description(datalink), datalink); return -1; } if ((pktdata[3] & JUNIPER_FLAG_EXT) == JUNIPER_FLAG_EXT) { - if (datalen < 6) + if (datalen < 6) { + warnx("%s (0x%x): Need at least 6 bytes for JUNIPER_FLAG_EXT but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + datalen); return -1; + } *l2offset = ntohs(*((uint16_t *)&pktdata[4])); *l2offset += 6; /* MGC + flags + ext_total_len */ @@ -300,8 +323,15 @@ get_l2len_protocol(const u_char *pktdata, if ((pktdata[3] & JUNIPER_FLAG_NO_L2) == JUNIPER_FLAG_NO_L2) { /* no L2 header present - *l2offset is actually IP offset */ uint32_t ip_hdr_offset = *l2offset; - if (datalen < ip_hdr_offset + 1) + uint32_t hdrSpaceNeeded = ip_hdr_offset + 1; + if (datalen < hdrSpaceNeeded) { + warnx("%s (0x%x): Need at least %u bytes for JUNIPER_FLAG_NO_L2 but only %u available", + pcap_datalink_val_to_description(datalink), + hdrSpaceNeeded, + datalink, + datalen); return -1; + } if ((pktdata[ip_hdr_offset] >> 4) == 4) *protocol = ETHERTYPE_IP; @@ -317,36 +347,49 @@ get_l2len_protocol(const u_char *pktdata, uint16_t ether_type; uint32_t l2_net_off = sizeof(*eth_hdr) + *l2offset; - if (datalen <= l2_net_off) + if (datalen <= l2_net_off) { + warnx("%s (0x%x): Need at least 4 bytes for DLT_EN10MB but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + datalen); return -1; + } eth_hdr = (eth_hdr_t *)(pktdata + *l2offset); ether_type = ntohs(eth_hdr->ether_type); if (parse_metadata(pktdata, datalen, ðer_type, &l2_net_off, l2offset, vlan_offset)) return -1; - if (datalen <= l2_net_off) - return -1; - *l2len = l2_net_off; if (ether_type > 1500) { /* Ethernet II frame - return in host order */ *protocol = ether_type; } else { /* 803.3 frame */ - if ((pktdata[l2_net_off] >> 4) == 4) + if ((pktdata[l2_net_off] >> 4) == 4) { *protocol = ETHERTYPE_IP; - else if ((pktdata[l2_net_off] >> 4) == 6) + } else if ((pktdata[l2_net_off] >> 4) == 6) { *protocol = ETHERTYPE_IP6; - else + } else { /* unsupported 802.3 protocol */ + warnx("%s (0x%x): unsupported 802.3 protocol %u", + pcap_datalink_val_to_description(datalink), + datalink, + ether_type); return -1; + } } break; } case DLT_PPP_SERIAL: - if ((size_t)datalen < sizeof(struct tcpr_pppserial_hdr)) + if ((size_t)datalen < sizeof(struct tcpr_pppserial_hdr)) { + warnx("%s (0x%x): Need at least %zu bytes for DLT_PPP_SERIAL but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + sizeof(struct tcpr_pppserial_hdr), + datalen); return -1; + } struct tcpr_pppserial_hdr *ppp = (struct tcpr_pppserial_hdr *)pktdata; *l2len = sizeof(*ppp); @@ -357,24 +400,42 @@ get_l2len_protocol(const u_char *pktdata, break; case DLT_C_HDLC: - if (datalen < CISCO_HDLC_LEN) + if (datalen < CISCO_HDLC_LEN) { + warnx("%s (0x%x): Need at least %u bytes for DLT_C_HDLC but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + CISCO_HDLC_LEN, + datalen); return -1; + } hdlc_hdr_t *hdlc_hdr = (hdlc_hdr_t *)pktdata; *l2len = sizeof(*hdlc_hdr); *protocol = ntohs(hdlc_hdr->protocol); break; case DLT_LINUX_SLL: - if (datalen < SLL_HDR_LEN) + if (datalen < SLL_HDR_LEN) { + warnx("%s (0x%x): Need at least %u bytes for DLT_LINUX_SLL but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + SLL_HDR_LEN, + datalen); return -1; + } *l2len = SLL_HDR_LEN; sll_hdr_t *sll_hdr = (sll_hdr_t *)pktdata; *protocol = ntohs(sll_hdr->sll_protocol); break; case DLT_LINUX_SLL2: - if (datalen < SLL2_HDR_LEN) + if (datalen < SLL2_HDR_LEN) { + warnx("%s (0x%x): Need at least %u bytes for DLT_LINUX_SLL2 but only %u available", + pcap_datalink_val_to_description(datalink), + datalink, + SLL2_HDR_LEN, + datalen); return -1; + } *l2len = SLL2_HDR_LEN; sll2_hdr_t *sll2_hdr = (sll2_hdr_t *)pktdata; diff --git a/src/send_packets.c b/src/send_packets.c index dab78115..fd057634 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -230,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: @@ -303,10 +305,13 @@ preload_pcap_file(tcpreplay_t *ctx, int idx) 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(options, pcap, &pkthdr, idx, prev_packet)) != NULL) { - if (options->flow_stats) - update_flow_stats(ctx, NULL, &pkthdr, pktdata, dlt); + if (options->flow_stats) { + ++packetnum; + update_flow_stats(ctx, NULL, &pkthdr, pktdata, dlt, packetnum); + } } /* mark this file as cached */ @@ -429,7 +434,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 @@ -717,7 +722,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 @@ -1310,7 +1315,7 @@ prepare_remaining_elements_of_batch(tcpreplay_t *ctx, 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); + update_flow_stats(ctx, ctx->options->cache_packets ? sp : NULL, &pkthdr, pktdata, datalink, *packetnum); } } if (pckt_count < sp->batch_size) { From 887077efbf6302493fcb6c42de0ec2d5318ef993 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 9 Jun 2024 12:30:45 -0700 Subject: [PATCH 69/69] Bug #835 - proper 802.3 (Ethernet I) handling We can only support IPv4 and IPv6. Suppress warning for other protocols. Also add extra checks for invalid types and lengths. --- docs/CHANGELOG | 1 + src/common/get.c | 27 ++++++++++++--------------- src/tcpedit/tcpedit.c | 2 +- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index d18e0870..ae814c94 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -11,6 +11,7 @@ - incorrect checksum for certain IPv4 packets - fixed by #846 (#844) - add check for IPv6 extension header length (#827 #842) - GitHub template for pull requests (#839) + - improved 802.3 (Ethernet I) handling and warning messages (#835) - handle IPv6 fragment extension header (#832 #837) - Linux tap interfaces fail intermittently (#828) - Infinite loop in tcprewrite at get.c (#827 #842) diff --git a/src/common/get.c b/src/common/get.c index 001b42ce..62fb1bb4 100644 --- a/src/common/get.c +++ b/src/common/get.c @@ -347,10 +347,11 @@ get_l2len_protocol(const u_char *pktdata, uint16_t ether_type; uint32_t l2_net_off = sizeof(*eth_hdr) + *l2offset; - if (datalen <= l2_net_off) { - warnx("%s (0x%x): Need at least 4 bytes for DLT_EN10MB but only %u available", + if (datalen <= l2_net_off + 4) { + warnx("%s (0x%x): Need at least %u bytes for DLT_EN10MB but only %u available", pcap_datalink_val_to_description(datalink), datalink, + l2_net_off + 4, datalen); return -1; } @@ -361,23 +362,19 @@ get_l2len_protocol(const u_char *pktdata, return -1; *l2len = l2_net_off; - if (ether_type > 1500) { + if (ether_type >= 1536) { /* Ethernet II frame - return in host order */ *protocol = ether_type; + } else if (ether_type > 1500) { + warnx("%s (0x%x): unsupported 802.3 length %u", + pcap_datalink_val_to_description(datalink), + datalink, + ether_type); + return -1; } else { /* 803.3 frame */ - if ((pktdata[l2_net_off] >> 4) == 4) { - *protocol = ETHERTYPE_IP; - } else if ((pktdata[l2_net_off] >> 4) == 6) { - *protocol = ETHERTYPE_IP6; - } else { - /* unsupported 802.3 protocol */ - warnx("%s (0x%x): unsupported 802.3 protocol %u", - pcap_datalink_val_to_description(datalink), - datalink, - ether_type); - return -1; - } + /* we don't modify 802.3 protocols */ + return -1; } break; } diff --git a/src/tcpedit/tcpedit.c b/src/tcpedit/tcpedit.c index 07ed5742..51b78bf1 100644 --- a/src/tcpedit/tcpedit.c +++ b/src/tcpedit/tcpedit.c @@ -94,7 +94,7 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr, u_char **pktdata ipflags = 0; /* not everything has a L3 header, so check for errors. returns proto in network byte order */ if ((l2proto = tcpedit_dlt_proto(tcpedit->dlt_ctx, src_dlt, packet, (int)(*pkthdr)->caplen)) < 0) { - dbgx(2, "Packet has no L3+ header: %s", tcpedit_geterr(tcpedit)); + dbgx(2, "Packet " COUNTER_SPEC " has no L3+ header: %s", tcpedit->runtime.packetnum, tcpedit_geterr(tcpedit)); return TCPEDIT_SOFT_ERROR; } else { dbgx(2, "Layer 3 protocol type is: 0x%04x", ntohs(l2proto));