summaryrefslogtreecommitdiffstats
path: root/plugins-root/check_icmp.c
diff options
context:
space:
mode:
authorSven Nierlein <sven@nierlein.org>2019-02-19 14:44:05 (GMT)
committerGitHub <noreply@github.com>2019-02-19 14:44:05 (GMT)
commitc40babbb6a4307a97564ef2591af25bc51675526 (patch)
tree7322be8117e340efbaeafd0ca14af7417a05660c /plugins-root/check_icmp.c
parentc0ff2b66e8baebee3bed23d02ae31470900bc45f (diff)
parent4a4ef0d6898cd1590561cd7685d1b8b02b757823 (diff)
downloadmonitoring-plugins-c40babbb6a4307a97564ef2591af25bc51675526.tar.gz
Merge pull request #1563 from jacobbaungard/ipv6_check_icmp
check_icmp: Add IPv6 support
Diffstat (limited to 'plugins-root/check_icmp.c')
-rw-r--r--plugins-root/check_icmp.c465
1 files changed, 336 insertions, 129 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index 4098874..e45fdf6 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{
@@ -381,6 +410,7 @@ main(int argc, char **argv)
381#ifdef SO_TIMESTAMP 410#ifdef SO_TIMESTAMP
382 int on = 1; 411 int on = 1;
383#endif 412#endif
413 char * opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:64";
384 414
385 setlocale (LC_ALL, ""); 415 setlocale (LC_ALL, "");
386 bindtextdomain (PACKAGE, LOCALEDIR); 416 bindtextdomain (PACKAGE, LOCALEDIR);
@@ -390,33 +420,8 @@ main(int argc, char **argv)
390 * that before pointer magic (esp. on network data) */ 420 * that before pointer magic (esp. on network data) */
391 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; 421 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
392 422
393 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) 423 address_family = -1;
394 sockets |= HAVE_ICMP; 424 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 425
421 /* get calling name the old-fashioned way for portability instead 426 /* get calling name the old-fashioned way for portability instead
422 * of relying on the glibc-ism __progname */ 427 * of relying on the glibc-ism __progname */
@@ -456,20 +461,35 @@ main(int argc, char **argv)
456 packets = 5; 461 packets = 5;
457 } 462 }
458 463
459 /* Parse extra opts if any */ 464 /* Parse protocol arguments first */
460 argv=np_extra_opts(&argc, argv, progname); 465 for(i = 1; i < argc; i++) {
461 466 while((arg = getopt(argc, argv, opts_str)) != EOF) {
462 /* support "--help" and "--version" */ 467 unsigned short size;
463 if(argc == 2) { 468 switch(arg) {
464 if(!strcmp(argv[1], "--help")) 469 case '4':
465 strcpy(argv[1], "-h"); 470 if (address_family != -1)
466 if(!strcmp(argv[1], "--version")) 471 crash("Multiple protocol versions not supported");
467 strcpy(argv[1], "-V"); 472 address_family = AF_INET;
473 break;
474 case '6':
475#ifdef USE_IPV6
476 if (address_family != -1)
477 crash("Multiple protocol versions not supported");
478 address_family = AF_INET6;
479#else
480 usage (_("IPv6 support not available\n"));
481#endif
482 break;
483 }
484 }
468 } 485 }
469 486
487 /* Reset argument scanning */
488 optind = 1;
489
470 /* parse the arguments */ 490 /* parse the arguments */
471 for(i = 1; i < argc; i++) { 491 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) { 492 while((arg = getopt(argc, argv, opts_str)) != EOF) {
473 unsigned short size; 493 unsigned short size;
474 switch(arg) { 494 switch(arg) {
475 case 'v': 495 case 'v':
@@ -530,10 +550,30 @@ main(int argc, char **argv)
530 case 'h': /* help */ 550 case 'h': /* help */
531 print_help (); 551 print_help ();
532 exit (STATE_UNKNOWN); 552 exit (STATE_UNKNOWN);
553 break;
533 } 554 }
534 } 555 }
535 } 556 }
536 557
558 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
559 environ = NULL;
560
561 /* use the pid to mark packets as ours */
562 /* Some systems have 32-bit pid_t so mask off only 16 bits */
563 pid = getpid() & 0xffff;
564 /* printf("pid = %u\n", pid); */
565
566 /* Parse extra opts if any */
567 argv=np_extra_opts(&argc, argv, progname);
568
569 /* support "--help" and "--version" */
570 if(argc == 2) {
571 if(!strcmp(argv[1], "--help"))
572 strcpy(argv[1], "-h");
573 if(!strcmp(argv[1], "--version"))
574 strcpy(argv[1], "-V");
575 }
576
537 argv = &argv[optind]; 577 argv = &argv[optind];
538 while(*argv) { 578 while(*argv) {
539 add_target(*argv); 579 add_target(*argv);
@@ -545,6 +585,30 @@ main(int argc, char **argv)
545 exit(3); 585 exit(3);
546 } 586 }
547 587
588 // add_target might change address_family
589 switch ( address_family ){
590 case AF_INET: icmp_proto = IPPROTO_ICMP;
591 break;
592 case AF_INET6: icmp_proto = IPPROTO_ICMPV6;
593 break;
594 default: crash("Address family not supported");
595 }
596 if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
597 sockets |= HAVE_ICMP;
598 else icmp_sockerrno = errno;
599
600
601#ifdef SO_TIMESTAMP
602 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
603 if(debug) printf("Warning: no SO_TIMESTAMP support\n");
604#endif // SO_TIMESTAMP
605
606 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
607 if (setuid(getuid()) == -1) {
608 printf("ERROR: Failed to drop privileges\n");
609 return 1;
610 }
611
548 if(!sockets) { 612 if(!sockets) {
549 if(icmp_sock == -1) { 613 if(icmp_sock == -1) {
550 errno = icmp_sockerrno; 614 errno = icmp_sockerrno;
@@ -633,7 +697,7 @@ main(int argc, char **argv)
633 } 697 }
634 698
635 host = list; 699 host = list;
636 table = malloc(sizeof(struct rta_host **) * targets); 700 table = (struct rta_host**)malloc(sizeof(struct rta_host **) * targets);
637 i = 0; 701 i = 0;
638 while(host) { 702 while(host) {
639 host->id = i*packets; 703 host->id = i*packets;
@@ -697,9 +761,15 @@ run_checks()
697 } 761 }
698} 762}
699 763
764
700/* response structure: 765/* response structure:
766 * IPv4:
701 * ip header : 20 bytes 767 * ip header : 20 bytes
702 * icmp header : 28 bytes 768 * icmp header : 28 bytes
769 * IPv6:
770 * ip header : 40 bytes
771 * icmp header : 28 bytes
772 * both:
703 * icmp echo reply : the rest 773 * icmp echo reply : the rest
704 */ 774 */
705static int 775static int
@@ -707,16 +777,27 @@ wait_for_reply(int sock, u_int t)
707{ 777{
708 int n, hlen; 778 int n, hlen;
709 static unsigned char buf[4096]; 779 static unsigned char buf[4096];
710 struct sockaddr_in resp_addr; 780 struct sockaddr_storage resp_addr;
711 struct ip *ip; 781 union ip_hdr *ip;
712 struct icmp icp; 782 union icmp_packet packet;
713 struct rta_host *host; 783 struct rta_host *host;
714 struct icmp_ping_data data; 784 struct icmp_ping_data data;
715 struct timeval wait_start, now; 785 struct timeval wait_start, now;
716 u_int tdiff, i, per_pkt_wait; 786 u_int tdiff, i, per_pkt_wait;
717 787
788 if (!(packet.buf = malloc(icmp_pkt_size))) {
789 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
790 icmp_pkt_size);
791 return -1; /* might be reached if we're in debug mode */
792 }
793
794 memset(packet.buf, 0, icmp_pkt_size);
795
718 /* if we can't listen or don't have anything to listen to, just return */ 796 /* if we can't listen or don't have anything to listen to, just return */
719 if(!t || !icmp_pkts_en_route) return 0; 797 if(!t || !icmp_pkts_en_route) {
798 free(packet.buf);
799 return 0;
800 }
720 801
721 gettimeofday(&wait_start, &tz); 802 gettimeofday(&wait_start, &tz);
722 803
@@ -735,7 +816,7 @@ wait_for_reply(int sock, u_int t)
735 816
736 /* reap responses until we hit a timeout */ 817 /* reap responses until we hit a timeout */
737 n = recvfrom_wto(sock, buf, sizeof(buf), 818 n = recvfrom_wto(sock, buf, sizeof(buf),
738 (struct sockaddr *)&resp_addr, &t, &now); 819 (struct sockaddr *)&resp_addr, &t, &now);
739 if(!n) { 820 if(!n) {
740 if(debug > 1) { 821 if(debug > 1) {
741 printf("recvfrom_wto() timed out during a %u usecs wait\n", 822 printf("recvfrom_wto() timed out during a %u usecs wait\n",
@@ -745,12 +826,23 @@ wait_for_reply(int sock, u_int t)
745 } 826 }
746 if(n < 0) { 827 if(n < 0) {
747 if(debug) printf("recvfrom_wto() returned errors\n"); 828 if(debug) printf("recvfrom_wto() returned errors\n");
829 free(packet.buf);
748 return n; 830 return n;
749 } 831 }
750 832
751 ip = (struct ip *)buf; 833 // FIXME: with ipv6 we don't have an ip header here
752 if(debug > 1) printf("received %u bytes from %s\n", 834 if (address_family != AF_INET6) {
753 ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr)); 835 ip = (union ip_hdr *)buf;
836
837 if(debug > 1) {
838 char address[INET6_ADDRSTRLEN];
839 parse_address(&resp_addr, address, sizeof(address));
840 printf("received %u bytes from %s\n",
841 address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen)
842 : ntohs(ip->ip.ip_len),
843 address);
844 }
845 }
754 846
755/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 847/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
756/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 848/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
@@ -759,12 +851,14 @@ wait_for_reply(int sock, u_int t)
759 * off the bottom 4 bits */ 851 * off the bottom 4 bits */
760/* hlen = (ip->ip_vhl & 0x0f) << 2; */ 852/* hlen = (ip->ip_vhl & 0x0f) << 2; */
761/* #else */ 853/* #else */
762 hlen = ip->ip_hl << 2; 854 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
763/* #endif */ 855/* #endif */
764 856
765 if(n < (hlen + ICMP_MINLEN)) { 857 if(n < (hlen + ICMP_MINLEN)) {
858 char address[INET6_ADDRSTRLEN];
859 parse_address(&resp_addr, address, sizeof(address));
766 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", 860 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)); 861 n, hlen + icmp_pkt_size, address);
768 } 862 }
769 /* else if(debug) { */ 863 /* else if(debug) { */
770 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */ 864 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
@@ -773,23 +867,39 @@ wait_for_reply(int sock, u_int t)
773 /* } */ 867 /* } */
774 868
775 /* check the response */ 869 /* check the response */
776 memcpy(&icp, buf + hlen, sizeof(icp));
777 870
778 if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || 871 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
779 ntohs(icp.icmp_seq) >= targets*packets) { 872/* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
873 : sizeof(struct icmp));*/
874
875 if( (address_family == PF_INET &&
876 (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY
877 || ntohs(packet.icp->icmp_seq) >= targets * packets))
878 || (address_family == PF_INET6 &&
879 (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY
880 || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
780 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); 881 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
781 handle_random_icmp(buf + hlen, &resp_addr); 882 handle_random_icmp(buf + hlen, &resp_addr);
782 continue; 883 continue;
783 } 884 }
784 885
785 /* this is indeed a valid response */ 886 /* this is indeed a valid response */
786 memcpy(&data, icp.icmp_data, sizeof(data)); 887 if (address_family == PF_INET) {
787 if (debug > 2) 888 memcpy(&data, packet.icp->icmp_data, sizeof(data));
788 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", 889 if (debug > 2)
789 (unsigned long)sizeof(data), ntohs(icp.icmp_id), 890 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
790 ntohs(icp.icmp_seq), icp.icmp_cksum); 891 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id),
892 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum);
893 host = table[ntohs(packet.icp->icmp_seq)/packets];
894 } else {
895 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
896 if (debug > 2)
897 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
898 (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id),
899 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum);
900 host = table[ntohs(packet.icp6->icmp6_seq)/packets];
901 }
791 902
792 host = table[ntohs(icp.icmp_seq)/packets];
793 tdiff = get_timevaldiff(&data.stime, &now); 903 tdiff = get_timevaldiff(&data.stime, &now);
794 904
795 host->time_waited += tdiff; 905 host->time_waited += tdiff;
@@ -801,22 +911,25 @@ wait_for_reply(int sock, u_int t)
801 host->rtmin = tdiff; 911 host->rtmin = tdiff;
802 912
803 if(debug) { 913 if(debug) {
914 char address[INET6_ADDRSTRLEN];
915 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", 916 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), 917 (float)tdiff / 1000, address,
806 ttl, ip->ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 918 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000);
807 } 919 }
808 920
809 /* if we're in hostcheck mode, exit with limited printouts */ 921 /* if we're in hostcheck mode, exit with limited printouts */
810 if(mode == MODE_HOSTCHECK) { 922 if(mode == MODE_HOSTCHECK) {
811 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" 923 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
812 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n", 924 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
813 host->name, icmp_recv, (float)tdiff / 1000, 925 host->name, icmp_recv, (float)tdiff / 1000,
814 icmp_recv, packets, (float)tdiff / 1000, 926 icmp_recv, packets, (float)tdiff / 1000,
815 (float)warn.rta / 1000, (float)crit.rta / 1000); 927 (float)warn.rta / 1000, (float)crit.rta / 1000);
816 exit(STATE_OK); 928 exit(STATE_OK);
817 } 929 }
818 } 930 }
819 931
932 free(packet.buf);
820 return 0; 933 return 0;
821} 934}
822 935
@@ -824,62 +937,81 @@ wait_for_reply(int sock, u_int t)
824static int 937static int
825send_icmp_ping(int sock, struct rta_host *host) 938send_icmp_ping(int sock, struct rta_host *host)
826{ 939{
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; 940 long int len;
833 struct icmp_ping_data data; 941 struct icmp_ping_data data;
834 struct msghdr hdr; 942 struct msghdr hdr;
835 struct iovec iov; 943 struct iovec iov;
836 struct timeval tv; 944 struct timeval tv;
837 struct sockaddr *addr; 945 void *buf = NULL;
838 946
839 if(sock == -1) { 947 if(sock == -1) {
840 errno = 0; 948 errno = 0;
841 crash("Attempt to send on bogus socket"); 949 crash("Attempt to send on bogus socket");
842 return -1; 950 return -1;
843 } 951 }
844 addr = (struct sockaddr *)&host->saddr_in;
845 952
846 if(!packet.buf) { 953 if(!buf) {
847 if (!(packet.buf = malloc(icmp_pkt_size))) { 954 if (!(buf = malloc(icmp_pkt_size))) {
848 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", 955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
849 icmp_pkt_size); 956 icmp_pkt_size);
850 return -1; /* might be reached if we're in debug mode */ 957 return -1; /* might be reached if we're in debug mode */
851 } 958 }
852 } 959 }
853 memset(packet.buf, 0, icmp_pkt_size); 960 memset(buf, 0, icmp_pkt_size);
854 961
855 if((gettimeofday(&tv, &tz)) == -1) return -1; 962 if((gettimeofday(&tv, &tz)) == -1) {
963 free(buf);
964 return -1;
965 }
856 966
857 data.ping_id = 10; /* host->icmp.icmp_sent; */ 967 data.ping_id = 10; /* host->icmp.icmp_sent; */
858 memcpy(&data.stime, &tv, sizeof(tv)); 968 memcpy(&data.stime, &tv, sizeof(tv));
859 memcpy(&packet.icp->icmp_data, &data, sizeof(data)); 969
860 packet.icp->icmp_type = ICMP_ECHO; 970 if (address_family == AF_INET) {
861 packet.icp->icmp_code = 0; 971 struct icmp *icp = (struct icmp*)buf;
862 packet.icp->icmp_cksum = 0; 972
863 packet.icp->icmp_id = htons(pid); 973 memcpy(&icp->icmp_data, &data, sizeof(data));
864 packet.icp->icmp_seq = htons(host->id++); 974
865 packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size); 975 icp->icmp_type = ICMP_ECHO;
866 976 icp->icmp_code = 0;
867 if (debug > 2) 977 icp->icmp_cksum = 0;
868 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", 978 icp->icmp_id = htons(pid);
869 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 979 icp->icmp_seq = htons(host->id++);
870 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, 980 icp->icmp_cksum = icmp_checksum((unsigned short*)buf, icmp_pkt_size);
871 host->name); 981
982 if (debug > 2)
983 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
984 (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name);
985 }
986 else {
987 struct icmp6_hdr *icp6 = (struct icmp6_hdr*)buf;
988 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
989 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
990 icp6->icmp6_code = 0;
991 icp6->icmp6_cksum = 0;
992 icp6->icmp6_id = htons(pid);
993 icp6->icmp6_seq = htons(host->id++);
994 // let checksum be calculated automatically
995
996 if (debug > 2) {
997 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
998 (unsigned long)sizeof(data), ntohs(icp6->icmp6_id),
999 ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name);
1000 }
1001 }
872 1002
873 memset(&iov, 0, sizeof(iov)); 1003 memset(&iov, 0, sizeof(iov));
874 iov.iov_base = packet.buf; 1004 iov.iov_base = buf;
875 iov.iov_len = icmp_pkt_size; 1005 iov.iov_len = icmp_pkt_size;
876 1006
877 memset(&hdr, 0, sizeof(hdr)); 1007 memset(&hdr, 0, sizeof(hdr));
878 hdr.msg_name = addr; 1008 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
879 hdr.msg_namelen = sizeof(struct sockaddr); 1009 hdr.msg_namelen = sizeof(struct sockaddr_storage);
880 hdr.msg_iov = &iov; 1010 hdr.msg_iov = &iov;
881 hdr.msg_iovlen = 1; 1011 hdr.msg_iovlen = 1;
882 1012
1013 errno = 0;
1014
883/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1015/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
884#ifdef MSG_CONFIRM 1016#ifdef MSG_CONFIRM
885 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1017 len = sendmsg(sock, &hdr, MSG_CONFIRM);
@@ -887,9 +1019,15 @@ send_icmp_ping(int sock, struct rta_host *host)
887 len = sendmsg(sock, &hdr, 0); 1019 len = sendmsg(sock, &hdr, 0);
888#endif 1020#endif
889 1021
1022 free(buf);
1023
890 if(len < 0 || (unsigned int)len != icmp_pkt_size) { 1024 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
891 if(debug) printf("Failed to send ping to %s\n", 1025 if(debug) {
892 inet_ntoa(host->saddr_in.sin_addr)); 1026 char address[INET6_ADDRSTRLEN];
1027 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1028 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1029 }
1030 errno = 0;
893 return -1; 1031 return -1;
894 } 1032 }
895 1033
@@ -934,7 +1072,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
934 1072
935 if(!n) return 0; /* timeout */ 1073 if(!n) return 0; /* timeout */
936 1074
937 slen = sizeof(struct sockaddr); 1075 slen = sizeof(struct sockaddr_storage);
938 1076
939 memset(&iov, 0, sizeof(iov)); 1077 memset(&iov, 0, sizeof(iov));
940 iov.iov_base = buf; 1078 iov.iov_base = buf;
@@ -958,6 +1096,7 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
958 break ; 1096 break ;
959 } 1097 }
960 } 1098 }
1099
961 if (!chdr) 1100 if (!chdr)
962#endif // SO_TIMESTAMP 1101#endif // SO_TIMESTAMP
963 gettimeofday(tv, &tz); 1102 gettimeofday(tv, &tz);
@@ -991,6 +1130,7 @@ finish(int sig)
991 1130
992 /* iterate thrice to calculate values, give output, and print perfparse */ 1131 /* iterate thrice to calculate values, give output, and print perfparse */
993 host = list; 1132 host = list;
1133
994 while(host) { 1134 while(host) {
995 if(!host->icmp_recv) { 1135 if(!host->icmp_recv) {
996 /* rta 0 is ofcourse not entirely correct, but will still show up 1136 /* rta 0 is ofcourse not entirely correct, but will still show up
@@ -1039,10 +1179,12 @@ finish(int sig)
1039 if(!host->icmp_recv) { 1179 if(!host->icmp_recv) {
1040 status = STATE_CRITICAL; 1180 status = STATE_CRITICAL;
1041 if(host->flags & FLAG_LOST_CAUSE) { 1181 if(host->flags & FLAG_LOST_CAUSE) {
1182 char address[INET6_ADDRSTRLEN];
1183 parse_address(&host->error_addr, address, sizeof(address));
1042 printf("%s: %s @ %s. rta nan, lost %d%%", 1184 printf("%s: %s @ %s. rta nan, lost %d%%",
1043 host->name, 1185 host->name,
1044 get_icmp_error_msg(host->icmp_type, host->icmp_code), 1186 get_icmp_error_msg(host->icmp_type, host->icmp_code),
1045 inet_ntoa(host->error_addr), 1187 address,
1046 100); 1188 100);
1047 } 1189 }
1048 else { /* not marked as lost cause, so we have no flags for it */ 1190 else { /* not marked as lost cause, so we have no flags for it */
@@ -1104,7 +1246,6 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1104 { 1246 {
1105 return 0; 1247 return 0;
1106 } 1248 }
1107
1108 ret = (later->tv_sec - early->tv_sec) * 1000000; 1249 ret = (later->tv_sec - early->tv_sec) * 1000000;
1109 ret += later->tv_usec - early->tv_usec; 1250 ret += later->tv_usec - early->tv_usec;
1110 1251
@@ -1112,18 +1253,35 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1112} 1253}
1113 1254
1114static int 1255static int
1115add_target_ip(char *arg, struct in_addr *in) 1256add_target_ip(char *arg, struct sockaddr_storage *in)
1116{ 1257{
1117 struct rta_host *host; 1258 struct rta_host *host;
1259 struct sockaddr_in *sin, *host_sin;
1260 struct sockaddr_in6 *sin6, *host_sin6;
1261
1262 if (address_family == AF_INET)
1263 sin = (struct sockaddr_in *)in;
1264 else
1265 sin6 = (struct sockaddr_in6 *)in;
1118 1266
1119 /* disregard obviously stupid addresses */ 1267
1120 if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY) 1268
1269 /* disregard obviously stupid addresses
1270 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1271 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE
1272 || sin->sin_addr.s_addr == INADDR_ANY)))
1273 || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1121 return -1; 1274 return -1;
1275 }
1122 1276
1123 /* no point in adding two identical IP's, so don't. ;) */ 1277 /* no point in adding two identical IP's, so don't. ;) */
1124 host = list; 1278 host = list;
1125 while(host) { 1279 while(host) {
1126 if(host->saddr_in.sin_addr.s_addr == in->s_addr) { 1280 host_sin = (struct sockaddr_in *)&host->saddr_in;
1281 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1282
1283 if( (address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr)
1284 || (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); 1285 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
1128 return -1; 1286 return -1;
1129 } 1287 }
@@ -1131,19 +1289,29 @@ add_target_ip(char *arg, struct in_addr *in)
1131 } 1289 }
1132 1290
1133 /* add the fresh ip */ 1291 /* add the fresh ip */
1134 host = malloc(sizeof(struct rta_host)); 1292 host = (struct rta_host*)malloc(sizeof(struct rta_host));
1135 if(!host) { 1293 if(!host) {
1294 char straddr[INET6_ADDRSTRLEN];
1295 parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
1136 crash("add_target_ip(%s, %s): malloc(%d) failed", 1296 crash("add_target_ip(%s, %s): malloc(%d) failed",
1137 arg, inet_ntoa(*in), sizeof(struct rta_host)); 1297 arg, straddr, sizeof(struct rta_host));
1138 } 1298 }
1139 memset(host, 0, sizeof(struct rta_host)); 1299 memset(host, 0, sizeof(struct rta_host));
1140 1300
1141 /* set the values. use calling name for output */ 1301 /* set the values. use calling name for output */
1142 host->name = strdup(arg); 1302 host->name = strdup(arg);
1143 1303
1144 /* fill out the sockaddr_in struct */ 1304 /* fill out the sockaddr_storage struct */
1145 host->saddr_in.sin_family = AF_INET; 1305 if(address_family == AF_INET) {
1146 host->saddr_in.sin_addr.s_addr = in->s_addr; 1306 host_sin = (struct sockaddr_in *)&host->saddr_in;
1307 host_sin->sin_family = AF_INET;
1308 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1309 }
1310 else {
1311 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1312 host_sin6->sin6_family = AF_INET6;
1313 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1314 }
1147 1315
1148 host->rtmin = DBL_MAX; 1316 host->rtmin = DBL_MAX;
1149 1317
@@ -1160,31 +1328,67 @@ add_target_ip(char *arg, struct in_addr *in)
1160static int 1328static int
1161add_target(char *arg) 1329add_target(char *arg)
1162{ 1330{
1163 int i; 1331 int error, result;
1164 struct hostent *he; 1332 struct sockaddr_storage ip;
1165 struct in_addr *in, ip; 1333 struct addrinfo hints, *res, *p;
1334 struct sockaddr_in *sin;
1335 struct sockaddr_in6 *sin6;
1336
1337 switch (address_family) {
1338 case -1:
1339 /* -4 and -6 are not specified on cmdline */
1340 address_family = AF_INET;
1341 sin = (struct sockaddr_in *)&ip;
1342 result = inet_pton(address_family, arg, &sin->sin_addr);
1343#ifdef USE_IPV6
1344 if( result != 1 ){
1345 address_family = AF_INET6;
1346 sin6 = (struct sockaddr_in6 *)&ip;
1347 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1348 }
1349#endif
1350 /* If we don't find any valid addresses, we still don't know the address_family */
1351 if ( result != 1) {
1352 address_family = -1;
1353 }
1354 break;
1355 case AF_INET:
1356 sin = (struct sockaddr_in *)&ip;
1357 result = inet_pton(address_family, arg, &sin->sin_addr);
1358 break;
1359 case AF_INET6:
1360 sin6 = (struct sockaddr_in6 *)&ip;
1361 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1362 break;
1363 default: crash("Address family not supported");
1364 }
1166 1365
1167 /* don't resolve if we don't have to */ 1366 /* don't resolve if we don't have to */
1168 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) { 1367 if(result == 1) {
1169 /* don't add all ip's if we were given a specific one */ 1368 /* don't add all ip's if we were given a specific one */
1170 return add_target_ip(arg, &ip); 1369 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 } 1370 }
1174 else { 1371 else {
1175 errno = 0; 1372 errno = 0;
1176 he = gethostbyname(arg); 1373 memset(&hints, 0, sizeof(hints));
1177 if(!he) { 1374 if (address_family == -1) {
1375 hints.ai_family = AF_UNSPEC;
1376 } else {
1377 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1378 }
1379 hints.ai_socktype = SOCK_RAW;
1380 if((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1178 errno = 0; 1381 errno = 0;
1179 crash("Failed to resolve %s", arg); 1382 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1180 return -1; 1383 return -1;
1181 } 1384 }
1385 address_family = res->ai_family;
1182 } 1386 }
1183 1387
1184 /* possibly add all the IP's as targets */ 1388 /* possibly add all the IP's as targets */
1185 for(i = 0; he->h_addr_list[i]; i++) { 1389 for(p = res; p != NULL; p = p->ai_next) {
1186 in = (struct in_addr *)he->h_addr_list[i]; 1390 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1187 add_target_ip(arg, in); 1391 add_target_ip(arg, &ip);
1188 1392
1189 /* this is silly, but it works */ 1393 /* this is silly, but it works */
1190 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1394 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1193,6 +1397,7 @@ add_target(char *arg)
1193 } 1397 }
1194 break; 1398 break;
1195 } 1399 }
1400 freeaddrinfo(res);
1196 1401
1197 return 0; 1402 return 0;
1198} 1403}
@@ -1203,7 +1408,7 @@ set_source_ip(char *arg)
1203 struct sockaddr_in src; 1408 struct sockaddr_in src;
1204 1409
1205 memset(&src, 0, sizeof(src)); 1410 memset(&src, 0, sizeof(src));
1206 src.sin_family = AF_INET; 1411 src.sin_family = address_family;
1207 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) 1412 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1208 src.sin_addr.s_addr = get_ip_address(arg); 1413 src.sin_addr.s_addr = get_ip_address(arg);
1209 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) 1414 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
@@ -1311,12 +1516,12 @@ get_threshold(char *str, threshold *th)
1311unsigned short 1516unsigned short
1312icmp_checksum(unsigned short *p, int n) 1517icmp_checksum(unsigned short *p, int n)
1313{ 1518{
1314 register unsigned short cksum; 1519 unsigned short cksum;
1315 register long sum = 0; 1520 long sum = 0;
1316 1521
1317 while(n > 1) { 1522 while(n > 2) {
1318 sum += *p++; 1523 sum += *p++;
1319 n -= 2; 1524 n -= sizeof(unsigned short);
1320 } 1525 }
1321 1526
1322 /* mop up the occasional odd byte */ 1527 /* mop up the occasional odd byte */
@@ -1347,6 +1552,8 @@ print_help(void)
1347 1552
1348 printf (" %s\n", "-H"); 1553 printf (" %s\n", "-H");
1349 printf (" %s\n", _("specify a target")); 1554 printf (" %s\n", _("specify a target"));
1555 printf (" %s\n", "[-4|-6]");
1556 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
1350 printf (" %s\n", "-w"); 1557 printf (" %s\n", "-w");
1351 printf (" %s", _("warning threshold (currently ")); 1558 printf (" %s", _("warning threshold (currently "));
1352 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 1559 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);