aboutsummaryrefslogtreecommitdiff
path: root/plugins-root/check_icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins-root/check_icmp.c')
-rw-r--r--plugins-root/check_icmp.c491
1 files changed, 330 insertions, 161 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index 4098874c..6aeedf45 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)
{
@@ -390,7 +419,90 @@ 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)
+ address_family = AF_INET;
+ int icmp_proto = IPPROTO_ICMP;
+
+ /* 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:64")) != EOF) {
+ unsigned short size;
+ switch(arg) {
+ case 'v':
+ debug++;
+ break;
+ case 'b':
+ size = (unsigned short)strtol(optarg,NULL,0);
+ if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
+ size < MAX_PING_DATA) {
+ icmp_data_size = size;
+ icmp_pkt_size = size + ICMP_MINLEN;
+ } else
+ usage_va("ICMP data length must be between: %d and %d",
+ sizeof(struct icmp) + sizeof(struct icmp_ping_data),
+ MAX_PING_DATA - 1);
+ break;
+ case 'i':
+ pkt_interval = get_timevar(optarg);
+ break;
+ case 'I':
+ target_interval = get_timevar(optarg);
+ break;
+ case 'w':
+ get_threshold(optarg, &warn);
+ break;
+ case 'c':
+ get_threshold(optarg, &crit);
+ break;
+ case 'n':
+ case 'p':
+ packets = strtoul(optarg, NULL, 0);
+ break;
+ case 't':
+ timeout = strtoul(optarg, NULL, 0);
+ if(!timeout) timeout = 10;
+ break;
+ case 'H':
+ add_target(optarg);
+ break;
+ case 'l':
+ ttl = (unsigned char)strtoul(optarg, NULL, 0);
+ break;
+ case 'm':
+ min_hosts_alive = (int)strtoul(optarg, NULL, 0);
+ break;
+ case 'd': /* implement later, for cluster checks */
+ warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
+ if(ptr) {
+ crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
+ }
+ break;
+ case 's': /* specify source IP address */
+ set_source_ip(optarg);
+ break;
+ case 'V': /* version */
+ print_revision (progname, NP_VERSION);
+ exit (STATE_UNKNOWN);
+ case 'h': /* help */
+ print_help ();
+ exit (STATE_UNKNOWN);
+ break;
+ case '4':
+ address_family = AF_INET;
+ icmp_proto = IPPROTO_ICMP;
+ break;
+ case '6':
+#ifdef USE_IPV6
+ address_family = AF_INET6;
+ icmp_proto = IPPROTO_ICMPV6;
+#else
+ usage (_("IPv6 support not available\n"));
+#endif
+ break;
+ }
+ }
+ }
+
+ if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
sockets |= HAVE_ICMP;
else icmp_sockerrno = errno;
@@ -403,7 +515,10 @@ main(int argc, char **argv)
/* else tcp_sockerrno = errno; */
/* now drop privileges (no effect if not setsuid or geteuid() == 0) */
- setuid(getuid());
+ if (setuid(getuid()) == -1) {
+ printf("ERROR: Failed to drop privileges\n");
+ return 1;
+ }
#ifdef SO_TIMESTAMP
if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
@@ -467,73 +582,6 @@ main(int argc, char **argv)
strcpy(argv[1], "-V");
}
- /* 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) {
- unsigned short size;
- switch(arg) {
- case 'v':
- debug++;
- break;
- case 'b':
- size = (unsigned short)strtol(optarg,NULL,0);
- if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
- size < MAX_PING_DATA) {
- icmp_data_size = size;
- icmp_pkt_size = size + ICMP_MINLEN;
- } else
- usage_va("ICMP data length must be between: %d and %d",
- sizeof(struct icmp) + sizeof(struct icmp_ping_data),
- MAX_PING_DATA - 1);
- break;
- case 'i':
- pkt_interval = get_timevar(optarg);
- break;
- case 'I':
- target_interval = get_timevar(optarg);
- break;
- case 'w':
- get_threshold(optarg, &warn);
- break;
- case 'c':
- get_threshold(optarg, &crit);
- break;
- case 'n':
- case 'p':
- packets = strtoul(optarg, NULL, 0);
- break;
- case 't':
- timeout = strtoul(optarg, NULL, 0);
- if(!timeout) timeout = 10;
- break;
- case 'H':
- add_target(optarg);
- break;
- case 'l':
- ttl = (unsigned char)strtoul(optarg, NULL, 0);
- break;
- case 'm':
- min_hosts_alive = (int)strtoul(optarg, NULL, 0);
- break;
- case 'd': /* implement later, for cluster checks */
- warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
- if(ptr) {
- crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
- }
- break;
- case 's': /* specify source IP address */
- set_source_ip(optarg);
- break;
- case 'V': /* version */
- print_revision (progname, NP_VERSION);
- exit (STATE_UNKNOWN);
- case 'h': /* help */
- print_help ();
- exit (STATE_UNKNOWN);
- }
- }
- }
-
argv = &argv[optind];
while(*argv) {
add_target(*argv);
@@ -633,7 +681,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 +745,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 +761,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 +800,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 +810,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 +835,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 +851,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 +895,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 +921,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 +1003,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 +1056,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 +1080,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
break ;
}
}
+
if (!chdr)
#endif // SO_TIMESTAMP
gettimeofday(tv, &tz);
@@ -991,6 +1114,7 @@ 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
@@ -1039,10 +1163,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 +1230,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 +1237,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;
- /* disregard obviously stupid addresses */
- if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY)
+ if (address_family == AF_INET)
+ sin = (struct sockaddr_in *)in;
+ else
+ sin6 = (struct sockaddr_in6 *)in;
+
+
+
+ /* 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 +1273,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) {
+ char straddr[INET6_ADDRSTRLEN];
+ parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
crash("add_target_ip(%s, %s): malloc(%d) failed",
- arg, inet_ntoa(*in), sizeof(struct rta_host));
+ 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 +1312,45 @@ 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 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));
+ 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;
}
}
/* 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 +1359,7 @@ add_target(char *arg)
}
break;
}
+ freeaddrinfo(res);
return 0;
}
@@ -1203,7 +1370,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 +1478,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 +1514,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);