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 /*Use the DCCP source port to multiplex DCCP Ping streams by PID*/
49 #define DCCP_SERVICE_CODE 0x50455246
50 #define DEFAULT_PORT 33434
52 #define DCCPPING_VERSION 1.0
53 #define MAX(x,y) (x>y ? x : y)
60 /*Structure for simpler IPv4/IPv6 Address handling*/
63 struct sockaddr_in *ipv4;
64 struct sockaddr_in6 *ipv6;
67 /*Possible Responses*/
77 /*Output strings corresponding to Possible Errors*/
78 static const char* response_good[] = {
80 "Closed Port (Reset)",
81 "Open Port (Response)",
87 static const char* response_dccp_reset[] = {
102 static const char* response_icmpv4_dest_unreach[] = {
103 "Destination Network Unreachable",
104 "Destination Host Unreachable",
105 "Destination Protocol Unreachable",
106 "Destination Port Unreachable",
107 "Fragmentation Required",
108 "Source Routing Failed",
109 "Destination Network Unknown",
110 "Destination Host Unknown",
111 "Source Host Isolated",
112 "Network Administratively Prohibited",
113 "Host Administratively Prohibited",
114 "Network Unreachable for Type of Service",
115 "Host Unreachable for Type of Service",
116 "Communication Administratively Prohibited",
117 "Host Precedence Violation",
118 "Presedence Cutoff in Effect"
121 static const char* response_icmpv4_ttl[] = {
123 "Fragment Reassembly Failed"
126 static const char* response_icmpv6_dest_unreach[] = {
127 "No Route to Destination",
128 "Communication Administratively Prohibited",
129 "Beyond Scope of Source Address",
130 "Address Unreachable",
132 "Source Failed Ingress/Eggress Policy",
133 "Rejected Source Route",
134 "Error in Source Routing"
137 static const char* response_icmpv6_packet_too_big = "Packet Too Big";
139 static const char* response_icmpv6_ttl[] = {
141 "Fragment Reassembly Failed"
144 static const char* response_icmpv6_param_prob[]={
145 "Erroneous Header Field",
146 "Unrecognized Next Header (DCCP not supported)",
147 "Unrecognized IPv6 Option"
153 /*Structure to keep track of information about a request*/
160 struct timeval reply;
161 enum response_type reply_type;
162 struct request *next;
163 struct request *prev;
166 /*Request Queue head structure*/
167 struct request_queue{
168 struct request *head;
169 struct request *tail;
172 /*Statistics about the requests and replies sent*/
175 int replies_received;
180 struct timeval start;
185 int count; /*Number of pings (-1 is infinity)*/
186 int no_resolve; /*1 if we shouldn't resolve IP addresses*/
187 int dest_port; /*Destination port*/
188 int src_port; /*Source port---used to encode pid*/
190 long interval; /*Delay between pings in ms*/
191 int ip_type; /*IPv4 or IPv6*/
192 ipaddr_ptr_t dest_addr; /*Destination Address*/
193 ipaddr_ptr_t src_addr; /*Source Address*/
194 int dccp_socket; /*DCCP Socket used to grab src addr/port*/
195 char* hostname; /*Originally requested hostname*/
199 int debug=0; /*set to 1 to turn on debugging information*/
200 struct request_queue queue; /*Queue of requests to track RTT/duplicate information*/
201 struct stats ping_stats; /*Ping Statistics*/
202 struct params parms; /*Parameters for ping*/
203 char addr2str_buf[1000]; /*Buffer for printing addresses*/
204 char addr2nm_buf[1000]; /*Buffer for printing addresses*/
205 char addr2both_buf[1000]; /*Buffer for printing addresses*/
208 void getAddresses(char *src, char* dst);
210 void handleDCCPpacket(int rcv_socket, int send_socket);
211 void handleICMP4packet(int rcv_socket);
212 void handleICMP6packet(int rcv_socket);
213 void buildRequestPacket(unsigned char* buffer, int *len, int seq);
214 void updateRequestPacket(unsigned char* buffer, int *len, int seq);
215 int logPacket(int req_seq, int packet_seq);
216 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2);
217 const char *get_error_string(int type, int v1, int v2);
220 char* addr2str(ipaddr_ptr_t *res, int nores);
223 void sanitize_environment();
224 void dbgprintf(int level, const char *fmt, ...);
227 /*Parse commandline options*/
228 int main(int argc, char *argv[])
237 ping_stats.replies_received=0;
238 ping_stats.requests_sent=0;
239 ping_stats.rtt_avg=0;
240 ping_stats.rtt_max=0;
241 ping_stats.rtt_min=0;
244 parms.dest_port=DEFAULT_PORT;
246 parms. interval=1000;
247 parms.ip_type=AF_UNSPEC;
248 parms.dest_addr.gen=NULL;
249 parms.src_addr.gen=NULL;
250 parms.dccp_socket=-1;
254 sanitize_environment();
256 while ((c = getopt(argc, argv, "64vhnc:p:i:dt:S:")) != -1) {
259 parms.ip_type=AF_INET6;
262 parms.ip_type=AF_INET;
265 parms.count = atoi(optarg);
267 dbgprintf(0, "Error: count must be positive");
272 parms.dest_port = atoi(optarg);
275 parms.interval = (long)(atof(optarg) * 1000.0);
276 if (parms.interval <= 0) {
277 dbgprintf(0, "Error: Invalid interval\n");
288 parms.ttl = atoi(optarg);
289 if (parms.ttl < 1 || parms.ttl > 255) {
290 dbgprintf(0,"Error: Invalid TTL\n");
300 /*Intentional Fall-through*/
314 parms.hostname=argv[0];
316 getAddresses(src, dst);
317 if(parms.src_addr.gen==NULL || parms.dest_addr.gen==NULL){
318 dbgprintf(0,"Error: Can't determine source or destination address\n");
322 signal(SIGINT, sigHandler);
325 free(parms.src_addr.gen);
326 free(parms.dest_addr.gen);
327 close(parms.dccp_socket);
332 void getAddresses(char *src, char* dst){
333 struct addrinfo hint;
334 struct addrinfo *dtmp, *stmp;
335 struct ifaddrs *temp, *cur;
337 struct sockaddr_in6* iv61;
338 struct sockaddr_in6* iv62;
339 struct sockaddr_in* iv41;
340 struct sockaddr_in* iv42;
344 /*Lookup destination Address*/
345 memset(&hint,0,sizeof(struct addrinfo));
346 hint.ai_family=parms.ip_type;
347 hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG;
349 if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){
350 dbgprintf(0,"Error: Couldn't lookup destination %s (%s)\n", dst, gai_strerror(err));
354 dbgprintf(0,"Error: Unknown Host %s\n", dst);
357 addrlen=dtmp->ai_addrlen;
358 hint.ai_family=parms.ip_type=dtmp->ai_family;
359 parms.dest_addr.gen=malloc(dtmp->ai_addrlen);
360 if(parms.dest_addr.gen==NULL){
361 dbgprintf(0,"Error: Can't allocate Memory\n");
364 memcpy(parms.dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen);
365 parms.dest_addr.gen->sa_family=dtmp->ai_family;
369 /*Get a meaningful source address*/
371 /*Use Commandline arg*/
373 /*Convert arg to address*/
374 if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){
375 dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err));
379 dbgprintf(0,"Error: Unknown Host %s\n", dst);
383 /*Compare to interface addresses*/
387 if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=stmp->ai_family){
388 /*Not matching ipv4/ipv6 of dest*/
392 if(stmp->ai_family==AF_INET){
393 iv41=(struct sockaddr_in*)stmp->ai_addr;
394 iv42=(struct sockaddr_in*)cur->ifa_addr;
395 if(memcmp(&iv41->sin_addr,&iv42->sin_addr, sizeof(iv41->sin_addr))==0){
396 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
397 if(parms.src_addr.gen==NULL){
398 dbgprintf(0,"Error: Can't allocate Memory\n");
401 parms.src_addr.gen->sa_family=parms.ip_type;
402 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
406 iv61=(struct sockaddr_in6*)stmp->ai_addr;
407 iv62=(struct sockaddr_in6*)cur->ifa_addr;
408 if(memcmp(&iv61->sin6_addr,&iv62->sin6_addr, sizeof(iv61->sin6_addr))==0){
409 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
410 if(parms.src_addr.gen==NULL){
411 dbgprintf(0,"Error: Can't allocate Memory\n");
414 parms.src_addr.gen->sa_family=parms.ip_type;
415 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
421 if(parms.src_addr.gen==NULL){
422 ipv.gen=(struct sockaddr*)stmp->ai_addr;
423 dbgprintf(0,"Error: Source Address %s does not belong to any interface!\n",addr2str(&ipv,1));
430 /*Create socket to auto respond for open connections and reserve a source port*/
431 parms.dccp_socket=socket(parms.ip_type,SOCK_DCCP, IPPROTO_DCCP);
432 if(parms.dccp_socket<0){
433 dbgprintf(0, "Error: Failed opening DCCP Socket (%s)\n",strerror(errno));
436 fcntl(parms.dccp_socket, F_SETFL, O_NONBLOCK);
439 if(parms.src_addr.gen==NULL){
440 /*Auto-detect source address*/
441 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
442 if(parms.src_addr.gen==NULL){
443 dbgprintf(0,"Error: Can't allocate Memory\n");
446 memset(parms.src_addr.gen,0,sizeof(struct sockaddr_storage));
447 parms.src_addr.gen->sa_family=parms.ip_type;
449 /*Bind to the given source address*/
450 if(bind(parms.dccp_socket,parms.src_addr.gen,sizeof(struct sockaddr_storage))<0){
451 dbgprintf(0, "Error: Failed bind() on DCCP socket (%s)\n",strerror(errno));
456 /*Connect socket to get source address/port*/
457 if(parms.ip_type==AF_INET){
458 parms.dest_addr.ipv4->sin_port=htons(parms.dest_port);
460 parms.dest_addr.ipv6->sin6_port=htons(parms.dest_port);
462 if(connect(parms.dccp_socket,parms.dest_addr.gen,sizeof(struct sockaddr_storage))<0){
463 if(errno!=EINPROGRESS){
464 dbgprintf(0, "Error: Failed connect() on DCCP socket (%s)\n",strerror(errno));
469 /*Get source address and port number!*/
470 addrlen=sizeof(struct sockaddr_storage);
471 if(getsockname(parms.dccp_socket,parms.src_addr.gen,(socklen_t*)&addrlen)<0){
472 dbgprintf(0, "Error: Failed getsockname() on DCCP socket (%s)\n",strerror(errno));
475 if(parms.ip_type==AF_INET){
476 parms.src_port=ntohs(parms.src_addr.ipv4->sin_port);
477 parms.dest_addr.ipv4->sin_port=0;
479 parms.src_port=ntohs(parms.src_addr.ipv6->sin6_port);
480 parms.dest_addr.ipv6->sin6_port=0;
485 /*Preform the ping functionality*/
491 unsigned char sbuffer[slen];
493 struct timeval timeout;
494 struct timeval t,delay, add;
499 rs=socket(parms.ip_type, SOCK_RAW ,IPPROTO_RAW);
501 dbgprintf(0, "Error opening raw socket\n");
504 ds=socket(parms.ip_type, SOCK_RAW ,IPPROTO_DCCP);
506 dbgprintf(0, "Error opening raw DCCP socket\n");
509 is4=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMP);
511 dbgprintf(0,"Error opening raw ICMPv4 socket\n");
514 is6=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMPV6);
516 dbgprintf(0,"Error opening raw ICMPv6 socket\n");
521 /*Build DCCP packet*/
523 buildRequestPacket(sbuffer,&slen,packet_seq);
524 if(parms.ip_type==AF_INET){
525 addrlen=sizeof(struct sockaddr_in);
527 addrlen=sizeof(struct sockaddr_in6);
531 printf("PINGING %s (%s) on DCCP port %i\n",parms.hostname, addr2str(&parms.dest_addr,1),parms.dest_port);
535 if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)parms.dest_addr.gen,addrlen)<0){
537 dbgprintf(0,"Error: sendto() failed (%s)\n",strerror(errno));
540 if(parms.count==0){done=1; break;}
542 if (logPacket(request_seq,packet_seq)<0){
543 dbgprintf(0,"Error: Couldn't record request!\n");
545 dbgprintf(2, "Sending DCCP Request to %s\n", addr2str(&parms.dest_addr,1));
547 /*Use select to wait on packets or until interval has passed*/
548 add.tv_sec=parms.interval/1000;
549 add.tv_usec=(parms.interval%1000)*1000;
550 gettimeofday(&t,NULL);
551 timeradd(&t,&add,&delay);
552 while(timercmp(&t,&delay,<)){
553 /*Prepare for select*/
558 timersub(&delay,&t,&timeout);
561 if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
563 dbgprintf(0,"Select() error (%s)\n",strerror(errno));
566 if(parms.count==0){done=1;break;}
568 if(FD_ISSET(ds,&sel)){
569 /*Data on the DCCP socket*/
570 handleDCCPpacket(ds,rs);
573 if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){
574 /*Data on the ICMPv4 socket*/
575 handleICMP4packet(is4);
577 if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){
578 /*Data on the ICMPv6 socket*/
579 handleICMP6packet(is6);
581 gettimeofday(&t,NULL);
590 updateRequestPacket(sbuffer,&slen, packet_seq);
599 void handleDCCPpacket(int rcv_socket, int send_socket){
601 unsigned char rbuffer[rlen];
602 ipaddr_ptr_t rcv_addr;
603 socklen_t rcv_addr_len;
604 struct dccp_hdr *dhdr;
605 struct dccp_hdr_reset *dhdr_re;
606 struct dccp_hdr_response *dhdr_rp;
607 struct dccp_hdr_ack_bits *dhdr_sync;
611 /*Memory for socket address*/
612 rcv_addr_len=sizeof(struct sockaddr_storage);
613 rcv_addr.gen=malloc(rcv_addr_len);
614 if(rcv_addr.gen==NULL){
615 dbgprintf(0,"Error: Can't Allocate Memory!\n");
620 rcv_addr_len=sizeof(struct sockaddr_storage);
621 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
623 dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
630 if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type
631 dbgprintf(2, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6");
636 if(rcv_addr.gen->sa_family==AF_INET){
638 if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr,
639 sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination
640 dbgprintf(2,"DCCP packet from 3rd host\n");
644 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
646 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
650 iph=(struct iphdr*)rbuffer;
651 ptr=rbuffer+iph->ihl*4;
654 if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr,
655 sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination
656 dbgprintf(2,"DCCP packet from 3rd host\n");
660 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
662 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
670 dhdr=(struct dccp_hdr*)ptr;
671 if(dhdr->dccph_sport!=htons(parms.dest_port)){
672 dbgprintf(2,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
676 if(dhdr->dccph_dport!=htons(parms.src_port)){
677 dbgprintf(2,"DCCP packet with wrong Destination Port\n");
683 if(dhdr->dccph_type==DCCP_PKT_RESET){
684 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
685 dbgprintf(2, "Tossing DCCP Reset packet that's small!\n");
688 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
691 if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){
692 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0);
694 /*Nothing else to do*/
696 if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
697 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
698 dbgprintf(2, "Tossing DCCP Response packet that's too small!\n");
703 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
704 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0);
706 /*DCCP socket opened in getAddresses() will send Reset*/
708 if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
709 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
710 dbgprintf(2, "Tossing DCCP Sync/SyncAck packet that's too small!\n");
715 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
716 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0);
718 /*DCCP socket opened in getAddresses() will send Reset*/
724 void handleICMP4packet(int rcv_socket){
726 unsigned char rbuffer[rlen];
727 ipaddr_ptr_t rcv_addr;
728 socklen_t rcv_addr_len;
729 struct icmphdr *icmp4;
730 struct dccp_hdr *dhdr;
731 struct dccp_hdr_ext *dhdre;
732 struct iphdr* ip4hdr;
735 /*Memory for socket address*/
736 rcv_addr_len=sizeof(struct sockaddr_storage);
737 rcv_addr.gen=malloc(rcv_addr_len);
738 if(rcv_addr.gen==NULL){
739 dbgprintf(0,"Error: Can't Allocate Memory!\n");
744 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
746 dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
753 iph=(struct iphdr*)rbuffer;
756 if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size
757 dbgprintf(2, "Packet smaller than possible ICMPv4 packet!\n");
762 icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4);
763 if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
764 dbgprintf(2, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
769 /*Check packet size again*/
770 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+4){
771 dbgprintf(2, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
776 /*Decode IPv4 header*/
777 ip4hdr=(struct iphdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr));
778 if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){
779 /*Source address doesn't match*/
780 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n");
784 if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){
785 /*Destination address doesn't match*/
786 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n");
790 if(ip4hdr->protocol!=IPPROTO_DCCP){
792 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n");
797 /*Decode DCCP header*/
798 dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4);
799 if(dhdr->dccph_dport!=htons(parms.dest_port)){
800 /*DCCP Destination Ports don't match*/
801 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n");
805 if(dhdr->dccph_sport!=htons(parms.src_port)){
806 /*DCCP Source Ports don't match*/
807 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n");
811 dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
814 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
815 logResponse(&rcv_addr,-1,ICMPv4,icmp4->type,icmp4->code);
817 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code);
823 void handleICMP6packet(int rcv_socket){
825 unsigned char rbuffer[rlen];
826 ipaddr_ptr_t rcv_addr;
827 socklen_t rcv_addr_len;
828 struct icmp6_hdr *icmp6;
829 struct ip6_hdr* ip6hdr;
830 struct dccp_hdr *dhdr;
831 struct dccp_hdr_ext *dhdre;
833 /*Memory for socket address*/
834 rcv_addr_len=sizeof(struct sockaddr_storage);
835 rcv_addr.gen=malloc(rcv_addr_len);
836 if(rcv_addr.gen==NULL){
837 dbgprintf(0,"Error: Can't Allocate Memory!\n");
842 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
843 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
846 if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
847 dbgprintf(2, "Packet smaller than possible ICMPv6 packet!\n");
852 icmp6=(struct icmp6_hdr*)rbuffer;
853 if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
854 && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
855 dbgprintf(2, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
860 /*Check packet size again*/
861 if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
862 dbgprintf(2, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
867 /*Decode IPv6 header*/
868 ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
869 if(memcmp(&parms.src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){
870 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n");
871 /*Source address doesn't match*/
875 if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){
876 /*Destination address doesn't match*/
877 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n");
881 if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
883 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n");
888 /*Decode DCCP header*/
889 dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
890 if(dhdr->dccph_dport!=htons(parms.dest_port)){
891 /*DCCP Destination Ports don't match*/
892 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n");
896 if(dhdr->dccph_sport!=htons(parms.src_port)){
897 /*DCCP Source Ports don't match*/
898 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n");
902 dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
905 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code);
910 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
911 struct dccp_hdr *dhdr;
912 struct dccp_hdr_ext *dhdre;
913 struct dccp_hdr_request *dhdrr;
914 struct iphdr* ip4hdr;
915 struct ip6_hdr* ip6hdr;
918 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
920 if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
921 dbgprintf(0, "Error: Insufficient buffer space\n");
925 memset(buffer, 0, *len);
929 if(parms.ip_type==AF_INET){
930 ip_hdr_len=sizeof(struct iphdr);
931 ip4hdr=(struct iphdr*)buffer;
932 ip4hdr->check=htons(0);
933 memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr));
934 ip4hdr->frag_off=htons(0);
935 ip4hdr->id=htons(1);//first
937 ip4hdr->protocol=IPPROTO_DCCP;
938 memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr));
940 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
941 ip4hdr->ttl=parms.ttl;
944 ip_hdr_len=sizeof(struct ip6_hdr);
945 ip6hdr=(struct ip6_hdr*)buffer;
946 memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr));
947 memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr));
948 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
949 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl;
950 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
951 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
955 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
956 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
957 dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
959 dhdr->dccph_checksum=0;
961 dhdr->dccph_doff=dccp_hdr_len/4;
962 dhdr->dccph_dport=htons(parms.dest_port);
963 dhdr->dccph_reserved=0;
964 dhdr->dccph_sport=htons(parms.src_port);
966 dhdr->dccph_type=DCCP_PKT_REQUEST;
967 dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
968 dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity.
969 dhdre->dccph_seq_low=htonl(seq);
970 dhdrr->dccph_req_service=htonl(DCCP_SERVICE_CODE);
973 if(parms.ip_type==AF_INET){
974 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
975 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
976 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
977 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
979 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
980 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
981 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
983 *len=ip_hdr_len+dccp_hdr_len;
987 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
988 struct dccp_hdr *dhdr;
989 struct dccp_hdr_ext *dhdre;
990 struct iphdr* ip4hdr;
993 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
997 if(parms.ip_type==AF_INET){
998 ip_hdr_len=sizeof(struct iphdr);
999 ip4hdr=(struct iphdr*)buffer;
1000 ip4hdr->check=htons(0);
1001 ip4hdr->id=htons(seq);
1003 ip_hdr_len=sizeof(struct ip6_hdr);
1007 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
1008 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
1009 dhdr->dccph_checksum=0;
1010 dhdre->dccph_seq_low=htonl(seq);
1013 if(parms.ip_type==AF_INET){
1014 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1015 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1016 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1017 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1019 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1020 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1021 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1023 *len=ip_hdr_len+dccp_hdr_len;
1027 int logPacket(int req_seq, int packet_seq){
1028 struct request *tmp;
1030 /*Add new request to queue*/
1031 tmp=malloc(sizeof(struct request));
1033 dbgprintf(0,"Error: Can't allocate Memory!\n");
1040 tmp->packet_seq=packet_seq;
1041 tmp->request_seq=req_seq;
1042 tmp->reply_type=UNKNOWN;
1043 gettimeofday(&tmp->sent,NULL);
1045 if(queue.head==NULL){
1046 queue.head=queue.tail=tmp;
1048 queue.head->prev=tmp;
1049 tmp->next=queue.head;
1053 /*Update Statistics*/
1054 if(ping_stats.requests_sent==0){
1055 gettimeofday(&ping_stats.start,NULL);
1057 ping_stats.requests_sent++;
1061 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){
1062 struct request *cur;
1065 if(queue.tail==NULL){
1066 dbgprintf(2,"Response received but no requests sent!\n");
1073 if(cur->packet_seq==seq){
1074 gettimeofday(&cur->reply,NULL);
1075 if(cur->num_replies>0){
1076 printf("Duplicate packet detected! (%i)\n",cur->request_seq);
1078 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1083 cur->reply_type=type;
1090 if(parms.ip_type==AF_INET && seq==-1){
1091 /*IPv4 didn't include enough of the packet to get sequence numbers!*/
1092 printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0));
1093 ping_stats.errors++;
1096 dbgprintf(2,"Response received but no requests sent with sequence number %i!\n", seq);
1101 diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec);
1105 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1107 printf( "Response from %s : seq=%i time=%.1fms\n",addr2str(src,0),cur->request_seq, diff);
1109 printf( "Response from %s : seq=%i time=%.1fms status=%s\n",
1110 addr2str(src,0),cur->request_seq, diff,response_good[type]);
1114 printf("%s from %s : seq=%i\n",get_error_string(type,v1,v2),addr2str(src,0),cur->request_seq);
1117 /*Update statistics*/
1118 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1120 if(cur->num_replies==1){
1121 ping_stats.rtt_avg=((ping_stats.replies_received*ping_stats.rtt_avg)+(diff))/(ping_stats.replies_received+1);
1122 ping_stats.replies_received++;
1124 ping_stats.errors++;
1126 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
1127 ping_stats.rtt_min=diff;
1129 if(diff > ping_stats.rtt_max){
1130 ping_stats.rtt_max=diff;
1134 ping_stats.errors++;
1136 gettimeofday(&ping_stats.stop,NULL);
1140 const char *get_error_string(int type, int v1, int v2){
1141 const char *label=NULL;
1144 if(v1>11){label=NULL;break;}
1145 label=response_dccp_reset[v1];
1150 if(v2>15){label=NULL;break;}
1151 label=response_icmpv4_dest_unreach[v2];
1154 if(v2>1){label=NULL;break;}
1155 label=response_icmpv4_ttl[v2];
1165 if(v2>7){label=NULL;break;}
1166 label=response_icmpv6_dest_unreach[v2];
1169 if(v2>0){label=NULL;break;}
1170 label=response_icmpv6_packet_too_big;
1173 if(v2>1){label=NULL;break;}
1174 label=response_icmpv6_ttl[v2];
1177 if(v2>2){label=NULL;break;}
1178 label=response_icmpv6_param_prob[v2];
1190 struct request *cur;
1191 struct request *tmp;
1209 gettimeofday(&ping_stats.stop,NULL);
1210 printf("-----------%s PING STATISTICS-----------\n",parms.hostname);
1212 diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
1213 (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
1215 ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
1216 printf("%i packets transmitted, %i received, %i errors, %.2f%% loss, time %ims\n",
1217 ping_stats.requests_sent,ping_stats.replies_received,ping_stats.errors,
1219 printf("rtt min/avg/max = %.1f/%.1f/%.1f ms\n",
1220 ping_stats.rtt_min,ping_stats.rtt_avg,ping_stats.rtt_max);
1227 char* addr2str(ipaddr_ptr_t *res, int nores){
1230 if (!res->gen->sa_family)
1233 if(res->gen->sa_family==AF_INET){
1234 size=sizeof(struct sockaddr_in);
1235 }else if(res->gen->sa_family==AF_INET6){
1236 size=sizeof(struct sockaddr_in6);
1240 if((ret=getnameinfo(res->gen, size,
1241 addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){
1242 dbgprintf(0,"Error: getnameinfo() returned %s\n",gai_strerror(ret));
1245 if (parms.no_resolve||nores){
1246 return addr2str_buf;
1248 addr2nm_buf[0] = '\0';
1249 getnameinfo(res->gen, size,
1250 addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN);
1251 snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf);
1252 return addr2both_buf;
1257 /*Usage information for program*/
1260 dbgprintf(0, "dccpping: [-v] [-V] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n");
1261 dbgprintf(0, " [-t ttl] [-S srcaddress] remote_host\n");
1263 dbgprintf(0, " -v Verbose. May be repeated for aditional verbosity.\n");
1264 dbgprintf(0, " -V Version information\n");
1265 dbgprintf(0, " -h Help\n");
1266 dbgprintf(0, " -n Numeric output only\n");
1267 dbgprintf(0, " -6 Force IPv6 mode\n");
1268 dbgprintf(0, " -4 Force IPv4 mode\n");
1273 dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero <sj323707@ohio.edu>\n", DCCPPING_VERSION);
1274 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
1275 dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n");
1279 /*Program will probably be run setuid, so be extra careful*/
1280 void sanitize_environment()
1282 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1285 extern char **environ;
1291 void dbgprintf(int level, const char *fmt, ...)
1295 va_start(args, fmt);
1296 vfprintf(stderr, fmt, args);