From 57bed13e60561b8b990c9414628d67983ff73bb6 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 23 Jun 2024 15:09:30 -0700 Subject: [PATCH 1/3] Feature #884 fix tcpprep -x -X include to EOF A rule such as `75-` needs to include packet 75 to the last packet in the file. --- docs/CHANGELOG | 3 ++- src/common/list.c | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/CHANGELOG b/docs/CHANGELOG index ae814c94..269df814 100644 --- a/docs/CHANGELOG +++ b/docs/CHANGELOG @@ -1,4 +1,5 @@ -06/08/2024 Version 4.5.0-beta2 +06/23/2024 Version 4.5.0-beta3 + - tcpreplay --include / --exclude to control which packets are replayed (#884) - 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) diff --git a/src/common/list.c b/src/common/list.c index 5075fd5a..e258e34c 100644 --- a/src/common/list.c +++ b/src/common/list.c @@ -44,6 +44,21 @@ new_list() return (newlist); } +static void +add_to_list(tcpr_list_t *list_ptr, const char *first, const char *second) +{ + list_ptr->min = strtoull(first, NULL, 0); + if (second != NULL) { + if (second[0] == '\0') { + list_ptr->max = 0; + } else { + list_ptr->max = strtoull(second, NULL, 0); + } + } else { + list_ptr->max = list_ptr->min; + } +} + /** * Processes a string (ourstr) containing the list in human readable * format and places the data in **list and finally returns 1 for @@ -57,7 +72,7 @@ parse_list(tcpr_list_t **listdata, char *ourstr) char *first, *second; int rcode; regex_t preg; - char regex[] = "^[0-9]+(-[0-9]+)?$"; + char regex[] = "^[0-9]+(-([0-9]+|\\s*))?$"; char *token = NULL; u_int i; @@ -91,12 +106,7 @@ parse_list(tcpr_list_t **listdata, char *ourstr) } } - list_ptr->min = strtoull(first, NULL, 0); - if (second != NULL) { - list_ptr->max = strtoull(second, NULL, 0); - } else { - list_ptr->max = list_ptr->min; - } + add_to_list(list_ptr, first, second); while (1) { this = strtok_r(NULL, ",", &token); @@ -123,12 +133,7 @@ parse_list(tcpr_list_t **listdata, char *ourstr) } } - listcur->min = strtoull(first, NULL, 0); - if (second != NULL) { - listcur->max = strtoull(second, NULL, 0); - } else { - listcur->max = listcur->min; - } + add_to_list(listcur, first, second); } regfree(&preg); @@ -144,10 +149,8 @@ tcpr_dir_t check_list(tcpr_list_t *list, COUNTER value) { tcpr_list_t *current; - current = list; - - do { - if ((current->min != 0) && (current->max != 0)) { + for (current = list; current; current = current->next) { + if (current->min != 0 && current->max != 0) { if ((value >= current->min) && (value <= current->max)) return 1; } else if (current->min == 0) { @@ -157,12 +160,7 @@ check_list(tcpr_list_t *list, COUNTER value) if (value >= current->min) return 1; } - - if (current->next != NULL) - current = current->next; - else - current = NULL; - } while (current != NULL); + } return 0; } @@ -173,6 +171,9 @@ check_list(tcpr_list_t *list, COUNTER value) void free_list(tcpr_list_t *list) { + if (list == NULL) + return; + /* recursively go down the list */ if (list->next != NULL) free_list(list->next); From 43b859a00f2ceebc61c0700fde89c9857186a3dd Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 23 Jun 2024 20:17:36 -0700 Subject: [PATCH 2/3] Feature #884 add --include / --exclude options Add ability to select the packets you want sent --- src/send_packets.c | 21 ++++++++++ src/tcpreplay_api.c | 3 ++ src/tcpreplay_api.h | 5 +++ src/tcpreplay_opts.def | 92 +++++++++++++++++++++++++++++++++++------- 4 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/send_packets.c b/src/send_packets.c index fd057634..e7969fe4 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -397,6 +397,17 @@ send_packets(tcpreplay_t *ctx, pcap_t *pcap, int idx) now_is_now = false; packetnum++; #if defined TCPREPLAY || defined TCPREPLAY_EDIT + /* look for include or exclude LIST match */ + if (options->list != NULL) { + bool rule_set = check_list(options->list, packetnum); + if ((rule_set && options->is_exclude) || (!rule_set && !options->is_exclude)) { + dbgx(2, "packet " COUNTER_SPEC " not sent due to %s rule", + packetnum, + options->is_exclude ? "exclude" : "include"); + continue; + } + } + /* do we use the snaplen (caplen) or the "actual" packet len? */ pktlen = options->use_pkthdr_len ? (COUNTER)pkthdr.len : (COUNTER)pkthdr.caplen; #elif TCPBRIDGE @@ -667,6 +678,16 @@ send_dual_packets(tcpreplay_t *ctx, pcap_t *pcap1, int cache_file_idx1, pcap_t * while (!ctx->abort && !(pktdata1 == NULL && pktdata2 == NULL)) { now_is_now = false; packetnum++; + /* look for include or exclude LIST match */ + if (options->list != NULL) { + bool rule_set = check_list(options->list, packetnum); + if ((rule_set && options->is_exclude) || (!rule_set && !options->is_exclude)) { + dbgx(2, "packet " COUNTER_SPEC " not sent due to %s rule", + packetnum, + options->is_exclude ? "exclude" : "include"); + continue; + } + } /* figure out which pcap file we need to process next * when get_next_packet() returns null for pktdata, the pkthdr diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index c4a412a4..7e379aef 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -487,6 +487,9 @@ tcpreplay_close(tcpreplay_t *ctx) intlist = intlistnext; } } + + /* free --include / --exclude list */ + free_list(options->list); } /** diff --git a/src/tcpreplay_api.h b/src/tcpreplay_api.h index c7154ed0..70540588 100644 --- a/src/tcpreplay_api.h +++ b/src/tcpreplay_api.h @@ -23,6 +23,7 @@ #include "defines.h" #include "config.h" #include +#include #include #include #include @@ -134,6 +135,10 @@ typedef struct tcpreplay_opt_s { int source_cnt; tcpreplay_source_t sources[MAX_FILES]; + /* --include / --exclude flag and rules list */ + bool is_exclude; + tcpr_list_t *list; + #ifdef ENABLE_VERBOSE /* tcpdump verbose printing */ bool verbose; diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index c3ddfa9d..bf424258 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -57,11 +57,13 @@ config-header = "config.h"; include = "#include \"defines.h\"\n" "#include \"tcpreplay.h\"\n" + "#include \"tcpreplay_api.h\"\n" "#include \"common.h\"\n" "#include \"config.h\"\n" "#include \n" "#include \n" - "#include \n"; + "#include \n" + "extern tcpreplay_t *ctx;"; homerc = "$$/"; @@ -286,6 +288,66 @@ option with --cachefile. EOText; }; +flag = { + name = include; + arg-type = string; + max = 1; + descrip = "Send only selected packet numbers"; + flags-cant = exclude; + flag-code = <<- EOInclude + + char *include; + include = safe_strdup(OPT_ARG(INCLUDE)); + + ctx->options->is_exclude = false; + if (!parse_list(&ctx->options->list, include)) + errx(-1, "Unable to parse include/exclude rule: %s", OPT_ARG(INCLUDE)); + + free(include); + +EOInclude; + doc = <<- EOText +Override default of processing all packets stored in the capture file and only +send packets that are part of a supplied list of packet numbers. + +@example +-x P:1-5,9,15,72- +@end example +would skip packets 1 through 5, the 9th and 15th packet, and packets 72 until the +end of the file +EOText; +}; + +flag = { + name = exclude; + arg-type = string; + max = 1; + descrip = "Send all but selected packet numbers"; + flags-cant = include; + flag-code = <<- EOExclude + + char *exclude; + exclude = safe_strdup(OPT_ARG(EXCLUDE)); + + ctx->options->is_exclude = true; + if (!parse_list(&ctx->options->list, exclude)) + errx(-1, "Unable to parse include/exclude rule: %s", OPT_ARG(EXCLUDE)); + + free(exclude); + +EOExclude; + doc = <<- EOText +Override default of processing all packets stored in the capture file and only +send packets that are NOT part of a supplied list of packet numbers. + +@example +-x P:1-5,9,15,72- +@end example +would skip packets 1 through 5, the 9th and 15th packet, and packets 72 until the +end of the file +EOText; +}; + flag = { ifdef = ENABLE_PCAP_FINDALLDEVS; @@ -543,19 +605,6 @@ 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; @@ -626,6 +675,21 @@ sending packets may cause equally long delays between printing statistics. 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 = { ifdef = HAVE_LIBXDP; name = xdp-batch-size; From 23b035b3436c84b53cd1bbe924013da6e980e035 Mon Sep 17 00:00:00 2001 From: Fred Klassen Date: Sun, 23 Jun 2024 21:21:26 -0700 Subject: [PATCH 3/3] Feature #884 tests for --include / --exclude options --- test/Makefile.am | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/Makefile.am b/test/Makefile.am index 19e85798..58852f70 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -40,7 +40,7 @@ EXTRA_DIST = test.pcap test.auto_bridge test.auto_client test.auto_router \ 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 \ - 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 \ @@ -209,7 +209,7 @@ tcprewrite: rewrite_portmap rewrite_range_portmap rewrite_endpoint \ 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 + replay_stats replay_dualfile replay_maxsleep replay_include replay_exclude prep_config: $(PRINTF) "%s" "[tcpprep] Config mode test: " @@ -819,6 +819,18 @@ replay_maxsleep: $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --maxsleep=20 $(TEST_PCAP) $(TEST_PCAP) >> test.log 2>&1 if [ $? ] ; then $(PRINTF) "\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t%s\n" "OK"; fi +replay_include: + $(PRINTF) "%s" "[tcpreplay] Include rule test: " + $(PRINTF) "%s\n" "*** [tcpreplay] Include rule test: " >> test.log + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --include=1-5,9,15,72- $(TEST_PCAP) $(TEST_PCAP) >> test.log 2>&1 + if [ $? ] ; then $(PRINTF) "\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi + +replay_exclude: + $(PRINTF) "%s" "[tcpreplay] Exclude rule test: " + $(PRINTF) "%s\n" "*** [tcpreplay] Exclude rule test: " >> test.log + $(TCPREPLAY) $(ENABLE_DEBUG) -i $(nic1) --include=7-11,3,20,22- $(TEST_PCAP) $(TEST_PCAP) >> test.log 2>&1 + if [ $? ] ; then $(PRINTF) "\t\t%s\n" "FAILED"; else $(PRINTF) "\t\t\t%s\n" "OK"; fi + clean: rm -f *1 test.log core* *~ primary.data secondary.data