summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars Michelsen <lm@mathias-kettner.de>2015-10-13 08:26:01 (GMT)
committerJacob Hansen <jhansen@op5.com>2018-12-07 09:51:21 (GMT)
commitfd6dc666538dd95f11811817ceb21604676e142c (patch)
treec113cedbe5ee016650cb321e33d39b9c3cea65b9
parentc7c4be2ad1fa97d5b8db0cff5f7204c605992047 (diff)
downloadmonitoring-plugins-fd6dc66.tar.gz
check_icmp: Add IPv6 support
This commit adds IPv6 capabilities to check_icmp. It is now possible to specify the address family using the arguments -4 (default) or -6. To make the change possible we had to move the argument parsing previous to creating the socket to be able to create it with the correct address family. This commit also fixes some gcc 4.9.2 compiler warnings. It has been tested with several current linux distributions (debian, ubuntu, rh, sles). This commit fixes monitoring-plugins/monitoring-plugins#1291
-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 4098874..6aeedf4 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -67,7 +67,9 @@ const char *email = "devel@monitoring-plugins.org";
67#include <netinet/in_systm.h> 67#include <netinet/in_systm.h>
68#include <netinet/in.h> 68#include <netinet/in.h>
69#include <netinet/ip.h> 69#include <netinet/ip.h>
70#include <netinet/ip6.h>
70#include <netinet/ip_icmp.h> 71#include <netinet/ip_icmp.h>
72#include <netinet/icmp6.h>
71#include <arpa/inet.h> 73#include <arpa/inet.h>
72#include <signal.h> 74#include <signal.h>
73#include <float.h> 75#include <float.h>
@@ -113,8 +115,8 @@ typedef struct rta_host {
113 unsigned short id; /* id in **table, and icmp pkts */ 115 unsigned short id; /* id in **table, and icmp pkts */
114 char *name; /* arg used for adding this host */ 116 char *name; /* arg used for adding this host */
115 char *msg; /* icmp error message, if any */ 117 char *msg; /* icmp error message, if any */
116 struct sockaddr_in saddr_in; /* the address of this host */ 118 struct sockaddr_storage saddr_in; /* the address of this host */
117 struct in_addr error_addr; /* stores address of error replies */ 119 struct sockaddr_storage error_addr; /* stores address of error replies */
118 unsigned long long time_waited; /* total time waited, in usecs */ 120 unsigned long long time_waited; /* total time waited, in usecs */
119 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */ 121 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
120 unsigned char icmp_type, icmp_code; /* type and code from errors */ 122 unsigned char icmp_type, icmp_code; /* type and code from errors */
@@ -140,6 +142,18 @@ typedef struct icmp_ping_data {
140 unsigned short ping_id; 142 unsigned short ping_id;
141} icmp_ping_data; 143} icmp_ping_data;
142 144
145typedef union ip_hdr {
146 struct ip ip;
147 struct ip6_hdr ip6;
148} ip_hdr;
149
150typedef union icmp_packet {
151 void *buf;
152 struct icmp *icp;
153 struct icmp6_hdr *icp6;
154 u_short *cksum_in;
155} icmp_packet;
156
143/* the different modes of this program are as follows: 157/* the different modes of this program are as follows:
144 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping) 158 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
145 * MODE_HOSTCHECK: Return immediately upon any sign of life 159 * MODE_HOSTCHECK: Return immediately upon any sign of life
@@ -190,8 +204,9 @@ static int get_threshold(char *str, threshold *th);
190static void run_checks(void); 204static void run_checks(void);
191static void set_source_ip(char *); 205static void set_source_ip(char *);
192static int add_target(char *); 206static int add_target(char *);
193static int add_target_ip(char *, struct in_addr *); 207static int add_target_ip(char *, struct sockaddr_storage *);
194static int handle_random_icmp(unsigned char *, struct sockaddr_in *); 208static int handle_random_icmp(unsigned char *, struct sockaddr_storage *);
209static void parse_address(struct sockaddr_storage *, char *, int);
195static unsigned short icmp_checksum(unsigned short *, int); 210static unsigned short icmp_checksum(unsigned short *, int);
196static void finish(int); 211static void finish(int);
197static void crash(const char *, ...); 212static void crash(const char *, ...);
@@ -300,7 +315,7 @@ get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
300} 315}
301 316
302static int 317static int
303handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) 318handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr)
304{ 319{
305 struct icmp p, sent_icmp; 320 struct icmp p, sent_icmp;
306 struct rta_host *host = NULL; 321 struct rta_host *host = NULL;
@@ -342,9 +357,11 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr)
342 /* it is indeed a response for us */ 357 /* it is indeed a response for us */
343 host = table[ntohs(sent_icmp.icmp_seq)/packets]; 358 host = table[ntohs(sent_icmp.icmp_seq)/packets];
344 if(debug) { 359 if(debug) {
360 char address[INET6_ADDRSTRLEN];
361 parse_address(addr, address, sizeof(address));
345 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", 362 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
346 get_icmp_error_msg(p.icmp_type, p.icmp_code), 363 get_icmp_error_msg(p.icmp_type, p.icmp_code),
347 inet_ntoa(addr->sin_addr), host->name); 364 address, host->name);
348 } 365 }
349 366
350 icmp_lost++; 367 icmp_lost++;
@@ -364,11 +381,23 @@ handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr)
364 } 381 }
365 host->icmp_type = p.icmp_type; 382 host->icmp_type = p.icmp_type;
366 host->icmp_code = p.icmp_code; 383 host->icmp_code = p.icmp_code;
367 host->error_addr.s_addr = addr->sin_addr.s_addr; 384 host->error_addr = *addr;
368 385
369 return 0; 386 return 0;
370} 387}
371 388
389void parse_address(struct sockaddr_storage *addr, char *address, int size)
390{
391 switch (address_family) {
392 case AF_INET:
393 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size);
394 break;
395 case AF_INET6:
396 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size);
397 break;
398 }
399}
400
372int 401int
373main(int argc, char **argv) 402main(int argc, char **argv)
374{ 403{
@@ -390,7 +419,90 @@ main(int argc, char **argv)
390 * that before pointer magic (esp. on network data) */ 419 * that before pointer magic (esp. on network data) */
391 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; 420 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
392 421
393 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) 422 address_family = AF_INET;
423 int icmp_proto = IPPROTO_ICMP;
424
425 /* parse the arguments */
426 for(i = 1; i < argc; i++) {
427 while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:s:i:b:I:l:m:64")) != EOF) {
428 unsigned short size;
429 switch(arg) {
430 case 'v':
431 debug++;
432 break;
433 case 'b':
434 size = (unsigned short)strtol(optarg,NULL,0);
435 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
436 size < MAX_PING_DATA) {
437 icmp_data_size = size;
438 icmp_pkt_size = size + ICMP_MINLEN;
439 } else
440 usage_va("ICMP data length must be between: %d and %d",
441 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
442 MAX_PING_DATA - 1);
443 break;
444 case 'i':
445 pkt_interval = get_timevar(optarg);
446 break;
447 case 'I':
448 target_interval = get_timevar(optarg);
449 break;
450 case 'w':
451 get_threshold(optarg, &warn);
452 break;
453 case 'c':
454 get_threshold(optarg, &crit);
455 break;
456 case 'n':
457 case 'p':
458 packets = strtoul(optarg, NULL, 0);
459 break;
460 case 't':
461 timeout = strtoul(optarg, NULL, 0);
462 if(!timeout) timeout = 10;
463 break;
464 case 'H':
465 add_target(optarg);
466 break;
467 case 'l':
468 ttl = (unsigned char)strtoul(optarg, NULL, 0);
469 break;
470 case 'm':
471 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
472 break;
473 case 'd': /* implement later, for cluster checks */
474 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
475 if(ptr) {
476 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
477 }
478 break;
479 case 's': /* specify source IP address */
480 set_source_ip(optarg);
481 break;
482 case 'V': /* version */
483 print_revision (progname, NP_VERSION);
484 exit (STATE_UNKNOWN);
485 case 'h': /* help */
486 print_help ();
487 exit (STATE_UNKNOWN);
488 break;
489 case '4':
490 address_family = AF_INET;
491 icmp_proto = IPPROTO_ICMP;
492 break;
493 case '6':
494#ifdef USE_IPV6
495 address_family = AF_INET6;
496 icmp_proto = IPPROTO_ICMPV6;
497#else
498 usage (_("IPv6 support not available\n"));
499#endif
500 break;
501 }
502 }
503 }
504
505 if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
394 sockets |= HAVE_ICMP; 506 sockets |= HAVE_ICMP;
395 else icmp_sockerrno = errno; 507 else icmp_sockerrno = errno;
396 508
@@ -403,7 +515,10 @@ main(int argc, char **argv)
403 /* else tcp_sockerrno = errno; */ 515 /* else tcp_sockerrno = errno; */
404 516
405 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 517 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
406 setuid(getuid()); 518 if (setuid(getuid()) == -1) {
519 printf("ERROR: Failed to drop privileges\n");
520 return 1;
521 }
407 522
408#ifdef SO_TIMESTAMP 523#ifdef SO_TIMESTAMP
409 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) 524 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
@@ -467,73 +582,6 @@ main(int argc, char **argv)
467 strcpy(argv[1], "-V"); 582 strcpy(argv[1], "-V");
468 } 583 }
469 584
470 /* parse the arguments */
471 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) {
473 unsigned short size;
474 switch(arg) {
475 case 'v':
476 debug++;
477 break;
478 case 'b':
479 size = (unsigned short)strtol(optarg,NULL,0);
480 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
481 size < MAX_PING_DATA) {
482 icmp_data_size = size;
483 icmp_pkt_size = size + ICMP_MINLEN;
484 } else
485 usage_va("ICMP data length must be between: %d and %d",
486 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
487 MAX_PING_DATA - 1);
488 break;
489 case 'i':
490 pkt_interval = get_timevar(optarg);
491 break;
492 case 'I':
493 target_interval = get_timevar(optarg);
494 break;
495 case 'w':
496 get_threshold(optarg, &warn);
497 break;
498 case 'c':
499 get_threshold(optarg, &crit);
500 break;
501 case 'n':
502 case 'p':
503 packets = strtoul(optarg, NULL, 0);
504 break;
505 case 't':
506 timeout = strtoul(optarg, NULL, 0);
507 if(!timeout) timeout = 10;
508 break;
509 case 'H':
510 add_target(optarg);
511 break;
512 case 'l':
513 ttl = (unsigned char)strtoul(optarg, NULL, 0);
514 break;
515 case 'm':
516 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
517 break;
518 case 'd': /* implement later, for cluster checks */
519 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
520 if(ptr) {
521 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
522 }
523 break;
524 case 's': /* specify source IP address */
525 set_source_ip(optarg);
526 break;
527 case 'V': /* version */
528 print_revision (progname, NP_VERSION);
529 exit (STATE_UNKNOWN);
530 case 'h': /* help */
531 print_help ();
532 exit (STATE_UNKNOWN);
533 }
534 }
535 }
536
537 argv = &argv[optind]; 585 argv = &argv[optind];
538 while(*argv) { 586 while(*argv) {
539 add_target(*argv); 587 add_target(*argv);
@@ -633,7 +681,7 @@ main(int argc, char **argv)
633 } 681 }
634 682
635 host = list; 683 host = list;
636 table = malloc(sizeof(struct rta_host **) * targets); 684 table = (struct rta_host**)malloc(sizeof(struct rta_host **) * targets);
637 i = 0; 685 i = 0;
638 while(host) { 686 while(host) {
639 host->id = i*packets; 687 host->id = i*packets;
@@ -697,9 +745,15 @@ run_checks()
697 } 745 }
698} 746}
699 747
748
700/* response structure: 749/* response structure:
750 * IPv4:
701 * ip header : 20 bytes 751 * ip header : 20 bytes
702 * icmp header : 28 bytes 752 * icmp header : 28 bytes
753 * IPv6:
754 * ip header : 40 bytes
755 * icmp header : 28 bytes
756 * both:
703 * icmp echo reply : the rest 757 * icmp echo reply : the rest
704 */ 758 */
705static int 759static int
@@ -707,16 +761,27 @@ wait_for_reply(int sock, u_int t)
707{ 761{
708 int n, hlen; 762 int n, hlen;
709 static unsigned char buf[4096]; 763 static unsigned char buf[4096];
710 struct sockaddr_in resp_addr; 764 struct sockaddr_storage resp_addr;
711 struct ip *ip; 765 union ip_hdr *ip;
712 struct icmp icp; 766 union icmp_packet packet;
713 struct rta_host *host; 767 struct rta_host *host;
714 struct icmp_ping_data data; 768 struct icmp_ping_data data;
715 struct timeval wait_start, now; 769 struct timeval wait_start, now;
716 u_int tdiff, i, per_pkt_wait; 770 u_int tdiff, i, per_pkt_wait;
717 771
772 if (!(packet.buf = malloc(icmp_pkt_size))) {
773 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
774 icmp_pkt_size);
775 return -1; /* might be reached if we're in debug mode */
776 }
777
778 memset(packet.buf, 0, icmp_pkt_size);
779
718 /* if we can't listen or don't have anything to listen to, just return */ 780 /* if we can't listen or don't have anything to listen to, just return */
719 if(!t || !icmp_pkts_en_route) return 0; 781 if(!t || !icmp_pkts_en_route) {
782 free(packet.buf);
783 return 0;
784 }
720 785
721 gettimeofday(&wait_start, &tz); 786 gettimeofday(&wait_start, &tz);
722 787
@@ -735,7 +800,7 @@ wait_for_reply(int sock, u_int t)
735 800
736 /* reap responses until we hit a timeout */ 801 /* reap responses until we hit a timeout */
737 n = recvfrom_wto(sock, buf, sizeof(buf), 802 n = recvfrom_wto(sock, buf, sizeof(buf),
738 (struct sockaddr *)&resp_addr, &t, &now); 803 (struct sockaddr *)&resp_addr, &t, &now);
739 if(!n) { 804 if(!n) {
740 if(debug > 1) { 805 if(debug > 1) {
741 printf("recvfrom_wto() timed out during a %u usecs wait\n", 806 printf("recvfrom_wto() timed out during a %u usecs wait\n",
@@ -745,12 +810,23 @@ wait_for_reply(int sock, u_int t)
745 } 810 }
746 if(n < 0) { 811 if(n < 0) {
747 if(debug) printf("recvfrom_wto() returned errors\n"); 812 if(debug) printf("recvfrom_wto() returned errors\n");
813 free(packet.buf);
748 return n; 814 return n;
749 } 815 }
750 816
751 ip = (struct ip *)buf; 817 // FIXME: with ipv6 we don't have an ip header here
752 if(debug > 1) printf("received %u bytes from %s\n", 818 if (address_family != AF_INET6) {
753 ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr)); 819 ip = (union ip_hdr *)buf;
820
821 if(debug > 1) {
822 char address[INET6_ADDRSTRLEN];
823 parse_address(&resp_addr, address, sizeof(address));
824 printf("received %u bytes from %s\n",
825 address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen)
826 : ntohs(ip->ip.ip_len),
827 address);
828 }
829 }
754 830
755/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 831/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
756/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 832/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
@@ -759,12 +835,14 @@ wait_for_reply(int sock, u_int t)
759 * off the bottom 4 bits */ 835 * off the bottom 4 bits */
760/* hlen = (ip->ip_vhl & 0x0f) << 2; */ 836/* hlen = (ip->ip_vhl & 0x0f) << 2; */
761/* #else */ 837/* #else */
762 hlen = ip->ip_hl << 2; 838 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
763/* #endif */ 839/* #endif */
764 840
765 if(n < (hlen + ICMP_MINLEN)) { 841 if(n < (hlen + ICMP_MINLEN)) {
842 char address[INET6_ADDRSTRLEN];
843 parse_address(&resp_addr, address, sizeof(address));
766 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", 844 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)); 845 n, hlen + icmp_pkt_size, address);
768 } 846 }
769 /* else if(debug) { */ 847 /* else if(debug) { */
770 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ 848 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
@@ -773,23 +851,39 @@ wait_for_reply(int sock, u_int t)
773 /* } */ 851 /* } */
774 852
775 /* check the response */ 853 /* check the response */
776 memcpy(&icp, buf + hlen, sizeof(icp));
777 854
778 if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || 855 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
779 ntohs(icp.icmp_seq) >= targets*packets) { 856/* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
857 : sizeof(struct icmp));*/
858
859 if( (address_family == PF_INET &&
860 (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY
861 || ntohs(packet.icp->icmp_seq) >= targets * packets))
862 || (address_family == PF_INET6 &&
863 (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY
864 || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
780 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); 865 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
781 handle_random_icmp(buf + hlen, &resp_addr); 866 handle_random_icmp(buf + hlen, &resp_addr);
782 continue; 867 continue;
783 } 868 }
784 869
785 /* this is indeed a valid response */ 870 /* this is indeed a valid response */
786 memcpy(&data, icp.icmp_data, sizeof(data)); 871 if (address_family == PF_INET) {
787 if (debug > 2) 872 memcpy(&data, packet.icp->icmp_data, sizeof(data));
788 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", 873 if (debug > 2)
789 (unsigned long)sizeof(data), ntohs(icp.icmp_id), 874 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
790 ntohs(icp.icmp_seq), icp.icmp_cksum); 875 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id),
876 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum);
877 host = table[ntohs(packet.icp->icmp_seq)/packets];
878 } else {
879 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
880 if (debug > 2)
881 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
882 (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id),
883 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum);
884 host = table[ntohs(packet.icp6->icmp6_seq)/packets];
885 }
791 886
792 host = table[ntohs(icp.icmp_seq)/packets];
793 tdiff = get_timevaldiff(&data.stime, &now); 887 tdiff = get_timevaldiff(&data.stime, &now);
794 888
795 host->time_waited += tdiff; 889 host->time_waited += tdiff;
@@ -801,22 +895,25 @@ wait_for_reply(int sock, u_int t)
801 host->rtmin = tdiff; 895 host->rtmin = tdiff;
802 896
803 if(debug) { 897 if(debug) {
898 char address[INET6_ADDRSTRLEN];
899 parse_address(&resp_addr, address, sizeof(address));
804 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", 900 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
805 (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr), 901 (float)tdiff / 1000, address,
806 ttl, ip->ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 902 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000);
807 } 903 }
808 904
809 /* if we're in hostcheck mode, exit with limited printouts */ 905 /* if we're in hostcheck mode, exit with limited printouts */
810 if(mode == MODE_HOSTCHECK) { 906 if(mode == MODE_HOSTCHECK) {
811 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" 907 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
812 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", 908 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
813 host->name, icmp_recv, (float)tdiff / 1000, 909 host->name, icmp_recv, (float)tdiff / 1000,
814 icmp_recv, packets, (float)tdiff / 1000, 910 icmp_recv, packets, (float)tdiff / 1000,
815 (float)warn.rta / 1000, (float)crit.rta / 1000); 911 (float)warn.rta / 1000, (float)crit.rta / 1000);
816 exit(STATE_OK); 912 exit(STATE_OK);
817 } 913 }
818 } 914 }
819 915
916 free(packet.buf);
820 return 0; 917 return 0;
821} 918}
822 919
@@ -824,62 +921,81 @@ wait_for_reply(int sock, u_int t)
824static int 921static int
825send_icmp_ping(int sock, struct rta_host *host) 922send_icmp_ping(int sock, struct rta_host *host)
826{ 923{
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; 924 long int len;
833 struct icmp_ping_data data; 925 struct icmp_ping_data data;
834 struct msghdr hdr; 926 struct msghdr hdr;
835 struct iovec iov; 927 struct iovec iov;
836 struct timeval tv; 928 struct timeval tv;
837 struct sockaddr *addr; 929 void *buf = NULL;
838 930
839 if(sock == -1) { 931 if(sock == -1) {
840 errno = 0; 932 errno = 0;
841 crash("Attempt to send on bogus socket"); 933 crash("Attempt to send on bogus socket");
842 return -1; 934 return -1;
843 } 935 }
844 addr = (struct sockaddr *)&host->saddr_in;
845 936
846 if(!packet.buf) { 937 if(!buf) {
847 if (!(packet.buf = malloc(icmp_pkt_size))) { 938 if (!(buf = malloc(icmp_pkt_size))) {
848 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", 939 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
849 icmp_pkt_size); 940 icmp_pkt_size);
850 return -1; /* might be reached if we're in debug mode */ 941 return -1; /* might be reached if we're in debug mode */
851 } 942 }
852 } 943 }
853 memset(packet.buf, 0, icmp_pkt_size); 944 memset(buf, 0, icmp_pkt_size);
854 945
855 if((gettimeofday(&tv, &tz)) == -1) return -1; 946 if((gettimeofday(&tv, &tz)) == -1) {
947 free(buf);
948 return -1;
949 }
856 950
857 data.ping_id = 10; /* host->icmp.icmp_sent; */ 951 data.ping_id = 10; /* host->icmp.icmp_sent; */
858 memcpy(&data.stime, &tv, sizeof(tv)); 952 memcpy(&data.stime, &tv, sizeof(tv));
859 memcpy(&packet.icp->icmp_data, &data, sizeof(data)); 953
860 packet.icp->icmp_type = ICMP_ECHO; 954 if (address_family == AF_INET) {
861 packet.icp->icmp_code = 0; 955 struct icmp *icp = (struct icmp*)buf;
862 packet.icp->icmp_cksum = 0; 956
863 packet.icp->icmp_id = htons(pid); 957 memcpy(&icp->icmp_data, &data, sizeof(data));
864 packet.icp->icmp_seq = htons(host->id++); 958
865 packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size); 959 icp->icmp_type = ICMP_ECHO;
866 960 icp->icmp_code = 0;
867 if (debug > 2) 961 icp->icmp_cksum = 0;
868 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", 962 icp->icmp_id = htons(pid);
869 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 963 icp->icmp_seq = htons(host->id++);
870 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, 964 icp->icmp_cksum = icmp_checksum((unsigned short*)buf, icmp_pkt_size);
871 host->name); 965
966 if (debug > 2)
967 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
968 (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name);
969 }
970 else {
971 struct icmp6_hdr *icp6 = (struct icmp6_hdr*)buf;
972 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
973 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
974 icp6->icmp6_code = 0;
975 icp6->icmp6_cksum = 0;
976 icp6->icmp6_id = htons(pid);
977 icp6->icmp6_seq = htons(host->id++);
978 // let checksum be calculated automatically
979
980 if (debug > 2) {
981 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
982 (unsigned long)sizeof(data), ntohs(icp6->icmp6_id),
983 ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name);
984 }
985 }
872 986
873 memset(&iov, 0, sizeof(iov)); 987 memset(&iov, 0, sizeof(iov));
874 iov.iov_base = packet.buf; 988 iov.iov_base = buf;
875 iov.iov_len = icmp_pkt_size; 989 iov.iov_len = icmp_pkt_size;
876 990
877 memset(&hdr, 0, sizeof(hdr)); 991 memset(&hdr, 0, sizeof(hdr));
878 hdr.msg_name = addr; 992 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
879 hdr.msg_namelen = sizeof(struct sockaddr); 993 hdr.msg_namelen = sizeof(struct sockaddr_storage);
880 hdr.msg_iov = &iov; 994 hdr.msg_iov = &iov;
881 hdr.msg_iovlen = 1; 995 hdr.msg_iovlen = 1;
882 996
997 errno = 0;
998
883/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 999/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
884#ifdef MSG_CONFIRM 1000#ifdef MSG_CONFIRM
885 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1001 len = sendmsg(sock, &hdr, MSG_CONFIRM);
@@ -887,9 +1003,15 @@ send_icmp_ping(int sock, struct rta_host *host)
887 len = sendmsg(sock, &hdr, 0); 1003 len = sendmsg(sock, &hdr, 0);
888#endif 1004#endif
889 1005
1006 free(buf);
1007
890 if(len < 0 || (unsigned int)len != icmp_pkt_size) { 1008 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
891 if(debug) printf("Failed to send ping to %s\n", 1009 if(debug) {
892 inet_ntoa(host->saddr_in.sin_addr)); 1010 char address[INET6_ADDRSTRLEN];
1011 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1012 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1013 }
1014 errno = 0;
893 return -1; 1015 return -1;
894 } 1016 }
895 1017
@@ -934,7 +1056,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
934 1056
935 if(!n) return 0; /* timeout */ 1057 if(!n) return 0; /* timeout */
936 1058
937 slen = sizeof(struct sockaddr); 1059 slen = sizeof(struct sockaddr_storage);
938 1060
939 memset(&iov, 0, sizeof(iov)); 1061 memset(&iov, 0, sizeof(iov));
940 iov.iov_base = buf; 1062 iov.iov_base = buf;
@@ -958,6 +1080,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
958 break ; 1080 break ;
959 } 1081 }
960 } 1082 }
1083
961 if (!chdr) 1084 if (!chdr)
962#endif // SO_TIMESTAMP 1085#endif // SO_TIMESTAMP
963 gettimeofday(tv, &tz); 1086 gettimeofday(tv, &tz);
@@ -991,6 +1114,7 @@ finish(int sig)
991 1114
992 /* iterate thrice to calculate values, give output, and print perfparse */ 1115 /* iterate thrice to calculate values, give output, and print perfparse */
993 host = list; 1116 host = list;
1117
994 while(host) { 1118 while(host) {
995 if(!host->icmp_recv) { 1119 if(!host->icmp_recv) {
996 /* rta 0 is ofcourse not entirely correct, but will still show up 1120 /* rta 0 is ofcourse not entirely correct, but will still show up
@@ -1039,10 +1163,12 @@ finish(int sig)
1039 if(!host->icmp_recv) { 1163 if(!host->icmp_recv) {
1040 status = STATE_CRITICAL; 1164 status = STATE_CRITICAL;
1041 if(host->flags & FLAG_LOST_CAUSE) { 1165 if(host->flags & FLAG_LOST_CAUSE) {
1166 char address[INET6_ADDRSTRLEN];
1167 parse_address(&host->error_addr, address, sizeof(address));
1042 printf("%s: %s @ %s. rta nan, lost %d%%", 1168 printf("%s: %s @ %s. rta nan, lost %d%%",
1043 host->name, 1169 host->name,
1044 get_icmp_error_msg(host->icmp_type, host->icmp_code), 1170 get_icmp_error_msg(host->icmp_type, host->icmp_code),
1045 inet_ntoa(host->error_addr), 1171 address,
1046 100); 1172 100);
1047 } 1173 }
1048 else { /* not marked as lost cause, so we have no flags for it */ 1174 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)
1104 { 1230 {
1105 return 0; 1231 return 0;
1106 } 1232 }
1107
1108 ret = (later->tv_sec - early->tv_sec) * 1000000; 1233 ret = (later->tv_sec - early->tv_sec) * 1000000;
1109 ret += later->tv_usec - early->tv_usec; 1234 ret += later->tv_usec - early->tv_usec;
1110 1235
@@ -1112,18 +1237,35 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1112} 1237}
1113 1238
1114static int 1239static int
1115add_target_ip(char *arg, struct in_addr *in) 1240add_target_ip(char *arg, struct sockaddr_storage *in)
1116{ 1241{
1117 struct rta_host *host; 1242 struct rta_host *host;
1243 struct sockaddr_in *sin, *host_sin;
1244 struct sockaddr_in6 *sin6, *host_sin6;
1118 1245
1119 /* disregard obviously stupid addresses */ 1246 if (address_family == AF_INET)
1120 if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY) 1247 sin = (struct sockaddr_in *)in;
1248 else
1249 sin6 = (struct sockaddr_in6 *)in;
1250
1251
1252
1253 /* disregard obviously stupid addresses
1254 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1255 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE
1256 || sin->sin_addr.s_addr == INADDR_ANY)))
1257 || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1121 return -1; 1258 return -1;
1259 }
1122 1260
1123 /* no point in adding two identical IP's, so don't. ;) */ 1261 /* no point in adding two identical IP's, so don't. ;) */
1124 host = list; 1262 host = list;
1125 while(host) { 1263 while(host) {
1126 if(host->saddr_in.sin_addr.s_addr == in->s_addr) { 1264 host_sin = (struct sockaddr_in *)&host->saddr_in;
1265 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1266
1267 if( (address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr)
1268 || (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); 1269 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
1128 return -1; 1270 return -1;
1129 } 1271 }
@@ -1131,19 +1273,29 @@ add_target_ip(char *arg, struct in_addr *in)
1131 } 1273 }
1132 1274
1133 /* add the fresh ip */ 1275 /* add the fresh ip */
1134 host = malloc(sizeof(struct rta_host)); 1276 host = (struct rta_host*)malloc(sizeof(struct rta_host));
1135 if(!host) { 1277 if(!host) {
1278 char straddr[INET6_ADDRSTRLEN];
1279 parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
1136 crash("add_target_ip(%s, %s): malloc(%d) failed", 1280 crash("add_target_ip(%s, %s): malloc(%d) failed",
1137 arg, inet_ntoa(*in), sizeof(struct rta_host)); 1281 arg, straddr, sizeof(struct rta_host));
1138 } 1282 }
1139 memset(host, 0, sizeof(struct rta_host)); 1283 memset(host, 0, sizeof(struct rta_host));
1140 1284
1141 /* set the values. use calling name for output */ 1285 /* set the values. use calling name for output */
1142 host->name = strdup(arg); 1286 host->name = strdup(arg);
1143 1287
1144 /* fill out the sockaddr_in struct */ 1288 /* fill out the sockaddr_storage struct */
1145 host->saddr_in.sin_family = AF_INET; 1289 if(address_family == AF_INET) {
1146 host->saddr_in.sin_addr.s_addr = in->s_addr; 1290 host_sin = (struct sockaddr_in *)&host->saddr_in;
1291 host_sin->sin_family = AF_INET;
1292 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1293 }
1294 else {
1295 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1296 host_sin6->sin6_family = AF_INET6;
1297 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1298 }
1147 1299
1148 host->rtmin = DBL_MAX; 1300 host->rtmin = DBL_MAX;
1149 1301
@@ -1160,31 +1312,45 @@ add_target_ip(char *arg, struct in_addr *in)
1160static int 1312static int
1161add_target(char *arg) 1313add_target(char *arg)
1162{ 1314{
1163 int i; 1315 int error, result;
1164 struct hostent *he; 1316 struct sockaddr_storage ip;
1165 struct in_addr *in, ip; 1317 struct addrinfo hints, *res, *p;
1318 struct sockaddr_in *sin;
1319 struct sockaddr_in6 *sin6;
1320
1321 switch (address_family) {
1322 case AF_INET:
1323 sin = (struct sockaddr_in *)&ip;
1324 result = inet_pton(address_family, arg, &sin->sin_addr);
1325 break;
1326 case AF_INET6:
1327 sin6 = (struct sockaddr_in6 *)&ip;
1328 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1329 break;
1330 default: crash("Address family not supported");
1331 }
1166 1332
1167 /* don't resolve if we don't have to */ 1333 /* don't resolve if we don't have to */
1168 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) { 1334 if(result == 1) {
1169 /* don't add all ip's if we were given a specific one */ 1335 /* don't add all ip's if we were given a specific one */
1170 return add_target_ip(arg, &ip); 1336 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 } 1337 }
1174 else { 1338 else {
1175 errno = 0; 1339 errno = 0;
1176 he = gethostbyname(arg); 1340 memset(&hints, 0, sizeof(hints));
1177 if(!he) { 1341 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1342 hints.ai_socktype = SOCK_RAW;
1343 if((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1178 errno = 0; 1344 errno = 0;
1179 crash("Failed to resolve %s", arg); 1345 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1180 return -1; 1346 return -1;
1181 } 1347 }
1182 } 1348 }
1183 1349
1184 /* possibly add all the IP's as targets */ 1350 /* possibly add all the IP's as targets */
1185 for(i = 0; he->h_addr_list[i]; i++) { 1351 for(p = res; p != NULL; p = p->ai_next) {
1186 in = (struct in_addr *)he->h_addr_list[i]; 1352 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1187 add_target_ip(arg, in); 1353 add_target_ip(arg, &ip);
1188 1354
1189 /* this is silly, but it works */ 1355 /* this is silly, but it works */
1190 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1356 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1193,6 +1359,7 @@ add_target(char *arg)
1193 } 1359 }
1194 break; 1360 break;
1195 } 1361 }
1362 freeaddrinfo(res);
1196 1363
1197 return 0; 1364 return 0;
1198} 1365}
@@ -1203,7 +1370,7 @@ set_source_ip(char *arg)
1203 struct sockaddr_in src; 1370 struct sockaddr_in src;
1204 1371
1205 memset(&src, 0, sizeof(src)); 1372 memset(&src, 0, sizeof(src));
1206 src.sin_family = AF_INET; 1373 src.sin_family = address_family;
1207 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) 1374 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1208 src.sin_addr.s_addr = get_ip_address(arg); 1375 src.sin_addr.s_addr = get_ip_address(arg);
1209 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) 1376 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
@@ -1311,12 +1478,12 @@ get_threshold(char *str, threshold *th)
1311unsigned short 1478unsigned short
1312icmp_checksum(unsigned short *p, int n) 1479icmp_checksum(unsigned short *p, int n)
1313{ 1480{
1314 register unsigned short cksum; 1481 unsigned short cksum;
1315 register long sum = 0; 1482 long sum = 0;
1316 1483
1317 while(n > 1) { 1484 while(n > 2) {
1318 sum += *p++; 1485 sum += *p++;
1319 n -= 2; 1486 n -= sizeof(unsigned short);
1320 } 1487 }
1321 1488
1322 /* mop up the occasional odd byte */ 1489 /* mop up the occasional odd byte */
@@ -1347,6 +1514,8 @@ print_help(void)
1347 1514
1348 printf (" %s\n", "-H"); 1515 printf (" %s\n", "-H");
1349 printf (" %s\n", _("specify a target")); 1516 printf (" %s\n", _("specify a target"));
1517 printf (" %s\n", "[-4|-6]");
1518 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
1350 printf (" %s\n", "-w"); 1519 printf (" %s\n", "-w");
1351 printf (" %s", _("warning threshold (currently ")); 1520 printf (" %s", _("warning threshold (currently "));
1352 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 1521 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);