X-Git-Url: http://sjero.net/git/?p=dccpping;a=blobdiff_plain;f=dccpping.c;h=b71a7e3bb08998aaa0b0be4e00d482e1ae4011a4;hp=8dc59c0a2d517e444ea054a3c99c8873da0dec5d;hb=HEAD;hpb=8739c9b4db1f612a3dd790c5aa62a19b907bc9c1 diff --git a/dccpping.c b/dccpping.c index 8dc59c0..b71a7e3 100644 --- a/dccpping.c +++ b/dccpping.c @@ -1,71 +1,235 @@ /****************************************************************************** -Author: Samuel Jero +Utility to ping hosts using DCCP Request packets to test for DCCP connectivity. + +Copyright (C) 2012 Samuel Jero + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -Date: 10/2012 +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. -Description: Program to ping hosts using DCCP REQ packets to test for DCCP connectivity. +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Author: Samuel Jero +Date: 11/2012 ******************************************************************************/ #include #include #include #include #include -#include #include -#include +#include +#include +#include +#include #include #include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include #include +#include #include -#include -#include +#include +#include "checksums.h" + + +#define DEFAULT_SERVICE_CODE 1885957735 +#define DEFAULT_PORT 33434 + +#define DCCPPING_VERSION 1.1 +#define MAX(x,y) (x>y ? x : y) extern int errno; +#ifndef NI_IDN +#define NI_IDN 32 +#endif +#ifndef SOL_DCCP +#define SOL_DCCP 269 +#endif -//Pseudo Header for checksums -#define IP4_ADDR_LEN 4 -struct ip4_pseudo_hdr{ - unsigned char src[IP4_ADDR_LEN]; - unsigned char dest[IP4_ADDR_LEN]; - unsigned int len; - unsigned char zero[3]; - unsigned char nxt; +/*Structure for simpler IPv4/IPv6 Address handling*/ +typedef union ipaddr{ + struct sockaddr *gen; + struct sockaddr_in *ipv4; + struct sockaddr_in6 *ipv6; +} ipaddr_ptr_t; + +/*Possible Responses*/ +enum response_type{ + UNKNOWN=0, + DCCP_RESET, + DCCP_RESPONSE, + DCCP_SYNC, + ICMPv4, + ICMPv6, }; -#define IP6_ADDR_LEN 16 -struct ip6_pseudo_hdr{ - unsigned char src[IP6_ADDR_LEN]; - unsigned char dest[IP6_ADDR_LEN]; - unsigned int len; - unsigned char zero[3]; - unsigned char nxt; + +/*Output strings corresponding to Possible Errors*/ +static const char* response_good[] = { + "Unknown", + "Closed Port (Reset)", + "Open Port (Response)", + "Open Port (Sync)", + "ICMPv4", + "ICMPv6" }; +static const char* response_dccp_reset[] = { + "Unspecified", + "Closed", + "Aborted", + "No Connection", + "Packet Error", + "Option Error", + "Mandatory Error", + "Connection Refused", + "Bad Service Code", + "Too Busy", + "Bad Init Cookie", + "Aggression Penalty" +}; + +static const char* response_icmpv4_dest_unreach[] = { + "Destination Network Unreachable", + "Destination Host Unreachable", + "Destination Protocol Unreachable", + "Destination Port Unreachable", + "Fragmentation Required", + "Source Routing Failed", + "Destination Network Unknown", + "Destination Host Unknown", + "Source Host Isolated", + "Network Administratively Prohibited", + "Host Administratively Prohibited", + "Network Unreachable for Type of Service", + "Host Unreachable for Type of Service", + "Communication Administratively Prohibited", + "Host Precedence Violation", + "Presedence Cutoff in Effect" +}; -int debug=0; /*set to 1 to turn on debugging information*/ -int count=-1; -int dest_port=33434; -int ttl=64; -long interval=1000; -int ip_type=AF_UNSPEC; -socklen_t addrlen; -struct sockaddr *dest_addr; -struct sockaddr *src_addr; +static const char* response_icmpv4_ttl[] = { + "TTL Expired", + "Fragment Reassembly Failed" +}; + +static const char* response_icmpv6_dest_unreach[] = { + "No Route to Destination", + "Communication Administratively Prohibited", + "Beyond Scope of Source Address", + "Address Unreachable", + "Port Unreachable", + "Source Failed Ingress/Eggress Policy", + "Rejected Source Route", + "Error in Source Routing" +}; + +static const char* response_icmpv6_packet_too_big = "Packet Too Big"; + +static const char* response_icmpv6_ttl[] = { + "TTL Expired", + "Fragment Reassembly Failed" +}; + +static const char* response_icmpv6_param_prob[]={ + "Erroneous Header Field", + "Unrecognized Next Header (DCCP not supported)", + "Unrecognized IPv6 Option" +}; + + +/*Structure to keep track of information about a request*/ +struct request{ + int request_seq; + int packet_seq; + int num_replies; + int num_errors; + struct timeval sent; + struct timeval reply; + enum response_type reply_type; + struct request *next; + struct request *prev; +}; + +/*Request Queue head structure*/ +struct request_queue{ + struct request *head; + struct request *tail; +}; + +/*Statistics about the requests and replies sent*/ +struct stats{ + int requests_sent; + int replies_received; + int duplicates; + int errors; + long long rtt_min; + long long rtt_sum; + long long rtt_sum2; + long rtt_max; + struct timeval start; + struct timeval stop; +}; + +struct params{ + int count; /*Number of pings (-1 is infinity)*/ + int no_resolve; /*1 if we shouldn't resolve IP addresses*/ + int dest_port; /*Destination port*/ + int src_port; /*Source port---used to encode pid*/ + int ttl; /*TTL*/ + long interval; /*Delay between pings in ms*/ + int ip_type; /*IPv4 or IPv6*/ + ipaddr_ptr_t dest_addr; /*Destination Address*/ + ipaddr_ptr_t src_addr; /*Source Address*/ + int dccp_socket; /*DCCP Socket used to grab src addr/port*/ + char* hostname; /*Originally requested hostname*/ + unsigned int service_code;/*DCCP Service Code*/ +}; + + +int debug=0; /*set to 1 to turn on debugging information*/ +struct request_queue queue; /*Queue of requests to track RTT/duplicate information*/ +struct stats ping_stats; /*Ping Statistics*/ +struct params parms; /*Parameters for ping*/ +char addr2str_buf[1000]; /*Buffer for printing addresses*/ +char addr2nm_buf[1000]; /*Buffer for printing addresses*/ +char addr2both_buf[1000]; /*Buffer for printing addresses*/ + + +void getAddresses(char *src, char* dst); void doping(); -int ip6_chksum(unsigned char* buff, int len, unsigned char* dest, unsigned char* src, int type, unsigned short* chksum); -int ip4_chksum(unsigned char* buff, int len, unsigned char* dest, unsigned char* src, int type, unsigned short* chksum); -u_int32_t checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum); -u_int32_t wrapsum(u_int32_t sum); -void dbgprintf(int level, const char *fmt, ...); -void sanitize_environment(); +void handleDCCPpacket(int rcv_socket, int send_socket); +void handleICMP4packet(int rcv_socket); +void handleICMP6packet(int rcv_socket); +void buildRequestPacket(unsigned char* buffer, int *len, int seq); +void updateRequestPacket(unsigned char* buffer, int *len, int seq); +int logPacket(int req_seq, int packet_seq); +int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2); +const char *get_error_string(int type, int v1, int v2); +void clearQueue(); +void sigHandler(); +void printStats(); +char* addr2str(ipaddr_ptr_t *res, int nores); void usage(); - +void version(); +void sanitize_environment(); +void dbgprintf(int level, const char *fmt, ...); +static long llsqrt(long long a); /*Parse commandline options*/ int main(int argc, char *argv[]) @@ -73,55 +237,90 @@ int main(int argc, char *argv[]) char c; char *src=NULL; char *dst=NULL; - int err; - struct addrinfo hint; - struct addrinfo *dtmp, *stmp; - struct ifaddrs *temp, *cur; - struct sockaddr_in6* iv6; - struct sockaddr_in6 lv6; - struct sockaddr_in lv4; - struct sockaddr_in* iv4; + char *tmp; + + /*Set Defaults*/ + queue.head=NULL; + queue.tail=NULL; + ping_stats.replies_received=0; + ping_stats.requests_sent=0; + ping_stats.rtt_sum=0; + ping_stats.rtt_sum2=0; + ping_stats.duplicates=0; + ping_stats.rtt_max=0; + ping_stats.rtt_min=0; + ping_stats.errors=0; + parms.count=-1; + parms.dest_port=DEFAULT_PORT; + parms.ttl=64; + parms. interval=1000; + parms.ip_type=AF_UNSPEC; + parms.dest_addr.gen=NULL; + parms.src_addr.gen=NULL; + parms.dccp_socket=-1; + parms.no_resolve=0; + parms.hostname=NULL; + parms.service_code=DEFAULT_SERVICE_CODE; sanitize_environment(); - while ((c = getopt(argc, argv, "64c:p:i:dt:S:")) != -1) { + while ((c = getopt(argc, argv, "64vVhnc:p:i:t:S:s:")) != -1) { switch (c) { case '6': - ip_type=AF_INET6; + parms.ip_type=AF_INET6; break; case '4': - ip_type=AF_INET; + parms.ip_type=AF_INET; break; case 'c': - count = atoi(optarg); - if(count<=0){ + parms.count = atoi(optarg); + if(parms.count<=0){ dbgprintf(0, "Error: count must be positive"); exit(1); } break; case 'p': - dest_port = atoi(optarg); + parms.dest_port = atoi(optarg); break; case 'i': - interval = (long)(atof(optarg) * 1000.0); - if (interval <= 0) { - fprintf(stderr, "Invalid interval\n"); + parms.interval = (long)(atof(optarg) * 1000.0); + if (parms.interval <= 0) { + dbgprintf(0, "Error: Invalid interval\n"); exit(1); } break; - case 'd': + case 'v': debug++; break; + case 'n': + parms.no_resolve=1; + break; case 't': - ttl = atoi(optarg); - if (ttl < 1 || ttl > 255) { - fprintf(stderr, "Invalid TTL\n"); + parms.ttl = atoi(optarg); + if (parms.ttl < 1 || parms.ttl > 255) { + dbgprintf(0,"Error: Invalid TTL\n"); + exit(1); + } + break; + case 's': + parms.service_code=strtol(optarg,&tmp,0); + if(*tmp!='\0'){ + dbgprintf(0,"Error: Invalid Service Code\n"); + exit(1); + } + if(parms.service_code<=0){ + dbgprintf(0, "Error: Service Code MUST be positive"); + exit(1); } break; case 'S': src=optarg; - //r = inet_aton(optarg, &src_ip); break; + case 'V': + version(); + break; + case 'h': + /*Intentional Fall-through*/ default: usage(); break; @@ -135,10 +334,40 @@ int main(int argc, char *argv[]) usage(); } dst=argv[0]; + parms.hostname=argv[0]; + + getAddresses(src, dst); + if(parms.src_addr.gen==NULL || parms.dest_addr.gen==NULL){ + dbgprintf(0,"Error: Can't determine source or destination address\n"); + exit(1); + } + + signal(SIGINT, sigHandler); + doping(); + + free(parms.src_addr.gen); + free(parms.dest_addr.gen); + close(parms.dccp_socket); + clearQueue(); + return 0; +} + +void getAddresses(char *src, char* dst){ + struct addrinfo hint; + struct addrinfo *dtmp, *stmp; + struct ifaddrs *temp, *cur; + ipaddr_ptr_t ipv; + struct sockaddr_in6* iv61; + struct sockaddr_in6* iv62; + struct sockaddr_in* iv41; + struct sockaddr_in* iv42; + int addrlen; + int err; + int opt; /*Lookup destination Address*/ memset(&hint,0,sizeof(struct addrinfo)); - hint.ai_family=ip_type; + hint.ai_family=parms.ip_type; hint.ai_flags=AI_V4MAPPED | AI_ADDRCONFIG; if((err=getaddrinfo(dst,NULL,&hint,&dtmp))!=0){ @@ -149,20 +378,23 @@ int main(int argc, char *argv[]) dbgprintf(0,"Error: Unknown Host %s\n", dst); exit(1); }else{ - hint.ai_family=ip_type=dtmp->ai_family; addrlen=dtmp->ai_addrlen; - dest_addr=malloc(dtmp->ai_addrlen); - if(dest_addr==NULL){ + hint.ai_family=parms.ip_type=dtmp->ai_family; + parms.dest_addr.gen=malloc(dtmp->ai_addrlen); + if(parms.dest_addr.gen==NULL){ dbgprintf(0,"Error: Can't allocate Memory\n"); exit(1); } - memcpy(dest_addr,dtmp->ai_addr,dtmp->ai_addrlen); + memcpy(parms.dest_addr.gen,dtmp->ai_addr,dtmp->ai_addrlen); + parms.dest_addr.gen->sa_family=dtmp->ai_family; } freeaddrinfo(dtmp); /*Get a meaningful source address*/ if(src!=NULL){ /*Use Commandline arg*/ + + /*Convert arg to address*/ if((err=getaddrinfo(src,NULL,&hint,&stmp))!=0){ dbgprintf(0,"Error: Source Address %s is invalid (%s)\n", src, gai_strerror(err)); exit(1); @@ -170,243 +402,926 @@ int main(int argc, char *argv[]) if(stmp==NULL){ dbgprintf(0,"Error: Unknown Host %s\n", dst); exit(1); - }else{ - src_addr=malloc(stmp->ai_addrlen); - if(src_addr==NULL){ - dbgprintf(0,"Error: Can't allocate Memory\n"); - exit(1); - } - memcpy(src_addr,stmp->ai_addr,stmp->ai_addrlen); } - freeaddrinfo(stmp); - }else{ - /*Guess a good source address*/ - inet_pton(AF_INET6,"::1",&lv6); - inet_pton(AF_INET,"127.0.0.1",&lv4); + + /*Compare to interface addresses*/ getifaddrs(&temp); cur=temp; while(cur!=NULL){ - if((cur->ifa_flags & IFF_BROADCAST) == IFF_BROADCAST){ /*Not broad cast*/ - cur=cur->ifa_next; - continue; - } - if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=ip_type){ /*Not matching ipv4/ipv6 of dest*/ + if(cur->ifa_addr==NULL || cur->ifa_addr->sa_family!=stmp->ai_family){ + /*Not matching ipv4/ipv6 of dest*/ cur=cur->ifa_next; continue; } - if(cur->ifa_addr!=NULL && cur->ifa_addr->sa_family==AF_INET6){ - iv6=(struct sockaddr_in6*)cur->ifa_addr; - if(iv6->sin6_scope_id!=0){ /*Not globally valid address, if ipv6*/ - cur=cur->ifa_next; - continue; - } - if(memcmp(&lv6.sin6_addr,&iv6->sin6_addr,sizeof(iv6->sin6_addr))==0){/*IPv6 loopback*/ - cur=cur->ifa_next; - continue; + if(stmp->ai_family==AF_INET){ + iv41=(struct sockaddr_in*)stmp->ai_addr; + iv42=(struct sockaddr_in*)cur->ifa_addr; + if(memcmp(&iv41->sin_addr,&iv42->sin_addr, sizeof(iv41->sin_addr))==0){ + parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage)); + if(parms.src_addr.gen==NULL){ + dbgprintf(0,"Error: Can't allocate Memory\n"); + exit(1); + } + parms.src_addr.gen->sa_family=parms.ip_type; + memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen); + break; } - } - if(cur->ifa_addr!=NULL && cur->ifa_addr->sa_family==AF_INET){ - iv4=(struct sockaddr_in*)cur->ifa_addr; - if(memcmp(&lv4.sin_addr,&iv4->sin_addr,sizeof(iv4->sin_addr))==0){/*IPv4 loopback*/ - cur=cur->ifa_next; - continue; + }else{ + iv61=(struct sockaddr_in6*)stmp->ai_addr; + iv62=(struct sockaddr_in6*)cur->ifa_addr; + if(memcmp(&iv61->sin6_addr,&iv62->sin6_addr, sizeof(iv61->sin6_addr))==0){ + parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage)); + if(parms.src_addr.gen==NULL){ + dbgprintf(0,"Error: Can't allocate Memory\n"); + exit(1); + } + parms.src_addr.gen->sa_family=parms.ip_type; + memcpy(parms.src_addr.gen,cur->ifa_addr,addrlen); + break; } } - - src_addr=malloc(sizeof(struct sockaddr_storage)); - if(src_addr==NULL){ - dbgprintf(0,"Error: Can't allocate Memory\n"); - exit(1); - } - src_addr->sa_family=ip_type; - memcpy(src_addr,cur->ifa_addr,addrlen); - break; + cur=cur->ifa_next; + } + if(parms.src_addr.gen==NULL){ + ipv.gen=(struct sockaddr*)stmp->ai_addr; + dbgprintf(0,"Error: Source Address %s does not belong to any interface!\n",addr2str(&ipv,1)); + exit(1); } freeifaddrs(temp); + freeaddrinfo(stmp); } - doping(); + /*Create socket to auto respond for open connections and reserve a source port*/ + parms.dccp_socket=socket(parms.ip_type,SOCK_DCCP, IPPROTO_DCCP); + if(parms.dccp_socket<0){ + dbgprintf(0, "Error: Failed opening DCCP Socket (%s)\n",strerror(errno)); + exit(1); + } + fcntl(parms.dccp_socket, F_SETFL, O_NONBLOCK); - free(src_addr); - free(dest_addr); - return 0; + if(parms.src_addr.gen==NULL){ + /*Auto-detect source address*/ + parms.src_addr.gen=malloc(sizeof(struct sockaddr_storage)); + if(parms.src_addr.gen==NULL){ + dbgprintf(0,"Error: Can't allocate Memory\n"); + exit(1); + } + memset(parms.src_addr.gen,0,sizeof(struct sockaddr_storage)); + parms.src_addr.gen->sa_family=parms.ip_type; + }else{ + /*Bind to the given source address*/ + if(bind(parms.dccp_socket,parms.src_addr.gen,sizeof(struct sockaddr_storage))<0){ + dbgprintf(0, "Error: Failed bind() on DCCP socket (%s)\n",strerror(errno)); + exit(1); + } + } + opt=htonl(parms.service_code); + if(setsockopt(parms.dccp_socket,SOL_DCCP, DCCP_SOCKOPT_SERVICE,&opt,sizeof(opt))<0){ + dbgprintf(0, "Error: Failed setsockopt() on DCCP socket (%s)\n",strerror(errno)); + exit(1); + } + + /*Connect socket to get source address/port*/ + if(parms.ip_type==AF_INET){ + parms.dest_addr.ipv4->sin_port=htons(parms.dest_port); + }else{ + parms.dest_addr.ipv6->sin6_port=htons(parms.dest_port); + } + if(connect(parms.dccp_socket,parms.dest_addr.gen,sizeof(struct sockaddr_storage))<0){ + if(errno!=EINPROGRESS){ + dbgprintf(0, "Error: Failed connect() on DCCP socket (%s)\n",strerror(errno)); + exit(1); + } + } + + /*Get source address and port number!*/ + addrlen=sizeof(struct sockaddr_storage); + if(getsockname(parms.dccp_socket,parms.src_addr.gen,(socklen_t*)&addrlen)<0){ + dbgprintf(0, "Error: Failed getsockname() on DCCP socket (%s)\n",strerror(errno)); + exit(1); + } + if(parms.ip_type==AF_INET){ + parms.src_port=ntohs(parms.src_addr.ipv4->sin_port); + parms.dest_addr.ipv4->sin_port=0; + }else{ + parms.src_port=ntohs(parms.src_addr.ipv6->sin6_port); + parms.dest_addr.ipv6->sin6_port=0; + } + return; } /*Preform the ping functionality*/ void doping(){ - int ds, is; + int rs, is4,is6,ds; int done=0; - int seq=8; - int slen=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request); - unsigned char sbuffer[slen+1]; - unsigned char rbuffer[1000]; - char pbuf[1000]; - struct dccp_hdr *dhdr; - struct dccp_hdr_ext *dhdre; - struct dccp_hdr_request *dhdrr; - struct sockaddr_storage rcv_addr; - socklen_t rcv_addr_len=sizeof(struct sockaddr_storage); - struct sockaddr_in *iv41, *iv42; - struct sockaddr_in6 *tmp; - int opt; - int rlen; - - dbgprintf(0, "In doping()\n"); + int addrlen; + int slen=1500; + unsigned char sbuffer[slen]; + fd_set sel; + struct timeval timeout; + struct timeval t,delay, add; + int request_seq=1; + int packet_seq; /*Open Sockets*/ - ds=socket(ip_type, SOCK_RAW | SOCK_NONBLOCK ,IPPROTO_DCCP); + rs=socket(parms.ip_type, SOCK_RAW ,IPPROTO_RAW); + if(rs<0){ + dbgprintf(0, "Error opening raw socket\n"); + exit(1); + } + ds=socket(parms.ip_type, SOCK_RAW ,IPPROTO_DCCP); if(ds<0){ dbgprintf(0, "Error opening raw DCCP socket\n"); exit(1); } - /*opt=1; - if(setsockopt(ds,IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt))<0){ - dbgprintf(0, "Error setting socket options on raw DCCP socket (%s)\n", strerror(errno)); + is4=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMP); + if(is4<0){ + dbgprintf(0,"Error opening raw ICMPv4 socket\n"); exit(1); - }*/ - is=socket(ip_type,SOCK_RAW | SOCK_NONBLOCK ,IPPROTO_ICMP); - if(is<0){ - dbgprintf(0,"Error opening raw ICMP socket\n"); + } + is6=socket(parms.ip_type,SOCK_RAW,IPPROTO_ICMPV6); + if(is6<0){ + dbgprintf(0,"Error opening raw ICMPv6 socket\n"); exit(1); } + /*Build DCCP packet*/ - dhdr=(struct dccp_hdr*)sbuffer; - dhdre=(struct dccp_hdr_ext*)(sbuffer+sizeof(struct dccp_hdr)); - dhdrr=(struct dccp_hdr_request*)(sbuffer+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)); + packet_seq=rand(); + buildRequestPacket(sbuffer,&slen,packet_seq); + if(parms.ip_type==AF_INET){ + addrlen=sizeof(struct sockaddr_in); + }else{ + addrlen=sizeof(struct sockaddr_in6); + } + + /*Start Message*/ + printf("PINGING %s (%s) on DCCP port %i\n",parms.hostname, addr2str(&parms.dest_addr,1),parms.dest_port); + + while(!done){ + /*Send Ping*/ + if(sendto(rs, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)parms.dest_addr.gen,addrlen)<0){ + if(errno!=EINTR){ + dbgprintf(0,"Error: sendto() failed (%s)\n",strerror(errno)); + } + } + if(parms.count==0){done=1; break;} + + if (logPacket(request_seq,packet_seq)<0){ + dbgprintf(0,"Error: Couldn't record request!\n"); + } + dbgprintf(2, "Sending DCCP Request to %s\n", addr2str(&parms.dest_addr,1)); + + /*Use select to wait on packets or until interval has passed*/ + add.tv_sec=parms.interval/1000; + add.tv_usec=(parms.interval%1000)*1000; + gettimeofday(&t,NULL); + timeradd(&t,&add,&delay); + while(timercmp(&t,&delay,<)){ + /*Prepare for select*/ + FD_ZERO(&sel); + FD_SET(ds,&sel); + FD_SET(is4,&sel); + FD_SET(is6,&sel); + timersub(&delay,&t,&timeout); + + /*Do select call*/ + if(select(MAX(ds+1,MAX(is4+1,is6+1)),&sel, NULL,NULL,&timeout)<0){ + if(errno!=EINTR){ + dbgprintf(0,"Select() error (%s)\n",strerror(errno)); + } + } + if(parms.count==0){done=1;break;} + + if(FD_ISSET(ds,&sel)){ + /*Data on the DCCP socket*/ + handleDCCPpacket(ds,rs); + + } + if(FD_ISSET(is4,&sel) && parms.ip_type==AF_INET){ + /*Data on the ICMPv4 socket*/ + handleICMP4packet(is4); + } + if(FD_ISSET(is6,&sel) && parms.ip_type==AF_INET6){ + /*Data on the ICMPv6 socket*/ + handleICMP6packet(is6); + } + gettimeofday(&t,NULL); + } + + /*Update count*/ + if(parms.count>-1){ + parms.count--; + } + request_seq++; + packet_seq=rand(); + updateRequestPacket(sbuffer,&slen, packet_seq); + } + + printStats(); + + close(rs); + close(is4); + close(is6); + close(ds); +} + +void handleDCCPpacket(int rcv_socket, int send_socket){ + int rlen=1500; + unsigned char rbuffer[rlen]; + ipaddr_ptr_t rcv_addr; + socklen_t rcv_addr_len; + struct dccp_hdr *dhdr; + struct dccp_hdr_reset *dhdr_re; + struct dccp_hdr_response *dhdr_rp; + struct dccp_hdr_ack_bits *dhdr_sync; + unsigned char* ptr; + struct iphdr* iph; + + /*Memory for socket address*/ + rcv_addr_len=sizeof(struct sockaddr_storage); + rcv_addr.gen=malloc(rcv_addr_len); + if(rcv_addr.gen==NULL){ + dbgprintf(0,"Error: Can't Allocate Memory!\n"); + exit(1); + } + + /*Receive Packet*/ + rcv_addr_len=sizeof(struct sockaddr_storage); + if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){ + if(errno!=EINTR){ + dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno)); + } + } + if(rlen<0){ + return; + } + + if(rcv_addr.gen->sa_family!=parms.ip_type){ //confirm IP type + dbgprintf(2, "DCCP packet on %s. Tossing.\n", (parms.ip_type==AF_INET) ? "IPv4" : "IPv6"); + free(rcv_addr.gen); + return; + } + + if(rcv_addr.gen->sa_family==AF_INET){ + /*IPv4*/ + if(memcmp(&rcv_addr.ipv4->sin_addr,&parms.dest_addr.ipv4->sin_addr, + sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ //not from destination + dbgprintf(2,"DCCP packet from 3rd host\n"); + free(rcv_addr.gen); + return; + } + if(rlen < sizeof(struct dccp_hdr)+sizeof(struct iphdr)){ //check packet size + + dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n"); + free(rcv_addr.gen); + return; + } + iph=(struct iphdr*)rbuffer; + ptr=rbuffer+iph->ihl*4; + }else{ + /*IPv6*/ + if(memcmp(&rcv_addr.ipv6->sin6_addr, &parms.dest_addr.ipv6->sin6_addr, + sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ //not from destination + dbgprintf(2,"DCCP packet from 3rd host\n"); + free(rcv_addr.gen); + return; + } + if(rlen < sizeof(struct dccp_hdr)){ //check packet size + + dbgprintf(2, "Packet smaller than possible DCCP packet received on DCCP socket\n"); + free(rcv_addr.gen); + return; + } + ptr=rbuffer; + } + + /*DCCP checks*/ + dhdr=(struct dccp_hdr*)ptr; + if(dhdr->dccph_sport!=htons(parms.dest_port)){ + dbgprintf(2,"DCCP packet with wrong Source Port (%i)\n", ntohs(dhdr->dccph_sport)); + free(rcv_addr.gen); + return; + } + if(dhdr->dccph_dport!=htons(parms.src_port)){ + dbgprintf(2,"DCCP packet with wrong Destination Port\n"); + free(rcv_addr.gen); + return; + } + + /*Pick Response*/ + if(dhdr->dccph_type==DCCP_PKT_RESET){ + if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_reset)){ + dbgprintf(2, "Tossing DCCP Reset packet that's small!\n"); + return; + } + dhdr_re=(struct dccp_hdr_reset*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)); + + /*Log*/ + if(dhdr_re->dccph_reset_code==DCCP_RESET_CODE_NO_CONNECTION){ + logResponse(&rcv_addr, ntohl(dhdr_re->dccph_reset_ack.dccph_ack_nr_low), DCCP_RESET,dhdr_re->dccph_reset_code,0); + } + /*Nothing else to do*/ + } + if(dhdr->dccph_type==DCCP_PKT_RESPONSE){ + if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_response)){ + dbgprintf(2, "Tossing DCCP Response packet that's too small!\n"); + return; + } + + /*Log*/ + dhdr_rp=(struct dccp_hdr_response*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)); + logResponse(&rcv_addr,ntohl(dhdr_rp->dccph_resp_ack.dccph_ack_nr_low),DCCP_RESPONSE,0,0); + + /*DCCP socket opened in getAddresses() will send Reset*/ + } + if(dhdr->dccph_type==DCCP_PKT_SYNC || dhdr->dccph_type==DCCP_PKT_SYNCACK){ + if(rlen < (ptr-rbuffer)+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_ack_bits)){ + dbgprintf(2, "Tossing DCCP Sync/SyncAck packet that's too small!\n"); + return; + } + + /*Log*/ + dhdr_sync=(struct dccp_hdr_ack_bits*)(ptr+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)); + logResponse(&rcv_addr,ntohl(dhdr_sync->dccph_ack_nr_low),DCCP_SYNC,0,0); + + /*DCCP socket opened in getAddresses() will send Reset*/ + } + + free(rcv_addr.gen); +} + +void handleICMP4packet(int rcv_socket){ + int rlen=1500; + unsigned char rbuffer[rlen]; + ipaddr_ptr_t rcv_addr; + socklen_t rcv_addr_len; + struct icmphdr *icmp4; + struct dccp_hdr *dhdr; + struct dccp_hdr_ext *dhdre; + struct iphdr* ip4hdr; + struct iphdr* iph; + + /*Memory for socket address*/ + rcv_addr_len=sizeof(struct sockaddr_storage); + rcv_addr.gen=malloc(rcv_addr_len); + if(rcv_addr.gen==NULL){ + dbgprintf(0,"Error: Can't Allocate Memory!\n"); + exit(1); + } + + /*Receive Packet*/ + if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){ + if(errno!=EINTR){ + dbgprintf(0, "Error on receive from ICMPv4 socket (%s)\n",strerror(errno)); + } + } + if(rlen<0){ + return; + } + + iph=(struct iphdr*)rbuffer; + + + if(rlen < sizeof(struct icmphdr)+sizeof(struct iphdr)){ //check packet size + dbgprintf(2, "Packet smaller than possible ICMPv4 packet!\n"); + free(rcv_addr.gen); + return; + } + + icmp4=(struct icmphdr*)(rbuffer+iph->ihl*4); + if(icmp4->type!=ICMP_DEST_UNREACH && icmp4->type!=ICMP_TIME_EXCEEDED){ //check icmp types + dbgprintf(2, "Tossing ICMPv4 packet of type %i\n", icmp4->type); + free(rcv_addr.gen); + return; + } + + /*Check packet size again*/ + if(rlenihl*4+sizeof(struct icmphdr)); + if(memcmp(&parms.src_addr.ipv4->sin_addr,&ip4hdr->saddr,sizeof(parms.src_addr.ipv4->sin_addr))!=0){ + /*Source address doesn't match*/ + dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 source address isn't us\n"); + free(rcv_addr.gen); + return; + } + if(memcmp(&parms.dest_addr.ipv4->sin_addr,&ip4hdr->daddr,sizeof(parms.dest_addr.ipv4->sin_addr))!=0){ + /*Destination address doesn't match*/ + dbgprintf(2,"Tossing ICMPv4 packet because the embedded IPv4 destination address isn't our target\n"); + free(rcv_addr.gen); + return; + } + if(ip4hdr->protocol!=IPPROTO_DCCP){ + /*Not DCCP!*/ + dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet isn't DCCP\n"); + free(rcv_addr.gen); + return; + } + + /*Decode DCCP header*/ + dhdr=(struct dccp_hdr*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4); + if(dhdr->dccph_dport!=htons(parms.dest_port)){ + /*DCCP Destination Ports don't match*/ + dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP destination port\n"); + free(rcv_addr.gen); + return; + } + if(dhdr->dccph_sport!=htons(parms.src_port)){ + /*DCCP Source Ports don't match*/ + dbgprintf(2,"Tossing ICMPv4 packet because the embedded packet doesn't have our DCCP source port\n"); + free(rcv_addr.gen); + return; + } + dhdre=(struct dccp_hdr_ext*)(rbuffer+iph->ihl*4+sizeof(struct icmphdr)+ip4hdr->ihl*4+sizeof(struct dccp_hdr)); + + /*Log*/ + if(rlentype,icmp4->code); + }else{ + logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low),ICMPv4,icmp4->type,icmp4->code); + } + free(rcv_addr.gen); + return; +} + +void handleICMP6packet(int rcv_socket){ + int rlen=1500; + unsigned char rbuffer[rlen]; + ipaddr_ptr_t rcv_addr; + socklen_t rcv_addr_len; + struct icmp6_hdr *icmp6; + struct ip6_hdr* ip6hdr; + struct dccp_hdr *dhdr; + struct dccp_hdr_ext *dhdre; + + /*Memory for socket address*/ + rcv_addr_len=sizeof(struct sockaddr_storage); + rcv_addr.gen=malloc(rcv_addr_len); + if(rcv_addr.gen==NULL){ + dbgprintf(0,"Error: Can't Allocate Memory!\n"); + exit(1); + } + + /*Receive Packet*/ + if((rlen=recvfrom(rcv_socket, &rbuffer, 1500,0,rcv_addr.gen,&rcv_addr_len))<0){ + dbgprintf(0, "Error on receive from ICMPv6 socket (%s)\n",strerror(errno)); + } + + if(rlen < sizeof(struct icmp6_hdr)){ //check packet size + dbgprintf(2, "Packet smaller than possible ICMPv6 packet!\n"); + free(rcv_addr.gen); + return; + } + + icmp6=(struct icmp6_hdr*)rbuffer; + if(icmp6->icmp6_type!=ICMP6_DST_UNREACH && icmp6->icmp6_type!=ICMP6_PACKET_TOO_BIG + && icmp6->icmp6_type!=ICMP6_TIME_EXCEEDED && icmp6->icmp6_type!=ICMP6_PARAM_PROB){ //check icmp types + dbgprintf(2, "Tossing ICMPv6 packet of type %i\n", icmp6->icmp6_type); + free(rcv_addr.gen); + return; + } + + /*Check packet size again*/ + if(rlensin6_addr,&ip6hdr->ip6_src,sizeof(parms.src_addr.ipv6->sin6_addr))!=0){ + dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 source address isn't us\n"); + /*Source address doesn't match*/ + free(rcv_addr.gen); + return; + } + if(memcmp(&parms.dest_addr.ipv6->sin6_addr,&ip6hdr->ip6_dst,sizeof(parms.dest_addr.ipv6->sin6_addr))!=0){ + /*Destination address doesn't match*/ + dbgprintf(2,"Tossing ICMPv6 packet because the embedded IPv6 destination address isn't our target\n"); + free(rcv_addr.gen); + return; + } + if(ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt!=IPPROTO_DCCP){ + /*Not DCCP!*/ + dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet isn't DCCP\n"); + free(rcv_addr.gen); + return; + } + + /*Decode DCCP header*/ + dhdr=(struct dccp_hdr*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)); + if(dhdr->dccph_dport!=htons(parms.dest_port)){ + /*DCCP Destination Ports don't match*/ + dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP destination port\n"); + free(rcv_addr.gen); + return; + } + if(dhdr->dccph_sport!=htons(parms.src_port)){ + /*DCCP Source Ports don't match*/ + dbgprintf(2,"Tossing ICMPv6 packet because the embedded packet doesn't have our DCCP source port\n"); + free(rcv_addr.gen); + return; + } + dhdre=(struct dccp_hdr_ext*)(rbuffer+sizeof(struct icmp6_hdr)+sizeof(struct ip6_hdr)+sizeof(struct dccp_hdr)); + + /*Log*/ + logResponse(&rcv_addr,ntohl(dhdre->dccph_seq_low), ICMPv6, icmp6->icmp6_type,icmp6->icmp6_code); + free(rcv_addr.gen); + return; +} + +void buildRequestPacket(unsigned char* buffer, int *len, int seq){ + struct dccp_hdr *dhdr; + struct dccp_hdr_ext *dhdre; + struct dccp_hdr_request *dhdrr; + struct iphdr* ip4hdr; + struct ip6_hdr* ip6hdr; + + int ip_hdr_len; + int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request); + + if(*len < dccp_hdr_len+sizeof(struct ip6_hdr)){ + dbgprintf(0, "Error: Insufficient buffer space\n"); + exit(1); + } + + memset(buffer, 0, *len); + + /*IP header*/ + ip4hdr=NULL; + if(parms.ip_type==AF_INET){ + ip_hdr_len=sizeof(struct iphdr); + ip4hdr=(struct iphdr*)buffer; + ip4hdr->check=htons(0); + memcpy(&ip4hdr->daddr, &parms.dest_addr.ipv4->sin_addr, sizeof(parms.dest_addr.ipv4->sin_addr)); + ip4hdr->frag_off=htons(0); + ip4hdr->id=htons(1);//first + ip4hdr->ihl=5; + ip4hdr->protocol=IPPROTO_DCCP; + memcpy(&ip4hdr->saddr, &parms.src_addr.ipv4->sin_addr, sizeof(parms.src_addr.ipv4->sin_addr)); + ip4hdr->tos=0; + ip4hdr->tot_len=htons(ip_hdr_len+dccp_hdr_len); + ip4hdr->ttl=parms.ttl; + ip4hdr->version=4; + }else{ + ip_hdr_len=sizeof(struct ip6_hdr); + ip6hdr=(struct ip6_hdr*)buffer; + memcpy(&ip6hdr->ip6_dst, &parms.dest_addr.ipv6->sin6_addr, sizeof(parms.dest_addr.ipv6->sin6_addr)); + memcpy(&ip6hdr->ip6_src, &parms.src_addr.ipv6->sin6_addr, sizeof(parms.src_addr.ipv6->sin6_addr)); + ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_flow=htonl(6<<28); //version, traffic class, flow label + ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim=parms.ttl; + ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_nxt=IPPROTO_DCCP; + ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_plen=htons(dccp_hdr_len); + } + + /*DCCP header*/ + dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len); + dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)); + dhdrr=(struct dccp_hdr_request*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)); dhdr->dccph_ccval=0; dhdr->dccph_checksum=0; dhdr->dccph_cscov=0; - dhdr->dccph_doff=slen/4; - dhdr->dccph_dport=htons(dest_port); + dhdr->dccph_doff=dccp_hdr_len/4; + dhdr->dccph_dport=htons(parms.dest_port); dhdr->dccph_reserved=0; - dhdr->dccph_sport=htons(dest_port+1); + dhdr->dccph_sport=htons(parms.src_port); dhdr->dccph_x=1; dhdr->dccph_type=DCCP_PKT_REQUEST; - dhdr->dccph_seq2=htonl(0); //Reserved for 48 bit sequence numbers - dhdr->dccph_seq=htonl(0); //just always make the high end 0 - dhdre->dccph_seq_low=htonl(seq)>>8; - dhdrr->dccph_req_service= htonl(0x50455246); - - opt=((unsigned char*)&dhdr->dccph_checksum) - &sbuffer[0]; - if (setsockopt(ds, IPPROTO_IPV6, IPV6_CHECKSUM, &opt, sizeof(opt)) < 0){ - dbgprintf(0, "Error setting up checksums on raw DCCP socket (%s)\n", strerror(errno)); + dhdr->dccph_seq2=htonl(0); //Reserved if using 48 bit sequence numbers + dhdr->dccph_seq=htonl(0); //High 16bits of sequence number. Always make 0 for simplicity. + dhdre->dccph_seq_low=htonl(seq); + dhdrr->dccph_req_service=htonl(parms.service_code); + + /*Checksums*/ + if(parms.ip_type==AF_INET){ + dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len, + (unsigned char*) &parms.dest_addr.ipv4->sin_addr, + (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP); + ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len); + }else{ + dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len, + (unsigned char*) &parms.dest_addr.ipv6->sin6_addr, + (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP); + } + *len=ip_hdr_len+dccp_hdr_len; + return; +} + +void updateRequestPacket(unsigned char* buffer, int *len, int seq){ + struct dccp_hdr *dhdr; + struct dccp_hdr_ext *dhdre; + struct iphdr* ip4hdr; + + int ip_hdr_len; + int dccp_hdr_len=sizeof(struct dccp_hdr)+sizeof(struct dccp_hdr_ext)+sizeof(struct dccp_hdr_request); + + /*IP header*/ + ip4hdr=NULL; + if(parms.ip_type==AF_INET){ + ip_hdr_len=sizeof(struct iphdr); + ip4hdr=(struct iphdr*)buffer; + ip4hdr->check=htons(0); + ip4hdr->id=htons(seq); + }else{ + ip_hdr_len=sizeof(struct ip6_hdr); + } + + /*DCCP header*/ + dhdr=(struct dccp_hdr*)(buffer+ip_hdr_len); + dhdre=(struct dccp_hdr_ext*)(buffer+ip_hdr_len+sizeof(struct dccp_hdr)); + dhdr->dccph_checksum=0; + dhdre->dccph_seq_low=htonl(seq); + + /*Checksums*/ + if(parms.ip_type==AF_INET){ + dhdr->dccph_checksum=ipv4_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len, + (unsigned char*) &parms.dest_addr.ipv4->sin_addr, + (unsigned char*)&parms.src_addr.ipv4->sin_addr, IPPROTO_DCCP); + ip4hdr->check=ipv4_chksum(buffer,ip_hdr_len); + }else{ + dhdr->dccph_checksum=ipv6_pseudohdr_chksum((buffer+ip_hdr_len), dccp_hdr_len, + (unsigned char*) &parms.dest_addr.ipv6->sin6_addr, + (unsigned char*)&parms.src_addr.ipv6->sin6_addr, IPPROTO_DCCP); + } + *len=ip_hdr_len+dccp_hdr_len; + return; +} + +int logPacket(int req_seq, int packet_seq){ + struct request *tmp; + + /*Add new request to queue*/ + tmp=malloc(sizeof(struct request)); + if(tmp==NULL){ + dbgprintf(0,"Error: Can't allocate Memory!\n"); exit(1); } - if(ip_type==AF_INET){ - iv41=(struct sockaddr_in*)dest_addr; - iv42=(struct sockaddr_in*)src_addr; - ip4_chksum(sbuffer, slen, (unsigned char*) &iv41->sin_addr, (unsigned char*)&iv42->sin_addr, 33,&dhdr->dccph_checksum); + tmp->next=NULL; + tmp->prev=NULL; + tmp->num_replies=0; + tmp->num_errors=0; + tmp->packet_seq=packet_seq; + tmp->request_seq=req_seq; + tmp->reply_type=UNKNOWN; + gettimeofday(&tmp->sent,NULL); + + if(queue.head==NULL){ + queue.head=queue.tail=tmp; + }else{ + queue.head->prev=tmp; + tmp->next=queue.head; + queue.head=tmp; } - while(!done){ - rcv_addr_len=sizeof(struct sockaddr_storage); - if(sendto(ds, &sbuffer, slen, MSG_DONTWAIT,(struct sockaddr*)dest_addr,addrlen)<0){ - dbgprintf(0,"Error: sendto failed\n"); - } - tmp=(struct sockaddr_in6*)dest_addr; - dbgprintf(0, "Sending DCCP Request to %s\n",inet_ntop(ip_type, (void*)&tmp->sin6_addr, pbuf, 1000)); + /*Update Statistics*/ + if(ping_stats.requests_sent==0){ + gettimeofday(&ping_stats.start,NULL); + } + ping_stats.requests_sent++; + return 0; +} - if((rlen=recvfrom(ds, &rbuffer, 1000,MSG_DONTWAIT,(struct sockaddr*)&rcv_addr,&rcv_addr_len))<0){ - if(errno!=EAGAIN){ - dbgprintf(0, "Error on receive from DCCP socket (%s)\n",strerror(errno)); - } - } - if(rlen>0){ - if(rlen< sizeof(struct dccp_hdr)){ - dbgprintf(0,"Received Non-DCCP data!\n"); +int logResponse(ipaddr_ptr_t *src, int seq, int type, int v1, int v2){ + struct request *cur; + long long diff; + + if(queue.tail==NULL){ + dbgprintf(2,"Response received but no requests sent!\n"); + return -1; + } + + /*Locate request*/ + cur=queue.tail; + while(cur!=NULL){ + if(cur->packet_seq==seq){ + gettimeofday(&cur->reply,NULL); + if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){ + cur->num_replies++; + }else{ + cur->num_errors++; } - dhdr=(struct dccp_hdr*)rbuffer; - tmp=(struct sockaddr_in6*)&rcv_addr; - dbgprintf(0,"Response from %s (%i)\n", inet_ntop(ip_type, (void*)&tmp->sin6_addr, pbuf, 1000),dhdr->dccph_type); + cur->reply_type=type; + break; } + cur=cur->prev; + } - if((rlen=recvfrom(is, &rbuffer, 1000,MSG_DONTWAIT,(struct sockaddr*)&rcv_addr,&rcv_addr_len))<0){ - if(errno!=EAGAIN){ - dbgprintf(0, "Error on receive from ICMP socket (%s)\n",strerror(errno)); - } + if(cur==NULL){ + if(parms.ip_type==AF_INET && seq==-1){ + /*IPv4 didn't include enough of the packet to get sequence numbers!*/ + printf("%s from %s\n",get_error_string(type,v1,v2),addr2str(src,0)); + ping_stats.errors++; + return 0; + }else{ + dbgprintf(2,"Response received but no requests sent with sequence number %i!\n", seq); + return -1; } - if(rlen>0){ - dbgprintf(0,"Received ICMP data!\n"); + } + + diff=(cur->reply.tv_usec + 1000000*cur->reply.tv_sec) - (cur->sent.tv_usec + 1000000*cur->sent.tv_sec); + + /*Print Message*/ + if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){ + if(debug==0){ + printf( "Response from %s : seq=%i time=%.1fms\n",addr2str(src,0),cur->request_seq, diff/1000.0); + }else{ + printf( "Response from %s : seq=%i time=%.1fms status=%s\n", + addr2str(src,0),cur->request_seq, diff/1000.0,response_good[type]); } + }else{ - if(count>-1){ - count--; + printf("%s from %s : seq=%i\n",get_error_string(type,v1,v2),addr2str(src,0),cur->request_seq); + } + + /*Update statistics*/ + if((type==DCCP_RESET && v1==3) || type==DCCP_RESPONSE || type==DCCP_SYNC){ + /*Good Response*/ + if(cur->num_replies==1){ + ping_stats.rtt_sum+=diff; + ping_stats.rtt_sum2+=(diff*diff); + ping_stats.replies_received++; + }else{ + printf("Duplicate packet detected! (%i)\n",cur->request_seq); + ping_stats.duplicates++; } - if(count==0){ - done=1; - break; + if(diff < ping_stats.rtt_min || ping_stats.rtt_min==0){ + ping_stats.rtt_min=diff; + } + if(diff > ping_stats.rtt_max){ + ping_stats.rtt_max=diff; } - sleep(interval/1000); + }else{ + /*Error*/ + ping_stats.errors++; } + gettimeofday(&ping_stats.stop,NULL); + return 0; +} - close(ds); - close(is); +const char *get_error_string(int type, int v1, int v2){ + const char *label=NULL; + switch(type){ + case DCCP_RESET: + if(v1>11){label=NULL;break;} + label=response_dccp_reset[v1]; + break; + case ICMPv4: + switch(v1){ + case 3: + if(v2>15){label=NULL;break;} + label=response_icmpv4_dest_unreach[v2]; + break; + case 11: + if(v2>1){label=NULL;break;} + label=response_icmpv4_ttl[v2]; + break; + default: + label=NULL; + break; + } + break; + case ICMPv6: + switch(v1){ + case 1: + if(v2>7){label=NULL;break;} + label=response_icmpv6_dest_unreach[v2]; + break; + case 2: + if(v2>0){label=NULL;break;} + label=response_icmpv6_packet_too_big; + break; + case 3: + if(v2>1){label=NULL;break;} + label=response_icmpv6_ttl[v2]; + break; + case 4: + if(v2>2){label=NULL;break;} + label=response_icmpv6_param_prob[v2]; + break; + default: + label=NULL; + break; + } + break; + } + return label; } -int ip6_chksum(unsigned char* buff, int len, unsigned char* dest, unsigned char* src, int type, unsigned short* chksum){ - struct ip6_pseudo_hdr hdr; +void clearQueue(){ + struct request *cur; + struct request *tmp; - //create pseudo header - memset(&hdr, 0, sizeof(struct ip6_pseudo_hdr)); - memcpy(hdr.src, src, IP6_ADDR_LEN); - memcpy(hdr.dest, dest, IP6_ADDR_LEN); - hdr.nxt=type; - hdr.len=htonl(len); + cur=queue.head; + while(cur!=NULL){ + tmp=cur; + cur=cur->next; + free(tmp); + } + queue.head=NULL; + queue.tail=NULL; + return; +} - //calculate total checksum - *chksum=wrapsum(checksum((unsigned char*)&hdr,sizeof(struct ip6_pseudo_hdr),checksum(buff,len,0))); -return 0; +void sigHandler(){ + /*Exit Quickly*/ + parms.count=0; } -int ip4_chksum(unsigned char* buff, int len, unsigned char* dest, unsigned char* src, int type, unsigned short* chksum){ - struct ip4_pseudo_hdr hdr; +void printStats(){ + int diff; + double ploss, rtt_avg, rtt_avg2, rtt_mdev; - //create pseudo header - memset(&hdr, 0, sizeof(struct ip4_pseudo_hdr)); - memcpy(hdr.src, src, IP4_ADDR_LEN); - memcpy(hdr.dest, dest, IP4_ADDR_LEN); - hdr.nxt=type; - hdr.len=htonl(len); + /*Compute Stats*/ + if(ping_stats.replies_received>0){ + rtt_avg=ping_stats.rtt_sum/(ping_stats.replies_received*1.0); + rtt_avg2=ping_stats.rtt_sum2/(ping_stats.replies_received*1.0); + rtt_mdev=llsqrt(rtt_avg2 - (rtt_avg*rtt_avg)); + }else{ + rtt_avg=0; + rtt_avg2=0; + rtt_mdev=0; + } + diff=(ping_stats.stop.tv_usec + 1000000*ping_stats.stop.tv_sec) - + (ping_stats.start.tv_usec + 1000000*ping_stats.start.tv_sec); + diff=diff/1000.0; - //calculate total checksum - *chksum=wrapsum(checksum((unsigned char*)&hdr,sizeof(struct ip4_pseudo_hdr),checksum(buff,len,0))); -return 0; -} + /*Print Stats*/ + gettimeofday(&ping_stats.stop,NULL); + printf("-----------%s PING STATISTICS-----------\n",parms.hostname); + ploss=(1.0*(ping_stats.requests_sent-ping_stats.replies_received)/ping_stats.requests_sent*1.0)*100; + printf("%i packets transmitted, %i received, ",ping_stats.requests_sent,ping_stats.replies_received); + if(ping_stats.duplicates>0){ + printf("%i duplicates, ",ping_stats.duplicates); + } + if(ping_stats.errors>0){ + printf("%i errors, ",ping_stats.errors); + } + printf("%.2f%% loss, time %ims\n",ploss,diff); + if(ping_stats.replies_received>ping_stats.requests_sent){ + printf("+Somebody is creating packets out of thing air!\n"); + } + printf("rtt min/avg/max/mdev = %.1f/%.1f/%.1f/%.1f ms\n", + ping_stats.rtt_min/1000.0,rtt_avg/1000.0,ping_stats.rtt_max/1000.0,rtt_mdev/1000.0); -u_int32_t checksum(unsigned char *buf, unsigned nbytes, u_int32_t sum) -{ - int i; - /* Checksum all the pairs of bytes first... */ - for (i = 0; i < (nbytes & ~1U); i += 2) { - sum += (u_int16_t)ntohs(*((u_int16_t *)(buf + i))); - if (sum > 0xFFFF) - sum -= 0xFFFF; - } - /* - * If there's a single byte left over, checksum it, too. - * Network byte order is big-endian, so the remaining byte is - * the high byte. - */ - if (i < nbytes) { - sum += buf[i] << 8; - if (sum > 0xFFFF) - sum -= 0xFFFF; - } - return (sum); } -u_int32_t wrapsum(u_int32_t sum) -{ - sum = ~sum & 0xFFFF; - return (htons(sum)); +char* addr2str(ipaddr_ptr_t *res, int nores){ + int size; + int ret; + if (!res->gen->sa_family) + return NULL; + + if(res->gen->sa_family==AF_INET){ + size=sizeof(struct sockaddr_in); + }else if(res->gen->sa_family==AF_INET6){ + size=sizeof(struct sockaddr_in6); + }else{ + return NULL; + } + if((ret=getnameinfo(res->gen, size, + addr2str_buf, sizeof (addr2str_buf), 0, 0, NI_NUMERICHOST))<0){ + dbgprintf(0,"Error: getnameinfo() returned %s\n",gai_strerror(ret)); + } + + if (parms.no_resolve||nores){ + return addr2str_buf; + }else{ + addr2nm_buf[0] = '\0'; + getnameinfo(res->gen, size, + addr2nm_buf, sizeof (addr2nm_buf), 0, 0, NI_IDN); + snprintf(addr2both_buf,1000," %s (%s)", addr2nm_buf[0] ? addr2nm_buf : addr2str_buf, addr2str_buf); + return addr2both_buf; + } + return NULL; } /*Usage information for program*/ void usage() { - dbgprintf(0, "dccpping: [-d] [-6|-4] [-c count] [-p port] [-i interval] [-t ttl] [-S srcaddress] remote_host\n"); + dbgprintf(0, "dccpping: [-v] [-V] [-h] [-n] [-6|-4] [-c count] [-p port] [-i interval]\n"); + dbgprintf(0, " [-t ttl] [-s service_code] [-S srcaddress] remote_host\n"); + dbgprintf(0, "\n"); + dbgprintf(0, " -v Verbose. May be repeated for aditional verbosity.\n"); + dbgprintf(0, " -V Version information\n"); + dbgprintf(0, " -h Help\n"); + dbgprintf(0, " -n Numeric output only\n"); + dbgprintf(0, " -6 Force IPv6 mode\n"); + dbgprintf(0, " -4 Force IPv4 mode\n"); + exit(0); +} + +void version(){ + dbgprintf(0, "dccpping version %.1f\nCopyright (C) 2012 Samuel Jero \n", DCCPPING_VERSION); + dbgprintf(0, "This program comes with ABSOLUTELY NO WARRANTY.\n"); + dbgprintf(0, "This is free software, and you are welcome to\nredistribute it under certain conditions.\n"); exit(0); } @@ -431,3 +1346,21 @@ void dbgprintf(int level, const char *fmt, ...) va_end(args); } } + +/*Square Root function for longs*/ +/*Borrowed from iputils/ping_common.c*/ +/* http://www.skbuff.net/iputils/ */ +static long llsqrt(long long a) +{ + long long prev = ~((long long)1 << 63); + long long x = a; + + if (x > 0) { + while (x < prev) { + prev = x; + x = (x+(a/x))/2; + } + } + + return (long)x; +}