forked from appneta/tcpreplay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtcpliveplay.c
1255 lines (1057 loc) · 46.4 KB
/
tcpliveplay.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Main Author & Publisher: Yazan H. Siam ([email protected])
* File: tcpliveplay.c
* Started as a Senior Design project @ North Carolina State University
* Last Updated Date: September 5, 2012
*
*/
/**
* Program Description:
* This program, 'tcpliveplay' replays a captured set of packets using new TCP connections with the
* captured TCP payloads against a remote host in order to do comprehensive vulnerability testings.
* This program takes in a "*.pcap" file that contains only one tcp flow connection and replays it
* against a live host exactly how the captured packets are laid out. At the beginning, the program
* establishes who the 'client' is and the 'server' is based on who initiates the SYN compares each
* packet's source ip against the ip of the 'client' (which is named local in the code) and the 'server'
* (remote) to correctly determine the expected seqs & acks. This also extracts the MACs of both local
* and remote clients. The program is also capable of rewriting the local and remote MAC & IP so that
* the packets are properly replayed when used on live networks. The current state of the program is that
* it takes in a pcap file on command line and writes a new file called "newfile.pcap" which is used thereafter
* for the rest of the program's calculations and set expectations. The program prints out a summary of the
* new file on the command prompt. Once the program is done, "newfile.pcap" is cleaned up.
* Program Design Overview:
* Before replaying the packets, the program reads in the pcap file that contains one tcp flow,
* and takes the SEQ/ACK #s.
* Based on the number of packets, a struct schedule of events are is set up. Based on
* the SEQ/ACK numbers read in, the schedule is setup to be relative numbers rather than
* absolute. This is done by starting with local packets, subtracting the first SEQ (which
* is that of the first SYN packet) from all the SEQs of the local packets then by subtracting
* the first remote sequence (which is that of the SYN-ACK packet) from all the local packet's
* ACKs. After the local side SEQ/ACK numbers are fixed to relative numbers, 'lseq_adjust'
* the locally generated random number for the SYN packet gets added to all the local SEQs
* to adjust the schedule to absolute number configuration. Then doing the remote side is similar
* except we only fix the remote ACKs based on our locally generated random number because
* we do not yet know the remote random number of the SYN-ACK packet. This means that at this
* point the entire schedule of local packets and remote packets are set in such a way that
* the local packets' SEQ's are absolute, but ACKs are relative and the remote packets' SEQ's are
* relative but ACKs as absolute. Once this is set, the replay starts by sending first SYN packet.
* If the remote host's acks with the SYN packet_SEQ+1 then we save their remote SEQ and adjust
* the local ACKs and remote SEQs in the struct schedule to be absolute based this remote SEQ.
* From this point on forward, we know or 'expect' what the remote host's ACKs and SEQs are exactly.
* If the remote host responds correctly as we expect (checking the schedule position expectation
* as packets are received) then we proceed in the schedule whether the next event is to send a local
* packet or wait for a remote packet to arrive.
*
* Usage: tcpliveplay <eth0/eth1> <file.pcap> <Destination IP [1.2.3.4]> <Destination mac [0a:1b:2c:3d:4e:5f]> <'random' dst port OR specify dport #>
*
* Example:
* yhsiam@yhsiam-VirtualBox:~$ tcpliveplay eth0 test1.pcap 192.168.1.4 52:57:01:11:31:92 random
*
* NOTE: This program may not completely replay the packets due to the remote host responding in an unexpected
* fashion such as responding with packets never seen in the given *.pcap file or coupling packets together, etc.
* if you have any suggestion on improving this software or if you find any bugs, please let me know at my
* email: [email protected]
*
* Past Contributors (Last contributed May 4, 2012): Andrew Leonard & Beau Luck
*
*/
#include "config.h"
#include "defines.h"
#include "common.h"
#include <signal.h>
#include <time.h>
#include <utime.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include "tcpliveplay.h"
#include "tcpliveplay_opts.h"
#include "common/sendpacket.h"
#include "common/utils.h"
#include "send_packets.h"
volatile int didsig;
#ifdef DEBUG /* set -DDEBUG=1 */
int debug = 0;
#endif
pcap_t *set_live_filter(char *dev, input_addr* hostip, unsigned int port);
pcap_t *set_offline_filter(char* file);
pcap_t *live_handle;
u_char *recvd_pkt_data = NULL;
unsigned int buf_write_index = 0;
unsigned int sched_index = 0;
unsigned int initial_rseq=0;
sendpacket_t *sp;
unsigned int seed = 0;
const u_char *packet_keeper_rprev = NULL;
ether_hdr *etherhdr_rprev = NULL; /*g for Global header pointers used in pcap_loop callback*/
tcp_hdr *tcphdr_rprev = NULL;
ipv4_hdr *iphdr_rprev = NULL;
unsigned int size_payload_prev = 0;
unsigned int finack_rindex = 0;
unsigned int acked_index = 0;
unsigned int diff_payload_index = 0;
bool different_payload = false;
volatile sig_atomic_t keep_going = 1;
int random_port();
unsigned int pkts_scheduled=0; /* packet counter */
struct tcp_sched* sched = NULL;
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
void catch_alarm (int sig);
int iface_addrs(char* iface, input_addr* ip, struct mac_addr* mac);
int extmac(char* new_rmac_ptr, struct mac_addr* new_remotemac);
int extip(char *ip_string, input_addr* new_remoteip);
int rewrite(input_addr* new_remoteip, struct mac_addr* new_remotemac, input_addr* myip, struct mac_addr* mymac, char* file, unsigned int new_src_port);
int setup_sched(struct tcp_sched* sched);
int relative_sched(struct tcp_sched* sched, u_int32_t first_rseq, int num_packets);
int fix_all_checksum_liveplay(ipv4_hdr *iphdr);
int compip(input_addr* lip, input_addr* rip, input_addr* pkgip);
int do_checksum_liveplay(u_int8_t *data, int proto, int len);
int do_checksum_math_liveplay(u_int16_t *data, int len);
/**
* This is the main function of the program that handles calling other
* functions to implemented the needed operations of the replay functionaily.
*/
int
main(int argc, char **argv)
{
unsigned int k;
int num_packets = 0;
static const char random_strg[] = "random";
char* iface = argv[1];
char* new_rmac_ptr;
char* new_rip_ptr;
input_addr new_remoteip;
struct mac_addr new_remotemac;
input_addr myip;
struct mac_addr mymac;
int new_src_port = 0;
unsigned int retransmissions = 0;
pcap_t *local_handle;
char errbuf[PCAP_ERRBUF_SIZE];
char ebuf[SENDPACKET_ERRBUF_SIZE];
int i;
optionProcess(&tcpliveplayOptions, argc, argv); /*Process AutoOpts for manpage options*/
if((argc < 5) || (argv[1]==NULL) || (argv[2]==NULL) || (argv[3]==NULL) || (argv[4]==NULL) || (argv[5]==NULL)){
printf("ERROR: Incorrect Usage!\n");
printf("Usage: tcpliveplay <eth0/eth1> <file.pcap> <Destination IP [1.2.3.4]> <Destination mac [0a:1b:2c:3d:4e:5f]> <specify 'random' or specific port#>\n");
printf("Example:\n yhsiam@yhsiam-VirtualBox:~$ sudo tcpliveplay eth0 test1.pcap 192.168.1.4 52:57:01:11:31:92 random\n\n");
exit(0);
}
if (strlen(iface) > IFNAMSIZ - 1)
errx(-1, "Invalid interface name %s\n", iface);
if (iface_addrs(iface, &myip, &mymac) < 0) /* Extract MAC of interface replay is being request on */
errx(-1, "Failed to access interface %s\n", iface);
/* open send function socket*/
if ((sp = sendpacket_open(iface, ebuf, TCPR_DIR_C2S, SP_TYPE_NONE, NULL)) == NULL)
errx(-1, "Can't open %s: %s", argv[1], ebuf);
/*for(int i = 0; i<10; i++) tolower(port_mode[i]);*/
if(strcmp(argv[5], random_strg)==0)
new_src_port = random_port();
else
new_src_port = atoi(argv[5]);
if (new_src_port < 0 || new_src_port > 65535)
errx(new_src_port, "Cannot use source port %d", new_src_port);
printf("new source port:: %d\n", new_src_port);
/* Establish a handler for SIGALRM signals. */
/* This is used as timeout for unresponsive remote hosts */
signal (SIGALRM, catch_alarm);
/* Extract new Remote MAC & IP inputed at command line */
new_rmac_ptr= argv[4];
new_rip_ptr = argv[3];
/* These function setup the MAC & IP addresses in the mac_addr & in_addr structs */
if (extmac(new_rmac_ptr, &new_remotemac) == ERROR)
errx(-1, "failed to parse mac address %s\n", new_rmac_ptr);
if (extip(new_rip_ptr, &new_remoteip) == ERROR)
errx(-1, "failed to parse IP address %s\n", new_rip_ptr);
/* Rewrites the given "*.pcap" file with all the new parameters and returns the number of packets */
/* that need to be replayed */
num_packets = rewrite(&new_remoteip, &new_remotemac, &myip, &mymac, argv[2], new_src_port);
if (num_packets < 2)
errx(-1, "Unable to rewrite PCAP file %s\n",argv[2]);
/* create schedule & set it up */
sched = (struct tcp_sched*) malloc(num_packets*sizeof(struct tcp_sched));
if (!sched)
err(-1, "out of memory\n");
pkts_scheduled = setup_sched(sched); /* Returns number of packets in schedule*/
/* Set up the schedule struct to be relative numbers rather than absolute*/
for (i = 0; i < num_packets; i++) {
sched[i].exp_rseq = 0;
sched[i].exp_rack = 0;
}
relative_sched(sched, sched[1].exp_rseq, num_packets);
printf("Packets Scheduled %u\n", pkts_scheduled);
/* Open socket for savedfile traffic to be sent*/
local_handle = pcap_open_offline("newfile.pcap", errbuf); /*call pcap library function*/
if (local_handle == NULL) {
fprintf(stderr,"Couldn't open pcap file %s: %s\n", "newfile.pcap", errbuf);
free(sched);
return(2);
}
/* Open socket for live traffic to be listed to*/
live_handle = set_live_filter(iface, &myip, new_src_port); /* returns a pcap_t that filters out traffic other than TCP*/
if (live_handle == NULL) {
fprintf(stderr,"Error occurred while listing on traffic: %s\n", errbuf);
free(sched);
return(2);
}
/* Printout when no packets are scheduled */
if(pkts_scheduled==0){
printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ ERROR:: There are no TCP packets to send +\n");
printf("+ Closing replay... +\n");
printf("+ Thank you for Playing, Play again! +\n");
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
free(sched);
return ERROR;
}
/* Start replay by sending the first packet, the SYN, from the schedule */
else if(sched[0].local){ /* Send first packet*/
sendpacket(sp, sched[sched_index].packet_ptr, sched[sched_index].pkthdr.len, &sched[sched_index].pkthdr);
printf("Sending Local Packet............... [%u]\n",sched_index+1);
sched_index++; /* Proceed in the schedule */
}
/* Main while loop that handles the decision making and the replay oprations */
while(sched_index<pkts_scheduled){
if(!keep_going) { /*Check the timeout variable */
printf("\n======================================================================\n");
printf("= TIMEOUT:: Remote host is not responding. You may have crashed =\n");
printf("= the host you replayed these packets against OR the packet sequence =\n");
printf("= changed since the capture was taken resulting in differing =\n");
printf("= expectations. Closing replay... =\n");
printf("======================================================================\n\n");
break;
}
/* tcphdr_rprev carries the last remote tcp header */
if(tcphdr_rprev == NULL) {
//printf("FIRST PASS!\n");
}
/* Check if received RST or RST-ACK flagged packets*/
else if((tcphdr_rprev->th_flags==TH_RST) || (tcphdr_rprev->th_flags==(TH_RST|TH_ACK))){
printf("\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ ERROR:: Remote host has requested to RESET the connection. +\n");
printf("+ Closing replay... +\n");
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
break;
}
/*Check if received earlier FIN-ACK than expected
else if((sched_index-1 < finack_rindex) && (tcphdr_rprev->th_flags==(TH_FIN|TH_ACK))){
printf("\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ ERROR:: Remote host sent an earlier FIN-ACK than expected. +\n");
printf("+ Closing replay... +\n");
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
return;
} */
/* Do the following if we receive a packet that ACKs for the same ACKing of next packet */
else if((tcphdr_rprev->th_seq==htonl(sched[sched_index].exp_rseq)) && (tcphdr_rprev->th_ack==htonl(sched[sched_index].exp_rack)) && (size_payload_prev>0)){
printf("Received Remote Packet............... [%u]\n",sched_index+1);
printf("Skipping Packet...................... [%u] to Packet [%u]\n",sched_index+1, sched_index+2);
printf("Next Remote Packet Expectation met.\nProceeding in replay...\n");
sched_index++;
}
/* Do the following if payload does not meet expectation and re-attempt with the remote host for 3 tries*/
else if(different_payload){
printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ WARNING: Remote host is not meeting packet size expectations. +\n");
printf("+ for packet %-u. Application layer data differs from capture being replayed. +\n", diff_payload_index+1);
printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
printf("Requesting retransmission.\n Proceeding...\n");
different_payload = false;
}
/* Local Packets */
if(sched[sched_index].local) {
/*Reset alarm timeout*/
alarm (ALARM_TIMEOUT);
printf("Sending Local Packet............... [%u]\n", sched_index + 1);
/* edit each packet tcphdr before sending based on the schedule*/
if(sched_index>0){
sched[sched_index].tcphdr->th_ack = htonl(sched[sched_index].curr_lack);
fix_all_checksum_liveplay(sched[sched_index].iphdr);
}
/* If 3 attempts of resending was made, then error out to the user */
if(sched[sched_index].sent_counter==3){
printf("\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ ERROR: Re-sent packet [%-u] 3 times, but remote host is not +\n", sched_index+1);
printf("+ responding as expected. 3 resend attempts are a maximum. +\n");
printf("+ Closing replay... +\n");
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n");
break;
}
/* If nothing goes wrong, then send the packet scheduled to be sent, then proceed in the schedule */
sendpacket(sp, sched[sched_index].packet_ptr, sched[sched_index].pkthdr.len, &sched[sched_index].pkthdr);
sched[sched_index].sent_counter++; /* Keep track of how many times this specific packet was attempted */
sched_index++; /* proceed */
}
/* Remote Packets */
else if(sched[sched_index].remote){
alarm (ALARM_TIMEOUT);
printf("Receiving Packets from remote host...\n");
pcap_dispatch(live_handle, 1, got_packet, NULL); /* Listen in on NIC for tcp packets */
//printf("pcap_loop returned\n");
}
} /* end of main while loop*/
pcap_breakloop(live_handle);
pcap_close(live_handle);
sendpacket_close(sp); /* Close Send socket*/
remove("newfile.pcap"); /* Remote the rewritten file that was created*/
for(k=0; k<pkts_scheduled; k++){
retransmissions+=sched[k].sent_counter;
}
/* User Debug Result Printouts*/
if(sched_index==pkts_scheduled){
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("~ CONGRATS!!! You have successfully Replayed your pcap file '%s'\n", argv[2]);
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n");
}
else {
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
printf("~ Unfortunately an error has occurred halting the replay of\n");
printf("~ the pcap file '%s'. Please see error above for details...\n", argv[2]);
printf("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n");
}
printf("----------------TCP Live Play Summary----------------\n");
printf("- Packets Scheduled to be Sent & Received: %-u\n", pkts_scheduled);
printf("- Actual Packets Sent & Received: %-u\n", sched_index);
printf("- Total Local Packet Re-Transmissions due to packet\n");
printf("- loss and/or differing payload size than expected: %-u\n", retransmissions);
printf("- Thank you for Playing, Play again!\n");
printf("----------------------------------------------------------\n\n");
free(sched);
restore_stdin();
return 0;
}
/*end of main() function*/
/**
* This function serves as a timer alarm
*/
void
catch_alarm (int sig){
keep_going = 0;
signal (sig, catch_alarm);
}
static int tcplp_rand(void)
{
struct timeval tv;
if (!seed) {
gettimeofday(&tv, NULL);
seed = (unsigned int)tv.tv_sec ^ (unsigned int)tv.tv_usec;
}
return tcpr_random(&seed);
}
/**
* This function returns a random number between 49152 and 65535
*/
int
random_port()
{
int random = tcplp_rand();
return (49152 + (random % 16383));
}
/**
* This function sets up the scheduled local ACK and Remote SEQ to be relative numbers,
* While it sets up the local SEQs and remote ACKs to be absolute within the schedule.
*/
int
relative_sched(struct tcp_sched* sched, u_int32_t first_rseq, int num_packets){
int i;
u_int32_t lseq_adjust = tcplp_rand();
printf("Random Local SEQ: %u\n",lseq_adjust);
u_int32_t first_lseq = sched[0].curr_lseq; /* SYN Packet SEQ number */
/* Fix schedule to relative and absolute */
for(i = 0; i < num_packets; i++){
if(sched[i].local){
sched[i].curr_lseq = sched[i].curr_lseq - first_lseq; /* Fix current local SEQ to relative */
sched[i].curr_lseq = sched[i].curr_lseq + lseq_adjust; /* Make absolute. lseq_adjust is the locally generated random number */
sched[i].curr_lack = sched[i].curr_lack - first_rseq; /* Fix current local ACK to relative */
if (sched[i].tcphdr)
sched[i].tcphdr->th_seq = htonl(sched[i].curr_lseq); /* Edit the actual packet header data */
fix_all_checksum_liveplay(sched[i].iphdr); /* Fix the checksum */
sched[i].exp_rseq = sched[i].exp_rseq - first_rseq;
sched[i].exp_rack = sched[i].exp_rack - first_lseq;
sched[i].exp_rack = sched[i].exp_rack + lseq_adjust;
}
else if(sched[i].remote){
sched[i].exp_rseq = sched[i].exp_rseq - first_rseq; /* Fix expected remote SEQ to be relative */
sched[i].exp_rack = sched[i].exp_rack - first_lseq; /* Fix expected remote ACK to be relative*/
sched[i].exp_rack = sched[i].exp_rack + lseq_adjust; /* Fix expected remote ACK to be absolute */
}
}
return SUCCESS;
}
/**
* This function sets up the schedule for the rest of the program
* extracting all the needed information from the given pcap file
* and coping into memory (into a struct format)
*
*/
int
setup_sched(struct tcp_sched* sched){
input_addr sip, dip; /* Source & Destination IP */
input_addr local_ip, remote_ip; /* ip address of client and server*/
pcap_t *local_handle;
const u_char *packet; /* The actual packet */
unsigned int flags=0;
struct pcap_pkthdr header; // The header that pcap gives us
unsigned int pkt_counter=0;
bool remote = false; /* flags to test if data is from 'client'=local or 'server'=remote */
bool local = false;
unsigned int i = 0;
local_ip.byte1=0;
local_ip.byte2=0;
local_ip.byte3=0;
local_ip.byte4=0;
remote_ip.byte1=0;
remote_ip.byte2=0;
remote_ip.byte3=0;
remote_ip.byte4=0;
char errbuf[PCAP_ERRBUF_SIZE];
local_handle = pcap_open_offline("newfile.pcap", errbuf); /*call pcap library function*/
if (local_handle == NULL) {
fprintf(stderr,"Couldn't open pcap file %s: %s\n", "newfile.pcap", errbuf);
return(2);
}
/*Before sending any packet, setup the schedule with the proper parameters*/
while((packet = safe_pcap_next(local_handle, &header))) {
/*temporary packet buffers*/
ether_hdr *etherhdr;
tcp_hdr *tcphdr;
ipv4_hdr *iphdr;
unsigned int size_ip;
unsigned int size_tcp;
unsigned int size_payload;
pkt_counter++; /*increment number of packets seen*/
memcpy(&sched[i].pkthdr, &header, sizeof(struct pcap_pkthdr));
sched[i].packet_ptr = safe_malloc(sched[i].pkthdr.len);
memcpy(sched[i].packet_ptr, packet, sched[i].pkthdr.len);
/* extract necessary data */
etherhdr = (ether_hdr*)(sched[i].packet_ptr);
iphdr = (ipv4_hdr *)(sched[i].packet_ptr + SIZE_ETHERNET);
size_ip = iphdr->ip_hl << 2;
if (size_ip < 20) {
printf("ERROR: Invalid IP header length: %u bytes\n", size_ip);
return 0;
}
tcphdr = (tcp_hdr *)(sched[i].packet_ptr + SIZE_ETHERNET + size_ip);
size_tcp = tcphdr->th_off*4;
if (size_tcp < 20) {
printf("ERROR: Invalid TCP header length: %u bytes\n", size_tcp);
return 0;
}
/* payload = (u_char *)(sched[i].packet_ptr + SIZE_ETHERNET + size_ip + size_tcp); */
size_payload = ntohs(iphdr->ip_len) - (size_ip + (size_tcp));
/* Source IP and Destination IP */
sip = iphdr->ip_src;
dip = iphdr->ip_dst;
flags = tcphdr->th_flags;
if (flags == TH_SYN){ /* set IPs who's local and who's remote based on the SYN flag */
local_ip = sip;
remote_ip = dip;
}
/*Compare IPs to see which packet is this coming from*/
if(compip(&local_ip, &remote_ip, &sip)==LOCAL_IP_MATCH){
local = true;
remote = false;
}
if(compip(&local_ip, &remote_ip, &sip)==REMOTE_IP_MATCH){
local = false;
remote = true;
}
/* Setup rest of Schedule, parameter by parameter */
/* Refer to header file for details on each of the parameters */
sched[i].etherhdr = etherhdr;
sched[i].iphdr = iphdr;
sched[i].tcphdr = tcphdr;
sched[i].size_ip = size_ip;
sched[i].size_tcp = size_tcp;
sched[i].size_payload = size_payload;
sched[i].sent_counter = 0;
/* Do the following only for the first packet (SYN)*/
if(i==0){
sched[i].length_last_ldata = 0;
sched[i].length_curr_ldata = 0;
sched[i].length_last_rdata = 0;
sched[i].length_curr_rdata = 0;
sched[i].local = true;
sched[i].remote = false;
sched[i].curr_lseq = ntohl(sched[i].tcphdr->th_seq);
sched[i].curr_lack = 0;
sched[i].exp_rseq = 0; /* Keep track of previous remote seq & ack #s*/
sched[i].exp_rack = 0;
}
/* Local Packet operations */
else if(local){
sched[i].length_last_ldata = sched[i-1].length_curr_ldata;
sched[i].length_curr_ldata = size_payload;
sched[i].length_last_rdata = sched[i-1].length_curr_rdata;
sched[i].length_curr_rdata = 0;
sched[i].local = true;
sched[i].remote = false;
sched[i].curr_lseq = ntohl(sched[i].tcphdr->th_seq);
sched[i].curr_lack = ntohl(sched[i].tcphdr->th_ack);
sched[i].exp_rseq = sched[i-1].exp_rseq; /* Keep track of previous remote seq & ack #s*/
sched[i].exp_rack = sched[i-1].exp_rack;
}
/* Remote Packet operations */
else if(remote){
sched[i].length_last_ldata = sched[i-1].length_curr_ldata;
sched[i].length_curr_ldata = 0;
sched[i].length_last_rdata = sched[i-1].length_curr_rdata;
sched[i].length_curr_rdata = size_payload;
sched[i].local = false;
sched[i].remote = true;
sched[i].curr_lseq = sched[i-1].curr_lseq;
sched[i].curr_lack = sched[i-1].curr_lack;
sched[i].exp_rseq = ntohl(sched[i].tcphdr->th_seq); /* Keep track of previous remote seq & ack #s*/
sched[i].exp_rack = ntohl(sched[i].tcphdr->th_ack);
/* Setup global variable where remote FIN-ACK exists*/
if(flags == (TH_FIN|TH_ACK)) finack_rindex = i;
//printf("REMOTE --------%d\n",i+1);
}
i++; /* increment schedule index */
} /*end internal loop for reading packets (all in one file)*/
pcap_close(local_handle); /*close the pcap file*/
return pkt_counter; /* Return number of packets scheduled */
}
/**
* This function returns a pcap_t for the live traffic handler which
* filters out traffic other than TCP
*
*/
pcap_t*
set_live_filter(char *dev, input_addr* hostip, unsigned int port)
{
pcap_t *handle = NULL; /* Session handle */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string buffer */
struct bpf_program fp; /* The compiled filter */
char filter_exp[52];
sprintf(filter_exp,"tcp and dst host %d.%d.%d.%d and dst port %u",
hostip->byte1, hostip->byte2, hostip->byte3, hostip->byte4, port); /* The filter expression */
bpf_u_int32 mask; /* Our network mask */
bpf_u_int32 net; /* Our IP */
/* Define the device */
if (dev == NULL) {
fprintf(stderr, "Couldn't find default device: %s\n", errbuf);
return handle;
}
/* Find the properties for the device */
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
/* Open the session in promiscuous mode */
handle = pcap_open_live(dev, BUFSIZ_PLUS, PROMISC_OFF, TIMEOUT_ms, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
return handle;
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return handle;
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return handle;
}
pcap_freecode(&fp);
return handle;
}
/**
* This function returns a pcap_t for the savedfile traffic handler which
* filters out traffic other than TCP
*
*/
pcap_t*
set_offline_filter(char* file)
{
pcap_t *handle; /* Session handle */
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */
struct bpf_program fp; /* The compiled filter */
char filter_exp[] = "tcp";
bpf_u_int32 net=0; /* Our IP */
/* Open savedfile */
handle = pcap_open_offline(file, errbuf);
if (handle == NULL) {
fprintf(stderr, "Couldn't open file %s\n", errbuf);
return handle;
}
/* Compile and apply the filter */
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return handle;
}
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return handle;
}
pcap_freecode(&fp);
return handle;
}
/**
* This is the callback function for pcap_loop
* This function is called every time we receive a remote packet
*/
void
got_packet(_U_ u_char *args, _U_ const struct pcap_pkthdr *header,
const u_char *packet)
{
ether_hdr *etherhdr;
tcp_hdr *tcphdr;
ipv4_hdr *iphdr;
unsigned int size_ip, size_tcp, size_payload;
unsigned int flags = 0;
/* Extract and examine received packet headers */
etherhdr = (ether_hdr*)(packet);
iphdr = (ipv4_hdr *)(packet + SIZE_ETHERNET);
size_ip = iphdr->ip_hl << 2;
if (size_ip < 20) {
printf("ERROR: Invalid IP header length: %u bytes\n", size_ip);
return;
}
tcphdr = (tcp_hdr *)(packet + SIZE_ETHERNET + size_ip);
size_tcp = tcphdr->th_off*4;
if (size_tcp < 20) {
printf("ERROR: Invalid TCP header length: %u bytes\n", size_tcp);
return;
}
size_payload = ntohs(iphdr->ip_len) - (size_ip + (size_tcp));
flags = tcphdr->th_flags;
/* Check correct SYN-ACK expectation, if so then proceed in fixing entire schedule from relative to absolute SEQs+ACKs */
if((flags == (TH_SYN|TH_ACK)) && (sched_index==1) && (tcphdr->th_ack==htonl(sched[sched_index-1].curr_lseq + 1))){
unsigned int j;
printf("Received Remote Packet............... [%u]\n", sched_index + 1);
printf("Remote Pakcet Expectation met.\nProceeding in replay....\n");
//printf("SYN-ACKed Random SEQ set!\n");
initial_rseq = ntohl(tcphdr->th_seq);
//printf("initial_rseq: %u\n", initial_rseq);
/* After we receiving the first SYN-ACK, then adjust the entire sched to be absolute rather than relative #s*/
sched[1].exp_rseq = sched[1].exp_rseq + initial_rseq;
for(j = 2; j<pkts_scheduled; j++){ /* Based on correctly receiving the random SEQ from the SYN-ACK packet, do the following:*/
if(sched[j].local){ /* Set local ACKs for entire sched to be absolute #s*/
sched[j].curr_lack = sched[j].curr_lack + initial_rseq;
}
else if(sched[j].remote){ /* Set remote SEQs for entire sched to be absolute #s*/
sched[j].exp_rseq = sched[j].exp_rseq + initial_rseq;
}
}
sched_index++; /* Proceed in the schedule*/
return;
}
printf(">Received a Remote Packet\n");
printf(">>Checking Expectations\n");
/* Handle Remote Packet Loss */
if(sched[sched_index].exp_rack > ntohl(tcphdr->th_ack)) {
//printf("Remote Packet Loss! Resending Lost packet\n");
sched_index=acked_index; /* Reset the schedule index back to the last correctly ACKed packet */
//printf("ACKED Index = %d\n", acked_index);
while(!sched[sched_index].local){
sched_index++;
}
return;
}
/* Handle Local Packet Loss <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<COME BACK TO THIS<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
else if((sched[sched_index].exp_rseq < ntohl(tcphdr->th_seq)) && sched[sched_index].remote){
/* Resend immediate previous LOCAL packet */
printf("Local Packet Loss! Resending Lost packet >> DupACK Issued!\n");
sched_index=acked_index; /* Reset the schedule index back to the last correctly ACKed packet */
/*sched[sched_index].sent_counter=0; Reset the re-transmission counter for this ACKed packet?*/
//printf("ACKED Index = %d\n", acked_index);
while(!sched[sched_index].local){
sched_index++;
}
return;
}
/* No Packet Loss... Proceed Normally (if expectations are met!) */
else if((tcphdr->th_seq==htonl(sched[sched_index].exp_rseq)) &&
(tcphdr->th_ack==htonl(sched[sched_index].exp_rack))){
printf("Received Remote Packet............... [%d]\n",sched_index+1);
/* Handles differing payload size and does not trigger on unnecessary ACK + window update issues*/
if((sched[sched_index].size_payload!=size_payload) && (size_payload!=0)){
printf("Payload size of received packet does not meet expectations\n");
/* Resent last local packet, maybe remote host behaves this time*/
different_payload=true;
/* Set global variable of where differing payload size is not meeting expectations*/
diff_payload_index = sched_index;
/*Treat this as packet loss, and attempt resetting index to resend packets where*/
/* packets were received matching expectation*/
sched_index=acked_index; /* Reset the schedule index back to the last correctly ACKed packet */
//printf("ACKED Index = %d\n", acked_index);
while(!sched[sched_index].local){
sched_index++;
}
return;
}
printf("Remote Packet Expectation met.\nProceeding in replay....\n");
sched_index++;
acked_index = sched_index; /*Keep track correctly ACKed packet index*/
}
/* Global variable to keep tack of last received packet info */
packet_keeper_rprev = packet;
etherhdr_rprev = etherhdr;
tcphdr_rprev = tcphdr;
iphdr_rprev = iphdr;
size_payload_prev = size_payload;
return;
}
/**
* This function prints out the ip address, given an in_addr struct pointer
*
*/
void
printip(input_addr* IP)
{
printf("[%d.%d.%d.%d]\n", IP->byte1, IP->byte2, IP->byte3, IP->byte4);
return;
}
/**
* This function prints out the mac address, given a mac_addr struct pointer
*
*/
void
printmac(struct mac_addr* MAC)
{
printf("[%x:%x:%x:%x:%x:%x]\n", MAC->byte1, MAC->byte2, MAC->byte3, MAC->byte4, MAC->byte5, MAC->byte6);
return;
}
/**
* This function compares two IPs,
* returns 1 if match with local ip
* returns 2 if matches with remote ip
* returns 0 if no match
*
*/
int
compip(input_addr* lip, input_addr* rip, input_addr* pkgip)
{
if((lip->byte1==pkgip->byte1)&&(lip->byte2==pkgip->byte2)&&(lip->byte3==pkgip->byte3)&&(lip->byte4==pkgip->byte4))
return LOCAL_IP_MATCH;
else if((rip->byte1==pkgip->byte1)&&(rip->byte2==pkgip->byte2)&&(rip->byte3==pkgip->byte3)&&(rip->byte4==pkgip->byte4))
return REMOTE_IP_MATCH;
else
return NO_MATCH;
}
/**
* This function sets the IP and MAC of a given interface (i.e. eth0)
* into in_addr & mac_addr struct pointers
*
*/
int iface_addrs(char* iface, input_addr* ip, struct mac_addr* mac)
{
int s;
struct ifreq buffer;
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
memset(&buffer, 0x00, sizeof(buffer));
strncpy(buffer.ifr_name, iface, sizeof(buffer.ifr_name)-1);
int res;
if ((res = ioctl(s, SIOCGIFADDR, &buffer)) < 0)
goto done;
struct in_addr localip = ((struct sockaddr_in *)&buffer.ifr_addr)->sin_addr;
#if defined( WORDS_BIGENDIAN )
ip->byte1 = (localip.s_addr)>>24;
ip->byte2 = ((localip.s_addr)>>16)&255;
ip->byte3 = ((localip.s_addr)>>8)&255;
ip->byte4 = (localip.s_addr)&255;
#else
ip->byte4 = (localip.s_addr)>>24;
ip->byte3 = ((localip.s_addr)>>16)&255;
ip->byte2 = ((localip.s_addr)>>8)&255;
ip->byte1 = (localip.s_addr)&255;
#endif
if ((res = ioctl(s, SIOCGIFHWADDR, &buffer)) < 0)
goto done;
mac->byte1 = buffer.ifr_hwaddr.sa_data[0];
mac->byte2 = buffer.ifr_hwaddr.sa_data[1];
mac->byte3 = buffer.ifr_hwaddr.sa_data[2];
mac->byte4 = buffer.ifr_hwaddr.sa_data[3];
mac->byte5 = buffer.ifr_hwaddr.sa_data[4];
mac->byte6 = buffer.ifr_hwaddr.sa_data[5];
done:
close(s);
return res;
}
/**
* This function rewrites the IPs and MACs of a given packet,
* creates a newfile.pcap. It returns the number of packets of the newfile.
* This function only starts rewriting the newfile once it sees the first
* SYN packet. This is so that the first packet in the newfile is always
* the first packet to be sent.
*/
int
rewrite(input_addr* new_remoteip, struct mac_addr* new_remotemac, input_addr* myip, struct mac_addr* mymac, char* file, unsigned int new_src_port)
{
char *newfile = "newfile.pcap";
int pkt_counter;
input_addr local_ip;
input_addr remote_ip;
const u_char *packet;
struct pcap_pkthdr *header;
pcap_dumper_t *dumpfile;
input_addr sip; /* Source IP */
int local_packets = 0;
bool initstep1 = false; /* keep track of successful handshake step */
bool warned = false;
local_ip.byte1=0;
local_ip.byte2=0;
local_ip.byte3=0;
local_ip.byte4=0;
remote_ip.byte1=0;
remote_ip.byte2=0;
remote_ip.byte3=0;
remote_ip.byte4=0;
pcap_t *pcap = set_offline_filter(file);
if (!pcap){
char ErrBuff [1024];
fprintf (stderr, "Cannot open PCAP file '%s' for reading\n", file);
fprintf(stderr, "%s\n",ErrBuff);
return PCAP_OPEN_ERROR;
}
dumpfile = pcap_dump_open(pcap, newfile);
if (!dumpfile) {
fprintf (stderr, "Cannot open PCAP file '%s' for writing\n", newfile);
return PCAP_OPEN_ERROR;
}
/*Modify each packet's IP & MAC based on the passed args then do a checksum of each packet*/
for (pkt_counter = 0; safe_pcap_next_ex(pcap, &header, &packet) > 0; pkt_counter++){
unsigned int flags, size_ip;
ether_hdr* etherhdr;
ipv4_hdr *iphdr;
tcp_hdr *tcphdr;
unsigned int size_tcp;
if (!warned && header->len > header->caplen) {
fprintf(stderr, "warning: packet capture truncated to %d byte packets\n",
header->caplen);
warned = true;
}
etherhdr = (ether_hdr*)(packet);
iphdr = (ipv4_hdr *)(packet + SIZE_ETHERNET);
size_ip = iphdr->ip_hl << 2;
if (size_ip < 20) {
printf("ERROR: Invalid IP header length: %u bytes\n", size_ip);
return ERROR;
}
tcphdr = (tcp_hdr *)(packet + SIZE_ETHERNET + size_ip);