summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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";
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);