summaryrefslogtreecommitdiffstats
path: root/plugins/check_icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/check_icmp.c')
-rw-r--r--plugins/check_icmp.c2241
1 files changed, 1012 insertions, 1229 deletions
diff --git a/plugins/check_icmp.c b/plugins/check_icmp.c
index 4571682..2f03552 100644
--- a/plugins/check_icmp.c
+++ b/plugins/check_icmp.c
@@ -1,1416 +1,1199 @@
1/* 1/*
2 * $Id$ 2 * $Id$
3 * 3 *
4 * This is a hack of fping2 made to work with nagios. 4 * Author: Andreas Ericsson <ae@op5.se>
5 * It's fast and removes the necessity of parsing another programs output.
6 * 5 *
7 * VIEWING NOTES: 6 * License: GNU GPL 2.0 or any later version.
8 * This file was formatted with tab indents at a tab stop of 4.
9 * 7 *
10 * It is highly recommended that your editor is set to this 8 * Relevant RFC's: 792 (ICMP), 791 (IP)
11 * tab stop setting for viewing and editing.
12 * 9 *
13 * COPYLEFT; 10 * This program was modeled somewhat after the check_icmp program,
14 * This programs copyright status is currently undetermined. Much of 11 * which was in turn a hack of fping (www.fping.org) but has been
15 * the code in it comes from the fping2 program which used to be licensed 12 * completely rewritten since to generate higher precision rta values,
16 * under the Stanford General Software License (available at 13 * and support several different modes as well as setting ttl to control.
17 * http://graphics.stanford.edu/software/license.html). It is presently 14 * redundant routes. The only remainders of fping is currently a few
18 * unclear what license (if any) applies to the original code at the 15 * function names.
19 * moment.
20 * 16 *
21 * The fping website can be found at http://www.fping.com
22 */ 17 */
23 18
24const char *progname = "check_icmp"; 19#include <sys/time.h>
25const char *revision = "$Revision$"; 20#include <sys/types.h>
26const char *copyright = "2004";
27const char *email = "nagiosplug-devel@lists.sourceforge.net";
28
29#include "common.h"
30#include "netutils.h"
31#include "utils.h"
32
33#include <stdio.h> 21#include <stdio.h>
34#include <errno.h>
35#include <time.h>
36#include <signal.h>
37
38#include <unistd.h>
39
40#include <stdlib.h> 22#include <stdlib.h>
41 23#include <stdarg.h>
42#include <string.h> 24#include <unistd.h>
43#include <stddef.h> 25#include <stddef.h>
44 26#include <errno.h>
45#include <sys/types.h> 27#include <string.h>
46#include <sys/time.h> 28#include <ctype.h>
29#include <netdb.h>
47#include <sys/socket.h> 30#include <sys/socket.h>
48
49#include <sys/file.h>
50
51#include <netinet/in_systm.h> 31#include <netinet/in_systm.h>
52#include <netinet/in.h> 32#include <netinet/in.h>
53
54#include <netinet/ip.h> 33#include <netinet/ip.h>
55#include <netinet/ip_icmp.h> 34#include <netinet/ip_icmp.h>
56
57#include <arpa/inet.h> 35#include <arpa/inet.h>
58#include <netdb.h> 36#include <signal.h>
59
60/* RS6000 has sys/select.h */
61#ifdef HAVE_SYS_SELECT_H
62#include <sys/select.h>
63#endif /* HAVE_SYS_SELECT_H */
64 37
65/* rta threshold values can't be larger than MAXTTL seconds */ 38/** sometimes undefined system macros (quite a few, actually) **/
66#ifndef MAXTTL 39#ifndef MAXTTL
67# define MAXTTL 255 40# define MAXTTL 255
68#endif
69#ifndef IPDEFTTL
70# define IPDEFTTL 64
71#endif 41#endif
72
73/*** externals ***/
74extern char *optarg;
75extern int optind, opterr;
76
77/*** Constants ***/
78//#define EMAIL "ae@op5.se"
79//#define VERSION "0.8.1"
80
81#ifndef INADDR_NONE 42#ifndef INADDR_NONE
82# define INADDR_NONE 0xffffffU 43# define INADDR_NONE 0xffffffU
83#endif 44#endif
84 45
85/*** Ping packet defines ***/ 46#ifndef SOL_IP
86/* data added after ICMP header for our nefarious purposes */ 47#define SOL_IP 0
87typedef struct ping_data {
88 unsigned int ping_count; /* counts up to -[n|p] count or 1 */
89 struct timeval ping_ts; /* time sent */
90} PING_DATA;
91
92#define MIN_PING_DATA sizeof(PING_DATA)
93#define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */
94#define SIZE_IP_HDR 20
95#define SIZE_ICMP_HDR ICMP_MINLEN /* from ip_icmp.h */
96#define MAX_PING_DATA (MAX_IP_PACKET - SIZE_IP_HDR - SIZE_ICMP_HDR)
97
98/*
99 * Interval is the minimum amount of time between sending a ping packet to
100 * any host.
101 *
102 * Perhost_interval is the minimum amount of time between sending a ping
103 * packet to a particular responding host
104 *
105 * Timeout is the initial amount of time between sending a ping packet to
106 * a particular non-responding host.
107 *
108 * Retry is the number of ping packets to send to a non-responding host
109 * before giving up (in is-it-alive mode).
110 *
111 * Backoff factor is how much longer to wait on successive retries.
112 */
113#ifndef DEFAULT_INTERVAL
114#define DEFAULT_INTERVAL 25 /* default time between packets (msec) */
115#endif
116
117#ifndef DEFAULT_RETRY
118#define DEFAULT_RETRY 1 /* number of times to retry a host */
119#endif 48#endif
120 49
121#ifndef DEFAULT_TIMEOUT 50/* we bundle these in one #ifndef, since they're all from BSD
122# define DEFAULT_TIMEOUT 1000 51 * Put individual #ifndef's around those that bother you */
52#ifndef ICMP_UNREACH_NET_UNKNOWN
53# define ICMP_UNREACH_NET_UNKNOWN 6
54# define ICMP_UNREACH_HOST_UNKNOWN 7
55# define ICMP_UNREACH_ISOLATED 8
56# define ICMP_UNREACH_NET_PROHIB 9
57# define ICMP_UNREACH_HOST_PROHIB 10
58# define ICMP_UNREACH_TOSNET 11
59# define ICMP_UNREACH_TOSHOST 12
123#endif 60#endif
124 61/* tru64 has the ones above, but not these */
125#ifndef DEFAULT_BACKOFF_FACTOR 62#ifndef ICMP_UNREACH_FILTER_PROHIB
126#define DEFAULT_BACKOFF_FACTOR 1.5 /* exponential timeout factor */ 63# define ICMP_UNREACH_FILTER_PROHIB 13
64# define ICMP_UNREACH_HOST_PRECEDENCE 14
65# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
127#endif 66#endif
128#define MIN_BACKOFF_FACTOR 1.0 /* exponential timeout factor */
129#define MAX_BACKOFF_FACTOR 5.0 /* exponential timeout factor */
130
131#ifndef DNS_TIMEOUT
132#define DNS_TIMEOUT 1000 /* time in usec for dns retry */
133#endif
134
135#ifndef MAX_RTA_THRESHOLD_VALUE
136# define MAX_RTA_THRESHOLD_VALUE 120*1000000 /* 2 minutes should be enough */
137#endif
138#ifndef MIN_RTA_THRESHOLD_VALUE
139# define MIN_RTA_THRESHOLD_VALUE 10000 /* minimum RTA threshold value */
140#endif
141
142/* sized so as to be like traditional ping */
143#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA + 44)
144
145/* maxima and minima */
146#define MAX_COUNT 50 /* max count even if we're root */
147#define MAX_RETRY 5
148#define MIN_INTERVAL 25 /* msecs */
149#define MIN_TIMEOUT 50 /* msecs */
150
151/* response time array flags */
152#define RESP_WAITING -1
153#define RESP_UNUSED -2
154
155#define ICMP_UNREACH_MAXTYPE 15
156
157/* entry used to keep track of each host we are pinging */
158struct host_entry {
159 int i; /* index into array */
160 char *name; /* name as given by user */
161 char *host; /* text description of host */
162 struct sockaddr_in saddr; /* internet address */
163 unsigned short **pr; /* TCP port range to check for connectivity */
164 struct timeval last_send_time; /* time of last packet sent */
165 unsigned int num_sent; /* number of ping packets sent */
166 unsigned int num_recv; /* number of pings received */
167 unsigned int total_time; /* sum of response times */
168 unsigned int status; /* this hosts status */
169 unsigned int running; /* unset when through sending */
170 unsigned int waiting; /* waiting for response */
171 int *resp_times; /* individual response times */
172 struct host_entry *prev, *next; /* doubly linked list */
173};
174
175typedef struct host_entry HOST_ENTRY;
176 67
177struct host_name_list {
178 char *entry;
179 struct host_name_list *next;
180};
181 68
182/* threshold structure */ 69/** typedefs and such **/
183struct threshold { 70enum states {
184 unsigned int pl; /* packet loss */
185 unsigned int rta; /* roundtrip time average */
186};
187typedef struct threshold threshold;
188
189/*****************************************************************************
190 * Global Variables *
191 *****************************************************************************/
192
193HOST_ENTRY *rrlist = NULL; /* linked list of hosts be pinged */
194HOST_ENTRY **table = NULL; /* array of pointers to items in the list */
195HOST_ENTRY *cursor;
196
197char *prog; /* our name */
198int ident; /* our pid, for marking icmp packets */
199int sock; /* socket */
200u_int debug = 0;
201
202/* threshold value defaults;
203 * WARNING; 60% packetloss or 200 msecs round trip average
204 * CRITICAL; 80% packetloss or 500 msecs round trip average */
205threshold warn = {60, 200 * 1000};
206threshold crit = {80, 500 * 1000};
207
208/* times get *100 because all times are calculated in 10 usec units, not ms */
209unsigned int retry = DEFAULT_RETRY;
210u_int timeout = DEFAULT_TIMEOUT * 100;
211u_int interval = DEFAULT_INTERVAL * 100;
212float backoff = DEFAULT_BACKOFF_FACTOR;
213u_int select_time; /* calculated using maximum threshold rta value */
214u_int ping_data_size = DEFAULT_PING_DATA_SIZE;
215u_int ping_pkt_size;
216unsigned int count = 5;
217unsigned int trials = 1;
218
219/* global stats */
220int total_replies = 0;
221int num_jobs = 0; /* number of hosts still to do */
222int num_hosts = 0; /* total number of hosts */
223int num_alive = 0; /* total number alive */
224int num_unreachable = 0; /* total number unreachable */
225int num_noaddress = 0; /* total number of addresses not found */
226int num_timeout = 0; /* number of timed out packets */
227int num_pingsent = 0; /* total pings sent */
228int num_pingreceived = 0; /* total pings received */
229int num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
230
231struct timeval current_time; /* current time (pseudo) */
232struct timeval my_start_time; /* conflict with utils.c 33, but not found ?? */
233struct timeval my_end_time; /* conflict with utils.c 33, but not found ?? */
234struct timeval last_send_time; /* time last ping was sent */
235struct timezone tz;
236
237/* switches */
238int generate_flag = 0; /* flag for IP list generation */
239int stats_flag, unreachable_flag, alive_flag;
240int elapsed_flag, version_flag, count_flag;
241int name_flag, addr_flag, backoff_flag;
242int multif_flag;
243
244/*** prototypes ***/
245void add_name(char *);
246void add_addr(char *, char *, struct in_addr);
247char *na_cat(char *, struct in_addr);
248char *cpystr(char *);
249void crash(char *);
250char *get_host_by_address(struct in_addr);
251int in_cksum(u_short *, int);
252void u_sleep(int);
253int recvfrom_wto(int, char *, int, struct sockaddr *, int);
254void remove_job(HOST_ENTRY *);
255void send_ping(int, HOST_ENTRY *);
256long timeval_diff(struct timeval *, struct timeval *);
257//void usage(void);
258int wait_for_reply(int);
259void finish(void);
260int handle_random_icmp(struct icmp *, struct sockaddr_in *);
261char *sprint_tm(int);
262int get_threshold(char *, threshold *);
263
264/* common functions */
265void print_help (void);
266void print_usage (void);
267
268/*** the various exit-states */
269/*enum {
270 STATE_OK = 0, 71 STATE_OK = 0,
271 STATE_WARNING, 72 STATE_WARNING,
272 STATE_CRITICAL, 73 STATE_CRITICAL,
273 STATE_UNKNOWN, 74 STATE_UNKNOWN,
274 STATE_DEPENDANT, 75 STATE_DEPENDENT,
275 STATE_OOB 76 STATE_OOB
276};*/
277
278/* the strings that correspond to them */
279/*
280char *status_string[STATE_OOB] = {
281 "OK",
282 "WARNING",
283 "CRITICAL",
284 "UNKNOWN",
285 "DEPENDANT"
286}; 77};
287*/
288 78
289int status = STATE_OK; 79typedef unsigned short range_t; /* type for get_range() -- unimplemented */
290int fin_stat = STATE_OK; 80
81typedef struct rta_host {
82 unsigned short id; /* id in **table, and icmp pkts */
83 char *name; /* arg used for adding this host */
84 char *msg; /* icmp error message, if any */
85 struct sockaddr_in saddr_in; /* the address of this host */
86 struct in_addr error_addr; /* stores address of error replies */
87 unsigned long long time_waited; /* total time waited, in usecs */
88 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
89 unsigned char icmp_type, icmp_code; /* type and code from errors */
90 unsigned short flags; /* control/status flags */
91 double rta; /* measured RTA */
92 unsigned char pl; /* measured packet loss */
93 struct rta_host *next; /* linked list */
94} rta_host;
95
96#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
97
98/* threshold structure. all values are maximum allowed, exclusive */
99typedef struct threshold {
100 unsigned char pl; /* max allowed packet loss in percent */
101 unsigned int rta; /* roundtrip time average, microseconds */
102} threshold;
103
104/* the data structure */
105typedef struct icmp_ping_data {
106 struct timeval stime; /* timestamp (saved in protocol struct as well) */
107 unsigned short ping_id;
108} icmp_ping_data;
109
110/* the different modes of this program are as follows:
111 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
112 * MODE_HOSTCHECK: Return immediately upon any sign of life
113 * In addition, sends packets to ALL addresses assigned
114 * to this host (as returned by gethostbyname() or
115 * gethostbyaddr() and expects one host only to be checked at
116 * a time. Therefore, any packet response what so ever will
117 * count as a sign of life, even when received outside
118 * crit.rta limit. Do not misspell any additional IP's.
119 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
120 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
121 * tcp and udp args does this)
122 */
123#define MODE_RTA 0
124#define MODE_HOSTCHECK 1
125#define MODE_ALL 2
126#define MODE_ICMP 3
127
128/* the different ping types we can do
129 * TODO: investigate ARP ping as well */
130#define HAVE_ICMP 1
131#define HAVE_UDP 2
132#define HAVE_TCP 4
133#define HAVE_ARP 8
134
135#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
136#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
137#define IP_HDR_SIZE 20
138#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
139#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
140
141/* various target states */
142#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
143#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
144#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
145#define TSTATE_UNREACH 0x08
146
147/** prototypes **/
148static void usage(unsigned char, char *);
149static u_int get_timevar(const char *);
150static u_int get_timevaldiff(struct timeval *, struct timeval *);
151static int wait_for_reply(int, u_int);
152static int recvfrom_wto(int, char *, unsigned int, struct sockaddr *, u_int *);
153static int send_icmp_ping(int, struct rta_host *);
154static int get_threshold(char *str, threshold *th);
155static void run_checks(void);
156static int add_target(char *);
157static int add_target_ip(char *, struct in_addr *);
158static int handle_random_icmp(struct icmp *, struct sockaddr_in *);
159static unsigned short icmp_checksum(unsigned short *, int);
160static void finish(int);
161static void crash(const char *, ...);
162
163/** external **/
164extern int optind, opterr, optopt;
165extern char *optarg;
166extern char **environ;
167
168/** global variables **/
169static char *progname;
170static struct rta_host **table, *cursor, *list;
171static threshold crit = {80, 500000}, warn = {40, 200000};
172static int mode, protocols, sockets, debug = 0, timeout = 10;
173static unsigned short icmp_pkt_size, icmp_data_size = DEFAULT_PING_DATA_SIZE;
174static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0;
175#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
176static unsigned short targets_down = 0, targets = 0, packets = 0;
177#define targets_alive (targets - targets_down)
178static unsigned int retry_interval, pkt_interval, target_interval;
179static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
180static pid_t pid;
181static struct timezone tz;
182static struct timeval prog_start;
183static unsigned long long max_completion_time = 0;
184static unsigned char ttl = 0; /* outgoing ttl */
185static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
186float pkt_backoff_factor = 1.5;
187float target_backoff_factor = 1.5;
188
189/** code start **/
190static void
191crash(const char *fmt, ...)
192{
193 va_list ap;
194
195 printf("%s: ", progname);
196
197 va_start(ap, fmt);
198 vprintf(fmt, ap);
199 va_end(ap);
200
201 if(errno) printf(": %s", strerror(errno));
202 puts("");
291 203
292/***************************************************************************** 204 exit(3);
293 * Code block start * 205}
294 *****************************************************************************/ 206
295int main(int argc, char **argv) 207
208static char *
209get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
296{ 210{
297 int c; 211 char *msg = "unreachable";
298 u_int lt, ht;
299 int advance;
300 struct protoent *proto;
301 uid_t uid;
302 struct host_name_list *host_ptr, *host_base_ptr;
303
304 if(strchr(argv[0], '/')) prog = strrchr(argv[0], '/') + 1;
305 else prog = argv[0];
306
307 setlocale (LC_ALL, "");
308 bindtextdomain (PACKAGE, LOCALEDIR);
309 textdomain (PACKAGE);
310
311 /* check if we are root */
312 if(geteuid()) {
313 printf(_("Root access needed (for raw sockets)\n"));
314 exit(STATE_UNKNOWN);
315 }
316 212
317 /* confirm that ICMP is available on this machine */ 213 if(debug > 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
318 if((proto = getprotobyname("icmp")) == NULL) 214 switch(icmp_type) {
319 crash(_("icmp: unknown protocol")); 215 case ICMP_UNREACH:
216 switch(icmp_code) {
217 case ICMP_UNREACH_NET: msg = "Net unreachable"; break;
218 case ICMP_UNREACH_HOST: msg = "Host unreachable"; break;
219 case ICMP_UNREACH_PROTOCOL: msg = "Protocol unreachable (firewall?)"; break;
220 case ICMP_UNREACH_PORT: msg = "Port unreachable (firewall?)"; break;
221 case ICMP_UNREACH_NEEDFRAG: msg = "Fragmentation needed"; break;
222 case ICMP_UNREACH_SRCFAIL: msg = "Source route failed"; break;
223 case ICMP_UNREACH_ISOLATED: msg = "Source host isolated"; break;
224 case ICMP_UNREACH_NET_UNKNOWN: msg = "Unknown network"; break;
225 case ICMP_UNREACH_HOST_UNKNOWN: msg = "Unknown host"; break;
226 case ICMP_UNREACH_NET_PROHIB: msg = "Network denied (firewall?)"; break;
227 case ICMP_UNREACH_HOST_PROHIB: msg = "Host denied (firewall?)"; break;
228 case ICMP_UNREACH_TOSNET: msg = "Bad TOS for network (firewall?)"; break;
229 case ICMP_UNREACH_TOSHOST: msg = "Bad TOS for host (firewall?)"; break;
230 case ICMP_UNREACH_FILTER_PROHIB: msg = "Prohibited by filter (firewall)"; break;
231 case ICMP_UNREACH_HOST_PRECEDENCE: msg = "Host precedence violation"; break;
232 case ICMP_UNREACH_PRECEDENCE_CUTOFF: msg = "Precedence cutoff"; break;
233 default: msg = "Invalid code"; break;
234 }
235 break;
320 236
321 /* create raw socket for ICMP calls (ping) */ 237 case ICMP_TIMXCEED:
322 sock = socket(AF_INET, SOCK_RAW, proto->p_proto); 238 /* really 'out of reach', or non-existant host behind a router serving
239 * two different subnets */
240 switch(icmp_code) {
241 case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break;
242 case ICMP_TIMXCEED_REASS: msg = "Fragment reassembly time exceeded"; break;
243 default: msg = "Invalid code"; break;
244 }
245 break;
323 246
324 if(sock < 0) 247 case ICMP_SOURCEQUENCH: msg = "Transmitting too fast"; break;
325 crash(_("Can't create raw socket")); 248 case ICMP_REDIRECT: msg = "Redirect (change route)"; break;
249 case ICMP_PARAMPROB: msg = "Bad IP header (required option absent)"; break;
326 250
327 /* drop privileges now that we have the socket */ 251 /* the following aren't error messages, so ignore */
328 if((uid = getuid())) { 252 case ICMP_TSTAMP:
329 seteuid(uid); 253 case ICMP_TSTAMPREPLY:
254 case ICMP_IREQ:
255 case ICMP_IREQREPLY:
256 case ICMP_MASKREQ:
257 case ICMP_MASKREPLY:
258 default: msg = ""; break;
330 } 259 }
331
332 if(argc < 2) print_usage();
333 260
334 ident = getpid() & 0xFFFF; 261 return msg;
262}
263
264static int
265handle_random_icmp(struct icmp *p, struct sockaddr_in *addr)
266{
267 struct icmp *sent_icmp = NULL;
268 struct rta_host *host = NULL;
269 unsigned char *ptr;
335 270
336 if(!(host_base_ptr = malloc(sizeof(struct host_name_list)))) { 271 if(p->icmp_type == ICMP_ECHO && p->icmp_id == pid) {
337 crash(_("Unable to allocate memory for host name list\n")); 272 /* echo request from us to us (pinging localhost) */
273 return 0;
338 } 274 }
339 host_ptr = host_base_ptr;
340
341 backoff_flag = 0;
342 opterr = 1;
343
344 /* get command line options
345 * -H denotes a host (actually ignored and picked up later)
346 * -h for help
347 * -V or -v for version
348 * -d to display hostnames rather than addresses
349 * -t sets timeout for packets and tcp connects
350 * -r defines retries (persistence)
351 * -p or -n sets packet count (5)
352 * -b sets packet size (56)
353 * -w sets warning threshhold (200,40%)
354 * -c sets critical threshhold (500,80%)
355 * -i sets interval for both packet transmissions and connect attempts
356 */
357#define OPT_STR "amH:hvVDdAp:n:b:r:t:i:w:c:"
358 while((c = getopt(argc, argv, OPT_STR)) != EOF) {
359 switch (c) {
360 case 'H':
361 if(!(host_ptr->entry = malloc(strlen(optarg) + 1))) {
362 crash(_("Failed to allocate memory for hostname"));
363 }
364 memset(host_ptr->entry, 0, strlen(optarg) + 1);
365 host_ptr->entry = memcpy(host_ptr->entry, optarg, strlen(optarg));
366 if(!(host_ptr->next = malloc(sizeof(struct host_name_list))))
367 crash(_("Failed to allocate memory for hostname"));
368 host_ptr = host_ptr->next;
369 host_ptr->next = NULL;
370// add_name(optarg);
371 break;
372 /* this is recognized, but silently ignored.
373 * host(s) are added later on */
374 275
375 break; 276 ptr = (unsigned char *)p;
376 case 'w': 277 if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)p, (void *)addr);
377 if(get_threshold(optarg, &warn)) { 278
378 printf(_("Illegal threshold pair specified for -%c"), c); 279 /* only handle a few types, since others can't possibly be replies to
379 print_usage(); 280 * us in a sane network (if it is anyway, it will be counted as lost
380 } 281 * at summary time, but not as quickly as a proper response */
381 break; 282 /* TIMXCEED can be an unreach from a router with multiple IP's which
283 * serves two different subnets on the same interface and a dead host
284 * on one net is pinged from the other. The router will respond to
285 * itself and thus set TTL=0 so as to not loop forever. Even when
286 * TIMXCEED actually sends a proper icmp response we will have passed
287 * too many hops to have a hope of reaching it later, in which case it
288 * indicates overconfidence in the network, poor routing or both. */
289 if(p->icmp_type != ICMP_UNREACH && p->icmp_type != ICMP_TIMXCEED &&
290 p->icmp_type != ICMP_SOURCEQUENCH && p->icmp_type != ICMP_PARAMPROB)
291 {
292 return 0;
293 }
382 294
383 case 'c': 295 /* might be for us. At least it holds the original package (according
384 if(get_threshold(optarg, &crit)) { 296 * to RFC 792). If it isn't, just ignore it */
385 printf(_("Illegal threshold pair specified for -%c"), c); 297 sent_icmp = (struct icmp *)(ptr + 28);
386 print_usage(); 298 if(sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != pid ||
387 } 299 sent_icmp->icmp_seq >= targets)
388 break; 300 {
301 if(debug) printf("Packet is no response to a packet we sent\n");
302 return 0;
303 }
389 304
390 case 't': 305 /* it is indeed a response for us */
391 if(!(timeout = (u_int) strtoul(optarg, NULL, 0) * 100)) { 306 host = table[sent_icmp->icmp_seq];
392 printf(_("Option -%c requires integer argument\n"), c); 307 if(debug) {
393 print_usage(); 308 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
394 } 309 get_icmp_error_msg(p->icmp_type, p->icmp_code),
395 break; 310 inet_ntoa(addr->sin_addr), host->name);
311 }
396 312
397 case 'r': 313 icmp_lost++;
398 if(!(retry = (u_int) strtoul(optarg, NULL, 0))) { 314 host->icmp_lost++;
399 printf(_("Option -%c requires integer argument\n"), c); 315 /* don't spend time on lost hosts any more */
400 print_usage(); 316 if(host->flags & FLAG_LOST_CAUSE) return 0;
401 }
402 break;
403 317
404 case 'i': 318 /* source quench means we're sending too fast, so increase the
405 if(!(interval = (u_int) strtoul(optarg, NULL, 0) * 100)) { 319 * interval and mark this packet lost */
406 printf(_("Option -%c requires positive non-zero integer argument\n"), c); 320 if(p->icmp_type == ICMP_SOURCEQUENCH) {
407 print_usage(); 321 pkt_interval *= pkt_backoff_factor;
408 } 322 target_interval *= target_backoff_factor;
409 break; 323 }
324 else {
325 targets_down++;
326 host->flags |= FLAG_LOST_CAUSE;
327 }
328 host->icmp_type = p->icmp_type;
329 host->icmp_code = p->icmp_code;
330 host->error_addr.s_addr = addr->sin_addr.s_addr;
410 331
411 case 'p': 332 return 0;
412 case 'n': 333}
413 if(!(count = (u_int) strtoul(optarg, NULL, 0))) {
414 printf(_("Option -%c requires positive non-zero integer argument\n"), c);
415 print_usage();
416 }
417 break;
418 334
335int
336main(int argc, char **argv)
337{
338 int i;
339 char *ptr;
340 long int arg;
341 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
342 int result;
343 struct rta_host *host;
344
345 /* we only need to be setsuid when we get the sockets, so do
346 * that before pointer magic (esp. on network data) */
347 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
348
349 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
350 sockets |= HAVE_ICMP;
351 else icmp_sockerrno = errno;
352
353 /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */
354 /* sockets |= HAVE_UDP; */
355 /* else udp_sockerrno = errno; */
356
357 /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */
358 /* sockets |= HAVE_TCP; */
359 /* else tcp_sockerrno = errno; */
360
361 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
362 setuid(getuid());
363
364 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
365 environ = NULL;
366
367 /* use the pid to mark packets as ours */
368 pid = getpid();
369 /* printf("pid = %u\n", pid); */
370
371 /* get calling name the old-fashioned way for portability instead
372 * of relying on the glibc-ism __progname */
373 ptr = strrchr(argv[0], '/');
374 if(ptr) progname = &ptr[1];
375 else progname = argv[0];
376
377 /* now set defaults. Use progname to set them initially (allows for
378 * superfast check_host program when target host is up */
379 cursor = list = NULL;
380 table = NULL;
381
382 mode = MODE_RTA;
383 crit.rta = 500000;
384 crit.pl = 80;
385 warn.rta = 200000;
386 warn.pl = 40;
387 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
388 pkt_interval = 80000; /* 80 msec packet interval by default */
389 packets = 5;
390
391 if(!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
392 mode = MODE_ICMP;
393 protocols = HAVE_ICMP;
394 }
395 else if(!strcmp(progname, "check_host")) {
396 mode = MODE_HOSTCHECK;
397 pkt_interval = 1000000;
398 packets = 5;
399 crit.rta = warn.rta = 1000000;
400 crit.pl = warn.pl = 100;
401 }
402 else if(!strcmp(progname, "check_rta_multi")) {
403 mode = MODE_ALL;
404 target_interval = 0;
405 pkt_interval = 50000;
406 packets = 5;
407 }
408
409 /* parse the arguments */
410 for(i = 1; i < argc; i++) {
411 while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:i:b:I:l:")) != EOF) {
412 switch(arg) {
413 case 'v':
414 debug++;
415 break;
419 case 'b': 416 case 'b':
420 if(!(ping_data_size = (u_int) strtoul(optarg, NULL, 0))) { 417 /* silently ignored for now */
421 printf(_("Option -%c requires integer argument\n"), c);
422 print_usage();
423 }
424 break; 418 break;
425 419 case 'i':
426 case 'h': 420 pkt_interval = get_timevar(optarg);
427 print_usage();
428 break; 421 break;
429 422 case 'I':
430 case 'e': 423 target_interval = get_timevar(optarg);
431 elapsed_flag = 1;
432 break; 424 break;
433 425 case 'w':
434 case 'm': 426 get_threshold(optarg, &warn);
435 multif_flag = 1;
436 break; 427 break;
437 428 case 'c':
438 case 'd': 429 get_threshold(optarg, &crit);
439 name_flag = 1;
440 break; 430 break;
441 431 case 'n':
442 case 'A': 432 case 'p':
443 addr_flag = 1; 433 packets = strtoul(optarg, NULL, 0);
444 break; 434 break;
445 435 case 't':
446 case 's': 436 timeout = strtoul(optarg, NULL, 0);
447 stats_flag = 1; 437 if(!timeout) timeout = 10;
448 break; 438 break;
449 439 case 'H':
450 case 'u': 440 add_target(optarg);
451 unreachable_flag = 1;
452 break; 441 break;
453 442 case 'l':
454 case 'a': 443 ttl = (unsigned char)strtoul(optarg, NULL, 0);
455 alive_flag = 1;
456 break; 444 break;
457 445 case 'd': /* implement later, for cluster checks */
458 case 'v': 446 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
459 printf("%s: Version %s $Date$\n", prog, VERSION); 447 if(ptr) {
460 printf("%s: comments to %s\n", prog, email); 448 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
461 exit(STATE_OK); 449 }
462
463 case 'g':
464 /* use IP list generation */
465 /* mutex with file input or command line targets */
466 generate_flag = 1;
467 break; 450 break;
468 451 case 'h': case 'V': default:
469 default: 452 usage(arg, NULL);
470 printf(_("Option flag -%c specified, but not recognized\n"), c);
471 print_usage();
472 break; 453 break;
454 }
473 } 455 }
474 } 456 }
475 457
476 /* arguments are parsed, so now we validate them */ 458 argv = &argv[optind];
477 459 while(*argv) {
478 if(count > 1) count_flag = 1; 460 add_target(*argv);
479 461 argv++;
480 /* set threshold values to 10usec units (inherited from fping.c) */
481 crit.rta = crit.rta / 10;
482 warn.rta = warn.rta / 10;
483 select_time = crit.rta;
484 /* this isn't critical, but will most likely not be what the user expects
485 * so we tell him/her about it, but keep running anyways */
486 if(warn.pl > crit.pl || warn.rta > crit.rta) {
487 select_time = warn.rta;
488 printf("(WARNING threshold > CRITICAL threshold) :: ");
489 fflush(stdout);
490 } 462 }
491 463 if(!targets) {
492 /* A timeout smaller than maximum rta threshold makes no sense */ 464 errno = 0;
493 if(timeout < crit.rta) timeout = crit.rta; 465 crash("No hosts to check");
494 else if(timeout < warn.rta) timeout = warn.rta; 466 exit(3);
495
496 if((interval < MIN_INTERVAL * 100 || retry > MAX_RETRY) && getuid()) {
497 printf(_("%s: these options are too risky for mere mortals.\n"), prog);
498 printf(_("%s: You need i >= %u and r < %u\n"),
499 prog, MIN_INTERVAL, MAX_RETRY);
500 printf(_("Current settings; i = %d, r = %d\n"),
501 interval / 100, retry);
502 print_usage();
503 } 467 }
504 468
505 if((ping_data_size > MAX_PING_DATA) || (ping_data_size < MIN_PING_DATA)) { 469 if(!sockets) {
506 printf(_("%s: data size %u not valid, must be between %u and %u\n"), 470 if(icmp_sock == -1) {
507 prog, ping_data_size, MIN_PING_DATA, MAX_PING_DATA); 471 errno = icmp_sockerrno;
508 print_usage(); 472 crash("Failed to obtain ICMP socket");
509 473 return -1;
474 }
475 /* if(udp_sock == -1) { */
476 /* errno = icmp_sockerrno; */
477 /* crash("Failed to obtain UDP socket"); */
478 /* return -1; */
479 /* } */
480 /* if(tcp_sock == -1) { */
481 /* errno = icmp_sockerrno; */
482 /* crash("Failed to obtain TCP socker"); */
483 /* return -1; */
484 /* } */
510 } 485 }
486 if(!ttl) ttl = 64;
511 487
512 if((backoff > MAX_BACKOFF_FACTOR) || (backoff < MIN_BACKOFF_FACTOR)) { 488 if(icmp_sock) {
513 printf(_("%s: backoff factor %.1f not valid, must be between %.1f and %.1f\n"), 489 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
514 prog, backoff, MIN_BACKOFF_FACTOR, MAX_BACKOFF_FACTOR); 490 if(debug) {
515 print_usage(); 491 if(result == -1) printf("setsockopt failed\n");
516 492 else printf("ttl set to %u\n", ttl);
493 }
517 } 494 }
518 495
519 if(count > MAX_COUNT) { 496 /* stupid users should be able to give whatever thresholds they want
520 printf(_("%s: count %u not valid, must be less than %u\n"), 497 * (nothing will break if they do), but some anal plugin maintainer
521 prog, count, MAX_COUNT); 498 * will probably add some printf() thing here later, so it might be
522 print_usage(); 499 * best to at least show them where to do it. ;) */
500 if(warn.pl > crit.pl) warn.pl = crit.pl;
501 if(warn.rta > crit.rta) warn.rta = crit.rta;
502 if(warn_down > crit_down) crit_down = warn_down;
503
504 signal(SIGINT, finish);
505 signal(SIGHUP, finish);
506 signal(SIGTERM, finish);
507 signal(SIGALRM, finish);
508 if(debug) printf("Setting alarm timeout to %u seconds\n", timeout);
509 alarm(timeout);
510
511 /* make sure we don't wait any longer than necessary */
512 gettimeofday(&prog_start, &tz);
513 max_completion_time =
514 ((targets * packets * pkt_interval) + (targets * target_interval)) +
515 (targets * packets * crit.rta) + crit.rta;
516
517 if(debug) {
518 printf("packets: %u, targets: %u\n"
519 "target_interval: %0.3f, pkt_interval %0.3f\n"
520 "crit.rta: %0.3f\n"
521 "max_completion_time: %0.3f\n",
522 packets, targets,
523 (float)target_interval / 1000, (float)pkt_interval / 1000,
524 (float)crit.rta / 1000,
525 (float)max_completion_time / 1000);
523 } 526 }
524 527
525 if(count_flag) { 528 if(debug) {
526 alive_flag = unreachable_flag = 0; 529 if(max_completion_time > (u_int)timeout * 1000000) {
530 printf("max_completion_time: %llu timeout: %u\n",
531 max_completion_time, timeout);
532 printf("Timout must be at lest %llu\n",
533 max_completion_time / 1000000 + 1);
534 }
527 } 535 }
528 536
529 trials = (count > retry + 1) ? count : retry + 1; 537 icmp_pkt_size = icmp_data_size + ICMP_MINLEN;
530 538 if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
531 /* handle host names supplied on command line or in a file */ 539 if(icmp_pkt_size < sizeof(struct icmp) + sizeof(struct icmp_ping_data)) {
532 /* if the generate_flag is on, then generate the IP list */ 540 icmp_pkt_size = sizeof(struct icmp) + sizeof(struct icmp_ping_data);
533 argv = &argv[optind];
534
535 /* cover allowable conditions */
536
537 /* generate requires command line parameters beyond the switches */
538 if(generate_flag && !*argv) {
539 printf(_("Generate flag requires command line parameters beyond switches\n"));
540 print_usage();
541 } 541 }
542 542 if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
543 if(*argv && !generate_flag) { 543
544 while(*argv) { 544 if(debug) {
545 if(!(host_ptr->entry = malloc(strlen(*argv) + 1))) { 545 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
546 crash(_("Failed to allocate memory for hostname")); 546 crit.rta, crit.pl, warn.rta, warn.pl);
547 } 547 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
548 memset(host_ptr->entry, 0, strlen(*argv) + 1); 548 pkt_interval, target_interval, retry_interval);
549 host_ptr->entry = memcpy(host_ptr->entry, *argv, strlen(*argv)); 549 printf("icmp_pkt_size: %u timeout: %u\n",
550 if(!(host_ptr->next = malloc(sizeof(struct host_name_list)))) 550 icmp_pkt_size, timeout);
551 crash(_("Failed to allocate memory for hostname"));
552 host_ptr = host_ptr->next;
553 host_ptr->next = NULL;
554
555// add_name(*argv);
556 argv++;
557 }
558 } 551 }
559 552
560 // now add all the hosts 553 if(packets > 20) {
561 host_ptr = host_base_ptr; 554 errno = 0;
562 while(host_ptr->next) { 555 crash("packets is > 20 (%d)", packets);
563 add_name(host_ptr->entry);
564 host_ptr = host_ptr->next;
565 } 556 }
566 557
567 if(!num_hosts) { 558 host = list;
568 printf(_("No hosts to work with!\n\n")); 559 table = malloc(sizeof(struct rta_host **) * (argc - 1));
569 print_usage(); 560 i = 0;
561 while(host) {
562 host->id = i;
563 table[i] = host;
564 host = host->next;
565 i++;
570 } 566 }
571 567
572 /* allocate array to hold outstanding ping requests */ 568 run_checks();
573 table = (HOST_ENTRY **) malloc(sizeof(HOST_ENTRY *) * num_hosts);
574 if(!table) crash(_("Can't malloc array of hosts"));
575
576 cursor = rrlist;
577
578 for(num_jobs = 0; num_jobs < num_hosts; num_jobs++) {
579 table[num_jobs] = cursor;
580 cursor->i = num_jobs;
581
582 cursor = cursor->next;
583 } /* FOR */
584 569
585 ping_pkt_size = ping_data_size + SIZE_ICMP_HDR; 570 errno = 0;
571 finish(0);
586 572
587 signal(SIGINT, (void *)finish); 573 return(0);
588 574}
589 gettimeofday(&my_start_time, &tz);
590 current_time = my_start_time;
591
592 last_send_time.tv_sec = current_time.tv_sec - 10000;
593
594 cursor = rrlist;
595 advance = 0;
596
597 /* main loop */
598 while(num_jobs) {
599 /* fetch all packets that receive within time boundaries */
600 while(num_pingsent &&
601 cursor &&
602 cursor->num_sent > cursor->num_recv &&
603 wait_for_reply(sock)) ;
604
605 if(cursor && advance) {
606 cursor = cursor->next;
607 }
608
609 gettimeofday(&current_time, &tz);
610 lt = timeval_diff(&current_time, &last_send_time);
611 ht = timeval_diff(&current_time, &cursor->last_send_time);
612
613 advance = 1;
614
615 /* if it's OK to send while counting or looping or starting */
616 if(lt > interval) {
617 /* send if starting or looping */
618 if((cursor->num_sent == 0)) {
619 send_ping(sock, cursor);
620 continue;
621 } /* IF */
622
623 /* send if counting and count not exceeded */
624 if(count_flag) {
625 if(cursor->num_sent < count) {
626 send_ping(sock, cursor);
627 continue;
628 } /* IF */
629 } /* IF */
630 } /* IF */
631
632 /* is-it-alive mode, and timeout exceeded while waiting for a reply */
633 /* and we haven't exceeded our retries */
634 if((lt > interval) && !count_flag && !cursor->num_recv &&
635 (ht > timeout) && (cursor->waiting < retry + 1)) {
636 num_timeout++;
637
638 /* try again */
639 send_ping(sock, cursor);
640 continue;
641 } /* IF */
642
643 /* didn't send, can we remove? */
644
645 /* remove if counting and count exceeded */
646 if(count_flag) {
647 if((cursor->num_sent >= count)) {
648 remove_job(cursor);
649 continue;
650 } /* IF */
651 } /* IF */
652 else {
653 /* normal mode, and we got one */
654 if(cursor->num_recv) {
655 remove_job(cursor);
656 continue;
657 } /* IF */
658 575
659 /* normal mode, and timeout exceeded while waiting for a reply */ 576static void
660 /* and we've run out of retries, so node is unreachable */ 577run_checks()
661 if((ht > timeout) && (cursor->waiting >= retry + 1)) { 578{
662 num_timeout++; 579 u_int i, t, result;
663 remove_job(cursor); 580 u_int final_wait, time_passed;
581
582 /* this loop might actually violate the pkt_interval or target_interval
583 * settings, but only if there aren't any packets on the wire which
584 * indicates that the target can handle an increased packet rate */
585 for(i = 0; i < packets; i++) {
586 for(t = 0; t < targets; t++) {
587 /* don't send useless packets */
588 if(!targets_alive) finish(0);
589 if(table[t]->flags & FLAG_LOST_CAUSE) {
590 if(debug) printf("%s is a lost cause. not sending any more\n",
591 table[t]->name);
664 continue; 592 continue;
593 }
594
595 /* we're still in the game, so send next packet */
596 (void)send_icmp_ping(icmp_sock, table[t]);
597 result = wait_for_reply(icmp_sock, target_interval);
598 }
599 result = wait_for_reply(icmp_sock, pkt_interval * targets);
600 }
665 601
666 } /* IF */ 602 if(icmp_pkts_en_route && targets_alive) {
667 } /* ELSE */ 603 time_passed = get_timevaldiff(NULL, NULL);
604 final_wait = max_completion_time - time_passed;
668 605
669 /* could send to this host, so keep considering it */ 606 if(debug) {
670 if(ht > interval) { 607 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
671 advance = 0; 608 time_passed, final_wait, max_completion_time);
609 }
610 if(time_passed > max_completion_time) {
611 if(debug) printf("Time passed. Finishing up\n");
612 finish(0);
672 } 613 }
673 } /* WHILE */
674 614
675 finish(); 615 /* catch the packets that might come in within the timeframe, but
676 return 0; 616 * haven't yet */
677} /* main() */ 617 if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
618 final_wait, (float)final_wait / 1000);
619 result = wait_for_reply(icmp_sock, final_wait);
620 }
621}
678 622
679/************************************************************ 623/* response structure:
680 * Description: 624 * ip header : 20 bytes
681 * 625 * icmp header : 28 bytes
682 * Main program clean up and exit point 626 * icmp echo reply : the rest
683 ************************************************************/ 627 */
684void finish() 628static int
629wait_for_reply(int sock, u_int t)
685{ 630{
686 int i; 631 int n, hlen;
687 HOST_ENTRY *h; 632 static char buf[4096];
688 633 struct sockaddr_in resp_addr;
689 gettimeofday(&my_end_time, &tz); 634 struct ip *ip;
690 635 struct icmp *icp, *sent_icmp;
691 /* tot up unreachables */ 636 struct rta_host *host;
692 for(i=0; i<num_hosts; i++) { 637 struct icmp_ping_data *data;
693 h = table[i]; 638 struct timeval wait_start, now;
694 639 u_int tdiff, i, per_pkt_wait;
695 if(!h->num_recv) { 640
696 num_unreachable++; 641 /* if we can't listen or don't have anything to listen to, just return */
697 status = fin_stat = STATE_CRITICAL; 642 if(!t || !icmp_pkts_en_route) return 0;
698 if(num_hosts == 1) { 643
699 printf("CRITICAL - %s is down (lost 100%%)|" 644 gettimeofday(&wait_start, &tz);
700 "rta=;%d;%d;; pl=100%%;%d;%d;;\n", 645
701 h->host, 646 i = t;
702 warn.rta / 100, crit.rta / 100, 647 per_pkt_wait = t / icmp_pkts_en_route;
703 warn.pl, crit.pl); 648 while(icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
704 } 649 t = per_pkt_wait;
705 else { 650
706 printf(_("%s is down (lost 100%%)"), h->host); 651 /* wrap up if all targets are declared dead */
707 } 652 if(!targets_alive ||
653 get_timevaldiff(&prog_start, NULL) >= max_completion_time ||
654 (mode == MODE_HOSTCHECK && targets_down))
655 {
656 finish(0);
708 } 657 }
709 else {
710 /* reset the status */
711 status = STATE_OK;
712 658
713 /* check for warning before critical, for debugging purposes */ 659 /* reap responses until we hit a timeout */
714 if(warn.rta <= h->total_time / h->num_recv) { 660 n = recvfrom_wto(sock, buf, sizeof(buf),
715/* printf("warn.rta exceeded\n"); 661 (struct sockaddr *)&resp_addr, &t);
716*/ status = STATE_WARNING; 662 if(!n) {
717 } 663 if(debug > 1) {
718 if(warn.pl <= ((h->num_sent - h->num_recv) * 100) / h->num_sent) { 664 printf("recvfrom_wto() timed out during a %u usecs wait\n",
719/* printf("warn.pl exceeded (pl=%d)\n", 665 per_pkt_wait);
720 ((h->num_sent - h->num_recv) * 100) / h->num_sent);
721*/ status = STATE_WARNING;
722 }
723 if(crit.rta <= h->total_time / h->num_recv) {
724/* printf("crit.rta exceeded\n");
725*/ status = STATE_CRITICAL;
726 }
727 if(crit.pl <= ((h->num_sent - h->num_recv) * 100) / h->num_sent) {
728/* printf("crit.pl exceeded (pl=%d)\n",
729 ((h->num_sent - h->num_recv) * 100) / h->num_sent);
730*/ status = STATE_CRITICAL;
731 } 666 }
667 continue; /* timeout for this one, so keep trying */
668 }
669 if(n < 0) {
670 if(debug) printf("recvfrom_wto() returned errors\n");
671 return n;
672 }
732 673
733 if(num_hosts == 1 || status != STATE_OK) { 674 ip = (struct ip *)buf;
734 printf("%s - %s: rta %s ms, lost %d%%", 675 if(debug > 1) printf("received %u bytes from %s\n",
735 state_text(status), h->host, 676 ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr));
736 sprint_tm(h->total_time / h->num_recv), 677
737 h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0 678/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
738 ); 679/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
739 /* perfdata only available for single-host stuff */ 680 /* alpha headers are decidedly broken. Using an ansi compiler,
740 if(num_hosts == 1) { 681 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
741 printf("|rta=%sms;%d;%d;; pl=%d%%;%d;%d;;\n", 682 * off the bottom 4 bits */
742 sprint_tm(h->total_time / h->num_recv), warn.rta / 100, crit.rta / 100, 683/* hlen = (ip->ip_vhl & 0x0f) << 2; */
743 h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0, warn.pl, crit.pl 684/* #else */
744 ); 685 hlen = ip->ip_hl << 2;
745 } 686/* #endif */
746 else printf(" :: "); 687
747 } 688 if(n < (hlen + ICMP_MINLEN)) {
689 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
690 n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr));
691 }
692 /* else if(debug) { */
693 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
694 /* hlen, ntohs(ip->ip_len) - hlen, */
695 /* sizeof(struct ip), icmp_pkt_size); */
696 /* } */
697
698 /* check the response */
699 icp = (struct icmp *)(buf + hlen);
700 sent_icmp = (struct icmp *)(buf + hlen + ICMP_MINLEN);
701 /* printf("buf: %p, icp: %p, distance: %u (expected %u)\n", */
702 /* buf, icp, */
703 /* (u_int)icp - (u_int)buf, hlen); */
704 /* printf("buf: %p, sent_icmp: %p, distance: %u (expected %u)\n", */
705 /* buf, sent_icmp, */
706 /* (u_int)sent_icmp - (u_int)buf, hlen + ICMP_MINLEN); */
707
708 if(icp->icmp_id != pid) {
709 handle_random_icmp(icp, &resp_addr);
710 continue;
711 }
748 712
749 /* fin_stat should always hold the WORST state */ 713 if(icp->icmp_type != ICMP_ECHOREPLY || icp->icmp_seq >= targets) {
750 if(fin_stat != STATE_CRITICAL && status != STATE_OK) { 714 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
751 fin_stat = status; 715 handle_random_icmp(icp, &resp_addr);
752 } 716 continue;
753 } 717 }
754 }
755 718
756 if(num_noaddress) { 719 /* this is indeed a valid response */
757 printf(_("No hostaddress specified.\n")); 720 data = (struct icmp_ping_data *)(icp->icmp_data);
758 print_usage();
759 }
760 else if(num_alive != num_hosts) {
761 /* for future multi-check support */
762 /*printf("num_alive != num_hosts (%d : %d)\n", num_alive, num_hosts);*/
763 fin_stat = STATE_CRITICAL;
764 }
765 721
766 if(num_hosts > 1) { 722 host = table[icp->icmp_seq];
767 if(num_alive == num_hosts) { 723 gettimeofday(&now, &tz);
768 printf(_("OK - All %d hosts are alive\n"), num_hosts); 724 tdiff = get_timevaldiff(&data->stime, &now);
725
726 host->time_waited += tdiff;
727 host->icmp_recv++;
728 icmp_recv++;
729
730 if(debug) {
731 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u\n",
732 (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr),
733 ttl, ip->ip_ttl);
769 } 734 }
770 else { 735
771 printf(_("CRITICAL - %d of %d hosts are alive\n"), num_alive, num_hosts); 736 /* if we're in hostcheck mode, exit with limited printouts */
737 if(mode == MODE_HOSTCHECK) {
738 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
739 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
740 host->name, icmp_recv, (float)tdiff / 1000,
741 icmp_recv, packets, (float)tdiff / 1000,
742 (float)warn.rta / 1000, (float)crit.rta / 1000);
743 exit(STATE_OK);
772 } 744 }
773 } 745 }
774 exit(fin_stat);
775}
776 746
747 return 0;
748}
777 749
778void send_ping(int lsock, HOST_ENTRY *h) 750/* the ping functions */
751static int
752send_icmp_ping(int sock, struct rta_host *host)
779{ 753{
780 char *buffer; 754 static char *buf = NULL; /* re-use so we prevent leaks */
755 long int len;
781 struct icmp *icp; 756 struct icmp *icp;
782 PING_DATA *pdp; 757 struct icmp_ping_data *data;
783 int n; 758 struct timeval tv;
784 759 struct sockaddr *addr;
785 buffer = (char *)malloc((size_t) ping_pkt_size);
786 if(!buffer)
787 crash(_("Can't malloc ping packet"));
788 760
789 memset(buffer, 0, ping_pkt_size * sizeof(char)); 761
790 icp = (struct icmp *)buffer; 762 if(sock == -1) {
763 errno = 0;
764 crash("Attempt to send on bogus socket");
765 return -1;
766 }
767 addr = (struct sockaddr *)&host->saddr_in;
768
769 if(!buf) {
770 buf = (char *)malloc(icmp_pkt_size + sizeof(struct ip));
771 if(!buf) {
772 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
773 icmp_pkt_size);
774 return -1; /* might be reached if we're in debug mode */
775 }
776 }
777 memset(buf, 0, icmp_pkt_size + sizeof(struct ip));
791 778
792 gettimeofday(&h->last_send_time, &tz); 779 if((gettimeofday(&tv, &tz)) == -1) return -1;
793 780
781 icp = (struct icmp *)buf;
794 icp->icmp_type = ICMP_ECHO; 782 icp->icmp_type = ICMP_ECHO;
795 icp->icmp_code = 0; 783 icp->icmp_code = 0;
796 icp->icmp_cksum = 0; 784 icp->icmp_cksum = 0;
797 icp->icmp_seq = h->i; 785 icp->icmp_id = pid;
798 icp->icmp_id = ident; 786 icp->icmp_seq = host->id;
799 787 data = (struct icmp_ping_data *)icp->icmp_data;
800 pdp = (PING_DATA *) (buffer + SIZE_ICMP_HDR); 788 data->ping_id = 10; /* host->icmp.icmp_sent; */
801 pdp->ping_ts = h->last_send_time; 789 memcpy(&data->stime, &tv, sizeof(struct timeval));
802 pdp->ping_count = h->num_sent; 790 icp->icmp_cksum = icmp_checksum((u_short *)icp, icmp_pkt_size);
803 791
804 icp->icmp_cksum = in_cksum((u_short *) icp, ping_pkt_size); 792 len = sendto(sock, buf, icmp_pkt_size, 0, (struct sockaddr *)addr,
805 793 sizeof(struct sockaddr));
806 n = sendto(lsock, buffer, ping_pkt_size, 0, 794
807 (struct sockaddr *)&h->saddr, sizeof(struct sockaddr_in)); 795 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
808 796 if(debug) printf("Failed to send ping to %s\n",
809 if(n < 0 || (unsigned int)n != ping_pkt_size) { 797 inet_ntoa(host->saddr_in.sin_addr));
810 if(unreachable_flag) { 798 return -1;
811 printf(_("%s error while sending ping: %s\n"), 799 }
812 h->host, strerror(errno));
813 } /* IF */
814
815 num_unreachable++;
816 remove_job(h);
817 } /* IF */
818 else {
819 /* mark this trial as outstanding */
820 h->resp_times[h->num_sent] = RESP_WAITING;
821 800
822 h->num_sent++; 801 icmp_sent++;
823 h->waiting++; 802 host->icmp_sent++;
824 num_pingsent++;
825 last_send_time = h->last_send_time;
826 } /* ELSE */
827 803
828 free(buffer); 804 return 0;
829} /* send_ping() */ 805}
830 806
831int wait_for_reply(int lsock) 807static int
808recvfrom_wto(int sock, char *buf, unsigned int len, struct sockaddr *saddr,
809 u_int *timo)
832{ 810{
833 int result; 811 u_int slen;
834 static char buffer[4096];
835 struct sockaddr_in response_addr;
836 struct ip *ip;
837 int hlen;
838 struct icmp *icp;
839 int n; 812 int n;
840 HOST_ENTRY *h = NULL; 813 struct timeval to, then, now;
841 long this_reply; 814 fd_set rd, wr;
842 int this_count;
843 struct timeval sent_time;
844
845 result = recvfrom_wto(lsock, buffer, sizeof(buffer),
846 (struct sockaddr *)&response_addr, select_time);
847
848 if(result < 0) return 0; /* timeout */
849
850 ip = (struct ip *)buffer;
851
852#if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ )
853 /* The alpha headers are decidedly broken.
854 * Using an ANSI compiler, it provides ip_vhl instead of ip_hl and
855 * ip_v. So, to get ip_hl, we mask off the bottom four bits.
856 */
857 hlen = (ip->ip_vhl & 0x0F) << 2;
858#else
859 hlen = ip->ip_hl << 2;
860#endif /* defined(__alpha__) && __STDC__ */
861 815
862 if(result < hlen + ICMP_MINLEN) { 816 if(!*timo) {
863 printf(_("Received packet too short for ICMP (%d bytes from %s)\n"), result, 817 if(debug) printf("*timo is not\n");
864 inet_ntoa(response_addr.sin_addr));
865
866 return (1); /* too short */
867 } /* IF */
868
869 icp = (struct icmp *)(buffer + hlen);
870 if(icp->icmp_type != ICMP_ECHOREPLY) {
871 /* handle some problem */
872 if(handle_random_icmp(icp, &response_addr))
873 num_othericmprcvd++;
874
875 return 1;
876 } /* IF */
877
878 if(icp->icmp_id != ident)
879 return 1; /* packet received, but not the one we are looking for! */
880
881 num_pingreceived++;
882
883 if(icp->icmp_seq >= (n_short) num_hosts)
884 return(1); /* packet received, don't worry about it anymore */
885
886 n = icp->icmp_seq;
887 h = table[n];
888
889 /* received ping is cool, so process it */
890
891 gettimeofday(&current_time, &tz);
892 h->waiting = 0;
893 h->num_recv++;
894
895 memcpy(&sent_time, icp->icmp_data + offsetof(PING_DATA, ping_ts),
896 sizeof(sent_time));
897 memcpy(&this_count, icp->icmp_data, sizeof(this_count));
898
899 this_reply = timeval_diff(&current_time, &sent_time);
900 h->total_time += this_reply;
901 total_replies++;
902
903 /* note reply time in array, probably */
904 if((this_count >= 0) && ((unsigned int)this_count < trials)) {
905 if(h->resp_times[this_count] != RESP_WAITING) {
906 printf(_("%s : duplicate for [%d], %d bytes, %s ms"),
907 h->host, this_count, result, sprint_tm(this_reply));
908
909 if(response_addr.sin_addr.s_addr != h->saddr.sin_addr.s_addr)
910 printf(" [<- %s]\n", inet_ntoa(response_addr.sin_addr));
911 } /* IF */
912 else h->resp_times[this_count] = this_reply;
913 } /* IF */
914 else {
915 /* count is out of bounds?? */
916 printf(_("%s : duplicate for [%d], %d bytes, %s ms\n"),
917 h->host, this_count, result, sprint_tm(this_reply));
918 } /* ELSE */
919
920 if(h->num_recv == 1) {
921 num_alive++;
922 } /* IF */
923
924 return num_jobs;
925} /* wait_for_reply() */
926
927int handle_random_icmp(struct icmp *p, struct sockaddr_in *addr)
928{
929 struct icmp *sent_icmp;
930 u_char *c;
931 HOST_ENTRY *h;
932
933 c = (u_char *) p;
934 switch (p->icmp_type) {
935 case ICMP_UNREACH:
936 sent_icmp = (struct icmp *)(c + 28);
937
938 if((sent_icmp->icmp_type == ICMP_ECHO) &&
939 (sent_icmp->icmp_id == ident) &&
940 (sent_icmp->icmp_seq < (n_short) num_hosts)) {
941 /* this is a response to a ping we sent */
942 h = table[sent_icmp->icmp_seq];
943
944 if(p->icmp_code > ICMP_UNREACH_MAXTYPE) {
945 printf(_("ICMP Unreachable (Invalid Code) from %s for ICMP Echo sent to %s"),
946 inet_ntoa(addr->sin_addr), h->host);
947
948 } /* IF */
949 else {
950 printf(_("ICMP Unreachable from %s for ICMP Echo sent to %s"),
951 inet_ntoa(addr->sin_addr), h->host);
952
953 } /* ELSE */
954
955 if(inet_addr(h->host) == INADDR_NONE)
956 printf(" (%s)", inet_ntoa(h->saddr.sin_addr));
957
958 printf("\n");
959
960 } /* IF */
961
962 return 1;
963
964 case ICMP_SOURCEQUENCH:
965 case ICMP_REDIRECT:
966 case ICMP_TIMXCEED:
967 case ICMP_PARAMPROB:
968 sent_icmp = (struct icmp *)(c + 28);
969 if((sent_icmp->icmp_type = ICMP_ECHO) &&
970 (sent_icmp->icmp_id = ident) &&
971 (sent_icmp->icmp_seq < (n_short) num_hosts)) {
972 /* this is a response to a ping we sent */
973 h = table[sent_icmp->icmp_seq];
974 printf(_("ICMP Unreachable from %s for ICMP Echo sent to %s"),
975 inet_ntoa(addr->sin_addr), h->host);
976
977 if(inet_addr(h->host) == INADDR_NONE)
978 printf(" (%s)", inet_ntoa(h->saddr.sin_addr));
979
980 printf("\n");
981 } /* IF */
982
983 return 2;
984
985 /* no way to tell whether any of these are sent due to our ping */
986 /* or not (shouldn't be, of course), so just discard */
987 case ICMP_TSTAMP:
988 case ICMP_TSTAMPREPLY:
989 case ICMP_IREQ:
990 case ICMP_IREQREPLY:
991 case ICMP_MASKREQ:
992 case ICMP_MASKREPLY:
993 default:
994 return 0; 818 return 0;
819 }
995 820
996 } /* SWITCH */ 821 to.tv_sec = *timo / 1000000;
997 822 to.tv_usec = (*timo - (to.tv_sec * 1000000));
998} /* handle_random_icmp() */
999
1000int in_cksum(u_short * p, int n)
1001{
1002 register u_short answer;
1003 register long sum = 0;
1004 u_short odd_byte = 0;
1005
1006 while(n > 1) {
1007 sum += *p++;
1008 n -= 2;
1009 } /* WHILE */
1010
1011 /* mop up an odd byte, if necessary */
1012 if(n == 1) {
1013 *(u_char *) (&odd_byte) = *(u_char *) p;
1014 sum += odd_byte;
1015 } /* IF */
1016
1017 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1018 sum += (sum >> 16); /* add carry */
1019 answer = ~sum; /* ones-complement, truncate */
1020
1021 return (answer);
1022
1023} /* in_cksum() */
1024
1025void add_name(char *name)
1026{
1027 struct hostent *host_ent;
1028 int ipaddress;
1029 struct in_addr *ipa = (struct in_addr *)&ipaddress;
1030 struct in_addr *host_add;
1031 char *nm;
1032 int i = 0;
1033
1034 if((ipaddress = inet_addr(name)) != -1) {
1035 /* input name is an IP addr, go with it */
1036 if(name_flag) {
1037 if(addr_flag)
1038 add_addr(name, na_cat(get_host_by_address(*ipa), *ipa), *ipa);
1039 else {
1040 nm = cpystr(get_host_by_address(*ipa));
1041 add_addr(name, nm, *ipa);
1042
1043 } /* ELSE */
1044 } /* IF */
1045 else add_addr(name, name, *ipa);
1046
1047 return;
1048 } /* IF */
1049
1050 /* input name is not an IP addr, maybe it's a host name */
1051 host_ent = gethostbyname(name);
1052 if(host_ent == NULL) {
1053 if(h_errno == TRY_AGAIN) {
1054 u_sleep(DNS_TIMEOUT);
1055 host_ent = gethostbyname(name);
1056 } /* IF */
1057
1058 if(host_ent == NULL) {
1059 printf(_("%s address not found\n"), name);
1060 num_noaddress++;
1061 return;
1062 } /* IF */
1063 } /* IF */
1064
1065 host_add = (struct in_addr *)*(host_ent->h_addr_list);
1066 if(host_add == NULL) {
1067 printf(_("%s has no address data\n"), name);
1068 num_noaddress++;
1069 return;
1070 } /* IF */
1071 else {
1072 /* it is indeed a hostname with a real address */
1073 while(host_add) {
1074 if(name_flag && addr_flag)
1075 add_addr(name, na_cat(name, *host_add), *host_add);
1076 else if(addr_flag) {
1077 nm = cpystr(inet_ntoa(*host_add));
1078 add_addr(name, nm, *host_add);
1079 } /* ELSE IF */
1080 else {
1081 add_addr(name, name, *host_add);
1082 }
1083
1084 if(!multif_flag) break;
1085
1086 host_add = (struct in_addr *)(host_ent->h_addr_list[++i]);
1087 } /* WHILE */
1088 } /* ELSE */
1089} /* add_name() */
1090
1091
1092char *na_cat(char *name, struct in_addr ipaddr)
1093{
1094 char *nm, *as;
1095
1096 as = inet_ntoa(ipaddr);
1097 nm = (char *)malloc(strlen(name) + strlen(as) + 4);
1098
1099 if(!nm)
1100 crash(_("Can't allocate some space for a string"));
1101 823
1102 strcpy(nm, name); 824 FD_ZERO(&rd);
1103 strcat(nm, " ("); 825 FD_ZERO(&wr);
1104 strcat(nm, as); 826 FD_SET(sock, &rd);
1105 strcat(nm, ")"); 827 errno = 0;
828 gettimeofday(&then, &tz);
829 n = select(sock + 1, &rd, &wr, NULL, &to);
830 if(n < 0) crash("select() in recvfrom_wto");
831 gettimeofday(&now, &tz);
832 *timo = get_timevaldiff(&then, &now);
1106 833
1107 return (nm); 834 if(!n) return 0; /* timeout */
1108 835
1109} /* na_cat() */ 836 slen = sizeof(struct sockaddr);
1110 837
838 return recvfrom(sock, buf, len, 0, saddr, &slen);
839}
1111 840
1112void add_addr(char *name, char *host, struct in_addr ipaddr) 841static void
842finish(int sig)
1113{ 843{
1114 HOST_ENTRY *p; 844 u_int i = 0;
1115 unsigned int n; 845 unsigned char pl;
1116 int *i; 846 double rta;
1117 847 struct rta_host *host;
1118 if(!(p = (HOST_ENTRY *) malloc(sizeof(HOST_ENTRY)))) { 848 char *status_string[] =
1119 crash(_("Can't allocate HOST_ENTRY")); 849 {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
850
851 alarm(0);
852 if(debug > 1) printf("finish(%d) called\n", sig);
853
854 if(icmp_sock != -1) close(icmp_sock);
855 if(udp_sock != -1) close(udp_sock);
856 if(tcp_sock != -1) close(tcp_sock);
857
858 if(debug) {
859 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
860 icmp_sent, icmp_recv, icmp_lost);
861 printf("targets: %u targets_alive: %u\n", targets, targets_alive);
1120 } 862 }
1121 863
1122 memset((char *)p, 0, sizeof(HOST_ENTRY)); 864 /* iterate thrice to calculate values, give output, and print perfparse */
1123 865 host = list;
1124 p->name = name; 866 while(host) {
1125 p->host = host; 867 if(!host->icmp_recv) {
1126 p->saddr.sin_family = AF_INET; 868 /* rta 0 is ofcourse not entirely correct, but will still show up
1127 p->saddr.sin_addr = ipaddr; 869 * conspicuosly as missing entries in perfparse and cacti */
1128 p->running = 1; 870 pl = 100;
871 rta = 0;
872 status = STATE_CRITICAL;
873 /* up the down counter if not already counted */
874 if(!(host->flags & FLAG_LOST_CAUSE) && targets_alive) targets_down++;
875 }
876 else {
877 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
878 rta = (double)host->time_waited / host->icmp_recv;
879 }
880 host->pl = pl;
881 host->rta = rta;
882 if(!status && (pl >= warn.pl || rta >= warn.rta)) status = STATE_WARNING;
883 if(pl >= crit.pl || rta >= crit.rta) status = STATE_CRITICAL;
1129 884
1130 /* array for response time results */ 885 host = host->next;
1131 if(!(i = (int *)malloc(trials * sizeof(int)))) {
1132 crash(_("Can't allocate resp_times array"));
1133 } 886 }
887 /* this is inevitable */
888 if(!targets_alive) status = STATE_CRITICAL;
889 printf("%s - ", status_string[status]);
890
891 host = list;
892 while(host) {
893 if(debug) puts("");
894 if(i) {
895 if(i < targets) printf(" :: ");
896 else printf("\n");
897 }
898 i++;
899 if(!host->icmp_recv) {
900 status = STATE_CRITICAL;
901 if(host->flags & FLAG_LOST_CAUSE) {
902 printf("%s: %s @ %s. rta nan, lost %d%%",
903 host->name,
904 get_icmp_error_msg(host->icmp_type, host->icmp_code),
905 inet_ntoa(host->error_addr),
906 100);
907 }
908 else { /* not marked as lost cause, so we have no flags for it */
909 printf("%s: rta nan, lost 100%%", host->name);
910 }
911 }
912 else { /* !icmp_recv */
913 printf("%s: rta %0.3fms, lost %u%%",
914 host->name, host->rta / 1000, host->pl);
915 }
1134 916
1135 for(n = 1; n < trials; n++) 917 host = host->next;
1136 i[n] = RESP_UNUSED; 918 }
1137
1138 p->resp_times = i;
1139
1140 if(!rrlist) {
1141 rrlist = p;
1142 p->next = p;
1143 p->prev = p;
1144 } /* IF */
1145 else {
1146 p->next = rrlist;
1147 p->prev = rrlist->prev;
1148 p->prev->next = p;
1149 p->next->prev = p;
1150 } /* ELSE */
1151
1152 num_hosts++;
1153} /* add_addr() */
1154
1155
1156void remove_job(HOST_ENTRY * h)
1157{
1158 h->running = 0;
1159 h->waiting = 0;
1160 num_jobs--;
1161
1162
1163 if(num_jobs) {
1164 /* remove us from list of active jobs */
1165 h->prev->next = h->next;
1166 h->next->prev = h->prev;
1167 if(h == cursor) cursor = h->next;
1168 } /* IF */
1169 else {
1170 cursor = NULL;
1171 rrlist = NULL;
1172 } /* ELSE */
1173
1174} /* remove_job() */
1175
1176
1177char *get_host_by_address(struct in_addr in)
1178{
1179 struct hostent *h;
1180 h = gethostbyaddr((char *)&in, sizeof(struct in_addr), AF_INET);
1181 919
1182 if(h == NULL || h->h_name == NULL) 920 /* iterate once more for pretty perfparse output */
1183 return inet_ntoa(in); 921 printf("|");
1184 else 922 i = 0;
1185 return (char *)h->h_name; 923 host = list;
924 while(host) {
925 if(debug) puts("");
926 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; ",
927 (targets > 1) ? host->name : "",
928 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000,
929 (targets > 1) ? host->name : "",
930 host->pl, warn.pl, crit.pl);
931
932 host = host->next;
933 }
1186 934
1187} /* get_host_by_address() */ 935 /* finish with an empty line */
936 puts("");
937 if(debug) printf("targets: %u, targets_alive: %u\n",
938 targets, targets_alive);
1188 939
940 exit(status);
941}
1189 942
1190char *cpystr(char *string) 943static u_int
944get_timevaldiff(struct timeval *early, struct timeval *later)
1191{ 945{
1192 char *dst; 946 u_int ret;
1193 947 struct timeval now;
1194 if(string) {
1195 dst = (char *)malloc(1 + strlen(string));
1196 if(!dst) crash(_("malloc() failed!"));
1197
1198 strcpy(dst, string);
1199 return dst;
1200
1201 } /* IF */
1202 else return NULL;
1203
1204} /* cpystr() */
1205 948
949 if(!later) {
950 gettimeofday(&now, &tz);
951 later = &now;
952 }
953 if(!early) early = &prog_start;
1206 954
1207void crash(char *msg) 955 /* if early > later we return 0 so as to indicate a timeout */
1208{ 956 if(early->tv_sec > early->tv_sec ||
1209 if(errno || h_errno) { 957 (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec))
1210 if(errno) 958 {
1211 printf("%s: %s : %s\n", prog, msg, strerror(errno)); 959 return 0;
1212 if(h_errno)
1213 printf(_("%s: %s : A network error occurred\n"), prog, msg);
1214 } 960 }
1215 else printf("%s: %s\n", prog, msg);
1216 961
1217 exit(STATE_UNKNOWN); 962 ret = (later->tv_sec - early->tv_sec) * 1000000;
1218} /* crash() */ 963 ret += later->tv_usec - early->tv_usec;
1219 964
965 return ret;
966}
1220 967
1221long timeval_diff(struct timeval *a, struct timeval *b) 968static int
969add_target_ip(char *arg, struct in_addr *in)
1222{ 970{
1223 double temp; 971 struct rta_host *host;
1224 972
1225 temp = (((a->tv_sec * 1000000) + a->tv_usec) - 973 /* disregard obviously stupid addresses */
1226 ((b->tv_sec * 1000000) + b->tv_usec)) / 10; 974 if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY)
1227 975 return -1;
1228 return (long)temp; 976
1229 977 /* no point in adding two identical IP's, so don't. ;) */
1230} /* timeval_diff() */ 978 host = list;
1231 979 while(host) {
980 if(host->saddr_in.sin_addr.s_addr == in->s_addr) {
981 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
982 return -1;
983 }
984 host = host->next;
985 }
1232 986
1233char *sprint_tm(int t) 987 /* add the fresh ip */
1234{ 988 host = malloc(sizeof(struct rta_host));
1235 static char buf[10]; 989 if(!host) {
990 crash("add_target_ip(%s, %s): malloc(%d) failed",
991 arg, inet_ntoa(*in), sizeof(struct rta_host));
992 }
993 memset(host, 0, sizeof(struct rta_host));
1236 994
1237 /* <= 0.99 ms */ 995 /* set the values. use calling name for output */
1238 if(t < 100) { 996 host->name = strdup(arg);
1239 sprintf(buf, "0.%02d", t);
1240 return (buf);
1241 } /* IF */
1242 997
1243 /* 1.00 - 9.99 ms */ 998 /* fill out the sockaddr_in struct */
1244 if(t < 1000) { 999 host->saddr_in.sin_family = AF_INET;
1245 sprintf(buf, "%d.%02d", t / 100, t % 100); 1000 host->saddr_in.sin_addr.s_addr = in->s_addr;
1246 return (buf);
1247 } /* IF */
1248 1001
1249 /* 10.0 - 99.9 ms */ 1002 if(!list) list = cursor = host;
1250 if(t < 10000) { 1003 else cursor->next = host;
1251 sprintf(buf, "%d.%d", t / 100, (t % 100) / 10);
1252 return (buf);
1253 } /* IF */
1254 1004
1255 /* >= 100 ms */ 1005 cursor = host;
1256 sprintf(buf, "%d", t / 100); 1006 targets++;
1257 return (buf);
1258} /* sprint_tm() */
1259 1007
1008 return 0;
1009}
1260 1010
1261/* 1011/* wrapper for add_target_ip */
1262 * select() is posix, so we expect it to be around 1012static int
1263 */ 1013add_target(char *arg)
1264void u_sleep(int u_sec)
1265{ 1014{
1266 int nfound; 1015 int i;
1267 struct timeval to; 1016 struct hostent *he;
1268 fd_set readset, writeset; 1017 struct in_addr *in, ip;
1269 1018
1270 to.tv_sec = u_sec / 1000000; 1019 /* don't resolve if we don't have to */
1271 to.tv_usec = u_sec - (to.tv_sec * 1000000); 1020 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) {
1272/* printf("u_sleep :: to.tv_sec: %d, to_tv_usec: %d\n", 1021 /* don't add all ip's if we were given a specific one */
1273 (int)to.tv_sec, (int)to.tv_usec); 1022 return add_target_ip(arg, &ip);
1274*/ 1023 /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */
1275 FD_ZERO(&writeset); 1024 /* if(!he) return add_target_ip(arg, in); */
1276 FD_ZERO(&readset); 1025 }
1277 nfound = select(0, &readset, &writeset, NULL, &to); 1026 else {
1278 if(nfound < 0) 1027 errno = 0;
1279 crash(_("select() in u_sleep:")); 1028 he = gethostbyname(arg);
1280 1029 if(!he) {
1281 return; 1030 errno = 0;
1282} /* u_sleep() */ 1031 crash("Failed to resolve %s", arg);
1283 1032 return -1;
1284 1033 }
1285/************************************************************
1286 * Description:
1287 *
1288 * receive with timeout
1289 * returns length of data read or -1 if timeout
1290 * crash on any other errrors
1291 ************************************************************/
1292/* TODO: add MSG_DONTWAIT to recvfrom flags (currently 0) */
1293int recvfrom_wto(int lsock, char *buf, int len, struct sockaddr *saddr, int timo)
1294{
1295 int nfound = 0, slen, n;
1296 struct timeval to;
1297 fd_set readset, writeset;
1298
1299 to.tv_sec = timo / 1000000;
1300 to.tv_usec = (timo - (to.tv_sec * 1000000)) * 10;
1301
1302/* printf("to.tv_sec: %d, to.tv_usec: %d\n", (int)to.tv_sec, (int)to.tv_usec);
1303*/
1304
1305 FD_ZERO(&readset);
1306 FD_ZERO(&writeset);
1307 FD_SET(lsock, &readset);
1308 nfound = select(lsock + 1, &readset, &writeset, NULL, &to);
1309 if(nfound < 0) crash(_("select() in recvfrom_wto"));
1310
1311 if(nfound == 0) return -1; /* timeout */
1312
1313 if(nfound) {
1314 slen = sizeof(struct sockaddr);
1315 n = recvfrom(sock, buf, len, 0, saddr, &slen);
1316 if(n < 0) crash(_("recvfrom"));
1317 return(n);
1318 } 1034 }
1319 1035
1320 return(0); /* 0 bytes read, so return it */ 1036 /* possibly add all the IP's as targets */
1321} /* recvfrom_wto() */ 1037 for(i = 0; he->h_addr_list[i]; i++) {
1038 in = (struct in_addr *)he->h_addr_list[i];
1039 add_target_ip(arg, in);
1322 1040
1041 /* this is silly, but it works */
1042 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
1043 printf("mode: %d\n", mode);
1044 continue;
1045 }
1046 break;
1047 }
1323 1048
1049 return 0;
1050}
1324/* 1051/*
1325 * u = micro 1052 * u = micro
1326 * m = milli 1053 * m = milli
1327 * s = seconds 1054 * s = seconds
1055 * return value is in microseconds
1328 */ 1056 */
1329int get_threshold(char *str, threshold *th) 1057static u_int
1058get_timevar(const char *str)
1059{
1060 char p, u, *ptr;
1061 unsigned int len;
1062 u_int i, d; /* integer and decimal, respectively */
1063 u_int factor = 1000; /* default to milliseconds */
1064
1065 if(!str) return 0;
1066 len = strlen(str);
1067 if(!len) return 0;
1068
1069 /* unit might be given as ms|m (millisec),
1070 * us|u (microsec) or just plain s, for seconds */
1071 u = p = '\0';
1072 u = str[len - 1];
1073 if(len >= 2 && !isdigit((int)str[len - 2])) p = str[len - 2];
1074 if(p && u == 's') u = p;
1075 else if(!p) p = u;
1076 if(debug > 2) printf("evaluating %s, u: %c, p: %c\n", str, u, p);
1077
1078 if(u == 'u') factor = 1; /* microseconds */
1079 else if(u == 'm') factor = 1000; /* milliseconds */
1080 else if(u == 's') factor = 1000000; /* seconds */
1081 if(debug > 2) printf("factor is %u\n", factor);
1082
1083 i = strtoul(str, &ptr, 0);
1084 if(!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
1085 return i * factor;
1086
1087 /* time specified in usecs can't have decimal points, so ignore them */
1088 if(factor == 1) return i;
1089
1090 d = strtoul(ptr + 1, NULL, 0);
1091
1092 /* d is decimal, so get rid of excess digits */
1093 while(d >= factor) d /= 10;
1094
1095 /* the last parenthesis avoids floating point exceptions. */
1096 return ((i * factor) + (d * (factor / 10)));
1097}
1098
1099/* not too good at checking errors, but it'll do (main() should barfe on -1) */
1100static int
1101get_threshold(char *str, threshold *th)
1330{ 1102{
1331 unsigned int i, factor = 0; 1103 char *p = NULL, i = 0;
1332 char *p = NULL;
1333 1104
1334 if(!str || !strlen(str) || !th) return -1; 1105 if(!str || !strlen(str) || !th) return -1;
1335 1106
1336 for(i=0; i<strlen(str); i++) { 1107 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1337 /* we happily accept decimal points in round trip time thresholds, 1108 p = &str[strlen(str) - 1];
1338 * but we ignore them quite blandly. The new way of specifying higher 1109 while(p != &str[1]) {
1339 * precision is to specify 'u' (for microseconds), 1110 if(*p == '%') *p = '\0';
1340 * 'm' (for millisecs - default) or 's' for seconds. */ 1111 else if(*p == ',' && i) {
1341 if(!p && !factor) { 1112 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1342 if(str[i] == 's') factor = 1000000; /* seconds */ 1113 th->pl = (unsigned char)strtoul(p+1, NULL, 0);
1343 else if(str[i] == 'm') factor = 1000; /* milliseconds */ 1114 break;
1344 else if(str[i] == 'u') factor = 1; /* microseconds */
1345 }
1346
1347 if(str[i] == '%') str[i] = '\0';
1348 else if(str[i] == ',' && !p && i != (strlen(str) - 1)) {
1349 p = &str[i+1];
1350 str[i] = '\0';
1351 } 1115 }
1116 i = 1;
1117 p--;
1352 } 1118 }
1119 th->rta = get_timevar(str);
1120
1121 if(!th->rta) return -1;
1353 1122
1354 /* default to milliseconds */ 1123 if(th->rta > MAXTTL * 1000000) th->rta = MAXTTL * 1000000;
1355 if(!factor) factor = 1000; 1124 if(th->pl > 100) th->pl = 100;
1356 1125
1357 if(!p || !strlen(p)) return -1;
1358 th->rta = (unsigned int)strtoul(str, NULL, 0) * factor;
1359 th->pl = (unsigned int)strtoul(p, NULL, 0);
1360 return 0; 1126 return 0;
1361} 1127}
1362 1128
1363void 1129unsigned short
1364print_help (void) 1130icmp_checksum(unsigned short *p, int n)
1365{ 1131{
1366 print_revision (progname, revision); 1132 register unsigned short cksum;
1133 register long sum = 0;
1367 1134
1368 printf ("Copyright (c) 2004 Andreas Ericsson <ae@op5.se>\n"); 1135 while(n > 1) {
1369 printf (COPYRIGHT, copyright, email); 1136 sum += *p++;
1137 n -= 2;
1138 }
1370 1139
1371 printf (_("This plugin will check hosts sending icmp pings\n\n")); 1140 /* mop up the occasional odd byte */
1141 if(n == 1) sum += (unsigned char)*p;
1372 1142
1373 print_usage (); 1143 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1144 sum += (sum >> 16); /* add carry */
1145 cksum = ~sum; /* ones-complement, trunc to 16 bits */
1374 1146
1375 printf (_(UT_HELP_VRSN)); 1147 return cksum;
1376
1377 printf (_("\
1378 -H, \n\
1379 Host name argument for servers\n\
1380 -b \n\
1381 ping packet size in bytes (default %d)\n\
1382 -n \n\
1383 number of pings to send to each target (default %d)\n\
1384 -r \n\
1385 number of retries (default %d)\n\
1386 -t \n\
1387 timeout value (in msec) (default %d)\n\
1388 -i \n\
1389 packet interval (in msec) (default %d)\n\
1390 -w \n\
1391 warning threshold pair, given as RTA[ums],PL[%%]\n\
1392 -c \n\
1393 critical threshold pair, given as RTA[ums],PL[%%]\n\
1394 -D \n\
1395 increase debug output level\n\n"),ping_data_size,count,retry,(timeout / 100),DEFAULT_INTERVAL);
1396
1397 printf (_(UT_WARN_CRIT));
1398
1399 printf (_(UT_TIMEOUT), DEFAULT_SOCKET_TIMEOUT);
1400
1401 printf (_(UT_VERBOSE));
1402
1403// printf (_("This plugin will check hosts sending icmp pings\n"));
1404
1405 printf (_(UT_SUPPORT));
1406} 1148}
1407 1149
1408void 1150/* make core plugin developers happy (silly, really) */
1409print_usage (void) 1151static void
1152usage(unsigned char arg, char *msg)
1410{ 1153{
1411 printf ("\ 1154 if(msg) printf("%s: %s\n", progname, msg);
1412Usage: %s -H <vhost> | [-b <ping packet size in bytes>] [-n <number of pings>]\n\ 1155
1413 [-r <number of retries>] [-t <timeout>] [-i packet interval]\n\ 1156 if(arg == 'V') {
1414 [-w <warning threshold>] [-c <critical threshold>]\n\ 1157 printf("$Id$\n");
1415 [-D <debug>] \n", progname); 1158 exit(STATE_UNKNOWN);
1159 }
1160
1161 printf("Usage: %s [options] [-H] host1 host2 hostn\n\n", progname);
1162
1163 if(arg != 'h') exit(3);
1164
1165 printf("Where options are any combination of:\n"
1166 " * -H | --host specify a target\n"
1167 " * -w | --warn warning threshold (currently %0.3fms,%u%%)\n"
1168 " * -c | --crit critical threshold (currently %0.3fms,%u%%)\n"
1169 " * -n | --packets number of packets to send (currently %u)\n"
1170 " * -i | --interval max packet interval (currently %0.3fms)\n"
1171 " * -I | --hostint max target interval (currently %0.3fms)\n"
1172 " * -l | --ttl TTL on outgoing packets (currently %u)\n"
1173 " * -t | --timeout timeout value (seconds, currently %u)\n"
1174 " * -b | --bytes icmp packet size (currenly ignored)\n"
1175 " -v | --verbose verbosity++\n"
1176 " -h | --help this cruft\n",
1177 (float)warn.rta / 1000, warn.pl, (float)crit.rta / 1000, crit.pl,
1178 packets,
1179 (float)pkt_interval / 1000, (float)target_interval / 1000,
1180 ttl, timeout);
1181
1182 puts("\nThe -H switch is optional. Naming a host (or several) to check is not.\n\n"
1183 "Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%\n"
1184 "packet loss. The default values should work well for most users.\n"
1185 "You can specify different RTA factors using the standardized abbreviations\n"
1186 "us (microseconds), ms (milliseconds, default) or just plain s for seconds.\n\n"
1187 "Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops\n"
1188 "are spent and CRITICAL if >= 14 hops are spent.\n"
1189 "NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not.\n\n"
1190 "The -v switch can be specified several times for increased verbosity.\n\n"
1191 "Long options are currently unsupported.\n\n"
1192 "Options marked with * require an argument\n");
1193
1194 puts("The latest version of this plugin can be found at http://oss.op5.se/nagios\n"
1195 "or https://devel.op5.se/oss until the day it is included in the official\n"
1196 "plugin distribution.\n");
1197
1198 exit(3);
1416} 1199}