/* $Id: tcpreplay.c,v 1.76 2003/12/11 03:05:59 aturner Exp $ */ /* * Copyright (c) 2001, 2002, 2003 Aaron Turner, Matt Bing. * 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 name of Anzen Computing, Inc. 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 #include #include #include #include #include #include #include #include #include #include "tcpreplay.h" #include "cache.h" #include "cidr.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" struct options options; char *cachedata = NULL; CIDR *cidrdata = 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_int32_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 DEBUG int debug = 0; #endif void replay_file(char *, int, char *, int); void replay_live(char *, int, char *, int); void usage(); void version(); void configfile(char *); void init(void); void apply_filter(pcap_t *); int main(int argc, char *argv[]) { char ebuf[256]; int ch, i; int l2enabled = 0; void *xX = NULL; char errbuf[PCAP_ERRBUF_SIZE]; init(); /* init our globals */ #ifdef DEBUG while ((ch = getopt(argc, argv, "bc:C:Df:Fhi:I:j:J:l:L:m:Mno:p:Pr:Rs:S:t:Tu:Vvw:W:x:X:?2:d:")) != -1) #else while ((ch = getopt(argc, argv, "bc:C:Df:Fhi:I:j:J:l:L:m:Mno:p:Pr:Rs:S:t:Tu:Vvw:W:x:X:?2:")) != -1) #endif 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)) usage(); 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 '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, 6) == 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, 6) == 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 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; 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 'o': /* starting offset */ options.offset = atol(optarg); 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; 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; break; case 'R': /* replay at top speed */ options.topspeed = 1; break; case 's': options.seed = atoi(optarg); options.fixchecksums = 0; /* seed already does this */ break; case 'S': options.sniff_snaplen = atoi(optarg); if ((options.sniff_snaplen < 0) || (options.sniff_snaplen > 65535)) { errx(1, "Invalid snaplen: %d", options.sniff_snaplen); } else if (options.sniff_snaplen == 0) { options.sniff_snaplen = 65535; } 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 */ 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; case 'V': version(); break; case 'x': if (include_exclude_mode != 0) errx(1, "Error: Can only specify -x OR -X"); include_exclude_mode = 'x'; if ((xX = parse_xX_str(include_exclude_mode, optarg)) == NULL) 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': if (include_exclude_mode != 0) errx(1, "Error: Can only specify -x OR -X"); include_exclude_mode = 'X'; if ((xX = parse_xX_str(include_exclude_mode, optarg)) == NULL) 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 '2': /* layer 2 header file */ l2enabled = 1; l2len = read_hexstring(optarg, l2data, L2DATALEN); 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.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 )"); } if ((options.sniff_bridge) && (intf2 == NULL)) { errx(1, "Bridging requires a secondary interface"); } if (options.seed != 0) { srand(options.seed); options.seed = random(); dbg(1, "random() picked: %d", options.seed); } /* * 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, 6) == 0) || (memcmp(options.intf2_mac, NULL_MAC, 6) == 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 */ 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)); } pcap_setfilter(pcap, &bpf); } 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) { const struct pcap_file_header *file_header = NULL; pcapnav_t *pcapnav = NULL; u_int32_t linktype = 0; pcapnav_init(); if ((pcapnav = pcapnav_open_offline(path)) == NULL) { errx(1, "Error opening file: %s", strerror(errno)); } file_header = pcapnav_get_file_header(pcapnav); linktype = file_header->linktype; validate_l2(path, l2enabled, l2data, l2len, linktype); apply_filter(pcapnav_pcap(pcapnav)); do_packets(pcapnav, NULL, linktype, l2enabled, l2data, l2len); pcapnav_close(pcapnav); } /* * 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); } } void configfile(char *file) { FILE *fp; char *argv[MAX_ARGS], buf[BUFSIZ]; int argc, i; void *xX; 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(); #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, 6) == 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, 6) == 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 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("offset", 2)) { options.offset = atol(argv[1]); } 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; } else if (ARGS("topspeed", 1)) { options.topspeed = 1; } 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 > 65535)) { errx(1, "Invalid sniff snaplen: %d", options.sniff_snaplen); } else if (options.sniff_snaplen == 0) { options.sniff_snaplen = 65535; } } else if (ARGS("offset", 2)) { options.offset = atol(argv[1]); } 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; } 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 ((xX = parse_xX_str(include_exclude_mode, argv[1])) == NULL) 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 ((xX = parse_xX_str(include_exclude_mode, argv[1])) == NULL) 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 { errx(1, "Skipping unrecognized: %s", argv[0]); } } } void version() { 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); fprintf(stderr, "Compiled against libpcapnav: %s\n", PCAPNAV_VERSION); exit(0); } void usage() { fprintf(stderr, "Usage: tcpreplay [args] \n"); fprintf(stderr, "-b\t\t\tBridge two broadcast domains in sniffer mode\n" "-c \t\tSplit traffic via cache file\n" "-C \tSplit traffic in CIDR Mode\n"); #ifdef DEBUG fprintf(stderr, "-d \t\tEnable debug output to STDERR\n"); #endif fprintf(stderr, "-D\t\t\tData dump mode (set this BEFORE -w and -W)\n" "-f \t\tSpecify configuration file\n" "-F\t\t\tFix IP, TCP and UDP checksums\n" "-h\t\t\tHelp\n" "-i \t\tPrimary interface to send traffic out of\n" "-I \t\tRewrite dest MAC on primary interface\n" "-j \t\tSecondary interface to send traffic out of\n" "-J \t\tRewrite dest MAC on secondary interface\n"); fprintf(stderr, "-l \t\tSpecify number of times to loop\n" "-m \t\tSet replay speed to given multiple\n" "-M\t\t\tDisable sending martian IP packets\n" "-n\t\t\tNot nosy mode (turn off promisc when sniffing)\n" "-o \t\tStarting byte offset\n" "-p \t\tSet replay speed to given rate (packets/sec)\n"); fprintf(stderr, "-P\t\t\tPrint PID\n" "-r \t\tSet replay speed to given rate (Mbps)\n" "-R\t\t\tSet replay speed to as fast as possible\n" "-s \t\tRandomize src/dst IP addresses w/ given seed\n" "-S \t\tSniff traffic and set the snaplen length\n" "-t \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\tVersion\n"); fprintf(stderr, "-w \t\tWrite (primary) packets or data to file\n" "-W \t\tWrite secondary packets or data to file\n" "-x \t\tOnly send the packets specified\n" "-X \t\tSend all the packets except those specified\n" "-2 \t\tLayer 2 data\n" " ...\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(); cache_bit = cache_byte = 0; }