index 728d4a000649639098dae59b9531d18777026f94..1ddc7561947fa633e7d273eebd434f8d9a5afed3 100644 (file)
-/*
- * $Id$
- *
- * Author: Andreas Ericsson <ae@op5.se>
- *
- * License: GNU GPL 2.0 or any later version.
- *
- * 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.
- *
- */
+ /******************************************************************************
+*
+* Nagios check_icmp plugin
+*
+* License: GPL
+* Copyright (c) 2005-2007 nagios-plugins team
+*
+* Original Author : Andreas Ericsson <ae@op5.se>
+*
+* 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
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 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$
+*
+*****************************************************************************/
+
+/* progname may change */
+/* char *progname = "check_icmp"; */
+char *progname;
+const char *revision = "$Revision$";
+const char *copyright = "2005-2007";
+const char *email = "nagiosplug-devel@lists.sourceforge.net";
+
+/** nagios plugins basic includes */
+#include "common.h"
+#include "netutils.h"
+#include "utils.h"
+#if HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <netdb.h>
#include <sys/socket.h>
+#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <signal.h>
-#include "common.h"
-#include "runcmd.h"
-#include "utils.h"
-#include "regex.h"
/** sometimes undefined system macros (quite a few, actually) **/
#ifndef MAXTTL
# define MAXTTL 255
#endif
#ifndef INADDR_NONE
-# define INADDR_NONE 0xffffffU
+# define INADDR_NONE (in_addr_t)(-1)
#endif
#ifndef SOL_IP
#endif
-/** typedefs and such **/
-enum states {
- STATE_OK = 0,
- STATE_WARNING,
- STATE_CRITICAL,
- STATE_UNKNOWN,
- STATE_DEPENDENT,
- STATE_OOB
-};
-
typedef unsigned short range_t; /* type for get_range() -- unimplemented */
typedef struct rta_host {
#define TSTATE_UNREACH 0x08
/** prototypes **/
-static void usage(unsigned char, char *);
+void print_help (void);
+void print_usage (void);
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 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 *);
extern char **environ;
/** global variables **/
-static char *progname;
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 long long max_completion_time = 0;
static unsigned char ttl = 0; /* outgoing ttl */
static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
+static int min_hosts_alive = -1;
float pkt_backoff_factor = 1.5;
float target_backoff_factor = 1.5;
static int
handle_random_icmp(struct icmp *p, struct sockaddr_in *addr)
{
- struct icmp *sent_icmp = NULL;
+ struct icmp sent_icmp;
struct rta_host *host = NULL;
unsigned char *ptr;
/* might be for us. At least it holds the original package (according
* to RFC 792). If it isn't, just ignore it */
- sent_icmp = (struct icmp *)(ptr + 28);
- if(sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != pid ||
- sent_icmp->icmp_seq >= targets)
+ 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)
{
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[sent_icmp.icmp_seq];
if(debug) {
printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
get_icmp_error_msg(p->icmp_type, p->icmp_code),
int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
int result;
struct rta_host *host;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+ /* print a helpful error message if geteuid != 0 */
+ np_warn_if_not_root();
+
/* we only need to be setsuid when we get the sockets, so do
* that before pointer magic (esp. on network data) */
icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
environ = NULL;
/* use the pid to mark packets as ours */
- pid = getpid();
+ /* Some systems have 32-bit pid_t so mask off only 16 bits */
+ pid = getpid() & 0xffff;
/* printf("pid = %u\n", pid); */
/* get calling name the old-fashioned way for portability instead
/* parse the arguments */
for(i = 1; i < argc; i++) {
- while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:i:b:I:l:")) != EOF) {
+ while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:s:i:b:I:l:m:")) != EOF) {
switch(arg) {
case 'v':
debug++;
case 'l':
ttl = (unsigned char)strtoul(optarg, NULL, 0);
break;
+ case 'm':
+ min_hosts_alive = (int)strtoul(optarg, NULL, 0);
+ break;
case 'd': /* implement later, for cluster checks */
warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
if(ptr) {
crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
}
break;
- case 'h': case 'V': default:
- usage(arg, NULL);
+ case 's': /* specify source IP address */
+ set_source_ip(optarg);
break;
+ case 'V': /* version */
+ /*print_revision (progname, revision);*/ /* FIXME: Why? */
+ exit (STATE_OK);
+ case 'h': /* help */
+ print_help ();
+ exit (STATE_OK);
}
}
}
crash("packets is > 20 (%d)", packets);
}
+ if(min_hosts_alive < -1) {
+ errno = 0;
+ crash("minimum alive hosts is negative (%i)", min_hosts_alive);
+ }
+
host = list;
table = malloc(sizeof(struct rta_host **) * (argc - 1));
i = 0;
static char buf[4096];
struct sockaddr_in resp_addr;
struct ip *ip;
- struct icmp *icp, *sent_icmp;
+ struct icmp icp;
struct rta_host *host;
- struct icmp_ping_data *data;
+ struct icmp_ping_data data;
struct timeval wait_start, now;
u_int tdiff, i, per_pkt_wait;
/* } */
/* check the response */
- icp = (struct icmp *)(buf + hlen);
- sent_icmp = (struct icmp *)(buf + hlen + ICMP_MINLEN);
- /* printf("buf: %p, icp: %p, distance: %u (expected %u)\n", */
- /* buf, icp, */
- /* (u_int)icp - (u_int)buf, hlen); */
- /* printf("buf: %p, sent_icmp: %p, distance: %u (expected %u)\n", */
- /* buf, sent_icmp, */
- /* (u_int)sent_icmp - (u_int)buf, hlen + ICMP_MINLEN); */
-
- if(icp->icmp_id != pid) {
- handle_random_icmp(icp, &resp_addr);
+ 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(icp.icmp_type != ICMP_ECHOREPLY || icp.icmp_seq >= targets) {
if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
- handle_random_icmp(icp, &resp_addr);
+ handle_random_icmp(&icp, &resp_addr);
continue;
}
/* this is indeed a valid response */
- data = (struct icmp_ping_data *)(icp->icmp_data);
+ memcpy(&data, icp.icmp_data, sizeof(data));
- host = table[icp->icmp_seq];
+ host = table[icp.icmp_seq];
gettimeofday(&now, &tz);
- tdiff = get_timevaldiff(&data->stime, &now);
+ tdiff = get_timevaldiff(&data.stime, &now);
host->time_waited += tdiff;
host->icmp_recv++;
static int
send_icmp_ping(int sock, struct rta_host *host)
{
- static char *buf = NULL; /* re-use so we prevent leaks */
+ static union {
+ char *buf; /* re-use so we prevent leaks */
+ struct icmp *icp;
+ u_short *cksum_in;
+ } packet = { NULL };
long int len;
- struct icmp *icp;
- struct icmp_ping_data *data;
+ struct icmp_ping_data data;
struct timeval tv;
struct sockaddr *addr;
-
if(sock == -1) {
errno = 0;
crash("Attempt to send on bogus socket");
}
addr = (struct sockaddr *)&host->saddr_in;
- if(!buf) {
- buf = (char *)malloc(icmp_pkt_size + sizeof(struct ip));
- if(!buf) {
+ if(!packet.buf) {
+ if (!(packet.buf = malloc(icmp_pkt_size))) {
crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
icmp_pkt_size);
return -1; /* might be reached if we're in debug mode */
}
}
- memset(buf, 0, icmp_pkt_size + sizeof(struct ip));
+ memset(packet.buf, 0, icmp_pkt_size);
if((gettimeofday(&tv, &tz)) == -1) return -1;
- icp = (struct icmp *)buf;
- icp->icmp_type = ICMP_ECHO;
- icp->icmp_code = 0;
- icp->icmp_cksum = 0;
- icp->icmp_id = pid;
- icp->icmp_seq = host->id;
- data = (struct icmp_ping_data *)icp->icmp_data;
- data->ping_id = 10; /* host->icmp.icmp_sent; */
- memcpy(&data->stime, &tv, sizeof(struct timeval));
- icp->icmp_cksum = icmp_checksum((u_short *)icp, icmp_pkt_size);
-
- len = sendto(sock, buf, icmp_pkt_size, 0, (struct sockaddr *)addr,
+ data.ping_id = 10; /* host->icmp.icmp_sent; */
+ memcpy(&data.stime, &tv, sizeof(tv));
+ memcpy(&packet.icp->icmp_data, &data, sizeof(data));
+ 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_cksum = icmp_checksum(packet.cksum_in, icmp_pkt_size);
+
+ len = sendto(sock, packet.buf, icmp_pkt_size, 0, (struct sockaddr *)addr,
sizeof(struct sockaddr));
if(len < 0 || (unsigned int)len != icmp_pkt_size) {
struct rta_host *host;
char *status_string[] =
{"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
+ int hosts_ok = 0;
+ int hosts_warn = 0;
alarm(0);
if(debug > 1) printf("finish(%d) called\n", sig);
}
host->pl = pl;
host->rta = rta;
- if(!status && (pl >= warn.pl || rta >= warn.rta)) status = STATE_WARNING;
- if(pl >= crit.pl || rta >= crit.rta) status = STATE_CRITICAL;
+ if(pl >= crit.pl || rta >= crit.rta) {
+ status = STATE_CRITICAL;
+ }
+ else if(!status && (pl >= warn.pl || rta >= warn.rta)) {
+ status = STATE_WARNING;
+ hosts_warn++;
+ }
+ else {
+ hosts_ok++;
+ }
host = host->next;
}
/* this is inevitable */
if(!targets_alive) status = STATE_CRITICAL;
+ if(min_hosts_alive > -1) {
+ if(hosts_ok >= min_hosts_alive) status = STATE_OK;
+ else if((hosts_ok + hosts_warn) >= min_hosts_alive) status = STATE_WARNING;
+ }
printf("%s - ", status_string[status]);
host = list;
host = host->next;
}
+ if(min_hosts_alive > -1) {
+ if(hosts_ok >= min_hosts_alive) status = STATE_OK;
+ else if((hosts_ok + hosts_warn) >= min_hosts_alive) status = STATE_WARNING;
+ }
+
/* finish with an empty line */
puts("");
- if(debug) printf("targets: %u, targets_alive: %u\n",
- targets, targets_alive);
+ if(debug) printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
+ targets, targets_alive, hosts_ok, hosts_warn, min_hosts_alive);
exit(status);
}
return 0;
}
+
+static void
+set_source_ip(char *arg)
+{
+ struct sockaddr_in src;
+
+ memset(&src, 0, sizeof(src));
+ src.sin_family = AF_INET;
+ if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
+ src.sin_addr.s_addr = get_ip_address(arg);
+ if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
+ crash("Cannot bind to IP address %s", arg);
+}
+
+/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
+static in_addr_t
+get_ip_address(const char *ifname)
+{
+#if defined(SIOCGIFADDR)
+ struct ifreq ifr;
+
+ 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;
+#else
+ errno = 0;
+ crash("Cannot get interface IP address on this platform.");
+#endif
+}
+
/*
* u = micro
* m = milli
return cksum;
}
-/* make core plugin developers happy (silly, really) */
-static void
-usage(unsigned char arg, char *msg)
+void
+print_help(void)
{
- if(msg) printf("%s: %s\n", progname, msg);
- if(arg == 'V') {
- printf("$Id$\n");
- exit(STATE_UNKNOWN);
- }
+ /*print_revision (progname, revision);*/ /* FIXME: Why? */
+
+ printf ("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
+ printf (COPYRIGHT, copyright, email);
+
+ printf ("\n\n");
+
+ print_usage ();
+
+ printf (_(UT_HELP_VRSN));
+
+ 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 (" %s\n", "-c");
+ printf (" %s", _("critical threshold (currently "));
+ printf ("%0.3fms,%u%%)\n", (float)crit.rta, crit.pl);
+ printf (" %s\n", "-s");
+ printf (" %s\n", _("specify a source IP address or device name"));
+ printf (" %s\n", "-n");
+ printf (" %s", _("number of packets to send (currently "));
+ printf ("%u)\n",packets);
+ printf (" %s\n", "-i");
+ printf (" %s", _("max packet interval (currently "));
+ printf ("%0.3fms)\n",(float)pkt_interval / 1000);
+ printf (" %s\n", "-I");
+ printf (" %s", _("max target interval (currently "));
+ printf ("%0.3fms)\n", (float)target_interval / 1000);
+ printf (" %s\n", "-m");
+ printf (" %s",_("number of alive hosts required for success"));
+ printf ("\n");
+ printf (" %s\n", "-l");
+ printf (" %s", _("TTL on outgoing packets (currently "));
+ printf ("%u)", 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", "-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."));
+/* -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 ("%s\n", _("Long options are currently unsupported."));
+ printf ("%s\n", _("Options marked with * require an argument"));
+*/
+ printf (_(UT_SUPPORT));
+
+ printf (_(UT_NOWARRANTY));
+}
- printf("Usage: %s [options] [-H] host1 host2 hostn\n\n", progname);
-
- if(arg != 'h') exit(3);
-
- printf("Where options are any combination of:\n"
- " * -H | --host specify a target\n"
- " * -w | --warn warning threshold (currently %0.3fms,%u%%)\n"
- " * -c | --crit critical threshold (currently %0.3fms,%u%%)\n"
- " * -n | --packets number of packets to send (currently %u)\n"
- " * -i | --interval max packet interval (currently %0.3fms)\n"
- " * -I | --hostint max target interval (currently %0.3fms)\n"
- " * -l | --ttl TTL on outgoing packets (currently %u)\n"
- " * -t | --timeout timeout value (seconds, currently %u)\n"
- " * -b | --bytes icmp packet size (currenly ignored)\n"
- " -v | --verbose verbosity++\n"
- " -h | --help this cruft\n",
- (float)warn.rta / 1000, warn.pl, (float)crit.rta / 1000, crit.pl,
- packets,
- (float)pkt_interval / 1000, (float)target_interval / 1000,
- ttl, timeout);
-
- puts("\nThe -H switch is optional. Naming a host (or several) to check is not.\n\n"
- "Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%\n"
- "packet loss. The default values should work well for most users.\n"
- "You can specify different RTA factors using the standardized abbreviations\n"
- "us (microseconds), ms (milliseconds, default) or just plain s for seconds.\n\n"
- "Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops\n"
- "are spent and CRITICAL if >= 14 hops are spent.\n"
- "NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not.\n\n"
- "The -v switch can be specified several times for increased verbosity.\n\n"
- "Long options are currently unsupported.\n\n"
- "Options marked with * require an argument\n");
-
- puts("The latest version of this plugin can be found at http://oss.op5.se/nagios\n"
- "or https://devel.op5.se/oss until the day it is included in the official\n"
- "plugin distribution.\n");
- exit(3);
+
+void
+print_usage (void)
+{
+ printf (_("Usage:"));
+ printf(" %s [options] [-H] host1 host2 hostn\n", progname);
}