|
@@ -0,0 +1,395 @@
|
|
|
|
+/*
|
|
|
|
+ tcpreplay.c
|
|
|
|
+
|
|
|
|
+ Matt Undy <mundy@anzen.com>
|
|
|
|
+
|
|
|
|
+ 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 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.
|
|
|
|
+
|
|
|
|
+ Notes:
|
|
|
|
+ The performance of paced modes could be increased somewhat by
|
|
|
|
+ estimating sleep time and only checking against the RTC ocasionally,
|
|
|
|
+ although this would also result in a less precise data rate,
|
|
|
|
+ especially on machines with an imprecise usleep().
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+#ifdef HAVE_CONFIG_H
|
|
|
|
+#include "config.h"
|
|
|
|
+#endif /* HAVE_CONFIG_H */
|
|
|
|
+
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+#if TIME_WITH_SYS_TIME
|
|
|
|
+# include <sys/time.h>
|
|
|
|
+# include <time.h>
|
|
|
|
+#else
|
|
|
|
+# if HAVE_SYS_TIME_H
|
|
|
|
+# include <sys/time.h>
|
|
|
|
+# else
|
|
|
|
+# include <time.h>
|
|
|
|
+# endif
|
|
|
|
+#endif
|
|
|
|
+#include <pcap.h>
|
|
|
|
+#include <libnet.h>
|
|
|
|
+
|
|
|
|
+#define TCPREPLAY_VERSION "1.0.1"
|
|
|
|
+
|
|
|
|
+#define PACE 1 /*0b01*/
|
|
|
|
+#define RATE 2 /*0b10*/
|
|
|
|
+#define TIME_GT(x,y) (x tv_sec>y tv_sec||(x tv_sec==y tv_sec&&x tv_usec>y tv_usec))
|
|
|
|
+#define TIME_LT(x,y) (x tv_sec<y tv_sec||(x tv_sec==y tv_sec&&x tv_usec<y tv_usec))
|
|
|
|
+#define TIME_ASSIGN(x,y) x tv_sec=y tv_sec; x tv_usec=y tv_usec;
|
|
|
|
+#define PRINT_TIMEVAL(x) fprintf(stderr, " tv_sec=%li tv_usec=%li\n",x tv_sec, x tv_usec);
|
|
|
|
+
|
|
|
|
+typedef struct timeval time_struct;
|
|
|
|
+extern int errno;
|
|
|
|
+int failed_writes, first_pkt,older_pkts;
|
|
|
|
+int pkts_sent;
|
|
|
|
+long long bytes_sent;
|
|
|
|
+int iterrations;
|
|
|
|
+int mode;
|
|
|
|
+int rate;
|
|
|
|
+int debug;
|
|
|
|
+float multiplier;
|
|
|
|
+struct timeval lasttime, lastpkt, behind_time, starttime_struc,
|
|
|
|
+ startpkt_struct, last_dpkt, starttime, pkttime_struct;
|
|
|
|
+int caplen;
|
|
|
|
+int iterations;
|
|
|
|
+char * interface;
|
|
|
|
+
|
|
|
|
+void usage(){
|
|
|
|
+ fprintf(stderr, "Version: " TCPREPLAY_VERSION "\nUsage: tcpreplay "
|
|
|
|
+ "[-i interface] [-l loops] [-m multiplier] [-r rate] <file>\n");
|
|
|
|
+ exit (1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* we aren't checking for overflow here. if it happens we have run
|
|
|
|
+ into the 2038 problem and the whole time handling is broken (or a bug). */
|
|
|
|
+
|
|
|
|
+void addtime(time_struct * op1,time_struct * op2,time_struct * result)
|
|
|
|
+{
|
|
|
|
+ result->tv_usec = op1->tv_usec+op2->tv_usec;
|
|
|
|
+ if (result->tv_usec > 999999) {
|
|
|
|
+ result->tv_usec -= 1000000;
|
|
|
|
+ op1->tv_sec++;
|
|
|
|
+ }
|
|
|
|
+ result->tv_sec = op1->tv_sec+op2->tv_sec;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ if op1>op2, return op1-op2 and 0
|
|
|
|
+ if op1<op2, return |op1-op2| and 1
|
|
|
|
+ (return a negative # with the sign bit in the return int)
|
|
|
|
+ if op2>op1, subtract op2 from op1 and set return int
|
|
|
|
+*/
|
|
|
|
+
|
|
|
|
+int subtime(time_struct * op1, time_struct * op2, time_struct * result)
|
|
|
|
+{
|
|
|
|
+ int borrow=0;
|
|
|
|
+ int sign=0;
|
|
|
|
+ time_struct * temp_time;
|
|
|
|
+
|
|
|
|
+ if (TIME_LT(op1->, op2->)) {
|
|
|
|
+ sign = 1;
|
|
|
|
+ temp_time = op1;
|
|
|
|
+ op1 = op2;
|
|
|
|
+ op2 = temp_time;
|
|
|
|
+ }
|
|
|
|
+ if (op1->tv_usec >= op2->tv_usec) {
|
|
|
|
+ result->tv_usec = op1->tv_usec-op2->tv_usec;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ result->tv_usec = (op1->tv_usec+1000000)-op2->tv_usec;
|
|
|
|
+ borrow = 1;
|
|
|
|
+ }
|
|
|
|
+ result->tv_sec = (op1->tv_sec-op2->tv_sec)-borrow;
|
|
|
|
+
|
|
|
|
+ return sign;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void divtime(time_struct * num, float denom) {
|
|
|
|
+ num->tv_sec = num->tv_sec/denom;
|
|
|
|
+ num->tv_usec = num->tv_usec/denom;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void write_packet(u_char * user,struct pcap_pkthdr* pcap_hdr, u_char * data)
|
|
|
|
+{
|
|
|
|
+ int write_status = -1;
|
|
|
|
+ float sec;
|
|
|
|
+ struct timeval now, dtime, dpkt, sleeptime;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ if (mode) {
|
|
|
|
+#if HAVE_GETTIMEOFDAY
|
|
|
|
+ gettimeofday(&now,NULL);
|
|
|
|
+#endif
|
|
|
|
+ iterations++;
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if ((!(iterations %99))&&debug>0) {
|
|
|
|
+ fprintf(stderr, "\nELAPSED TIME %lu sec %li usec\n\n",
|
|
|
|
+ now.tv_sec-starttime_struc.tv_sec,
|
|
|
|
+ now.tv_usec - starttime_struc.tv_usec);
|
|
|
|
+ fprintf(stderr, "\nELAPSED PACKET TIME %lu sec %li usec\n\n",
|
|
|
|
+ pcap_hdr->ts.tv_sec - startpkt_struct.tv_sec,
|
|
|
|
+ pcap_hdr->ts.tv_usec - startpkt_struct.tv_usec);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ if (first_pkt) {
|
|
|
|
+ first_pkt = 0;
|
|
|
|
+ TIME_ASSIGN(starttime_struc., now.);
|
|
|
|
+ TIME_ASSIGN(lastpkt., pcap_hdr->ts.);
|
|
|
|
+ TIME_ASSIGN(startpkt_struct., pcap_hdr->ts.);
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ if (subtime(&now, &starttime_struc, &dtime)) {
|
|
|
|
+ fprintf(stderr, "The RTC has decreased since last read (or someone multithreaded this wrong)\n");
|
|
|
|
+ /*if it is earlier than the last time we executed this loop,
|
|
|
|
+ something is very wrong*/
|
|
|
|
+ abort();
|
|
|
|
+ }
|
|
|
|
+ if (mode & PACE) {
|
|
|
|
+ if (subtime(&pcap_hdr->ts, &lastpkt, &dpkt)) {
|
|
|
|
+ /*Uncomment this if you want to stop on out of order packets
|
|
|
|
+ in the dump file*/
|
|
|
|
+ /*fprintf(stderr, "packets in pcap file not in increasing time of arrival\n");
|
|
|
|
+ abort();*/
|
|
|
|
+ older_pkts++;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ divtime(&dpkt, multiplier);
|
|
|
|
+ addtime(&dpkt, &pkttime_struct, &pkttime_struct);
|
|
|
|
+ }
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if (debug > 2) {
|
|
|
|
+ fprintf(stderr, "dtime: ");
|
|
|
|
+ PRINT_TIMEVAL(dtime.);
|
|
|
|
+ fprintf(stderr, "pkttime_struct: ");
|
|
|
|
+ PRINT_TIMEVAL(pkttime_struct.);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ }
|
|
|
|
+ if (mode & RATE) {
|
|
|
|
+ /*calculate time packet should take, add to total packet time. */
|
|
|
|
+ sec = (float)pcap_hdr->caplen / (float)rate;
|
|
|
|
+ dpkt.tv_sec = sec;
|
|
|
|
+ dpkt.tv_usec = (sec - dpkt.tv_sec) * 1000000;
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if (debug > 2) {
|
|
|
|
+ fprintf(stderr, "dpkt:");
|
|
|
|
+ PRINT_TIMEVAL(dpkt.);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ addtime(&dpkt, &pkttime_struct, &pkttime_struct);
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if (debug > 2) {
|
|
|
|
+ fprintf(stderr, "pkttime_struct: ");
|
|
|
|
+ PRINT_TIMEVAL(pkttime_struct.);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ }
|
|
|
|
+ if (subtime(&dtime, &pkttime_struct, &sleeptime)) {
|
|
|
|
+ /*we are ahead by pkttime if true*/
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if (debug > 2) {
|
|
|
|
+ fprintf(stderr, "sleeptime: ");
|
|
|
|
+ PRINT_TIMEVAL(sleeptime.);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ sleep(sleeptime.tv_sec);
|
|
|
|
+ usleep(sleeptime.tv_usec);
|
|
|
|
+ }
|
|
|
|
+ TIME_ASSIGN(lastpkt., pcap_hdr->ts.);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ while (write_status < 0) {
|
|
|
|
+ write_status = write_link_layer((struct link_int *)user, interface,
|
|
|
|
+ data,pcap_hdr->caplen);
|
|
|
|
+#ifdef DEBUG
|
|
|
|
+ if (debug > 1) {
|
|
|
|
+ fprintf(stderr, "write_status = %i\n", write_status);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ if (write_status < 0) {
|
|
|
|
+ /*if errno = = 55(ENOBUFS) loop, otherwise break*/
|
|
|
|
+
|
|
|
|
+ if (errno != ENOBUFS) {
|
|
|
|
+ fprintf(stderr, "errno = %i\n", errno);
|
|
|
|
+ perror("pcap_inject");
|
|
|
|
+ exit(1);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ failed_writes++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ bytes_sent += write_status;
|
|
|
|
+ pkts_sent++;
|
|
|
|
+}
|
|
|
|
+int main(int argc, char * argv[])
|
|
|
|
+{
|
|
|
|
+ pcap_t * in_file;
|
|
|
|
+ struct link_int * write_if;
|
|
|
|
+ float Mrate = 0;
|
|
|
|
+ double starttime_local, startusec;
|
|
|
|
+ char ebuf[256];
|
|
|
|
+ struct timeval tp;
|
|
|
|
+ extern char *optarg;
|
|
|
|
+ extern int optind;
|
|
|
|
+ int ch, iterrated,files = 0;
|
|
|
|
+ char * name = NULL;
|
|
|
|
+ int stdinput = 0;
|
|
|
|
+ debug = 0;
|
|
|
|
+ iterrations = 1;
|
|
|
|
+ mode = 0;
|
|
|
|
+ interface = NULL;
|
|
|
|
+
|
|
|
|
+ while ((ch = getopt(argc, argv, "i:l:r:m:h")) != -1)
|
|
|
|
+ switch(ch) {
|
|
|
|
+ case 'd':
|
|
|
|
+ debug++;
|
|
|
|
+ break;
|
|
|
|
+ case 'm':
|
|
|
|
+ multiplier = atof(optarg);
|
|
|
|
+ mode = mode | PACE;
|
|
|
|
+ break;
|
|
|
|
+ case 'r':
|
|
|
|
+ mode = mode | RATE;
|
|
|
|
+ Mrate = atof(optarg);
|
|
|
|
+ rate = (Mrate * (1024 * 1024)) / 8;
|
|
|
|
+ break;
|
|
|
|
+ case 'l':
|
|
|
|
+ iterrations = atoi(optarg);
|
|
|
|
+ break;
|
|
|
|
+ case 'i':
|
|
|
|
+ interface = optarg;
|
|
|
|
+ break;
|
|
|
|
+ case 'h':
|
|
|
|
+ usage();
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ usage();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((mode & PACE) && (mode & RATE)) {
|
|
|
|
+ usage();
|
|
|
|
+ }
|
|
|
|
+ if ((mode & PACE) && multiplier == 0) {
|
|
|
|
+ fprintf(stderr, "invalid pace multiplier - use a nonzero multiplier\n");
|
|
|
|
+ usage();
|
|
|
|
+ }
|
|
|
|
+ if ((mode & RATE) && (Mrate == 0)) {
|
|
|
|
+ fprintf(stderr, "invalid sending rate - use a nonzero rate\n");
|
|
|
|
+ usage();
|
|
|
|
+ }
|
|
|
|
+ if (interface == NULL) {
|
|
|
|
+ if (!(interface = pcap_lookupdev(ebuf))) {
|
|
|
|
+ fprintf(stderr, "can't determine default interface: %s\n", ebuf);
|
|
|
|
+ exit(1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ iterations = 0;
|
|
|
|
+ older_pkts = 0;
|
|
|
|
+#if HAVE_GETTIMEOFDAY
|
|
|
|
+ gettimeofday(&tp,NULL);
|
|
|
|
+#endif
|
|
|
|
+ /*if you don't have gettimeofday, you will have to add the appropriate
|
|
|
|
+ syscall for your system*/
|
|
|
|
+ starttime_local = tp.tv_sec;
|
|
|
|
+ startusec = tp.tv_usec;
|
|
|
|
+ failed_writes = 0;
|
|
|
|
+ pkts_sent = 0;
|
|
|
|
+ bytes_sent = 0;
|
|
|
|
+ write_if = open_link_interface(interface,ebuf);
|
|
|
|
+ if (write_if <= 0) {
|
|
|
|
+ fprintf(stderr, "output i/f: %s\n",ebuf);
|
|
|
|
+ exit(1);
|
|
|
|
+ }
|
|
|
|
+ /*need to account for no args*/
|
|
|
|
+
|
|
|
|
+ if (argc == optind) {
|
|
|
|
+ stdinput = 1;
|
|
|
|
+ }
|
|
|
|
+ for (files=0;files < argc-optind || stdinput;files++) {
|
|
|
|
+ if (stdinput) {
|
|
|
|
+ name = malloc(4);
|
|
|
|
+ strcpy(name,"-");
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ name = argv[files+optind];
|
|
|
|
+ }
|
|
|
|
+ for (iterrated = 0; iterrated < iterrations; iterrated++){
|
|
|
|
+ first_pkt = 1;
|
|
|
|
+#if HAVE_GETTIMEOFDAY
|
|
|
|
+ gettimeofday(&tp,NULL);
|
|
|
|
+#endif
|
|
|
|
+ pkttime_struct.tv_sec = 0;
|
|
|
|
+ pkttime_struct.tv_usec = 0;
|
|
|
|
+ lasttime.tv_sec = tp.tv_sec;
|
|
|
|
+ lasttime.tv_usec = tp.tv_usec;
|
|
|
|
+ in_file = pcap_open_offline(name, ebuf);
|
|
|
|
+ if (!in_file) {
|
|
|
|
+ fprintf(stderr, "Error opening %s for reading\n",argv[1]);
|
|
|
|
+ fprintf(stderr, "in_file: %s\n",ebuf);
|
|
|
|
+ exit (1);
|
|
|
|
+ }
|
|
|
|
+ if (pcap_dispatch(in_file, 0,(void *)&write_packet,
|
|
|
|
+ (u_char *) write_if) == -1) {
|
|
|
|
+ pcap_perror(in_file, argv[1]);
|
|
|
|
+ }
|
|
|
|
+ pcap_close(in_file);
|
|
|
|
+ }
|
|
|
|
+ stdinput = 0;
|
|
|
|
+ }
|
|
|
|
+ if (failed_writes) {
|
|
|
|
+ fprintf(stderr, "%i write attempts failed from full buffers and were repeated\n", failed_writes);
|
|
|
|
+ }
|
|
|
|
+ if (older_pkts) {
|
|
|
|
+ fprintf(stderr, "%i packet delays removed because they were out of order\n", older_pkts);
|
|
|
|
+ }
|
|
|
|
+ fprintf(stderr, "%i packets sucessfully sent", pkts_sent);
|
|
|
|
+#if HAVE_GETTIMEOFDAY
|
|
|
|
+ gettimeofday(&tp, NULL);
|
|
|
|
+#endif
|
|
|
|
+ fprintf(stderr, " in %f seconds(%f packets per second)\n",
|
|
|
|
+ (tp.tv_sec - starttime_local + ((tp.tv_usec - startusec) / 1000000)),
|
|
|
|
+ (pkts_sent / ((tp.tv_sec - starttime_local) * 1000000 +
|
|
|
|
+ ((tp.tv_usec - startusec)))) * 1000000);
|
|
|
|
+ fprintf(stderr, "%lld bytes sucessfully sent", bytes_sent);
|
|
|
|
+
|
|
|
|
+ fprintf(stderr, "(%f bytes per second\n",
|
|
|
|
+ (bytes_sent / ((tp.tv_sec - starttime_local) * 1000000 +
|
|
|
|
+ ((tp.tv_usec - startusec)))) * 1000000);
|
|
|
|
+ fprintf(stderr, "%f megabits per second)\n",
|
|
|
|
+ (((bytes_sent / ((tp.tv_sec - starttime_local) * 1000000 +
|
|
|
|
+ ((tp.tv_usec-startusec)))) * 1000000) * 8) /
|
|
|
|
+ (1024 * 1024));
|
|
|
|
+ exit(0);
|
|
|
|
+}
|