Skip to content

Commit

Permalink
Merge pull request #853 from jasonlue/topic/jasonlue/direct-traffic-t…
Browse files Browse the repository at this point in the history
…o-pcap

add -w output.pcap command line option to direct the output to a pcap
  • Loading branch information
fklassen authored Jun 3, 2024
2 parents b73ac4f + 90f3277 commit 457a0f6
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 120 deletions.
161 changes: 100 additions & 61 deletions src/common/sendpacket.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <sys/mman.h>
static sendpacket_t *sendpacket_open_xsk(const char *, char *) _U_;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions src/common/sendpacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down
135 changes: 77 additions & 58 deletions src/tcpreplay_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Loading

0 comments on commit 457a0f6

Please sign in to comment.