1 /******************************************************************************
2 Utility to convert a LTP flow to a TCP flow for LTP analysis via tcptrace.
4 Copyright (C) 2013 Samuel Jero <sj323707@ohio.edu>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 Author: Samuel Jero <sj323707@ohio.edu>
23 1)Only handles one LTP "connection". There isn't a good way to separate
24 different LTP "connections" from new sessions of the same "connection".
25 Use Tcpdump filters to separate connections. Libpcap filtering could also
27 2)Uses some special types from Linux (u_char, u_int32_t)
28 ******************************************************************************/
34 int MAX_SESSION=500000;
38 /* Forward declarations*/
41 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
42 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength);
43 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf);
44 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
45 int claim2sack(const u_char* odata, int ses_id, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
46 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
47 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
50 /*Parse commandline options and open files*/
51 int main(int argc, char *argv[])
61 /*parse commandline options*/
62 if(argc<4 || argc > 8){
63 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
67 /*loop through commandline options*/
68 for(int i=1; i < argc; i++){
70 if(lfile==NULL){ /*assign first non-dash argument to the ltp file*/
74 tfile=argv[i]; /*assign second non-dash argument to the dccp file*/
76 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
81 if(argv[i][1]=='d' && strlen(argv[i])==2){ /*debug option*/
84 if(argv[i][1]=='t'){ /*Encapsulation option*/
87 if(argv[i][1]=='b'){ /* Block size option*/
88 tmp=atoi(&argv[i][2]);
92 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
96 if(argv[i][1]=='s'){ /*Session range option*/
97 state.ses_min=strtol(&argv[i][2],&temp,10);
99 state.ses_max=strtol(temp,NULL,10);
104 if(lfile==NULL || tfile==NULL || type==NULL){
105 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
109 if(state.ses_min<=0 || state.ses_max<=0){
114 /*all options validated*/
117 dbgprintf(1,"Debug On\n");
118 dbgprintf(1,"Input file: %s\n", lfile);
119 dbgprintf(1,"Output file: %s\n", tfile);
120 dbgprintf(1,"Encapsulation: %s\n", type);
121 dbgprintf(1,"Block Size: %i\n", MAX_SESSION);
122 if(state.ses_min>=0){
123 dbgprintf(1, "Session Range: %i-%i\n", state.ses_min, state.ses_max);
125 dbgprintf(1, "Session Range: all\n");
129 /*determine encapsulation type*/
132 /*attempt to open input file*/
133 state.in=pcap_open_offline(lfile, erbuffer);
135 printf("Error opening input file\n");
139 /*attempt to open output file*/
140 state.out=pcap_dump_open(state.in,tfile);
142 printf("Error opening output file\n");
152 u_char *user=(u_char*)state.out;
153 pcap_loop(state.in, -1, handle_packet, user);
155 /*add correct TCP termination, if needed*/
156 if(state.en_ops && state.en_ops->fin){
157 (*state.en_ops->fin)();
162 pcap_close(state.in);
163 pcap_dump_close(state.out);
167 /*initialize the converter state*/
175 state.ses.size=TBL_SZ;
179 state.ses.table=malloc(TBL_SZ*sizeof(struct tbl));
180 if(state.ses.table==NULL){
181 dbgprintf(0,"Error: Couldn't allocate Memory!\n");
193 /*clean up the session table*/
196 /*cleanup the claims in each session*/
197 for(int i=0; i < MAX_SESSION; i ++){
198 if(state.ses.table[i].a_lst!=NULL){
199 tmp=state.ses.table[i].a_lst;
208 free(state.ses.table);
213 /*call back function for pcap_loop--do basic packet handling*/
214 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
218 const u_char *optr=bytes;
222 struct pcap_pkthdr nh;
224 /*create new libpcap header*/
225 memcpy(&nh, h, sizeof(struct pcap_pkthdr));
227 /*create buffer for new packet*/
228 nptr=ndata=malloc(MAX_PACKET);
230 dbgprintf(0,"Error: Couldn't allocate Memory\n");
234 /*make sure the packet is all zero*/
235 memset(ndata, 0, MAX_PACKET);
239 /*handle encapsulations*/
240 if(state.en_ops && state.en_ops->pre && state.en_ops->post){
241 if((*state.en_ops->pre)(&nh, &optr, &nptr, &length, &nlength)<0){
244 tlen=convert_packet(&nh, optr, nptr, length, nlength);
249 if((*state.en_ops->post)(tlen, ndata)<0){
257 pcap_dump(user,&nh, ndata);
263 /*do all the ltp to tcp conversions---returns length of TCP segment*/
264 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength)
270 struct ltp_hdr_d ltph;
278 if(h==NULL || odata==NULL || ndata==NULL|| datalength<1|| newlength < sizeof(struct tcphdr)){
282 /*cast TCP header pointers*/
283 tcph=(struct tcphdr*)ncur;
284 tcpopt=ncur+ sizeof(struct tcphdr);
286 /*set TCP standard features*/
287 tcph->source=htons(1113);
288 tcph->dest=htons(1113);
290 tcph->check=htonl(0);
295 /*Grab LTP control/version byte*/
296 ltph.vers_ctl=ctl=*odata;
300 /* check LTP version*/
301 if((ctl & 0xF0) !=0){
302 dbgprintf(1,"Error: Invalid Packet Version\n");
306 /* Parse LTP Session ID*/
307 ltph.sender_id=evaluate_sdnv(odata, &eaten);
310 if(ltph.sender_id<0 || datalength<0){
311 dbgprintf(1,"Error: Bad SDNV!\n");
315 ltph.session_num=evaluate_sdnv(odata, &eaten);
318 if(ltph.session_num<0 || datalength<0){
319 dbgprintf(1,"Error: Bad SDNV!\n");
324 if(state.ses_min>=0){
325 if((ltph.session_num < state.ses_min)|| (ltph.session_num > state.ses_max)){
326 dbgprintf(3, "Filtering session: %i\n", ltph.session_num);
332 /* We don't want to be confused if we have multiple LTP connections in this capture*/
333 if(state.sender_id==-1){
334 state.sender_id=ltph.sender_id;
336 if(state.sender_id!=ltph.sender_id){
337 dbgprintf(1,"Note: Packet from different Sender!!\n");
342 /*Parse and Remove LTP header extensions*/
344 ltph.extensions=*odata;
346 hext=ltph.extensions >> 4;
348 tmp=evaluate_sdnv(odata+1, &eaten);
349 if(tmp==0 || tmp < eaten || tmp < 0 || datalength < tmp){
350 dbgprintf(1,"Error: Packet has bad extension length!\n");
353 /* ignore all extensions*/
362 /*make changes by packet type*/
363 if((ctl & 0x0C)==0x0){//RED DATA
364 dbgprintf(2,"Packet Type: RED DATA\n");
366 /*Initialize the TCP connection, if this is the start*/
367 if(state.started==0){
369 if(state.en_ops && state.en_ops->handshake){
370 (*state.en_ops->handshake)(h);
374 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
378 /*Finalize TCP Header*/
385 if(newlength < dlen + tcph->doff*4){
386 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
391 memset(ndata + tcph->doff*4, 'A', dlen);
393 /*Adjust Lengths (packet and TCP segment)*/
394 h->len+=tcph->doff*4+dlen;
395 h->caplen+=tcph->doff*4+dlen;
396 return tcph->doff*4+dlen;
399 if((ctl & 0x0C)==0x4){//GREEN DATA
400 dbgprintf(2,"Packet Type: GREEN DATA\n");
402 /*Initialize the TCP connection, if this is the start*/
403 if(state.started==0){
405 if(state.en_ops && state.en_ops->handshake){
406 (*state.en_ops->handshake)(h);
410 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
414 /*Finalize TCP Header*/
421 if(newlength < dlen + tcph->doff*4){
422 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
427 memset(ndata+ tcph->doff*4, 'A', dlen);
429 /*Adjust Lengths (packet and TCP segment)*/
430 h->len+=tcph->doff*4+dlen;
431 h->caplen+=tcph->doff*4+dlen;
432 return tcph->doff*4+dlen;
435 if((ctl & 0x0F)==0x8){//REPORT SEGMENT
436 dbgprintf(2,"Packet Type: REPORT SEGMENT\n");
439 if(newlength < 4*8+4+sizeof(struct tcphdr)){
440 dbgprintf(1, "Error: Indicated Packet Length is may be beyond end of buffer!\n");
444 if(handle_report(odata, <ph, tcph, tcpopt, &dlen, datalength)<0){
448 /*Finalize TCP Header*/
455 if(newlength < tcph->doff*4){
456 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
460 /*Adjust Lengths (packet and TCP segment)*/
461 h->len+=tcph->doff*4+dlen;
462 h->caplen+=tcph->doff*4+dlen;
463 return tcph->doff*4+dlen;
466 if((ctl & 0x0F)==0x9){//REPORT ACK
467 dbgprintf(2,"Packet Type: REPORT ACK\n");
469 /*Get Seq and Ack Numbers*/
470 if(seq_from_session(<ph, tcph)<0){
473 tcph->ack_seq=htonl(state.ack_num);
475 /*Finalize TCP Header*/
476 tcph->window=htons(WIN_FACTOR);
483 if(newlength < tcph->doff*4){
484 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
488 /*Adjust Lengths (packet and TCP segment)*/
489 h->len+=sizeof(struct tcphdr);
490 h->caplen+=sizeof(struct tcphdr);
491 return sizeof(struct tcphdr);
494 if((ctl & 0x0D)==0xC){ //CANCEL SEGMENT
495 dbgprintf(2,"Packet Type: CANCEL SEGMENT\n");
497 /*Get Seq and Ack Numbers*/
498 if((ctl & 0x02)==0x00){
500 if(seq_from_session(<ph, tcph)<0){
503 tcph->ack_seq=htonl(state.ack_num);
506 if(ack_from_session(<ph, tcph)<0){
509 tcph->seq=htonl(state.ack_num++);
512 /*Finalize TCP Header*/
513 tcph->window=htons(0);
520 if(newlength < tcph->doff*4){
521 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
525 /*Adjust Lengths (packet and TCP segment)*/
526 h->len+=sizeof(struct tcphdr);
527 h->caplen+=sizeof(struct tcphdr);
528 return sizeof(struct tcphdr);
531 if((ctl & 0x0D)==0xD){//CANCEL ACK
532 dbgprintf(2,"Packet Type: CANCEL ACK\n");
534 /*Get Seq and Ack Numbers*/
535 if((ctl & 0x02)==0x00){
537 if(seq_from_session(<ph, tcph)<0){
540 tcph->ack_seq=htonl(state.ack_num);
543 if(ack_from_session(<ph, tcph)<0){
546 tcph->seq=htonl(state.ack_num++);
549 /*Finalize TCP Header*/
550 tcph->window=htons(0);
557 if(newlength < tcph->doff*4){
558 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
562 /*Adjust Lengths (packet and TCP segment)*/
563 h->len+=sizeof(struct tcphdr);
564 h->caplen+=sizeof(struct tcphdr);
565 return sizeof(struct tcphdr);
568 /*Anything else is bad!*/
569 dbgprintf(1,"Error: Invalid Packet Type\n");
573 /* Determine Sequence and Acknowledgment Numbers for Data Segments*/
574 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf)
580 /* Get LTP Client Service ID*/
581 ltph->cls_id=evaluate_sdnv(odata, &eaten);
584 if(ltph->cls_id <0 || rbuf <0){
585 dbgprintf(1,"Error: Bad SDNV!\n");
589 /*Get LTP Data Offset*/
590 ltph->offset=evaluate_sdnv(odata, &eaten);
593 if(ltph->offset <0 || rbuf <0){
594 dbgprintf(1,"Error: Bad SDNV!\n");
598 /*Get LTP Data Length*/
599 ltph->length=evaluate_sdnv(odata, &eaten);
602 if(ltph->length <0 || rbuf <0){
603 dbgprintf(1,"Error: Bad SDNV!\n");
607 /* find LTP session */
609 for(i=0; i < state.ses.size; i++){
610 if(ltph->session_num==state.ses.table[i].num){
616 /* Not Found, add it*/
618 i=(state.seq_ses_id++)%state.ses.size;
619 state.ses.table[i].num=ltph->session_num;
620 state.ses.table[i].seq_id=state.seq_ses_id;
621 if(state.seq_ses_id==1){
623 state.ses.table[i].start=state.seq_num;
625 state.ses.table[i].start=state.seq_num + MAX_SESSION - state.ses.table[(state.seq_ses_id-2)%state.ses.size].length;
629 /* Compute TCP Sequence Number */
630 tcph->seq=htonl(state.ses.table[i].start + ltph->offset);
631 if(state.ses.table[i].start + ltph->offset+ltph->length > state.seq_num){
632 state.seq_num=state.ses.table[i].start + ltph->offset + ltph->length;
635 /* Update LTP Session Length*/
636 if(ltph->offset+ltph->length > state.ses.table[i].length){
637 state.ses.table[i].length=ltph->offset+ltph->length;
640 /* Computer TCP Ack Number*/
641 tcph->ack_seq=htonl(state.ack_num);
643 /* Get Amount of Data in LTP Segment*/
646 /* Compute TCP Window*/
647 tcph->window=htons(WIN_FACTOR);
651 /* Get Sequence and Acknowledgment Number for LTP Reports. Add TCP SACKS*/
652 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
659 /*Set TCP Data Length*/
663 /*Get LTP Report ID*/
664 ltph->report=evaluate_sdnv(odata, &eaten);
667 if(ltph->report <0 || rbuf<0){
668 dbgprintf(1,"Error: Bad SDNV!\n");
672 /*Get LTP Checkpoint ID*/
673 ltph->checkpoint=evaluate_sdnv(odata, &eaten);
676 if(ltph->checkpoint <0 || rbuf<0){
677 dbgprintf(1,"Error: Bad SDNV!\n");
681 /*Get LTP Report Upper Bound*/
682 ltph->ubound=evaluate_sdnv(odata, &eaten);
685 if(ltph->ubound <0 || rbuf<0){
686 dbgprintf(1,"Error: Bad SDNV!\n");
690 /*Get LTP Report Lower Bound*/
691 ltph->lbound=evaluate_sdnv(odata, &eaten);
694 if(ltph->lbound <0 || rbuf<0){
695 dbgprintf(1,"Error: Bad SDNV!\n");
699 /*Get Number of LTP Reception Claims*/
700 ltph->rclaims=evaluate_sdnv(odata, &eaten);
703 if(ltph->rclaims <0 || rbuf<0){
704 dbgprintf(1,"Error: Bad SDNV!\n");
708 /* find LTP session */
710 for(i=0; i < state.ses.size; i++){
711 if(ltph->session_num==state.ses.table[i].num){
717 /* LTP session Not Found*/
720 dbgprintf(1,"Error: Session for Report Not Found!\n");
726 data_recv=claim2sack(odata, i, ltph, tcph, tcpopt, dlen, rbuf);
731 /*Add TCP sequence number*/
732 tcph->seq=htonl(state.ack_num);
734 /* Compute TCP Window*/
735 tcph->window=htons(WIN_FACTOR);
737 /* Add TCP Ack Number*/
738 tcph->ack_seq=htonl(state.ses.table[i].start + data_recv+1);
742 /* Convert LTP Reception Claims into TCP SACKS*/
743 int claim2sack(const u_char* odata, int ses_id,struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
754 /*Add the Start of the TCP SACK Option*/
763 /* Cast to TCP SACK BLOCK pointer*/
764 t_block=(u_int32_t*)tcpopt;
766 /* Loop over at most 4 Claims*/
773 for(int i=0; i < max; i++){
776 coffset=evaluate_sdnv(odata, &eaten);
779 if(coffset <0 || rbuf<0){
780 dbgprintf(1,"Error: Bad SDNV!\n");
783 clength=evaluate_sdnv(odata, &eaten);
786 if(clength <0 || rbuf<0){
787 dbgprintf(1,"Error: Bad SDNV!\n");
791 /* Add claim to session info*/
792 if(!state.ses.table[ses_id].a_lst){
793 state.ses.table[ses_id].a_lst=malloc(sizeof(struct cl));
794 if(!state.ses.table[ses_id].a_lst){
795 dbgprintf(0,"Error: Couldn't allocate Memory\n");
798 tmp=state.ses.table[ses_id].a_lst;
800 tmp=state.ses.table[ses_id].a_lst;
804 tmp->next=malloc(sizeof(struct cl));
806 dbgprintf(0,"Error: Couldn't allocate Memory\n");
814 *t_block=htonl(state.ses.table[ses_id].start+coffset); //Left Side
816 *t_block=htonl(state.ses.table[ses_id].start+coffset+clength+1); //Right Side
819 /*Update TCP header length*/
824 /*Calculate Ack number*/
828 tmp=state.ses.table[ses_id].a_lst;
831 if((tmp->offset<=coffset) && ((tmp->offset+tmp->length) > coffset)){
832 coffset=tmp->offset+tmp->length;
838 state.ses.table[ses_id].acked=coffset;
842 /* Get a TCP seq number from the LTP Session ID for Report ACK and Session Cancellation*/
843 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
848 /*Find LTP session */
850 for(i=0; i < state.ses.size; i++){
851 if(ltph->session_num==state.ses.table[i].num){
857 /*LTP session Not Found*/
859 dbgprintf(1,"Error: Session for Segment Not Found!\n");
863 /* Add TCP Sequence Number */
864 tcph->seq=htonl(state.ses.table[i].start + state.ses.table[i].length);
865 /* No length, so don't update state.ses.table[i].length*/
869 /* Get an TCP Ack number from the LTP session ID for session cancellation*/
870 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
876 /* find LTP session */
878 for(i=0; i < state.ses.size; i++){
879 if(ltph->session_num==state.ses.table[i].num){
885 /* LTP session Not Found*/
888 dbgprintf(1,"Error: Session for Segment Not Found!\n");
893 /* Add TCP Ack Number*/
894 tcph->ack_seq=htonl(state.ses.table[i].start + state.ses.table[i].acked+1);
899 void dbgprintf(int level, const char *fmt, ...)
904 vfprintf(stderr, fmt, args);