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 ******************************************************************************/
31 #define LTPTRACE_VERSION 0.3
32 #define COPYRIGHT_YEAR 2013
35 int MAX_SESSION=500000;
39 /* Forward declarations*/
42 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes);
43 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength);
44 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf);
45 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
46 int claim2sack(const u_char* odata, int ses_id, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf);
47 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
48 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph);
53 /*Parse commandline options and open files*/
54 int main(int argc, char *argv[])
64 /*parse commandline options*/
69 /*loop through commandline options*/
70 for(int i=1; i < argc; i++){
71 if(argv[i][0]!='-' || (argv[i][0]=='-' && strlen(argv[i])==1)){
72 if(lfile==NULL || argv[i][0]=='-'){
73 /*assign first non-dash (or only dash) argument to the dccp file*/
77 tfile=argv[i]; /*assign second non-dash argument to the dccp file*/
83 if(argv[i][1]=='v' && strlen(argv[i])==2){ /*debug option*/
86 if(argv[i][1]=='V' && strlen(argv[i])==2){ /*Version option*/
89 if(argv[i][1]=='h'&& strlen(argv[i])==2){ /* help*/
92 if(argv[i][1]=='t'){ /*Encapsulation option*/
95 if(argv[i][1]=='b'){ /* Block size option*/
96 tmp=atoi(&argv[i][2]);
103 if(argv[i][1]=='s'){ /*Session range option*/
104 state.ses_min=strtol(&argv[i][2],&temp,10);
106 state.ses_max=strtol(temp,NULL,10);
111 if(lfile==NULL || tfile==NULL || type==NULL){
115 if(state.ses_min<=0 || state.ses_max<=0){
120 /*all options validated*/
123 dbgprintf(1,"Input file: %s\n", lfile);
124 dbgprintf(1,"Output file: %s\n", tfile);
125 dbgprintf(1,"Encapsulation: %s\n", type);
126 dbgprintf(1,"Block Size: %i\n", MAX_SESSION);
127 if(state.ses_min>=0){
128 dbgprintf(1, "Session Range: %i-%i\n", state.ses_min, state.ses_max);
130 dbgprintf(1, "Session Range: all\n");
134 /*determine encapsulation type*/
137 /*attempt to open input file*/
138 state.in=pcap_open_offline(lfile, erbuffer);
140 printf("Error opening input file\n");
144 /*attempt to open output file*/
145 state.out=pcap_dump_open(state.in,tfile);
147 printf("Error opening output file\n");
157 u_char *user=(u_char*)state.out;
158 pcap_loop(state.in, -1, handle_packet, user);
160 /*add correct TCP termination, if needed*/
161 if(state.en_ops && state.en_ops->fin){
162 (*state.en_ops->fin)();
167 pcap_close(state.in);
168 pcap_dump_close(state.out);
172 /*initialize the converter state*/
180 state.ses.size=TBL_SZ;
184 state.ses.table=malloc(TBL_SZ*sizeof(struct tbl));
185 if(state.ses.table==NULL){
186 dbgprintf(0,"Error: Couldn't allocate Memory!\n");
198 /*clean up the session table*/
201 /*cleanup the claims in each session*/
202 for(int i=0; i < MAX_SESSION; i ++){
203 if(state.ses.table[i].a_lst!=NULL){
204 tmp=state.ses.table[i].a_lst;
213 free(state.ses.table);
218 /*call back function for pcap_loop--do basic packet handling*/
219 void handle_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
223 const u_char *optr=bytes;
227 struct pcap_pkthdr nh;
229 /*create new libpcap header*/
230 memcpy(&nh, h, sizeof(struct pcap_pkthdr));
232 /*create buffer for new packet*/
233 nptr=ndata=malloc(MAX_PACKET);
235 dbgprintf(0,"Error: Couldn't allocate Memory\n");
239 /*make sure the packet is all zero*/
240 memset(ndata, 0, MAX_PACKET);
244 /*handle encapsulations*/
245 if(state.en_ops && state.en_ops->pre && state.en_ops->post){
246 if((*state.en_ops->pre)(&nh, &optr, &nptr, &length, &nlength)<0){
249 tlen=convert_packet(&nh, optr, nptr, length, nlength);
254 if((*state.en_ops->post)(tlen, ndata)<0){
262 pcap_dump(user,&nh, ndata);
268 /*do all the ltp to tcp conversions---returns length of TCP segment*/
269 int convert_packet(struct pcap_pkthdr *h, const u_char *odata, u_char *ndata, int datalength, int newlength)
275 struct ltp_hdr_d ltph;
283 if(h==NULL || odata==NULL || ndata==NULL|| datalength<1|| newlength < sizeof(struct tcphdr)){
287 /*cast TCP header pointers*/
288 tcph=(struct tcphdr*)ncur;
289 tcpopt=ncur+ sizeof(struct tcphdr);
291 /*set TCP standard features*/
292 tcph->source=htons(1113);
293 tcph->dest=htons(1113);
295 tcph->check=htonl(0);
300 /*Grab LTP control/version byte*/
301 ltph.vers_ctl=ctl=*odata;
305 /* check LTP version*/
306 if((ctl & 0xF0) !=0){
307 dbgprintf(1,"Error: Invalid Packet Version\n");
311 /* Parse LTP Session ID*/
312 ltph.sender_id=evaluate_sdnv(odata, &eaten);
315 if(ltph.sender_id<0 || datalength<0){
316 dbgprintf(1,"Error: Bad SDNV!\n");
320 ltph.session_num=evaluate_sdnv(odata, &eaten);
323 if(ltph.session_num<0 || datalength<0){
324 dbgprintf(1,"Error: Bad SDNV!\n");
329 if(state.ses_min>=0){
330 if((ltph.session_num < state.ses_min)|| (ltph.session_num > state.ses_max)){
331 dbgprintf(3, "Filtering session: %i\n", ltph.session_num);
337 /* We don't want to be confused if we have multiple LTP connections in this capture*/
338 if(state.sender_id==-1){
339 state.sender_id=ltph.sender_id;
341 if(state.sender_id!=ltph.sender_id){
342 dbgprintf(1,"Note: Packet from different Sender!!\n");
347 /*Parse and Remove LTP header extensions*/
349 ltph.extensions=*odata;
351 hext=ltph.extensions >> 4;
353 tmp=evaluate_sdnv(odata+1, &eaten);
354 if(tmp==0 || tmp < eaten || tmp < 0 || datalength < tmp){
355 dbgprintf(1,"Error: Packet has bad extension length!\n");
358 /* ignore all extensions*/
367 /*make changes by packet type*/
368 if((ctl & 0x0C)==0x0){//RED DATA
369 dbgprintf(2,"Packet Type: RED DATA\n");
371 /*Initialize the TCP connection, if this is the start*/
372 if(state.started==0){
374 if(state.en_ops && state.en_ops->handshake){
375 (*state.en_ops->handshake)(h);
379 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
383 /*Finalize TCP Header*/
390 if(newlength < dlen + tcph->doff*4){
391 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
396 memset(ndata + tcph->doff*4, 'A', dlen);
398 /*Adjust Lengths (packet and TCP segment)*/
399 h->len+=tcph->doff*4+dlen;
400 h->caplen+=tcph->doff*4+dlen;
401 return tcph->doff*4+dlen;
404 if((ctl & 0x0C)==0x4){//GREEN DATA
405 dbgprintf(2,"Packet Type: GREEN DATA\n");
407 /*Initialize the TCP connection, if this is the start*/
408 if(state.started==0){
410 if(state.en_ops && state.en_ops->handshake){
411 (*state.en_ops->handshake)(h);
415 if(handle_data(odata, <ph, tcph, &dlen, datalength)<0){
419 /*Finalize TCP Header*/
426 if(newlength < dlen + tcph->doff*4){
427 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
432 memset(ndata+ tcph->doff*4, 'A', dlen);
434 /*Adjust Lengths (packet and TCP segment)*/
435 h->len+=tcph->doff*4+dlen;
436 h->caplen+=tcph->doff*4+dlen;
437 return tcph->doff*4+dlen;
440 if((ctl & 0x0F)==0x8){//REPORT SEGMENT
441 dbgprintf(2,"Packet Type: REPORT SEGMENT\n");
444 if(newlength < 4*8+4+sizeof(struct tcphdr)){
445 dbgprintf(1, "Error: Indicated Packet Length is may be beyond end of buffer!\n");
449 if(handle_report(odata, <ph, tcph, tcpopt, &dlen, datalength)<0){
453 /*Finalize TCP Header*/
460 if(newlength < tcph->doff*4){
461 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
465 /*Adjust Lengths (packet and TCP segment)*/
466 h->len+=tcph->doff*4+dlen;
467 h->caplen+=tcph->doff*4+dlen;
468 return tcph->doff*4+dlen;
471 if((ctl & 0x0F)==0x9){//REPORT ACK
472 dbgprintf(2,"Packet Type: REPORT ACK\n");
474 /*Get Seq and Ack Numbers*/
475 if(seq_from_session(<ph, tcph)<0){
478 tcph->ack_seq=htonl(state.ack_num);
480 /*Finalize TCP Header*/
481 tcph->window=htons(WIN_FACTOR);
488 if(newlength < tcph->doff*4){
489 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
493 /*Adjust Lengths (packet and TCP segment)*/
494 h->len+=sizeof(struct tcphdr);
495 h->caplen+=sizeof(struct tcphdr);
496 return sizeof(struct tcphdr);
499 if((ctl & 0x0D)==0xC){ //CANCEL SEGMENT
500 dbgprintf(2,"Packet Type: CANCEL SEGMENT\n");
502 /*Get Seq and Ack Numbers*/
503 if((ctl & 0x02)==0x00){
505 if(seq_from_session(<ph, tcph)<0){
508 tcph->ack_seq=htonl(state.ack_num);
511 if(ack_from_session(<ph, tcph)<0){
514 tcph->seq=htonl(state.ack_num++);
517 /*Finalize TCP Header*/
518 tcph->window=htons(0);
525 if(newlength < tcph->doff*4){
526 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
530 /*Adjust Lengths (packet and TCP segment)*/
531 h->len+=sizeof(struct tcphdr);
532 h->caplen+=sizeof(struct tcphdr);
533 return sizeof(struct tcphdr);
536 if((ctl & 0x0D)==0xD){//CANCEL ACK
537 dbgprintf(2,"Packet Type: CANCEL ACK\n");
539 /*Get Seq and Ack Numbers*/
540 if((ctl & 0x02)==0x00){
542 if(seq_from_session(<ph, tcph)<0){
545 tcph->ack_seq=htonl(state.ack_num);
548 if(ack_from_session(<ph, tcph)<0){
551 tcph->seq=htonl(state.ack_num++);
554 /*Finalize TCP Header*/
555 tcph->window=htons(0);
562 if(newlength < tcph->doff*4){
563 dbgprintf(1, "Error: Indicated Packet Length is beyond end of buffer!\n");
567 /*Adjust Lengths (packet and TCP segment)*/
568 h->len+=sizeof(struct tcphdr);
569 h->caplen+=sizeof(struct tcphdr);
570 return sizeof(struct tcphdr);
573 /*Anything else is bad!*/
574 dbgprintf(1,"Error: Invalid Packet Type\n");
578 /* Determine Sequence and Acknowledgment Numbers for Data Segments*/
579 int handle_data(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, int* dlen, int rbuf)
585 /* Get LTP Client Service ID*/
586 ltph->cls_id=evaluate_sdnv(odata, &eaten);
589 if(ltph->cls_id <0 || rbuf <0){
590 dbgprintf(1,"Error: Bad SDNV!\n");
594 /*Get LTP Data Offset*/
595 ltph->offset=evaluate_sdnv(odata, &eaten);
598 if(ltph->offset <0 || rbuf <0){
599 dbgprintf(1,"Error: Bad SDNV!\n");
603 /*Get LTP Data Length*/
604 ltph->length=evaluate_sdnv(odata, &eaten);
607 if(ltph->length <0 || rbuf <0){
608 dbgprintf(1,"Error: Bad SDNV!\n");
612 /* find LTP session */
614 for(i=0; i < state.ses.size; i++){
615 if(ltph->session_num==state.ses.table[i].num){
621 /* Not Found, add it*/
623 i=(state.seq_ses_id++)%state.ses.size;
624 state.ses.table[i].num=ltph->session_num;
625 state.ses.table[i].seq_id=state.seq_ses_id;
626 if(state.seq_ses_id==1){
628 state.ses.table[i].start=state.seq_num;
630 state.ses.table[i].start=state.seq_num + MAX_SESSION - state.ses.table[(state.seq_ses_id-2)%state.ses.size].length;
634 /* Compute TCP Sequence Number */
635 tcph->seq=htonl(state.ses.table[i].start + ltph->offset);
636 if(state.ses.table[i].start + ltph->offset+ltph->length > state.seq_num){
637 state.seq_num=state.ses.table[i].start + ltph->offset + ltph->length;
640 /* Update LTP Session Length*/
641 if(ltph->offset+ltph->length > state.ses.table[i].length){
642 state.ses.table[i].length=ltph->offset+ltph->length;
645 /* Computer TCP Ack Number*/
646 tcph->ack_seq=htonl(state.ack_num);
648 /* Get Amount of Data in LTP Segment*/
651 /* Compute TCP Window*/
652 tcph->window=htons(WIN_FACTOR);
656 /* Get Sequence and Acknowledgment Number for LTP Reports. Add TCP SACKS*/
657 int handle_report(const u_char* odata, struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
664 /*Set TCP Data Length*/
668 /*Get LTP Report ID*/
669 ltph->report=evaluate_sdnv(odata, &eaten);
672 if(ltph->report <0 || rbuf<0){
673 dbgprintf(1,"Error: Bad SDNV!\n");
677 /*Get LTP Checkpoint ID*/
678 ltph->checkpoint=evaluate_sdnv(odata, &eaten);
681 if(ltph->checkpoint <0 || rbuf<0){
682 dbgprintf(1,"Error: Bad SDNV!\n");
686 /*Get LTP Report Upper Bound*/
687 ltph->ubound=evaluate_sdnv(odata, &eaten);
690 if(ltph->ubound <0 || rbuf<0){
691 dbgprintf(1,"Error: Bad SDNV!\n");
695 /*Get LTP Report Lower Bound*/
696 ltph->lbound=evaluate_sdnv(odata, &eaten);
699 if(ltph->lbound <0 || rbuf<0){
700 dbgprintf(1,"Error: Bad SDNV!\n");
704 /*Get Number of LTP Reception Claims*/
705 ltph->rclaims=evaluate_sdnv(odata, &eaten);
708 if(ltph->rclaims <0 || rbuf<0){
709 dbgprintf(1,"Error: Bad SDNV!\n");
713 /* find LTP session */
715 for(i=0; i < state.ses.size; i++){
716 if(ltph->session_num==state.ses.table[i].num){
722 /* LTP session Not Found*/
725 dbgprintf(1,"Error: Session for Report Not Found!\n");
731 data_recv=claim2sack(odata, i, ltph, tcph, tcpopt, dlen, rbuf);
736 /*Add TCP sequence number*/
737 tcph->seq=htonl(state.ack_num);
739 /* Compute TCP Window*/
740 tcph->window=htons(WIN_FACTOR);
742 /* Add TCP Ack Number*/
743 tcph->ack_seq=htonl(state.ses.table[i].start + data_recv+1);
747 /* Convert LTP Reception Claims into TCP SACKS*/
748 int claim2sack(const u_char* odata, int ses_id,struct ltp_hdr_d* ltph, struct tcphdr *tcph, u_char* tcpopt, int* dlen, int rbuf)
759 /*Add the Start of the TCP SACK Option*/
768 /* Cast to TCP SACK BLOCK pointer*/
769 t_block=(u_int32_t*)tcpopt;
771 /* Loop over at most 4 Claims*/
778 for(int i=0; i < max; i++){
781 coffset=evaluate_sdnv(odata, &eaten);
784 if(coffset <0 || rbuf<0){
785 dbgprintf(1,"Error: Bad SDNV!\n");
788 clength=evaluate_sdnv(odata, &eaten);
791 if(clength <0 || rbuf<0){
792 dbgprintf(1,"Error: Bad SDNV!\n");
796 /* Add claim to session info*/
797 if(!state.ses.table[ses_id].a_lst){
798 state.ses.table[ses_id].a_lst=malloc(sizeof(struct cl));
799 if(!state.ses.table[ses_id].a_lst){
800 dbgprintf(0,"Error: Couldn't allocate Memory\n");
803 tmp=state.ses.table[ses_id].a_lst;
805 tmp=state.ses.table[ses_id].a_lst;
809 tmp->next=malloc(sizeof(struct cl));
811 dbgprintf(0,"Error: Couldn't allocate Memory\n");
819 *t_block=htonl(state.ses.table[ses_id].start+coffset); //Left Side
821 *t_block=htonl(state.ses.table[ses_id].start+coffset+clength+1); //Right Side
824 /*Update TCP header length*/
829 /*Calculate Ack number*/
833 tmp=state.ses.table[ses_id].a_lst;
836 if((tmp->offset<=coffset) && ((tmp->offset+tmp->length) > coffset)){
837 coffset=tmp->offset+tmp->length;
843 state.ses.table[ses_id].acked=coffset;
847 /* Get a TCP seq number from the LTP Session ID for Report ACK and Session Cancellation*/
848 int seq_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
853 /*Find LTP session */
855 for(i=0; i < state.ses.size; i++){
856 if(ltph->session_num==state.ses.table[i].num){
862 /*LTP session Not Found*/
864 dbgprintf(1,"Error: Session for Segment Not Found!\n");
868 /* Add TCP Sequence Number */
869 tcph->seq=htonl(state.ses.table[i].start + state.ses.table[i].length);
870 /* No length, so don't update state.ses.table[i].length*/
874 /* Get an TCP Ack number from the LTP session ID for session cancellation*/
875 int ack_from_session(struct ltp_hdr_d* ltph, struct tcphdr *tcph)
881 /* find LTP session */
883 for(i=0; i < state.ses.size; i++){
884 if(ltph->session_num==state.ses.table[i].num){
890 /* LTP session Not Found*/
893 dbgprintf(1,"Error: Session for Segment Not Found!\n");
898 /* Add TCP Ack Number*/
899 tcph->ack_seq=htonl(state.ses.table[i].start + state.ses.table[i].acked+1);
905 dbgprintf(0, "ltp2tcp version %.1f\n",LTPTRACE_VERSION);
906 dbgprintf(0, "Copyright (C) %i Samuel Jero <sj323707@ohio.edu>\n",COPYRIGHT_YEAR);
907 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
908 dbgprintf(0, "This is free software, and you are welcome to\n");
909 dbgprintf(0, "redistribute it under certain conditions.\n");
913 /*Usage information for program*/
916 dbgprintf(0,"Usage: ltp2tcp -t{encapsulation} [-v] [-V] [-h] [-b{block_size}] [-s{start session}-{end session}] ltp_file tcp_file\n");
917 dbgprintf(0, " -v verbose. May be repeated for additional verbosity.\n");
918 dbgprintf(0, " -V Version information\n");
919 dbgprintf(0, " -h Help\n");
920 dbgprintf(0, " -t type of encapsulation (udp,dccp,sll)\n");
921 dbgprintf(0, " -b LTP block size (NOT the bundle size) that is being used over this connection\n");
922 dbgprintf(0, " -s Requests a graph of only the specified range of sessions\n");
927 void dbgprintf(int level, const char *fmt, ...)
932 vfprintf(stderr, fmt, args);