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.1
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;
185 struct timeval start;
190 int count; /*Number of pings (-1 is infinity)*/
191 int no_resolve; /*1 if we shouldn't resolve IP addresses*/
192 int dest_port; /*Destination port*/
193 int src_port; /*Source port---used to encode pid*/
195 long interval; /*Delay between pings in ms*/
196 int ip_type; /*IPv4 or IPv6*/
197 ipaddr_ptr_t dest_addr; /*Destination Address*/
198 ipaddr_ptr_t src_addr; /*Source Address*/
199 int dccp_socket; /*DCCP Socket used to grab src addr/port*/
200 char* hostname; /*Originally requested hostname*/
201 unsigned int service_code;/*DCCP Service Code*/
205 int debug=0; /*set to 1 to turn on debugging information*/
206 struct request_queue queue; /*Queue of requests to track RTT/duplicate information*/
207 struct stats ping_stats; /*Ping Statistics*/
208 struct params parms; /*Parameters for ping*/
209 char addr2str_buf[1000]; /*Buffer for printing addresses*/
210 char addr2nm_buf[1000]; /*Buffer for printing addresses*/
211 char addr2both_buf[1000]; /*Buffer for printing addresses*/
214 void getAddresses(char *src, char* dst);
216 void handleDCCPpacket(int rcv_socket, int send_socket);
217 void handleICMP4packet(int rcv_socket);
218 void handleICMP6packet(int rcv_socket);
219 void buildRequestPacket(unsigned char* buffer, int *len, int seq);
220 void updateRequestPacket(unsigned char* buffer, int *len, int seq);
221 int logPacket(int req_seq, int packet_seq);
222 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2);
223 const char *get_error_string(int type, int v1, int v2);
227 char* addr2str(ipaddr_ptr_t *res, int nores);
230 void sanitize_environment();
231 void dbgprintf(int level, const char *fmt, ...);
232 static long llsqrt(long long a);
234 /*Parse commandline options*/
235 int main(int argc, char *argv[])
245 ping_stats.replies_received=0;
246 ping_stats.requests_sent=0;
247 ping_stats.rtt_sum=0;
248 ping_stats.rtt_sum2=0;
249 ping_stats.duplicates=0;
250 ping_stats.rtt_max=0;
251 ping_stats.rtt_min=0;
254 parms.dest_port=DEFAULT_PORT;
256 parms. interval=1000;
257 parms.ip_type=AF_UNSPEC;
258 parms.dest_addr.gen=NULL;
259 parms.src_addr.gen=NULL;
260 parms.dccp_socket=-1;
263 parms.service_code=DEFAULT_SERVICE_CODE;
265 sanitize_environment();
267 while ((c = getopt(argc, argv, "64vVhnc:p:i:t:S:s:")) != -1) {
270 parms.ip_type=AF_INET6;
273 parms.ip_type=AF_INET;
276 parms.count = atoi(optarg);
278 dbgprintf(0, "Error: count must be positive");
283 parms.dest_port = atoi(optarg);
286 parms.interval = (long)(atof(optarg) * 1000.0);
287 if (parms.interval <= 0) {
288 dbgprintf(0, "Error: Invalid interval\n");
299 parms.ttl = atoi(optarg);
300 if (parms.ttl < 1 || parms.ttl > 255) {
301 dbgprintf(0,"Error: Invalid TTL\n");
306 parms.service_code=strtol(optarg,&tmp,0);
308 dbgprintf(0,"Error: Invalid Service Code\n");
311 if(parms.service_code<=0){
312 dbgprintf(0, "Error: Service Code MUST be positive");
323 /*Intentional Fall-through*/
337 parms.hostname=argv[0];
339 getAddresses(src, dst);
340 if(parms.src_addr.gen==NULL || parms.dest_addr.gen==NULL){
341 dbgprintf(0,"Error: Can't determine source or destination address\n");
345 signal(SIGINT, sigHandler);
348 free(parms.src_addr.gen);
349 free(parms.dest_addr.gen);
350 close(parms.dccp_socket);
355 void getAddresses(char *src, char* dst){
356 struct addrinfo hint;
357 struct addrinfo *dtmp, *stmp;
358 struct ifaddrs *temp, *cur;
360 struct sockaddr_in6* iv61;
361 struct sockaddr_in6* iv62;
362 struct sockaddr_in* iv41;
363 struct sockaddr_in* iv42;
368 /*Lookup destination Address*/
369 memset(&hint,0,sizeof(struct addrinfo));
370 hint.ai_family=parms.ip_type;
371 hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG;
373 if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){
374 dbgprintf(0,"Error: Couldn't lookup destination %s (%s)\n", dst, gai_strerror(err));
378 dbgprintf(0,"Error: Unknown Host %s\n", dst);
381 addrlen=dtmp->ai_addrlen;
382 hint.ai_family=parms.ip_type=dtmp->ai_family;
383 parms.dest_addr.gen=malloc(dtmp->ai_addrlen);
384 if(parms.dest_addr.gen==NULL){
385 dbgprintf(0,"Error: Can't allocate Memory\n");
388 memcpy(parms.dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen);
389 parms.dest_addr.gen->sa_family=dtmp->ai_family;
393 /*Get a meaningful source address*/
395 /*Use Commandline arg*/
397 /*Convert arg to address*/
398 if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){
399 dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err));
403 dbgprintf(0,"Error: Unknown Host %s\n", dst);
407 /*Compare to interface addresses*/
411 if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=stmp->ai_family){
412 /*Not matching ipv4/ipv6 of dest*/
416 if(stmp->ai_family==AF_INET){
417 iv41=(struct sockaddr_in*)stmp->ai_addr;
418 iv42=(struct sockaddr_in*)cur->ifa_addr;
419 if(memcmp(&iv41->sin_addr,&iv42->sin_addr, sizeof(iv41->sin_addr))==0){
420 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
421 if(parms.src_addr.gen==NULL){
422 dbgprintf(0,"Error: Can't allocate Memory\n");
425 parms.src_addr.gen->sa_family=parms.ip_type;
426 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
430 iv61=(struct sockaddr_in6*)stmp->ai_addr;
431 iv62=(struct sockaddr_in6*)cur->ifa_addr;
432 if(memcmp(&iv61->sin6_addr,&iv62->sin6_addr, sizeof(iv61->sin6_addr))==0){
433 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
434 if(parms.src_addr.gen==NULL){
435 dbgprintf(0,"Error: Can't allocate Memory\n");
438 parms.src_addr.gen->sa_family=parms.ip_type;
439 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
445 if(parms.src_addr.gen==NULL){
446 ipv.gen=(struct sockaddr*)stmp->ai_addr;
447 dbgprintf(0,"Error: Source Address %s does not belong to any interface!\n",addr2str(&ipv,1));
454 /*Create socket to auto respond for open connections and reserve a source port*/
455 parms.dccp_socket=socket(parms.ip_type,SOCK_DCCP, IPPROTO_DCCP);
456 if(parms.dccp_socket<0){
457 dbgprintf(0, "Error: Failed opening DCCP Socket (%s)\n",strerror(errno));
460 fcntl(parms.dccp_socket, F_SETFL, O_NONBLOCK);
463 if(parms.src_addr.gen==NULL){
464 /*Auto-detect source address*/
465 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
466 if(parms.src_addr.gen==NULL){
467 dbgprintf(0,"Error: Can't allocate Memory\n");
470 memset(parms.src_addr.gen,0,sizeof(struct sockaddr_storage));
471 parms.src_addr.gen->sa_family=parms.ip_type;
473 /*Bind to the given source address*/
474 if(bind(parms.dccp_socket,parms.src_addr.gen,sizeof(struct sockaddr_storage))<0){
475 dbgprintf(0, "Error: Failed bind() on DCCP socket (%s)\n",strerror(errno));
479 opt=htonl(parms.service_code);
480 if(setsockopt(parms.dccp_socket,SOL_DCCP, DCCP_SOCKOPT_SERVICE,&opt,sizeof(opt))<0){
481 dbgprintf(0, "Error: Failed setsockopt() on DCCP socket (%s)\n",strerror(errno));
485 /*Connect socket to get source address/port*/
486 if(parms.ip_type==AF_INET){
487 parms.dest_addr.ipv4->sin_port=htons(parms.dest_port);
489 parms.dest_addr.ipv6->sin6_port=htons(parms.dest_port);
491 if(connect(parms.dccp_socket,parms.dest_addr.gen,sizeof(struct sockaddr_storage))<0){
492 if(errno!=EINPROGRESS){
493 dbgprintf(0, "Error: Failed connect() on DCCP socket (%s)\n",strerror(errno));
498 /*Get source address and port number!*/
499 addrlen=sizeof(struct sockaddr_storage);
500 if(getsockname(parms.dccp_socket,parms.src_addr.gen,(socklen_t*)&addrlen)<0){
501 dbgprintf(0, "Error: Failed getsockname() on DCCP socket (%s)\n",strerror(errno));
504 if(parms.ip_type==AF_INET){
505 parms.src_port=ntohs(parms.src_addr.ipv4->sin_port);
506 parms.dest_addr.ipv4->sin_port=0;
508 parms.src_port=ntohs(parms.src_addr.ipv6->sin6_port);
509 parms.dest_addr.ipv6->sin6_port=0;
514 /*Preform the ping functionality*/
520 unsigned char sbuffer[slen];
522 struct timeval timeout;
523 struct timeval t,delay, add;
528 rs=socket(parms.ip_type, SOCK_RAW ,IPPROTO_RAW);
530 dbgprintf(0, "Error opening raw socket\n");
533 ds=socket(parms.ip_type, SOCK_RAW ,IPPROTO_DCCP);
535 dbgprintf(0, "Error opening raw DCCP socket\n");
538 is4=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMP);
540 dbgprintf(0,"Error opening raw ICMPv4 socket\n");
543 is6=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMPV6);
545 dbgprintf(0,"Error opening raw ICMPv6 socket\n");
550 /*Build DCCP packet*/
552 buildRequestPacket(sbuffer,&slen,packet_seq);
553 if(parms.ip_type==AF_INET){
554 addrlen=sizeof(struct sockaddr_in);
556 addrlen=sizeof(struct sockaddr_in6);
560 printf("PINGING %s (%s) on DCCP port %i\n",parms.hostname, addr2str(&parms.dest_addr,1),parms.dest_port);
564 if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)parms.dest_addr.gen,addrlen)<0){
566 dbgprintf(0,"Error: sendto() failed (%s)\n",strerror(errno));
569 if(parms.count==0){done=1; break;}
571 if (logPacket(request_seq,packet_seq)<0){
572 dbgprintf(0,"Error: Couldn't record request!\n");
574 dbgprintf(2, "Sending DCCP Request to %s\n", addr2str(&parms.dest_addr,1));
576 /*Use select to wait on packets or until interval has passed*/
577 add.tv_sec=parms.interval/1000;
578 add.tv_usec=(parms.interval%1000)*1000;
579 gettimeofday(&t,NULL);
580 timeradd(&t,&add,&delay);
581 while(timercmp(&t,&delay,<)){
582 /*Prepare for select*/
587 timersub(&delay,&t,&timeout);
590 if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
592 dbgprintf(0,"Select() error (%s)\n",strerror(errno));
595 if(parms.count==0){done=1;break;}
597 if(FD_ISSET(ds,&sel)){
598 /*Data on the DCCP socket*/
599 handleDCCPpacket(ds,rs);
602 if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){
603 /*Data on the ICMPv4 socket*/
604 handleICMP4packet(is4);
606 if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){
607 /*Data on the ICMPv6 socket*/
608 handleICMP6packet(is6);
610 gettimeofday(&t,NULL);
619 updateRequestPacket(sbuffer,&slen, packet_seq);
630 void handleDCCPpacket(int rcv_socket, int send_socket){
632 unsigned char rbuffer[rlen];
633 ipaddr_ptr_t rcv_addr;
634 socklen_t rcv_addr_len;
635 struct dccp_hdr *dhdr;
636 struct dccp_hdr_reset *dhdr_re;
637 struct dccp_hdr_response *dhdr_rp;
638 struct dccp_hdr_ack_bits *dhdr_sync;
642 /*Memory for socket address*/
643 rcv_addr_len=sizeof(struct sockaddr_storage);
644 rcv_addr.gen=malloc(rcv_addr_len);
645 if(rcv_addr.gen==NULL){
646 dbgprintf(0,"Error: Can't Allocate Memory!\n");
651 rcv_addr_len=sizeof(struct sockaddr_storage);
652 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
654 dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
661 if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type
662 dbgprintf(2, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6");
667 if(rcv_addr.gen->sa_family==AF_INET){
669 if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr,
670 sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination
671 dbgprintf(2,"DCCP packet from 3rd host\n");
675 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
677 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
681 iph=(struct iphdr*)rbuffer;
682 ptr=rbuffer+iph->ihl*4;
685 if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr,
686 sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination
687 dbgprintf(2,"DCCP packet from 3rd host\n");
691 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
693 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
701 dhdr=(struct dccp_hdr*)ptr;
702 if(dhdr->dccph_sport!=htons(parms.dest_port)){
703 dbgprintf(2,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
707 if(dhdr->dccph_dport!=htons(parms.src_port)){
708 dbgprintf(2,"DCCP packet with wrong Destination Port\n");
714 if(dhdr->dccph_type==DCCP_PKT_RESET){
715 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
716 dbgprintf(2, "Tossing DCCP Reset packet that's small!\n");
719 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
722 if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){
723 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0);
725 /*Nothing else to do*/
727 if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
728 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
729 dbgprintf(2, "Tossing DCCP Response packet that's too small!\n");
734 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
735 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0);
737 /*DCCP socket opened in getAddresses() will send Reset*/
739 if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
740 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
741 dbgprintf(2, "Tossing DCCP Sync/SyncAck packet that's too small!\n");
746 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
747 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0);
749 /*DCCP socket opened in getAddresses() will send Reset*/
755 void handleICMP4packet(int rcv_socket){
757 unsigned char rbuffer[rlen];
758 ipaddr_ptr_t rcv_addr;
759 socklen_t rcv_addr_len;
760 struct icmphdr *icmp4;
761 struct dccp_hdr *dhdr;
762 struct dccp_hdr_ext *dhdre;
763 struct iphdr* ip4hdr;
766 /*Memory for socket address*/
767 rcv_addr_len=sizeof(struct sockaddr_storage);
768 rcv_addr.gen=malloc(rcv_addr_len);
769 if(rcv_addr.gen==NULL){
770 dbgprintf(0,"Error: Can't Allocate Memory!\n");
775 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
777 dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
784 iph=(struct iphdr*)rbuffer;
787 if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size
788 dbgprintf(2, "Packet smaller than possible ICMPv4 packet!\n");
793 icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4);
794 if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
795 dbgprintf(2, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
800 /*Check packet size again*/
801 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+4){
802 dbgprintf(2, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
807 /*Decode IPv4 header*/
808 ip4hdr=(struct iphdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr));
809 if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){
810 /*Source address doesn't match*/
811 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n");
815 if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){
816 /*Destination address doesn't match*/
817 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n");
821 if(ip4hdr->protocol!=IPPROTO_DCCP){
823 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n");
828 /*Decode DCCP header*/
829 dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4);
830 if(dhdr->dccph_dport!=htons(parms.dest_port)){
831 /*DCCP Destination Ports don't match*/
832 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n");
836 if(dhdr->dccph_sport!=htons(parms.src_port)){
837 /*DCCP Source Ports don't match*/
838 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n");
842 dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
845 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
846 logResponse(&rcv_addr,-1,ICMPv4,icmp4->type,icmp4->code);
848 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code);
854 void handleICMP6packet(int rcv_socket){
856 unsigned char rbuffer[rlen];
857 ipaddr_ptr_t rcv_addr;
858 socklen_t rcv_addr_len;
859 struct icmp6_hdr *icmp6;
860 struct ip6_hdr* ip6hdr;
861 struct dccp_hdr *dhdr;
862 struct dccp_hdr_ext *dhdre;
864 /*Memory for socket address*/
865 rcv_addr_len=sizeof(struct sockaddr_storage);
866 rcv_addr.gen=malloc(rcv_addr_len);
867 if(rcv_addr.gen==NULL){
868 dbgprintf(0,"Error: Can't Allocate Memory!\n");
873 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
874 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
877 if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
878 dbgprintf(2, "Packet smaller than possible ICMPv6 packet!\n");
883 icmp6=(struct icmp6_hdr*)rbuffer;
884 if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
885 && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
886 dbgprintf(2, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
891 /*Check packet size again*/
892 if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
893 dbgprintf(2, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
898 /*Decode IPv6 header*/
899 ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
900 if(memcmp(&parms.src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){
901 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n");
902 /*Source address doesn't match*/
906 if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){
907 /*Destination address doesn't match*/
908 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n");
912 if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
914 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n");
919 /*Decode DCCP header*/
920 dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
921 if(dhdr->dccph_dport!=htons(parms.dest_port)){
922 /*DCCP Destination Ports don't match*/
923 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n");
927 if(dhdr->dccph_sport!=htons(parms.src_port)){
928 /*DCCP Source Ports don't match*/
929 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n");
933 dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
936 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code);
941 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
942 struct dccp_hdr *dhdr;
943 struct dccp_hdr_ext *dhdre;
944 struct dccp_hdr_request *dhdrr;
945 struct iphdr* ip4hdr;
946 struct ip6_hdr* ip6hdr;
949 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
951 if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
952 dbgprintf(0, "Error: Insufficient buffer space\n");
956 memset(buffer, 0, *len);
960 if(parms.ip_type==AF_INET){
961 ip_hdr_len=sizeof(struct iphdr);
962 ip4hdr=(struct iphdr*)buffer;
963 ip4hdr->check=htons(0);
964 memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr));
965 ip4hdr->frag_off=htons(0);
966 ip4hdr->id=htons(1);//first
968 ip4hdr->protocol=IPPROTO_DCCP;
969 memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr));
971 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
972 ip4hdr->ttl=parms.ttl;
975 ip_hdr_len=sizeof(struct ip6_hdr);
976 ip6hdr=(struct ip6_hdr*)buffer;
977 memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr));
978 memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr));
979 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
980 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl;
981 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
982 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
986 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
987 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
988 dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
990 dhdr->dccph_checksum=0;
992 dhdr->dccph_doff=dccp_hdr_len/4;
993 dhdr->dccph_dport=htons(parms.dest_port);
994 dhdr->dccph_reserved=0;
995 dhdr->dccph_sport=htons(parms.src_port);
997 dhdr->dccph_type=DCCP_PKT_REQUEST;
998 dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
999 dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity.
1000 dhdre->dccph_seq_low=htonl(seq);
1001 dhdrr->dccph_req_service=htonl(parms.service_code);
1004 if(parms.ip_type==AF_INET){
1005 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1006 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1007 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1008 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1010 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1011 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1012 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1014 *len=ip_hdr_len+dccp_hdr_len;
1018 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
1019 struct dccp_hdr *dhdr;
1020 struct dccp_hdr_ext *dhdre;
1021 struct iphdr* ip4hdr;
1024 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
1028 if(parms.ip_type==AF_INET){
1029 ip_hdr_len=sizeof(struct iphdr);
1030 ip4hdr=(struct iphdr*)buffer;
1031 ip4hdr->check=htons(0);
1032 ip4hdr->id=htons(seq);
1034 ip_hdr_len=sizeof(struct ip6_hdr);
1038 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
1039 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
1040 dhdr->dccph_checksum=0;
1041 dhdre->dccph_seq_low=htonl(seq);
1044 if(parms.ip_type==AF_INET){
1045 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1046 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1047 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1048 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1050 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1051 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1052 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1054 *len=ip_hdr_len+dccp_hdr_len;
1058 int logPacket(int req_seq, int packet_seq){
1059 struct request *tmp;
1061 /*Add new request to queue*/
1062 tmp=malloc(sizeof(struct request));
1064 dbgprintf(0,"Error: Can't allocate Memory!\n");
1071 tmp->packet_seq=packet_seq;
1072 tmp->request_seq=req_seq;
1073 tmp->reply_type=UNKNOWN;
1074 gettimeofday(&tmp->sent,NULL);
1076 if(queue.head==NULL){
1077 queue.head=queue.tail=tmp;
1079 queue.head->prev=tmp;
1080 tmp->next=queue.head;
1084 /*Update Statistics*/
1085 if(ping_stats.requests_sent==0){
1086 gettimeofday(&ping_stats.start,NULL);
1088 ping_stats.requests_sent++;
1092 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){
1093 struct request *cur;
1096 if(queue.tail==NULL){
1097 dbgprintf(2,"Response received but no requests sent!\n");
1104 if(cur->packet_seq==seq){
1105 gettimeofday(&cur->reply,NULL);
1106 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1111 cur->reply_type=type;
1118 if(parms.ip_type==AF_INET && seq==-1){
1119 /*IPv4 didn't include enough of the packet to get sequence numbers!*/
1120 printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0));
1121 ping_stats.errors++;
1124 dbgprintf(2,"Response received but no requests sent with sequence number %i!\n", seq);
1129 diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec);
1132 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1134 printf( "Response from %s : seq=%i time=%.1fms\n",addr2str(src,0),cur->request_seq, diff/1000.0);
1136 printf( "Response from %s : seq=%i time=%.1fms status=%s\n",
1137 addr2str(src,0),cur->request_seq, diff/1000.0,response_good[type]);
1141 printf("%s from %s : seq=%i\n",get_error_string(type,v1,v2),addr2str(src,0),cur->request_seq);
1144 /*Update statistics*/
1145 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1147 if(cur->num_replies==1){
1148 ping_stats.rtt_sum+=diff;
1149 ping_stats.rtt_sum2+=(diff*diff);
1150 ping_stats.replies_received++;
1152 printf("Duplicate packet detected! (%i)\n",cur->request_seq);
1153 ping_stats.duplicates++;
1155 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
1156 ping_stats.rtt_min=diff;
1158 if(diff > ping_stats.rtt_max){
1159 ping_stats.rtt_max=diff;
1163 ping_stats.errors++;
1165 gettimeofday(&ping_stats.stop,NULL);
1169 const char *get_error_string(int type, int v1, int v2){
1170 const char *label=NULL;
1173 if(v1>11){label=NULL;break;}
1174 label=response_dccp_reset[v1];
1179 if(v2>15){label=NULL;break;}
1180 label=response_icmpv4_dest_unreach[v2];
1183 if(v2>1){label=NULL;break;}
1184 label=response_icmpv4_ttl[v2];
1194 if(v2>7){label=NULL;break;}
1195 label=response_icmpv6_dest_unreach[v2];
1198 if(v2>0){label=NULL;break;}
1199 label=response_icmpv6_packet_too_big;
1202 if(v2>1){label=NULL;break;}
1203 label=response_icmpv6_ttl[v2];
1206 if(v2>2){label=NULL;break;}
1207 label=response_icmpv6_param_prob[v2];
1219 struct request *cur;
1220 struct request *tmp;
1240 double ploss, rtt_avg, rtt_avg2, rtt_mdev;
1243 if(ping_stats.replies_received>0){
1244 rtt_avg=ping_stats.rtt_sum/(ping_stats.replies_received*1.0);
1245 rtt_avg2=ping_stats.rtt_sum2/(ping_stats.replies_received*1.0);
1246 rtt_mdev=llsqrt(rtt_avg2 - (rtt_avg*rtt_avg));
1252 diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
1253 (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
1257 gettimeofday(&ping_stats.stop,NULL);
1258 printf("-----------%s PING STATISTICS-----------\n",parms.hostname);
1259 ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
1260 printf("%i packets transmitted, %i received, ",ping_stats.requests_sent,ping_stats.replies_received);
1261 if(ping_stats.duplicates>0){
1262 printf("%i duplicates, ",ping_stats.duplicates);
1264 if(ping_stats.errors>0){
1265 printf("%i errors, ",ping_stats.errors);
1267 printf("%.2f%% loss, time %ims\n",ploss,diff);
1268 if(ping_stats.replies_received>ping_stats.requests_sent){
1269 printf("+Somebody is creating packets out of thing air!\n");
1271 printf("rtt min/avg/max/mdev = %.1f/%.1f/%.1f/%.1f ms\n",
1272 ping_stats.rtt_min/1000.0,rtt_avg/1000.0,ping_stats.rtt_max/1000.0,rtt_mdev/1000.0);
1276 char* addr2str(ipaddr_ptr_t *res, int nores){
1279 if (!res->gen->sa_family)
1282 if(res->gen->sa_family==AF_INET){
1283 size=sizeof(struct sockaddr_in);
1284 }else if(res->gen->sa_family==AF_INET6){
1285 size=sizeof(struct sockaddr_in6);
1289 if((ret=getnameinfo(res->gen, size,
1290 addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){
1291 dbgprintf(0,"Error: getnameinfo() returned %s\n",gai_strerror(ret));
1294 if (parms.no_resolve||nores){
1295 return addr2str_buf;
1297 addr2nm_buf[0] = '\0';
1298 getnameinfo(res->gen, size,
1299 addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN);
1300 snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf);
1301 return addr2both_buf;
1306 /*Usage information for program*/
1309 dbgprintf(0, "dccpping: [-v] [-V] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n");
1310 dbgprintf(0, " [-t ttl] [-s service_code] [-S srcaddress] remote_host\n");
1312 dbgprintf(0, " -v Verbose. May be repeated for aditional verbosity.\n");
1313 dbgprintf(0, " -V Version information\n");
1314 dbgprintf(0, " -h Help\n");
1315 dbgprintf(0, " -n Numeric output only\n");
1316 dbgprintf(0, " -6 Force IPv6 mode\n");
1317 dbgprintf(0, " -4 Force IPv4 mode\n");
1322 dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero <sj323707@ohio.edu>\n", DCCPPING_VERSION);
1323 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
1324 dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n");
1328 /*Program will probably be run setuid, so be extra careful*/
1329 void sanitize_environment()
1331 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1334 extern char **environ;
1340 void dbgprintf(int level, const char *fmt, ...)
1344 va_start(args, fmt);
1345 vfprintf(stderr, fmt, args);
1350 /*Square Root function for longs*/
1351 /*Borrowed from iputils/ping_common.c*/
1352 /* http://www.skbuff.net/iputils/ */
1353 static long llsqrt(long long a)
1355 long long prev = ~((long long)1 << 63);