1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291 |
- /* $Id: tcpreplay.c 884 2004-11-08 17:04:47Z aturner $ */
- /*
- * Copyright (c) 2001-2004 Aaron Turner, Matt Bing.
- * All rights reserved.
- *
- * Copyright (c) 1999 Anzen Computing. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Anzen Computing, Inc.
- * 4. Neither the names of the copyright owners nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "config.h"
- #include <ctype.h>
- #include <fcntl.h>
- #include <libnet.h>
- #ifdef HAVE_PCAPNAV
- #include <pcapnav.h>
- #else
- #include "fakepcapnav.h"
- #endif
- #include <pcap.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include "tcpreplay.h"
- #include "tcpdump.h"
- #include "cache.h"
- #include "cidr.h"
- #include "portmap.h"
- #include "list.h"
- #include "err.h"
- #include "do_packets.h"
- #include "xX.h"
- #include "signal_handler.h"
- #include "replay_live.h"
- #include "utils.h"
- #include "edit_packet.h"
- #include "fakepcap.h"
- struct options options;
- char *cachedata = NULL;
- CIDR *cidrdata = NULL;
- CIDRMAP *cidrmap_data1 = NULL, *cidrmap_data2 = NULL;
- PORTMAP *portmap_data = NULL;
- struct timeval begin, end;
- u_int64_t bytes_sent, failed, pkts_sent;
- char *cache_file = NULL, *intf = NULL, *intf2 = NULL;
- int cache_bit, cache_byte;
- u_int64_t cache_packets;
- volatile int didsig;
- struct bpf_program bpf;
- int include_exclude_mode = 0;
- CIDR *xX_cidr = NULL;
- LIST *xX_list = NULL;
- char l2data[L2DATALEN] = "";
- int l2len = LIBNET_ETH_H;
- int maxpacket = 0;
- /* we get this from libpcap */
- extern char pcap_version[];
- #ifdef HAVE_TCPDUMP
- /* tcpdump handle */
- tcpdump_t tcpdump;
- #endif
- #ifdef DEBUG
- int debug = 0;
- #endif
- void replay_file(char *path, int l2enabled, char *l2data, int l2len);
- void replay_live(char *iface, int l2enabled, char *l2data, int l2len);
- void validate_l2(char *name, int l2enabled, char *l2data, int l2len, int linktype);
- void usage(void);
- void version(void);
- void configfile(char *file);
- void init(void);
- void apply_filter(pcap_t *pcap);
- int
- main(int argc, char *argv[])
- {
- char ebuf[256];
- int ch, i, nat_interface = 0;
- int l2enabled = 0;
- void *xX = NULL;
- char errbuf[PCAP_ERRBUF_SIZE];
- init(); /* init our globals */
- while ((ch =
- getopt(argc, argv,
- "bc:C:De:f:Fhi:I:j:J:k:K:l:L:m:MnN:o:Op:Pr:Rs:S:t:Tu:Vw:W:x:X:12:4:"
- #ifdef HAVE_TCPDUMP
- "vA:"
- #endif
- #ifdef DEBUG
- "d:"
- #endif
- )) != -1)
- switch (ch) {
- case 'b': /* sniff/send bi-directionally */
- options.sniff_bridge = 1;
- options.topspeed = 1;
- break;
- case 'c': /* cache file */
- cache_file = optarg;
- cache_packets = read_cache(&cachedata, cache_file);
- break;
- case 'C': /* cidr matching */
- options.cidr = 1;
- if (!parse_cidr(&cidrdata, optarg, ","))
- errx(1, "Unable to parse -C");
- break;
- #ifdef DEBUG
- case 'd': /* enable debug */
- debug = atoi(optarg);
- break;
- #endif
- case 'D': /* dump only data (no headers) to file (-w/-W) */
- options.datadump_mode = 1;
- options.topspeed = 1;
- break;
- case 'e': /* rewrite IP's to two end points */
- options.rewriteip ++;
- if (!parse_endpoints(&cidrmap_data1, &cidrmap_data2, optarg))
- errx(1, "Unable to parse -e");
- break;
- case 'f': /* config file */
- configfile(optarg);
- break;
- case 'F': /* force fixing checksums */
- options.fixchecksums = 1;
- break;
- case 'i': /* interface */
- intf = optarg;
- break;
- case 'I': /* primary dest mac */
- mac2hex(optarg, options.intf1_mac, sizeof(options.intf1_mac));
- if (memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", optarg);
- break;
- case 'j': /* secondary interface */
- intf2 = optarg;
- break;
- case 'J': /* secondary dest mac */
- mac2hex(optarg, options.intf2_mac, sizeof(options.intf2_mac));
- if (memcmp(options.intf2_mac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", optarg);
- break;
- case 'k': /* primary source mac */
- mac2hex(optarg, options.intf1_smac, sizeof(options.intf1_smac));
- if (memcmp(options.intf1_smac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", optarg);
- break;
- case 'K': /* secondary source mac */
- mac2hex(optarg, options.intf2_smac, sizeof(options.intf2_smac));
- if (memcmp(options.intf2_smac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", optarg);
- break;
- case 'l': /* loop count */
- options.n_iter = atoi(optarg);
- if (options.n_iter < 0)
- errx(1, "Invalid loop count: %s", optarg);
- break;
- case 'L': /* limit sending X packets */
- options.limit_send = strtoull(optarg, NULL, 0);
- if (options.limit_send <= 0)
- errx(1, "-L <limit> must be positive");
- break;
- case 'm': /* multiplier */
- options.mult = atof(optarg);
- if (options.mult <= 0)
- errx(1, "Invalid multiplier: %s", optarg);
- options.rate = 0.0;
- options.packetrate = 0.0;
- options.one_at_a_time = 0;
- options.topspeed = 0;
- break;
- case 'M': /* disable sending martians */
- options.no_martians = 1;
- break;
- case 'n': /* don't be nosy, non-promisc mode */
- options.promisc = 0;
- break;
- case 'N': /* rewrite IP addresses using our pseudo-nat */
- options.rewriteip ++;
- nat_interface ++;
- /* first -N is primary nic */
- if (nat_interface == 1) {
- if (! parse_cidr_map(&cidrmap_data1, optarg))
- errx(1, "Invalid primary NAT string");
- } else { /* after that, secondary nic */
- if (! parse_cidr_map(&cidrmap_data2, optarg))
- errx(1, "Invalid secondary NAT string");
- }
- break;
- case 'o': /* starting offset */
- #ifdef HAVE_PCAPNAV
- options.offset = strtoull(optarg, NULL, 0);
- #else
- errx(1,
- "tcpreplay was not compiled with libpcapnav. Unable to use -o");
- #endif
- break;
- case 'O': /* One interface/file */
- options.one_output = 1;
- break;
- case 'p': /* packets/sec */
- options.packetrate = atof(optarg);
- if (options.packetrate <= 0)
- errx(1, "Invalid packetrate value: %s", optarg);
- options.rate = 0.0;
- options.mult = 0.0;
- options.one_at_a_time = 0;
- options.topspeed = 0;
- break;
- case 'P': /* print our PID */
- fprintf(stderr, "PID: %hu\n", getpid());
- break;
- case 'r': /* target rate */
- options.rate = atof(optarg);
- if (options.rate <= 0)
- errx(1, "Invalid rate: %s", optarg);
- /* convert to bytes */
- options.rate = (options.rate * (1024 * 1024)) / 8;
- options.mult = 0.0;
- options.packetrate = 0.0;
- options.one_at_a_time = 0;
- options.topspeed = 0;
- break;
- case 'R': /* replay at top speed */
- options.topspeed = 1;
- options.mult = 0.0;
- options.rate = 0.0;
- options.one_at_a_time = 0;
- options.packetrate = 0.0;
- break;
- case 's':
- options.seed = atoi(optarg);
- break;
- case 'S': /* enable live replay mode w/ snaplen */
- options.sniff_snaplen = atoi(optarg);
- if ((options.sniff_snaplen < 0) || (options.sniff_snaplen > MAX_SNAPLEN)) {
- errx(1, "Invalid snaplen: %d", options.sniff_snaplen);
- }
- else if (options.sniff_snaplen == 0) {
- options.sniff_snaplen = MAX_SNAPLEN;
- }
- break;
- case 't': /* MTU */
- options.mtu = atoi(optarg);
- break;
- case 'T': /* Truncate frames > MTU */
- options.truncate = 1;
- break;
- case 'w': /* write packets to file */
- if (!options.datadump_mode) {
- if ((options.savepcap =
- pcap_open_dead(DLT_EN10MB, 0xffff)) == NULL)
- errx(1, "error setting primary output file linktype");
- if ((options.savedumper =
- pcap_dump_open(options.savepcap, optarg)) == NULL)
- errx(1, "pcap_dump_open() error: %s",
- pcap_geterr(options.savepcap));
-
- warnx("saving primary packets in %s", optarg);
- }
- else {
- if ((options.datadumpfile =
- creat(optarg,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
- errx(1, "error creating primary output file: %s\n%s",
- optarg, strerror(errno));
- warnx("saving primary data in %s", optarg);
- }
- break;
- case 'W': /* write packets to second file */
- /* don't bother opening a second file in one_output mode */
- if (options.one_output)
- break;
- if (!options.datadump_mode) {
- if ((options.savepcap2 =
- pcap_open_dead(DLT_EN10MB, 0xffff)) == NULL)
- errx(1, "error setting secondary output file linktype");
- if ((options.savedumper2 =
- pcap_dump_open(options.savepcap2, optarg)) == NULL)
- errx(1, "pcap_dump_open() error: %s",
- pcap_geterr(options.savepcap2));
- warnx("saving secondary packets in %s", optarg);
- }
- else {
- if ((options.datadumpfile2 =
- creat(optarg,
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
- errx(1, "error creating secondary output file: %s\n%s",
- optarg, strerror(errno));
- warnx("saving secondary data in %s", optarg);
- }
- break;
- case 'u': /* untruncate packet */
- if (strcmp("pad", optarg) == 0) {
- options.trunc = PAD_PACKET;
- }
- else if (strcmp("trunc", optarg) == 0) {
- options.trunc = TRUNC_PACKET;
- }
- else {
- errx(1, "Invalid untruncate option: %s", optarg);
- }
- options.fixchecksums = 0; /* untruncating already does this */
- break;
- #ifdef HAVE_TCPDUMP
- case 'v': /* verbose: print packet decodes via tcpdump */
- options.verbose = 1;
- break;
- case 'A':
- tcpdump.args = optarg;
- break;
- #endif
- case 'V': /* print version info */
- version();
- break;
- case 'x': /* include mode */
- if (include_exclude_mode != 0)
- errx(1, "Error: Can only specify -x OR -X");
- include_exclude_mode = 'x';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, optarg, &xX)) == 0)
- errx(1, "Unable to parse -x: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- } else if (! (include_exclude_mode & xXBPF)) {
- xX_cidr = (CIDR *) xX;
- }
- break;
- case 'X': /* exclude mode */
- if (include_exclude_mode != 0)
- errx(1, "Error: Can only specify -x OR -X");
- include_exclude_mode = 'X';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, optarg, &xX)) == 0)
- errx(1, "Unable to parse -X: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- } else {
- xX_cidr = (CIDR *) xX;
- }
- break;
- case '1': /* replay one packet at a time */
- options.one_at_a_time = 1;
- options.mult = 0.0;
- options.rate = 0.0;
- options.packetrate = 0;
- options.topspeed = 0;
- break;
- case '2': /* layer 2 header file */
- l2enabled = 1;
- l2len = read_hexstring(optarg, l2data, L2DATALEN);
- break;
- case '4':
- options.rewriteports = 1;
- if (! parse_portmap(&portmap_data, optarg))
- errx(1, "Invalid port mapping");
- break;
- default:
- usage();
- }
- argc -= optind;
- argv += optind;
- if ((argc == 0) && (!options.sniff_bridge))
- errx(1, "Must specify one or more pcap files to process");
- if (argc > 1)
- for (i = 0; i < argc; i++)
- if (!strcmp("-", argv[i]))
- errx(1, "stdin must be the only file specified");
- if (intf == NULL)
- errx(1, "Must specify a primary interface");
- if ((intf2 == NULL) && (cache_file != NULL))
- errx(1, "Needs secondary interface with cache");
- if ((intf2 != NULL) && (!options.sniff_bridge) &&
- (!options.cidr && (cache_file == NULL)))
- errx(1, "Needs cache or cidr match with secondary interface");
- if (options.sniff_bridge && (options.savepcap ||
- options.savedumper ||
- options.savepcap2 || options.savedumper2)) {
- errx(1, "Bridge mode excludes saving packets or data to file");
- }
- if ((intf2 != NULL) && options.datadump_mode && (!options.one_output) &&
- ((options.datadumpfile == 0) || (options.datadumpfile2 == 0)))
- errx(1,
- "You must specify two output files when splitting traffic in data dump mode");
- if ((options.offset) && (options.sniff_snaplen != -1)) {
- errx(1, "You can't specify an offset when sniffing a live network");
- }
- if ((!options.promisc) && (options.sniff_snaplen == -1)) {
- errx(1,
- "Not nosy can't be specified except when sniffing a live network");
- }
- if ((options.sniff_bridge) && (options.sniff_snaplen == -1)) {
- errx(1, "Bridging requires sniff mode (-S <snaplen>)");
- }
- if ((options.sniff_bridge) && (intf2 == NULL)) {
- errx(1, "Bridging requires a secondary interface");
- }
- if ((options.sniff_snaplen != -1) && options.one_at_a_time) {
- errx(1, "Sniffing live traffic excludes one at a time mode");
- }
- if (options.one_output && options.sniff_bridge) {
- errx(1, "One output mode and bridge mode are incompatible");
- }
- if ((options.rewriteip > 1) && (options.rewriteip != nat_interface)) {
- errx(1, "Using both -N and -e are not supported");
- }
- if (options.seed != 0) {
- srand(options.seed);
- options.seed = random();
- dbg(1, "random() picked: %d", options.seed);
- }
- /*
- * If we have one and only one -N, then use the same map data
- * for both interfaces/files
- */
- if ((cidrmap_data1 != NULL) && (cidrmap_data2 == NULL))
- cidrmap_data2 = cidrmap_data1;
- /*
- * some options are limited if we change the type of header
- * we're making a half-assed assumption that any header
- * length = LIBNET_ETH_H is actually 802.3. This will
- * prolly bite some poor slob later using some wierd
- * header type in their pcaps, but I don't really care right now
- */
- if (l2len != LIBNET_ETH_H) {
- /*
- * we can't untruncate packets with a different lenght
- * ethernet header because we don't take the lenghts
- * into account when doing the pointer math
- */
- if (options.trunc)
- errx(1, "You can't use -u with non-802.3 frames");
- /*
- * we also can't rewrite macs for non-802.3
- */
- if ((memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf2_mac, NULL_MAC, LIBNET_ETH_H) == 0))
- errx(1,
- "You can't rewrite destination MAC's with non-802.3 frames");
- }
- /* open interfaces for writing */
- if ((options.intf1 = libnet_init(LIBNET_LINK_ADV, intf, ebuf)) == NULL)
- errx(1, "Libnet can't open %s: %s", intf, ebuf);
- if (intf2 != NULL) {
- if ((options.intf2 = libnet_init(LIBNET_LINK_ADV, intf2, ebuf)) == NULL)
- errx(1, "Libnet can't open %s: %s", intf2, ebuf);
- }
- /* open bridge interfaces for reading */
- if (options.sniff_bridge) {
- if ((options.listen1 =
- pcap_open_live(intf, options.sniff_snaplen,
- options.promisc, PCAP_TIMEOUT, errbuf)) == NULL) {
- errx(1, "Libpcap can't open %s: %s", intf, errbuf);
- }
- apply_filter(options.listen1);
- if ((options.listen2 =
- pcap_open_live(intf2, options.sniff_snaplen,
- options.promisc, PCAP_TIMEOUT, errbuf)) == NULL) {
- errx(1, "Libpcap can't open %s: %s", intf2, errbuf);
- }
- apply_filter(options.listen2);
- /* sanity checks for the linktype */
- if (pcap_datalink(options.listen1) != pcap_datalink(options.listen2)) {
- errx(1, "Unable to bridge different datalink types");
- }
- /* abort on non-supported link types */
- if (pcap_datalink(options.listen1) == DLT_LINUX_SLL) {
- errx(1, "Unable to bridge Linux Cooked Capture format");
- }
- else if (pcap_datalink(options.listen1) == DLT_NULL) {
- errx(1, "Unable to bridge BSD loopback format");
- }
- else if (pcap_datalink(options.listen1) == DLT_LOOP) {
- errx(1, "Unable to bridge loopback interface");
- }
- /*
- * only need to validate once since we're guaranteed both interfaces
- * use the same link type
- */
- validate_l2(intf, l2enabled, l2data, l2len,
- pcap_datalink(options.listen1));
- warnx("listening on: %s %s", intf, intf2);
- }
- if (options.savepcap == NULL)
- warnx("sending on: %s %s", intf, intf2 == NULL ? "" : intf2);
- /* init the signal handlers */
- init_signal_handlers();
- if (gettimeofday(&begin, NULL) < 0)
- err(1, "gettimeofday() failed");
- /* don't use the standard main loop in bridge mode */
- if (options.sniff_bridge) {
- cache_byte = 0;
- cache_bit = 0;
- do_bridge(options.listen1, options.listen2, l2enabled, l2data, l2len);
- pcap_close(options.listen1);
- pcap_close(options.listen2);
- libnet_destroy(options.intf1);
- libnet_destroy(options.intf2);
- exit(0);
- }
- /* main loop for non-bridge mode */
- if (options.n_iter > 0) {
- while (options.n_iter--) { /* limited loop */
- for (i = 0; i < argc; i++) {
- /* reset cache markers for each iteration */
- cache_byte = 0;
- cache_bit = 0;
- /* replay file or live network depending on snaplen */
- if (options.sniff_snaplen == -1) {
- replay_file(argv[i], l2enabled, l2data, l2len);
- }
- else {
- replay_live(argv[i], l2enabled, l2data, l2len);
- }
- }
- }
- }
- else {
- /* loop forever */
- while (1) {
- for (i = 0; i < argc; i++) {
- /* reset cache markers for each iteration */
- cache_byte = 0;
- cache_bit = 0;
- /* replay file or live network depending on snaplen */
- if (options.sniff_snaplen == -1) {
- replay_file(argv[i], l2enabled, l2data, l2len);
- }
- else {
- replay_live(argv[i], l2enabled, l2data, l2len);
- }
- }
- }
- }
- if (bytes_sent > 0)
- packet_stats();
- /* save the pcap write file */
- if (options.savepcap != NULL)
- pcap_dump_close(options.savedumper);
- if (options.savepcap2 != NULL)
- pcap_dump_close(options.savedumper2);
- /* close the data dump files */
- if (options.datadumpfile)
- close(options.datadumpfile);
- if (options.datadumpfile2)
- close(options.datadumpfile2);
- return 0;
- } /* main() */
- /*
- * replay a live network on another interface
- * but only in a single direction (non-bridge mode)
- */
- void
- replay_live(char *iface, int l2enabled, char *l2data, int l2len)
- {
- pcap_t *pcap = NULL;
- u_int32_t linktype = 0;
- char errbuf[PCAP_ERRBUF_SIZE];
- /* if no interface specified, pick one */
- if ((!iface || !*iface) && !(iface = pcap_lookupdev(errbuf))) {
- errx(1, "Error determing live capture device : %s", errbuf);
- }
- if (strcmp(intf, iface) == 0) {
- warnx("WARNING: Listening and sending on the same interface!");
- }
- /* open the interface */
- if ((pcap = pcap_open_live(iface, options.sniff_snaplen,
- options.promisc, 0, errbuf)) == NULL) {
- errx(1, "Error opening live capture: %s", errbuf);
- }
- linktype = pcap_datalink(pcap);
- validate_l2(iface, l2enabled, l2data, l2len, linktype);
- /* do we apply a bpf filter? */
- if (options.bpf_filter != NULL) {
- if (pcap_compile(pcap, &bpf, options.bpf_filter,
- options.bpf_optimize, 0) != 0) {
- errx(1, "Error compiling BPF filter: %s", pcap_geterr(pcap));
- }
- if (pcap_setfilter(pcap, &bpf) != 0)
- errx(1, "Unable to apply BPF filter: %s", pcap_geterr(pcap));
- }
- do_packets(NULL, pcap, linktype, l2enabled, l2data, l2len);
- pcap_close(pcap);
- }
- /*
- * replay a pcap file out an interface
- */
- void
- replay_file(char *path, int l2enabled, char *l2data, int l2len)
- {
- pcap_t *pcap = NULL;
- pcapnav_t *pcapnav = NULL;
- u_int32_t linktype = 0;
- pcapnav_init();
- #ifdef HAVE_TCPDUMP
- if (options.verbose) {
- tcpdump.filename = path;
- tcpdump_open(&tcpdump);
- }
- #endif
- if ((pcapnav = pcapnav_open_offline(path)) == NULL) {
- errx(1, "Error opening file %s: %s", path, strerror(errno));
- }
- pcap = pcapnav_pcap(pcapnav);
- linktype = pcap_datalink(pcap);
- validate_l2(path, l2enabled, l2data, l2len, linktype);
- apply_filter(pcapnav_pcap(pcapnav));
- do_packets(pcapnav, NULL, linktype, l2enabled, l2data, l2len);
- pcapnav_close(pcapnav);
- #ifdef HAVE_TCPDUMP
- tcpdump_close(&tcpdump);
- #endif
- }
- /*
- * applys a BPF filter if applicable
- */
- void
- apply_filter(pcap_t * pcap)
- {
- /* do we apply a bpf filter? */
- if (options.bpf_filter != NULL) {
- if (pcap_compile(pcap, &bpf, options.bpf_filter,
- options.bpf_optimize, 0) != 0) {
- errx(1, "Error compiling BPF filter: %s", pcap_geterr(pcap));
- }
- pcap_setfilter(pcap, &bpf);
- }
- }
- /*
- * if linktype not DLT_EN10MB we have to see if we can send the frames
- * if DLT_LINUX_SLL AND (options.intf1_mac OR l2enabled), then OK
- * else if l2enabled, then ok
- */
- void
- validate_l2(char *name, int l2enabled, char *l2data, int l2len, int linktype)
- {
- dbg(1, "Linktype is %s\n", pcap_datalink_val_to_description(linktype));
- switch (linktype) {
- case DLT_EN10MB:
- /* nothing to do here */
- break;
- case DLT_LINUX_SLL:
-
- /* single output mode */
- if (! options.intf2) {
- /* if SLL, then either -2 or -I are ok */
- if ((memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0) && (!l2enabled)) {
- warnx("Unable to process pcap without -2 or -I: %s", name);
- return;
- }
- }
-
- /* dual output mode */
- else {
- /* if using dual interfaces, make sure -2 or -J & -I) is set */
- if (((memcmp(options.intf2_mac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0)) &&
- (! l2enabled)) {
- errx(1, "Unable to process pcap with -j without -2 or -I & -J: %s", name);
- return;
- }
- }
- break;
-
- case DLT_CHDLC:
- /* Cisco HDLC (used at least for SONET) */
- /*
- * HDLC has a 4byte header, a 2 byte address type (0x0f00 is unicast
- * is all I know) and a 2 byte protocol type
- */
-
- /* single output mode */
- if (! options.intf2) {
- /* Need either a full l2 header or -I & -k */
- if (((memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf1_smac, NULL_MAC, LIBNET_ETH_H) == 0)) &&
- (! l2enabled)) {
- errx(1, "Unable to process pcap without -2 or -I and -k: %s", name);
- return;
- }
- }
-
- /* dual output mode */
- else {
- /* Need to have a l2 header or -J, -K, -I, -k */
- if (((memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf1_smac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf2_mac, NULL_MAC, LIBNET_ETH_H) == 0) ||
- (memcmp(options.intf2_smac, NULL_MAC, LIBNET_ETH_H) == 0)) &&
- (! l2enabled)) {
- errx(1, "Unable to process pcap with -j without -2 or -J, -I, -K & -k: %s", name);
- return;
- }
- }
- break;
-
- case DLT_RAW:
- if (!l2enabled) {
- errx(1, "Unable to process pcap without -2: %s", name);
- return;
- }
- break;
- default:
- errx(1, "validate_l2(): Unsupported datalink type: %s (0x%x)",
- pcap_datalink_val_to_description(linktype), linktype);
- break;
- }
- /* calculate the maxpacket based on the l2len, linktype and mtu */
- if (l2enabled) {
- /* custom L2 header */
- dbg(1, "Using custom L2 header to calculate max frame size");
- maxpacket = options.mtu + l2len;
- }
- else if (linktype == DLT_EN10MB) {
- /* ethernet */
- dbg(1, "Using Ethernet to calculate max frame size");
- maxpacket = options.mtu + LIBNET_ETH_H;
- }
- else {
- /* oh fuck, we don't know what the hell this is, we'll just assume ethernet */
- maxpacket = options.mtu + LIBNET_ETH_H;
- warnx("Unable to determine layer 2 encapsulation, assuming ethernet\n"
- "You may need to increase the MTU (-t <size>) if you get errors");
- }
- }
- /*
- * parse the configfile, and put all the values into options
- */
- void
- configfile(char *file)
- {
- FILE *fp;
- char *argv[MAX_ARGS], buf[BUFSIZ];
- int argc, i;
- void *xX;
- int nat_interface = 0;
- if ((fp = fopen(file, "r")) == NULL)
- errx(1, "Could not open config file %s", file);
- for (i = 1; fgets(buf, sizeof(buf), fp) != NULL; i++) {
- if (*buf == '#' || *buf == '\r' || *buf == '\n')
- continue;
- if ((argc = argv_create(buf, MAX_ARGS, argv)) < 1) {
- warnx("couldn't parse arguments (line %d)", i);
- break;
- }
- #define ARGS(x, y) ( (!strcmp(argv[0], x)) && (argc == y) )
- if (ARGS("sniff_bridge", 1)) {
- options.sniff_bridge = 1;
- }
- else if (ARGS("cachefile", 2)) {
- cache_file = strdup(argv[1]);
- cache_packets = read_cache(&cachedata, cache_file);
- }
- else if (ARGS("cidr", 2)) {
- options.cidr = 1;
- if (!parse_cidr(&cidrdata, argv[1], ","))
- usage();
- }
- else if (ARGS("endpoints", 2)) {
- options.rewriteip ++;
- if (!parse_endpoints(&cidrmap_data1, &cidrmap_data2, argv[1]))
- usage();
- }
- #ifdef DEBUG
- else if (ARGS("debug", 1)) {
- debug = 1;
- }
- #endif
- else if (ARGS("datadump_mode", 1)) {
- options.datadump_mode = 1;
- }
- else if (ARGS("fixchecksums", 1)) {
- options.fixchecksums = 1;
- }
- else if (ARGS("l2data", 2)) {
- l2len = read_hexstring(argv[1], l2data, L2DATALEN);
- }
- else if (ARGS("intf", 2)) {
- intf = strdup(argv[1]);
- }
- else if (ARGS("primary_mac", 2)) {
- mac2hex(argv[1], options.intf1_mac, sizeof(options.intf1_mac));
- if (memcmp(options.intf1_mac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", argv[1]);
- }
- else if (ARGS("primary_smac", 2)) {
- mac2hex(argv[1], options.intf1_smac, sizeof(options.intf1_smac));
- if (memcmp(options.intf1_smac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", argv[1]);
- }
- else if (ARGS("second_intf", 2)) {
- intf2 = strdup(argv[1]);
- }
- else if (ARGS("second_mac", 2)) {
- mac2hex(argv[1], options.intf2_mac, sizeof(options.intf2_mac));
- if (memcmp(options.intf2_mac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", argv[1]);
- }
- else if (ARGS("second_smac", 2)) {
- mac2hex(argv[1], options.intf2_smac, sizeof(options.intf2_smac));
- if (memcmp(options.intf2_smac, NULL_MAC, LIBNET_ETH_H) == 0)
- errx(1, "Invalid mac address: %s", argv[1]);
- }
- else if (ARGS("loop", 2)) {
- options.n_iter = atoi(argv[1]);
- if (options.n_iter < 0)
- errx(1, "Invalid loop count: %s", argv[1]);
- }
- else if (ARGS("limit_send", 2)) {
- options.limit_send = strtoull(argv[1], NULL, 0);
- if (options.limit_send <= 0)
- errx(1, "limit_send <limit> must be positive");
- }
- else if (ARGS("multiplier", 2)) {
- options.mult = atof(argv[1]);
- if (options.mult <= 0)
- errx(1, "Invalid multiplier: %s", argv[1]);
- options.rate = 0.0;
- }
- else if (ARGS("no_martians", 1)) {
- options.no_martians = 1;
- }
- else if (ARGS("nat", 2)) {
- options.rewriteip ++;
- nat_interface ++;
- if (nat_interface == 1) {
- if (! parse_cidr_map(&cidrmap_data1, argv[1]))
- errx(1, "Invalid primary NAT string");
- } else {
- if (! parse_cidr_map(&cidrmap_data2, argv[1]))
- errx(1, "Invalid secondary NAT string");
- }
- }
- else if (ARGS("portmap", 2)) {
- options.rewriteports = 1;
- if (! parse_portmap(&portmap_data, argv[1]))
- errx(1, "Invalid port mapping");
- }
- #ifdef HAVE_PCAPNAV
- else if (ARGS("offset", 2)) {
- options.offset = strtoull(argv[1], NULL, 0);
- }
- #endif
- else if (ARGS("one_output", 1)) {
- options.one_output = 1;
- }
- else if (ARGS("one_at_a_time", 1)) {
- options.one_at_a_time = 1;
- options.rate = 0.0;
- options.mult = 0.0;
- options.topspeed = 0;
- options.packetrate = 0;
- }
- else if (ARGS("rate", 2)) {
- options.rate = atof(argv[1]);
- if (options.rate <= 0)
- errx(1, "Invalid rate: %s", argv[1]);
- /* convert to bytes */
- options.rate = (options.rate * (1024 * 1024)) / 8;
- options.mult = 0.0;
- options.topspeed = 0;
- options.packetrate = 0;
- }
- else if (ARGS("topspeed", 1)) {
- options.topspeed = 1;
- options.rate = 0.0;
- options.packetrate = 0;
- options.mult = 0.0;
- options.one_at_a_time = 0;
- }
- else if (ARGS("mtu", 2)) {
- options.mtu = atoi(argv[1]);
- }
- else if (ARGS("not_nosy", 1)) {
- options.promisc = 0;
- }
- else if (ARGS("truncate", 1)) {
- options.truncate = 1;
- }
- else if (ARGS("untruncate", 2)) {
- if (strcmp("pad", argv[1]) == 0) {
- options.trunc = PAD_PACKET;
- }
- else if (strcmp("trunc", argv[1]) == 0) {
- options.trunc = TRUNC_PACKET;
- }
- else {
- errx(1, "Invalid untruncate option: %s", argv[1]);
- }
- }
- else if (ARGS("seed", 2)) {
- options.seed = atol(argv[1]);
- }
- else if (ARGS("sniff_snaplen", 2)) {
- options.sniff_snaplen = atoi(argv[1]);
- if ((options.sniff_snaplen < 0) || (options.sniff_snaplen > MAX_SNAPLEN)) {
- errx(1, "Invalid sniff snaplen: %d", options.sniff_snaplen);
- }
- else if (options.sniff_snaplen == 0) {
- options.sniff_snaplen = MAX_SNAPLEN;
- }
- }
- else if (ARGS("packetrate", 2)) {
- options.packetrate = atof(argv[1]);
- if (options.packetrate < 0)
- errx(1, "Invalid packetrate option: %s", argv[1]);
- options.rate = 0.0;
- options.mult = 0.0;
- options.topspeed = 0;
- options.one_at_a_time = 0;
- }
- else if (ARGS("include", 2)) {
- if (include_exclude_mode != 0)
- errx(1,
- "Error: Can only specify include (-x) OR exclude (-X) ");
- include_exclude_mode = 'x';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, argv[1], &xX)) == 0)
- errx(1, "Unable to parse include: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- }
- else if (!include_exclude_mode & xXBPF) {
- xX_cidr = (CIDR *) xX;
- }
- }
- else if (ARGS("exclude", 2)) {
- if (include_exclude_mode != 0)
- errx(1, "Error: Can only specify include (-x) OR exclude (-X)");
- include_exclude_mode = 'X';
- if ((include_exclude_mode =
- parse_xX_str(include_exclude_mode, argv[1], &xX)) == 0)
- errx(1, "Unable to parse exclude: %s", optarg);
- if (include_exclude_mode & xXPacket) {
- xX_list = (LIST *) xX;
- }
- else if (!include_exclude_mode & xXBPF) {
- xX_cidr = (CIDR *) xX;
- }
- }
- else if (ARGS("primary_write", 2)) {
- if (!options.datadump_mode) {
- if ((options.savepcap =
- pcap_open_dead(DLT_EN10MB, 0xffff)) == NULL)
- errx(1, "error setting primary output file linktype");
- if ((options.savedumper =
- pcap_dump_open(options.savepcap, argv[1])) == NULL)
- errx(1, "pcap_dump_open() error: %s",
- pcap_geterr(options.savepcap));
- warnx("saving primary packets in %s", argv[1]);
- }
- else {
- if ((options.datadumpfile =
- creat(argv[1],
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
- errx(1, "error creating primary output file: %s\n%s",
- argv[1], strerror(errno));
- warnx("saving primary data in %s", argv[1]);
- }
- }
- }
- else if (ARGS("second_write", 2)) {
- if (!options.datadump_mode) {
- if ((options.savepcap2 =
- pcap_open_dead(DLT_EN10MB, 0xffff)) == NULL)
- errx(1, "error setting secondary output file linktype");
- if ((options.savedumper2 =
- pcap_dump_open(options.savepcap2, argv[1])) == NULL)
- errx(1, "pcap_dump_open() error: %s",
- pcap_geterr(options.savepcap2));
- warnx("saving secondary packets in %s", argv[1]);
- }
- else {
- if ((options.datadumpfile2 =
- creat(argv[1],
- S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
- errx(1, "error creating secondary output file: %s\n%s",
- argv[1], strerror(errno));
- warnx("saving secondary data in %s", argv[1]);
- }
- }
- else if (ARGS("verbose", 1)) {
- options.verbose = 1;
- }
- else if (ARGS("tcpdump_args", 2)) {
- tcpdump.args = argv[1];
- }
- else {
- errx(1, "Skipping unrecognized: %s", argv[0]);
- }
- }
- }
- void
- version(void)
- {
- fprintf(stderr, "tcpreplay version: %s", VERSION);
- #ifdef DEBUG
- fprintf(stderr, " (debug)\n");
- #else
- fprintf(stderr, "\n");
- #endif
- fprintf(stderr, "Cache file supported: %s\n", CACHEVERSION);
- fprintf(stderr, "Compiled against libnet: %s\n", LIBNET_VERSION);
- fprintf(stderr, "Compiled against libpcap: %s\n", pcap_version);
- #ifdef HAVE_PCAPNAV
- fprintf(stderr, "Compiled against libpcapnav: %s\n", PCAPNAV_VERSION);
- #else
- fprintf(stderr, "Not compiled against libpcapnav.\n");
- #endif
- #ifdef HAVE_TCPDUMP
- fprintf(stderr, "Using tcpdump located in: %s\n", TCPDUMP_BINARY);
- #else
- fprintf(stderr, "Not using tcpdump.\n");
- #endif
- exit(0);
- }
- void
- usage(void)
- {
- printf("Usage: tcpreplay [args] <file(s)>\n"
- "-A \"<args>\"\t\tPass arguments to tcpdump decoder (use w/ -v)\n"
- "-b\t\t\tBridge two broadcast domains in sniffer mode\n"
- "-c <cachefile>\t\tSplit traffic via cache file\n"
- "-C <CIDR1,CIDR2,...>\tSplit traffic by matching src IP\n");
- #ifdef DEBUG
- printf("-d <level>\t\tEnable debug output to STDERR\n");
- #endif
- printf("-D\t\t\tData dump mode (set this BEFORE -w and -W)\n"
- "-e <ip1:ip2>\t\tSpecify IP endpoint rewriting\n"
- "-f <configfile>\t\tSpecify configuration file\n"
- "-F\t\t\tFix IP, TCP, UDP and ICMP checksums\n"
- "-h\t\t\tHelp\n"
- "-i <nic>\t\tPrimary interface to send traffic out of\n"
- "-I <mac>\t\tRewrite dest MAC on primary interface\n"
- "-j <nic>\t\tSecondary interface to send traffic out of\n"
- "-J <mac>\t\tRewrite dest MAC on secondary interface\n"
- "-k <mac>\t\tRewrite source MAC on primary interface\n"
- "-K <mac>\t\tRewrite source MAC on secondary interface\n");
- printf("-l <loop>\t\tSpecify number of times to loop\n"
- "-L <limit>\t\tSpecify the maximum number of packets to send\n"
- "-m <multiple>\t\tSet replay speed to given multiple\n"
- "-M\t\t\tDisable sending martian IP packets\n"
- "-n\t\t\tNot nosy mode (not promisc in sniff/bridge mode)\n"
- "-N <CIDR1:CIDR2,...>\tRewrite IP's via pseudo-NAT\n"
- #ifdef HAVE_PCAPNAV
- "-o <offset>\t\tStarting byte offset\n"
- #endif
- "-O\t\t\tOne output mode\n"
- "-p <packetrate>\t\tSet replay speed to given rate (packets/sec)\n");
- printf("-P\t\t\tPrint PID\n"
- "-r <rate>\t\tSet replay speed to given rate (Mbps)\n"
- "-R\t\t\tSet replay speed to as fast as possible\n"
- "-s <seed>\t\tRandomize src/dst IP addresses w/ given seed\n"
- "-S <snaplen>\t\tSniff interface(s) and set the snaplen length\n"
- "-t <mtu>\t\tOverride MTU (defaults to 1500)\n"
- "-T\t\t\tTruncate packets > MTU so they can be sent\n"
- "-u pad|trunc\t\tPad/Truncate packets which are larger than the snaplen\n"
- "-v\t\t\tVerbose: print packet decodes for each packet sent\n"
- "-V\t\t\tVersion\n");
- printf("-w <file>\t\tWrite (primary) packets or data to file\n"
- "-W <file>\t\tWrite secondary packets or data to file\n"
- "-x <match>\t\tOnly send the packets specified\n"
- "-X <match>\t\tSend all the packets except those specified\n"
- "-1\t\t\tSend one packet per key press\n"
- "-2 <datafile>\t\tLayer 2 data\n"
- "-4 <PORT1:PORT2,...>\tRewrite port numbers\n"
- "<file1> <file2> ...\tFile list to replay\n");
- exit(1);
- }
- /*
- * Initialize globals
- */
- void
- init(void)
- {
- bytes_sent = failed = pkts_sent = 0;
- intf = intf2 = NULL;
- memset(&options, 0, sizeof(options));
- /* Default mode is to replay pcap once in real-time */
- options.mult = 1.0;
- options.n_iter = 1;
- options.rate = 0.0;
- options.packetrate = 0.0;
- /* set the default MTU size */
- options.mtu = DEFAULT_MTU;
- /* set the bpf optimize */
- options.bpf_optimize = BPF_OPTIMIZE;
- /* sniff mode options */
- options.sniff_snaplen = -1; /* disabled */
- options.promisc = 1; /* listen in promisc mode by default */
- /* poll timeout (in ms) defaults to infinate */
- options.poll_timeout = -1;
- /* disable limit send */
- options.limit_send = -1;
- /* init the RBTree */
- rbinit();
- #ifdef HAVE_TCPDUMP
- /* clear out tcpdump struct */
- memset(&tcpdump, '\0', sizeof(tcpdump_t));
- #endif
- cache_bit = cache_byte = 0;
- if (fcntl(STDERR_FILENO, F_SETFL, O_NONBLOCK) < 0)
- warnx("Unable to set STDERR to non-blocking: %s", strerror(errno));
- }
- void
- packet_stats()
- {
- float bytes_sec = 0.0, mb_sec = 0.0;
- int pkts_sec = 0;
- char bits[3];
- if (gettimeofday(&end, NULL) < 0)
- err(1, "gettimeofday");
- timersub(&end, &begin, &begin);
- if (timerisset(&begin)) {
- if (bytes_sent) {
- bytes_sec =
- bytes_sent / (begin.tv_sec + (float)begin.tv_usec / 1000000);
- mb_sec = (bytes_sec * 8) / (1024 * 1024);
- }
- if (pkts_sent)
- pkts_sec =
- pkts_sent / (begin.tv_sec + (float)begin.tv_usec / 1000000);
- }
- snprintf(bits, sizeof(bits), "%d", begin.tv_usec);
- fprintf(stderr, " %llu packets (%llu bytes) sent in %d.%s seconds\n",
- pkts_sent, bytes_sent, begin.tv_sec, bits);
- fprintf(stderr, " %.1f bytes/sec %.2f megabits/sec %d packets/sec\n",
- bytes_sec, mb_sec, pkts_sec);
- if (failed) {
- fprintf(stderr,
- " %llu write attempts failed from full buffers and were repeated\n",
- failed);
- }
- }
|