]> sjero.net Git - dccpping/blob - dccpping.c
Add signal handler to print stats on SIGINT
[dccpping] / dccpping.c
1 /******************************************************************************
2 Author: Samuel Jero <sj323707@ohio.edu>
3
4 Date: 10/2012
5
6 Description: Program to ping hosts using DCCP REQ packets to test for DCCP connectivity.
7 ******************************************************************************/
8 #include <stdarg.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <strings.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <signal.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/socket.h>
19 #include <sys/select.h>
20 #include <netinet/ip.h>
21 #include <netinet/ip6.h>
22 #include <netinet/in.h>
23 #include <netinet/ip_icmp.h>
24 #include <netinet/icmp6.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #include <net/if.h>
28 #include <ifaddrs.h>
29 #include <linux/dccp.h>
30 #include "checksums.h"
31
32
33 #define MAX(x,y) (x>y ? x : y)
34 /*Structure for simpler IPv4/IPv6 Address handling*/
35 typedef union ipaddr{
36         struct sockaddr *gen;
37         struct sockaddr_in *ipv4;
38         struct sockaddr_in6 *ipv6;
39 } ipaddr_ptr_t;
40
41 enum responses{
42         UNKNOWN=0,
43         RESET,
44         RESPONSE,
45         SYNC,
46         DEST_UNREACHABLE,
47         TTL_EXPIRATION,
48         TOO_BIG,
49         PARAMETER_PROBLEM
50 };
51 char* response_label[]= {
52 "Unknown",
53 "Closed Port (Reset)",
54 "Open Port (Response)",
55 "Open Port (Sync)",
56 "Destination Unreachable",
57 "TTL Expiration",
58 "Packet Too Big",
59 "DCCP Not Supported (Parameter Problem)"
60 };
61
62
63 struct request{
64         int                             seq;
65         int                             num_replies;
66         int                             num_errors;
67         struct timeval  sent;
68         struct timeval  reply;
69         enum responses  reply_type;
70         struct request  *next;
71         struct request  *prev;
72 };
73
74 struct stats{
75         int                             requests_sent;
76         int                             replies_received;
77         int                             errors;
78         double                  rtt_min;
79         double                  rtt_avg;
80         double                  rtt_max;
81         struct timeval  start;
82         struct timeval  stop;
83 };
84
85 struct request_queue{
86         struct request *head;
87         struct request *tail;
88 };
89
90
91 int debug=0;                    /*set to 1 to turn on debugging information*/
92 int count=-1;                   /*Default number of pings (-1 is infinity)*/
93 int dest_port=33434;    /*Default port*/
94 int ttl=64;                             /*Default TTL*/
95 long interval=1000;             /*Default delay between pings in ms*/
96 int ip_type=AF_UNSPEC;  /*IPv4 or IPv6*/
97 ipaddr_ptr_t dest_addr; /*Destination Address*/
98 ipaddr_ptr_t src_addr;  /*Source Address*/
99 struct request_queue    queue;
100 struct stats                    ping_stats;
101 extern int errno;
102
103
104 void getAddresses(char *src, char* dst);
105 void doping();
106 void handleDCCPpacket(int rcv_socket, int send_socket);
107 void handleICMP4packet(int rcv_socket);
108 void handleICMP6packet(int rcv_socket);
109 void buildRequestPacket(unsigned char* buffer, int *len, int seq);
110 void updateRequestPacket(unsigned char* buffer, int *len, int seq);
111 int logPacket(int seq);
112 int logResponse(ipaddr_ptr_t *src, int seq, int type);
113 void clearQueue();
114 void sigHandler();
115 void usage();
116 void sanitize_environment();
117 void dbgprintf(int level, const char *fmt, ...);
118
119
120 /*Parse commandline options*/
121 int main(int argc, char *argv[])
122 {
123         char c;
124         char *src=NULL;
125         char *dst=NULL;
126         queue.head=NULL;
127         queue.tail=NULL;
128         ping_stats.replies_received=0;
129         ping_stats.requests_sent=0;
130         ping_stats.rtt_avg=0;
131         ping_stats.rtt_max=0;
132         ping_stats.rtt_min=0;
133         ping_stats.errors=0;
134
135         sanitize_environment();
136
137         while ((c = getopt(argc, argv, "64c:p:i:dt:S:")) != -1) {
138                 switch (c) {
139                         case '6':
140                                 ip_type=AF_INET6;
141                                 break;
142                         case '4':
143                                 ip_type=AF_INET;
144                                 break;
145                         case 'c':
146                                 count = atoi(optarg);
147                                 if(count<=0){
148                                         dbgprintf(0, "Error: count must be positive");
149                                         exit(1);
150                                 }
151                                 break;
152                         case 'p':
153                                 dest_port = atoi(optarg);
154                                 break;
155                         case 'i':
156                                 interval = (long)(atof(optarg) * 1000.0);
157                                 if (interval <= 0) {
158                                         fprintf(stderr, "Invalid interval\n");
159                                         exit(1);
160                                 }
161                                 break;
162                         case 'd':
163                                 debug++;
164                                 break;
165                         case 't':
166                                 ttl = atoi(optarg);
167                                 if (ttl < 1 || ttl > 255) {
168                                         fprintf(stderr, "Invalid TTL\n");
169                                 }
170                                 break;
171                         case 'S':
172                                 src=optarg;
173                                 break;
174                         default:
175                                 usage();
176                                 break;
177                 }
178         }
179
180         argc -= optind;
181         argv += optind;
182
183         if (argc != 1) {
184                 usage();
185         }
186         dst=argv[0];
187
188         getAddresses(src, dst);
189         if(src_addr.gen==NULL || dest_addr.gen==NULL){
190                 dbgprintf(0,"Error: Can't determine source or destination address\n");
191                 exit(1);
192         }
193
194         signal(SIGINT, sigHandler);
195         doping();
196
197         free(src_addr.gen);
198         free(dest_addr.gen);
199         clearQueue();
200         return 0;
201 }
202
203 void getAddresses(char *src, char* dst){
204         struct addrinfo hint;
205         struct addrinfo *dtmp, *stmp;
206         struct ifaddrs *temp, *cur;
207         struct sockaddr_in6* iv6;
208         int addrlen;
209         int err;
210
211         /*Lookup destination Address*/
212         memset(&hint,0,sizeof(struct addrinfo));
213         hint.ai_family=ip_type;
214         hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG;
215
216         if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){
217                 dbgprintf(0,"Error: Couldn't lookup destination %s (%s)\n", dst, gai_strerror(err));
218                 exit(1);
219         }
220         if(dtmp==NULL){
221                 dbgprintf(0,"Error: Unknown Host %s\n", dst);
222                 exit(1);
223         }else{
224                 addrlen=dtmp->ai_addrlen;
225                 hint.ai_family=ip_type=dtmp->ai_family;
226                 dest_addr.gen=malloc(dtmp->ai_addrlen);
227                 if(dest_addr.gen==NULL){
228                         dbgprintf(0,"Error: Can't allocate Memory\n");
229                         exit(1);
230                 }
231                 memcpy(dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen);
232         }
233         freeaddrinfo(dtmp);
234
235         /*Get a meaningful source address*/
236         if(src!=NULL){
237                 /*Use Commandline arg*/
238                 if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){
239                         dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err));
240                         exit(1);
241                 }
242                 if(stmp==NULL){
243                         dbgprintf(0,"Error: Unknown Host %s\n", dst);
244                         exit(1);
245                 }else{
246                         addrlen=stmp->ai_addrlen;
247                         src_addr.gen=malloc(stmp->ai_addrlen);
248                         if(src_addr.gen==NULL){
249                                 dbgprintf(0,"Error: Can't allocate Memory\n");
250                                 exit(1);
251                         }
252                         memcpy(src_addr.gen,stmp->ai_addr,stmp->ai_addrlen);
253                 }
254                 freeaddrinfo(stmp);
255         }else{
256                 /*Guess a good source address*/
257                 getifaddrs(&temp);
258                 cur=temp;
259                 while(cur!=NULL){
260                         if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=ip_type){ /*Not matching ipv4/ipv6 of dest*/
261                                 cur=cur->ifa_next;
262                                 continue;
263                         }
264                         if(cur->ifa_flags & IFF_LOOPBACK){ /*Don't use loopback addresses*/
265                                 cur=cur->ifa_next;
266                                 continue;
267                         }
268                         if(cur->ifa_addr!=NULL && cur->ifa_addr->sa_family==AF_INET6){
269                                 iv6=(struct sockaddr_in6*)cur->ifa_addr;
270
271                                 if(iv6->sin6_scope_id!=0){ /*Not globally valid address, if ipv6*/
272                                         cur=cur->ifa_next;
273                                         continue;
274                                 }
275                         }
276
277                         src_addr.gen=malloc(sizeof(struct sockaddr_storage));
278                         if(src_addr.gen==NULL){
279                                 dbgprintf(0,"Error: Can't allocate Memory\n");
280                                 exit(1);
281                         }
282                         src_addr.gen->sa_family=ip_type;
283                         memcpy(src_addr.gen,cur->ifa_addr,addrlen);
284                         //break;
285                         cur=cur->ifa_next;
286                 }
287                 freeifaddrs(temp);
288         }
289         return;
290 }
291
292 /*Preform the ping functionality*/
293 void doping(){
294         int rs, is4,is6,ds;
295         int done=0;
296         int addrlen;
297         int slen=1500;
298         unsigned char sbuffer[slen];
299         fd_set sel;
300         struct timeval timeout;
301         struct timeval t,delay, add;
302         char pbuf[1000];
303         int seq=1;
304
305         /*Open Sockets*/
306         rs=socket(ip_type, SOCK_RAW ,IPPROTO_RAW);
307         if(rs<0){
308                 dbgprintf(0, "Error opening raw socket\n");
309                 exit(1);
310         }
311         ds=socket(ip_type, SOCK_RAW ,IPPROTO_DCCP);
312         if(ds<0){
313                 dbgprintf(0, "Error opening raw DCCP socket\n");
314                 exit(1);
315         }
316         is4=socket(ip_type,SOCK_RAW,IPPROTO_ICMP);
317         if(is4<0){
318                 dbgprintf(0,"Error opening raw ICMPv4 socket\n");
319                 exit(1);
320         }
321         is6=socket(ip_type,SOCK_RAW,IPPROTO_ICMPV6);
322         if(is6<0){
323                 dbgprintf(0,"Error opening raw ICMPv6 socket\n");
324                 exit(1);
325         }
326
327
328         /*Build DCCP packet*/
329         buildRequestPacket(sbuffer,&slen,seq);
330         if(ip_type==AF_INET){
331                 addrlen=sizeof(struct sockaddr_in);
332         }else{
333                 addrlen=sizeof(struct sockaddr_in6);
334         }
335
336         /*Start Message*/
337         if(ip_type==AF_INET){
338                 dbgprintf(0, "PINGING %s on DCCP port %i\n",
339                                 inet_ntop(ip_type, (void*)&dest_addr.ipv4->sin_addr, pbuf, 1000),dest_port);
340         }else{
341                 dbgprintf(0, "PINGING %s on DCCP port %i\n",
342                                 inet_ntop(ip_type, (void*)&dest_addr.ipv6->sin6_addr, pbuf, 1000),dest_port);
343         }
344
345         while(!done){
346                 /*Send Ping*/
347                 if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)dest_addr.gen,addrlen)<0){
348                         if(errno!=EINTR){
349                                 dbgprintf(0,"Error: sendto failed\n");
350                         }
351                 }
352                 if(count==0){done=1; break;}
353
354                 if (logPacket(seq)<0){
355                         dbgprintf(0,"Error: Couldn't record request!\n");
356                 }
357                 if(ip_type==AF_INET){
358                         dbgprintf(1, "Sending DCCP Request to %s\n",inet_ntop(ip_type, (void*)&dest_addr.ipv4->sin_addr, pbuf, 1000));
359                 }else{
360                         dbgprintf(1, "Sending DCCP Request to %s\n",inet_ntop(ip_type, (void*)&dest_addr.ipv6->sin6_addr, pbuf, 1000));
361                 }
362
363                 /*Use select to wait on packets or until interval has passed*/
364                 add.tv_sec=interval/1000;
365                 add.tv_usec=(interval%1000)*1000;
366                 gettimeofday(&t,NULL);
367                 timeradd(&t,&add,&delay);
368                 while(timercmp(&t,&delay,<)){
369                         /*Prepare for select*/
370                         FD_ZERO(&sel);
371                         FD_SET(ds,&sel);
372                         FD_SET(is4,&sel);
373                         FD_SET(is6,&sel);
374                         timersub(&delay,&t,&timeout);
375
376                         /*Do select call*/
377                         if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
378                                 if(errno!=EINTR){
379                                         dbgprintf(0,"Select() error (%s)\n",strerror(errno));
380                                 }
381                         }
382                         if(count==0){done=1;break;}
383
384                         if(FD_ISSET(ds,&sel)){
385                                 /*Data on the DCCP socket*/
386                                 handleDCCPpacket(ds,rs);
387
388                         }
389                         if(FD_ISSET(is4,&sel) && ip_type==AF_INET){
390                                 /*Data on the ICMPv4 socket*/
391                                 handleICMP4packet(is4);
392                         }
393                         if(FD_ISSET(is6,&sel) && ip_type==AF_INET6){
394                                 /*Data on the ICMPv6 socket*/
395                                 handleICMP6packet(is6);
396                         }
397                         gettimeofday(&t,NULL);
398                 }
399
400                 /*Update count*/
401                 if(count>-1){
402                         count--;
403                 }
404                 seq++;
405                 updateRequestPacket(sbuffer,&slen, seq);
406         }
407
408         close(rs);
409         close(is4);
410         close(is6);
411         close(ds);
412 }
413
414 void handleDCCPpacket(int rcv_socket, int send_socket){
415         int rlen=1500;
416         unsigned char rbuffer[rlen];
417         ipaddr_ptr_t rcv_addr;
418         socklen_t rcv_addr_len;
419         struct dccp_hdr *dhdr;
420         struct dccp_hdr_reset *dhdr_re;
421         struct dccp_hdr_response *dhdr_rp;
422         struct dccp_hdr_ack_bits *dhdr_sync;
423         unsigned char* ptr;
424         struct iphdr* iph;
425
426         /*Memory for socket address*/
427         rcv_addr_len=sizeof(struct sockaddr_storage);
428         rcv_addr.gen=malloc(rcv_addr_len);
429         if(rcv_addr.gen==NULL){
430                 dbgprintf(0,"Error: Can't Allocate Memory!\n");
431                 exit(1);
432         }
433
434         /*Receive Packet*/
435         rcv_addr_len=sizeof(struct sockaddr_storage);
436         if((rlen=recvfrom(rcv_socket, &rbuffer, 1000,0,rcv_addr.gen,&rcv_addr_len))<0){
437                 if(errno!=EINTR){
438                         dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
439                 }
440         }
441         if(rlen<0){
442                 return;
443         }
444
445         if(rcv_addr.gen->sa_family!=ip_type){ //confirm IP type
446                 dbgprintf(1, "DCCP packet on %s. Tossing.\n", (ip_type==AF_INET) ? "IPv4" : "IPv6");
447                 free(rcv_addr.gen);
448                 return;
449         }
450
451         if(rcv_addr.gen->sa_family==AF_INET){
452                 /*IPv4*/
453                 if(memcmp(&rcv_addr.ipv4->sin_addr,&dest_addr.ipv4->sin_addr,
454                                 sizeof(dest_addr.ipv4->sin_addr))!=0){ //not from destination
455                         dbgprintf(1,"DCCP packet from 3rd host\n");
456                         free(rcv_addr.gen);
457                         return;
458                 }
459                 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
460
461                         dbgprintf(1, "Packet smaller than possible DCCP packet received on DCCP socket\n");
462                         free(rcv_addr.gen);
463                         return;
464                 }
465                 iph=(struct iphdr*)rbuffer;
466                 ptr=rbuffer+iph->ihl*4;
467         }else{
468                 /*IPv6*/
469                 if(memcmp(&rcv_addr.ipv6->sin6_addr, &dest_addr.ipv6->sin6_addr,
470                                 sizeof(dest_addr.ipv6->sin6_addr))!=0){ //not from destination
471                         dbgprintf(1,"DCCP packet from 3rd host\n");
472                         free(rcv_addr.gen);
473                         return;
474                 }
475                 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
476
477                         dbgprintf(1, "Packet smaller than possible DCCP packet received on DCCP socket\n");
478                         free(rcv_addr.gen);
479                         return;
480                 }
481                 ptr=rbuffer;
482         }
483
484         /*DCCP checks*/
485         dhdr=(struct dccp_hdr*)ptr;
486         if(dhdr->dccph_sport!=htons(dest_port)){
487                 dbgprintf(1,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
488                 free(rcv_addr.gen);
489                 return;
490         }
491         if(dhdr->dccph_dport!=htons(dest_port)){
492                 dbgprintf(1,"DCCP packet with wrong Destination Port\n");
493                 free(rcv_addr.gen);
494                 return;
495         }
496
497         /*Pick Response*/
498         if(dhdr->dccph_type==DCCP_PKT_RESET){
499                 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
500                         dbgprintf(1, "Error: Reset packet too small!");
501                         return;
502                 }
503                 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
504                 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), RESET);
505                 /*Nothing else to do*/
506         }
507         if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
508                 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
509                         dbgprintf(1, "Error: Response packet too small!");
510                         return;
511                 }
512                 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
513                 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),RESPONSE);
514                 /*TODO:Send Close back*/
515         }
516         if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
517                 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
518                         dbgprintf(1, "Error: Response packet too small!");
519                         return;
520                 }
521                 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
522                 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),SYNC);
523                 /*TODO:Send Reset*/
524         }
525
526         free(rcv_addr.gen);
527 }
528
529 void handleICMP4packet(int rcv_socket){
530         int rlen=1500;
531         unsigned char rbuffer[rlen];
532         ipaddr_ptr_t rcv_addr;
533         socklen_t rcv_addr_len;
534         struct icmphdr *icmp4;
535         struct dccp_hdr *dhdr;
536         struct dccp_hdr_ext *dhdre;
537         struct iphdr* ip4hdr;
538         int type;
539
540         /*Memory for socket address*/
541         rcv_addr_len=sizeof(struct sockaddr_storage);
542         rcv_addr.gen=malloc(rcv_addr_len);
543         if(rcv_addr.gen==NULL){
544                 dbgprintf(0,"Error: Can't Allocate Memory!\n");
545                 exit(1);
546         }
547
548         /*Receive Packet*/
549         if((rlen=recvfrom(rcv_socket, &rbuffer, 1000,0,rcv_addr.gen,&rcv_addr_len))<0){
550                 if(errno!=EINTR){
551                         dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
552                 }
553         }
554         if(rlen<0){
555                 return;
556         }
557
558         if(rlen < sizeof(struct icmphdr)){ //check packet size
559                 dbgprintf(1, "Packet smaller than possible ICMPv4 packet!\n");
560                 free(rcv_addr.gen);
561                 return;
562         }
563
564         icmp4=(struct icmphdr*)rbuffer;
565         if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
566                 dbgprintf(1, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
567                 free(rcv_addr.gen);
568                 return;
569         }
570
571         /*Check packet size again*/
572         if(rlen<sizeof(struct icmphdr)+sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
573                 dbgprintf(1, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
574                 free(rcv_addr.gen);
575                 return;
576         }
577
578         /*Decode IPv4 header*/
579         ip4hdr=(struct iphdr*)(rbuffer+sizeof(struct icmphdr));
580         if(memcmp(&src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(src_addr.ipv4->sin_addr))!=0){
581                 /*Source address doesn't match*/
582                 free(rcv_addr.gen);
583                 return;
584         }
585         if(memcmp(&dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(dest_addr.ipv4->sin_addr))!=0){
586                 /*Destination address doesn't match*/
587                 free(rcv_addr.gen);
588                 return;
589         }
590         if(ip4hdr->protocol!=IPPROTO_DCCP){
591                 /*Not DCCP!*/
592                 free(rcv_addr.gen);
593                 return;
594         }
595
596         /*Decode DCCP header*/
597         dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmphdr)+ip4hdr->ihl*4);
598         if(dhdr->dccph_dport!=htons(dest_port)){
599                 /*DCCP Destination Ports don't match*/
600                 free(rcv_addr.gen);
601                 return;
602         }
603         if(dhdr->dccph_sport!=htons(dest_port)){
604                 /*DCCP Source Ports don't match*/
605                 free(rcv_addr.gen);
606                 return;
607         }
608         dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
609
610         /*Log*/
611         if(icmp4->type==ICMP_DEST_UNREACH){
612                 type=DEST_UNREACHABLE;
613         }
614         if(icmp4->type==ICMP_TIME_EXCEEDED){
615                 type=TTL_EXPIRATION;
616         }
617         logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),type);
618         free(rcv_addr.gen);
619         return;
620 }
621
622 void handleICMP6packet(int rcv_socket){
623         int rlen=1500;
624         unsigned char rbuffer[rlen];
625         ipaddr_ptr_t rcv_addr;
626         socklen_t rcv_addr_len;
627         struct icmp6_hdr *icmp6;
628         struct ip6_hdr* ip6hdr;
629         struct dccp_hdr *dhdr;
630         struct dccp_hdr_ext *dhdre;
631         int type;
632
633         /*Memory for socket address*/
634         rcv_addr_len=sizeof(struct sockaddr_storage);
635         rcv_addr.gen=malloc(rcv_addr_len);
636         if(rcv_addr.gen==NULL){
637                 dbgprintf(0,"Error: Can't Allocate Memory!\n");
638                 exit(1);
639         }
640
641         /*Receive Packet*/
642         if((rlen=recvfrom(rcv_socket, &rbuffer, 1000,0,rcv_addr.gen,&rcv_addr_len))<0){
643                 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
644         }
645
646         if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
647                 dbgprintf(1, "Packet smaller than possible ICMPv6 packet!\n");
648                 free(rcv_addr.gen);
649                 return;
650         }
651
652         icmp6=(struct icmp6_hdr*)rbuffer;
653         if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
654                         && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
655                 dbgprintf(1, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
656                 free(rcv_addr.gen);
657                 return;
658         }
659
660         /*Check packet size again*/
661         if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
662                 dbgprintf(1, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
663                 free(rcv_addr.gen);
664                 return;
665         }
666
667         /*Decode IPv6 header*/
668         ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
669         if(memcmp(&src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(src_addr.ipv6->sin6_addr))!=0){
670                 /*Source address doesn't match*/
671                 free(rcv_addr.gen);
672                 return;
673         }
674         if(memcmp(&dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(dest_addr.ipv6->sin6_addr))!=0){
675                 /*Destination address doesn't match*/
676                 free(rcv_addr.gen);
677                 return;
678         }
679         if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
680                 /*Not DCCP!*/
681                 free(rcv_addr.gen);
682                 return;
683         }
684
685         /*Decode DCCP header*/
686         dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
687         if(dhdr->dccph_dport!=htons(dest_port)){
688                 /*DCCP Destination Ports don't match*/
689                 free(rcv_addr.gen);
690                 return;
691         }
692         if(dhdr->dccph_sport!=htons(dest_port)){
693                 /*DCCP Source Ports don't match*/
694                 free(rcv_addr.gen);
695                 return;
696         }
697         dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
698
699
700
701         /*Log*/
702         if(icmp6->icmp6_type==ICMP6_DST_UNREACH){
703                 type=DEST_UNREACHABLE;
704         }
705         if(icmp6->icmp6_type==ICMP6_PACKET_TOO_BIG){
706                 type=TOO_BIG;
707         }
708         if(icmp6->icmp6_type==ICMP6_TIME_EXCEEDED){
709                 type=TTL_EXPIRATION;
710         }
711         if(icmp6->icmp6_type==ICMP6_PARAM_PROB){
712                 type=PARAMETER_PROBLEM;
713         }
714         logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),type);
715         free(rcv_addr.gen);
716         return;
717 }
718
719 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
720         struct dccp_hdr *dhdr;
721         struct dccp_hdr_ext *dhdre;
722         struct dccp_hdr_request *dhdrr;
723         struct iphdr* ip4hdr;
724         struct ip6_hdr* ip6hdr;
725
726         int ip_hdr_len;
727         int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
728
729         if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
730                 dbgprintf(0, "Error: Insufficient buffer space\n");
731                 exit(1);
732         }
733
734         memset(buffer, 0, *len);
735
736         /*IP header*/
737         ip4hdr=NULL;
738         if(ip_type==AF_INET){
739                 ip_hdr_len=sizeof(struct iphdr);
740                 ip4hdr=(struct iphdr*)buffer;
741                 ip4hdr->check=htons(0);
742                 memcpy(&ip4hdr->daddr, &dest_addr.ipv4->sin_addr, sizeof(dest_addr.ipv4->sin_addr));
743                 ip4hdr->frag_off=htons(0);
744                 ip4hdr->id=htons(1);//first
745                 ip4hdr->ihl=5;
746                 ip4hdr->protocol=IPPROTO_DCCP;
747                 memcpy(&ip4hdr->saddr, &src_addr.ipv4->sin_addr, sizeof(src_addr.ipv4->sin_addr));
748                 ip4hdr->tos=0;
749                 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
750                 ip4hdr->ttl=ttl;
751                 ip4hdr->version=4;
752         }else{
753                 ip_hdr_len=sizeof(struct ip6_hdr);
754                 ip6hdr=(struct ip6_hdr*)buffer;
755                 memcpy(&ip6hdr->ip6_dst, &dest_addr.ipv6->sin6_addr, sizeof(dest_addr.ipv6->sin6_addr));
756                 memcpy(&ip6hdr->ip6_src, &src_addr.ipv6->sin6_addr, sizeof(src_addr.ipv6->sin6_addr));
757                 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
758                 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=ttl;
759                 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
760                 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
761         }
762
763         /*DCCP header*/
764         dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
765         dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
766         dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
767         dhdr->dccph_ccval=0;
768         dhdr->dccph_checksum=0;
769         dhdr->dccph_cscov=0;
770         dhdr->dccph_doff=dccp_hdr_len/4;
771         dhdr->dccph_dport=htons(dest_port);
772         dhdr->dccph_reserved=0;
773         dhdr->dccph_sport=htons(dest_port);
774         dhdr->dccph_x=1;
775         dhdr->dccph_type=DCCP_PKT_REQUEST;
776         dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
777         dhdr->dccph_seq=htonl(0);  //High 16bits of sequence number. Always make 0 for simplicity.
778         dhdre->dccph_seq_low=htonl(seq);
779         dhdrr->dccph_req_service= htonl(0x50455246);
780
781         /*Checksums*/
782         if(ip_type==AF_INET){
783                 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
784                                 (unsigned char*) &dest_addr.ipv4->sin_addr,
785                                 (unsigned char*)&src_addr.ipv4->sin_addr, IPPROTO_DCCP);
786                 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
787         }else{
788                 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
789                                 (unsigned char*) &dest_addr.ipv6->sin6_addr,
790                                 (unsigned char*)&src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
791         }
792         *len=ip_hdr_len+dccp_hdr_len;
793         return;
794 }
795
796 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
797         struct dccp_hdr *dhdr;
798         struct dccp_hdr_ext *dhdre;
799         struct iphdr* ip4hdr;
800
801         int ip_hdr_len;
802         int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
803
804         /*IP header*/
805         ip4hdr=NULL;
806         if(ip_type==AF_INET){
807                 ip_hdr_len=sizeof(struct iphdr);
808                 ip4hdr=(struct iphdr*)buffer;
809                 ip4hdr->check=htons(0);
810                 ip4hdr->id=htons(seq);
811         }else{
812                 ip_hdr_len=sizeof(struct ip6_hdr);
813         }
814
815         /*DCCP header*/
816         dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
817         dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
818         dhdr->dccph_checksum=0;
819         dhdre->dccph_seq_low=htonl(seq);
820
821         /*Checksums*/
822         if(ip_type==AF_INET){
823                 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
824                                 (unsigned char*) &dest_addr.ipv4->sin_addr,
825                                 (unsigned char*)&src_addr.ipv4->sin_addr, IPPROTO_DCCP);
826                 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
827         }else{
828                 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
829                                 (unsigned char*) &dest_addr.ipv6->sin6_addr,
830                                 (unsigned char*)&src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
831         }
832         *len=ip_hdr_len+dccp_hdr_len;
833         return;
834 }
835
836 int logPacket(int seq){
837         struct request *tmp;
838
839         /*Add new request to queue*/
840         tmp=malloc(sizeof(struct request));
841         if(tmp==NULL){
842                 dbgprintf(0,"Error: Can't allocate Memory!\n");
843                 exit(1);
844         }
845         tmp->next=NULL;
846         tmp->prev=NULL;
847         tmp->num_replies=0;
848         tmp->num_errors=0;
849         tmp->seq=seq;
850         tmp->reply_type=UNKNOWN;
851         gettimeofday(&tmp->sent,NULL);
852
853         if(queue.head==NULL){
854                 queue.head=queue.tail=tmp;
855         }else{
856                 queue.head->prev=tmp;
857                 tmp->next=queue.head;
858                 queue.head=tmp;
859         }
860
861         /*Update Statistics*/
862         if(ping_stats.requests_sent==0){
863                 gettimeofday(&ping_stats.start,NULL);
864         }
865         ping_stats.requests_sent++;
866         return 0;
867 }
868
869 int logResponse(ipaddr_ptr_t *src, int seq, int type){
870         struct request *cur;
871         double diff;
872         char pbuf[1000];
873
874         if(queue.tail==NULL){
875                 dbgprintf(1,"Response received but no requests sent!\n");
876                 return -1;
877         }
878
879         /*Locate request*/
880         cur=queue.tail;
881         while(cur!=NULL){
882                 if(cur->seq==seq){
883                         gettimeofday(&cur->reply,NULL);
884                         if(cur->num_replies>0){
885                                 dbgprintf(0,"Duplicate packet detected! (%i)\n", seq);
886                         }
887                         if(type<DEST_UNREACHABLE && type!=UNKNOWN){
888                                 cur->num_replies++;
889                         }else{
890                                 cur->num_errors++;
891                         }
892                         cur->reply_type=type;
893                         break;
894                 }
895                 cur=cur->prev;
896         }
897
898         if(cur==NULL){
899                 dbgprintf(1,"Response received but no requests sent with sequence number %i!\n", seq);
900                 return -1;
901         }
902
903         diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec);
904         diff=diff/1000.0;
905
906         /*Print Message*/
907         if(type<DEST_UNREACHABLE && type!=UNKNOWN){
908                 if(ip_type==AF_INET){
909                         dbgprintf(0, "Response from %s : seq=%i  time=%.1fms  status=%s\n",
910                                         inet_ntop(ip_type, (void*)&src->ipv4->sin_addr, pbuf, 1000),
911                                         seq, diff,response_label[type]);
912                 }else{
913                         dbgprintf(0, "Response from %s : seq=%i  time=%.1fms  status=%s\n",
914                                         inet_ntop(ip_type, (void*)&src->ipv6->sin6_addr, pbuf, 1000),
915                                         seq, diff,response_label[type]);
916                 }
917         }else{
918                 if(ip_type==AF_INET){
919                         dbgprintf(0, "%s from %s : seq=%i\n",response_label[type],
920                                         inet_ntop(ip_type, (void*)&src->ipv4->sin_addr, pbuf, 1000),
921                                         seq);
922                 }else{
923                         dbgprintf(0, "%s from %s : seq=%i\n",response_label[type],
924                                         inet_ntop(ip_type, (void*)&src->ipv6->sin6_addr, pbuf, 1000),
925                                         seq);
926                 }
927         }
928
929         /*Update statistics*/
930         if(type<DEST_UNREACHABLE && type!=UNKNOWN){
931                 /*Good Response*/
932                 if(cur->num_replies==1){
933                         ping_stats.rtt_avg=((ping_stats.replies_received*ping_stats.rtt_avg)+(diff))/(ping_stats.replies_received+1);
934                         ping_stats.replies_received++;
935                 }else{
936                         ping_stats.errors++;
937                 }
938                 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
939                         ping_stats.rtt_min=diff;
940                 }
941                 if(diff > ping_stats.rtt_max){
942                         ping_stats.rtt_max=diff;
943                 }
944         }else{
945                 /*Error*/
946                 ping_stats.errors++;
947         }
948         gettimeofday(&ping_stats.stop,NULL);
949         return 0;
950 }
951
952 void clearQueue(){
953         struct request *cur;
954         struct request *tmp;
955
956         cur=queue.head;
957         while(cur!=NULL){
958                 tmp=cur;
959                 cur=cur->next;
960                 free(tmp);
961         }
962         queue.head=NULL;
963         queue.tail=NULL;
964         return;
965 }
966
967 void sigHandler(){
968         char pbuf[1000];
969         int diff;
970         double ploss;
971
972         /*Print Stats*/
973         if(ip_type==AF_INET){
974                 dbgprintf(0,"-----------%s PING STATISTICS-----------\n",
975                                 inet_ntop(ip_type, (void*)&dest_addr.ipv4->sin_addr, pbuf, 1000));
976         }else if(ip_type==AF_INET6){
977                 dbgprintf(0,"-----------%s PING STATISTICS-----------\n",
978                                 inet_ntop(ip_type, (void*)&dest_addr.ipv6->sin6_addr, pbuf, 1000));
979         }
980         diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
981                         (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
982         diff=diff/1000.0;
983         ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
984         dbgprintf(0,"%i packets transmitted, %i received, %i errors, %.2f%% loss, time %ims\n",
985                         ping_stats.requests_sent,ping_stats.replies_received,ping_stats.errors,
986                         ploss,diff);
987         dbgprintf(0,"rtt min/avg/max = %.1f/%.1f/%.1f ms\n",
988                         ping_stats.rtt_min,ping_stats.rtt_avg,ping_stats.rtt_max);
989
990
991         /*Exit Quickly*/
992         count=0;
993 }
994
995 /*Usage information for program*/
996 void usage()
997 {
998         dbgprintf(0, "dccpping: [-d] [-6|-4] [-c count] [-p port] [-i interval] [-t ttl] [-S srcaddress] remote_host\n");
999         exit(0);
1000 }
1001
1002 /*Program will probably be run setuid, so be extra careful*/
1003 void sanitize_environment()
1004 {
1005 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1006         clearenv();
1007 #else
1008         extern char **environ;
1009         environ = NULL;
1010 #endif
1011 }
1012
1013 /*Debug Printf*/
1014 void dbgprintf(int level, const char *fmt, ...)
1015 {
1016     va_list args;
1017     if(debug>=level){
1018         va_start(args, fmt);
1019         vfprintf(stderr, fmt, args);
1020         va_end(args);
1021     }
1022 }