1 /******************************************************************************
2 Utility to ping hosts using DCCP Request packets to test for DCCP connectivity.
4 Copyright (C) 2012 Samuel Jero <sj323707@ohio.edu>
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 Author: Samuel Jero <sj323707@ohio.edu>
21 ******************************************************************************/
31 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <sys/select.h>
35 #include <netinet/ip.h>
36 #include <netinet/ip6.h>
37 #include <netinet/in.h>
38 #include <netinet/ip_icmp.h>
39 #include <netinet/icmp6.h>
40 #include <arpa/inet.h>
44 #include <linux/dccp.h>
45 #include "checksums.h"
48 #define DEFAULT_SERVICE_CODE 0x50455246
49 #define DEFAULT_PORT 33434
52 #define DCCPPING_VERSION 1.0
53 #define MAX(x,y) (x>y ? x : y)
63 /*Structure for simpler IPv4/IPv6 Address handling*/
66 struct sockaddr_in *ipv4;
67 struct sockaddr_in6 *ipv6;
70 /*Possible Responses*/
80 /*Output strings corresponding to Possible Errors*/
81 static const char* response_good[] = {
83 "Closed Port (Reset)",
84 "Open Port (Response)",
90 static const char* response_dccp_reset[] = {
105 static const char* response_icmpv4_dest_unreach[] = {
106 "Destination Network Unreachable",
107 "Destination Host Unreachable",
108 "Destination Protocol Unreachable",
109 "Destination Port Unreachable",
110 "Fragmentation Required",
111 "Source Routing Failed",
112 "Destination Network Unknown",
113 "Destination Host Unknown",
114 "Source Host Isolated",
115 "Network Administratively Prohibited",
116 "Host Administratively Prohibited",
117 "Network Unreachable for Type of Service",
118 "Host Unreachable for Type of Service",
119 "Communication Administratively Prohibited",
120 "Host Precedence Violation",
121 "Presedence Cutoff in Effect"
124 static const char* response_icmpv4_ttl[] = {
126 "Fragment Reassembly Failed"
129 static const char* response_icmpv6_dest_unreach[] = {
130 "No Route to Destination",
131 "Communication Administratively Prohibited",
132 "Beyond Scope of Source Address",
133 "Address Unreachable",
135 "Source Failed Ingress/Eggress Policy",
136 "Rejected Source Route",
137 "Error in Source Routing"
140 static const char* response_icmpv6_packet_too_big = "Packet Too Big";
142 static const char* response_icmpv6_ttl[] = {
144 "Fragment Reassembly Failed"
147 static const char* response_icmpv6_param_prob[]={
148 "Erroneous Header Field",
149 "Unrecognized Next Header (DCCP not supported)",
150 "Unrecognized IPv6 Option"
156 /*Structure to keep track of information about a request*/
163 struct timeval reply;
164 enum response_type reply_type;
165 struct request *next;
166 struct request *prev;
169 /*Request Queue head structure*/
170 struct request_queue{
171 struct request *head;
172 struct request *tail;
175 /*Statistics about the requests and replies sent*/
178 int replies_received;
183 struct timeval start;
188 int count; /*Number of pings (-1 is infinity)*/
189 int no_resolve; /*1 if we shouldn't resolve IP addresses*/
190 int dest_port; /*Destination port*/
191 int src_port; /*Source port---used to encode pid*/
193 long interval; /*Delay between pings in ms*/
194 int ip_type; /*IPv4 or IPv6*/
195 ipaddr_ptr_t dest_addr; /*Destination Address*/
196 ipaddr_ptr_t src_addr; /*Source Address*/
197 int dccp_socket; /*DCCP Socket used to grab src addr/port*/
198 char* hostname; /*Originally requested hostname*/
199 unsigned int service_code;/*DCCP Service Code*/
203 int debug=0; /*set to 1 to turn on debugging information*/
204 struct request_queue queue; /*Queue of requests to track RTT/duplicate information*/
205 struct stats ping_stats; /*Ping Statistics*/
206 struct params parms; /*Parameters for ping*/
207 char addr2str_buf[1000]; /*Buffer for printing addresses*/
208 char addr2nm_buf[1000]; /*Buffer for printing addresses*/
209 char addr2both_buf[1000]; /*Buffer for printing addresses*/
212 void getAddresses(char *src, char* dst);
214 void handleDCCPpacket(int rcv_socket, int send_socket);
215 void handleICMP4packet(int rcv_socket);
216 void handleICMP6packet(int rcv_socket);
217 void buildRequestPacket(unsigned char* buffer, int *len, int seq);
218 void updateRequestPacket(unsigned char* buffer, int *len, int seq);
219 int logPacket(int req_seq, int packet_seq);
220 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2);
221 const char *get_error_string(int type, int v1, int v2);
225 char* addr2str(ipaddr_ptr_t *res, int nores);
228 void sanitize_environment();
229 void dbgprintf(int level, const char *fmt, ...);
232 /*Parse commandline options*/
233 int main(int argc, char *argv[])
243 ping_stats.replies_received=0;
244 ping_stats.requests_sent=0;
245 ping_stats.rtt_avg=0;
246 ping_stats.rtt_max=0;
247 ping_stats.rtt_min=0;
250 parms.dest_port=DEFAULT_PORT;
252 parms. interval=1000;
253 parms.ip_type=AF_UNSPEC;
254 parms.dest_addr.gen=NULL;
255 parms.src_addr.gen=NULL;
256 parms.dccp_socket=-1;
259 parms.service_code=DEFAULT_SERVICE_CODE;
261 sanitize_environment();
263 while ((c = getopt(argc, argv, "64vVhnc:p:i:t:S:s:")) != -1) {
266 parms.ip_type=AF_INET6;
269 parms.ip_type=AF_INET;
272 parms.count = atoi(optarg);
274 dbgprintf(0, "Error: count must be positive");
279 parms.dest_port = atoi(optarg);
282 parms.interval = (long)(atof(optarg) * 1000.0);
283 if (parms.interval <= 0) {
284 dbgprintf(0, "Error: Invalid interval\n");
295 parms.ttl = atoi(optarg);
296 if (parms.ttl < 1 || parms.ttl > 255) {
297 dbgprintf(0,"Error: Invalid TTL\n");
302 parms.service_code=strtol(optarg,&tmp,0);
304 dbgprintf(0,"Error: Invalid Service Code\n");
307 if(parms.service_code<=0){
308 dbgprintf(0, "Error: Service Code MUST be positive");
319 /*Intentional Fall-through*/
333 parms.hostname=argv[0];
335 getAddresses(src, dst);
336 if(parms.src_addr.gen==NULL || parms.dest_addr.gen==NULL){
337 dbgprintf(0,"Error: Can't determine source or destination address\n");
341 signal(SIGINT, sigHandler);
344 free(parms.src_addr.gen);
345 free(parms.dest_addr.gen);
346 close(parms.dccp_socket);
351 void getAddresses(char *src, char* dst){
352 struct addrinfo hint;
353 struct addrinfo *dtmp, *stmp;
354 struct ifaddrs *temp, *cur;
356 struct sockaddr_in6* iv61;
357 struct sockaddr_in6* iv62;
358 struct sockaddr_in* iv41;
359 struct sockaddr_in* iv42;
364 /*Lookup destination Address*/
365 memset(&hint,0,sizeof(struct addrinfo));
366 hint.ai_family=parms.ip_type;
367 hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG;
369 if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){
370 dbgprintf(0,"Error: Couldn't lookup destination %s (%s)\n", dst, gai_strerror(err));
374 dbgprintf(0,"Error: Unknown Host %s\n", dst);
377 addrlen=dtmp->ai_addrlen;
378 hint.ai_family=parms.ip_type=dtmp->ai_family;
379 parms.dest_addr.gen=malloc(dtmp->ai_addrlen);
380 if(parms.dest_addr.gen==NULL){
381 dbgprintf(0,"Error: Can't allocate Memory\n");
384 memcpy(parms.dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen);
385 parms.dest_addr.gen->sa_family=dtmp->ai_family;
389 /*Get a meaningful source address*/
391 /*Use Commandline arg*/
393 /*Convert arg to address*/
394 if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){
395 dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err));
399 dbgprintf(0,"Error: Unknown Host %s\n", dst);
403 /*Compare to interface addresses*/
407 if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=stmp->ai_family){
408 /*Not matching ipv4/ipv6 of dest*/
412 if(stmp->ai_family==AF_INET){
413 iv41=(struct sockaddr_in*)stmp->ai_addr;
414 iv42=(struct sockaddr_in*)cur->ifa_addr;
415 if(memcmp(&iv41->sin_addr,&iv42->sin_addr, sizeof(iv41->sin_addr))==0){
416 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
417 if(parms.src_addr.gen==NULL){
418 dbgprintf(0,"Error: Can't allocate Memory\n");
421 parms.src_addr.gen->sa_family=parms.ip_type;
422 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
426 iv61=(struct sockaddr_in6*)stmp->ai_addr;
427 iv62=(struct sockaddr_in6*)cur->ifa_addr;
428 if(memcmp(&iv61->sin6_addr,&iv62->sin6_addr, sizeof(iv61->sin6_addr))==0){
429 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
430 if(parms.src_addr.gen==NULL){
431 dbgprintf(0,"Error: Can't allocate Memory\n");
434 parms.src_addr.gen->sa_family=parms.ip_type;
435 memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen);
441 if(parms.src_addr.gen==NULL){
442 ipv.gen=(struct sockaddr*)stmp->ai_addr;
443 dbgprintf(0,"Error: Source Address %s does not belong to any interface!\n",addr2str(&ipv,1));
450 /*Create socket to auto respond for open connections and reserve a source port*/
451 parms.dccp_socket=socket(parms.ip_type,SOCK_DCCP, IPPROTO_DCCP);
452 if(parms.dccp_socket<0){
453 dbgprintf(0, "Error: Failed opening DCCP Socket (%s)\n",strerror(errno));
456 fcntl(parms.dccp_socket, F_SETFL, O_NONBLOCK);
459 if(parms.src_addr.gen==NULL){
460 /*Auto-detect source address*/
461 parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage));
462 if(parms.src_addr.gen==NULL){
463 dbgprintf(0,"Error: Can't allocate Memory\n");
466 memset(parms.src_addr.gen,0,sizeof(struct sockaddr_storage));
467 parms.src_addr.gen->sa_family=parms.ip_type;
469 /*Bind to the given source address*/
470 if(bind(parms.dccp_socket,parms.src_addr.gen,sizeof(struct sockaddr_storage))<0){
471 dbgprintf(0, "Error: Failed bind() on DCCP socket (%s)\n",strerror(errno));
475 opt=htonl(parms.service_code);
476 if(setsockopt(parms.dccp_socket,SOL_DCCP, DCCP_SOCKOPT_SERVICE,&opt,sizeof(opt))<0){
477 dbgprintf(0, "Error: Failed setsockopt() on DCCP socket (%s)\n",strerror(errno));
481 /*Connect socket to get source address/port*/
482 if(parms.ip_type==AF_INET){
483 parms.dest_addr.ipv4->sin_port=htons(parms.dest_port);
485 parms.dest_addr.ipv6->sin6_port=htons(parms.dest_port);
487 if(connect(parms.dccp_socket,parms.dest_addr.gen,sizeof(struct sockaddr_storage))<0){
488 if(errno!=EINPROGRESS){
489 dbgprintf(0, "Error: Failed connect() on DCCP socket (%s)\n",strerror(errno));
494 /*Get source address and port number!*/
495 addrlen=sizeof(struct sockaddr_storage);
496 if(getsockname(parms.dccp_socket,parms.src_addr.gen,(socklen_t*)&addrlen)<0){
497 dbgprintf(0, "Error: Failed getsockname() on DCCP socket (%s)\n",strerror(errno));
500 if(parms.ip_type==AF_INET){
501 parms.src_port=ntohs(parms.src_addr.ipv4->sin_port);
502 parms.dest_addr.ipv4->sin_port=0;
504 parms.src_port=ntohs(parms.src_addr.ipv6->sin6_port);
505 parms.dest_addr.ipv6->sin6_port=0;
510 /*Preform the ping functionality*/
516 unsigned char sbuffer[slen];
518 struct timeval timeout;
519 struct timeval t,delay, add;
524 rs=socket(parms.ip_type, SOCK_RAW ,IPPROTO_RAW);
526 dbgprintf(0, "Error opening raw socket\n");
529 ds=socket(parms.ip_type, SOCK_RAW ,IPPROTO_DCCP);
531 dbgprintf(0, "Error opening raw DCCP socket\n");
534 is4=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMP);
536 dbgprintf(0,"Error opening raw ICMPv4 socket\n");
539 is6=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMPV6);
541 dbgprintf(0,"Error opening raw ICMPv6 socket\n");
546 /*Build DCCP packet*/
548 buildRequestPacket(sbuffer,&slen,packet_seq);
549 if(parms.ip_type==AF_INET){
550 addrlen=sizeof(struct sockaddr_in);
552 addrlen=sizeof(struct sockaddr_in6);
556 printf("PINGING %s (%s) on DCCP port %i\n",parms.hostname, addr2str(&parms.dest_addr,1),parms.dest_port);
560 if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)parms.dest_addr.gen,addrlen)<0){
562 dbgprintf(0,"Error: sendto() failed (%s)\n",strerror(errno));
565 if(parms.count==0){done=1; break;}
567 if (logPacket(request_seq,packet_seq)<0){
568 dbgprintf(0,"Error: Couldn't record request!\n");
570 dbgprintf(2, "Sending DCCP Request to %s\n", addr2str(&parms.dest_addr,1));
572 /*Use select to wait on packets or until interval has passed*/
573 add.tv_sec=parms.interval/1000;
574 add.tv_usec=(parms.interval%1000)*1000;
575 gettimeofday(&t,NULL);
576 timeradd(&t,&add,&delay);
577 while(timercmp(&t,&delay,<)){
578 /*Prepare for select*/
583 timersub(&delay,&t,&timeout);
586 if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){
588 dbgprintf(0,"Select() error (%s)\n",strerror(errno));
591 if(parms.count==0){done=1;break;}
593 if(FD_ISSET(ds,&sel)){
594 /*Data on the DCCP socket*/
595 handleDCCPpacket(ds,rs);
598 if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){
599 /*Data on the ICMPv4 socket*/
600 handleICMP4packet(is4);
602 if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){
603 /*Data on the ICMPv6 socket*/
604 handleICMP6packet(is6);
606 gettimeofday(&t,NULL);
615 updateRequestPacket(sbuffer,&slen, packet_seq);
626 void handleDCCPpacket(int rcv_socket, int send_socket){
628 unsigned char rbuffer[rlen];
629 ipaddr_ptr_t rcv_addr;
630 socklen_t rcv_addr_len;
631 struct dccp_hdr *dhdr;
632 struct dccp_hdr_reset *dhdr_re;
633 struct dccp_hdr_response *dhdr_rp;
634 struct dccp_hdr_ack_bits *dhdr_sync;
638 /*Memory for socket address*/
639 rcv_addr_len=sizeof(struct sockaddr_storage);
640 rcv_addr.gen=malloc(rcv_addr_len);
641 if(rcv_addr.gen==NULL){
642 dbgprintf(0,"Error: Can't Allocate Memory!\n");
647 rcv_addr_len=sizeof(struct sockaddr_storage);
648 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
650 dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno));
657 if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type
658 dbgprintf(2, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6");
663 if(rcv_addr.gen->sa_family==AF_INET){
665 if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr,
666 sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination
667 dbgprintf(2,"DCCP packet from 3rd host\n");
671 if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size
673 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
677 iph=(struct iphdr*)rbuffer;
678 ptr=rbuffer+iph->ihl*4;
681 if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr,
682 sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination
683 dbgprintf(2,"DCCP packet from 3rd host\n");
687 if(rlen < sizeof(struct dccp_hdr)){ //check packet size
689 dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n");
697 dhdr=(struct dccp_hdr*)ptr;
698 if(dhdr->dccph_sport!=htons(parms.dest_port)){
699 dbgprintf(2,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport));
703 if(dhdr->dccph_dport!=htons(parms.src_port)){
704 dbgprintf(2,"DCCP packet with wrong Destination Port\n");
710 if(dhdr->dccph_type==DCCP_PKT_RESET){
711 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){
712 dbgprintf(2, "Tossing DCCP Reset packet that's small!\n");
715 dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
718 if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){
719 logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0);
721 /*Nothing else to do*/
723 if(dhdr->dccph_type==DCCP_PKT_RESPONSE){
724 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){
725 dbgprintf(2, "Tossing DCCP Response packet that's too small!\n");
730 dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
731 logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0);
733 /*DCCP socket opened in getAddresses() will send Reset*/
735 if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){
736 if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){
737 dbgprintf(2, "Tossing DCCP Sync/SyncAck packet that's too small!\n");
742 dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
743 logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0);
745 /*DCCP socket opened in getAddresses() will send Reset*/
751 void handleICMP4packet(int rcv_socket){
753 unsigned char rbuffer[rlen];
754 ipaddr_ptr_t rcv_addr;
755 socklen_t rcv_addr_len;
756 struct icmphdr *icmp4;
757 struct dccp_hdr *dhdr;
758 struct dccp_hdr_ext *dhdre;
759 struct iphdr* ip4hdr;
762 /*Memory for socket address*/
763 rcv_addr_len=sizeof(struct sockaddr_storage);
764 rcv_addr.gen=malloc(rcv_addr_len);
765 if(rcv_addr.gen==NULL){
766 dbgprintf(0,"Error: Can't Allocate Memory!\n");
771 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
773 dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno));
780 iph=(struct iphdr*)rbuffer;
783 if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size
784 dbgprintf(2, "Packet smaller than possible ICMPv4 packet!\n");
789 icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4);
790 if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types
791 dbgprintf(2, "Tossing ICMPv4 packet of type %i\n", icmp4->type);
796 /*Check packet size again*/
797 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+4){
798 dbgprintf(2, "Tossing ICMPv4 packet that's too small to contain DCCP header!\n");
803 /*Decode IPv4 header*/
804 ip4hdr=(struct iphdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr));
805 if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){
806 /*Source address doesn't match*/
807 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n");
811 if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){
812 /*Destination address doesn't match*/
813 dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n");
817 if(ip4hdr->protocol!=IPPROTO_DCCP){
819 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n");
824 /*Decode DCCP header*/
825 dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4);
826 if(dhdr->dccph_dport!=htons(parms.dest_port)){
827 /*DCCP Destination Ports don't match*/
828 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n");
832 if(dhdr->dccph_sport!=htons(parms.src_port)){
833 /*DCCP Source Ports don't match*/
834 dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n");
838 dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr));
841 if(rlen<sizeof(struct icmphdr)+2*sizeof(struct iphdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
842 logResponse(&rcv_addr,-1,ICMPv4,icmp4->type,icmp4->code);
844 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code);
850 void handleICMP6packet(int rcv_socket){
852 unsigned char rbuffer[rlen];
853 ipaddr_ptr_t rcv_addr;
854 socklen_t rcv_addr_len;
855 struct icmp6_hdr *icmp6;
856 struct ip6_hdr* ip6hdr;
857 struct dccp_hdr *dhdr;
858 struct dccp_hdr_ext *dhdre;
860 /*Memory for socket address*/
861 rcv_addr_len=sizeof(struct sockaddr_storage);
862 rcv_addr.gen=malloc(rcv_addr_len);
863 if(rcv_addr.gen==NULL){
864 dbgprintf(0,"Error: Can't Allocate Memory!\n");
869 if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){
870 dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno));
873 if(rlen < sizeof(struct icmp6_hdr)){ //check packet size
874 dbgprintf(2, "Packet smaller than possible ICMPv6 packet!\n");
879 icmp6=(struct icmp6_hdr*)rbuffer;
880 if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG
881 && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types
882 dbgprintf(2, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type);
887 /*Check packet size again*/
888 if(rlen<sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)){
889 dbgprintf(2, "Tossing ICMPv6 packet that's too small to contain DCCP header!\n");
894 /*Decode IPv6 header*/
895 ip6hdr=(struct ip6_hdr*)(rbuffer+sizeof(struct icmp6_hdr));
896 if(memcmp(&parms.src_addr.ipv6->sin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){
897 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n");
898 /*Source address doesn't match*/
902 if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){
903 /*Destination address doesn't match*/
904 dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n");
908 if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){
910 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n");
915 /*Decode DCCP header*/
916 dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr));
917 if(dhdr->dccph_dport!=htons(parms.dest_port)){
918 /*DCCP Destination Ports don't match*/
919 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n");
923 if(dhdr->dccph_sport!=htons(parms.src_port)){
924 /*DCCP Source Ports don't match*/
925 dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n");
929 dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr));
932 logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code);
937 void buildRequestPacket(unsigned char* buffer, int *len, int seq){
938 struct dccp_hdr *dhdr;
939 struct dccp_hdr_ext *dhdre;
940 struct dccp_hdr_request *dhdrr;
941 struct iphdr* ip4hdr;
942 struct ip6_hdr* ip6hdr;
945 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
947 if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){
948 dbgprintf(0, "Error: Insufficient buffer space\n");
952 memset(buffer, 0, *len);
956 if(parms.ip_type==AF_INET){
957 ip_hdr_len=sizeof(struct iphdr);
958 ip4hdr=(struct iphdr*)buffer;
959 ip4hdr->check=htons(0);
960 memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr));
961 ip4hdr->frag_off=htons(0);
962 ip4hdr->id=htons(1);//first
964 ip4hdr->protocol=IPPROTO_DCCP;
965 memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr));
967 ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len);
968 ip4hdr->ttl=parms.ttl;
971 ip_hdr_len=sizeof(struct ip6_hdr);
972 ip6hdr=(struct ip6_hdr*)buffer;
973 memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr));
974 memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr));
975 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label
976 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl;
977 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP;
978 ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len);
982 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
983 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
984 dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext));
986 dhdr->dccph_checksum=0;
988 dhdr->dccph_doff=dccp_hdr_len/4;
989 dhdr->dccph_dport=htons(parms.dest_port);
990 dhdr->dccph_reserved=0;
991 dhdr->dccph_sport=htons(parms.src_port);
993 dhdr->dccph_type=DCCP_PKT_REQUEST;
994 dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers
995 dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity.
996 dhdre->dccph_seq_low=htonl(seq);
997 dhdrr->dccph_req_service=htonl(parms.service_code);
1000 if(parms.ip_type==AF_INET){
1001 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1002 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1003 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1004 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1006 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1007 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1008 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1010 *len=ip_hdr_len+dccp_hdr_len;
1014 void updateRequestPacket(unsigned char* buffer, int *len, int seq){
1015 struct dccp_hdr *dhdr;
1016 struct dccp_hdr_ext *dhdre;
1017 struct iphdr* ip4hdr;
1020 int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request);
1024 if(parms.ip_type==AF_INET){
1025 ip_hdr_len=sizeof(struct iphdr);
1026 ip4hdr=(struct iphdr*)buffer;
1027 ip4hdr->check=htons(0);
1028 ip4hdr->id=htons(seq);
1030 ip_hdr_len=sizeof(struct ip6_hdr);
1034 dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len);
1035 dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr));
1036 dhdr->dccph_checksum=0;
1037 dhdre->dccph_seq_low=htonl(seq);
1040 if(parms.ip_type==AF_INET){
1041 dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1042 (unsigned char*) &parms.dest_addr.ipv4->sin_addr,
1043 (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP);
1044 ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len);
1046 dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len,
1047 (unsigned char*) &parms.dest_addr.ipv6->sin6_addr,
1048 (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP);
1050 *len=ip_hdr_len+dccp_hdr_len;
1054 int logPacket(int req_seq, int packet_seq){
1055 struct request *tmp;
1057 /*Add new request to queue*/
1058 tmp=malloc(sizeof(struct request));
1060 dbgprintf(0,"Error: Can't allocate Memory!\n");
1067 tmp->packet_seq=packet_seq;
1068 tmp->request_seq=req_seq;
1069 tmp->reply_type=UNKNOWN;
1070 gettimeofday(&tmp->sent,NULL);
1072 if(queue.head==NULL){
1073 queue.head=queue.tail=tmp;
1075 queue.head->prev=tmp;
1076 tmp->next=queue.head;
1080 /*Update Statistics*/
1081 if(ping_stats.requests_sent==0){
1082 gettimeofday(&ping_stats.start,NULL);
1084 ping_stats.requests_sent++;
1088 int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){
1089 struct request *cur;
1092 if(queue.tail==NULL){
1093 dbgprintf(2,"Response received but no requests sent!\n");
1100 if(cur->packet_seq==seq){
1101 gettimeofday(&cur->reply,NULL);
1102 if(cur->num_replies>0){
1103 printf("Duplicate packet detected! (%i)\n",cur->request_seq);
1105 if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){
1110 cur->reply_type=type;
1117 if(parms.ip_type==AF_INET && seq==-1){
1118 /*IPv4 didn't include enough of the packet to get sequence numbers!*/
1119 printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0));
1120 ping_stats.errors++;
1123 dbgprintf(2,"Response received but no requests sent with sequence number %i!\n", seq);
1128 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);
1136 printf( "Response from %s : seq=%i time=%.1fms status=%s\n",
1137 addr2str(src,0),cur->request_seq, diff,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_avg=((ping_stats.replies_received*ping_stats.rtt_avg)+(diff))/(ping_stats.replies_received+1);
1149 ping_stats.replies_received++;
1151 ping_stats.errors++;
1153 if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){
1154 ping_stats.rtt_min=diff;
1156 if(diff > ping_stats.rtt_max){
1157 ping_stats.rtt_max=diff;
1161 ping_stats.errors++;
1163 gettimeofday(&ping_stats.stop,NULL);
1167 const char *get_error_string(int type, int v1, int v2){
1168 const char *label=NULL;
1171 if(v1>11){label=NULL;break;}
1172 label=response_dccp_reset[v1];
1177 if(v2>15){label=NULL;break;}
1178 label=response_icmpv4_dest_unreach[v2];
1181 if(v2>1){label=NULL;break;}
1182 label=response_icmpv4_ttl[v2];
1192 if(v2>7){label=NULL;break;}
1193 label=response_icmpv6_dest_unreach[v2];
1196 if(v2>0){label=NULL;break;}
1197 label=response_icmpv6_packet_too_big;
1200 if(v2>1){label=NULL;break;}
1201 label=response_icmpv6_ttl[v2];
1204 if(v2>2){label=NULL;break;}
1205 label=response_icmpv6_param_prob[v2];
1217 struct request *cur;
1218 struct request *tmp;
1240 gettimeofday(&ping_stats.stop,NULL);
1241 printf("-----------%s PING STATISTICS-----------\n",parms.hostname);
1243 diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) -
1244 (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec);
1246 ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100;
1247 printf("%i packets transmitted, %i received, %i errors, %.2f%% loss, time %ims\n",
1248 ping_stats.requests_sent,ping_stats.replies_received,ping_stats.errors,
1250 printf("rtt min/avg/max = %.1f/%.1f/%.1f ms\n",
1251 ping_stats.rtt_min,ping_stats.rtt_avg,ping_stats.rtt_max);
1255 char* addr2str(ipaddr_ptr_t *res, int nores){
1258 if (!res->gen->sa_family)
1261 if(res->gen->sa_family==AF_INET){
1262 size=sizeof(struct sockaddr_in);
1263 }else if(res->gen->sa_family==AF_INET6){
1264 size=sizeof(struct sockaddr_in6);
1268 if((ret=getnameinfo(res->gen, size,
1269 addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){
1270 dbgprintf(0,"Error: getnameinfo() returned %s\n",gai_strerror(ret));
1273 if (parms.no_resolve||nores){
1274 return addr2str_buf;
1276 addr2nm_buf[0] = '\0';
1277 getnameinfo(res->gen, size,
1278 addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN);
1279 snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf);
1280 return addr2both_buf;
1285 /*Usage information for program*/
1288 dbgprintf(0, "dccpping: [-v] [-V] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n");
1289 dbgprintf(0, " [-t ttl] [-s service_code] [-S srcaddress] remote_host\n");
1291 dbgprintf(0, " -v Verbose. May be repeated for aditional verbosity.\n");
1292 dbgprintf(0, " -V Version information\n");
1293 dbgprintf(0, " -h Help\n");
1294 dbgprintf(0, " -n Numeric output only\n");
1295 dbgprintf(0, " -6 Force IPv6 mode\n");
1296 dbgprintf(0, " -4 Force IPv4 mode\n");
1301 dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero <sj323707@ohio.edu>\n", DCCPPING_VERSION);
1302 dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n");
1303 dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n");
1307 /*Program will probably be run setuid, so be extra careful*/
1308 void sanitize_environment()
1310 #if defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
1313 extern char **environ;
1319 void dbgprintf(int level, const char *fmt, ...)
1323 va_start(args, fmt);
1324 vfprintf(stderr, fmt, args);