check_disk: Fail on missing arguments for --warning and --critical and fix a test...
[monitoring-plugins.git] / plugins-root / check_icmp.c
blobf788d42825fb6ed0477869e1dedac235c24b68fa
1 /*****************************************************************************
3 * Monitoring check_icmp plugin
5 * License: GPL
6 * Copyright (c) 2005-2008 Monitoring Plugins Development Team
7 * Original Author : Andreas Ericsson <ae@op5.se>
9 * Description:
11 * This file contains the check_icmp plugin
13 * Relevant RFC's: 792 (ICMP), 791 (IP)
15 * This program was modeled somewhat after the check_icmp program,
16 * which was in turn a hack of fping (www.fping.org) but has been
17 * completely rewritten since to generate higher precision rta values,
18 * and support several different modes as well as setting ttl to control.
19 * redundant routes. The only remainders of fping is currently a few
20 * function names.
23 * This program is free software: you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation, either version 3 of the License, or
26 * (at your option) any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 *****************************************************************************/
39 /* progname may change */
40 /* char *progname = "check_icmp"; */
41 char *progname;
42 const char *copyright = "2005-2008";
43 const char *email = "devel@monitoring-plugins.org";
45 /** Monitoring Plugins basic includes */
46 #include "../plugins/common.h"
47 #include "netutils.h"
48 #include "utils.h"
50 #if HAVE_SYS_SOCKIO_H
51 #include <sys/sockio.h>
52 #endif
54 #include <sys/time.h>
55 #include <errno.h>
56 #include <signal.h>
57 #include <ctype.h>
58 #include <float.h>
59 #include <net/if.h>
60 #include <netinet/in_systm.h>
61 #include <netinet/in.h>
62 #include <netinet/ip.h>
63 #include <netinet/ip6.h>
64 #include <netinet/ip_icmp.h>
65 #include <netinet/icmp6.h>
66 #include <arpa/inet.h>
69 /** sometimes undefined system macros (quite a few, actually) **/
70 #ifndef MAXTTL
71 # define MAXTTL 255
72 #endif
73 #ifndef INADDR_NONE
74 # define INADDR_NONE (in_addr_t)(-1)
75 #endif
77 #ifndef SOL_IP
78 #define SOL_IP 0
79 #endif
81 /* we bundle these in one #ifndef, since they're all from BSD
82 * Put individual #ifndef's around those that bother you */
83 #ifndef ICMP_UNREACH_NET_UNKNOWN
84 # define ICMP_UNREACH_NET_UNKNOWN 6
85 # define ICMP_UNREACH_HOST_UNKNOWN 7
86 # define ICMP_UNREACH_ISOLATED 8
87 # define ICMP_UNREACH_NET_PROHIB 9
88 # define ICMP_UNREACH_HOST_PROHIB 10
89 # define ICMP_UNREACH_TOSNET 11
90 # define ICMP_UNREACH_TOSHOST 12
91 #endif
92 /* tru64 has the ones above, but not these */
93 #ifndef ICMP_UNREACH_FILTER_PROHIB
94 # define ICMP_UNREACH_FILTER_PROHIB 13
95 # define ICMP_UNREACH_HOST_PRECEDENCE 14
96 # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97 #endif
99 typedef unsigned short range_t; /* type for get_range() -- unimplemented */
101 typedef struct rta_host {
102 unsigned short id; /* id in **table, and icmp pkts */
103 char *name; /* arg used for adding this host */
104 char *msg; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in; /* the address of this host */
106 struct sockaddr_storage error_addr; /* stores address of error replies */
107 unsigned long long time_waited; /* total time waited, in usecs */
108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
109 unsigned char icmp_type, icmp_code; /* type and code from errors */
110 unsigned short flags; /* control/status flags */
111 double rta; /* measured RTA */
112 int rta_status; // check result for RTA checks
113 double rtmax; /* max rtt */
114 double rtmin; /* min rtt */
115 double jitter; /* measured jitter */
116 int jitter_status; // check result for Jitter checks
117 double jitter_max; /* jitter rtt maximum */
118 double jitter_min; /* jitter rtt minimum */
119 double EffectiveLatency;
120 double mos; /* Mean opnion score */
121 int mos_status; // check result for MOS checks
122 double score; /* score */
123 int score_status; // check result for score checks
124 u_int last_tdiff;
125 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl; /* measured packet loss */
127 int pl_status; // check result for packet loss checks
128 struct rta_host *next; /* linked list */
129 int order_status; // check result for packet order checks
130 } rta_host;
132 #define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
134 /* threshold structure. all values are maximum allowed, exclusive */
135 typedef struct threshold {
136 unsigned char pl; /* max allowed packet loss in percent */
137 unsigned int rta; /* roundtrip time average, microseconds */
138 double jitter; /* jitter time average, microseconds */
139 double mos; /* MOS */
140 double score; /* Score */
141 } threshold;
143 /* the data structure */
144 typedef struct icmp_ping_data {
145 struct timeval stime; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id;
147 } icmp_ping_data;
149 typedef union ip_hdr {
150 struct ip ip;
151 struct ip6_hdr ip6;
152 } ip_hdr;
154 typedef union icmp_packet {
155 void *buf;
156 struct icmp *icp;
157 struct icmp6_hdr *icp6;
158 u_short *cksum_in;
159 } icmp_packet;
161 /* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
174 #define MODE_RTA 0
175 #define MODE_HOSTCHECK 1
176 #define MODE_ALL 2
177 #define MODE_ICMP 3
179 enum enum_threshold_mode {
180 const_rta_mode,
181 const_packet_loss_mode,
182 const_jitter_mode,
183 const_mos_mode,
184 const_score_mode
187 typedef enum enum_threshold_mode threshold_mode;
189 /* the different ping types we can do
190 * TODO: investigate ARP ping as well */
191 #define HAVE_ICMP 1
192 #define HAVE_UDP 2
193 #define HAVE_TCP 4
194 #define HAVE_ARP 8
196 #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197 #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198 #define IP_HDR_SIZE 20
199 #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200 #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
202 /* various target states */
203 #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204 #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205 #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206 #define TSTATE_UNREACH 0x08
208 /** prototypes **/
209 void print_help (void);
210 void print_usage (void);
211 static u_int get_timevar(const char *);
212 static u_int get_timevaldiff(struct timeval *, struct timeval *);
213 static in_addr_t get_ip_address(const char *);
214 static int wait_for_reply(int, u_int);
215 static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval*);
216 static int send_icmp_ping(int, struct rta_host *);
217 static int get_threshold(char *str, threshold *th);
218 static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode);
219 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode);
220 static void run_checks(void);
221 static void set_source_ip(char *);
222 static int add_target(char *);
223 static int add_target_ip(char *, struct sockaddr_storage *);
224 static int handle_random_icmp(unsigned char *, struct sockaddr_storage *);
225 static void parse_address(struct sockaddr_storage *, char *, int);
226 static unsigned short icmp_checksum(uint16_t *, size_t);
227 static void finish(int);
228 static void crash(const char *, ...);
230 /** external **/
231 extern int optind;
232 extern char *optarg;
233 extern char **environ;
235 /** global variables **/
236 static struct rta_host **table, *cursor, *list;
238 static threshold crit = {
239 .pl = 80,
240 .rta = 500000,
241 .jitter = 0.0,
242 .mos = 0.0,
243 .score = 0.0
245 static threshold warn = {
246 .pl = 40,
247 .rta = 200000,
248 .jitter = 0.0,
249 .mos = 0.0,
250 .score = 0.0
253 static int mode, protocols, sockets, debug = 0, timeout = 10;
254 static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE;
255 static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN;
257 static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0;
258 #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
259 static unsigned short targets_down = 0, targets = 0, packets = 0;
260 #define targets_alive (targets - targets_down)
261 static unsigned int retry_interval, pkt_interval, target_interval;
262 static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
263 static pid_t pid;
264 static struct timezone tz;
265 static struct timeval prog_start;
266 static unsigned long long max_completion_time = 0;
267 static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
268 static int min_hosts_alive = -1;
269 float pkt_backoff_factor = 1.5;
270 float target_backoff_factor = 1.5;
271 bool rta_mode=false;
272 bool pl_mode=false;
273 bool jitter_mode=false;
274 bool score_mode=false;
275 bool mos_mode=false;
276 bool order_mode=false;
278 /** code start **/
279 static void
280 crash(const char *fmt, ...)
282 va_list ap;
284 printf("%s: ", progname);
286 va_start(ap, fmt);
287 vprintf(fmt, ap);
288 va_end(ap);
290 if(errno) printf(": %s", strerror(errno));
291 puts("");
293 exit(3);
297 static const char *
298 get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
300 const char *msg = "unreachable";
302 if(debug > 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
303 switch(icmp_type) {
304 case ICMP_UNREACH:
305 switch(icmp_code) {
306 case ICMP_UNREACH_NET: msg = "Net unreachable"; break;
307 case ICMP_UNREACH_HOST: msg = "Host unreachable"; break;
308 case ICMP_UNREACH_PROTOCOL: msg = "Protocol unreachable (firewall?)"; break;
309 case ICMP_UNREACH_PORT: msg = "Port unreachable (firewall?)"; break;
310 case ICMP_UNREACH_NEEDFRAG: msg = "Fragmentation needed"; break;
311 case ICMP_UNREACH_SRCFAIL: msg = "Source route failed"; break;
312 case ICMP_UNREACH_ISOLATED: msg = "Source host isolated"; break;
313 case ICMP_UNREACH_NET_UNKNOWN: msg = "Unknown network"; break;
314 case ICMP_UNREACH_HOST_UNKNOWN: msg = "Unknown host"; break;
315 case ICMP_UNREACH_NET_PROHIB: msg = "Network denied (firewall?)"; break;
316 case ICMP_UNREACH_HOST_PROHIB: msg = "Host denied (firewall?)"; break;
317 case ICMP_UNREACH_TOSNET: msg = "Bad TOS for network (firewall?)"; break;
318 case ICMP_UNREACH_TOSHOST: msg = "Bad TOS for host (firewall?)"; break;
319 case ICMP_UNREACH_FILTER_PROHIB: msg = "Prohibited by filter (firewall)"; break;
320 case ICMP_UNREACH_HOST_PRECEDENCE: msg = "Host precedence violation"; break;
321 case ICMP_UNREACH_PRECEDENCE_CUTOFF: msg = "Precedence cutoff"; break;
322 default: msg = "Invalid code"; break;
324 break;
326 case ICMP_TIMXCEED:
327 /* really 'out of reach', or non-existent host behind a router serving
328 * two different subnets */
329 switch(icmp_code) {
330 case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break;
331 case ICMP_TIMXCEED_REASS: msg = "Fragment reassembly time exceeded"; break;
332 default: msg = "Invalid code"; break;
334 break;
336 case ICMP_SOURCEQUENCH: msg = "Transmitting too fast"; break;
337 case ICMP_REDIRECT: msg = "Redirect (change route)"; break;
338 case ICMP_PARAMPROB: msg = "Bad IP header (required option absent)"; break;
340 /* the following aren't error messages, so ignore */
341 case ICMP_TSTAMP:
342 case ICMP_TSTAMPREPLY:
343 case ICMP_IREQ:
344 case ICMP_IREQREPLY:
345 case ICMP_MASKREQ:
346 case ICMP_MASKREPLY:
347 default: msg = ""; break;
350 return msg;
353 static int
354 handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr)
356 struct icmp p, sent_icmp;
357 struct rta_host *host = NULL;
359 memcpy(&p, packet, sizeof(p));
360 if(p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) {
361 /* echo request from us to us (pinging localhost) */
362 return 0;
365 if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr);
367 /* only handle a few types, since others can't possibly be replies to
368 * us in a sane network (if it is anyway, it will be counted as lost
369 * at summary time, but not as quickly as a proper response */
370 /* TIMXCEED can be an unreach from a router with multiple IP's which
371 * serves two different subnets on the same interface and a dead host
372 * on one net is pinged from the other. The router will respond to
373 * itself and thus set TTL=0 so as to not loop forever. Even when
374 * TIMXCEED actually sends a proper icmp response we will have passed
375 * too many hops to have a hope of reaching it later, in which case it
376 * indicates overconfidence in the network, poor routing or both. */
377 if(p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED &&
378 p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB)
380 return 0;
383 /* might be for us. At least it holds the original package (according
384 * to RFC 792). If it isn't, just ignore it */
385 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
386 if(sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid ||
387 ntohs(sent_icmp.icmp_seq) >= targets*packets)
389 if(debug) printf("Packet is no response to a packet we sent\n");
390 return 0;
393 /* it is indeed a response for us */
394 host = table[ntohs(sent_icmp.icmp_seq)/packets];
395 if(debug) {
396 char address[INET6_ADDRSTRLEN];
397 parse_address(addr, address, sizeof(address));
398 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
399 get_icmp_error_msg(p.icmp_type, p.icmp_code),
400 address, host->name);
403 icmp_lost++;
404 host->icmp_lost++;
405 /* don't spend time on lost hosts any more */
406 if(host->flags & FLAG_LOST_CAUSE) return 0;
408 /* source quench means we're sending too fast, so increase the
409 * interval and mark this packet lost */
410 if(p.icmp_type == ICMP_SOURCEQUENCH) {
411 pkt_interval *= pkt_backoff_factor;
412 target_interval *= target_backoff_factor;
414 else {
415 targets_down++;
416 host->flags |= FLAG_LOST_CAUSE;
418 host->icmp_type = p.icmp_type;
419 host->icmp_code = p.icmp_code;
420 host->error_addr = *addr;
422 return 0;
425 void parse_address(struct sockaddr_storage *addr, char *address, int size)
427 switch (address_family) {
428 case AF_INET:
429 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size);
430 break;
431 case AF_INET6:
432 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size);
433 break;
438 main(int argc, char **argv)
440 int i;
441 char *ptr;
442 long int arg;
443 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
444 int result;
445 struct rta_host *host;
446 #ifdef HAVE_SIGACTION
447 struct sigaction sig_action;
448 #endif
449 #ifdef SO_TIMESTAMP
450 int on = 1;
451 #endif
452 char *source_ip = NULL;
453 char * opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
454 setlocale (LC_ALL, "");
455 bindtextdomain (PACKAGE, LOCALEDIR);
456 textdomain (PACKAGE);
458 /* we only need to be setsuid when we get the sockets, so do
459 * that before pointer magic (esp. on network data) */
460 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
462 address_family = -1;
463 int icmp_proto = IPPROTO_ICMP;
465 /* get calling name the old-fashioned way for portability instead
466 * of relying on the glibc-ism __progname */
467 ptr = strrchr(argv[0], '/');
468 if(ptr) progname = &ptr[1];
469 else progname = argv[0];
471 /* now set defaults. Use progname to set them initially (allows for
472 * superfast check_host program when target host is up */
473 cursor = list = NULL;
474 table = NULL;
476 mode = MODE_RTA;
477 /* Default critical thresholds */
478 crit.rta = 500000;
479 crit.pl = 80;
480 crit.jitter = 50;
481 crit.mos= 3;
482 crit.score=70;
483 /* Default warning thresholds */
484 warn.rta = 200000;
485 warn.pl = 40;
486 warn.jitter = 40;
487 warn.mos= 3.5;
488 warn.score=80;
490 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
491 pkt_interval = 80000; /* 80 msec packet interval by default */
492 packets = 5;
494 if(!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
495 mode = MODE_ICMP;
496 protocols = HAVE_ICMP;
498 else if(!strcmp(progname, "check_host")) {
499 mode = MODE_HOSTCHECK;
500 pkt_interval = 1000000;
501 packets = 5;
502 crit.rta = warn.rta = 1000000;
503 crit.pl = warn.pl = 100;
505 else if(!strcmp(progname, "check_rta_multi")) {
506 mode = MODE_ALL;
507 target_interval = 0;
508 pkt_interval = 50000;
509 packets = 5;
512 /* support "--help" and "--version" */
513 if(argc == 2) {
514 if(!strcmp(argv[1], "--help"))
515 strcpy(argv[1], "-h");
516 if(!strcmp(argv[1], "--version"))
517 strcpy(argv[1], "-V");
520 /* Parse protocol arguments first */
521 for(i = 1; i < argc; i++) {
522 while((arg = getopt(argc, argv, opts_str)) != EOF) {
523 switch(arg) {
524 case '4':
525 if (address_family != -1)
526 crash("Multiple protocol versions not supported");
527 address_family = AF_INET;
528 break;
529 case '6':
530 #ifdef USE_IPV6
531 if (address_family != -1)
532 crash("Multiple protocol versions not supported");
533 address_family = AF_INET6;
534 #else
535 usage (_("IPv6 support not available\n"));
536 #endif
537 break;
542 /* Reset argument scanning */
543 optind = 1;
545 unsigned long size;
546 bool err;
547 /* parse the arguments */
548 for(i = 1; i < argc; i++) {
549 while((arg = getopt(argc, argv, opts_str)) != EOF) {
550 switch(arg) {
551 case 'v':
552 debug++;
553 break;
554 case 'b':
555 size = strtol(optarg,NULL,0);
556 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
557 size < MAX_PING_DATA) {
558 icmp_data_size = size;
559 icmp_pkt_size = size + ICMP_MINLEN;
560 } else
561 usage_va("ICMP data length must be between: %lu and %lu",
562 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
563 MAX_PING_DATA - 1);
564 break;
565 case 'i':
566 pkt_interval = get_timevar(optarg);
567 break;
568 case 'I':
569 target_interval = get_timevar(optarg);
570 break;
571 case 'w':
572 get_threshold(optarg, &warn);
573 break;
574 case 'c':
575 get_threshold(optarg, &crit);
576 break;
577 case 'n':
578 case 'p':
579 packets = strtoul(optarg, NULL, 0);
580 break;
581 case 't':
582 timeout = strtoul(optarg, NULL, 0);
583 if(!timeout) timeout = 10;
584 break;
585 case 'H':
586 add_target(optarg);
587 break;
588 case 'l':
589 ttl = (int)strtoul(optarg, NULL, 0);
590 break;
591 case 'm':
592 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
593 break;
594 case 'd': /* implement later, for cluster checks */
595 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
596 if(ptr) {
597 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
599 break;
600 case 's': /* specify source IP address */
601 source_ip = optarg;
602 break;
603 case 'V': /* version */
604 print_revision (progname, NP_VERSION);
605 exit (STATE_UNKNOWN);
606 case 'h': /* help */
607 print_help ();
608 exit (STATE_UNKNOWN);
609 break;
610 case 'R': /* RTA mode */
611 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
612 if (!err) {
613 crash("Failed to parse RTA threshold");
616 rta_mode=true;
617 break;
618 case 'P': /* packet loss mode */
619 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
620 if (!err) {
621 crash("Failed to parse packet loss threshold");
624 pl_mode=true;
625 break;
626 case 'J': /* jitter mode */
627 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode);
628 if (!err) {
629 crash("Failed to parse jitter threshold");
632 jitter_mode=true;
633 break;
634 case 'M': /* MOS mode */
635 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode);
636 if (!err) {
637 crash("Failed to parse MOS threshold");
640 mos_mode=true;
641 break;
642 case 'S': /* score mode */
643 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode);
644 if (!err) {
645 crash("Failed to parse score threshold");
648 score_mode=true;
649 break;
650 case 'O': /* out of order mode */
651 order_mode=true;
652 break;
657 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
658 environ = NULL;
660 /* use the pid to mark packets as ours */
661 /* Some systems have 32-bit pid_t so mask off only 16 bits */
662 pid = getpid() & 0xffff;
663 /* printf("pid = %u\n", pid); */
665 /* Parse extra opts if any */
666 argv=np_extra_opts(&argc, argv, progname);
668 argv = &argv[optind];
669 while(*argv) {
670 add_target(*argv);
671 argv++;
674 if(!targets) {
675 errno = 0;
676 crash("No hosts to check");
679 // add_target might change address_family
680 switch ( address_family ){
681 case AF_INET: icmp_proto = IPPROTO_ICMP;
682 break;
683 case AF_INET6: icmp_proto = IPPROTO_ICMPV6;
684 break;
685 default: crash("Address family not supported");
687 if((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
688 sockets |= HAVE_ICMP;
689 else icmp_sockerrno = errno;
691 if( source_ip )
692 set_source_ip(source_ip);
694 #ifdef SO_TIMESTAMP
695 if(setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
696 if(debug) printf("Warning: no SO_TIMESTAMP support\n");
697 #endif // SO_TIMESTAMP
699 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
700 if (setuid(getuid()) == -1) {
701 printf("ERROR: Failed to drop privileges\n");
702 return 1;
705 if(!sockets) {
706 if(icmp_sock == -1) {
707 errno = icmp_sockerrno;
708 crash("Failed to obtain ICMP socket");
709 return -1;
711 /* if(udp_sock == -1) { */
712 /* errno = icmp_sockerrno; */
713 /* crash("Failed to obtain UDP socket"); */
714 /* return -1; */
715 /* } */
716 /* if(tcp_sock == -1) { */
717 /* errno = icmp_sockerrno; */
718 /* crash("Failed to obtain TCP socker"); */
719 /* return -1; */
720 /* } */
722 if(!ttl) ttl = 64;
724 if(icmp_sock) {
725 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
726 if(debug) {
727 if(result == -1) printf("setsockopt failed\n");
728 else printf("ttl set to %u\n", ttl);
732 /* stupid users should be able to give whatever thresholds they want
733 * (nothing will break if they do), but some anal plugin maintainer
734 * will probably add some printf() thing here later, so it might be
735 * best to at least show them where to do it. ;) */
736 if(warn.pl > crit.pl) warn.pl = crit.pl;
737 if(warn.rta > crit.rta) warn.rta = crit.rta;
738 if(warn_down > crit_down) crit_down = warn_down;
739 if(warn.jitter > crit.jitter) crit.jitter = warn.jitter;
740 if(warn.mos < crit.mos) warn.mos = crit.mos;
741 if(warn.score < crit.score) warn.score = crit.score;
743 #ifdef HAVE_SIGACTION
744 sig_action.sa_sigaction = NULL;
745 sig_action.sa_handler = finish;
746 sigfillset(&sig_action.sa_mask);
747 sig_action.sa_flags = SA_NODEFER|SA_RESTART;
748 sigaction(SIGINT, &sig_action, NULL);
749 sigaction(SIGHUP, &sig_action, NULL);
750 sigaction(SIGTERM, &sig_action, NULL);
751 sigaction(SIGALRM, &sig_action, NULL);
752 #else /* HAVE_SIGACTION */
753 signal(SIGINT, finish);
754 signal(SIGHUP, finish);
755 signal(SIGTERM, finish);
756 signal(SIGALRM, finish);
757 #endif /* HAVE_SIGACTION */
758 if(debug) printf("Setting alarm timeout to %u seconds\n", timeout);
759 alarm(timeout);
761 /* make sure we don't wait any longer than necessary */
762 gettimeofday(&prog_start, &tz);
763 max_completion_time =
764 ((targets * packets * pkt_interval) + (targets * target_interval)) +
765 (targets * packets * crit.rta) + crit.rta;
767 if(debug) {
768 printf("packets: %u, targets: %u\n"
769 "target_interval: %0.3f, pkt_interval %0.3f\n"
770 "crit.rta: %0.3f\n"
771 "max_completion_time: %0.3f\n",
772 packets, targets,
773 (float)target_interval / 1000, (float)pkt_interval / 1000,
774 (float)crit.rta / 1000,
775 (float)max_completion_time / 1000);
778 if(debug) {
779 if(max_completion_time > (u_int)timeout * 1000000) {
780 printf("max_completion_time: %llu timeout: %u\n",
781 max_completion_time, timeout);
782 printf("Timeout must be at least %llu\n",
783 max_completion_time / 1000000 + 1);
787 if(debug) {
788 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
789 crit.rta, crit.pl, warn.rta, warn.pl);
790 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
791 pkt_interval, target_interval, retry_interval);
792 printf("icmp_pkt_size: %u timeout: %u\n",
793 icmp_pkt_size, timeout);
796 if(packets > 20) {
797 errno = 0;
798 crash("packets is > 20 (%d)", packets);
801 if(min_hosts_alive < -1) {
802 errno = 0;
803 crash("minimum alive hosts is negative (%i)", min_hosts_alive);
806 host = list;
807 table = malloc(sizeof(struct rta_host *) * targets);
808 if(!table) {
809 crash("main(): malloc failed for host table");
812 i = 0;
813 while(host) {
814 host->id = i*packets;
815 table[i] = host;
816 host = host->next;
817 i++;
820 run_checks();
822 errno = 0;
823 finish(0);
825 return(0);
828 static void
829 run_checks()
831 u_int i, t;
832 u_int final_wait, time_passed;
834 /* this loop might actually violate the pkt_interval or target_interval
835 * settings, but only if there aren't any packets on the wire which
836 * indicates that the target can handle an increased packet rate */
837 for(i = 0; i < packets; i++) {
838 for(t = 0; t < targets; t++) {
839 /* don't send useless packets */
840 if(!targets_alive) finish(0);
841 if(table[t]->flags & FLAG_LOST_CAUSE) {
842 if(debug) printf("%s is a lost cause. not sending any more\n",
843 table[t]->name);
844 continue;
847 /* we're still in the game, so send next packet */
848 (void)send_icmp_ping(icmp_sock, table[t]);
849 wait_for_reply(icmp_sock, target_interval);
851 wait_for_reply(icmp_sock, pkt_interval * targets);
854 if(icmp_pkts_en_route && targets_alive) {
855 time_passed = get_timevaldiff(NULL, NULL);
856 final_wait = max_completion_time - time_passed;
858 if(debug) {
859 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
860 time_passed, final_wait, max_completion_time);
862 if(time_passed > max_completion_time) {
863 if(debug) printf("Time passed. Finishing up\n");
864 finish(0);
867 /* catch the packets that might come in within the timeframe, but
868 * haven't yet */
869 if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
870 final_wait, (float)final_wait / 1000);
871 wait_for_reply(icmp_sock, final_wait);
876 /* response structure:
877 * IPv4:
878 * ip header : 20 bytes
879 * icmp header : 28 bytes
880 * IPv6:
881 * ip header : 40 bytes
882 * icmp header : 28 bytes
883 * both:
884 * icmp echo reply : the rest
886 static int
887 wait_for_reply(int sock, u_int t)
889 int n, hlen;
890 static unsigned char buf[65536];
891 struct sockaddr_storage resp_addr;
892 union ip_hdr *ip;
893 union icmp_packet packet;
894 struct rta_host *host;
895 struct icmp_ping_data data;
896 struct timeval wait_start, now;
897 u_int tdiff, i, per_pkt_wait;
898 double jitter_tmp;
900 if (!(packet.buf = malloc(icmp_pkt_size))) {
901 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
902 icmp_pkt_size);
903 return -1; /* might be reached if we're in debug mode */
906 memset(packet.buf, 0, icmp_pkt_size);
908 /* if we can't listen or don't have anything to listen to, just return */
909 if(!t || !icmp_pkts_en_route) {
910 free(packet.buf);
911 return 0;
914 gettimeofday(&wait_start, &tz);
916 i = t;
917 per_pkt_wait = t / icmp_pkts_en_route;
918 while(icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
919 t = per_pkt_wait;
921 /* wrap up if all targets are declared dead */
922 if(!targets_alive ||
923 get_timevaldiff(&prog_start, NULL) >= max_completion_time ||
924 (mode == MODE_HOSTCHECK && targets_down))
926 finish(0);
929 /* reap responses until we hit a timeout */
930 n = recvfrom_wto(sock, buf, sizeof(buf),
931 (struct sockaddr *)&resp_addr, &t, &now);
932 if(!n) {
933 if(debug > 1) {
934 printf("recvfrom_wto() timed out during a %u usecs wait\n",
935 per_pkt_wait);
937 continue; /* timeout for this one, so keep trying */
939 if(n < 0) {
940 if(debug) printf("recvfrom_wto() returned errors\n");
941 free(packet.buf);
942 return n;
945 // FIXME: with ipv6 we don't have an ip header here
946 if (address_family != AF_INET6) {
947 ip = (union ip_hdr *)buf;
949 if(debug > 1) {
950 char address[INET6_ADDRSTRLEN];
951 parse_address(&resp_addr, address, sizeof(address));
952 printf("received %u bytes from %s\n",
953 address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen)
954 : ntohs(ip->ip.ip_len),
955 address);
959 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
960 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
961 /* alpha headers are decidedly broken. Using an ansi compiler,
962 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
963 * off the bottom 4 bits */
964 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
965 /* #else */
966 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
967 /* #endif */
969 if(n < (hlen + ICMP_MINLEN)) {
970 char address[INET6_ADDRSTRLEN];
971 parse_address(&resp_addr, address, sizeof(address));
972 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
973 n, hlen + icmp_pkt_size, address);
975 /* else if(debug) { */
976 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
977 /* hlen, ntohs(ip->ip_len) - hlen, */
978 /* sizeof(struct ip), icmp_pkt_size); */
979 /* } */
981 /* check the response */
983 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
984 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
985 : sizeof(struct icmp));*/
987 if( (address_family == PF_INET &&
988 (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY
989 || ntohs(packet.icp->icmp_seq) >= targets * packets))
990 || (address_family == PF_INET6 &&
991 (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY
992 || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
993 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
994 handle_random_icmp(buf + hlen, &resp_addr);
995 continue;
998 /* this is indeed a valid response */
999 if (address_family == PF_INET) {
1000 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1001 if (debug > 2)
1002 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
1003 (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id),
1004 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum);
1005 host = table[ntohs(packet.icp->icmp_seq)/packets];
1006 } else {
1007 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1008 if (debug > 2)
1009 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
1010 (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id),
1011 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum);
1012 host = table[ntohs(packet.icp6->icmp6_seq)/packets];
1015 tdiff = get_timevaldiff(&data.stime, &now);
1017 if (host->last_tdiff>0) {
1018 /* Calculate jitter */
1019 if (host->last_tdiff > tdiff) {
1020 jitter_tmp = host->last_tdiff - tdiff;
1021 } else {
1022 jitter_tmp = tdiff - host->last_tdiff;
1025 if (host->jitter==0) {
1026 host->jitter=jitter_tmp;
1027 host->jitter_max=jitter_tmp;
1028 host->jitter_min=jitter_tmp;
1029 } else {
1030 host->jitter+=jitter_tmp;
1032 if (jitter_tmp < host->jitter_min) {
1033 host->jitter_min=jitter_tmp;
1036 if (jitter_tmp > host->jitter_max) {
1037 host->jitter_max=jitter_tmp;
1041 /* Check if packets in order */
1042 if (host->last_icmp_seq >= packet.icp->icmp_seq)
1043 host->order_status=STATE_CRITICAL;
1045 host->last_tdiff=tdiff;
1047 host->last_icmp_seq=packet.icp->icmp_seq;
1049 host->time_waited += tdiff;
1050 host->icmp_recv++;
1051 icmp_recv++;
1052 if (tdiff > (unsigned int)host->rtmax)
1053 host->rtmax = tdiff;
1054 if (tdiff < (unsigned int)host->rtmin)
1055 host->rtmin = tdiff;
1057 if(debug) {
1058 char address[INET6_ADDRSTRLEN];
1059 parse_address(&resp_addr, address, sizeof(address));
1061 switch(address_family) {
1062 case AF_INET: {
1063 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1064 (float)tdiff / 1000,
1065 address,
1066 ttl,
1067 ip->ip.ip_ttl,
1068 (float)host->rtmax / 1000,
1069 (float)host->rtmin / 1000);
1070 break;
1072 case AF_INET6: {
1073 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n",
1074 (float)tdiff / 1000,
1075 address,
1076 ttl,
1077 (float)host->rtmax / 1000,
1078 (float)host->rtmin / 1000);
1083 /* if we're in hostcheck mode, exit with limited printouts */
1084 if(mode == MODE_HOSTCHECK) {
1085 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1086 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1087 host->name, icmp_recv, (float)tdiff / 1000,
1088 icmp_recv, packets, (float)tdiff / 1000,
1089 (float)warn.rta / 1000, (float)crit.rta / 1000);
1090 exit(STATE_OK);
1094 free(packet.buf);
1095 return 0;
1098 /* the ping functions */
1099 static int
1100 send_icmp_ping(int sock, struct rta_host *host)
1102 long int len;
1103 size_t addrlen;
1104 struct icmp_ping_data data;
1105 struct msghdr hdr;
1106 struct iovec iov;
1107 struct timeval tv;
1108 void *buf = NULL;
1110 if(sock == -1) {
1111 errno = 0;
1112 crash("Attempt to send on bogus socket");
1113 return -1;
1116 if(!buf) {
1117 if (!(buf = malloc(icmp_pkt_size))) {
1118 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
1119 icmp_pkt_size);
1120 return -1; /* might be reached if we're in debug mode */
1123 memset(buf, 0, icmp_pkt_size);
1125 if((gettimeofday(&tv, &tz)) == -1) {
1126 free(buf);
1127 return -1;
1130 data.ping_id = 10; /* host->icmp.icmp_sent; */
1131 memcpy(&data.stime, &tv, sizeof(tv));
1133 if (address_family == AF_INET) {
1134 struct icmp *icp = (struct icmp*)buf;
1135 addrlen = sizeof(struct sockaddr_in);
1137 memcpy(&icp->icmp_data, &data, sizeof(data));
1139 icp->icmp_type = ICMP_ECHO;
1140 icp->icmp_code = 0;
1141 icp->icmp_cksum = 0;
1142 icp->icmp_id = htons(pid);
1143 icp->icmp_seq = htons(host->id++);
1144 icp->icmp_cksum = icmp_checksum((uint16_t*)buf, (size_t)icmp_pkt_size);
1146 if (debug > 2)
1147 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1148 (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name);
1150 else {
1151 struct icmp6_hdr *icp6 = (struct icmp6_hdr*)buf;
1152 addrlen = sizeof(struct sockaddr_in6);
1154 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
1156 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1157 icp6->icmp6_code = 0;
1158 icp6->icmp6_cksum = 0;
1159 icp6->icmp6_id = htons(pid);
1160 icp6->icmp6_seq = htons(host->id++);
1161 // let checksum be calculated automatically
1163 if (debug > 2) {
1164 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1165 (unsigned long)sizeof(data), ntohs(icp6->icmp6_id),
1166 ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name);
1170 memset(&iov, 0, sizeof(iov));
1171 iov.iov_base = buf;
1172 iov.iov_len = icmp_pkt_size;
1174 memset(&hdr, 0, sizeof(hdr));
1175 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
1176 hdr.msg_namelen = addrlen;
1177 hdr.msg_iov = &iov;
1178 hdr.msg_iovlen = 1;
1180 errno = 0;
1182 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1183 #ifdef MSG_CONFIRM
1184 len = sendmsg(sock, &hdr, MSG_CONFIRM);
1185 #else
1186 len = sendmsg(sock, &hdr, 0);
1187 #endif
1189 free(buf);
1191 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
1192 if(debug) {
1193 char address[INET6_ADDRSTRLEN];
1194 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1195 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1197 errno = 0;
1198 return -1;
1201 icmp_sent++;
1202 host->icmp_sent++;
1204 return 0;
1207 static int
1208 recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr,
1209 u_int *timo, struct timeval* tv)
1211 u_int slen;
1212 int n, ret;
1213 struct timeval to, then, now;
1214 fd_set rd, wr;
1215 #ifdef HAVE_MSGHDR_MSG_CONTROL
1216 char ans_data[4096];
1217 #endif // HAVE_MSGHDR_MSG_CONTROL
1218 struct msghdr hdr;
1219 struct iovec iov;
1220 #ifdef SO_TIMESTAMP
1221 struct cmsghdr* chdr;
1222 #endif
1224 if(!*timo) {
1225 if(debug) printf("*timo is not\n");
1226 return 0;
1229 to.tv_sec = *timo / 1000000;
1230 to.tv_usec = (*timo - (to.tv_sec * 1000000));
1232 FD_ZERO(&rd);
1233 FD_ZERO(&wr);
1234 FD_SET(sock, &rd);
1235 errno = 0;
1236 gettimeofday(&then, &tz);
1237 n = select(sock + 1, &rd, &wr, NULL, &to);
1238 if(n < 0) crash("select() in recvfrom_wto");
1239 gettimeofday(&now, &tz);
1240 *timo = get_timevaldiff(&then, &now);
1242 if(!n) return 0; /* timeout */
1244 slen = sizeof(struct sockaddr_storage);
1246 memset(&iov, 0, sizeof(iov));
1247 iov.iov_base = buf;
1248 iov.iov_len = len;
1250 memset(&hdr, 0, sizeof(hdr));
1251 hdr.msg_name = saddr;
1252 hdr.msg_namelen = slen;
1253 hdr.msg_iov = &iov;
1254 hdr.msg_iovlen = 1;
1255 #ifdef HAVE_MSGHDR_MSG_CONTROL
1256 hdr.msg_control = ans_data;
1257 hdr.msg_controllen = sizeof(ans_data);
1258 #endif
1260 ret = recvmsg(sock, &hdr, 0);
1261 #ifdef SO_TIMESTAMP
1262 for(chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1263 if(chdr->cmsg_level == SOL_SOCKET
1264 && chdr->cmsg_type == SO_TIMESTAMP
1265 && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1266 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv));
1267 break ;
1271 if (!chdr)
1272 #endif // SO_TIMESTAMP
1273 gettimeofday(tv, &tz);
1274 return (ret);
1277 static void
1278 finish(int sig)
1280 u_int i = 0;
1281 unsigned char pl;
1282 double rta;
1283 struct rta_host *host;
1284 const char *status_string[] =
1285 {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1286 int hosts_ok = 0;
1287 int hosts_warn = 0;
1288 int this_status;
1289 double R;
1291 alarm(0);
1292 if(debug > 1) printf("finish(%d) called\n", sig);
1294 if(icmp_sock != -1) close(icmp_sock);
1295 if(udp_sock != -1) close(udp_sock);
1296 if(tcp_sock != -1) close(tcp_sock);
1298 if(debug) {
1299 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
1300 icmp_sent, icmp_recv, icmp_lost);
1301 printf("targets: %u targets_alive: %u\n", targets, targets_alive);
1304 /* iterate thrice to calculate values, give output, and print perfparse */
1305 status=STATE_OK;
1306 host = list;
1308 while(host) {
1309 this_status = STATE_OK;
1311 if(!host->icmp_recv) {
1312 /* rta 0 is ofcourse not entirely correct, but will still show up
1313 * conspicuously as missing entries in perfparse and cacti */
1314 pl = 100;
1315 rta = 0;
1316 status = STATE_CRITICAL;
1317 /* up the down counter if not already counted */
1318 if(!(host->flags & FLAG_LOST_CAUSE) && targets_alive) targets_down++;
1319 } else {
1320 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1321 rta = (double)host->time_waited / host->icmp_recv;
1324 if (host->icmp_recv>1) {
1326 * This algorithm is probably pretty much blindly copied from
1327 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1328 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1329 * According to some quick research MOS originates from the Audio/Video transport network area.
1330 * Whether it can and should be computed from ICMP data, I can not say.
1332 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1334 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1336 * More links:
1337 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1339 host->jitter=(host->jitter / (host->icmp_recv - 1)/1000);
1342 * Take the average round trip latency (in milliseconds), add
1343 * round trip jitter, but double the impact to latency
1344 * then add 10 for protocol latencies (in milliseconds).
1346 host->EffectiveLatency = (rta/1000) + host->jitter * 2 + 10;
1348 if (host->EffectiveLatency < 160) {
1349 R = 93.2 - (host->EffectiveLatency / 40);
1350 } else {
1351 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1354 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1355 // loss of 5% will be entered as 5).
1356 R = R - (pl * 2.5);
1358 if (R < 0) {
1359 R = 0;
1362 host->score = R;
1363 host->mos= 1 + ((0.035) * R) + ((.000007) * R * (R-60) * (100-R));
1364 } else {
1365 host->jitter=0;
1366 host->jitter_min=0;
1367 host->jitter_max=0;
1368 host->mos=0;
1371 host->pl = pl;
1372 host->rta = rta;
1374 /* if no new mode selected, use old schema */
1375 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1376 rta_mode = true;
1377 pl_mode = true;
1380 /* Check which mode is on and do the warn / Crit stuff */
1381 if (rta_mode) {
1382 if(rta >= crit.rta) {
1383 this_status = STATE_CRITICAL;
1384 status = STATE_CRITICAL;
1385 host->rta_status=STATE_CRITICAL;
1386 } else if(status!=STATE_CRITICAL && (rta >= warn.rta)) {
1387 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1388 status = STATE_WARNING;
1389 host->rta_status=STATE_WARNING;
1393 if (pl_mode) {
1394 if(pl >= crit.pl) {
1395 this_status = STATE_CRITICAL;
1396 status = STATE_CRITICAL;
1397 host->pl_status=STATE_CRITICAL;
1398 } else if(status!=STATE_CRITICAL && (pl >= warn.pl)) {
1399 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1400 status = STATE_WARNING;
1401 host->pl_status=STATE_WARNING;
1405 if (jitter_mode) {
1406 if(host->jitter >= crit.jitter) {
1407 this_status = STATE_CRITICAL;
1408 status = STATE_CRITICAL;
1409 host->jitter_status=STATE_CRITICAL;
1410 } else if(status!=STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1411 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1412 status = STATE_WARNING;
1413 host->jitter_status=STATE_WARNING;
1417 if (mos_mode) {
1418 if(host->mos <= crit.mos) {
1419 this_status = STATE_CRITICAL;
1420 status = STATE_CRITICAL;
1421 host->mos_status=STATE_CRITICAL;
1422 } else if(status!=STATE_CRITICAL && (host->mos <= warn.mos)) {
1423 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1424 status = STATE_WARNING;
1425 host->mos_status=STATE_WARNING;
1429 if (score_mode) {
1430 if(host->score <= crit.score) {
1431 this_status = STATE_CRITICAL;
1432 status = STATE_CRITICAL;
1433 host->score_status=STATE_CRITICAL;
1434 } else if(status!=STATE_CRITICAL && (host->score <= warn.score)) {
1435 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1436 status = STATE_WARNING;
1437 host->score_status=STATE_WARNING;
1441 if (this_status == STATE_WARNING) {
1442 hosts_warn++;
1443 } else if (this_status == STATE_OK) {
1444 hosts_ok++;
1447 host = host->next;
1451 /* this is inevitable */
1452 if(!targets_alive) status = STATE_CRITICAL;
1453 if(min_hosts_alive > -1) {
1454 if(hosts_ok >= min_hosts_alive) status = STATE_OK;
1455 else if((hosts_ok + hosts_warn) >= min_hosts_alive) status = STATE_WARNING;
1457 printf("%s - ", status_string[status]);
1459 host = list;
1460 while(host) {
1462 if(debug) puts("");
1463 if(i) {
1464 if(i < targets) printf(" :: ");
1465 else printf("\n");
1467 i++;
1468 if(!host->icmp_recv) {
1469 status = STATE_CRITICAL;
1470 host->rtmin=0;
1471 host->jitter_min=0;
1472 if(host->flags & FLAG_LOST_CAUSE) {
1473 char address[INET6_ADDRSTRLEN];
1474 parse_address(&host->error_addr, address, sizeof(address));
1475 printf("%s: %s @ %s. rta nan, lost %d%%",
1476 host->name,
1477 get_icmp_error_msg(host->icmp_type, host->icmp_code),
1478 address,
1479 100);
1480 } else { /* not marked as lost cause, so we have no flags for it */
1481 printf("%s: rta nan, lost 100%%", host->name);
1483 } else { /* !icmp_recv */
1484 printf("%s", host->name);
1485 /* rta text output */
1486 if (rta_mode) {
1487 if (status == STATE_OK)
1488 printf(" rta %0.3fms", host->rta / 1000);
1489 else if (status==STATE_WARNING && host->rta_status==status)
1490 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta/1000);
1491 else if (status==STATE_CRITICAL && host->rta_status==status)
1492 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta/1000);
1494 /* pl text output */
1495 if (pl_mode) {
1496 if (status == STATE_OK)
1497 printf(" lost %u%%", host->pl);
1498 else if (status==STATE_WARNING && host->pl_status==status)
1499 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1500 else if (status==STATE_CRITICAL && host->pl_status==status)
1501 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1503 /* jitter text output */
1504 if (jitter_mode) {
1505 if (status == STATE_OK)
1506 printf(" jitter %0.3fms", (float)host->jitter);
1507 else if (status==STATE_WARNING && host->jitter_status==status)
1508 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1509 else if (status==STATE_CRITICAL && host->jitter_status==status)
1510 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1512 /* mos text output */
1513 if (mos_mode) {
1514 if (status == STATE_OK)
1515 printf(" MOS %0.1f", (float)host->mos);
1516 else if (status==STATE_WARNING && host->mos_status==status)
1517 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1518 else if (status==STATE_CRITICAL && host->mos_status==status)
1519 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1521 /* score text output */
1522 if (score_mode) {
1523 if (status == STATE_OK)
1524 printf(" Score %u", (int)host->score);
1525 else if (status==STATE_WARNING && host->score_status==status )
1526 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1527 else if (status==STATE_CRITICAL && host->score_status==status )
1528 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1530 /* order statis text output */
1531 if (order_mode) {
1532 if (status == STATE_OK)
1533 printf(" Packets in order");
1534 else if (status==STATE_CRITICAL && host->order_status==status)
1535 printf(" Packets out of order");
1538 host = host->next;
1541 /* iterate once more for pretty perfparse output */
1542 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1543 printf("|");
1545 i = 0;
1546 host = list;
1547 while(host) {
1548 if(debug) puts("");
1550 if (rta_mode) {
1551 if (host->pl<100) {
1552 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ",
1553 (targets > 1) ? host->name : "",
1554 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000,
1555 (targets > 1) ? host->name : "", (float)host->rtmax / 1000,
1556 (targets > 1) ? host->name : "", (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1557 } else {
1558 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ",
1559 (targets > 1) ? host->name : "",
1560 (targets > 1) ? host->name : "",
1561 (targets > 1) ? host->name : "");
1565 if (pl_mode) {
1566 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1569 if (jitter_mode) {
1570 if (host->pl<100) {
1571 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1572 (targets > 1) ? host->name : "",
1573 (float)host->jitter,
1574 (float)warn.jitter,
1575 (float)crit.jitter,
1576 (targets > 1) ? host->name : "",
1577 (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1578 (float)host->jitter_min / 1000
1580 } else {
1581 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ",
1582 (targets > 1) ? host->name : "",
1583 (targets > 1) ? host->name : "",
1584 (targets > 1) ? host->name : "");
1588 if (mos_mode) {
1589 if (host->pl<100) {
1590 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ",
1591 (targets > 1) ? host->name : "",
1592 (float)host->mos,
1593 (float)warn.mos,
1594 (float)crit.mos);
1595 } else {
1596 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1600 if (score_mode) {
1601 if (host->pl<100) {
1602 printf("%sscore=%u;%u;%u;0;100 ",
1603 (targets > 1) ? host->name : "",
1604 (int)host->score,
1605 (int)warn.score,
1606 (int)crit.score);
1607 } else {
1608 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1612 host = host->next;
1615 if(min_hosts_alive > -1) {
1616 if(hosts_ok >= min_hosts_alive) status = STATE_OK;
1617 else if((hosts_ok + hosts_warn) >= min_hosts_alive) status = STATE_WARNING;
1620 /* finish with an empty line */
1621 puts("");
1622 if(debug) printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1623 targets, targets_alive, hosts_ok, hosts_warn, min_hosts_alive);
1625 exit(status);
1628 static u_int
1629 get_timevaldiff(struct timeval *early, struct timeval *later)
1631 u_int ret;
1632 struct timeval now;
1634 if(!later) {
1635 gettimeofday(&now, &tz);
1636 later = &now;
1638 if(!early) early = &prog_start;
1640 /* if early > later we return 0 so as to indicate a timeout */
1641 if(early->tv_sec > later->tv_sec ||
1642 (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec))
1644 return 0;
1646 ret = (later->tv_sec - early->tv_sec) * 1000000;
1647 ret += later->tv_usec - early->tv_usec;
1649 return ret;
1652 static int
1653 add_target_ip(char *arg, struct sockaddr_storage *in)
1655 struct rta_host *host;
1656 struct sockaddr_in *sin, *host_sin;
1657 struct sockaddr_in6 *sin6, *host_sin6;
1659 if (address_family == AF_INET)
1660 sin = (struct sockaddr_in *)in;
1661 else
1662 sin6 = (struct sockaddr_in6 *)in;
1666 /* disregard obviously stupid addresses
1667 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1668 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE
1669 || sin->sin_addr.s_addr == INADDR_ANY)))
1670 || (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1671 return -1;
1674 /* no point in adding two identical IP's, so don't. ;) */
1675 host = list;
1676 while(host) {
1677 host_sin = (struct sockaddr_in *)&host->saddr_in;
1678 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1680 if( (address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr)
1681 || (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1682 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
1683 return -1;
1685 host = host->next;
1688 /* add the fresh ip */
1689 host = (struct rta_host*)malloc(sizeof(struct rta_host));
1690 if(!host) {
1691 char straddr[INET6_ADDRSTRLEN];
1692 parse_address((struct sockaddr_storage*)&in, straddr, sizeof(straddr));
1693 crash("add_target_ip(%s, %s): malloc(%lu) failed",
1694 arg, straddr, sizeof(struct rta_host));
1696 memset(host, 0, sizeof(struct rta_host));
1698 /* set the values. use calling name for output */
1699 host->name = strdup(arg);
1702 /* fill out the sockaddr_storage struct */
1703 if(address_family == AF_INET) {
1704 host_sin = (struct sockaddr_in *)&host->saddr_in;
1705 host_sin->sin_family = AF_INET;
1706 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1708 else {
1709 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1710 host_sin6->sin6_family = AF_INET6;
1711 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1714 /* fill out the sockaddr_in struct */
1715 host->rtmin = INFINITY;
1716 host->rtmax = 0;
1717 host->jitter=0;
1718 host->jitter_max=0;
1719 host->jitter_min=INFINITY;
1720 host->last_tdiff=0;
1721 host->order_status=STATE_OK;
1722 host->last_icmp_seq=0;
1723 host->rta_status=0;
1724 host->pl_status=0;
1725 host->jitter_status=0;
1726 host->mos_status=0;
1727 host->score_status=0;
1728 host->pl_status=0;
1731 if(!list) list = cursor = host;
1732 else cursor->next = host;
1734 cursor = host;
1735 targets++;
1737 return 0;
1740 /* wrapper for add_target_ip */
1741 static int
1742 add_target(char *arg)
1744 int error, result = -1;
1745 struct sockaddr_storage ip;
1746 struct addrinfo hints, *res, *p;
1747 struct sockaddr_in *sin;
1748 struct sockaddr_in6 *sin6;
1750 switch (address_family) {
1751 case -1:
1752 /* -4 and -6 are not specified on cmdline */
1753 address_family = AF_INET;
1754 sin = (struct sockaddr_in *)&ip;
1755 result = inet_pton(address_family, arg, &sin->sin_addr);
1756 #ifdef USE_IPV6
1757 if( result != 1 ){
1758 address_family = AF_INET6;
1759 sin6 = (struct sockaddr_in6 *)&ip;
1760 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1762 #endif
1763 /* If we don't find any valid addresses, we still don't know the address_family */
1764 if ( result != 1) {
1765 address_family = -1;
1767 break;
1768 case AF_INET:
1769 sin = (struct sockaddr_in *)&ip;
1770 result = inet_pton(address_family, arg, &sin->sin_addr);
1771 break;
1772 case AF_INET6:
1773 sin6 = (struct sockaddr_in6 *)&ip;
1774 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1775 break;
1776 default: crash("Address family not supported");
1779 /* don't resolve if we don't have to */
1780 if(result == 1) {
1781 /* don't add all ip's if we were given a specific one */
1782 return add_target_ip(arg, &ip);
1784 else {
1785 errno = 0;
1786 memset(&hints, 0, sizeof(hints));
1787 if (address_family == -1) {
1788 hints.ai_family = AF_UNSPEC;
1789 } else {
1790 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1792 hints.ai_socktype = SOCK_RAW;
1793 if((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1794 errno = 0;
1795 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1796 return -1;
1798 address_family = res->ai_family;
1801 /* possibly add all the IP's as targets */
1802 for(p = res; p != NULL; p = p->ai_next) {
1803 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1804 add_target_ip(arg, &ip);
1806 /* this is silly, but it works */
1807 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
1808 if(debug > 2) printf("mode: %d\n", mode);
1809 continue;
1811 break;
1813 freeaddrinfo(res);
1815 return 0;
1818 static void
1819 set_source_ip(char *arg)
1821 struct sockaddr_in src;
1823 memset(&src, 0, sizeof(src));
1824 src.sin_family = address_family;
1825 if((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1826 src.sin_addr.s_addr = get_ip_address(arg);
1827 if(bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
1828 crash("Cannot bind to IP address %s", arg);
1831 /* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1832 static in_addr_t
1833 get_ip_address(const char *ifname)
1835 // TODO: Rewrite this so the function return an error and we exit somewhere else
1836 struct sockaddr_in ip;
1837 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1838 #if defined(SIOCGIFADDR)
1839 struct ifreq ifr;
1841 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
1843 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
1845 if(ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1)
1846 crash("Cannot determine IP address of interface %s", ifname);
1848 memcpy(&ip, &ifr.ifr_addr, sizeof(ip));
1849 #else
1850 (void) ifname;
1851 errno = 0;
1852 crash("Cannot get interface IP address on this platform.");
1853 #endif
1854 return ip.sin_addr.s_addr;
1858 * u = micro
1859 * m = milli
1860 * s = seconds
1861 * return value is in microseconds
1863 static u_int
1864 get_timevar(const char *str)
1866 char p, u, *ptr;
1867 size_t len;
1868 u_int i, d; /* integer and decimal, respectively */
1869 u_int factor = 1000; /* default to milliseconds */
1871 if(!str) return 0;
1872 len = strlen(str);
1873 if(!len) return 0;
1875 /* unit might be given as ms|m (millisec),
1876 * us|u (microsec) or just plain s, for seconds */
1877 p = '\0';
1878 u = str[len - 1];
1879 if(len >= 2 && !isdigit((int)str[len - 2])) p = str[len - 2];
1880 if(p && u == 's') u = p;
1881 else if(!p) p = u;
1882 if(debug > 2) printf("evaluating %s, u: %c, p: %c\n", str, u, p);
1884 if(u == 'u') factor = 1; /* microseconds */
1885 else if(u == 'm') factor = 1000; /* milliseconds */
1886 else if(u == 's') factor = 1000000; /* seconds */
1887 if(debug > 2) printf("factor is %u\n", factor);
1889 i = strtoul(str, &ptr, 0);
1890 if(!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
1891 return i * factor;
1893 /* time specified in usecs can't have decimal points, so ignore them */
1894 if(factor == 1) return i;
1896 d = strtoul(ptr + 1, NULL, 0);
1898 /* d is decimal, so get rid of excess digits */
1899 while(d >= factor) d /= 10;
1901 /* the last parenthesis avoids floating point exceptions. */
1902 return ((i * factor) + (d * (factor / 10)));
1905 /* not too good at checking errors, but it'll do (main() should barfe on -1) */
1906 static int
1907 get_threshold(char *str, threshold *th)
1909 char *p = NULL, i = 0;
1911 if(!str || !strlen(str) || !th) return -1;
1913 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1914 p = &str[strlen(str) - 1];
1915 while(p != &str[1]) {
1916 if(*p == '%') *p = '\0';
1917 else if(*p == ',' && i) {
1918 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1919 th->pl = (unsigned char)strtoul(p+1, NULL, 0);
1920 break;
1922 i = 1;
1923 p--;
1925 th->rta = get_timevar(str);
1927 if(!th->rta) return -1;
1929 if(th->rta > MAXTTL * 1000000) th->rta = MAXTTL * 1000000;
1930 if(th->pl > 100) th->pl = 100;
1932 return 0;
1936 * This functions receives a pointer to a string which should contain a threshold for the
1937 * rta, packet_loss, jitter, mos or score mode in the form number,number[m|%]* assigns the
1938 * parsed number to the corresponding threshold variable.
1939 * @param[in,out] str String containing the given threshold values
1940 * @param[in] length strlen(str)
1941 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
1942 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
1943 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively)
1945 static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) {
1946 if (!str || !length || !warn || !crit) return false;
1949 // p points to the last char in str
1950 char *p = &str[length - 1];
1952 // first_iteration is bof-stop on stupid libc's
1953 bool first_iteration = true;
1955 while(p != &str[0]) {
1956 if( (*p == 'm') || (*p == '%') ) {
1957 *p = '\0';
1958 } else if(*p == ',' && !first_iteration) {
1959 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1961 char *start_of_value = p + 1;
1963 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)){
1964 return false;
1968 first_iteration = false;
1969 p--;
1972 return parse_threshold2_helper(p, strlen(p), warn, mode);
1975 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) {
1976 char *resultChecker = {0};
1978 switch (mode) {
1979 case const_rta_mode:
1980 thr->rta = strtod(s, &resultChecker) * 1000;
1981 break;
1982 case const_packet_loss_mode:
1983 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0);
1984 break;
1985 case const_jitter_mode:
1986 thr->jitter = strtod(s, &resultChecker);
1988 break;
1989 case const_mos_mode:
1990 thr->mos = strtod(s, &resultChecker);
1991 break;
1992 case const_score_mode:
1993 thr->score = strtod(s, &resultChecker);
1994 break;
1997 if (resultChecker == s) {
1998 // Failed to parse
1999 return false;
2002 if (resultChecker != (s + length)) {
2003 // Trailing symbols
2004 return false;
2007 return true;
2010 unsigned short
2011 icmp_checksum(uint16_t *p, size_t n)
2013 unsigned short cksum;
2014 long sum = 0;
2016 /* sizeof(uint16_t) == 2 */
2017 while(n >= 2) {
2018 sum += *(p++);
2019 n -= 2;
2022 /* mop up the occasional odd byte */
2023 if(n == 1) sum += *((uint8_t *)p -1);
2025 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2026 sum += (sum >> 16); /* add carry */
2027 cksum = ~sum; /* ones-complement, trunc to 16 bits */
2029 return cksum;
2032 void
2033 print_help(void)
2035 /*print_revision (progname);*/ /* FIXME: Why? */
2036 printf ("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2038 printf (COPYRIGHT, copyright, email);
2040 printf ("\n\n");
2042 print_usage ();
2044 printf (UT_HELP_VRSN);
2045 printf (UT_EXTRA_OPTS);
2047 printf (" %s\n", "-H");
2048 printf (" %s\n", _("specify a target"));
2049 printf (" %s\n", "[-4|-6]");
2050 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
2051 printf (" %s\n", "-w");
2052 printf (" %s", _("warning threshold (currently "));
2053 printf ("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);
2054 printf (" %s\n", "-c");
2055 printf (" %s", _("critical threshold (currently "));
2056 printf ("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl);
2058 printf (" %s\n", "-R");
2059 printf (" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms"));
2060 printf (" %s\n", "-P");
2061 printf (" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2062 printf (" %s\n", "-J");
2063 printf (" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2064 printf (" %s\n", "-M");
2065 printf (" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2066 printf (" %s\n", "-S");
2067 printf (" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2068 printf (" %s\n", "-O");
2069 printf (" %s\n", _("detect out of order ICMP packts "));
2070 printf (" %s\n", "-H");
2071 printf (" %s\n", _("specify a target"));
2072 printf (" %s\n", "-s");
2073 printf (" %s\n", _("specify a source IP address or device name"));
2074 printf (" %s\n", "-n");
2075 printf (" %s", _("number of packets to send (currently "));
2076 printf ("%u)\n",packets);
2077 printf (" %s\n", "-p");
2078 printf (" %s", _("number of packets to send (currently "));
2079 printf ("%u)\n",packets);
2080 printf (" %s\n", "-i");
2081 printf (" %s", _("max packet interval (currently "));
2082 printf ("%0.3fms)\n",(float)pkt_interval / 1000);
2083 printf (" %s\n", "-I");
2084 printf (" %s", _("max target interval (currently "));
2085 printf ("%0.3fms)\n", (float)target_interval / 1000);
2086 printf (" %s\n", "-m");
2087 printf (" %s",_("number of alive hosts required for success"));
2088 printf ("\n");
2089 printf (" %s\n", "-l");
2090 printf (" %s", _("TTL on outgoing packets (currently "));
2091 printf ("%u)\n", ttl);
2092 printf (" %s\n", "-t");
2093 printf (" %s",_("timeout value (seconds, currently "));
2094 printf ("%u)\n", timeout);
2095 printf (" %s\n", "-b");
2096 printf (" %s\n", _("Number of icmp data bytes to send"));
2097 printf (" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"),icmp_data_size, ICMP_MINLEN);
2098 printf (" %s\n", "-v");
2099 printf (" %s\n", _("verbose"));
2100 printf ("\n");
2101 printf ("%s\n", _("Notes:"));
2102 printf (" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2103 printf (" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not."));
2104 printf ("\n");
2105 printf (" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2106 printf (" %s\n", _("packet loss. The default values should work well for most users."));
2107 printf (" %s\n", _("You can specify different RTA factors using the standardized abbreviations"));
2108 printf (" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2109 /* -d not yet implemented */
2110 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops"));
2111 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2112 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2113 printf ("\n");
2114 printf (" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2115 /* printf ("%s\n", _("Long options are currently unsupported."));
2116 printf ("%s\n", _("Options marked with * require an argument"));
2119 printf (UT_SUPPORT);
2124 void
2125 print_usage (void)
2127 printf ("%s\n", _("Usage:"));
2128 printf(" %s [options] [-H] host1 host2 hostN\n", progname);