int relative_sched(struct tcp_sched* sched, u_int32_t first_rseq, int num_packets) { int i; u_int32_t lseq_adjust; srand(time(NULL)); lseq_adjust = rand(); /*Local SEQ number for SYN packet*/ 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 */ 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 expeted remote ACK to be absolute */ } } return SUCCESS; }
/** * 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(in_addr* new_remoteip, struct mac_addr* new_remotemac, in_addr* myip, struct mac_addr* mymac, char* file, unsigned int new_src_port) { ether_hdr* etherhdr; ipv4_hdr *iphdr; tcp_hdr *tcphdr; in_addr local_ip; in_addr remote_ip; unsigned int size_ip; unsigned int size_tcp; char* newfile = "newfile.pcap"; char ErrBuff [1024]; int pkt_counter, len; const u_char *packet; struct pcap_pkthdr *header; in_addr sip; /* Source IP */ unsigned int flags; int local_packets = 0; bool initstep1 = false; /* keep track of successful handshake step */ 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; /*Read the header of the PCAP*/ int fp_for_header = open(file,O_RDONLY); if(fp_for_header < 0) { fprintf(stderr, "Cannot open PCAP file to get file header.\n"); return PCAP_OPEN_ERROR; } u_int8_t data[sizeof(struct pcap_file_header)]; len = read(fp_for_header, data, sizeof(struct pcap_file_header)); if(len == 0) { fprintf(stderr, "Could not read from file.\n"); } close(fp_for_header); /*Open file for reading each packet*/ int fp = open(newfile,O_CREAT|O_WRONLY,S_IRWXU); if(fp < 0) { fprintf(stderr, "Cannot open file: %s for writing.\n",newfile); return PCAP_OPEN_ERROR; } /* Write the header to new file */ len = write(fp, data,sizeof(struct pcap_file_header)); pcap_t *pcap = set_offline_filter(file); if (!pcap) { fprintf (stderr, "Cannot open PCAP file '%s'\n", file); fprintf(stderr, "%s\n",ErrBuff); 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; pcap_next_ex(pcap, &header, &packet) > 0; pkt_counter++) { 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); size_tcp = tcphdr->th_off*4; if (size_tcp < 20) { printf("ERROR: Invalid TCP header length: %u bytes\n", size_tcp); return ERROR; } /* payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp); */ sip = iphdr->ip_src; flags = tcphdr->th_flags; /* set IPs who's local and who's remote based on the SYN flag */ if(flags == TH_SYN) { local_ip = iphdr->ip_src; remote_ip = iphdr->ip_dst; initstep1 = true; /* This flag is set to signify the first encounter of the SYN within the pacp*/ } if(compip(&local_ip, &remote_ip, &sip)==LOCAL_IP_MATCH) { /* Set the source MAC */ etherhdr->ether_shost[0] = mymac->byte1; etherhdr->ether_shost[1] = mymac->byte2; etherhdr->ether_shost[2] = mymac->byte3; etherhdr->ether_shost[3] = mymac->byte4; etherhdr->ether_shost[4] = mymac->byte5; etherhdr->ether_shost[5] = mymac->byte6; /* Set the source IP */ iphdr->ip_src = *myip; /* Set the destination IP */ iphdr->ip_dst = *new_remoteip; /* Set the destination MAC */ etherhdr->ether_dhost[0] = new_remotemac->byte1; etherhdr->ether_dhost[1] = new_remotemac->byte2; etherhdr->ether_dhost[2] = new_remotemac->byte3; etherhdr->ether_dhost[3] = new_remotemac->byte4; etherhdr->ether_dhost[4] = new_remotemac->byte5; etherhdr->ether_dhost[5] = new_remotemac->byte6; /* This is to change the source port, whether it is specified as random or as a port # by the user */ tcphdr->th_sport = htons(new_src_port); } else if(compip(&local_ip, &remote_ip, &sip)==REMOTE_IP_MATCH) { /* Set the destination MAC */ etherhdr->ether_dhost[0] = mymac->byte1; etherhdr->ether_dhost[1] = mymac->byte2; etherhdr->ether_dhost[2] = mymac->byte3; etherhdr->ether_dhost[3] = mymac->byte4; etherhdr->ether_dhost[4] = mymac->byte5; etherhdr->ether_dhost[5] = mymac->byte6; /* Set the destination IP */ iphdr->ip_dst = *myip; /* Set the source IP */ iphdr->ip_src = *new_remoteip; /* Set the source MAC */ etherhdr->ether_shost[0] = new_remotemac->byte1; etherhdr->ether_shost[1] = new_remotemac->byte2; etherhdr->ether_shost[2] = new_remotemac->byte3; etherhdr->ether_shost[3] = new_remotemac->byte4; etherhdr->ether_shost[4] = new_remotemac->byte5; etherhdr->ether_shost[5] = new_remotemac->byte6; /* This is to change the source port, whether it is specified as random or as a port # by the user */ tcphdr->th_dport = htons(new_src_port); } /*Calculate & fix checksum for newly edited-packet*/ fix_all_checksum_liveplay(iphdr); if(initstep1) { /*only start rewriting new pcap with SYN packets on wards*/ local_packets ++; len = write(fp,header,16); if(len == 0) { fprintf(stderr, "Error occurred writing pcap_header.\n"); pcap_close(pcap); close(fp); return REWRITE_ERROR; } len = write(fp,packet,header->caplen); if(len == 0) { fprintf(stderr, "Error occurred writing pcap data.\n"); pcap_close(pcap); close(fp); return REWRITE_ERROR; } } } /* end of while loop */ pcap_close (pcap); close(fp); return local_packets; }
/** * 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; unsigned int num_packets=0; char port_mode[10]; /* does user specify random port generation?*/ char random_strg[7] = "random"; char* iface = argv[1]; char* new_rmac_ptr; char* new_rip_ptr; in_addr new_remoteip; struct mac_addr new_remotemac; in_addr myip; struct mac_addr mymac; /*temporary packet buffers*/ unsigned int new_src_port = 0; unsigned int retransmissions = 0; pcap_t *local_handle; char errbuf[PCAP_ERRBUF_SIZE]; char ebuf[SENDPACKET_ERRBUF_SIZE]; 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> <Destinatin 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); } iface_addrs(iface, &myip, &mymac); /* Extract MAC of interface replay is being request on */ /* open send function socket*/ if ((sp = sendpacket_open(iface, ebuf, TCPR_DIR_C2S, SP_TYPE_NONE)) == NULL) errx(-1, "Can't open %s: %s", argv[1], ebuf); /* random dport vs. specified dport operation*/ strcpy(port_mode, argv[5]); /*for(int i = 0; i<10; i++) tolower(port_mode[i]);*/ if(strcmp(port_mode, random_strg)==0) { new_src_port = random_port(); } else new_src_port = atoi(argv[5]); /*else { printf("Port specification error. Please specify 'random' for random source port generation between 49152 and 65535 OR specifiy a specific source port number.\n"); return; }*/ 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 inputted 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 */ extmac(new_rmac_ptr, &new_remotemac); extip(new_rip_ptr, &new_remoteip); /* 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); /* create schedule & set it up */ sched = (struct tcp_sched*) malloc(num_packets*sizeof(struct tcp_sched)); pkts_scheduled = setup_sched(sched); /* Returns number of packets in schedule*/ /* Set up the schedule struct to be relative numbers rather than absolute*/ relative_sched(sched, sched[1].exp_rseq, num_packets); printf("Packets Scheduled %d\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); 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 occured while listing on traffic: %s\n", errbuf); return(2); } /* Printout when no packets are scheduled */ if(pkts_scheduled==0) { printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); printf("+ ERROR:: There are no TCP packets to sent +\n"); printf("+ Closing replay... +\n"); printf("+ Thank you for Playing, Play again! +\n"); printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n"); 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............... [%d]\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............... [%d]\n",sched_index+1); printf("Skipping Packet...................... [%d] to Packet [%d]\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 %-d. 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............... [%d]\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 [%-d] 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: %-d \n", pkts_scheduled); printf("- Actual Packets Sent & Received: %-d \n", sched_index); printf("- Total Local Packet Re-Transmissions due to packet \n"); printf("- loss and/or differing payload size than expected: %-d \n", retransmissions); printf("- Thank you for Playing, Play again! \n"); printf("----------------------------------------------------------\n\n"); return 0; }
/** * 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) { ether_hdr* etherhdr; ipv4_hdr *iphdr; tcp_hdr *tcphdr; input_addr local_ip; input_addr remote_ip; unsigned int size_ip; unsigned int size_tcp; char* newfile = "newfile.pcap"; char ErrBuff [1024]; int pkt_counter; const u_char *packet; struct pcap_pkthdr *header; pcap_dumper_t *dumpfile; input_addr sip; /* Source IP */ unsigned int flags; 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){ 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; pcap_next_ex(pcap, &header, &packet) > 0; pkt_counter++){ 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); size_tcp = tcphdr->th_off*4; if (size_tcp < 20) { printf("ERROR: Invalid TCP header length: %u bytes\n", size_tcp); return ERROR; } /* payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp); */ sip = iphdr->ip_src; flags = tcphdr->th_flags; /* set IPs who's local and who's remote based on the SYN flag */ if(flags == TH_SYN){ local_ip = iphdr->ip_src; remote_ip = iphdr->ip_dst; initstep1 = true; /* This flag is set to signify the first encounter of the SYN within the pacp*/ } if(compip(&local_ip, &remote_ip, &sip)==LOCAL_IP_MATCH){ /* Set the source MAC */ etherhdr->ether_shost[0] = mymac->byte1; etherhdr->ether_shost[1] = mymac->byte2; etherhdr->ether_shost[2] = mymac->byte3; etherhdr->ether_shost[3] = mymac->byte4; etherhdr->ether_shost[4] = mymac->byte5; etherhdr->ether_shost[5] = mymac->byte6; /* Set the source IP */ iphdr->ip_src = *myip; /* Set the destination IP */ iphdr->ip_dst = *new_remoteip; /* Set the destination MAC */ etherhdr->ether_dhost[0] = new_remotemac->byte1; etherhdr->ether_dhost[1] = new_remotemac->byte2; etherhdr->ether_dhost[2] = new_remotemac->byte3; etherhdr->ether_dhost[3] = new_remotemac->byte4; etherhdr->ether_dhost[4] = new_remotemac->byte5; etherhdr->ether_dhost[5] = new_remotemac->byte6; /* This is to change the source port, whether it is specified as random or as a port # by the user */ tcphdr->th_sport = htons(new_src_port); } else if(compip(&local_ip, &remote_ip, &sip)==REMOTE_IP_MATCH){ /* Set the destination MAC */ etherhdr->ether_dhost[0] = mymac->byte1; etherhdr->ether_dhost[1] = mymac->byte2; etherhdr->ether_dhost[2] = mymac->byte3; etherhdr->ether_dhost[3] = mymac->byte4; etherhdr->ether_dhost[4] = mymac->byte5; etherhdr->ether_dhost[5] = mymac->byte6; /* Set the destination IP */ iphdr->ip_dst = *myip; /* Set the source IP */ iphdr->ip_src = *new_remoteip; /* Set the source MAC */ etherhdr->ether_shost[0] = new_remotemac->byte1; etherhdr->ether_shost[1] = new_remotemac->byte2; etherhdr->ether_shost[2] = new_remotemac->byte3; etherhdr->ether_shost[3] = new_remotemac->byte4; etherhdr->ether_shost[4] = new_remotemac->byte5; etherhdr->ether_shost[5] = new_remotemac->byte6; /* This is to change the source port, whether it is specified as random or as a port # by the user */ tcphdr->th_dport = htons(new_src_port); } /*Calculate & fix checksum for newly edited-packet*/ fix_all_checksum_liveplay(iphdr); if(initstep1){ /*only start rewriting new pcap with SYN packets on wards*/ local_packets ++; pcap_dump((u_char *)dumpfile, header, packet); } } /* end of while loop */ pcap_close (pcap); pcap_dump_close(dumpfile); return local_packets; }