diff options
Diffstat (limited to 'plugins-root/check_icmp.c')
-rw-r--r-- | plugins-root/check_icmp.c | 473 |
1 files changed, 340 insertions, 133 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 9ed12ba1..01ae174a 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -67,7 +67,9 @@ const char *email = "devel@monitoring-plugins.org"; #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> +#include <netinet/ip6.h> #include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> #include <arpa/inet.h> #include <signal.h> #include <float.h> @@ -113,8 +115,8 @@ typedef struct rta_host { unsigned short id; /* id in **table, and icmp pkts */ char *name; /* arg used for adding this host */ char *msg; /* icmp error message, if any */ - struct sockaddr_in saddr_in; /* the address of this host */ - struct in_addr error_addr; /* stores address of error replies */ + struct sockaddr_storage saddr_in; /* the address of this host */ + struct sockaddr_storage error_addr; /* stores address of error replies */ unsigned long long time_waited; /* total time waited, in usecs */ unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ unsigned char icmp_type, icmp_code; /* type and code from errors */ @@ -140,6 +142,18 @@ typedef struct icmp_ping_data { unsigned short ping_id; } icmp_ping_data; +typedef union ip_hdr { + struct ip ip; + struct ip6_hdr ip6; +} ip_hdr; + +typedef union icmp_packet { + void *buf; + struct icmp *icp; + struct icmp6_hdr *icp6; + u_short *cksum_in; +} icmp_packet; + /* the different modes of this program are as follows: * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) * MODE_HOSTCHECK: Return immediately upon any sign of life @@ -190,8 +204,9 @@ static int get_threshold(char *str, threshold *th); static void run_checks(void); static void set_source_ip(char *); static int add_target(char *); -static int add_target_ip(char *, struct in_addr *); -static int handle_random_icmp(unsigned char *, struct sockaddr_in *); +static int add_target_ip(char *, struct sockaddr_storage *); +static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); +static void parse_address(struct sockaddr_storage *, char *, int); static unsigned short icmp_checksum(unsigned short *, int); static void finish(int); static void crash(const char *, ...); @@ -300,7 +315,7 @@ get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) } static int -handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) +handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { struct icmp p, sent_icmp; struct rta_host *host = NULL; @@ -342,9 +357,11 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) /* it is indeed a response for us */ host = table[ntohs(sent_icmp.icmp_seq)/packets]; if(debug) { + char address[INET6_ADDRSTRLEN]; + parse_address(addr, address, sizeof(address)); printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", - get_icmp_error_msg(p.icmp_type, p.icmp_code), - inet_ntoa(addr->sin_addr), host->name); + get_icmp_error_msg(p.icmp_type, p.icmp_code), + address, host->name); } icmp_lost++; @@ -364,11 +381,23 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) } host->icmp_type = p.icmp_type; host->icmp_code = p.icmp_code; - host->error_addr.s_addr = addr->sin_addr.s_addr; + host->error_addr = *addr; return 0; } +void parse_address(struct sockaddr_storage *addr, char *address, int size) +{ + switch (address_family) { + case AF_INET: + inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); + break; + case AF_INET6: + inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); + break; + } +} + int main(int argc, char **argv) { @@ -381,6 +410,7 @@ main(int argc, char **argv) #ifdef SO_TIMESTAMP int on = 1; #endif + char * opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:64"; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); @@ -390,33 +420,8 @@ main(int argc, char **argv) * that before pointer magic (esp. on network data) */ icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; - if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) - sockets |= HAVE_ICMP; - else icmp_sockerrno = errno; - - /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */ - /* sockets |= HAVE_UDP; */ - /* else udp_sockerrno = errno; */ - - /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */ - /* sockets |= HAVE_TCP; */ - /* else tcp_sockerrno = errno; */ - - /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ - setuid(getuid()); - -#ifdef SO_TIMESTAMP - if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) - if(debug) printf("Warning: no SO_TIMESTAMP support\n"); -#endif // SO_TIMESTAMP - - /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ - environ = NULL; - - /* use the pid to mark packets as ours */ - /* Some systems have 32-bit pid_t so mask off only 16 bits */ - pid = getpid() & 0xffff; - /* printf("pid = %u\n", pid); */ + address_family = -1; + int icmp_proto = IPPROTO_ICMP; /* get calling name the old-fashioned way for portability instead * of relying on the glibc-ism __progname */ @@ -456,20 +461,35 @@ main(int argc, char **argv) packets = 5; } - /* Parse extra opts if any */ - argv=np_extra_opts(&argc, argv, progname); - - /* support "--help" and "--version" */ - if(argc == 2) { - if(!strcmp(argv[1], "--help")) - strcpy(argv[1], "-h"); - if(!strcmp(argv[1], "--version")) - strcpy(argv[1], "-V"); + /* Parse protocol arguments first */ + for(i = 1; i < argc; i++) { + while((arg = getopt(argc, argv, opts_str)) != EOF) { + unsigned short size; + switch(arg) { + case '4': + if (address_family != -1) + crash("Multiple protocol versions not supported"); + address_family = AF_INET; + break; + case '6': +#ifdef USE_IPV6 + if (address_family != -1) + crash("Multiple protocol versions not supported"); + address_family = AF_INET6; +#else + usage (_("IPv6 support not available\n")); +#endif + break; + } + } } + /* Reset argument scanning */ + optind = 1; + /* parse the arguments */ for(i = 1; i < argc; i++) { - while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:s:i:b:I:l:m:")) != EOF) { + while((arg = getopt(argc, argv, opts_str)) != EOF) { unsigned short size; switch(arg) { case 'v': @@ -482,7 +502,7 @@ main(int argc, char **argv) icmp_data_size = size; icmp_pkt_size = size + ICMP_MINLEN; } else - usage_va("ICMP data length must be between: %d and %d", + usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data), MAX_PING_DATA - 1); break; @@ -530,10 +550,30 @@ main(int argc, char **argv) case 'h': /* help */ print_help (); exit (STATE_UNKNOWN); + break; } } } + /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ + environ = NULL; + + /* use the pid to mark packets as ours */ + /* Some systems have 32-bit pid_t so mask off only 16 bits */ + pid = getpid() & 0xffff; + /* printf("pid = %u\n", pid); */ + + /* Parse extra opts if any */ + argv=np_extra_opts(&argc, argv, progname); + + /* support "--help" and "--version" */ + if(argc == 2) { + if(!strcmp(argv[1], "--help")) + strcpy(argv[1], "-h"); + if(!strcmp(argv[1], "--version")) + strcpy(argv[1], "-V"); + } + argv = &argv[optind]; while(*argv) { add_target(*argv); @@ -545,6 +585,30 @@ main(int argc, char **argv) exit(3); } + // add_target might change address_family + switch ( address_family ){ + case AF_INET: icmp_proto = IPPROTO_ICMP; + break; + case AF_INET6: icmp_proto = IPPROTO_ICMPV6; + break; + default: crash("Address family not supported"); + } + if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) + sockets |= HAVE_ICMP; + else icmp_sockerrno = errno; + + +#ifdef SO_TIMESTAMP + if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) + if(debug) printf("Warning: no SO_TIMESTAMP support\n"); +#endif // SO_TIMESTAMP + + /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ + if (setuid(getuid()) == -1) { + printf("ERROR: Failed to drop privileges\n"); + return 1; + } + if(!sockets) { if(icmp_sock == -1) { errno = icmp_sockerrno; @@ -608,7 +672,7 @@ main(int argc, char **argv) if(max_completion_time > (u_int)timeout * 1000000) { printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); - printf("Timout must be at lest %llu\n", + printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); } } @@ -633,7 +697,7 @@ main(int argc, char **argv) } host = list; - table = malloc(sizeof(struct rta_host **) * targets); + table = (struct rta_host**)malloc(sizeof(struct rta_host **) * targets); i = 0; while(host) { host->id = i*packets; @@ -697,9 +761,15 @@ run_checks() } } + /* response structure: + * IPv4: * ip header : 20 bytes * icmp header : 28 bytes + * IPv6: + * ip header : 40 bytes + * icmp header : 28 bytes + * both: * icmp echo reply : the rest */ static int @@ -707,16 +777,27 @@ wait_for_reply(int sock, u_int t) { int n, hlen; static unsigned char buf[4096]; - struct sockaddr_in resp_addr; - struct ip *ip; - struct icmp icp; + struct sockaddr_storage resp_addr; + union ip_hdr *ip; + union icmp_packet packet; struct rta_host *host; struct icmp_ping_data data; struct timeval wait_start, now; u_int tdiff, i, per_pkt_wait; + if (!(packet.buf = malloc(icmp_pkt_size))) { + crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", + icmp_pkt_size); + return -1; /* might be reached if we're in debug mode */ + } + + memset(packet.buf, 0, icmp_pkt_size); + /* if we can't listen or don't have anything to listen to, just return */ - if(!t || !icmp_pkts_en_route) return 0; + if(!t || !icmp_pkts_en_route) { + free(packet.buf); + return 0; + } gettimeofday(&wait_start, &tz); @@ -735,7 +816,7 @@ wait_for_reply(int sock, u_int t) /* reap responses until we hit a timeout */ n = recvfrom_wto(sock, buf, sizeof(buf), - (struct sockaddr *)&resp_addr, &t, &now); + (struct sockaddr *)&resp_addr, &t, &now); if(!n) { if(debug > 1) { printf("recvfrom_wto() timed out during a %u usecs wait\n", @@ -745,12 +826,23 @@ wait_for_reply(int sock, u_int t) } if(n < 0) { if(debug) printf("recvfrom_wto() returned errors\n"); + free(packet.buf); return n; } - ip = (struct ip *)buf; - if(debug > 1) printf("received %u bytes from %s\n", - ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr)); + // FIXME: with ipv6 we don't have an ip header here + if (address_family != AF_INET6) { + ip = (union ip_hdr *)buf; + + if(debug > 1) { + char address[INET6_ADDRSTRLEN]; + parse_address(&resp_addr, address, sizeof(address)); + printf("received %u bytes from %s\n", + address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) + : ntohs(ip->ip.ip_len), + address); + } + } /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ @@ -759,12 +851,14 @@ wait_for_reply(int sock, u_int t) * off the bottom 4 bits */ /* hlen = (ip->ip_vhl & 0x0f) << 2; */ /* #else */ - hlen = ip->ip_hl << 2; + hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2; /* #endif */ if(n < (hlen + ICMP_MINLEN)) { + char address[INET6_ADDRSTRLEN]; + parse_address(&resp_addr, address, sizeof(address)); crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", - n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr)); + n, hlen + icmp_pkt_size, address); } /* else if(debug) { */ /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ @@ -773,23 +867,39 @@ wait_for_reply(int sock, u_int t) /* } */ /* check the response */ - memcpy(&icp, buf + hlen, sizeof(icp)); - if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || - ntohs(icp.icmp_seq) >= targets*packets) { + memcpy(packet.buf, buf + hlen, icmp_pkt_size); +/* address_family == AF_INET6 ? sizeof(struct icmp6_hdr) + : sizeof(struct icmp));*/ + + if( (address_family == PF_INET && + (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY + || ntohs(packet.icp->icmp_seq) >= targets * packets)) + || (address_family == PF_INET6 && + (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY + || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); handle_random_icmp(buf + hlen, &resp_addr); continue; } /* this is indeed a valid response */ - memcpy(&data, icp.icmp_data, sizeof(data)); - if (debug > 2) - printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", - (unsigned long)sizeof(data), ntohs(icp.icmp_id), - ntohs(icp.icmp_seq), icp.icmp_cksum); + if (address_family == PF_INET) { + memcpy(&data, packet.icp->icmp_data, sizeof(data)); + if (debug > 2) + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", + (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), + ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); + host = table[ntohs(packet.icp->icmp_seq)/packets]; + } else { + memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); + if (debug > 2) + printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", + (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), + ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); + host = table[ntohs(packet.icp6->icmp6_seq)/packets]; + } - host = table[ntohs(icp.icmp_seq)/packets]; tdiff = get_timevaldiff(&data.stime, &now); host->time_waited += tdiff; @@ -801,22 +911,25 @@ wait_for_reply(int sock, u_int t) host->rtmin = tdiff; if(debug) { + char address[INET6_ADDRSTRLEN]; + parse_address(&resp_addr, address, sizeof(address)); printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", - (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr), - ttl, ip->ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); + (float)tdiff / 1000, address, + ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); } /* if we're in hostcheck mode, exit with limited printouts */ if(mode == MODE_HOSTCHECK) { printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" - "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", - host->name, icmp_recv, (float)tdiff / 1000, - icmp_recv, packets, (float)tdiff / 1000, - (float)warn.rta / 1000, (float)crit.rta / 1000); + "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", + host->name, icmp_recv, (float)tdiff / 1000, + icmp_recv, packets, (float)tdiff / 1000, + (float)warn.rta / 1000, (float)crit.rta / 1000); exit(STATE_OK); } } + free(packet.buf); return 0; } @@ -824,62 +937,81 @@ wait_for_reply(int sock, u_int t) static int send_icmp_ping(int sock, struct rta_host *host) { - static union { - void *buf; /* re-use so we prevent leaks */ - struct icmp *icp; - u_short *cksum_in; - } packet = { NULL }; long int len; struct icmp_ping_data data; struct msghdr hdr; struct iovec iov; struct timeval tv; - struct sockaddr *addr; + void *buf = NULL; if(sock == -1) { errno = 0; crash("Attempt to send on bogus socket"); return -1; } - addr = (struct sockaddr *)&host->saddr_in; - if(!packet.buf) { - if (!(packet.buf = malloc(icmp_pkt_size))) { + if(!buf) { + if (!(buf = malloc(icmp_pkt_size))) { crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); return -1; /* might be reached if we're in debug mode */ } } - memset(packet.buf, 0, icmp_pkt_size); + memset(buf, 0, icmp_pkt_size); - if((gettimeofday(&tv, &tz)) == -1) return -1; + if((gettimeofday(&tv, &tz)) == -1) { + free(buf); + return -1; + } data.ping_id = 10; /* host->icmp.icmp_sent; */ memcpy(&data.stime, &tv, sizeof(tv)); - memcpy(&packet.icp->icmp_data, &data, sizeof(data)); - packet.icp->icmp_type = ICMP_ECHO; - packet.icp->icmp_code = 0; - packet.icp->icmp_cksum = 0; - packet.icp->icmp_id = htons(pid); - packet.icp->icmp_seq = htons(host->id++); - packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size); - - if (debug > 2) - printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", - (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), - ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, - host->name); + + if (address_family == AF_INET) { + struct icmp *icp = (struct icmp*)buf; + + memcpy(&icp->icmp_data, &data, sizeof(data)); + + icp->icmp_type = ICMP_ECHO; + icp->icmp_code = 0; + icp->icmp_cksum = 0; + icp->icmp_id = htons(pid); + icp->icmp_seq = htons(host->id++); + icp->icmp_cksum = icmp_checksum((unsigned short*)buf, icmp_pkt_size); + + if (debug > 2) + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", + (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); + } + else { + struct icmp6_hdr *icp6 = (struct icmp6_hdr*)buf; + memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data)); + icp6->icmp6_type = ICMP6_ECHO_REQUEST; + icp6->icmp6_code = 0; + icp6->icmp6_cksum = 0; + icp6->icmp6_id = htons(pid); + icp6->icmp6_seq = htons(host->id++); + // let checksum be calculated automatically + + if (debug > 2) { + printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", + (unsigned long)sizeof(data), ntohs(icp6->icmp6_id), + ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); + } + } memset(&iov, 0, sizeof(iov)); - iov.iov_base = packet.buf; + iov.iov_base = buf; iov.iov_len = icmp_pkt_size; memset(&hdr, 0, sizeof(hdr)); - hdr.msg_name = addr; - hdr.msg_namelen = sizeof(struct sockaddr); + hdr.msg_name = (struct sockaddr *)&host->saddr_in; + hdr.msg_namelen = sizeof(struct sockaddr_storage); hdr.msg_iov = &iov; hdr.msg_iovlen = 1; + errno = 0; + /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ #ifdef MSG_CONFIRM len = sendmsg(sock, &hdr, MSG_CONFIRM); @@ -887,9 +1019,15 @@ send_icmp_ping(int sock, struct rta_host *host) len = sendmsg(sock, &hdr, 0); #endif + free(buf); + if(len < 0 || (unsigned int)len != icmp_pkt_size) { - if(debug) printf("Failed to send ping to %s\n", - inet_ntoa(host->saddr_in.sin_addr)); + if(debug) { + char address[INET6_ADDRSTRLEN]; + parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); + printf("Failed to send ping to %s: %s\n", address, strerror(errno)); + } + errno = 0; return -1; } @@ -934,7 +1072,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, if(!n) return 0; /* timeout */ - slen = sizeof(struct sockaddr); + slen = sizeof(struct sockaddr_storage); memset(&iov, 0, sizeof(iov)); iov.iov_base = buf; @@ -958,6 +1096,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, break ; } } + if (!chdr) #endif // SO_TIMESTAMP gettimeofday(tv, &tz); @@ -991,10 +1130,11 @@ finish(int sig) /* iterate thrice to calculate values, give output, and print perfparse */ host = list; + while(host) { if(!host->icmp_recv) { /* rta 0 is ofcourse not entirely correct, but will still show up - * conspicuosly as missing entries in perfparse and cacti */ + * conspicuously as missing entries in perfparse and cacti */ pl = 100; rta = 0; status = STATE_CRITICAL; @@ -1039,10 +1179,12 @@ finish(int sig) if(!host->icmp_recv) { status = STATE_CRITICAL; if(host->flags & FLAG_LOST_CAUSE) { + char address[INET6_ADDRSTRLEN]; + parse_address(&host->error_addr, address, sizeof(address)); printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), - inet_ntoa(host->error_addr), + address, 100); } else { /* not marked as lost cause, so we have no flags for it */ @@ -1104,7 +1246,6 @@ get_timevaldiff(struct timeval *early, struct timeval *later) { return 0; } - ret = (later->tv_sec - early->tv_sec) * 1000000; ret += later->tv_usec - early->tv_usec; @@ -1112,18 +1253,35 @@ get_timevaldiff(struct timeval *early, struct timeval *later) } static int -add_target_ip(char *arg, struct in_addr *in) +add_target_ip(char *arg, struct sockaddr_storage *in) { struct rta_host *host; + struct sockaddr_in *sin, *host_sin; + struct sockaddr_in6 *sin6, *host_sin6; + + if (address_family == AF_INET) + sin = (struct sockaddr_in *)in; + else + sin6 = (struct sockaddr_in6 *)in; - /* disregard obviously stupid addresses */ - if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY) + + + /* disregard obviously stupid addresses + * (I didn't find an ipv6 equivalent to INADDR_NONE) */ + if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE + || sin->sin_addr.s_addr == INADDR_ANY))) + || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { return -1; + } /* no point in adding two identical IP's, so don't. ;) */ host = list; while(host) { - if(host->saddr_in.sin_addr.s_addr == in->s_addr) { + host_sin = (struct sockaddr_in *)&host->saddr_in; + host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; + + if( (address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) + || (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) { if(debug) printf("Identical IP already exists. Not adding %s\n", arg); return -1; } @@ -1131,19 +1289,29 @@ add_target_ip(char *arg, struct in_addr *in) } /* add the fresh ip */ - host = malloc(sizeof(struct rta_host)); + host = (struct rta_host*)malloc(sizeof(struct rta_host)); if(!host) { - crash("add_target_ip(%s, %s): malloc(%d) failed", - arg, inet_ntoa(*in), sizeof(struct rta_host)); + char straddr[INET6_ADDRSTRLEN]; + parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr)); + crash("add_target_ip(%s, %s): malloc(%lu) failed", + arg, straddr, sizeof(struct rta_host)); } memset(host, 0, sizeof(struct rta_host)); /* set the values. use calling name for output */ host->name = strdup(arg); - /* fill out the sockaddr_in struct */ - host->saddr_in.sin_family = AF_INET; - host->saddr_in.sin_addr.s_addr = in->s_addr; + /* fill out the sockaddr_storage struct */ + if(address_family == AF_INET) { + host_sin = (struct sockaddr_in *)&host->saddr_in; + host_sin->sin_family = AF_INET; + host_sin->sin_addr.s_addr = sin->sin_addr.s_addr; + } + else { + host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; + host_sin6->sin6_family = AF_INET6; + memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr); + } host->rtmin = DBL_MAX; @@ -1160,31 +1328,67 @@ add_target_ip(char *arg, struct in_addr *in) static int add_target(char *arg) { - int i; - struct hostent *he; - struct in_addr *in, ip; + int error, result; + struct sockaddr_storage ip; + struct addrinfo hints, *res, *p; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + switch (address_family) { + case -1: + /* -4 and -6 are not specified on cmdline */ + address_family = AF_INET; + sin = (struct sockaddr_in *)&ip; + result = inet_pton(address_family, arg, &sin->sin_addr); +#ifdef USE_IPV6 + if( result != 1 ){ + address_family = AF_INET6; + sin6 = (struct sockaddr_in6 *)&ip; + result = inet_pton(address_family, arg, &sin6->sin6_addr); + } +#endif + /* If we don't find any valid addresses, we still don't know the address_family */ + if ( result != 1) { + address_family = -1; + } + break; + case AF_INET: + sin = (struct sockaddr_in *)&ip; + result = inet_pton(address_family, arg, &sin->sin_addr); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&ip; + result = inet_pton(address_family, arg, &sin6->sin6_addr); + break; + default: crash("Address family not supported"); + } /* don't resolve if we don't have to */ - if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) { + if(result == 1) { /* don't add all ip's if we were given a specific one */ return add_target_ip(arg, &ip); - /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */ - /* if(!he) return add_target_ip(arg, in); */ } else { errno = 0; - he = gethostbyname(arg); - if(!he) { + memset(&hints, 0, sizeof(hints)); + if (address_family == -1) { + hints.ai_family = AF_UNSPEC; + } else { + hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6; + } + hints.ai_socktype = SOCK_RAW; + if((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { errno = 0; - crash("Failed to resolve %s", arg); + crash("Failed to resolve %s: %s", arg, gai_strerror(error)); return -1; } + address_family = res->ai_family; } /* possibly add all the IP's as targets */ - for(i = 0; he->h_addr_list[i]; i++) { - in = (struct in_addr *)he->h_addr_list[i]; - add_target_ip(arg, in); + for(p = res; p != NULL; p = p->ai_next) { + memcpy(&ip, p->ai_addr, p->ai_addrlen); + add_target_ip(arg, &ip); /* this is silly, but it works */ if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { @@ -1193,6 +1397,7 @@ add_target(char *arg) } break; } + freeaddrinfo(res); return 0; } @@ -1203,7 +1408,7 @@ set_source_ip(char *arg) struct sockaddr_in src; memset(&src, 0, sizeof(src)); - src.sin_family = AF_INET; + src.sin_family = address_family; if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) src.sin_addr.s_addr = get_ip_address(arg); if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) @@ -1311,12 +1516,12 @@ get_threshold(char *str, threshold *th) unsigned short icmp_checksum(unsigned short *p, int n) { - register unsigned short cksum; - register long sum = 0; + unsigned short cksum; + long sum = 0; - while(n > 1) { + while(n > 2) { sum += *p++; - n -= 2; + n -= sizeof(unsigned short); } /* mop up the occasional odd byte */ @@ -1347,6 +1552,8 @@ print_help(void) printf (" %s\n", "-H"); printf (" %s\n", _("specify a target")); + printf (" %s\n", "[-4|-6]"); + printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); printf (" %s\n", "-w"); printf (" %s", _("warning threshold (currently ")); printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); |