src/replay.c
/* $Id$ */
/*
* Copyright (c) 2001-2010 Aaron Turner <aturner at synfin dot net>
* Copyright (c) 2013-2024 Fred Klassen <tcpreplay at appneta dot com> - 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 <http://www.gnu.org/licenses/>.
*/
#include "defines.h"
#include "config.h"
#include "common.h"
#include "send_packets.h"
#include "tcpreplay_api.h"
#include <string.h>
static int replay_file(tcpreplay_t *ctx, int idx);
static int replay_two_files(tcpreplay_t *ctx, int idx1, int idx2);
static int replay_cache(tcpreplay_t *ctx, int idx);
static int replay_two_caches(tcpreplay_t *ctx, int idx1, int idx2);
static int replay_fd(tcpreplay_t *ctx, int idx);
static int replay_two_fds(tcpreplay_t *ctx, int idx1, int idx2);
/**
* \brief Internal tcpreplay method to replay a given index
*
* This is used by tcpreplay_replay() to actually send the packets
*/
int
tcpr_replay_index(tcpreplay_t *ctx)
{
int rcode = 0;
int idx;
assert(ctx);
/* only process a single file */
if (!ctx->options->dualfile) {
/* process each pcap file in order */
for (idx = 0; idx < ctx->options->source_cnt && !ctx->abort; idx++) {
/* reset cache markers for each iteration */
switch (ctx->options->sources[idx].type) {
case source_filename:
rcode = replay_file(ctx, idx);
break;
case source_fd:
rcode = replay_fd(ctx, idx);
break;
case source_cache:
rcode = replay_cache(ctx, idx);
break;
default:
tcpreplay_seterr(ctx, "Invalid source type: %d", ctx->options->sources[idx].type);
rcode = -1;
}
}
}
/* dual file mode: two files, two interfaces */
else {
/* process each pcap file in order */
for (idx = 0; idx < ctx->options->source_cnt && !ctx->abort; idx += 2) {
if (ctx->options->sources[idx].type != ctx->options->sources[(idx + 1)].type) {
tcpreplay_seterr(ctx, "Both source indexes (%d, %d) must be of the same type", idx, (idx + 1));
return -1;
}
switch (ctx->options->sources[idx].type) {
case source_filename:
rcode = replay_two_files(ctx, idx, (idx + 1));
break;
case source_fd:
rcode = replay_two_fds(ctx, idx, (idx + 1));
break;
case source_cache:
rcode = replay_two_caches(ctx, idx, (idx + 1));
break;
default:
tcpreplay_seterr(ctx, "Invalid source type: %d", ctx->options->sources[idx].type);
rcode = -1;
}
}
}
if (rcode < 0) {
ctx->running = false;
return -1;
}
return rcode;
}
/**
* \brief replay a pcap file out interface(s)
*
* Internal to tcpreplay. Does the heavy lifting.
*/
static int
replay_file(tcpreplay_t *ctx, int idx)
{
char *path;
pcap_t *pcap = NULL;
char ebuf[PCAP_ERRBUF_SIZE];
assert(ctx);
assert(ctx->options->sources[idx].type == source_filename);
path = ctx->options->sources[idx].filename;
/* read from pcap file if we haven't cached things yet */
if (!ctx->options->preload_pcap) {
if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
ctx->options->file_cache[idx].dlt = pcap_datalink(pcap);
#ifdef HAVE_PCAP_SNAPSHOT
if (pcap_snapshot(pcap) < 65535)
warnx("%s was captured using a snaplen of %d bytes. This may mean you have truncated packets.",
path,
pcap_snapshot(pcap));
#endif
} else {
if (!ctx->options->file_cache[idx].cached) {
if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
ctx->options->file_cache[idx].dlt = pcap_datalink(pcap);
}
}
#ifdef ENABLE_VERBOSE
if (ctx->options->verbose) {
/* in cache mode, we may not have opened the file */
if (pcap == NULL)
if ((pcap = tcpr_pcap_open(path, ebuf)) == NULL) {
tcpreplay_seterr(ctx, "Error opening pcap file: %s", ebuf);
return -1;
}
ctx->options->file_cache[idx].dlt = pcap_datalink(pcap);
/* init tcpdump */
tcpdump_open(ctx->options->tcpdump, pcap);
}
#endif
if (pcap != NULL) {
if (ctx->intf1dlt == -1)
ctx->intf1dlt = sendpacket_get_dlt(ctx->intf1);
#if 0
if ((ctx->intf1dlt >= 0) && (ctx->intf1dlt != pcap_datalink(pcap)))
warnx("%s DLT (%s) does not match that of the outbound interface: %s (%s)",
path, pcap_datalink_val_to_name(pcap_datalink(pcap)),
ctx->options->intf1->device, pcap_datalink_val_to_name(ctx->intf1dlt));
#endif
if (ctx->intf1dlt != ctx->options->file_cache[idx].dlt)
tcpreplay_setwarn(ctx,
"%s DLT (%s) does not match that of the outbound interface: %s (%s)",
path,
pcap_datalink_val_to_name(pcap_datalink(pcap)),
ctx->intf1->device,
pcap_datalink_val_to_name(ctx->intf1dlt));
}
ctx->stats.active_pcap = ctx->options->sources[idx].filename;
send_packets(ctx, pcap, idx);
if (pcap != NULL)
pcap_close(pcap);
#ifdef ENABLE_VERBOSE
tcpdump_close(ctx->options->tcpdump);
#endif
return 0;
}
/**
* \brief replay two pcap files out two interfaces
*
* Internal to tcpreplay, does the heavy lifting for --dualfile
*/
static int
replay_two_files(tcpreplay_t *ctx, int idx1, int idx2)
{
char *path1, *path2;
pcap_t *pcap1 = NULL, *pcap2 = NULL;
char ebuf[PCAP_ERRBUF_SIZE];
int rcode = 0;
assert(ctx);
assert(ctx->options->sources[idx1].type == source_filename);
assert(ctx->options->sources[idx2].type == source_filename);
path1 = ctx->options->sources[idx1].filename;
path2 = ctx->options->sources[idx2].filename;
/* can't use stdin in dualfile mode */
if ((strncmp(path1, "-", strlen(path1)) == 0) || (strncmp(path2, "-", strlen(path2)) == 0)) {
tcpreplay_seterr(ctx, "%s", "Invalid use of STDIN '-' in dual file mode");
return -1;
}
/* read from first pcap file if we haven't cached things yet */
if (!ctx->options->preload_pcap) {
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 = 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 = 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 = 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);
}
}
if (pcap1 != NULL) {
#ifdef HAVE_PCAP_SNAPSHOT
if (pcap_snapshot(pcap1) < 65535) {
tcpreplay_setwarn(ctx,
"%s was captured using a snaplen of %d bytes. This may mean you have truncated packets.",
path1,
pcap_snapshot(pcap1));
rcode = -2;
}
if (pcap_snapshot(pcap2) < 65535) {
tcpreplay_setwarn(ctx,
"%s was captured using a snaplen of %d bytes. This may mean you have truncated packets.",
path2,
pcap_snapshot(pcap2));
rcode = -2;
}
#endif
if (ctx->intf1dlt == -1)
ctx->intf1dlt = sendpacket_get_dlt(ctx->intf1);
if ((ctx->intf1dlt >= 0) && (ctx->intf1dlt != pcap_datalink(pcap1))) {
tcpreplay_setwarn(ctx,
"%s DLT (%s) does not match that of the outbound interface: %s (%s)",
path1,
pcap_datalink_val_to_name(pcap_datalink(pcap1)),
ctx->intf1->device,
pcap_datalink_val_to_name(ctx->intf1dlt));
rcode = -2;
}
if (ctx->intf2dlt == -1)
ctx->intf2dlt = sendpacket_get_dlt(ctx->intf2);
if ((ctx->intf2dlt >= 0) && (ctx->intf2dlt != pcap_datalink(pcap2))) {
tcpreplay_setwarn(ctx,
"%s DLT (%s) does not match that of the outbound interface: %s (%s)",
path2,
pcap_datalink_val_to_name(pcap_datalink(pcap2)),
ctx->intf2->device,
pcap_datalink_val_to_name(ctx->intf2dlt));
rcode = -2;
}
if (ctx->intf1dlt != ctx->intf2dlt) {
tcpreplay_seterr(ctx, "DLT mismatch for %s (%d) and %s (%d)", path1, ctx->intf1dlt, path2, ctx->intf2dlt);
return -1;
}
}
#ifdef ENABLE_VERBOSE
if (ctx->options->verbose) {
/* in cache mode, we may not have opened the file */
if (pcap1 == 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);
}
/* init tcpdump */
tcpdump_open(ctx->options->tcpdump, pcap1);
}
#endif
send_dual_packets(ctx, pcap1, idx1, pcap2, idx2);
if (pcap1 != NULL)
pcap_close(pcap1);
if (pcap2 != NULL)
pcap_close(pcap2);
#ifdef ENABLE_VERBOSE
tcpdump_close(ctx->options->tcpdump);
#endif
return rcode;
}
/**
* \brief Replay index using existing memory cache
*
* FIXME
*/
static int
replay_cache(tcpreplay_t *ctx, int idx)
{
assert(ctx);
assert(ctx->options->sources[idx].type == source_cache);
return 0;
}
/**
* \brief Replay two indexes using existing memory cache
*
* FIXME
*/
static int
replay_two_caches(tcpreplay_t *ctx, int idx1, int idx2)
{
assert(ctx);
assert(ctx->options->sources[idx1].type == source_cache);
assert(ctx->options->sources[idx2].type == source_cache);
return 0;
}
/**
* \brief Replay index which is a file descriptor
*
* FIXME
*/
static int
replay_fd(tcpreplay_t *ctx, int idx)
{
assert(ctx);
assert(ctx->options->sources[idx].type == source_fd);
return 0;
}
/**
* \brief Replay two indexes which are a file descriptor
*
* FIXME
*/
static int
replay_two_fds(tcpreplay_t *ctx, int idx1, int idx2)
{
assert(ctx);
assert(ctx->options->sources[idx1].type == source_fd);
assert(ctx->options->sources[idx2].type == source_fd);
return 0;
}