From 6f1a97f18b54e51b8a816e904f4d121364255ef9 Mon Sep 17 00:00:00 2001 From: jason lu Date: Tue, 14 May 2024 04:32:55 -0700 Subject: [PATCH] add -w output.pcap command line option to direct the output to a pcap file instead of normal NIC --- src/common/sendpacket.c | 143 +++++++++++++++++++++++++++------------- src/common/sendpacket.h | 9 ++- src/tcpreplay_api.c | 134 +++++++++++++++++++++---------------- src/tcpreplay_opts.def | 16 ++++- 4 files changed, 195 insertions(+), 107 deletions(-) diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 9dc378f6..747a7ca3 100644 --- a/src/common/sendpacket.c +++ b/src/common/sendpacket.c @@ -27,6 +27,7 @@ * injection method, then by all means add it here (and send me a patch). * * Anyways, long story short, for now the order of preference is: + * 0. pcap_dump * 1. TX_RING * 2. PF_PACKET * 3. BPF @@ -211,6 +212,7 @@ static struct tcpr_ether_addr *sendpacket_get_hwaddr_pcap(sendpacket_t *) _U_; #undef INJECT_METHOD #define INJECT_METHOD "pcap_sendpacket()" #endif +static sendpacket_t *sendpacket_open_pcap_dump(const char *, char *) _U_; static void sendpacket_seterr(sendpacket_t *sp, const char *fmt, ...); static sendpacket_t *sendpacket_open_khial(const char *, char *) _U_; @@ -434,6 +436,11 @@ sendpacket(sendpacket_t *sp, const u_char *data, size_t len, struct pcap_pkthdr break; + case SP_TYPE_LIBPCAP_DUMP: + pcap_dump((u_char *)sp->handle.dump.dump, pkthdr, data); + retcode = len; + break; + case SP_TYPE_NETMAP: #ifdef HAVE_NETMAP retcode = sendpacket_send_netmap(sp, data, len); @@ -495,55 +502,58 @@ 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)) { - sp = sendpacket_open_khial(device, errbuf); - - } else { - switch (sdata.st_mode & S_IFMT) { - case S_IFBLK: - errx(-1, "\"%s\" is a block device and is not a valid Tcpreplay device", device); - case S_IFDIR: - errx(-1, "\"%s\" is a directory and is not a valid Tcpreplay device", device); - case S_IFIFO: - errx(-1, "\"%s\" is a FIFO and is not a valid Tcpreplay device", device); - case S_IFLNK: - errx(-1, "\"%s\" is a symbolic link and is not a valid Tcpreplay device", device); - case S_IFREG: - errx(-1, "\"%s\" is a file and is not a valid Tcpreplay device", device); - default: - errx(-1, "\"%s\" is not a valid Tcpreplay device", device); + if (sendpacket_type == SP_TYPE_LIBPCAP_DUMP){ + sp = sendpacket_open_pcap_dump(device, errbuf); + }else{ + #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)) { + sp = sendpacket_open_khial(device, errbuf); + + } else { + switch (sdata.st_mode & S_IFMT) { + case S_IFBLK: + errx(-1, "\"%s\" is a block device and is not a valid Tcpreplay device", device); + case S_IFDIR: + errx(-1, "\"%s\" is a directory and is not a valid Tcpreplay device", device); + case S_IFIFO: + errx(-1, "\"%s\" is a FIFO and is not a valid Tcpreplay device", device); + case S_IFLNK: + errx(-1, "\"%s\" is a symbolic link and is not a valid Tcpreplay device", device); + case S_IFREG: + errx(-1, "\"%s\" is a file and is not a valid Tcpreplay device", device); + default: + errx(-1, "\"%s\" is not a valid Tcpreplay device", device); + } } + #ifdef HAVE_TUNTAP + } else if (strncmp(device, "tap", 3) == 0 && !device_exists) { + sp = sendpacket_open_tuntap(device, errbuf); + #endif + } else { + #ifdef HAVE_NETMAP + if (sendpacket_type == SP_TYPE_NETMAP) + sp = (sendpacket_t *)sendpacket_open_netmap(device, errbuf, arg); + else + #endif + #if defined HAVE_PF_PACKET + sp = sendpacket_open_pf(device, errbuf); + #elif defined HAVE_BPF + sp = sendpacket_open_bpf(device, errbuf); + #elif defined HAVE_LIBDNET + sp = sendpacket_open_libdnet(device, errbuf); + #elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) + sp = sendpacket_open_pcap(device, errbuf); + #else + #error "No defined packet injection method for sendpacket_open()" + #endif } -#ifdef HAVE_TUNTAP - } else if (strncmp(device, "tap", 3) == 0 && !device_exists) { - sp = sendpacket_open_tuntap(device, errbuf); -#endif - } else { -#ifdef HAVE_NETMAP - if (sendpacket_type == SP_TYPE_NETMAP) - sp = (sendpacket_t *)sendpacket_open_netmap(device, errbuf, arg); - else -#endif -#if defined HAVE_PF_PACKET - sp = sendpacket_open_pf(device, errbuf); -#elif defined HAVE_BPF - sp = sendpacket_open_bpf(device, errbuf); -#elif defined HAVE_LIBDNET - sp = sendpacket_open_libdnet(device, errbuf); -#elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) - sp = sendpacket_open_pcap(device, errbuf); -#else -#error "No defined packet injection method for sendpacket_open()" -#endif } - if (sp) { sp->open = 1; sp->cache_dir = direction; @@ -631,6 +641,11 @@ sendpacket_close(sendpacket_t *sp) #endif break; + case SP_TYPE_LIBPCAP_DUMP: + pcap_dump_close(sp->handle.dump.dump); + pcap_close(sp->handle.dump.pcap); + break; + case SP_TYPE_LIBDNET: #ifdef HAVE_LIBDNET eth_close(sp->handle.ldnet); @@ -671,6 +686,9 @@ sendpacket_get_hwaddr(sendpacket_t *sp) if (sp->handle_type == SP_TYPE_KHIAL) { addr = sendpacket_get_hwaddr_khial(sp); + } else if( sp->handle_type == SP_TYPE_LIBPCAP_DUMP) { + sendpacket_seterr(sp, "Error: sendpacket_get_hwaddr() not yet supported for pcap dump"); + return NULL; } else { #if defined HAVE_PF_PACKET addr = sendpacket_get_hwaddr_pf(sp); @@ -767,6 +785,37 @@ sendpacket_get_hwaddr_pcap(sendpacket_t *sp) } #endif /* HAVE_PCAP_INJECT || HAVE_PCAP_SENDPACKET */ +/** + * Inner sendpacket_open() method for using libpcap + */ +static sendpacket_t * +sendpacket_open_pcap_dump(const char *device, char *errbuf) +{ + pcap_t *pcap; + pcap_dumper_t* dump; + sendpacket_t *sp; + + assert(device); + assert(errbuf); + + dbg(1, "sendpacket: using Libpcap"); + + pcap = pcap_open_dead(DLT_EN10MB, 65535); + if ((dump = pcap_dump_open(pcap, device)) == NULL){ + char* err_msg = pcap_geterr(pcap); + strlcpy(errbuf, err_msg, PCAP_ERRBUF_SIZE); + pcap_close(pcap); + return NULL; + } + + sp = (sendpacket_t *)safe_malloc(sizeof(sendpacket_t)); + strlcpy(sp->device, device, sizeof(sp->device)); + sp->handle.dump.pcap = pcap; + sp->handle.dump.dump = dump; + sp->handle_type = SP_TYPE_LIBPCAP_DUMP; + return sp; +} + #if defined HAVE_LIBDNET && !defined HAVE_PF_PACKET && !defined HAVE_BPF /** * Inner sendpacket_open() method for using libdnet @@ -1236,7 +1285,7 @@ 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) { + if (sp->handle_type == SP_TYPE_KHIAL || sp->handle_type == SP_TYPE_NETMAP || sp->handle_type == SP_TYPE_TUNTAP || sp->handle_type == SP_TYPE_LIBPCAP_DUMP) { /* always EN10MB */ } else { #if defined HAVE_BPF diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index e1dd6a42..68c00d28 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -67,7 +67,8 @@ typedef enum sendpacket_type_e { SP_TYPE_TX_RING, SP_TYPE_KHIAL, SP_TYPE_NETMAP, - SP_TYPE_TUNTAP + SP_TYPE_TUNTAP, + SP_TYPE_LIBPCAP_DUMP } sendpacket_type_t; /* these are the file_operations ioctls */ @@ -80,8 +81,14 @@ typedef enum khial_direction_e { KHIAL_DIRECTION_TX, } khial_direction_t; +typedef struct pcap_dump_s{ + pcap_t *pcap; + pcap_dumper_t* dump; +} pcap_dump_t; + union sendpacket_handle { pcap_t *pcap; + pcap_dump_t dump; int fd; #ifdef HAVE_LIBDNET eth_t *ldnet; diff --git a/src/tcpreplay_api.c b/src/tcpreplay_api.c index d429b91b..a099e034 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -328,74 +328,92 @@ tcpreplay_post_args(tcpreplay_t *ctx, int argc) tcpreplay_setwarn(ctx, "%s", "--pktlen may cause problems. Use with caution."); } - if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF1))) == NULL) { - if (!strncmp(OPT_ARG(INTF1), "netmap:", 7) || !strncmp(OPT_ARG(INTF1), "vale", 4)) - tcpreplay_seterr(ctx, "Unable to connect to netmap interface %s. Ensure netmap module is installed (see INSTALL).", - OPT_ARG(INTF1)); - else - tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF1)); - - ret = -1; - goto out; - } + switch (WHICH_IDX_INTF1){ + case INDEX_OPT_WRITE: + options->intf1_name = safe_strdup(OPT_ARG(INTF1)); + ctx->sp_type = SP_TYPE_LIBPCAP_DUMP; + /* open interfaces for writing */ + if ((ctx->intf1 = sendpacket_open(options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) { + tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf1_name, ebuf); + ret = -1; + goto out; + } + break; + + case INDEX_OPT_INTF1: + if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF1))) == NULL) { + if (!strncmp(OPT_ARG(INTF1), "netmap:", 7) || !strncmp(OPT_ARG(INTF1), "vale", 4)) + tcpreplay_seterr(ctx, "Unable to connect to netmap interface %s. Ensure netmap module is installed (see INSTALL).", + OPT_ARG(INTF1)); + else + tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF1)); + + ret = -1; + goto out; + } - if (!strncmp(intname, "netmap:", 7) || !strncmp(intname, "vale:", 5)) { -#ifdef HAVE_NETMAP - options->netmap = 1; - ctx->sp_type = SP_TYPE_NETMAP; -#else - tcpreplay_seterr(ctx, "%s", "tcpreplay_api not compiled with netmap support"); - ret = -1; - goto out; -#endif - } + if (!strncmp(intname, "netmap:", 7) || !strncmp(intname, "vale:", 5)) { + #ifdef HAVE_NETMAP + options->netmap = 1; + ctx->sp_type = SP_TYPE_NETMAP; + #else + tcpreplay_seterr(ctx, "%s", "tcpreplay_api not compiled with netmap support"); + ret = -1; + goto out; + #endif + } - options->intf1_name = safe_strdup(intname); + options->intf1_name = safe_strdup(intname); - /* open interfaces for writing */ - if ((ctx->intf1 = sendpacket_open(options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) { - tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf1_name, ebuf); - ret = -1; - goto out; - } + /* open interfaces for writing */ + if ((ctx->intf1 = sendpacket_open(options->intf1_name, ebuf, TCPR_DIR_C2S, ctx->sp_type, ctx)) == NULL) { + tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf1_name, ebuf); + ret = -1; + goto out; + } -#if defined HAVE_NETMAP - ctx->intf1->netmap_delay = ctx->options->netmap_delay; -#endif + #if defined HAVE_NETMAP + ctx->intf1->netmap_delay = ctx->options->netmap_delay; + #endif - ctx->intf1dlt = sendpacket_get_dlt(ctx->intf1); + ctx->intf1dlt = sendpacket_get_dlt(ctx->intf1); - if (HAVE_OPT(INTF2)) { - if (!HAVE_OPT(CACHEFILE) && !HAVE_OPT(DUALFILE)) { - tcpreplay_seterr(ctx, "--intf2=%s requires either --cachefile or --dualfile", OPT_ARG(INTF2)); - ret = -1; - goto out; - } - if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF2))) == NULL) { - tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF2)); - ret = -1; - goto out; - } + if (HAVE_OPT(INTF2)) { + if (!HAVE_OPT(CACHEFILE) && !HAVE_OPT(DUALFILE)) { + tcpreplay_seterr(ctx, "--intf2=%s requires either --cachefile or --dualfile", OPT_ARG(INTF2)); + ret = -1; + goto out; + } + if ((intname = get_interface(ctx->intlist, OPT_ARG(INTF2))) == NULL) { + tcpreplay_seterr(ctx, "Invalid interface name/alias: %s", OPT_ARG(INTF2)); + ret = -1; + goto out; + } - options->intf2_name = safe_strdup(intname); + options->intf2_name = safe_strdup(intname); - /* open interface for writing */ - if ((ctx->intf2 = sendpacket_open(options->intf2_name, ebuf, TCPR_DIR_S2C, ctx->sp_type, ctx)) == NULL) { - tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf2_name, ebuf); - } + /* open interface for writing */ + if ((ctx->intf2 = sendpacket_open(options->intf2_name, ebuf, TCPR_DIR_S2C, ctx->sp_type, ctx)) == NULL) { + tcpreplay_seterr(ctx, "Can't open %s: %s", options->intf2_name, ebuf); + } -#if defined HAVE_NETMAP - ctx->intf2->netmap_delay = ctx->options->netmap_delay; -#endif + #if defined HAVE_NETMAP + ctx->intf2->netmap_delay = ctx->options->netmap_delay; + #endif + + ctx->intf2dlt = sendpacket_get_dlt(ctx->intf2); + if (ctx->intf2dlt != ctx->intf1dlt) { + tcpreplay_seterr(ctx, "DLT type mismatch for %s (%s) and %s (%s)", + options->intf1_name, pcap_datalink_val_to_name(ctx->intf1dlt), + options->intf2_name, pcap_datalink_val_to_name(ctx->intf2dlt)); + ret = -1; + goto out; + } + } + break; - ctx->intf2dlt = sendpacket_get_dlt(ctx->intf2); - if (ctx->intf2dlt != ctx->intf1dlt) { - tcpreplay_seterr(ctx, "DLT type mismatch for %s (%s) and %s (%s)", - options->intf1_name, pcap_datalink_val_to_name(ctx->intf1dlt), - options->intf2_name, pcap_datalink_val_to_name(ctx->intf2dlt)); - ret = -1; - goto out; - } + default: + assert(false); //shouldn't happen! } if (HAVE_OPT(CACHEFILE)) { diff --git a/src/tcpreplay_opts.def b/src/tcpreplay_opts.def index a0036f76..d8748d67 100644 --- a/src/tcpreplay_opts.def +++ b/src/tcpreplay_opts.def @@ -255,7 +255,7 @@ EOText; }; /* - * Outputs: -i, -I + * Outputs: -i, -I, -w */ flag = { @@ -263,6 +263,7 @@ flag = { value = i; arg-type = string; max = 1; + equivalence = intf1; must-set; descrip = "Client to server/RX/primary traffic output interface"; doc = <<- EOText @@ -286,6 +287,19 @@ option with --cachefile. EOText; }; +flag = { + name = write; + value = w; + arg-type = string; + max = 1; + equivalence = intf1; + flags-cant = intf2; + descrip = "Pcap file to receive traffic outputs"; + doc = <<- EOText +Optional pcap file name used to receive traffic. +EOText; +}; + flag = { ifdef = ENABLE_PCAP_FINDALLDEVS;