diff --git a/src/common/sendpacket.c b/src/common/sendpacket.c index 6b060aad..87185e1b 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 @@ -223,6 +224,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_; #ifdef HAVE_LIBXDP #include static sendpacket_t *sendpacket_open_xsk(const char *, char *) _U_; @@ -457,6 +459,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); @@ -536,62 +543,60 @@ 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 = + + (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 -#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 + #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 + #elif defined HAVE_LIBDNET sp = sendpacket_open_libdnet(device, errbuf); -#elif (defined HAVE_PCAP_INJECT || defined HAVE_PCAP_SENDPACKET) + #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 + #else + #error "No defined packet injection method for sendpacket_open()" + #endif + } } - if (sp) { sp->open = 1; sp->cache_dir = direction; @@ -679,6 +684,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); @@ -726,6 +736,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); @@ -824,6 +837,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 @@ -1260,16 +1304,11 @@ sendpacket_get_dlt(sendpacket_t *sp) { int dlt = DLT_EN10MB; - 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 (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 + int rcode; #if defined HAVE_BPF if ((ioctl(sp->handle.fd, BIOCGDLT, &dlt)) < 0) { diff --git a/src/common/sendpacket.h b/src/common/sendpacket.h index 9e54efcc..0e12f49c 100644 --- a/src/common/sendpacket.h +++ b/src/common/sendpacket.h @@ -68,6 +68,7 @@ typedef enum sendpacket_type_e { SP_TYPE_KHIAL, SP_TYPE_NETMAP, SP_TYPE_TUNTAP, + SP_TYPE_LIBPCAP_DUMP SP_TYPE_LIBXDP } sendpacket_type_t; @@ -81,8 +82,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 453b92d7..19518cf3 100644 --- a/src/tcpreplay_api.c +++ b/src/tcpreplay_api.c @@ -338,76 +338,95 @@ 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)); + 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; + } - 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; - } #ifdef HAVE_LIBXDP - ctx->intf1->batch_size = OPT_VALUE_XDP_BATCH_SIZE; -#endif -#if defined HAVE_NETMAP - ctx->intf1->netmap_delay = ctx->options->netmap_delay; + ctx->intf1->batch_size = OPT_VALUE_XDP_BATCH_SIZE; #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 e3536fde..67b9edea 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;