1 /******************************************************************************
6 Description: Program to convert a LTP flow to a TCP flow for LTP analysis via
10 1)Only handles one LTP "connection". There isn't a good way to separate
11 different LTP "connections" from new sessions of the same "connection".
12 Use Tcpdump filters to separate connections. Libpcap filtering could also
14 2)Uses some special types from Linux (u_char, u_int32_t)
15 ******************************************************************************/
21 int MAX_SESSION=500000;
25 /* Forward declarations*/
28 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
29 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength);
30 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf);
31 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
32 int claim2sack(const u_char* odata, int ses_id, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
33 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
34 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
37 /*Parse commandline options and open files*/
38 int main(int argc, char *argv[])
48 /*parse commandline options*/
49 if(argc<4 || argc > 8){
50 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
54 /*loop through commandline options*/
55 for(int i=1; i < argc; i++){
57 if(lfile==NULL){ /*assign first non-dash argument to the ltp file*/
61 tfile=argv[i]; /*assign second non-dash argument to the dccp file*/
63 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
68 if(argv[i][1]=='d' && strlen(argv[i])==2){ /*debug option*/
71 if(argv[i][1]=='t'){ /*Encapsulation option*/
74 if(argv[i][1]=='b'){ /* Block size option*/
75 tmp=atoi(&argv[i][2]);
79 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
83 if(argv[i][1]=='s'){ /*Session range option*/
84 state.ses_min=strtol(&argv[i][2],&temp,10);
86 state.ses_max=strtol(temp,NULL,10);
91 if(lfile==NULL || tfile==NULL || type==NULL){
92 printf("Usage: ltp2tcp -t{encapsulation} ltp_file tcp_file [-d] [-b{block_size}]\n");
96 if(state.ses_min<=0 || state.ses_max<=0){
101 /*all options validated*/
104 dbgprintf(1,"Debug On\n");
105 dbgprintf(1,"Input file: %s\n", lfile);
106 dbgprintf(1,"Output file: %s\n", tfile);
107 dbgprintf(1,"Encapsulation: %s\n", type);
108 dbgprintf(1,"Block Size: %i\n", MAX_SESSION);
109 if(state.ses_min>=0){
110 dbgprintf(1, "Session Range: %i-%i\n", state.ses_min, state.ses_max);
112 dbgprintf(1, "Session Range: all\n");
116 /*determine encapsulation type*/
119 /*attempt to open input file*/
120 state.in=pcap_open_offline(lfile, erbuffer);
122 printf("Error opening input file\n");
126 /*attempt to open output file*/
127 state.out=pcap_dump_open(state.in,tfile);
129 printf("Error opening output file\n");
139 u_char *user=(u_char*)state.out;
140 pcap_loop(state.in, -1, handle_packet, user);
142 /*add correct TCP termination, if needed*/
143 if(state.en_ops && state.en_ops->fin){
144 (*state.en_ops->fin)();
149 pcap_close(state.in);
150 pcap_dump_close(state.out);
154 /*initialize the converter state*/
162 state.ses.size=TBL_SZ;
166 state.ses.table=malloc(TBL_SZ*sizeof(struct tbl));
167 if(state.ses.table==NULL){
168 dbgprintf(0,"Error: Couldn't allocate Memory!\n");
180 /*clean up the session table*/
183 /*cleanup the claims in each session*/
184 for(int i=0; i < MAX_SESSION; i ++){
185 if(state.ses.table[i].a_lst!=NULL){
186 tmp=state.ses.table[i].a_lst;
195 free(state.ses.table);
200 /*call back function for pcap_loop--do basic packet handling*/
201 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
205 const u_char *optr=bytes;
209 struct pcap_pkthdr nh;
211 /*create new libpcap header*/
212 memcpy(&nh, h, sizeof(struct pcap_pkthdr));
214 /*create buffer for new packet*/
215 nptr=ndata=malloc(MAX_PACKET);
217 dbgprintf(0,"Error: Couldn't allocate Memory\n");
221 /*make sure the packet is all zero*/
222 memset(ndata, 0, MAX_PACKET);
226 /*handle encapsulations*/
227 if(state.en_ops && state.en_ops->pre && state.en_ops->post){
228 if((*state.en_ops->pre)(&nh, &optr, &nptr, &length, &nlength)<0){
231 tlen=convert_packet(&nh, optr, nptr, length, nlength);
236 if((*state.en_ops->post)(tlen, ndata)<0){
244 pcap_dump(user,&nh, ndata);
250 /*do all the ltp to tcp conversions---returns length of TCP segment*/
251 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength)
257 struct ltp_hdr_d ltph;
265 if(h==NULL || odata==NULL || ndata==NULL|| datalength<1|| newlength < sizeof(struct tcphdr)){
269 /*cast TCP header pointers*/
270 tcph=(struct tcphdr*)ncur;
271 tcpopt=ncur+ sizeof(struct tcphdr);
273 /*set TCP standard features*/
274 tcph->source=htons(1113);
275 tcph->dest=htons(1113);
277 tcph->check=htonl(0);
282 /*Grab LTP control/version byte*/
283 ltph.vers_ctl=ctl=*odata;
287 /* check LTP version*/
288 if((ctl & 0xF0) !=0){
289 dbgprintf(1,"Error: Invalid Packet Version\n");
293 /* Parse LTP Session ID*/
294 ltph.sender_id=evaluate_sdnv(odata, &eaten);
297 if(ltph.sender_id<0 || datalength<0){
298 dbgprintf(1,"Error: Bad SDNV!\n");
302 ltph.session_num=evaluate_sdnv(odata, &eaten);
305 if(ltph.session_num<0 || datalength<0){
306 dbgprintf(1,"Error: Bad SDNV!\n");
311 if(state.ses_min>=0){
312 if((ltph.session_num < state.ses_min)|| (ltph.session_num > state.ses_max)){
313 dbgprintf(3, "Filtering session: %i\n", ltph.session_num);
319 /* We don't want to be confused if we have multiple LTP connections in this capture*/
320 if(state.sender_id==-1){
321 state.sender_id=ltph.sender_id;
323 if(state.sender_id!=ltph.sender_id){
324 dbgprintf(1,"Note: Packet from different Sender!!\n");
329 /*Parse and Remove LTP header extensions*/
331 ltph.extensions=*odata;
333 hext=ltph.extensions >> 4;
335 tmp=evaluate_sdnv(odata+1, &eaten);
336 if(tmp==0 || tmp < eaten || tmp < 0 || datalength < tmp){
337 dbgprintf(1,"Error: Packet has bad extension length!\n");
340 /* ignore all extensions*/
349 /*make changes by packet type*/
350 if((ctl & 0x0C)==0x0){//RED DATA
351 dbgprintf(2,"Packet Type: RED DATA\n");
353 /*Initialize the TCP connection, if this is the start*/
354 if(state.started==0){
356 if(state.en_ops && state.en_ops->handshake){
357 (*state.en_ops->handshake)(h);
361 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
365 /*Finalize TCP Header*/
372 if(newlength < dlen + tcph->doff*4){
373 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
378 memset(ndata + tcph->doff*4, 'A', dlen);
380 /*Adjust Lengths (packet and TCP segment)*/
381 h->len+=tcph->doff*4+dlen;
382 h->caplen+=tcph->doff*4+dlen;
383 return tcph->doff*4+dlen;
386 if((ctl & 0x0C)==0x4){//GREEN DATA
387 dbgprintf(2,"Packet Type: GREEN DATA\n");
389 /*Initialize the TCP connection, if this is the start*/
390 if(state.started==0){
392 if(state.en_ops && state.en_ops->handshake){
393 (*state.en_ops->handshake)(h);
397 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
401 /*Finalize TCP Header*/
408 if(newlength < dlen + tcph->doff*4){
409 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
414 memset(ndata+ tcph->doff*4, 'A', dlen);
416 /*Adjust Lengths (packet and TCP segment)*/
417 h->len+=tcph->doff*4+dlen;
418 h->caplen+=tcph->doff*4+dlen;
419 return tcph->doff*4+dlen;
422 if((ctl & 0x0F)==0x8){//REPORT SEGMENT
423 dbgprintf(2,"Packet Type: REPORT SEGMENT\n");
426 if(newlength < 4*8+4+sizeof(struct tcphdr)){
427 dbgprintf(1, "Error: Indicated Packet Length is may be beyond end of buffer!\n");
431 if(handle_report(odata, <ph, tcph, tcpopt, &dlen, datalength)<0){
435 /*Finalize TCP Header*/
442 if(newlength < tcph->doff*4){
443 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
447 /*Adjust Lengths (packet and TCP segment)*/
448 h->len+=tcph->doff*4+dlen;
449 h->caplen+=tcph->doff*4+dlen;
450 return tcph->doff*4+dlen;
453 if((ctl & 0x0F)==0x9){//REPORT ACK
454 dbgprintf(2,"Packet Type: REPORT ACK\n");
456 /*Get Seq and Ack Numbers*/
457 if(seq_from_session(<ph, tcph)<0){
460 tcph->ack_seq=htonl(state.ack_num);
462 /*Finalize TCP Header*/
463 tcph->window=htons(WIN_FACTOR);
470 if(newlength < tcph->doff*4){
471 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
475 /*Adjust Lengths (packet and TCP segment)*/
476 h->len+=sizeof(struct tcphdr);
477 h->caplen+=sizeof(struct tcphdr);
478 return sizeof(struct tcphdr);
481 if((ctl & 0x0D)==0xC){ //CANCEL SEGMENT
482 dbgprintf(2,"Packet Type: CANCEL SEGMENT\n");
484 /*Get Seq and Ack Numbers*/
485 if((ctl & 0x02)==0x00){
487 if(seq_from_session(<ph, tcph)<0){
490 tcph->ack_seq=htonl(state.ack_num);
493 if(ack_from_session(<ph, tcph)<0){
496 tcph->seq=htonl(state.ack_num++);
499 /*Finalize TCP Header*/
500 tcph->window=htons(0);
507 if(newlength < tcph->doff*4){
508 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
512 /*Adjust Lengths (packet and TCP segment)*/
513 h->len+=sizeof(struct tcphdr);
514 h->caplen+=sizeof(struct tcphdr);
515 return sizeof(struct tcphdr);
518 if((ctl & 0x0D)==0xD){//CANCEL ACK
519 dbgprintf(2,"Packet Type: CANCEL ACK\n");
521 /*Get Seq and Ack Numbers*/
522 if((ctl & 0x02)==0x00){
524 if(seq_from_session(<ph, tcph)<0){
527 tcph->ack_seq=htonl(state.ack_num);
530 if(ack_from_session(<ph, tcph)<0){
533 tcph->seq=htonl(state.ack_num++);
536 /*Finalize TCP Header*/
537 tcph->window=htons(0);
544 if(newlength < tcph->doff*4){
545 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
549 /*Adjust Lengths (packet and TCP segment)*/
550 h->len+=sizeof(struct tcphdr);
551 h->caplen+=sizeof(struct tcphdr);
552 return sizeof(struct tcphdr);
555 /*Anything else is bad!*/
556 dbgprintf(1,"Error: Invalid Packet Type\n");
560 /* Determine Sequence and Acknowledgment Numbers for Data Segments*/
561 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf)
567 /* Get LTP Client Service ID*/
568 ltph->cls_id=evaluate_sdnv(odata, &eaten);
571 if(ltph->cls_id <0 || rbuf <0){
572 dbgprintf(1,"Error: Bad SDNV!\n");
576 /*Get LTP Data Offset*/
577 ltph->offset=evaluate_sdnv(odata, &eaten);
580 if(ltph->offset <0 || rbuf <0){
581 dbgprintf(1,"Error: Bad SDNV!\n");
585 /*Get LTP Data Length*/
586 ltph->length=evaluate_sdnv(odata, &eaten);
589 if(ltph->length <0 || rbuf <0){
590 dbgprintf(1,"Error: Bad SDNV!\n");
594 /* find LTP session */
596 for(i=0; i < state.ses.size; i++){
597 if(ltph->session_num==state.ses.table[i].num){
603 /* Not Found, add it*/
605 i=(state.seq_ses_id++)%state.ses.size;
606 state.ses.table[i].num=ltph->session_num;
607 state.ses.table[i].seq_id=state.seq_ses_id;
608 if(state.seq_ses_id==1){
610 state.ses.table[i].start=state.seq_num;
612 state.ses.table[i].start=state.seq_num + MAX_SESSION - state.ses.table[(state.seq_ses_id-2)%state.ses.size].length;
616 /* Compute TCP Sequence Number */
617 tcph->seq=htonl(state.ses.table[i].start + ltph->offset);
618 if(state.ses.table[i].start + ltph->offset+ltph->length > state.seq_num){
619 state.seq_num=state.ses.table[i].start + ltph->offset + ltph->length;
622 /* Update LTP Session Length*/
623 if(ltph->offset+ltph->length > state.ses.table[i].length){
624 state.ses.table[i].length=ltph->offset+ltph->length;
627 /* Computer TCP Ack Number*/
628 tcph->ack_seq=htonl(state.ack_num);
630 /* Get Amount of Data in LTP Segment*/
633 /* Compute TCP Window*/
634 tcph->window=htons(WIN_FACTOR);
638 /* Get Sequence and Acknowledgment Number for LTP Reports. Add TCP SACKS*/
639 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
646 /*Set TCP Data Length*/
650 /*Get LTP Report ID*/
651 ltph->report=evaluate_sdnv(odata, &eaten);
654 if(ltph->report <0 || rbuf<0){
655 dbgprintf(1,"Error: Bad SDNV!\n");
659 /*Get LTP Checkpoint ID*/
660 ltph->checkpoint=evaluate_sdnv(odata, &eaten);
663 if(ltph->checkpoint <0 || rbuf<0){
664 dbgprintf(1,"Error: Bad SDNV!\n");
668 /*Get LTP Report Upper Bound*/
669 ltph->ubound=evaluate_sdnv(odata, &eaten);
672 if(ltph->ubound <0 || rbuf<0){
673 dbgprintf(1,"Error: Bad SDNV!\n");
677 /*Get LTP Report Lower Bound*/
678 ltph->lbound=evaluate_sdnv(odata, &eaten);
681 if(ltph->lbound <0 || rbuf<0){
682 dbgprintf(1,"Error: Bad SDNV!\n");
686 /*Get Number of LTP Reception Claims*/
687 ltph->rclaims=evaluate_sdnv(odata, &eaten);
690 if(ltph->rclaims <0 || rbuf<0){
691 dbgprintf(1,"Error: Bad SDNV!\n");
695 /* find LTP session */
697 for(i=0; i < state.ses.size; i++){
698 if(ltph->session_num==state.ses.table[i].num){
704 /* LTP session Not Found*/
707 dbgprintf(1,"Error: Session for Report Not Found!\n");
713 data_recv=claim2sack(odata, i, ltph, tcph, tcpopt, dlen, rbuf);
718 /*Add TCP sequence number*/
719 tcph->seq=htonl(state.ack_num);
721 /* Compute TCP Window*/
722 tcph->window=htons(WIN_FACTOR);
724 /* Add TCP Ack Number*/
725 tcph->ack_seq=htonl(state.ses.table[i].start + data_recv+1);
729 /* Convert LTP Reception Claims into TCP SACKS*/
730 int claim2sack(const u_char* odata, int ses_id,struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
741 /*Add the Start of the TCP SACK Option*/
750 /* Cast to TCP SACK BLOCK pointer*/
751 t_block=(u_int32_t*)tcpopt;
753 /* Loop over at most 4 Claims*/
760 for(int i=0; i < max; i++){
763 coffset=evaluate_sdnv(odata, &eaten);
766 if(coffset <0 || rbuf<0){
767 dbgprintf(1,"Error: Bad SDNV!\n");
770 clength=evaluate_sdnv(odata, &eaten);
773 if(clength <0 || rbuf<0){
774 dbgprintf(1,"Error: Bad SDNV!\n");
778 /* Add claim to session info*/
779 if(!state.ses.table[ses_id].a_lst){
780 state.ses.table[ses_id].a_lst=malloc(sizeof(struct cl));
781 if(!state.ses.table[ses_id].a_lst){
782 dbgprintf(0,"Error: Couldn't allocate Memory\n");
785 tmp=state.ses.table[ses_id].a_lst;
787 tmp=state.ses.table[ses_id].a_lst;
791 tmp->next=malloc(sizeof(struct cl));
793 dbgprintf(0,"Error: Couldn't allocate Memory\n");
801 *t_block=htonl(state.ses.table[ses_id].start+coffset); //Left Side
803 *t_block=htonl(state.ses.table[ses_id].start+coffset+clength+1); //Right Side
806 /*Update TCP header length*/
811 /*Calculate Ack number*/
815 tmp=state.ses.table[ses_id].a_lst;
818 if((tmp->offset<=coffset) && ((tmp->offset+tmp->length) > coffset)){
819 coffset=tmp->offset+tmp->length;
825 state.ses.table[ses_id].acked=coffset;
829 /* Get a TCP seq number from the LTP Session ID for Report ACK and Session Cancellation*/
830 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
835 /*Find LTP session */
837 for(i=0; i < state.ses.size; i++){
838 if(ltph->session_num==state.ses.table[i].num){
844 /*LTP session Not Found*/
846 dbgprintf(1,"Error: Session for Segment Not Found!\n");
850 /* Add TCP Sequence Number */
851 tcph->seq=htonl(state.ses.table[i].start + state.ses.table[i].length);
852 /* No length, so don't update state.ses.table[i].length*/
856 /* Get an TCP Ack number from the LTP session ID for session cancellation*/
857 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
863 /* find LTP session */
865 for(i=0; i < state.ses.size; i++){
866 if(ltph->session_num==state.ses.table[i].num){
872 /* LTP session Not Found*/
875 dbgprintf(1,"Error: Session for Segment Not Found!\n");
880 /* Add TCP Ack Number*/
881 tcph->ack_seq=htonl(state.ses.table[i].start + state.ses.table[i].acked+1);
886 void dbgprintf(int level, const char *fmt, ...)
891 vfprintf(stderr, fmt, args);