summaryrefslogtreecommitdiffstats
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.c573
1 files changed, 399 insertions, 174 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index 9ed12ba..1d47e9f 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -1,39 +1,39 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2*
3* Monitoring check_icmp plugin 3* Monitoring check_icmp plugin
4* 4*
5* License: GPL 5* License: GPL
6* Copyright (c) 2005-2008 Monitoring Plugins Development Team 6* Copyright (c) 2005-2008 Monitoring Plugins Development Team
7* Original Author : Andreas Ericsson <ae@op5.se> 7* Original Author : Andreas Ericsson <ae@op5.se>
8* 8*
9* Description: 9* Description:
10* 10*
11* This file contains the check_icmp plugin 11* This file contains the check_icmp plugin
12* 12*
13* Relevant RFC's: 792 (ICMP), 791 (IP) 13* Relevant RFC's: 792 (ICMP), 791 (IP)
14* 14*
15* This program was modeled somewhat after the check_icmp program, 15* This program was modeled somewhat after the check_icmp program,
16* which was in turn a hack of fping (www.fping.org) but has been 16* which was in turn a hack of fping (www.fping.org) but has been
17* completely rewritten since to generate higher precision rta values, 17* completely rewritten since to generate higher precision rta values,
18* and support several different modes as well as setting ttl to control. 18* and support several different modes as well as setting ttl to control.
19* redundant routes. The only remainders of fping is currently a few 19* redundant routes. The only remainders of fping is currently a few
20* function names. 20* function names.
21* 21*
22* 22*
23* This program is free software: you can redistribute it and/or modify 23* This program is free software: you can redistribute it and/or modify
24* it under the terms of the GNU General Public License as published by 24* it under the terms of the GNU General Public License as published by
25* the Free Software Foundation, either version 3 of the License, or 25* the Free Software Foundation, either version 3 of the License, or
26* (at your option) any later version. 26* (at your option) any later version.
27* 27*
28* This program is distributed in the hope that it will be useful, 28* This program is distributed in the hope that it will be useful,
29* but WITHOUT ANY WARRANTY; without even the implied warranty of 29* but WITHOUT ANY WARRANTY; without even the implied warranty of
30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31* GNU General Public License for more details. 31* GNU General Public License for more details.
32* 32*
33* You should have received a copy of the GNU General Public License 33* You should have received a copy of the GNU General Public License
34* along with this program. If not, see <http://www.gnu.org/licenses/>. 34* along with this program. If not, see <http://www.gnu.org/licenses/>.
35* 35*
36* 36*
37*****************************************************************************/ 37*****************************************************************************/
38 38
39/* progname may change */ 39/* progname may change */
@@ -50,27 +50,20 @@ const char *email = "devel@monitoring-plugins.org";
50#if HAVE_SYS_SOCKIO_H 50#if HAVE_SYS_SOCKIO_H
51#include <sys/sockio.h> 51#include <sys/sockio.h>
52#endif 52#endif
53#include <sys/ioctl.h> 53
54#include <sys/time.h> 54#include <sys/time.h>
55#include <sys/types.h>
56#include <stdio.h>
57#include <stdlib.h>
58#include <stdarg.h>
59#include <unistd.h>
60#include <stddef.h>
61#include <errno.h> 55#include <errno.h>
62#include <string.h> 56#include <signal.h>
63#include <ctype.h> 57#include <ctype.h>
64#include <netdb.h> 58#include <float.h>
65#include <sys/socket.h>
66#include <net/if.h> 59#include <net/if.h>
67#include <netinet/in_systm.h> 60#include <netinet/in_systm.h>
68#include <netinet/in.h> 61#include <netinet/in.h>
69#include <netinet/ip.h> 62#include <netinet/ip.h>
63#include <netinet/ip6.h>
70#include <netinet/ip_icmp.h> 64#include <netinet/ip_icmp.h>
65#include <netinet/icmp6.h>
71#include <arpa/inet.h> 66#include <arpa/inet.h>
72#include <signal.h>
73#include <float.h>
74 67
75 68
76/** sometimes undefined system macros (quite a few, actually) **/ 69/** sometimes undefined system macros (quite a few, actually) **/
@@ -103,18 +96,14 @@ const char *email = "devel@monitoring-plugins.org";
103# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
104#endif 97#endif
105 98
106#ifndef DBL_MAX
107# define DBL_MAX 9.9999999999e999
108#endif
109
110typedef unsigned short range_t; /* type for get_range() -- unimplemented */ 99typedef unsigned short range_t; /* type for get_range() -- unimplemented */
111 100
112typedef struct rta_host { 101typedef struct rta_host {
113 unsigned short id; /* id in **table, and icmp pkts */ 102 unsigned short id; /* id in **table, and icmp pkts */
114 char *name; /* arg used for adding this host */ 103 char *name; /* arg used for adding this host */
115 char *msg; /* icmp error message, if any */ 104 char *msg; /* icmp error message, if any */
116 struct sockaddr_in saddr_in; /* the address of this host */ 105 struct sockaddr_storage saddr_in; /* the address of this host */
117 struct in_addr error_addr; /* stores address of error replies */ 106 struct sockaddr_storage error_addr; /* stores address of error replies */
118 unsigned long long time_waited; /* total time waited, in usecs */ 107 unsigned long long time_waited; /* total time waited, in usecs */
119 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ 108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
120 unsigned char icmp_type, icmp_code; /* type and code from errors */ 109 unsigned char icmp_type, icmp_code; /* type and code from errors */
@@ -140,6 +129,18 @@ typedef struct icmp_ping_data {
140 unsigned short ping_id; 129 unsigned short ping_id;
141} icmp_ping_data; 130} icmp_ping_data;
142 131
132typedef union ip_hdr {
133 struct ip ip;
134 struct ip6_hdr ip6;
135} ip_hdr;
136
137typedef union icmp_packet {
138 void *buf;
139 struct icmp *icp;
140 struct icmp6_hdr *icp6;
141 u_short *cksum_in;
142} icmp_packet;
143
143/* the different modes of this program are as follows: 144/* the different modes of this program are as follows:
144 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) 145 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
145 * MODE_HOSTCHECK: Return immediately upon any sign of life 146 * MODE_HOSTCHECK: Return immediately upon any sign of life
@@ -190,9 +191,10 @@ static int get_threshold(char *str, threshold *th);
190static void run_checks(void); 191static void run_checks(void);
191static void set_source_ip(char *); 192static void set_source_ip(char *);
192static int add_target(char *); 193static int add_target(char *);
193static int add_target_ip(char *, struct in_addr *); 194static int add_target_ip(char *, struct sockaddr_storage *);
194static int handle_random_icmp(unsigned char *, struct sockaddr_in *); 195static int handle_random_icmp(unsigned char *, struct sockaddr_storage *);
195static unsigned short icmp_checksum(unsigned short *, int); 196static void parse_address(struct sockaddr_storage *, char *, int);
197static unsigned short icmp_checksum(uint16_t *, size_t);
196static void finish(int); 198static void finish(int);
197static void crash(const char *, ...); 199static void crash(const char *, ...);
198 200
@@ -208,7 +210,7 @@ static int mode, protocols, sockets, debug = 0, timeout = 10;
208static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 210static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE;
209static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 211static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN;
210 212
211static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0; 213static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0;
212#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 214#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
213static unsigned short targets_down = 0, targets = 0, packets = 0; 215static unsigned short targets_down = 0, targets = 0, packets = 0;
214#define targets_alive (targets - targets_down) 216#define targets_alive (targets - targets_down)
@@ -218,7 +220,6 @@ static pid_t pid;
218static struct timezone tz; 220static struct timezone tz;
219static struct timeval prog_start; 221static struct timeval prog_start;
220static unsigned long long max_completion_time = 0; 222static unsigned long long max_completion_time = 0;
221static unsigned char ttl = 0; /* outgoing ttl */
222static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 223static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
223static int min_hosts_alive = -1; 224static int min_hosts_alive = -1;
224float pkt_backoff_factor = 1.5; 225float pkt_backoff_factor = 1.5;
@@ -273,7 +274,7 @@ get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
273 break; 274 break;
274 275
275 case ICMP_TIMXCEED: 276 case ICMP_TIMXCEED:
276 /* really 'out of reach', or non-existant host behind a router serving 277 /* really 'out of reach', or non-existent host behind a router serving
277 * two different subnets */ 278 * two different subnets */
278 switch(icmp_code) { 279 switch(icmp_code) {
279 case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break; 280 case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break;
@@ -300,7 +301,7 @@ get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
300} 301}
301 302
302static int 303static int
303handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) 304handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr)
304{ 305{
305 struct icmp p, sent_icmp; 306 struct icmp p, sent_icmp;
306 struct rta_host *host = NULL; 307 struct rta_host *host = NULL;
@@ -342,9 +343,11 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr)
342 /* it is indeed a response for us */ 343 /* it is indeed a response for us */
343 host = table[ntohs(sent_icmp.icmp_seq)/packets]; 344 host = table[ntohs(sent_icmp.icmp_seq)/packets];
344 if(debug) { 345 if(debug) {
346 char address[INET6_ADDRSTRLEN];
347 parse_address(addr, address, sizeof(address));
345 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", 348 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
346 get_icmp_error_msg(p.icmp_type, p.icmp_code), 349 get_icmp_error_msg(p.icmp_type, p.icmp_code),
347 inet_ntoa(addr->sin_addr), host->name); 350 address, host->name);
348 } 351 }
349 352
350 icmp_lost++; 353 icmp_lost++;
@@ -364,11 +367,23 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr)
364 } 367 }
365 host->icmp_type = p.icmp_type; 368 host->icmp_type = p.icmp_type;
366 host->icmp_code = p.icmp_code; 369 host->icmp_code = p.icmp_code;
367 host->error_addr.s_addr = addr->sin_addr.s_addr; 370 host->error_addr = *addr;
368 371
369 return 0; 372 return 0;
370} 373}
371 374
375void parse_address(struct sockaddr_storage *addr, char *address, int size)
376{
377 switch (address_family) {
378 case AF_INET:
379 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size);
380 break;
381 case AF_INET6:
382 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size);
383 break;
384 }
385}
386
372int 387int
373main(int argc, char **argv) 388main(int argc, char **argv)
374{ 389{
@@ -381,6 +396,8 @@ main(int argc, char **argv)
381#ifdef SO_TIMESTAMP 396#ifdef SO_TIMESTAMP
382 int on = 1; 397 int on = 1;
383#endif 398#endif
399 char *source_ip = NULL;
400 char * opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:64";
384 401
385 setlocale (LC_ALL, ""); 402 setlocale (LC_ALL, "");
386 bindtextdomain (PACKAGE, LOCALEDIR); 403 bindtextdomain (PACKAGE, LOCALEDIR);
@@ -390,33 +407,8 @@ main(int argc, char **argv)
390 * that before pointer magic (esp. on network data) */ 407 * that before pointer magic (esp. on network data) */
391 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; 408 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
392 409
393 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) 410 address_family = -1;
394 sockets |= HAVE_ICMP; 411 int icmp_proto = IPPROTO_ICMP;
395 else icmp_sockerrno = errno;
396
397 /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */
398 /* sockets |= HAVE_UDP; */
399 /* else udp_sockerrno = errno; */
400
401 /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */
402 /* sockets |= HAVE_TCP; */
403 /* else tcp_sockerrno = errno; */
404
405 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
406 setuid(getuid());
407
408#ifdef SO_TIMESTAMP
409 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
410 if(debug) printf("Warning: no SO_TIMESTAMP support\n");
411#endif // SO_TIMESTAMP
412
413 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
414 environ = NULL;
415
416 /* use the pid to mark packets as ours */
417 /* Some systems have 32-bit pid_t so mask off only 16 bits */
418 pid = getpid() & 0xffff;
419 /* printf("pid = %u\n", pid); */
420 412
421 /* get calling name the old-fashioned way for portability instead 413 /* get calling name the old-fashioned way for portability instead
422 * of relying on the glibc-ism __progname */ 414 * of relying on the glibc-ism __progname */
@@ -456,9 +448,6 @@ main(int argc, char **argv)
456 packets = 5; 448 packets = 5;
457 } 449 }
458 450
459 /* Parse extra opts if any */
460 argv=np_extra_opts(&argc, argv, progname);
461
462 /* support "--help" and "--version" */ 451 /* support "--help" and "--version" */
463 if(argc == 2) { 452 if(argc == 2) {
464 if(!strcmp(argv[1], "--help")) 453 if(!strcmp(argv[1], "--help"))
@@ -467,10 +456,35 @@ main(int argc, char **argv)
467 strcpy(argv[1], "-V"); 456 strcpy(argv[1], "-V");
468 } 457 }
469 458
459 /* Parse protocol arguments first */
460 for(i = 1; i < argc; i++) {
461 while((arg = getopt(argc, argv, opts_str)) != EOF) {
462 switch(arg) {
463 case '4':
464 if (address_family != -1)
465 crash("Multiple protocol versions not supported");
466 address_family = AF_INET;
467 break;
468 case '6':
469#ifdef USE_IPV6
470 if (address_family != -1)
471 crash("Multiple protocol versions not supported");
472 address_family = AF_INET6;
473#else
474 usage (_("IPv6 support not available\n"));
475#endif
476 break;
477 }
478 }
479 }
480
481 /* Reset argument scanning */
482 optind = 1;
483
484 unsigned short size;
470 /* parse the arguments */ 485 /* parse the arguments */
471 for(i = 1; i < argc; i++) { 486 for(i = 1; i < argc; i++) {
472 while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:s:i:b:I:l:m:")) != EOF) { 487 while((arg = getopt(argc, argv, opts_str)) != EOF) {
473 unsigned short size;
474 switch(arg) { 488 switch(arg) {
475 case 'v': 489 case 'v':
476 debug++; 490 debug++;
@@ -482,7 +496,7 @@ main(int argc, char **argv)
482 icmp_data_size = size; 496 icmp_data_size = size;
483 icmp_pkt_size = size + ICMP_MINLEN; 497 icmp_pkt_size = size + ICMP_MINLEN;
484 } else 498 } else
485 usage_va("ICMP data length must be between: %d and %d", 499 usage_va("ICMP data length must be between: %lu and %lu",
486 sizeof(struct icmp) + sizeof(struct icmp_ping_data), 500 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
487 MAX_PING_DATA - 1); 501 MAX_PING_DATA - 1);
488 break; 502 break;
@@ -510,7 +524,7 @@ main(int argc, char **argv)
510 add_target(optarg); 524 add_target(optarg);
511 break; 525 break;
512 case 'l': 526 case 'l':
513 ttl = (unsigned char)strtoul(optarg, NULL, 0); 527 ttl = (int)strtoul(optarg, NULL, 0);
514 break; 528 break;
515 case 'm': 529 case 'm':
516 min_hosts_alive = (int)strtoul(optarg, NULL, 0); 530 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
@@ -522,7 +536,7 @@ main(int argc, char **argv)
522 } 536 }
523 break; 537 break;
524 case 's': /* specify source IP address */ 538 case 's': /* specify source IP address */
525 set_source_ip(optarg); 539 source_ip = optarg;
526 break; 540 break;
527 case 'V': /* version */ 541 case 'V': /* version */
528 print_revision (progname, NP_VERSION); 542 print_revision (progname, NP_VERSION);
@@ -530,10 +544,22 @@ main(int argc, char **argv)
530 case 'h': /* help */ 544 case 'h': /* help */
531 print_help (); 545 print_help ();
532 exit (STATE_UNKNOWN); 546 exit (STATE_UNKNOWN);
547 break;
533 } 548 }
534 } 549 }
535 } 550 }
536 551
552 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
553 environ = NULL;
554
555 /* use the pid to mark packets as ours */
556 /* Some systems have 32-bit pid_t so mask off only 16 bits */
557 pid = getpid() & 0xffff;
558 /* printf("pid = %u\n", pid); */
559
560 /* Parse extra opts if any */
561 argv=np_extra_opts(&argc, argv, progname);
562
537 argv = &argv[optind]; 563 argv = &argv[optind];
538 while(*argv) { 564 while(*argv) {
539 add_target(*argv); 565 add_target(*argv);
@@ -545,6 +571,32 @@ main(int argc, char **argv)
545 exit(3); 571 exit(3);
546 } 572 }
547 573
574 // add_target might change address_family
575 switch ( address_family ){
576 case AF_INET: icmp_proto = IPPROTO_ICMP;
577 break;
578 case AF_INET6: icmp_proto = IPPROTO_ICMPV6;
579 break;
580 default: crash("Address family not supported");
581 }
582 if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
583 sockets |= HAVE_ICMP;
584 else icmp_sockerrno = errno;
585
586 if( source_ip )
587 set_source_ip(source_ip);
588
589#ifdef SO_TIMESTAMP
590 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
591 if(debug) printf("Warning: no SO_TIMESTAMP support\n");
592#endif // SO_TIMESTAMP
593
594 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
595 if (setuid(getuid()) == -1) {
596 printf("ERROR: Failed to drop privileges\n");
597 return 1;
598 }
599
548 if(!sockets) { 600 if(!sockets) {
549 if(icmp_sock == -1) { 601 if(icmp_sock == -1) {
550 errno = icmp_sockerrno; 602 errno = icmp_sockerrno;
@@ -608,7 +660,7 @@ main(int argc, char **argv)
608 if(max_completion_time > (u_int)timeout * 1000000) { 660 if(max_completion_time > (u_int)timeout * 1000000) {
609 printf("max_completion_time: %llu timeout: %u\n", 661 printf("max_completion_time: %llu timeout: %u\n",
610 max_completion_time, timeout); 662 max_completion_time, timeout);
611 printf("Timout must be at lest %llu\n", 663 printf("Timeout must be at least %llu\n",
612 max_completion_time / 1000000 + 1); 664 max_completion_time / 1000000 + 1);
613 } 665 }
614 } 666 }
@@ -633,7 +685,7 @@ main(int argc, char **argv)
633 } 685 }
634 686
635 host = list; 687 host = list;
636 table = malloc(sizeof(struct rta_host **) * targets); 688 table = (struct rta_host**)malloc(sizeof(struct rta_host **) * targets);
637 i = 0; 689 i = 0;
638 while(host) { 690 while(host) {
639 host->id = i*packets; 691 host->id = i*packets;
@@ -653,7 +705,7 @@ main(int argc, char **argv)
653static void 705static void
654run_checks() 706run_checks()
655{ 707{
656 u_int i, t, result; 708 u_int i, t;
657 u_int final_wait, time_passed; 709 u_int final_wait, time_passed;
658 710
659 /* this loop might actually violate the pkt_interval or target_interval 711 /* this loop might actually violate the pkt_interval or target_interval
@@ -671,9 +723,9 @@ run_checks()
671 723
672 /* we're still in the game, so send next packet */ 724 /* we're still in the game, so send next packet */
673 (void)send_icmp_ping(icmp_sock, table[t]); 725 (void)send_icmp_ping(icmp_sock, table[t]);
674 result = wait_for_reply(icmp_sock, target_interval); 726 wait_for_reply(icmp_sock, target_interval);
675 } 727 }
676 result = wait_for_reply(icmp_sock, pkt_interval * targets); 728 wait_for_reply(icmp_sock, pkt_interval * targets);
677 } 729 }
678 730
679 if(icmp_pkts_en_route && targets_alive) { 731 if(icmp_pkts_en_route && targets_alive) {
@@ -693,30 +745,47 @@ run_checks()
693 * haven't yet */ 745 * haven't yet */
694 if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n", 746 if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
695 final_wait, (float)final_wait / 1000); 747 final_wait, (float)final_wait / 1000);
696 result = wait_for_reply(icmp_sock, final_wait); 748 wait_for_reply(icmp_sock, final_wait);
697 } 749 }
698} 750}
699 751
752
700/* response structure: 753/* response structure:
754 * IPv4:
701 * ip header : 20 bytes 755 * ip header : 20 bytes
702 * icmp header : 28 bytes 756 * icmp header : 28 bytes
757 * IPv6:
758 * ip header : 40 bytes
759 * icmp header : 28 bytes
760 * both:
703 * icmp echo reply : the rest 761 * icmp echo reply : the rest
704 */ 762 */
705static int 763static int
706wait_for_reply(int sock, u_int t) 764wait_for_reply(int sock, u_int t)
707{ 765{
708 int n, hlen; 766 int n, hlen;
709 static unsigned char buf[4096]; 767 static unsigned char buf[65536];
710 struct sockaddr_in resp_addr; 768 struct sockaddr_storage resp_addr;
711 struct ip *ip; 769 union ip_hdr *ip;
712 struct icmp icp; 770 union icmp_packet packet;
713 struct rta_host *host; 771 struct rta_host *host;
714 struct icmp_ping_data data; 772 struct icmp_ping_data data;
715 struct timeval wait_start, now; 773 struct timeval wait_start, now;
716 u_int tdiff, i, per_pkt_wait; 774 u_int tdiff, i, per_pkt_wait;
717 775
776 if (!(packet.buf = malloc(icmp_pkt_size))) {
777 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
778 icmp_pkt_size);
779 return -1; /* might be reached if we're in debug mode */
780 }
781
782 memset(packet.buf, 0, icmp_pkt_size);
783
718 /* if we can't listen or don't have anything to listen to, just return */ 784 /* if we can't listen or don't have anything to listen to, just return */
719 if(!t || !icmp_pkts_en_route) return 0; 785 if(!t || !icmp_pkts_en_route) {
786 free(packet.buf);
787 return 0;
788 }
720 789
721 gettimeofday(&wait_start, &tz); 790 gettimeofday(&wait_start, &tz);
722 791
@@ -735,7 +804,7 @@ wait_for_reply(int sock, u_int t)
735 804
736 /* reap responses until we hit a timeout */ 805 /* reap responses until we hit a timeout */
737 n = recvfrom_wto(sock, buf, sizeof(buf), 806 n = recvfrom_wto(sock, buf, sizeof(buf),
738 (struct sockaddr *)&resp_addr, &t, &now); 807 (struct sockaddr *)&resp_addr, &t, &now);
739 if(!n) { 808 if(!n) {
740 if(debug > 1) { 809 if(debug > 1) {
741 printf("recvfrom_wto() timed out during a %u usecs wait\n", 810 printf("recvfrom_wto() timed out during a %u usecs wait\n",
@@ -745,12 +814,23 @@ wait_for_reply(int sock, u_int t)
745 } 814 }
746 if(n < 0) { 815 if(n < 0) {
747 if(debug) printf("recvfrom_wto() returned errors\n"); 816 if(debug) printf("recvfrom_wto() returned errors\n");
817 free(packet.buf);
748 return n; 818 return n;
749 } 819 }
750 820
751 ip = (struct ip *)buf; 821 // FIXME: with ipv6 we don't have an ip header here
752 if(debug > 1) printf("received %u bytes from %s\n", 822 if (address_family != AF_INET6) {
753 ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr)); 823 ip = (union ip_hdr *)buf;
824
825 if(debug > 1) {
826 char address[INET6_ADDRSTRLEN];
827 parse_address(&resp_addr, address, sizeof(address));
828 printf("received %u bytes from %s\n",
829 address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen)
830 : ntohs(ip->ip.ip_len),
831 address);
832 }
833 }
754 834
755/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 835/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
756/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 836/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
@@ -759,12 +839,14 @@ wait_for_reply(int sock, u_int t)
759 * off the bottom 4 bits */ 839 * off the bottom 4 bits */
760/* hlen = (ip->ip_vhl & 0x0f) << 2; */ 840/* hlen = (ip->ip_vhl & 0x0f) << 2; */
761/* #else */ 841/* #else */
762 hlen = ip->ip_hl << 2; 842 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
763/* #endif */ 843/* #endif */
764 844
765 if(n < (hlen + ICMP_MINLEN)) { 845 if(n < (hlen + ICMP_MINLEN)) {
846 char address[INET6_ADDRSTRLEN];
847 parse_address(&resp_addr, address, sizeof(address));
766 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", 848 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
767 n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr)); 849 n, hlen + icmp_pkt_size, address);
768 } 850 }
769 /* else if(debug) { */ 851 /* else if(debug) { */
770 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ 852 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
@@ -773,23 +855,39 @@ wait_for_reply(int sock, u_int t)
773 /* } */ 855 /* } */
774 856
775 /* check the response */ 857 /* check the response */
776 memcpy(&icp, buf + hlen, sizeof(icp));
777 858
778 if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || 859 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
779 ntohs(icp.icmp_seq) >= targets*packets) { 860/* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
861 : sizeof(struct icmp));*/
862
863 if( (address_family == PF_INET &&
864 (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY
865 || ntohs(packet.icp->icmp_seq) >= targets * packets))
866 || (address_family == PF_INET6 &&
867 (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY
868 || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
780 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); 869 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
781 handle_random_icmp(buf + hlen, &resp_addr); 870 handle_random_icmp(buf + hlen, &resp_addr);
782 continue; 871 continue;
783 } 872 }
784 873
785 /* this is indeed a valid response */ 874 /* this is indeed a valid response */
786 memcpy(&data, icp.icmp_data, sizeof(data)); 875 if (address_family == PF_INET) {
787 if (debug > 2) 876 memcpy(&data, packet.icp->icmp_data, sizeof(data));
788 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", 877 if (debug > 2)
789 (unsigned long)sizeof(data), ntohs(icp.icmp_id), 878 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
790 ntohs(icp.icmp_seq), icp.icmp_cksum); 879 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id),
880 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum);
881 host = table[ntohs(packet.icp->icmp_seq)/packets];
882 } else {
883 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
884 if (debug > 2)
885 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
886 (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id),
887 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum);
888 host = table[ntohs(packet.icp6->icmp6_seq)/packets];
889 }
791 890
792 host = table[ntohs(icp.icmp_seq)/packets];
793 tdiff = get_timevaldiff(&data.stime, &now); 891 tdiff = get_timevaldiff(&data.stime, &now);
794 892
795 host->time_waited += tdiff; 893 host->time_waited += tdiff;
@@ -801,22 +899,43 @@ wait_for_reply(int sock, u_int t)
801 host->rtmin = tdiff; 899 host->rtmin = tdiff;
802 900
803 if(debug) { 901 if(debug) {
804 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", 902 char address[INET6_ADDRSTRLEN];
805 (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr), 903 parse_address(&resp_addr, address, sizeof(address));
806 ttl, ip->ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 904
905 switch(address_family) {
906 case AF_INET: {
907 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
908 (float)tdiff / 1000,
909 address,
910 ttl,
911 ip->ip.ip_ttl,
912 (float)host->rtmax / 1000,
913 (float)host->rtmin / 1000);
914 break;
915 };
916 case AF_INET6: {
917 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n",
918 (float)tdiff / 1000,
919 address,
920 ttl,
921 (float)host->rtmax / 1000,
922 (float)host->rtmin / 1000);
923 };
924 }
807 } 925 }
808 926
809 /* if we're in hostcheck mode, exit with limited printouts */ 927 /* if we're in hostcheck mode, exit with limited printouts */
810 if(mode == MODE_HOSTCHECK) { 928 if(mode == MODE_HOSTCHECK) {
811 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" 929 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
812 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", 930 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
813 host->name, icmp_recv, (float)tdiff / 1000, 931 host->name, icmp_recv, (float)tdiff / 1000,
814 icmp_recv, packets, (float)tdiff / 1000, 932 icmp_recv, packets, (float)tdiff / 1000,
815 (float)warn.rta / 1000, (float)crit.rta / 1000); 933 (float)warn.rta / 1000, (float)crit.rta / 1000);
816 exit(STATE_OK); 934 exit(STATE_OK);
817 } 935 }
818 } 936 }
819 937
938 free(packet.buf);
820 return 0; 939 return 0;
821} 940}
822 941
@@ -824,62 +943,86 @@ wait_for_reply(int sock, u_int t)
824static int 943static int
825send_icmp_ping(int sock, struct rta_host *host) 944send_icmp_ping(int sock, struct rta_host *host)
826{ 945{
827 static union {
828 void *buf; /* re-use so we prevent leaks */
829 struct icmp *icp;
830 u_short *cksum_in;
831 } packet = { NULL };
832 long int len; 946 long int len;
947 size_t addrlen;
833 struct icmp_ping_data data; 948 struct icmp_ping_data data;
834 struct msghdr hdr; 949 struct msghdr hdr;
835 struct iovec iov; 950 struct iovec iov;
836 struct timeval tv; 951 struct timeval tv;
837 struct sockaddr *addr; 952 void *buf = NULL;
838 953
839 if(sock == -1) { 954 if(sock == -1) {
840 errno = 0; 955 errno = 0;
841 crash("Attempt to send on bogus socket"); 956 crash("Attempt to send on bogus socket");
842 return -1; 957 return -1;
843 } 958 }
844 addr = (struct sockaddr *)&host->saddr_in;
845 959
846 if(!packet.buf) { 960 if(!buf) {
847 if (!(packet.buf = malloc(icmp_pkt_size))) { 961 if (!(buf = malloc(icmp_pkt_size))) {
848 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", 962 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
849 icmp_pkt_size); 963 icmp_pkt_size);
850 return -1; /* might be reached if we're in debug mode */ 964 return -1; /* might be reached if we're in debug mode */
851 } 965 }
852 } 966 }
853 memset(packet.buf, 0, icmp_pkt_size); 967 memset(buf, 0, icmp_pkt_size);
854 968
855 if((gettimeofday(&tv, &tz)) == -1) return -1; 969 if((gettimeofday(&tv, &tz)) == -1) {
970 free(buf);
971 return -1;
972 }
856 973
857 data.ping_id = 10; /* host->icmp.icmp_sent; */ 974 data.ping_id = 10; /* host->icmp.icmp_sent; */
858 memcpy(&data.stime, &tv, sizeof(tv)); 975 memcpy(&data.stime, &tv, sizeof(tv));
859 memcpy(&packet.icp->icmp_data, &data, sizeof(data)); 976
860 packet.icp->icmp_type = ICMP_ECHO; 977 if (address_family == AF_INET) {
861 packet.icp->icmp_code = 0; 978 struct icmp *icp = (struct icmp*)buf;
862 packet.icp->icmp_cksum = 0; 979 addrlen = sizeof(struct sockaddr_in);
863 packet.icp->icmp_id = htons(pid); 980
864 packet.icp->icmp_seq = htons(host->id++); 981 memcpy(&icp->icmp_data, &data, sizeof(data));
865 packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size); 982
866 983 icp->icmp_type = ICMP_ECHO;
867 if (debug > 2) 984 icp->icmp_code = 0;
868 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", 985 icp->icmp_cksum = 0;
869 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 986 icp->icmp_id = htons(pid);
870 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, 987 icp->icmp_seq = htons(host->id++);
871 host->name); 988 icp->icmp_cksum = icmp_checksum((uint16_t*)buf, (size_t)icmp_pkt_size);
989
990 if (debug > 2)
991 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
992 (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name);
993 }
994 else {
995 struct icmp6_hdr *icp6 = (struct icmp6_hdr*)buf;
996 addrlen = sizeof(struct sockaddr_in6);
997
998 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
999
1000 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1001 icp6->icmp6_code = 0;
1002 icp6->icmp6_cksum = 0;
1003 icp6->icmp6_id = htons(pid);
1004 icp6->icmp6_seq = htons(host->id++);
1005 // let checksum be calculated automatically
1006
1007 if (debug > 2) {
1008 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1009 (unsigned long)sizeof(data), ntohs(icp6->icmp6_id),
1010 ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name);
1011 }
1012 }
872 1013
873 memset(&iov, 0, sizeof(iov)); 1014 memset(&iov, 0, sizeof(iov));
874 iov.iov_base = packet.buf; 1015 iov.iov_base = buf;
875 iov.iov_len = icmp_pkt_size; 1016 iov.iov_len = icmp_pkt_size;
876 1017
877 memset(&hdr, 0, sizeof(hdr)); 1018 memset(&hdr, 0, sizeof(hdr));
878 hdr.msg_name = addr; 1019 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
879 hdr.msg_namelen = sizeof(struct sockaddr); 1020 hdr.msg_namelen = addrlen;
880 hdr.msg_iov = &iov; 1021 hdr.msg_iov = &iov;
881 hdr.msg_iovlen = 1; 1022 hdr.msg_iovlen = 1;
882 1023
1024 errno = 0;
1025
883/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1026/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
884#ifdef MSG_CONFIRM 1027#ifdef MSG_CONFIRM
885 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1028 len = sendmsg(sock, &hdr, MSG_CONFIRM);
@@ -887,9 +1030,15 @@ send_icmp_ping(int sock, struct rta_host *host)
887 len = sendmsg(sock, &hdr, 0); 1030 len = sendmsg(sock, &hdr, 0);
888#endif 1031#endif
889 1032
1033 free(buf);
1034
890 if(len < 0 || (unsigned int)len != icmp_pkt_size) { 1035 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
891 if(debug) printf("Failed to send ping to %s\n", 1036 if(debug) {
892 inet_ntoa(host->saddr_in.sin_addr)); 1037 char address[INET6_ADDRSTRLEN];
1038 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1039 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1040 }
1041 errno = 0;
893 return -1; 1042 return -1;
894 } 1043 }
895 1044
@@ -934,7 +1083,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
934 1083
935 if(!n) return 0; /* timeout */ 1084 if(!n) return 0; /* timeout */
936 1085
937 slen = sizeof(struct sockaddr); 1086 slen = sizeof(struct sockaddr_storage);
938 1087
939 memset(&iov, 0, sizeof(iov)); 1088 memset(&iov, 0, sizeof(iov));
940 iov.iov_base = buf; 1089 iov.iov_base = buf;
@@ -958,6 +1107,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
958 break ; 1107 break ;
959 } 1108 }
960 } 1109 }
1110
961 if (!chdr) 1111 if (!chdr)
962#endif // SO_TIMESTAMP 1112#endif // SO_TIMESTAMP
963 gettimeofday(tv, &tz); 1113 gettimeofday(tv, &tz);
@@ -991,10 +1141,11 @@ finish(int sig)
991 1141
992 /* iterate thrice to calculate values, give output, and print perfparse */ 1142 /* iterate thrice to calculate values, give output, and print perfparse */
993 host = list; 1143 host = list;
1144
994 while(host) { 1145 while(host) {
995 if(!host->icmp_recv) { 1146 if(!host->icmp_recv) {
996 /* rta 0 is ofcourse not entirely correct, but will still show up 1147 /* rta 0 is ofcourse not entirely correct, but will still show up
997 * conspicuosly as missing entries in perfparse and cacti */ 1148 * conspicuously as missing entries in perfparse and cacti */
998 pl = 100; 1149 pl = 100;
999 rta = 0; 1150 rta = 0;
1000 status = STATE_CRITICAL; 1151 status = STATE_CRITICAL;
@@ -1039,10 +1190,12 @@ finish(int sig)
1039 if(!host->icmp_recv) { 1190 if(!host->icmp_recv) {
1040 status = STATE_CRITICAL; 1191 status = STATE_CRITICAL;
1041 if(host->flags & FLAG_LOST_CAUSE) { 1192 if(host->flags & FLAG_LOST_CAUSE) {
1193 char address[INET6_ADDRSTRLEN];
1194 parse_address(&host->error_addr, address, sizeof(address));
1042 printf("%s: %s @ %s. rta nan, lost %d%%", 1195 printf("%s: %s @ %s. rta nan, lost %d%%",
1043 host->name, 1196 host->name,
1044 get_icmp_error_msg(host->icmp_type, host->icmp_code), 1197 get_icmp_error_msg(host->icmp_type, host->icmp_code),
1045 inet_ntoa(host->error_addr), 1198 address,
1046 100); 1199 100);
1047 } 1200 }
1048 else { /* not marked as lost cause, so we have no flags for it */ 1201 else { /* not marked as lost cause, so we have no flags for it */
@@ -1068,7 +1221,7 @@ finish(int sig)
1068 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, 1221 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000,
1069 (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl, 1222 (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl,
1070 (targets > 1) ? host->name : "", (float)host->rtmax / 1000, 1223 (targets > 1) ? host->name : "", (float)host->rtmax / 1000,
1071 (targets > 1) ? host->name : "", (host->rtmin < DBL_MAX) ? (float)host->rtmin / 1000 : (float)0); 1224 (targets > 1) ? host->name : "", (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1072 1225
1073 host = host->next; 1226 host = host->next;
1074 } 1227 }
@@ -1104,7 +1257,6 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1104 { 1257 {
1105 return 0; 1258 return 0;
1106 } 1259 }
1107
1108 ret = (later->tv_sec - early->tv_sec) * 1000000; 1260 ret = (later->tv_sec - early->tv_sec) * 1000000;
1109 ret += later->tv_usec - early->tv_usec; 1261 ret += later->tv_usec - early->tv_usec;
1110 1262
@@ -1112,18 +1264,35 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1112} 1264}
1113 1265
1114static int 1266static int
1115add_target_ip(char *arg, struct in_addr *in) 1267add_target_ip(char *arg, struct sockaddr_storage *in)
1116{ 1268{
1117 struct rta_host *host; 1269 struct rta_host *host;
1270 struct sockaddr_in *sin, *host_sin;
1271 struct sockaddr_in6 *sin6, *host_sin6;
1118 1272
1119 /* disregard obviously stupid addresses */ 1273 if (address_family == AF_INET)
1120 if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY) 1274 sin = (struct sockaddr_in *)in;
1275 else
1276 sin6 = (struct sockaddr_in6 *)in;
1277
1278
1279
1280 /* disregard obviously stupid addresses
1281 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1282 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE
1283 || sin->sin_addr.s_addr == INADDR_ANY)))
1284 || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1121 return -1; 1285 return -1;
1286 }
1122 1287
1123 /* no point in adding two identical IP's, so don't. ;) */ 1288 /* no point in adding two identical IP's, so don't. ;) */
1124 host = list; 1289 host = list;
1125 while(host) { 1290 while(host) {
1126 if(host->saddr_in.sin_addr.s_addr == in->s_addr) { 1291 host_sin = (struct sockaddr_in *)&host->saddr_in;
1292 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1293
1294 if( (address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr)
1295 || (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1127 if(debug) printf("Identical IP already exists. Not adding %s\n", arg); 1296 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
1128 return -1; 1297 return -1;
1129 } 1298 }
@@ -1131,21 +1300,31 @@ add_target_ip(char *arg, struct in_addr *in)
1131 } 1300 }
1132 1301
1133 /* add the fresh ip */ 1302 /* add the fresh ip */
1134 host = malloc(sizeof(struct rta_host)); 1303 host = (struct rta_host*)malloc(sizeof(struct rta_host));
1135 if(!host) { 1304 if(!host) {
1136 crash("add_target_ip(%s, %s): malloc(%d) failed", 1305 char straddr[INET6_ADDRSTRLEN];
1137 arg, inet_ntoa(*in), sizeof(struct rta_host)); 1306 parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
1307 crash("add_target_ip(%s, %s): malloc(%lu) failed",
1308 arg, straddr, sizeof(struct rta_host));
1138 } 1309 }
1139 memset(host, 0, sizeof(struct rta_host)); 1310 memset(host, 0, sizeof(struct rta_host));
1140 1311
1141 /* set the values. use calling name for output */ 1312 /* set the values. use calling name for output */
1142 host->name = strdup(arg); 1313 host->name = strdup(arg);
1143 1314
1144 /* fill out the sockaddr_in struct */ 1315 /* fill out the sockaddr_storage struct */
1145 host->saddr_in.sin_family = AF_INET; 1316 if(address_family == AF_INET) {
1146 host->saddr_in.sin_addr.s_addr = in->s_addr; 1317 host_sin = (struct sockaddr_in *)&host->saddr_in;
1318 host_sin->sin_family = AF_INET;
1319 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1320 }
1321 else {
1322 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1323 host_sin6->sin6_family = AF_INET6;
1324 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1325 }
1147 1326
1148 host->rtmin = DBL_MAX; 1327 host->rtmin = INFINITY;
1149 1328
1150 if(!list) list = cursor = host; 1329 if(!list) list = cursor = host;
1151 else cursor->next = host; 1330 else cursor->next = host;
@@ -1160,31 +1339,67 @@ add_target_ip(char *arg, struct in_addr *in)
1160static int 1339static int
1161add_target(char *arg) 1340add_target(char *arg)
1162{ 1341{
1163 int i; 1342 int error, result = -1;
1164 struct hostent *he; 1343 struct sockaddr_storage ip;
1165 struct in_addr *in, ip; 1344 struct addrinfo hints, *res, *p;
1345 struct sockaddr_in *sin;
1346 struct sockaddr_in6 *sin6;
1347
1348 switch (address_family) {
1349 case -1:
1350 /* -4 and -6 are not specified on cmdline */
1351 address_family = AF_INET;
1352 sin = (struct sockaddr_in *)&ip;
1353 result = inet_pton(address_family, arg, &sin->sin_addr);
1354#ifdef USE_IPV6
1355 if( result != 1 ){
1356 address_family = AF_INET6;
1357 sin6 = (struct sockaddr_in6 *)&ip;
1358 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1359 }
1360#endif
1361 /* If we don't find any valid addresses, we still don't know the address_family */
1362 if ( result != 1) {
1363 address_family = -1;
1364 }
1365 break;
1366 case AF_INET:
1367 sin = (struct sockaddr_in *)&ip;
1368 result = inet_pton(address_family, arg, &sin->sin_addr);
1369 break;
1370 case AF_INET6:
1371 sin6 = (struct sockaddr_in6 *)&ip;
1372 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1373 break;
1374 default: crash("Address family not supported");
1375 }
1166 1376
1167 /* don't resolve if we don't have to */ 1377 /* don't resolve if we don't have to */
1168 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) { 1378 if(result == 1) {
1169 /* don't add all ip's if we were given a specific one */ 1379 /* don't add all ip's if we were given a specific one */
1170 return add_target_ip(arg, &ip); 1380 return add_target_ip(arg, &ip);
1171 /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */
1172 /* if(!he) return add_target_ip(arg, in); */
1173 } 1381 }
1174 else { 1382 else {
1175 errno = 0; 1383 errno = 0;
1176 he = gethostbyname(arg); 1384 memset(&hints, 0, sizeof(hints));
1177 if(!he) { 1385 if (address_family == -1) {
1386 hints.ai_family = AF_UNSPEC;
1387 } else {
1388 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1389 }
1390 hints.ai_socktype = SOCK_RAW;
1391 if((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1178 errno = 0; 1392 errno = 0;
1179 crash("Failed to resolve %s", arg); 1393 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1180 return -1; 1394 return -1;
1181 } 1395 }
1396 address_family = res->ai_family;
1182 } 1397 }
1183 1398
1184 /* possibly add all the IP's as targets */ 1399 /* possibly add all the IP's as targets */
1185 for(i = 0; he->h_addr_list[i]; i++) { 1400 for(p = res; p != NULL; p = p->ai_next) {
1186 in = (struct in_addr *)he->h_addr_list[i]; 1401 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1187 add_target_ip(arg, in); 1402 add_target_ip(arg, &ip);
1188 1403
1189 /* this is silly, but it works */ 1404 /* this is silly, but it works */
1190 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1405 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1193,6 +1408,7 @@ add_target(char *arg)
1193 } 1408 }
1194 break; 1409 break;
1195 } 1410 }
1411 freeaddrinfo(res);
1196 1412
1197 return 0; 1413 return 0;
1198} 1414}
@@ -1203,7 +1419,7 @@ set_source_ip(char *arg)
1203 struct sockaddr_in src; 1419 struct sockaddr_in src;
1204 1420
1205 memset(&src, 0, sizeof(src)); 1421 memset(&src, 0, sizeof(src));
1206 src.sin_family = AF_INET; 1422 src.sin_family = address_family;
1207 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) 1423 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1208 src.sin_addr.s_addr = get_ip_address(arg); 1424 src.sin_addr.s_addr = get_ip_address(arg);
1209 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) 1425 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
@@ -1214,20 +1430,26 @@ set_source_ip(char *arg)
1214static in_addr_t 1430static in_addr_t
1215get_ip_address(const char *ifname) 1431get_ip_address(const char *ifname)
1216{ 1432{
1433 // TODO: Rewrite this so the function return an error and we exit somewhere else
1434 struct sockaddr_in ip;
1435 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1217#if defined(SIOCGIFADDR) 1436#if defined(SIOCGIFADDR)
1218 struct ifreq ifr; 1437 struct ifreq ifr;
1219 struct sockaddr_in ip;
1220 1438
1221 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); 1439 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
1440
1222 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; 1441 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
1442
1223 if(ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1) 1443 if(ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1)
1224 crash("Cannot determine IP address of interface %s", ifname); 1444 crash("Cannot determine IP address of interface %s", ifname);
1445
1225 memcpy(&ip, &ifr.ifr_addr, sizeof(ip)); 1446 memcpy(&ip, &ifr.ifr_addr, sizeof(ip));
1226 return ip.sin_addr.s_addr;
1227#else 1447#else
1448 (void) ifname;
1228 errno = 0; 1449 errno = 0;
1229 crash("Cannot get interface IP address on this platform."); 1450 crash("Cannot get interface IP address on this platform.");
1230#endif 1451#endif
1452 return ip.sin_addr.s_addr;
1231} 1453}
1232 1454
1233/* 1455/*
@@ -1309,18 +1531,19 @@ get_threshold(char *str, threshold *th)
1309} 1531}
1310 1532
1311unsigned short 1533unsigned short
1312icmp_checksum(unsigned short *p, int n) 1534icmp_checksum(uint16_t *p, size_t n)
1313{ 1535{
1314 register unsigned short cksum; 1536 unsigned short cksum;
1315 register long sum = 0; 1537 long sum = 0;
1316 1538
1317 while(n > 1) { 1539 /* sizeof(uint16_t) == 2 */
1318 sum += *p++; 1540 while(n >= 2) {
1541 sum += *(p++);
1319 n -= 2; 1542 n -= 2;
1320 } 1543 }
1321 1544
1322 /* mop up the occasional odd byte */ 1545 /* mop up the occasional odd byte */
1323 if(n == 1) sum += (unsigned char)*p; 1546 if(n == 1) sum += *((uint8_t *)p -1);
1324 1547
1325 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 1548 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1326 sum += (sum >> 16); /* add carry */ 1549 sum += (sum >> 16); /* add carry */
@@ -1347,6 +1570,8 @@ print_help(void)
1347 1570
1348 printf (" %s\n", "-H"); 1571 printf (" %s\n", "-H");
1349 printf (" %s\n", _("specify a target")); 1572 printf (" %s\n", _("specify a target"));
1573 printf (" %s\n", "[-4|-6]");
1574 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
1350 printf (" %s\n", "-w"); 1575 printf (" %s\n", "-w");
1351 printf (" %s", _("warning threshold (currently ")); 1576 printf (" %s", _("warning threshold (currently "));
1352 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 1577 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);