X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=plugins-root%2Fcheck_icmp.c;h=5f0224d54ec863317aa113bd9f1d3fed8a7076a3;hb=884aee06674755ef44e4042803ae84a0faac5b10;hp=700fe8b689516ab0ef0a75f083eaac54b899a418;hpb=aa215f26d9617c9c63f82ab690641fa99f1471b1;p=nagiosplug.git diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c index 700fe8b..5f0224d 100644 --- a/plugins-root/check_icmp.c +++ b/plugins-root/check_icmp.c @@ -1,52 +1,45 @@ - /****************************************************************************** -* +/***************************************************************************** +* * Nagios check_icmp plugin -* +* * License: GPL -* Copyright (c) 2005-2007 nagios-plugins team -* +* Copyright (c) 2005-2008 Nagios Plugins Development Team * Original Author : Andreas Ericsson -* -* Last Modified: $Date$ -* +* * Description: -* +* * This file contains the check_icmp plugin -* -* Relevant RFC's: 792 (ICMP), 791 (IP) -* -* This program was modeled somewhat after the check_icmp program, -* which was in turn a hack of fping (www.fping.org) but has been -* completely rewritten since to generate higher precision rta values, -* and support several different modes as well as setting ttl to control. -* redundant routes. The only remainders of fping is currently a few -* function names. -* -* License Information: -* -* This program is free software; you can redistribute it and/or modify +* +* Relevant RFC's: 792 (ICMP), 791 (IP) +* +* This program was modeled somewhat after the check_icmp program, +* which was in turn a hack of fping (www.fping.org) but has been +* completely rewritten since to generate higher precision rta values, +* and support several different modes as well as setting ttl to control. +* redundant routes. The only remainders of fping is currently a few +* function names. +* +* +* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or +* the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. -* +* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. -* +* * You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -* -* $Id$ +* along with this program. If not, see . +* * *****************************************************************************/ /* progname may change */ /* char *progname = "check_icmp"; */ char *progname; -const char *revision = "$Revision$"; -const char *copyright = "2005-2007"; +const char *copyright = "2005-2008"; const char *email = "nagiosplug-devel@lists.sourceforge.net"; /** nagios plugins basic includes */ @@ -77,6 +70,7 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; #include #include #include +#include /** sometimes undefined system macros (quite a few, actually) **/ @@ -109,6 +103,9 @@ const char *email = "nagiosplug-devel@lists.sourceforge.net"; # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 #endif +#ifndef DBL_MAX +# define DBL_MAX 9.9999999999e999 +#endif typedef unsigned short range_t; /* type for get_range() -- unimplemented */ @@ -123,6 +120,8 @@ typedef struct rta_host { unsigned char icmp_type, icmp_code; /* type and code from errors */ unsigned short flags; /* control/status flags */ double rta; /* measured RTA */ + double rtmax; /* max rtt */ + double rtmin; /* min rtt */ unsigned char pl; /* measured packet loss */ struct rta_host *next; /* linked list */ } rta_host; @@ -185,14 +184,14 @@ static u_int get_timevar(const char *); static u_int get_timevaldiff(struct timeval *, struct timeval *); static in_addr_t get_ip_address(const char *); static int wait_for_reply(int, u_int); -static int recvfrom_wto(int, char *, unsigned int, struct sockaddr *, u_int *); +static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *); static int send_icmp_ping(int, struct rta_host *); static int get_threshold(char *str, threshold *th); static void run_checks(void); static void set_source_ip(char *); static int add_target(char *); static int add_target_ip(char *, struct in_addr *); -static int handle_random_icmp(struct icmp *, struct sockaddr_in *); +static int handle_random_icmp(unsigned char *, struct sockaddr_in *); static unsigned short icmp_checksum(unsigned short *, int); static void finish(int); static void crash(const char *, ...); @@ -206,7 +205,9 @@ extern char **environ; static struct rta_host **table, *cursor, *list; static threshold crit = {80, 500000}, warn = {40, 200000}; static int mode, protocols, sockets, debug = 0, timeout = 10; -static unsigned short icmp_pkt_size, icmp_data_size = DEFAULT_PING_DATA_SIZE; +static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; +static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; + static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0; #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) static unsigned short targets_down = 0, targets = 0, packets = 0; @@ -299,19 +300,18 @@ get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) } static int -handle_random_icmp(struct icmp *p, struct sockaddr_in *addr) +handle_random_icmp(unsigned char *packet, struct sockaddr_in *addr) { - struct icmp sent_icmp; + struct icmp p, sent_icmp; struct rta_host *host = NULL; - unsigned char *ptr; - if(p->icmp_type == ICMP_ECHO && p->icmp_id == pid) { + memcpy(&p, packet, sizeof(p)); + if(p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { /* echo request from us to us (pinging localhost) */ return 0; } - ptr = (unsigned char *)p; - if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)p, (void *)addr); + if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); /* only handle a few types, since others can't possibly be replies to * us in a sane network (if it is anyway, it will be counted as lost @@ -323,27 +323,27 @@ handle_random_icmp(struct icmp *p, struct sockaddr_in *addr) * TIMXCEED actually sends a proper icmp response we will have passed * too many hops to have a hope of reaching it later, in which case it * indicates overconfidence in the network, poor routing or both. */ - if(p->icmp_type != ICMP_UNREACH && p->icmp_type != ICMP_TIMXCEED && - p->icmp_type != ICMP_SOURCEQUENCH && p->icmp_type != ICMP_PARAMPROB) + if(p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && + p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { return 0; } /* might be for us. At least it holds the original package (according * to RFC 792). If it isn't, just ignore it */ - memcpy(&sent_icmp, ptr + 28, sizeof(sent_icmp)); - if(sent_icmp.icmp_type != ICMP_ECHO || sent_icmp.icmp_id != pid || - sent_icmp.icmp_seq >= targets) + memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); + if(sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || + ntohs(sent_icmp.icmp_seq) >= targets*packets) { if(debug) printf("Packet is no response to a packet we sent\n"); return 0; } /* it is indeed a response for us */ - host = table[sent_icmp.icmp_seq]; + host = table[ntohs(sent_icmp.icmp_seq)/packets]; if(debug) { printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", - get_icmp_error_msg(p->icmp_type, p->icmp_code), + get_icmp_error_msg(p.icmp_type, p.icmp_code), inet_ntoa(addr->sin_addr), host->name); } @@ -354,7 +354,7 @@ handle_random_icmp(struct icmp *p, struct sockaddr_in *addr) /* source quench means we're sending too fast, so increase the * interval and mark this packet lost */ - if(p->icmp_type == ICMP_SOURCEQUENCH) { + if(p.icmp_type == ICMP_SOURCEQUENCH) { pkt_interval *= pkt_backoff_factor; target_interval *= target_backoff_factor; } @@ -362,8 +362,8 @@ handle_random_icmp(struct icmp *p, struct sockaddr_in *addr) targets_down++; host->flags |= FLAG_LOST_CAUSE; } - host->icmp_type = p->icmp_type; - host->icmp_code = p->icmp_code; + host->icmp_type = p.icmp_type; + host->icmp_code = p.icmp_code; host->error_addr.s_addr = addr->sin_addr.s_addr; return 0; @@ -382,7 +382,7 @@ main(int argc, char **argv) setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); - + /* print a helpful error message if geteuid != 0 */ np_warn_if_not_root(); @@ -451,15 +451,28 @@ main(int argc, char **argv) packets = 5; } + /* Parse extra opts if any */ + argv=np_extra_opts(&argc, argv, progname); + /* parse the arguments */ for(i = 1; i < argc; i++) { while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:s:i:b:I:l:m:")) != EOF) { + long size; switch(arg) { case 'v': debug++; break; case 'b': - /* silently ignored for now */ + size = strtol(optarg,NULL,0); + if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && + size < MAX_PING_DATA) { + icmp_data_size = size; + icmp_pkt_size = size + ICMP_MINLEN; + } else + usage_va("ICMP data length must be between: %d and %d", + sizeof(struct icmp) + sizeof(struct icmp_ping_data), + MAX_PING_DATA - 1); + break; case 'i': pkt_interval = get_timevar(optarg); @@ -500,7 +513,7 @@ main(int argc, char **argv) set_source_ip(optarg); break; case 'V': /* version */ - /*print_revision (progname, revision);*/ /* FIXME: Why? */ + print_revision (progname, NP_VERSION); exit (STATE_OK); case 'h': /* help */ print_help (); @@ -588,13 +601,6 @@ main(int argc, char **argv) } } - icmp_pkt_size = icmp_data_size + ICMP_MINLEN; - if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size); - if(icmp_pkt_size < sizeof(struct icmp) + sizeof(struct icmp_ping_data)) { - icmp_pkt_size = sizeof(struct icmp) + sizeof(struct icmp_ping_data); - } - if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size); - if(debug) { printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); @@ -618,7 +624,7 @@ main(int argc, char **argv) table = malloc(sizeof(struct rta_host **) * (argc - 1)); i = 0; while(host) { - host->id = i; + host->id = i*packets; table[i] = host; host = host->next; i++; @@ -650,7 +656,7 @@ run_checks() table[t]->name); continue; } - + /* we're still in the game, so send next packet */ (void)send_icmp_ping(icmp_sock, table[t]); result = wait_for_reply(icmp_sock, target_interval); @@ -688,7 +694,7 @@ static int wait_for_reply(int sock, u_int t) { int n, hlen; - static char buf[4096]; + static unsigned char buf[4096]; struct sockaddr_in resp_addr; struct ip *ip; struct icmp icp; @@ -757,32 +763,35 @@ wait_for_reply(int sock, u_int t) /* check the response */ memcpy(&icp, buf + hlen, sizeof(icp)); - if(icp.icmp_id != pid) { - handle_random_icmp(&icp, &resp_addr); - continue; - } - - if(icp.icmp_type != ICMP_ECHOREPLY || icp.icmp_seq >= targets) { + if(ntohs(icp.icmp_id) != pid || icp.icmp_type != ICMP_ECHOREPLY || + ntohs(icp.icmp_seq) >= targets*packets) { if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n"); - handle_random_icmp(&icp, &resp_addr); + handle_random_icmp(buf + hlen, &resp_addr); continue; } /* this is indeed a valid response */ memcpy(&data, icp.icmp_data, sizeof(data)); + if (debug > 2) + printf("ICMP echo-reply of len %u, id %u, seq %u, cksum 0x%X\n", + sizeof(data), ntohs(icp.icmp_id), ntohs(icp.icmp_seq), icp.icmp_cksum); - host = table[icp.icmp_seq]; + host = table[ntohs(icp.icmp_seq)/packets]; gettimeofday(&now, &tz); tdiff = get_timevaldiff(&data.stime, &now); host->time_waited += tdiff; host->icmp_recv++; icmp_recv++; + if (tdiff > host->rtmax) + host->rtmax = tdiff; + if (tdiff < host->rtmin) + host->rtmin = tdiff; if(debug) { - printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u\n", + printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr), - ttl, ip->ip_ttl); + ttl, ip->ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); } /* if we're in hostcheck mode, exit with limited printouts */ @@ -804,7 +813,7 @@ static int send_icmp_ping(int sock, struct rta_host *host) { static union { - char *buf; /* re-use so we prevent leaks */ + void *buf; /* re-use so we prevent leaks */ struct icmp *icp; u_short *cksum_in; } packet = { NULL }; @@ -837,10 +846,14 @@ send_icmp_ping(int sock, struct rta_host *host) packet.icp->icmp_type = ICMP_ECHO; packet.icp->icmp_code = 0; packet.icp->icmp_cksum = 0; - packet.icp->icmp_id = pid; - packet.icp->icmp_seq = host->id; + packet.icp->icmp_id = htons(pid); + packet.icp->icmp_seq = htons(host->id++); packet.icp->icmp_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size); + if (debug > 2) + printf("Sending ICMP echo-request of len %u, id %u, seq %u, cksum 0x%X to host %s\n", + sizeof(data), ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum, host->name); + len = sendto(sock, packet.buf, icmp_pkt_size, 0, (struct sockaddr *)addr, sizeof(struct sockaddr)); @@ -857,7 +870,7 @@ send_icmp_ping(int sock, struct rta_host *host) } static int -recvfrom_wto(int sock, char *buf, unsigned int len, struct sockaddr *saddr, +recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo) { u_int slen; @@ -989,11 +1002,12 @@ finish(int sig) host = list; while(host) { if(debug) puts(""); - printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; ", + printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "", host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, - (targets > 1) ? host->name : "", - host->pl, warn.pl, crit.pl); + (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl, + (targets > 1) ? host->name : "", (float)host->rtmax / 1000, + (targets > 1) ? host->name : "", (host->rtmin < DBL_MAX) ? (float)host->rtmin / 1000 : (float)0); host = host->next; } @@ -1024,7 +1038,7 @@ get_timevaldiff(struct timeval *early, struct timeval *later) if(!early) early = &prog_start; /* if early > later we return 0 so as to indicate a timeout */ - if(early->tv_sec > early->tv_sec || + if(early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { return 0; @@ -1070,6 +1084,8 @@ add_target_ip(char *arg, struct in_addr *in) host->saddr_in.sin_family = AF_INET; host->saddr_in.sin_addr.s_addr = in->s_addr; + host->rtmin = DBL_MAX; + if(!list) list = cursor = host; else cursor->next = host; @@ -1111,7 +1127,7 @@ add_target(char *arg) /* this is silly, but it works */ if(mode == MODE_HOSTCHECK || mode == MODE_ALL) { - printf("mode: %d\n", mode); + if(debug > 2) printf("mode: %d\n", mode); continue; } break; @@ -1139,12 +1155,14 @@ get_ip_address(const char *ifname) { #if defined(SIOCGIFADDR) struct ifreq ifr; + struct sockaddr_in ip; strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1); ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0'; if(ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1) crash("Cannot determine IP address of interface %s", ifname); - return ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; + memcpy(&ip, &ifr.ifr_addr, sizeof(ip)); + return ip.sin_addr.s_addr; #else errno = 0; crash("Cannot get interface IP address on this platform."); @@ -1254,25 +1272,26 @@ void print_help(void) { - /*print_revision (progname, revision);*/ /* FIXME: Why? */ - + /*print_revision (progname);*/ /* FIXME: Why? */ + printf ("Copyright (c) 2005 Andreas Ericsson \n"); printf (COPYRIGHT, copyright, email); - + printf ("\n\n"); - + print_usage (); - - printf (_(UT_HELP_VRSN)); - + + printf (UT_HELP_VRSN); + printf (UT_EXTRA_OPTS); + printf (" %s\n", "-H"); printf (" %s\n", _("specify a target")); printf (" %s\n", "-w"); printf (" %s", _("warning threshold (currently ")); - printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000 , warn.pl / 1000); + printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); printf (" %s\n", "-c"); printf (" %s", _("critical threshold (currently ")); - printf ("%0.3fms,%u%%)\n", (float)crit.rta, crit.pl); + printf ("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); printf (" %s\n", "-s"); printf (" %s\n", _("specify a source IP address or device name")); printf (" %s\n", "-n"); @@ -1289,33 +1308,35 @@ print_help(void) printf ("\n"); printf (" %s\n", "-l"); printf (" %s", _("TTL on outgoing packets (currently ")); - printf ("%u)", ttl); + printf ("%u)\n", ttl); printf (" %s\n", "-t"); printf (" %s",_("timeout value (seconds, currently ")); printf ("%u)\n", timeout); printf (" %s\n", "-b"); - printf (" %s\n", _("icmp packet size (currenly ignored)")); + printf (" %s\n", _("Number of icmp data bytes to send")); + printf (" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"),icmp_data_size, ICMP_MINLEN); printf (" %s\n", "-v"); printf (" %s\n", _("verbose")); printf ("\n"); - printf ("%s\n\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); - printf ("%s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); - printf ("%s\n", _("packet loss. The default values should work well for most users.")); - printf ("%s\n", _("You can specify different RTA factors using the standardized abbreviations")); - printf ("%s\n\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); + printf ("%s\n", _("Notes:")); + printf (" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); + printf ("\n"); + printf (" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); + printf (" %s\n", _("packet loss. The default values should work well for most users.")); + printf (" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); + printf (" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); /* -d not yet implemented */ /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/ - printf ("%s\n\n", _("The -v switch can be specified several times for increased verbosity.")); - + printf ("\n"); + printf (" %s\n", _("The -v switch can be specified several times for increased verbosity.")); /* printf ("%s\n", _("Long options are currently unsupported.")); printf ("%s\n", _("Options marked with * require an argument")); */ - printf (_(UT_SUPPORT)); - - printf (_(UT_NOWARRANTY)); + + printf (UT_SUPPORT); } @@ -1324,5 +1345,5 @@ void print_usage (void) { printf (_("Usage:")); - printf(" %s [options] [-H] host1 host2 hostn\n", progname); + printf(" %s [options] [-H] host1 host2 hostN\n", progname); }