1 /******************************************************************************
2 Utility to ping hosts using DCCP Request packets to test for DCCP connectivity.
4 Copyright (C) 2012 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>
21 ******************************************************************************/
31 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <netinet/ip.h>
36 #include <netinet/ip6.h>
37 #include <netinet/in.h>
38 #include <netinet/ip_icmp.h>
39 #include <netinet/icmp6.h>
40 #include <arpa/inet.h>
44 #include <linux/dccp.h>
45 #include "checksums.h"
48 #define DEFAULT_SERVICE_CODE 0x50455246
49 #define DEFAULT_PORT 33434
52 #define DCCPPING_VERSION 1.0
53 #define MAX(x,y) (x>y ? x : y)
63 /*Structure for simpler IPv4/IPv6 Address handling*/
66 struct sockaddr_in *ipv4;
67 struct sockaddr_in6 *ipv6;
70 /*Possible Responses*/
80 /*Output strings corresponding to Possible Errors*/
81 static const char* response_good[] = {
83 "Closed Port (Reset)",
84 "Open Port (Response)",
90 static const char* response_dccp_reset[] = {
105 static const char* response_icmpv4_dest_unreach[] = {
106 "Destination Network Unreachable",
107 "Destination Host Unreachable",
108 "Destination Protocol Unreachable",
109 "Destination Port Unreachable",
110 "Fragmentation Required",
111 "Source Routing Failed",
112 "Destination Network Unknown",
113 "Destination Host Unknown",
114 "Source Host Isolated",
115 "Network Administratively Prohibited",
116 "Host Administratively Prohibited",
117 "Network Unreachable for Type of Service",
118 "Host Unreachable for Type of Service",
119 "Communication Administratively Prohibited",
120 "Host Precedence Violation",
121 "Presedence Cutoff in Effect"
124 static const char* response_icmpv4_ttl[] = {
126 "Fragment Reassembly Failed"
129 static const char* response_icmpv6_dest_unreach[] = {
130 "No Route to Destination",
131 "Communication Administratively Prohibited",
132 "Beyond Scope of Source Address",
133 "Address Unreachable",
135 "Source Failed Ingress/Eggress Policy",
136 "Rejected Source Route",
137 "Error in Source Routing"
140 static const char* response_icmpv6_packet_too_big = "Packet Too Big";
142 static const char* response_icmpv6_ttl[] = {
144 "Fragment Reassembly Failed"
147 static const char* response_icmpv6_param_prob[]={
148 "Erroneous Header Field",
149 "Unrecognized Next Header (DCCP not supported)",
150 "Unrecognized IPv6 Option"
156 /*Structure to keep track of information about a request*/
163 struct timeval reply;
164 enum response_type reply_type;
165 struct request *next;
166 struct request *prev;
169 /*Request Queue head structure*/
170 struct request_queue{
171 struct request *head;
172 struct request *tail;
175 /*Statistics about the requests and replies sent*/
178 int replies_received;
183 struct timeval start;
188 int count; /*Number of pings (-1 is infinity)*/
189 int no_resolve; /*1 if we shouldn't resolve IP addresses*/
190 int dest_port; /*Destination port*/
191 int src_port; /*Source port---used to encode pid*/
193 long interval; /*Delay between pings in ms*/
194 int ip_type; /*IPv4 or IPv6*/
195 ipaddr_ptr_t dest_addr; /*Destination Address*/
196 ipaddr_ptr_t src_addr; /*Source Address*/
197 int dccp_socket; /*DCCP Socket used to grab src addr/port*/
198 char* hostname; /*Originally requested hostname*/
199 unsigned int service_code;/*DCCP Service Code*/
203 int debug=0; /*set to 1 to turn on debugging information*/
204 struct request_queue queue; /*Queue of requests to track RTT/duplicate information*/
205 struct stats ping_stats; /*Ping Statistics*/
206 struct params parms; /*Parameters for ping*/
207 char addr2str_buf[1000]; /*Buffer for printing addresses*/
208 char addr2nm_buf[1000]; /*Buffer for printing addresses*/
209 char addr2both_buf[1000]; /*Buffer for printing addresses*/
212 void getAddresses(char *src, char* dst);
214 void handleDCCPpacket(int rcv_socket, int send_socket);
215 void handleICMP4packet(int rcv_socket);
216 void handleICMP6packet(int rcv_socket);
217 void buildRequestPacket(unsigned char* buffer, int *len, int seq);
218 void updateRequestPacket(unsigned char* buffer, int *len, int seq);
219 int logPacket(int req_seq, int packet_seq);
220 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2);
221 const char *get_error_string(int type, int v1, int v2);
224 char* addr2str(ipaddr_ptr_t *res, int nores);
227 void sanitize_environment();
228 void dbgprintf(int level, const char *fmt, ...);
231 /*Parse commandline options*/
232 int main(int argc, char *argv[])
242 ping_stats.replies_received=0;
243 ping_stats.requests_sent=0;
244 ping_stats.rtt_avg=0;
245 ping_stats.rtt_max=0;
246 ping_stats.rtt_min=0;
249 parms.dest_port=DEFAULT_PORT;
251 parms. interval=1000;
252 parms.ip_type=AF_UNSPEC;
253 parms.dest_addr.gen=NULL;
254 parms.src_addr.gen=NULL;
255 parms.dccp_socket=-1;
258 parms.service_code=DEFAULT_SERVICE_CODE;
260 sanitize_environment();
262 while ((c = getopt(argc, argv, "64vVhnc:p:i:t:S:s:")) != -1) {
265 parms.ip_type=AF_INET6;
268 parms.ip_type=AF_INET;
271 parms.count = atoi(optarg);
273 dbgprintf(0, "Error: count must be positive");
278 parms.dest_port = atoi(optarg);
281 parms.interval = (long)(atof(optarg) * 1000.0);
282 if (parms.interval <= 0) {
283 dbgprintf(0, "Error: Invalid interval\n");
294 parms.ttl = atoi(optarg);
295 if (parms.ttl < 1 || parms.ttl > 255) {
296 dbgprintf(0,"Error: Invalid TTL\n");
301 parms.service_code=strtol(optarg,&tmp,0);
303 dbgprintf(0,"Error: Invalid Service Code\n");
306 if(parms.service_code<=0){
307 dbgprintf(0, "Error: Service Code MUST be positive");
318 /*Intentional Fall-through*/
332 parms.hostname=argv[0];
334 getAddresses(src, dst);
335 if(parms.src_addr.gen==NULL || parms.dest_addr.gen==NULL){
336 dbgprintf(0,"Error: Can't determine source or destination address\n");
340 signal(SIGINT, sigHandler);
343 free(parms.src_addr.gen);
344 free(parms.dest_addr.gen);
345 close(parms.dccp_socket);
350 void getAddresses(char *src, char* dst){
351 struct addrinfo hint;
352 struct addrinfo *dtmp, *stmp;
353 struct ifaddrs *temp, *cur;
355 struct sockaddr_in6* iv61;
356 struct sockaddr_in6* iv62;
357 struct sockaddr_in* iv41;
358 struct sockaddr_in* iv42;
363 /*Lookup destination Address*/
364 memset(&hint,0,sizeof(struct addrinfo));
365 hint.ai_family=parms.ip_type;
366 hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG;
368 if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){
369 dbgprintf(0,"Error: Couldn't lookup destination %s (%s)\n", dst, gai_strerror(err));
373 dbgprintf(0,"Error: Unknown Host %s\n", dst);
376 addrlen=dtmp->ai_addrlen;
377 hint.ai_family=parms.ip_type=dtmp->ai_family;
378 parms.dest_addr.gen=malloc(dtmp->ai_addrlen);
379 if(parms.dest_addr.gen==NULL){
380 dbgprintf(0,"Error: Can't allocate Memory\n");
383 memcpy(parms.dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen);
384 parms.dest_addr.gen->sa_family=dtmp->ai_family;
388 /*Get a meaningful source address*/
390 /*Use Commandline arg*/
392 /*Convert arg to address*/
393 if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){
394 dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err));
398 dbgprintf(0,"Error: Unknown Host %s\n", dst);
402 /*Compare to interface addresses*/
406 if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=stmp->ai_family){
407 /*Not matching ipv4/ipv6 of dest*/
411 if(stmp->ai_family==AF_INET){
412 iv41=(struct sockaddr_in*)stmp->ai_addr;
413 iv42=(struct sockaddr_in*)cur->ifa_addr;
414 if(memcmp(&iv41->sin_addr,&iv42->sin_addr, sizeof(iv41->sin_addr))==0){
415 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
416 if(parms.src_addr.gen==NULL){
417 dbgprintf(0,"Error: Can't allocate Memory\n");
420 parms.src_addr.gen->sa_family=parms.ip_type;
421 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
425 iv61=(struct sockaddr_in6*)stmp->ai_addr;
426 iv62=(struct sockaddr_in6*)cur->ifa_addr;
427 if(memcmp(&iv61->sin6_addr,&iv62->sin6_addr, sizeof(iv61->sin6_addr))==0){
428 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
429 if(parms.src_addr.gen==NULL){
430 dbgprintf(0,"Error: Can't allocate Memory\n");
433 parms.src_addr.gen->sa_family=parms.ip_type;
434 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
440 if(parms.src_addr.gen==NULL){
441 ipv.gen=(struct sockaddr*)stmp->ai_addr;
442 dbgprintf(0,"Error: Source Address %s does not belong to any interface!\n",addr2str(&ipv,1));
449 /*Create socket to auto respond for open connections and reserve a source port*/
450 parms.dccp_socket=socket(parms.ip_type,SOCK_DCCP, IPPROTO_DCCP);
451 if(parms.dccp_socket<0){
452 dbgprintf(0, "Error: Failed opening DCCP Socket (%s)\n",strerror(errno));
455 fcntl(parms.dccp_socket, F_SETFL, O_NONBLOCK);
458 if(parms.src_addr.gen==NULL){
459 /*Auto-detect source address*/
460 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
461 if(parms.src_addr.gen==NULL){
462 dbgprintf(0,"Error: Can't allocate Memory\n");
465 memset(parms.src_addr.gen,0,sizeof(struct sockaddr_storage));
466 parms.src_addr.gen->sa_family=parms.ip_type;
468 /*Bind to the given source address*/
469 if(bind(parms.dccp_socket,parms.src_addr.gen,sizeof(struct sockaddr_storage))<0){
470 dbgprintf(0, "Error: Failed bind() on DCCP socket (%s)\n",strerror(errno));
474 opt=htonl(parms.service_code);
475 if(setsockopt(parms.dccp_socket,SOL_DCCP, DCCP_SOCKOPT_SERVICE,&opt,sizeof(opt))<0){
476 dbgprintf(0, "Error: Failed setsockopt() on DCCP socket (%s)\n",strerror(errno));
480 /*Connect socket to get source address/port*/
481 if(parms.ip_type==AF_INET){
482 parms.dest_addr.ipv4->sin_port=htons(parms.dest_port);
484 parms.dest_addr.ipv6->sin6_port=htons(parms.dest_port);
486 if(connect(parms.dccp_socket,parms.dest_addr.gen,sizeof(struct sockaddr_storage))<0){
487 if(errno!=EINPROGRESS){
488 dbgprintf(0, "Error: Failed connect() on DCCP socket (%s)\n",strerror(errno));
493 /*Get source address and port number!*/
494 addrlen=sizeof(struct sockaddr_storage);
495 if(getsockname(parms.dccp_socket,parms.src_addr.gen,(socklen_t*)&addrlen)<0){
496 dbgprintf(0, "Error: Failed getsockname() on DCCP socket (%s)\n",strerror(errno));
499 if(parms.ip_type==AF_INET){
500 parms.src_port=ntohs(parms.src_addr.ipv4->sin_port);
501 parms.dest_addr.ipv4->sin_port=0;
503 parms.src_port=ntohs(parms.src_addr.ipv6->sin6_port);
504 parms.dest_addr.ipv6->sin6_port=0;
509 /*Preform the ping functionality*/
515 unsigned char sbuffer[slen];
517 struct timeval timeout;
518 struct timeval t,delay, add;
523 rs=socket(parms.ip_type, SOCK_RAW ,IPPROTO_RAW);
525 dbgprintf(0, "Error opening raw socket\n");
528 ds=socket(parms.ip_type, SOCK_RAW ,IPPROTO_DCCP);
530 dbgprintf(0, "Error opening raw DCCP socket\n");
533 is4=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMP);
535 dbgprintf(0,"Error opening raw ICMPv4 socket\n");
538 is6=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMPV6);
540 dbgprintf(0,"Error opening raw ICMPv6 socket\n");
545 /*Build DCCP packet*/
547 buildRequestPacket(sbuffer,&slen,packet_seq);
548 if(parms.ip_type==AF_INET){
549 addrlen=sizeof(struct sockaddr_in);
551 addrlen=sizeof(struct sockaddr_in6);
555 printf("PINGING %s (%s) on DCCP port %i\n",parms.hostname, addr2str(&parms.dest_addr,1),parms.dest_port);
559 if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)parms.dest_addr.gen,addrlen)<0){
561 dbgprintf(0,"Error: sendto() failed (%s)\n",strerror(errno));
564 if(parms.count==0){done=1; break;}
566 if (logPacket(request_seq,packet_seq)<0){
567 dbgprintf(0,"Error: Couldn't record request!\n");
569 dbgprintf(2, "Sending DCCP Request to %s\n", addr2str(&parms.dest_addr,1));
571 /*Use select to wait on packets or until interval has passed*/
572 add.tv_sec=parms.interval/1000;
573 add.tv_usec=(parms.interval%1000)*1000;
574 gettimeofday(&t,NULL);
575 timeradd(&t,&add,&delay);
576 while(timercmp(&t,&delay,<)){
577 /*Prepare for select*/
582 timersub(&delay,&t,&timeout);
585 if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
587 dbgprintf(0,"Select() error (%s)\n",strerror(errno));
590 if(parms.count==0){done=1;break;}
592 if(FD_ISSET(ds,&sel)){
593 /*Data on the DCCP socket*/
594 handleDCCPpacket(ds,rs);
597 if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){
598 /*Data on the ICMPv4 socket*/
599 handleICMP4packet(is4);
601 if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){
602 /*Data on the ICMPv6 socket*/
603 handleICMP6packet(is6);
605 gettimeofday(&t,NULL);
614 updateRequestPacket(sbuffer,&slen, packet_seq);
623 void handleDCCPpacket(int rcv_socket, int send_socket){
625 unsigned char rbuffer[rlen];
626 ipaddr_ptr_t rcv_addr;
627 socklen_t rcv_addr_len;
628 struct dccp_hdr *dhdr;
629 struct dccp_hdr_reset *dhdr_re;
630 struct dccp_hdr_response *dhdr_rp;
631 struct dccp_hdr_ack_bits *dhdr_sync;
635 /*Memory for socket address*/
636 rcv_addr_len=sizeof(struct sockaddr_storage);
637 rcv_addr.gen=malloc(rcv_addr_len);
638 if(rcv_addr.gen==NULL){
639 dbgprintf(0,"Error: Can't Allocate Memory!\n");
644 rcv_addr_len=sizeof(struct sockaddr_storage);
645 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
647 dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
654 if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type
655 dbgprintf(2, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6");
660 if(rcv_addr.gen->sa_family==AF_INET){
662 if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr,
663 sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination
664 dbgprintf(2,"DCCP packet from 3rd host\n");
668 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
670 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
674 iph=(struct iphdr*)rbuffer;
675 ptr=rbuffer+iph->ihl*4;
678 if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr,
679 sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination
680 dbgprintf(2,"DCCP packet from 3rd host\n");
684 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
686 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
694 dhdr=(struct dccp_hdr*)ptr;
695 if(dhdr->dccph_sport!=htons(parms.dest_port)){
696 dbgprintf(2,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
700 if(dhdr->dccph_dport!=htons(parms.src_port)){
701 dbgprintf(2,"DCCP packet with wrong Destination Port\n");
707 if(dhdr->dccph_type==DCCP_PKT_RESET){
708 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
709 dbgprintf(2, "Tossing DCCP Reset packet that's small!\n");
712 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
715 if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){
716 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0);
718 /*Nothing else to do*/
720 if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
721 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
722 dbgprintf(2, "Tossing DCCP Response packet that's too small!\n");
727 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
728 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0);
730 /*DCCP socket opened in getAddresses() will send Reset*/
732 if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
733 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
734 dbgprintf(2, "Tossing DCCP Sync/SyncAck packet that's too small!\n");
739 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
740 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0);
742 /*DCCP socket opened in getAddresses() will send Reset*/
748 void handleICMP4packet(int rcv_socket){
750 unsigned char rbuffer[rlen];
751 ipaddr_ptr_t rcv_addr;
752 socklen_t rcv_addr_len;
753 struct icmphdr *icmp4;
754 struct dccp_hdr *dhdr;
755 struct dccp_hdr_ext *dhdre;
756 struct iphdr* ip4hdr;
759 /*Memory for socket address*/
760 rcv_addr_len=sizeof(struct sockaddr_storage);
761 rcv_addr.gen=malloc(rcv_addr_len);
762 if(rcv_addr.gen==NULL){
763 dbgprintf(0,"Error: Can't Allocate Memory!\n");
768 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
770 dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
777 iph=(struct iphdr*)rbuffer;
780 if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size
781 dbgprintf(2, "Packet smaller than possible ICMPv4 packet!\n");
786 icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4);
787 if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
788 dbgprintf(2, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
793 /*Check packet size again*/
794 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+4){
795 dbgprintf(2, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
800 /*Decode IPv4 header*/
801 ip4hdr=(struct iphdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr));
802 if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){
803 /*Source address doesn't match*/
804 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n");
808 if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){
809 /*Destination address doesn't match*/
810 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n");
814 if(ip4hdr->protocol!=IPPROTO_DCCP){
816 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n");
821 /*Decode DCCP header*/
822 dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4);
823 if(dhdr->dccph_dport!=htons(parms.dest_port)){
824 /*DCCP Destination Ports don't match*/
825 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n");
829 if(dhdr->dccph_sport!=htons(parms.src_port)){
830 /*DCCP Source Ports don't match*/
831 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n");
835 dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
838 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
839 logResponse(&rcv_addr,-1,ICMPv4,icmp4->type,icmp4->code);
841 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code);
847 void handleICMP6packet(int rcv_socket){
849 unsigned char rbuffer[rlen];
850 ipaddr_ptr_t rcv_addr;
851 socklen_t rcv_addr_len;
852 struct icmp6_hdr *icmp6;
853 struct ip6_hdr* ip6hdr;
854 struct dccp_hdr *dhdr;
855 struct dccp_hdr_ext *dhdre;
857 /*Memory for socket address*/
858 rcv_addr_len=sizeof(struct sockaddr_storage);
859 rcv_addr.gen=malloc(rcv_addr_len);
860 if(rcv_addr.gen==NULL){
861 dbgprintf(0,"Error: Can't Allocate Memory!\n");
866 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
867 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
870 if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
871 dbgprintf(2, "Packet smaller than possible ICMPv6 packet!\n");
876 icmp6=(struct icmp6_hdr*)rbuffer;
877 if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
878 && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
879 dbgprintf(2, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
884 /*Check packet size again*/
885 if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
886 dbgprintf(2, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
891 /*Decode IPv6 header*/
892 ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
893 if(memcmp(&parms.src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){
894 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n");
895 /*Source address doesn't match*/
899 if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){
900 /*Destination address doesn't match*/
901 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n");
905 if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
907 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n");
912 /*Decode DCCP header*/
913 dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
914 if(dhdr->dccph_dport!=htons(parms.dest_port)){
915 /*DCCP Destination Ports don't match*/
916 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n");
920 if(dhdr->dccph_sport!=htons(parms.src_port)){
921 /*DCCP Source Ports don't match*/
922 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n");
926 dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
929 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code);
934 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
935 struct dccp_hdr *dhdr;
936 struct dccp_hdr_ext *dhdre;
937 struct dccp_hdr_request *dhdrr;
938 struct iphdr* ip4hdr;
939 struct ip6_hdr* ip6hdr;
942 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
944 if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
945 dbgprintf(0, "Error: Insufficient buffer space\n");
949 memset(buffer, 0, *len);
953 if(parms.ip_type==AF_INET){
954 ip_hdr_len=sizeof(struct iphdr);
955 ip4hdr=(struct iphdr*)buffer;
956 ip4hdr->check=htons(0);
957 memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr));
958 ip4hdr->frag_off=htons(0);
959 ip4hdr->id=htons(1);//first
961 ip4hdr->protocol=IPPROTO_DCCP;
962 memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr));
964 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
965 ip4hdr->ttl=parms.ttl;
968 ip_hdr_len=sizeof(struct ip6_hdr);
969 ip6hdr=(struct ip6_hdr*)buffer;
970 memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr));
971 memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr));
972 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
973 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl;
974 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
975 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
979 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
980 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
981 dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
983 dhdr->dccph_checksum=0;
985 dhdr->dccph_doff=dccp_hdr_len/4;
986 dhdr->dccph_dport=htons(parms.dest_port);
987 dhdr->dccph_reserved=0;
988 dhdr->dccph_sport=htons(parms.src_port);
990 dhdr->dccph_type=DCCP_PKT_REQUEST;
991 dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
992 dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity.
993 dhdre->dccph_seq_low=htonl(seq);
994 dhdrr->dccph_req_service=htonl(parms.service_code);
997 if(parms.ip_type==AF_INET){
998 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
999 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1000 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1001 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1003 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1004 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1005 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1007 *len=ip_hdr_len+dccp_hdr_len;
1011 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
1012 struct dccp_hdr *dhdr;
1013 struct dccp_hdr_ext *dhdre;
1014 struct iphdr* ip4hdr;
1017 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
1021 if(parms.ip_type==AF_INET){
1022 ip_hdr_len=sizeof(struct iphdr);
1023 ip4hdr=(struct iphdr*)buffer;
1024 ip4hdr->check=htons(0);
1025 ip4hdr->id=htons(seq);
1027 ip_hdr_len=sizeof(struct ip6_hdr);
1031 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
1032 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
1033 dhdr->dccph_checksum=0;
1034 dhdre->dccph_seq_low=htonl(seq);
1037 if(parms.ip_type==AF_INET){
1038 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1039 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1040 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1041 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1043 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1044 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1045 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1047 *len=ip_hdr_len+dccp_hdr_len;
1051 int logPacket(int req_seq, int packet_seq){
1052 struct request *tmp;
1054 /*Add new request to queue*/
1055 tmp=malloc(sizeof(struct request));
1057 dbgprintf(0,"Error: Can't allocate Memory!\n");
1064 tmp->packet_seq=packet_seq;
1065 tmp->request_seq=req_seq;
1066 tmp->reply_type=UNKNOWN;
1067 gettimeofday(&tmp->sent,NULL);
1069 if(queue.head==NULL){
1070 queue.head=queue.tail=tmp;
1072 queue.head->prev=tmp;
1073 tmp->next=queue.head;
1077 /*Update Statistics*/
1078 if(ping_stats.requests_sent==0){
1079 gettimeofday(&ping_stats.start,NULL);
1081 ping_stats.requests_sent++;
1085 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){
1086 struct request *cur;
1089 if(queue.tail==NULL){
1090 dbgprintf(2,"Response received but no requests sent!\n");
1097 if(cur->packet_seq==seq){
1098 gettimeofday(&cur->reply,NULL);
1099 if(cur->num_replies>0){
1100 printf("Duplicate packet detected! (%i)\n",cur->request_seq);
1102 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1107 cur->reply_type=type;
1114 if(parms.ip_type==AF_INET && seq==-1){
1115 /*IPv4 didn't include enough of the packet to get sequence numbers!*/
1116 printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0));
1117 ping_stats.errors++;
1120 dbgprintf(2,"Response received but no requests sent with sequence number %i!\n", seq);
1125 diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec);
1129 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1131 printf( "Response from %s : seq=%i time=%.1fms\n",addr2str(src,0),cur->request_seq, diff);
1133 printf( "Response from %s : seq=%i time=%.1fms status=%s\n",
1134 addr2str(src,0),cur->request_seq, diff,response_good[type]);
1138 printf("%s from %s : seq=%i\n",get_error_string(type,v1,v2),addr2str(src,0),cur->request_seq);
1141 /*Update statistics*/
1142 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1144 if(cur->num_replies==1){
1145 ping_stats.rtt_avg=((ping_stats.replies_received*ping_stats.rtt_avg)+(diff))/(ping_stats.replies_received+1);
1146 ping_stats.replies_received++;
1148 ping_stats.errors++;
1150 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
1151 ping_stats.rtt_min=diff;
1153 if(diff > ping_stats.rtt_max){
1154 ping_stats.rtt_max=diff;
1158 ping_stats.errors++;
1160 gettimeofday(&ping_stats.stop,NULL);
1164 const char *get_error_string(int type, int v1, int v2){
1165 const char *label=NULL;
1168 if(v1>11){label=NULL;break;}
1169 label=response_dccp_reset[v1];
1174 if(v2>15){label=NULL;break;}
1175 label=response_icmpv4_dest_unreach[v2];
1178 if(v2>1){label=NULL;break;}
1179 label=response_icmpv4_ttl[v2];
1189 if(v2>7){label=NULL;break;}
1190 label=response_icmpv6_dest_unreach[v2];
1193 if(v2>0){label=NULL;break;}
1194 label=response_icmpv6_packet_too_big;
1197 if(v2>1){label=NULL;break;}
1198 label=response_icmpv6_ttl[v2];
1201 if(v2>2){label=NULL;break;}
1202 label=response_icmpv6_param_prob[v2];
1214 struct request *cur;
1215 struct request *tmp;
1233 gettimeofday(&ping_stats.stop,NULL);
1234 printf("-----------%s PING STATISTICS-----------\n",parms.hostname);
1236 diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
1237 (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
1239 ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
1240 printf("%i packets transmitted, %i received, %i errors, %.2f%% loss, time %ims\n",
1241 ping_stats.requests_sent,ping_stats.replies_received,ping_stats.errors,
1243 printf("rtt min/avg/max = %.1f/%.1f/%.1f ms\n",
1244 ping_stats.rtt_min,ping_stats.rtt_avg,ping_stats.rtt_max);
1251 char* addr2str(ipaddr_ptr_t *res, int nores){
1254 if (!res->gen->sa_family)
1257 if(res->gen->sa_family==AF_INET){
1258 size=sizeof(struct sockaddr_in);
1259 }else if(res->gen->sa_family==AF_INET6){
1260 size=sizeof(struct sockaddr_in6);
1264 if((ret=getnameinfo(res->gen, size,
1265 addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){
1266 dbgprintf(0,"Error: getnameinfo() returned %s\n",gai_strerror(ret));
1269 if (parms.no_resolve||nores){
1270 return addr2str_buf;
1272 addr2nm_buf[0] = '\0';
1273 getnameinfo(res->gen, size,
1274 addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN);
1275 snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf);
1276 return addr2both_buf;
1281 /*Usage information for program*/
1284 dbgprintf(0, "dccpping: [-v] [-V] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n");
1285 dbgprintf(0, " [-t ttl] [-s service_code] [-S srcaddress] remote_host\n");
1287 dbgprintf(0, " -v Verbose. May be repeated for aditional verbosity.\n");
1288 dbgprintf(0, " -V Version information\n");
1289 dbgprintf(0, " -h Help\n");
1290 dbgprintf(0, " -n Numeric output only\n");
1291 dbgprintf(0, " -6 Force IPv6 mode\n");
1292 dbgprintf(0, " -4 Force IPv4 mode\n");
1297 dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero <sj323707@ohio.edu>\n", DCCPPING_VERSION);
1298 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
1299 dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n");
1303 /*Program will probably be run setuid, so be extra careful*/
1304 void sanitize_environment()
1306 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1309 extern char **environ;
1315 void dbgprintf(int level, const char *fmt, ...)
1319 va_start(args, fmt);
1320 vfprintf(stderr, fmt, args);