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 if(parms.ip_type==AF_INET){
546 dbgprintf(1, "Sending DCCP Request to %s\n",
547 addr2str(&parms.dest_addr,0));
549 dbgprintf(1, "Sending DCCP Request to %s\n",
550 addr2str(&parms.dest_addr,0));
553 /*Use select to wait on packets or until interval has passed*/
554 add.tv_sec=parms.interval/1000;
555 add.tv_usec=(parms.interval%1000)*1000;
556 gettimeofday(&t,NULL);
557 timeradd(&t,&add,&delay);
558 while(timercmp(&t,&delay,<)){
559 /*Prepare for select*/
564 timersub(&delay,&t,&timeout);
567 if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
569 dbgprintf(0,"Select() error (%s)\n",strerror(errno));
572 if(parms.count==0){done=1;break;}
574 if(FD_ISSET(ds,&sel)){
575 /*Data on the DCCP socket*/
576 handleDCCPpacket(ds,rs);
579 if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){
580 /*Data on the ICMPv4 socket*/
581 handleICMP4packet(is4);
583 if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){
584 /*Data on the ICMPv6 socket*/
585 handleICMP6packet(is6);
587 gettimeofday(&t,NULL);
596 updateRequestPacket(sbuffer,&slen, packet_seq);
605 void handleDCCPpacket(int rcv_socket, int send_socket){
607 unsigned char rbuffer[rlen];
608 ipaddr_ptr_t rcv_addr;
609 socklen_t rcv_addr_len;
610 struct dccp_hdr *dhdr;
611 struct dccp_hdr_reset *dhdr_re;
612 struct dccp_hdr_response *dhdr_rp;
613 struct dccp_hdr_ack_bits *dhdr_sync;
617 /*Memory for socket address*/
618 rcv_addr_len=sizeof(struct sockaddr_storage);
619 rcv_addr.gen=malloc(rcv_addr_len);
620 if(rcv_addr.gen==NULL){
621 dbgprintf(0,"Error: Can't Allocate Memory!\n");
626 rcv_addr_len=sizeof(struct sockaddr_storage);
627 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
629 dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
636 if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type
637 dbgprintf(1, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6");
642 if(rcv_addr.gen->sa_family==AF_INET){
644 if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr,
645 sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination
646 dbgprintf(1,"DCCP packet from 3rd host\n");
650 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
652 dbgprintf(1, "Packet smaller than possible DCCP packet received on DCCP socket\n");
656 iph=(struct iphdr*)rbuffer;
657 ptr=rbuffer+iph->ihl*4;
660 if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr,
661 sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination
662 dbgprintf(1,"DCCP packet from 3rd host\n");
666 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
668 dbgprintf(1, "Packet smaller than possible DCCP packet received on DCCP socket\n");
676 dhdr=(struct dccp_hdr*)ptr;
677 if(dhdr->dccph_sport!=htons(parms.dest_port)){
678 dbgprintf(1,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
682 if(dhdr->dccph_dport!=htons(parms.src_port)){
683 dbgprintf(1,"DCCP packet with wrong Destination Port\n");
689 if(dhdr->dccph_type==DCCP_PKT_RESET){
690 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
691 dbgprintf(1, "Tossing DCCP Reset packet that's small!\n");
694 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
697 if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){
698 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0);
700 /*Nothing else to do*/
702 if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
703 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
704 dbgprintf(1, "Tossing DCCP Response packet that's too small!\n");
709 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
710 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0);
712 /*DCCP socket opened in getAddresses() will send Reset*/
714 if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
715 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
716 dbgprintf(1, "Tossing DCCP Sync/SyncAck packet that's too small!\n");
721 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
722 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0);
724 /*DCCP socket opened in getAddresses() will send Reset*/
730 void handleICMP4packet(int rcv_socket){
732 unsigned char rbuffer[rlen];
733 ipaddr_ptr_t rcv_addr;
734 socklen_t rcv_addr_len;
735 struct icmphdr *icmp4;
736 struct dccp_hdr *dhdr;
737 struct dccp_hdr_ext *dhdre;
738 struct iphdr* ip4hdr;
741 /*Memory for socket address*/
742 rcv_addr_len=sizeof(struct sockaddr_storage);
743 rcv_addr.gen=malloc(rcv_addr_len);
744 if(rcv_addr.gen==NULL){
745 dbgprintf(0,"Error: Can't Allocate Memory!\n");
750 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
752 dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
759 iph=(struct iphdr*)rbuffer;
762 if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size
763 dbgprintf(1, "Packet smaller than possible ICMPv4 packet!\n");
768 icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4);
769 if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
770 dbgprintf(1, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
775 /*Check packet size again*/
776 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+4){
777 dbgprintf(1, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
782 /*Decode IPv4 header*/
783 ip4hdr=(struct iphdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr));
784 if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){
785 /*Source address doesn't match*/
786 dbgprintf(1,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n");
790 if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){
791 /*Destination address doesn't match*/
792 dbgprintf(1,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n");
796 if(ip4hdr->protocol!=IPPROTO_DCCP){
798 dbgprintf(1,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n");
803 /*Decode DCCP header*/
804 dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4);
805 if(dhdr->dccph_dport!=htons(parms.dest_port)){
806 /*DCCP Destination Ports don't match*/
807 dbgprintf(1,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n");
811 if(dhdr->dccph_sport!=htons(parms.src_port)){
812 /*DCCP Source Ports don't match*/
813 dbgprintf(1,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n");
817 dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
820 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
821 logResponse(&rcv_addr,-1,ICMPv4,icmp4->type,icmp4->code);
823 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code);
829 void handleICMP6packet(int rcv_socket){
831 unsigned char rbuffer[rlen];
832 ipaddr_ptr_t rcv_addr;
833 socklen_t rcv_addr_len;
834 struct icmp6_hdr *icmp6;
835 struct ip6_hdr* ip6hdr;
836 struct dccp_hdr *dhdr;
837 struct dccp_hdr_ext *dhdre;
839 /*Memory for socket address*/
840 rcv_addr_len=sizeof(struct sockaddr_storage);
841 rcv_addr.gen=malloc(rcv_addr_len);
842 if(rcv_addr.gen==NULL){
843 dbgprintf(0,"Error: Can't Allocate Memory!\n");
848 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
849 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
852 if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
853 dbgprintf(1, "Packet smaller than possible ICMPv6 packet!\n");
858 icmp6=(struct icmp6_hdr*)rbuffer;
859 if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
860 && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
861 dbgprintf(1, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
866 /*Check packet size again*/
867 if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
868 dbgprintf(1, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
873 /*Decode IPv6 header*/
874 ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
875 if(memcmp(&parms.src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){
876 dbgprintf(1,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n");
877 /*Source address doesn't match*/
881 if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){
882 /*Destination address doesn't match*/
883 dbgprintf(1,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n");
887 if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
889 dbgprintf(1,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n");
894 /*Decode DCCP header*/
895 dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
896 if(dhdr->dccph_dport!=htons(parms.dest_port)){
897 /*DCCP Destination Ports don't match*/
898 dbgprintf(1,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n");
902 if(dhdr->dccph_sport!=htons(parms.src_port)){
903 /*DCCP Source Ports don't match*/
904 dbgprintf(1,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n");
908 dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
911 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code);
916 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
917 struct dccp_hdr *dhdr;
918 struct dccp_hdr_ext *dhdre;
919 struct dccp_hdr_request *dhdrr;
920 struct iphdr* ip4hdr;
921 struct ip6_hdr* ip6hdr;
924 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
926 if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
927 dbgprintf(0, "Error: Insufficient buffer space\n");
931 memset(buffer, 0, *len);
935 if(parms.ip_type==AF_INET){
936 ip_hdr_len=sizeof(struct iphdr);
937 ip4hdr=(struct iphdr*)buffer;
938 ip4hdr->check=htons(0);
939 memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr));
940 ip4hdr->frag_off=htons(0);
941 ip4hdr->id=htons(1);//first
943 ip4hdr->protocol=IPPROTO_DCCP;
944 memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr));
946 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
947 ip4hdr->ttl=parms.ttl;
950 ip_hdr_len=sizeof(struct ip6_hdr);
951 ip6hdr=(struct ip6_hdr*)buffer;
952 memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr));
953 memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr));
954 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
955 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl;
956 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
957 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
961 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
962 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
963 dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
965 dhdr->dccph_checksum=0;
967 dhdr->dccph_doff=dccp_hdr_len/4;
968 dhdr->dccph_dport=htons(parms.dest_port);
969 dhdr->dccph_reserved=0;
970 dhdr->dccph_sport=htons(parms.src_port);
972 dhdr->dccph_type=DCCP_PKT_REQUEST;
973 dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
974 dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity.
975 dhdre->dccph_seq_low=htonl(seq);
976 dhdrr->dccph_req_service=htonl(DCCP_SERVICE_CODE);
979 if(parms.ip_type==AF_INET){
980 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
981 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
982 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
983 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
985 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
986 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
987 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
989 *len=ip_hdr_len+dccp_hdr_len;
993 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
994 struct dccp_hdr *dhdr;
995 struct dccp_hdr_ext *dhdre;
996 struct iphdr* ip4hdr;
999 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
1003 if(parms.ip_type==AF_INET){
1004 ip_hdr_len=sizeof(struct iphdr);
1005 ip4hdr=(struct iphdr*)buffer;
1006 ip4hdr->check=htons(0);
1007 ip4hdr->id=htons(seq);
1009 ip_hdr_len=sizeof(struct ip6_hdr);
1013 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
1014 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
1015 dhdr->dccph_checksum=0;
1016 dhdre->dccph_seq_low=htonl(seq);
1019 if(parms.ip_type==AF_INET){
1020 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1021 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1022 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1023 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1025 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1026 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1027 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1029 *len=ip_hdr_len+dccp_hdr_len;
1033 int logPacket(int req_seq, int packet_seq){
1034 struct request *tmp;
1036 /*Add new request to queue*/
1037 tmp=malloc(sizeof(struct request));
1039 dbgprintf(0,"Error: Can't allocate Memory!\n");
1046 tmp->packet_seq=packet_seq;
1047 tmp->request_seq=req_seq;
1048 tmp->reply_type=UNKNOWN;
1049 gettimeofday(&tmp->sent,NULL);
1051 if(queue.head==NULL){
1052 queue.head=queue.tail=tmp;
1054 queue.head->prev=tmp;
1055 tmp->next=queue.head;
1059 /*Update Statistics*/
1060 if(ping_stats.requests_sent==0){
1061 gettimeofday(&ping_stats.start,NULL);
1063 ping_stats.requests_sent++;
1067 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){
1068 struct request *cur;
1071 if(queue.tail==NULL){
1072 dbgprintf(1,"Response received but no requests sent!\n");
1079 if(cur->packet_seq==seq){
1080 gettimeofday(&cur->reply,NULL);
1081 if(cur->num_replies>0){
1082 printf("Duplicate packet detected! (%i)\n",cur->request_seq);
1084 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1089 cur->reply_type=type;
1096 if(parms.ip_type==AF_INET && seq==-1){
1097 /*IPv4 didn't include enough of the packet to get sequence numbers!*/
1098 printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0));
1099 ping_stats.errors++;
1102 dbgprintf(1,"Response received but no requests sent with sequence number %i!\n", seq);
1107 diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec);
1111 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1112 printf( "Response from %s : seq=%i time=%.1fms status=%s\n",
1113 addr2str(src,0),cur->request_seq, diff,response_good[type]);
1116 printf("%s from %s : seq=%i\n",get_error_string(type,v1,v2),addr2str(src,0),cur->request_seq);
1119 /*Update statistics*/
1120 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1122 if(cur->num_replies==1){
1123 ping_stats.rtt_avg=((ping_stats.replies_received*ping_stats.rtt_avg)+(diff))/(ping_stats.replies_received+1);
1124 ping_stats.replies_received++;
1126 ping_stats.errors++;
1128 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
1129 ping_stats.rtt_min=diff;
1131 if(diff > ping_stats.rtt_max){
1132 ping_stats.rtt_max=diff;
1136 ping_stats.errors++;
1138 gettimeofday(&ping_stats.stop,NULL);
1142 const char *get_error_string(int type, int v1, int v2){
1143 const char *label=NULL;
1146 if(v1>11){label=NULL;break;}
1147 label=response_dccp_reset[v1];
1152 if(v2>15){label=NULL;break;}
1153 label=response_icmpv4_dest_unreach[v2];
1156 if(v2>1){label=NULL;break;}
1157 label=response_icmpv4_ttl[v2];
1167 if(v2>7){label=NULL;break;}
1168 label=response_icmpv6_dest_unreach[v2];
1171 if(v2>0){label=NULL;break;}
1172 label=response_icmpv6_packet_too_big;
1175 if(v2>1){label=NULL;break;}
1176 label=response_icmpv6_ttl[v2];
1179 if(v2>2){label=NULL;break;}
1180 label=response_icmpv6_param_prob[v2];
1192 struct request *cur;
1193 struct request *tmp;
1211 gettimeofday(&ping_stats.stop,NULL);
1212 printf("-----------%s PING STATISTICS-----------\n",parms.hostname);
1214 diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
1215 (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
1217 ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
1218 printf("%i packets transmitted, %i received, %i errors, %.2f%% loss, time %ims\n",
1219 ping_stats.requests_sent,ping_stats.replies_received,ping_stats.errors,
1221 printf("rtt min/avg/max = %.1f/%.1f/%.1f ms\n",
1222 ping_stats.rtt_min,ping_stats.rtt_avg,ping_stats.rtt_max);
1229 char* addr2str(ipaddr_ptr_t *res, int nores){
1232 if (!res->gen->sa_family)
1235 if(res->gen->sa_family==AF_INET){
1236 size=sizeof(struct sockaddr_in);
1237 }else if(res->gen->sa_family==AF_INET6){
1238 size=sizeof(struct sockaddr_in6);
1242 if((ret=getnameinfo(res->gen, size,
1243 addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){
1244 dbgprintf(0,"Error! %s\n",gai_strerror(ret));
1247 if (parms.no_resolve||nores){
1248 return addr2str_buf;
1250 addr2nm_buf[0] = '\0';
1251 getnameinfo(res->gen, size,
1252 addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN);
1253 snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf);
1254 return addr2both_buf;
1259 /*Usage information for program*/
1262 dbgprintf(0, "dccpping: [-d] [-v] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n");
1263 dbgprintf(0, " [-t ttl] [-S srcaddress] remote_host\n");
1265 dbgprintf(0, " -d Debug. May be repeated for aditional verbosity\n");
1266 dbgprintf(0, " -v Version information\n");
1267 dbgprintf(0, " -h Help\n");
1268 dbgprintf(0, " -n Numeric output only\n");
1269 dbgprintf(0, " -6 Force IPv6 mode\n");
1270 dbgprintf(0, " -4 Force IPv4 mode\n");
1275 dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero <sj323707@ohio.edu>\n", DCCPPING_VERSION);
1276 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
1277 dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n");
1281 /*Program will probably be run setuid, so be extra careful*/
1282 void sanitize_environment()
1284 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1287 extern char **environ;
1293 void dbgprintf(int level, const char *fmt, ...)
1297 va_start(args, fmt);
1298 vfprintf(stderr, fmt, args);