summaryrefslogtreecommitdiffstats
path: root/plugins-root/check_icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins-root/check_icmp.c')
-rw-r--r--plugins-root/check_icmp.c530
1 files changed, 397 insertions, 133 deletions
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index 8b563e4..01ae174 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
@@ -184,14 +198,15 @@ static u_int get_timevar(const char *);
184static u_int get_timevaldiff(struct timeval *, struct timeval *); 198static u_int get_timevaldiff(struct timeval *, struct timeval *);
185static in_addr_t get_ip_address(const char *); 199static in_addr_t get_ip_address(const char *);
186static int wait_for_reply(int, u_int); 200static int wait_for_reply(int, u_int);
187static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *); 201static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval*);
188static int send_icmp_ping(int, struct rta_host *); 202static int send_icmp_ping(int, struct rta_host *);
189static int get_threshold(char *str, threshold *th); 203static 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{
@@ -378,6 +407,10 @@ main(int argc, char **argv)
378 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno; 407 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
379 int result; 408 int result;
380 struct rta_host *host; 409 struct rta_host *host;
410#ifdef SO_TIMESTAMP
411 int on = 1;
412#endif
413 char * opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:64";
381 414
382 setlocale (LC_ALL, ""); 415 setlocale (LC_ALL, "");
383 bindtextdomain (PACKAGE, LOCALEDIR); 416 bindtextdomain (PACKAGE, LOCALEDIR);
@@ -387,28 +420,8 @@ main(int argc, char **argv)
387 * that before pointer magic (esp. on network data) */ 420 * that before pointer magic (esp. on network data) */
388 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0; 421 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
389 422
390 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) 423 address_family = -1;
391 sockets |= HAVE_ICMP; 424 int icmp_proto = IPPROTO_ICMP;
392 else icmp_sockerrno = errno;
393
394 /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */
395 /* sockets |= HAVE_UDP; */
396 /* else udp_sockerrno = errno; */
397
398 /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */
399 /* sockets |= HAVE_TCP; */
400 /* else tcp_sockerrno = errno; */
401
402 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
403 setuid(getuid());
404
405 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
406 environ = NULL;
407
408 /* use the pid to mark packets as ours */
409 /* Some systems have 32-bit pid_t so mask off only 16 bits */
410 pid = getpid() & 0xffff;
411 /* printf("pid = %u\n", pid); */
412 425
413 /* get calling name the old-fashioned way for portability instead 426 /* get calling name the old-fashioned way for portability instead
414 * of relying on the glibc-ism __progname */ 427 * of relying on the glibc-ism __progname */
@@ -448,33 +461,48 @@ main(int argc, char **argv)
448 packets = 5; 461 packets = 5;
449 } 462 }
450 463
451 /* Parse extra opts if any */ 464 /* Parse protocol arguments first */
452 argv=np_extra_opts(&argc, argv, progname); 465 for(i = 1; i < argc; i++) {
453 466 while((arg = getopt(argc, argv, opts_str)) != EOF) {
454 /* support "--help" and "--version" */ 467 unsigned short size;
455 if(argc == 2) { 468 switch(arg) {
456 if(!strcmp(argv[1], "--help")) 469 case '4':
457 strcpy(argv[1], "-h"); 470 if (address_family != -1)
458 if(!strcmp(argv[1], "--version")) 471 crash("Multiple protocol versions not supported");
459 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 }
460 } 485 }
461 486
487 /* Reset argument scanning */
488 optind = 1;
489
462 /* parse the arguments */ 490 /* parse the arguments */
463 for(i = 1; i < argc; i++) { 491 for(i = 1; i < argc; i++) {
464 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) {
465 long size; 493 unsigned short size;
466 switch(arg) { 494 switch(arg) {
467 case 'v': 495 case 'v':
468 debug++; 496 debug++;
469 break; 497 break;
470 case 'b': 498 case 'b':
471 size = strtol(optarg,NULL,0); 499 size = (unsigned short)strtol(optarg,NULL,0);
472 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && 500 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
473 size < MAX_PING_DATA) { 501 size < MAX_PING_DATA) {
474 icmp_data_size = size; 502 icmp_data_size = size;
475 icmp_pkt_size = size + ICMP_MINLEN; 503 icmp_pkt_size = size + ICMP_MINLEN;
476 } else 504 } else
477 usage_va("ICMP data length must be between: %d and %d", 505 usage_va("ICMP data length must be between: %lu and %lu",
478 sizeof(struct icmp) + sizeof(struct icmp_ping_data), 506 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
479 MAX_PING_DATA - 1); 507 MAX_PING_DATA - 1);
480 break; 508 break;
@@ -518,14 +546,34 @@ main(int argc, char **argv)
518 break; 546 break;
519 case 'V': /* version */ 547 case 'V': /* version */
520 print_revision (progname, NP_VERSION); 548 print_revision (progname, NP_VERSION);
521 exit (STATE_OK); 549 exit (STATE_UNKNOWN);
522 case 'h': /* help */ 550 case 'h': /* help */
523 print_help (); 551 print_help ();
524 exit (STATE_OK); 552 exit (STATE_UNKNOWN);
553 break;
525 } 554 }
526 } 555 }
527 } 556 }
528 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
529 argv = &argv[optind]; 577 argv = &argv[optind];
530 while(*argv) { 578 while(*argv) {
531 add_target(*argv); 579 add_target(*argv);
@@ -537,6 +585,30 @@ main(int argc, char **argv)
537 exit(3); 585 exit(3);
538 } 586 }
539 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
540 if(!sockets) { 612 if(!sockets) {
541 if(icmp_sock == -1) { 613 if(icmp_sock == -1) {
542 errno = icmp_sockerrno; 614 errno = icmp_sockerrno;
@@ -600,7 +672,7 @@ main(int argc, char **argv)
600 if(max_completion_time > (u_int)timeout * 1000000) { 672 if(max_completion_time > (u_int)timeout * 1000000) {
601 printf("max_completion_time: %llu timeout: %u\n", 673 printf("max_completion_time: %llu timeout: %u\n",
602 max_completion_time, timeout); 674 max_completion_time, timeout);
603 printf("Timout must be at lest %llu\n", 675 printf("Timeout must be at least %llu\n",
604 max_completion_time / 1000000 + 1); 676 max_completion_time / 1000000 + 1);
605 } 677 }
606 } 678 }
@@ -625,7 +697,7 @@ main(int argc, char **argv)
625 } 697 }
626 698
627 host = list; 699 host = list;
628 table = malloc(sizeof(struct rta_host **) * targets); 700 table = (struct rta_host**)malloc(sizeof(struct rta_host **) * targets);
629 i = 0; 701 i = 0;
630 while(host) { 702 while(host) {
631 host->id = i*packets; 703 host->id = i*packets;
@@ -689,9 +761,15 @@ run_checks()
689 } 761 }
690} 762}
691 763
764
692/* response structure: 765/* response structure:
766 * IPv4:
693 * ip header : 20 bytes 767 * ip header : 20 bytes
694 * icmp header : 28 bytes 768 * icmp header : 28 bytes
769 * IPv6:
770 * ip header : 40 bytes
771 * icmp header : 28 bytes
772 * both:
695 * icmp echo reply : the rest 773 * icmp echo reply : the rest
696 */ 774 */
697static int 775static int
@@ -699,16 +777,27 @@ wait_for_reply(int sock, u_int t)
699{ 777{
700 int n, hlen; 778 int n, hlen;
701 static unsigned char buf[4096]; 779 static unsigned char buf[4096];
702 struct sockaddr_in resp_addr; 780 struct sockaddr_storage resp_addr;
703 struct ip *ip; 781 union ip_hdr *ip;
704 struct icmp icp; 782 union icmp_packet packet;
705 struct rta_host *host; 783 struct rta_host *host;
706 struct icmp_ping_data data; 784 struct icmp_ping_data data;
707 struct timeval wait_start, now; 785 struct timeval wait_start, now;
708 u_int tdiff, i, per_pkt_wait; 786 u_int tdiff, i, per_pkt_wait;
709 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
710 /* 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 */
711 if(!t || !icmp_pkts_en_route) return 0; 797 if(!t || !icmp_pkts_en_route) {
798 free(packet.buf);
799 return 0;
800 }
712 801
713 gettimeofday(&wait_start, &tz); 802 gettimeofday(&wait_start, &tz);
714 803
@@ -727,7 +816,7 @@ wait_for_reply(int sock, u_int t)
727 816
728 /* reap responses until we hit a timeout */ 817 /* reap responses until we hit a timeout */
729 n = recvfrom_wto(sock, buf, sizeof(buf), 818 n = recvfrom_wto(sock, buf, sizeof(buf),
730 (struct sockaddr *)&resp_addr, &t); 819 (struct sockaddr *)&resp_addr, &t, &now);
731 if(!n) { 820 if(!n) {
732 if(debug > 1) { 821 if(debug > 1) {
733 printf("recvfrom_wto() timed out during a %u usecs wait\n", 822 printf("recvfrom_wto() timed out during a %u usecs wait\n",
@@ -737,12 +826,23 @@ wait_for_reply(int sock, u_int t)
737 } 826 }
738 if(n < 0) { 827 if(n < 0) {
739 if(debug) printf("recvfrom_wto() returned errors\n"); 828 if(debug) printf("recvfrom_wto() returned errors\n");
829 free(packet.buf);
740 return n; 830 return n;
741 } 831 }
742 832
743 ip = (struct ip *)buf; 833 // FIXME: with ipv6 we don't have an ip header here
744 if(debug > 1) printf("received %u bytes from %s\n", 834 if (address_family != AF_INET6) {
745 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 }
746 846
747/* 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 */
748/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 848/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
@@ -751,12 +851,14 @@ wait_for_reply(int sock, u_int t)
751 * off the bottom 4 bits */ 851 * off the bottom 4 bits */
752/* hlen = (ip->ip_vhl & 0x0f) << 2; */ 852/* hlen = (ip->ip_vhl & 0x0f) << 2; */
753/* #else */ 853/* #else */
754 hlen = ip->ip_hl << 2; 854 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
755/* #endif */ 855/* #endif */
756 856
757 if(n < (hlen + ICMP_MINLEN)) { 857 if(n < (hlen + ICMP_MINLEN)) {
858 char address[INET6_ADDRSTRLEN];
859 parse_address(&resp_addr, address, sizeof(address));
758 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",
759 n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr)); 861 n, hlen + icmp_pkt_size, address);
760 } 862 }
761 /* else if(debug) { */ 863 /* else if(debug) { */
762 /* 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", */
@@ -765,23 +867,39 @@ wait_for_reply(int sock, u_int t)
765 /* } */ 867 /* } */
766 868
767 /* check the response */ 869 /* check the response */
768 memcpy(&icp, buf + hlen, sizeof(icp));
769 870
770 if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || 871 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
771 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))) {
772 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); 881 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
773 handle_random_icmp(buf + hlen, &resp_addr); 882 handle_random_icmp(buf + hlen, &resp_addr);
774 continue; 883 continue;
775 } 884 }
776 885
777 /* this is indeed a valid response */ 886 /* this is indeed a valid response */
778 memcpy(&data, icp.icmp_data, sizeof(data)); 887 if (address_family == PF_INET) {
779 if (debug > 2) 888 memcpy(&data, packet.icp->icmp_data, sizeof(data));
780 printf("ICMP echo-reply of len %u, id %u, seq %u, cksum 0x%X\n", 889 if (debug > 2)
781 sizeof(data), ntohs(icp.icmp_id), ntohs(icp.icmp_seq), icp.icmp_cksum); 890 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
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 }
782 902
783 host = table[ntohs(icp.icmp_seq)/packets];
784 gettimeofday(&now, &tz);
785 tdiff = get_timevaldiff(&data.stime, &now); 903 tdiff = get_timevaldiff(&data.stime, &now);
786 904
787 host->time_waited += tdiff; 905 host->time_waited += tdiff;
@@ -793,22 +911,25 @@ wait_for_reply(int sock, u_int t)
793 host->rtmin = tdiff; 911 host->rtmin = tdiff;
794 912
795 if(debug) { 913 if(debug) {
914 char address[INET6_ADDRSTRLEN];
915 parse_address(&resp_addr, address, sizeof(address));
796 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",
797 (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr), 917 (float)tdiff / 1000, address,
798 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);
799 } 919 }
800 920
801 /* if we're in hostcheck mode, exit with limited printouts */ 921 /* if we're in hostcheck mode, exit with limited printouts */
802 if(mode == MODE_HOSTCHECK) { 922 if(mode == MODE_HOSTCHECK) {
803 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|" 923 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
804 "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",
805 host->name, icmp_recv, (float)tdiff / 1000, 925 host->name, icmp_recv, (float)tdiff / 1000,
806 icmp_recv, packets, (float)tdiff / 1000, 926 icmp_recv, packets, (float)tdiff / 1000,
807 (float)warn.rta / 1000, (float)crit.rta / 1000); 927 (float)warn.rta / 1000, (float)crit.rta / 1000);
808 exit(STATE_OK); 928 exit(STATE_OK);
809 } 929 }
810 } 930 }
811 931
932 free(packet.buf);
812 return 0; 933 return 0;
813} 934}
814 935
@@ -816,54 +937,97 @@ wait_for_reply(int sock, u_int t)
816static int 937static int
817send_icmp_ping(int sock, struct rta_host *host) 938send_icmp_ping(int sock, struct rta_host *host)
818{ 939{
819 static union {
820 void *buf; /* re-use so we prevent leaks */
821 struct icmp *icp;
822 u_short *cksum_in;
823 } packet = { NULL };
824 long int len; 940 long int len;
825 struct icmp_ping_data data; 941 struct icmp_ping_data data;
942 struct msghdr hdr;
943 struct iovec iov;
826 struct timeval tv; 944 struct timeval tv;
827 struct sockaddr *addr; 945 void *buf = NULL;
828 946
829 if(sock == -1) { 947 if(sock == -1) {
830 errno = 0; 948 errno = 0;
831 crash("Attempt to send on bogus socket"); 949 crash("Attempt to send on bogus socket");
832 return -1; 950 return -1;
833 } 951 }
834 addr = (struct sockaddr *)&host->saddr_in;
835 952
836 if(!packet.buf) { 953 if(!buf) {
837 if (!(packet.buf = malloc(icmp_pkt_size))) { 954 if (!(buf = malloc(icmp_pkt_size))) {
838 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",
839 icmp_pkt_size); 956 icmp_pkt_size);
840 return -1; /* might be reached if we're in debug mode */ 957 return -1; /* might be reached if we're in debug mode */
841 } 958 }
842 } 959 }
843 memset(packet.buf, 0, icmp_pkt_size); 960 memset(buf, 0, icmp_pkt_size);
844 961
845 if((gettimeofday(&tv, &tz)) == -1) return -1; 962 if((gettimeofday(&tv, &tz)) == -1) {
963 free(buf);
964 return -1;
965 }
846 966
847 data.ping_id = 10; /* host->icmp.icmp_sent; */ 967 data.ping_id = 10; /* host->icmp.icmp_sent; */
848 memcpy(&data.stime, &tv, sizeof(tv)); 968 memcpy(&data.stime, &tv, sizeof(tv));
849 memcpy(&packet.icp->icmp_data, &data, sizeof(data));
850 packet.icp->icmp_type = ICMP_ECHO;
851 packet.icp->icmp_code = 0;
852 packet.icp->icmp_cksum = 0;
853 packet.icp->icmp_id = htons(pid);
854 packet.icp->icmp_seq = htons(host->id++);
855 packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size);
856 969
857 if (debug > 2) 970 if (address_family == AF_INET) {
858 printf("Sending ICMP echo-request of len %u, id %u, seq %u, cksum 0x%X to host %s\n", 971 struct icmp *icp = (struct icmp*)buf;
859 sizeof(data), ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, host->name); 972
973 memcpy(&icp->icmp_data, &data, sizeof(data));
974
975 icp->icmp_type = ICMP_ECHO;
976 icp->icmp_code = 0;
977 icp->icmp_cksum = 0;
978 icp->icmp_id = htons(pid);
979 icp->icmp_seq = htons(host->id++);
980 icp->icmp_cksum = icmp_checksum((unsigned short*)buf, icmp_pkt_size);
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 }
1002
1003 memset(&iov, 0, sizeof(iov));
1004 iov.iov_base = buf;
1005 iov.iov_len = icmp_pkt_size;
1006
1007 memset(&hdr, 0, sizeof(hdr));
1008 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
1009 hdr.msg_namelen = sizeof(struct sockaddr_storage);
1010 hdr.msg_iov = &iov;
1011 hdr.msg_iovlen = 1;
1012
1013 errno = 0;
1014
1015/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1016#ifdef MSG_CONFIRM
1017 len = sendmsg(sock, &hdr, MSG_CONFIRM);
1018#else
1019 len = sendmsg(sock, &hdr, 0);
1020#endif
860 1021
861 len = sendto(sock, packet.buf, icmp_pkt_size, 0, (struct sockaddr *)addr, 1022 free(buf);
862 sizeof(struct sockaddr));
863 1023
864 if(len < 0 || (unsigned int)len != icmp_pkt_size) { 1024 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
865 if(debug) printf("Failed to send ping to %s\n", 1025 if(debug) {
866 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;
867 return -1; 1031 return -1;
868 } 1032 }
869 1033
@@ -875,12 +1039,18 @@ send_icmp_ping(int sock, struct rta_host *host)
875 1039
876static int 1040static int
877recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, 1041recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
878 u_int *timo) 1042 u_int *timo, struct timeval* tv)
879{ 1043{
880 u_int slen; 1044 u_int slen;
881 int n; 1045 int n, ret;
882 struct timeval to, then, now; 1046 struct timeval to, then, now;
883 fd_set rd, wr; 1047 fd_set rd, wr;
1048 char ans_data[4096];
1049 struct msghdr hdr;
1050 struct iovec iov;
1051#ifdef SO_TIMESTAMP
1052 struct cmsghdr* chdr;
1053#endif
884 1054
885 if(!*timo) { 1055 if(!*timo) {
886 if(debug) printf("*timo is not\n"); 1056 if(debug) printf("*timo is not\n");
@@ -902,9 +1072,35 @@ recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
902 1072
903 if(!n) return 0; /* timeout */ 1073 if(!n) return 0; /* timeout */
904 1074
905 slen = sizeof(struct sockaddr); 1075 slen = sizeof(struct sockaddr_storage);
1076
1077 memset(&iov, 0, sizeof(iov));
1078 iov.iov_base = buf;
1079 iov.iov_len = len;
1080
1081 memset(&hdr, 0, sizeof(hdr));
1082 hdr.msg_name = saddr;
1083 hdr.msg_namelen = slen;
1084 hdr.msg_iov = &iov;
1085 hdr.msg_iovlen = 1;
1086 hdr.msg_control = ans_data;
1087 hdr.msg_controllen = sizeof(ans_data);
1088
1089 ret = recvmsg(sock, &hdr, 0);
1090#ifdef SO_TIMESTAMP
1091 for(chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1092 if(chdr->cmsg_level == SOL_SOCKET
1093 && chdr->cmsg_type == SO_TIMESTAMP
1094 && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1095 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv));
1096 break ;
1097 }
1098 }
906 1099
907 return recvfrom(sock, buf, len, 0, saddr, &slen); 1100 if (!chdr)
1101#endif // SO_TIMESTAMP
1102 gettimeofday(tv, &tz);
1103 return (ret);
908} 1104}
909 1105
910static void 1106static void
@@ -934,10 +1130,11 @@ finish(int sig)
934 1130
935 /* iterate thrice to calculate values, give output, and print perfparse */ 1131 /* iterate thrice to calculate values, give output, and print perfparse */
936 host = list; 1132 host = list;
1133
937 while(host) { 1134 while(host) {
938 if(!host->icmp_recv) { 1135 if(!host->icmp_recv) {
939 /* 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
940 * conspicuosly as missing entries in perfparse and cacti */ 1137 * conspicuously as missing entries in perfparse and cacti */
941 pl = 100; 1138 pl = 100;
942 rta = 0; 1139 rta = 0;
943 status = STATE_CRITICAL; 1140 status = STATE_CRITICAL;
@@ -982,10 +1179,12 @@ finish(int sig)
982 if(!host->icmp_recv) { 1179 if(!host->icmp_recv) {
983 status = STATE_CRITICAL; 1180 status = STATE_CRITICAL;
984 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));
985 printf("%s: %s @ %s. rta nan, lost %d%%", 1184 printf("%s: %s @ %s. rta nan, lost %d%%",
986 host->name, 1185 host->name,
987 get_icmp_error_msg(host->icmp_type, host->icmp_code), 1186 get_icmp_error_msg(host->icmp_type, host->icmp_code),
988 inet_ntoa(host->error_addr), 1187 address,
989 100); 1188 100);
990 } 1189 }
991 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 */
@@ -1047,7 +1246,6 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1047 { 1246 {
1048 return 0; 1247 return 0;
1049 } 1248 }
1050
1051 ret = (later->tv_sec - early->tv_sec) * 1000000; 1249 ret = (later->tv_sec - early->tv_sec) * 1000000;
1052 ret += later->tv_usec - early->tv_usec; 1250 ret += later->tv_usec - early->tv_usec;
1053 1251
@@ -1055,18 +1253,35 @@ get_timevaldiff(struct timeval *early, struct timeval *later)
1055} 1253}
1056 1254
1057static int 1255static int
1058add_target_ip(char *arg, struct in_addr *in) 1256add_target_ip(char *arg, struct sockaddr_storage *in)
1059{ 1257{
1060 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;
1061 1266
1062 /* disregard obviously stupid addresses */ 1267
1063 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))) {
1064 return -1; 1274 return -1;
1275 }
1065 1276
1066 /* no point in adding two identical IP's, so don't. ;) */ 1277 /* no point in adding two identical IP's, so don't. ;) */
1067 host = list; 1278 host = list;
1068 while(host) { 1279 while(host) {
1069 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)) {
1070 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);
1071 return -1; 1286 return -1;
1072 } 1287 }
@@ -1074,19 +1289,29 @@ add_target_ip(char *arg, struct in_addr *in)
1074 } 1289 }
1075 1290
1076 /* add the fresh ip */ 1291 /* add the fresh ip */
1077 host = malloc(sizeof(struct rta_host)); 1292 host = (struct rta_host*)malloc(sizeof(struct rta_host));
1078 if(!host) { 1293 if(!host) {
1079 crash("add_target_ip(%s, %s): malloc(%d) failed", 1294 char straddr[INET6_ADDRSTRLEN];
1080 arg, inet_ntoa(*in), sizeof(struct rta_host)); 1295 parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
1296 crash("add_target_ip(%s, %s): malloc(%lu) failed",
1297 arg, straddr, sizeof(struct rta_host));
1081 } 1298 }
1082 memset(host, 0, sizeof(struct rta_host)); 1299 memset(host, 0, sizeof(struct rta_host));
1083 1300
1084 /* set the values. use calling name for output */ 1301 /* set the values. use calling name for output */
1085 host->name = strdup(arg); 1302 host->name = strdup(arg);
1086 1303
1087 /* fill out the sockaddr_in struct */ 1304 /* fill out the sockaddr_storage struct */
1088 host->saddr_in.sin_family = AF_INET; 1305 if(address_family == AF_INET) {
1089 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 }
1090 1315
1091 host->rtmin = DBL_MAX; 1316 host->rtmin = DBL_MAX;
1092 1317
@@ -1103,31 +1328,67 @@ add_target_ip(char *arg, struct in_addr *in)
1103static int 1328static int
1104add_target(char *arg) 1329add_target(char *arg)
1105{ 1330{
1106 int i; 1331 int error, result;
1107 struct hostent *he; 1332 struct sockaddr_storage ip;
1108 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 }
1109 1365
1110 /* don't resolve if we don't have to */ 1366 /* don't resolve if we don't have to */
1111 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) { 1367 if(result == 1) {
1112 /* 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 */
1113 return add_target_ip(arg, &ip); 1369 return add_target_ip(arg, &ip);
1114 /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */
1115 /* if(!he) return add_target_ip(arg, in); */
1116 } 1370 }
1117 else { 1371 else {
1118 errno = 0; 1372 errno = 0;
1119 he = gethostbyname(arg); 1373 memset(&hints, 0, sizeof(hints));
1120 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) {
1121 errno = 0; 1381 errno = 0;
1122 crash("Failed to resolve %s", arg); 1382 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1123 return -1; 1383 return -1;
1124 } 1384 }
1385 address_family = res->ai_family;
1125 } 1386 }
1126 1387
1127 /* possibly add all the IP's as targets */ 1388 /* possibly add all the IP's as targets */
1128 for(i = 0; he->h_addr_list[i]; i++) { 1389 for(p = res; p != NULL; p = p->ai_next) {
1129 in = (struct in_addr *)he->h_addr_list[i]; 1390 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1130 add_target_ip(arg, in); 1391 add_target_ip(arg, &ip);
1131 1392
1132 /* this is silly, but it works */ 1393 /* this is silly, but it works */
1133 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1394 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1136,6 +1397,7 @@ add_target(char *arg)
1136 } 1397 }
1137 break; 1398 break;
1138 } 1399 }
1400 freeaddrinfo(res);
1139 1401
1140 return 0; 1402 return 0;
1141} 1403}
@@ -1146,7 +1408,7 @@ set_source_ip(char *arg)
1146 struct sockaddr_in src; 1408 struct sockaddr_in src;
1147 1409
1148 memset(&src, 0, sizeof(src)); 1410 memset(&src, 0, sizeof(src));
1149 src.sin_family = AF_INET; 1411 src.sin_family = address_family;
1150 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) 1412 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1151 src.sin_addr.s_addr = get_ip_address(arg); 1413 src.sin_addr.s_addr = get_ip_address(arg);
1152 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) 1414 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
@@ -1183,7 +1445,7 @@ static u_int
1183get_timevar(const char *str) 1445get_timevar(const char *str)
1184{ 1446{
1185 char p, u, *ptr; 1447 char p, u, *ptr;
1186 unsigned int len; 1448 size_t len;
1187 u_int i, d; /* integer and decimal, respectively */ 1449 u_int i, d; /* integer and decimal, respectively */
1188 u_int factor = 1000; /* default to milliseconds */ 1450 u_int factor = 1000; /* default to milliseconds */
1189 1451
@@ -1254,12 +1516,12 @@ get_threshold(char *str, threshold *th)
1254unsigned short 1516unsigned short
1255icmp_checksum(unsigned short *p, int n) 1517icmp_checksum(unsigned short *p, int n)
1256{ 1518{
1257 register unsigned short cksum; 1519 unsigned short cksum;
1258 register long sum = 0; 1520 long sum = 0;
1259 1521
1260 while(n > 1) { 1522 while(n > 2) {
1261 sum += *p++; 1523 sum += *p++;
1262 n -= 2; 1524 n -= sizeof(unsigned short);
1263 } 1525 }
1264 1526
1265 /* mop up the occasional odd byte */ 1527 /* mop up the occasional odd byte */
@@ -1290,6 +1552,8 @@ print_help(void)
1290 1552
1291 printf (" %s\n", "-H"); 1553 printf (" %s\n", "-H");
1292 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"));
1293 printf (" %s\n", "-w"); 1557 printf (" %s\n", "-w");
1294 printf (" %s", _("warning threshold (currently ")); 1558 printf (" %s", _("warning threshold (currently "));
1295 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 1559 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);