check_icmp: Update copyright
[monitoring-plugins.git] / plugins-root / check_icmp.c
blob2fd92cdc0749952f11126104583dbaf1c2ce086b
1 /*****************************************************************************
3 * Monitoring check_icmp plugin
5 * License: GPL
6 * Copyright (c) 2005-2024 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-2024";
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>
68 /** sometimes undefined system macros (quite a few, actually) **/
69 #ifndef MAXTTL
70 # define MAXTTL 255
71 #endif
72 #ifndef INADDR_NONE
73 # define INADDR_NONE (in_addr_t)(-1)
74 #endif
76 #ifndef SOL_IP
77 # define SOL_IP 0
78 #endif
80 /* we bundle these in one #ifndef, since they're all from BSD
81 * Put individual #ifndef's around those that bother you */
82 #ifndef ICMP_UNREACH_NET_UNKNOWN
83 # define ICMP_UNREACH_NET_UNKNOWN 6
84 # define ICMP_UNREACH_HOST_UNKNOWN 7
85 # define ICMP_UNREACH_ISOLATED 8
86 # define ICMP_UNREACH_NET_PROHIB 9
87 # define ICMP_UNREACH_HOST_PROHIB 10
88 # define ICMP_UNREACH_TOSNET 11
89 # define ICMP_UNREACH_TOSHOST 12
90 #endif
91 /* tru64 has the ones above, but not these */
92 #ifndef ICMP_UNREACH_FILTER_PROHIB
93 # define ICMP_UNREACH_FILTER_PROHIB 13
94 # define ICMP_UNREACH_HOST_PRECEDENCE 14
95 # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
96 #endif
98 typedef unsigned short range_t; /* type for get_range() -- unimplemented */
100 typedef struct rta_host {
101 unsigned short id; /* id in **table, and icmp pkts */
102 char *name; /* arg used for adding this host */
103 char *msg; /* icmp error message, if any */
104 struct sockaddr_storage saddr_in; /* the address of this host */
105 struct sockaddr_storage error_addr; /* stores address of error replies */
106 unsigned long long time_waited; /* total time waited, in usecs */
107 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
108 unsigned char icmp_type, icmp_code; /* type and code from errors */
109 unsigned short flags; /* control/status flags */
110 double rta; /* measured RTA */
111 int rta_status; // check result for RTA checks
112 double rtmax; /* max rtt */
113 double rtmin; /* min rtt */
114 double jitter; /* measured jitter */
115 int jitter_status; // check result for Jitter checks
116 double jitter_max; /* jitter rtt maximum */
117 double jitter_min; /* jitter rtt minimum */
118 double EffectiveLatency;
119 double mos; /* Mean opnion score */
120 int mos_status; // check result for MOS checks
121 double score; /* score */
122 int score_status; // check result for score checks
123 u_int last_tdiff;
124 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
125 unsigned char pl; /* measured packet loss */
126 int pl_status; // check result for packet loss checks
127 struct rta_host *next; /* linked list */
128 int order_status; // check result for packet order checks
129 } rta_host;
131 #define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 /* threshold structure. all values are maximum allowed, exclusive */
134 typedef struct threshold {
135 unsigned char pl; /* max allowed packet loss in percent */
136 unsigned int rta; /* roundtrip time average, microseconds */
137 double jitter; /* jitter time average, microseconds */
138 double mos; /* MOS */
139 double score; /* Score */
140 } threshold;
142 /* the data structure */
143 typedef struct icmp_ping_data {
144 struct timeval stime; /* timestamp (saved in protocol struct as well) */
145 unsigned short ping_id;
146 } icmp_ping_data;
148 typedef union ip_hdr {
149 struct ip ip;
150 struct ip6_hdr ip6;
151 } ip_hdr;
153 typedef union icmp_packet {
154 void *buf;
155 struct icmp *icp;
156 struct icmp6_hdr *icp6;
157 u_short *cksum_in;
158 } icmp_packet;
160 /* the different modes of this program are as follows:
161 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
162 * MODE_HOSTCHECK: Return immediately upon any sign of life
163 * In addition, sends packets to ALL addresses assigned
164 * to this host (as returned by gethostbyname() or
165 * gethostbyaddr() and expects one host only to be checked at
166 * a time. Therefore, any packet response what so ever will
167 * count as a sign of life, even when received outside
168 * crit.rta limit. Do not misspell any additional IP's.
169 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
170 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
171 * tcp and udp args does this)
173 #define MODE_RTA 0
174 #define MODE_HOSTCHECK 1
175 #define MODE_ALL 2
176 #define MODE_ICMP 3
178 enum enum_threshold_mode { const_rta_mode, const_packet_loss_mode, const_jitter_mode, const_mos_mode, const_score_mode };
180 typedef enum enum_threshold_mode threshold_mode;
182 /* the different ping types we can do
183 * TODO: investigate ARP ping as well */
184 #define HAVE_ICMP 1
185 #define HAVE_UDP 2
186 #define HAVE_TCP 4
187 #define HAVE_ARP 8
189 #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
190 #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
191 #define IP_HDR_SIZE 20
192 #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
193 #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
195 /* various target states */
196 #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
197 #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
198 #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
199 #define TSTATE_UNREACH 0x08
201 /** prototypes **/
202 void print_help(void);
203 void print_usage(void);
204 static u_int get_timevar(const char *);
205 static u_int get_timevaldiff(struct timeval *, struct timeval *);
206 static in_addr_t get_ip_address(const char *);
207 static int wait_for_reply(int, u_int);
208 static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *);
209 static int send_icmp_ping(int, struct rta_host *);
210 static int get_threshold(char *str, threshold *th);
211 static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode);
212 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode);
213 static void run_checks(void);
214 static void set_source_ip(char *);
215 static int add_target(char *);
216 static int add_target_ip(char *, struct sockaddr_storage *);
217 static int handle_random_icmp(unsigned char *, struct sockaddr_storage *);
218 static void parse_address(struct sockaddr_storage *, char *, int);
219 static unsigned short icmp_checksum(uint16_t *, size_t);
220 static void finish(int);
221 static void crash(const char *, ...);
223 /** external **/
224 extern int optind;
225 extern char *optarg;
226 extern char **environ;
228 /** global variables **/
229 static struct rta_host **table, *cursor, *list;
231 static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0};
232 static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0};
234 static int mode, protocols, sockets, debug = 0, timeout = 10;
235 static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE;
236 static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN;
238 static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0;
239 #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
240 static unsigned short targets_down = 0, targets = 0, packets = 0;
241 #define targets_alive (targets - targets_down)
242 static unsigned int retry_interval, pkt_interval, target_interval;
243 static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
244 static pid_t pid;
245 static struct timezone tz;
246 static struct timeval prog_start;
247 static unsigned long long max_completion_time = 0;
248 static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
249 static int min_hosts_alive = -1;
250 static float pkt_backoff_factor = 1.5;
251 static float target_backoff_factor = 1.5;
252 static bool rta_mode = false;
253 static bool pl_mode = false;
254 static bool jitter_mode = false;
255 static bool score_mode = false;
256 static bool mos_mode = false;
257 static bool order_mode = false;
259 /** code start **/
260 static void crash(const char *fmt, ...) {
261 va_list ap;
263 printf("%s: ", progname);
265 va_start(ap, fmt);
266 vprintf(fmt, ap);
267 va_end(ap);
269 if (errno)
270 printf(": %s", strerror(errno));
271 puts("");
273 exit(3);
276 static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) {
277 const char *msg = "unreachable";
279 if (debug > 1)
280 printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
281 switch (icmp_type) {
282 case ICMP_UNREACH:
283 switch (icmp_code) {
284 case ICMP_UNREACH_NET:
285 msg = "Net unreachable";
286 break;
287 case ICMP_UNREACH_HOST:
288 msg = "Host unreachable";
289 break;
290 case ICMP_UNREACH_PROTOCOL:
291 msg = "Protocol unreachable (firewall?)";
292 break;
293 case ICMP_UNREACH_PORT:
294 msg = "Port unreachable (firewall?)";
295 break;
296 case ICMP_UNREACH_NEEDFRAG:
297 msg = "Fragmentation needed";
298 break;
299 case ICMP_UNREACH_SRCFAIL:
300 msg = "Source route failed";
301 break;
302 case ICMP_UNREACH_ISOLATED:
303 msg = "Source host isolated";
304 break;
305 case ICMP_UNREACH_NET_UNKNOWN:
306 msg = "Unknown network";
307 break;
308 case ICMP_UNREACH_HOST_UNKNOWN:
309 msg = "Unknown host";
310 break;
311 case ICMP_UNREACH_NET_PROHIB:
312 msg = "Network denied (firewall?)";
313 break;
314 case ICMP_UNREACH_HOST_PROHIB:
315 msg = "Host denied (firewall?)";
316 break;
317 case ICMP_UNREACH_TOSNET:
318 msg = "Bad TOS for network (firewall?)";
319 break;
320 case ICMP_UNREACH_TOSHOST:
321 msg = "Bad TOS for host (firewall?)";
322 break;
323 case ICMP_UNREACH_FILTER_PROHIB:
324 msg = "Prohibited by filter (firewall)";
325 break;
326 case ICMP_UNREACH_HOST_PRECEDENCE:
327 msg = "Host precedence violation";
328 break;
329 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
330 msg = "Precedence cutoff";
331 break;
332 default:
333 msg = "Invalid code";
334 break;
336 break;
338 case ICMP_TIMXCEED:
339 /* really 'out of reach', or non-existent host behind a router serving
340 * two different subnets */
341 switch (icmp_code) {
342 case ICMP_TIMXCEED_INTRANS:
343 msg = "Time to live exceeded in transit";
344 break;
345 case ICMP_TIMXCEED_REASS:
346 msg = "Fragment reassembly time exceeded";
347 break;
348 default:
349 msg = "Invalid code";
350 break;
352 break;
354 case ICMP_SOURCEQUENCH:
355 msg = "Transmitting too fast";
356 break;
357 case ICMP_REDIRECT:
358 msg = "Redirect (change route)";
359 break;
360 case ICMP_PARAMPROB:
361 msg = "Bad IP header (required option absent)";
362 break;
364 /* the following aren't error messages, so ignore */
365 case ICMP_TSTAMP:
366 case ICMP_TSTAMPREPLY:
367 case ICMP_IREQ:
368 case ICMP_IREQREPLY:
369 case ICMP_MASKREQ:
370 case ICMP_MASKREPLY:
371 default:
372 msg = "";
373 break;
376 return msg;
379 static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) {
380 struct icmp p, sent_icmp;
381 struct rta_host *host = NULL;
383 memcpy(&p, packet, sizeof(p));
384 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) {
385 /* echo request from us to us (pinging localhost) */
386 return 0;
389 if (debug)
390 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr);
392 /* only handle a few types, since others can't possibly be replies to
393 * us in a sane network (if it is anyway, it will be counted as lost
394 * at summary time, but not as quickly as a proper response */
395 /* TIMXCEED can be an unreach from a router with multiple IP's which
396 * serves two different subnets on the same interface and a dead host
397 * on one net is pinged from the other. The router will respond to
398 * itself and thus set TTL=0 so as to not loop forever. Even when
399 * TIMXCEED actually sends a proper icmp response we will have passed
400 * too many hops to have a hope of reaching it later, in which case it
401 * indicates overconfidence in the network, poor routing or both. */
402 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) {
403 return 0;
406 /* might be for us. At least it holds the original package (according
407 * to RFC 792). If it isn't, just ignore it */
408 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
409 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) {
410 if (debug)
411 printf("Packet is no response to a packet we sent\n");
412 return 0;
415 /* it is indeed a response for us */
416 host = table[ntohs(sent_icmp.icmp_seq) / packets];
417 if (debug) {
418 char address[INET6_ADDRSTRLEN];
419 parse_address(addr, address, sizeof(address));
420 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name);
423 icmp_lost++;
424 host->icmp_lost++;
425 /* don't spend time on lost hosts any more */
426 if (host->flags & FLAG_LOST_CAUSE)
427 return 0;
429 /* source quench means we're sending too fast, so increase the
430 * interval and mark this packet lost */
431 if (p.icmp_type == ICMP_SOURCEQUENCH) {
432 pkt_interval *= pkt_backoff_factor;
433 target_interval *= target_backoff_factor;
434 } else {
435 targets_down++;
436 host->flags |= FLAG_LOST_CAUSE;
438 host->icmp_type = p.icmp_type;
439 host->icmp_code = p.icmp_code;
440 host->error_addr = *addr;
442 return 0;
445 void parse_address(struct sockaddr_storage *addr, char *address, int size) {
446 switch (address_family) {
447 case AF_INET:
448 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size);
449 break;
450 case AF_INET6:
451 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size);
452 break;
456 int main(int argc, char **argv) {
457 int i;
458 char *ptr;
459 long int arg;
460 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
461 int result;
462 struct rta_host *host;
463 #ifdef HAVE_SIGACTION
464 struct sigaction sig_action;
465 #endif
466 #ifdef SO_TIMESTAMP
467 int on = 1;
468 #endif
469 char *source_ip = NULL;
470 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
471 setlocale(LC_ALL, "");
472 bindtextdomain(PACKAGE, LOCALEDIR);
473 textdomain(PACKAGE);
475 /* we only need to be setsuid when we get the sockets, so do
476 * that before pointer magic (esp. on network data) */
477 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
479 address_family = -1;
480 int icmp_proto = IPPROTO_ICMP;
482 /* get calling name the old-fashioned way for portability instead
483 * of relying on the glibc-ism __progname */
484 ptr = strrchr(argv[0], '/');
485 if (ptr)
486 progname = &ptr[1];
487 else
488 progname = argv[0];
490 /* now set defaults. Use progname to set them initially (allows for
491 * superfast check_host program when target host is up */
492 cursor = list = NULL;
493 table = NULL;
495 mode = MODE_RTA;
496 /* Default critical thresholds */
497 crit.rta = 500000;
498 crit.pl = 80;
499 crit.jitter = 50;
500 crit.mos = 3;
501 crit.score = 70;
502 /* Default warning thresholds */
503 warn.rta = 200000;
504 warn.pl = 40;
505 warn.jitter = 40;
506 warn.mos = 3.5;
507 warn.score = 80;
509 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
510 pkt_interval = 80000; /* 80 msec packet interval by default */
511 packets = 5;
513 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
514 mode = MODE_ICMP;
515 protocols = HAVE_ICMP;
516 } else if (!strcmp(progname, "check_host")) {
517 mode = MODE_HOSTCHECK;
518 pkt_interval = 1000000;
519 packets = 5;
520 crit.rta = warn.rta = 1000000;
521 crit.pl = warn.pl = 100;
522 } else if (!strcmp(progname, "check_rta_multi")) {
523 mode = MODE_ALL;
524 target_interval = 0;
525 pkt_interval = 50000;
526 packets = 5;
529 /* support "--help" and "--version" */
530 if (argc == 2) {
531 if (!strcmp(argv[1], "--help"))
532 strcpy(argv[1], "-h");
533 if (!strcmp(argv[1], "--version"))
534 strcpy(argv[1], "-V");
537 /* Parse protocol arguments first */
538 for (i = 1; i < argc; i++) {
539 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
540 switch (arg) {
541 case '4':
542 if (address_family != -1)
543 crash("Multiple protocol versions not supported");
544 address_family = AF_INET;
545 break;
546 case '6':
547 #ifdef USE_IPV6
548 if (address_family != -1)
549 crash("Multiple protocol versions not supported");
550 address_family = AF_INET6;
551 #else
552 usage(_("IPv6 support not available\n"));
553 #endif
554 break;
559 /* Reset argument scanning */
560 optind = 1;
562 unsigned long size;
563 bool err;
564 /* parse the arguments */
565 for (i = 1; i < argc; i++) {
566 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
567 switch (arg) {
568 case 'v':
569 debug++;
570 break;
571 case 'b':
572 size = strtol(optarg, NULL, 0);
573 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
574 icmp_data_size = size;
575 icmp_pkt_size = size + ICMP_MINLEN;
576 } else
577 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data), MAX_PING_DATA - 1);
578 break;
579 case 'i':
580 pkt_interval = get_timevar(optarg);
581 break;
582 case 'I':
583 target_interval = get_timevar(optarg);
584 break;
585 case 'w':
586 get_threshold(optarg, &warn);
587 break;
588 case 'c':
589 get_threshold(optarg, &crit);
590 break;
591 case 'n':
592 case 'p':
593 packets = strtoul(optarg, NULL, 0);
594 break;
595 case 't':
596 timeout = strtoul(optarg, NULL, 0);
597 if (!timeout)
598 timeout = 10;
599 break;
600 case 'H':
601 add_target(optarg);
602 break;
603 case 'l':
604 ttl = (int)strtoul(optarg, NULL, 0);
605 break;
606 case 'm':
607 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
608 break;
609 case 'd': /* implement later, for cluster checks */
610 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
611 if (ptr) {
612 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
614 break;
615 case 's': /* specify source IP address */
616 source_ip = optarg;
617 break;
618 case 'V': /* version */
619 print_revision(progname, NP_VERSION);
620 exit(STATE_UNKNOWN);
621 case 'h': /* help */
622 print_help();
623 exit(STATE_UNKNOWN);
624 break;
625 case 'R': /* RTA mode */
626 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
627 if (!err) {
628 crash("Failed to parse RTA threshold");
631 rta_mode = true;
632 break;
633 case 'P': /* packet loss mode */
634 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
635 if (!err) {
636 crash("Failed to parse packet loss threshold");
639 pl_mode = true;
640 break;
641 case 'J': /* jitter mode */
642 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode);
643 if (!err) {
644 crash("Failed to parse jitter threshold");
647 jitter_mode = true;
648 break;
649 case 'M': /* MOS mode */
650 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode);
651 if (!err) {
652 crash("Failed to parse MOS threshold");
655 mos_mode = true;
656 break;
657 case 'S': /* score mode */
658 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode);
659 if (!err) {
660 crash("Failed to parse score threshold");
663 score_mode = true;
664 break;
665 case 'O': /* out of order mode */
666 order_mode = true;
667 break;
672 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
673 environ = NULL;
675 /* use the pid to mark packets as ours */
676 /* Some systems have 32-bit pid_t so mask off only 16 bits */
677 pid = getpid() & 0xffff;
678 /* printf("pid = %u\n", pid); */
680 /* Parse extra opts if any */
681 argv = np_extra_opts(&argc, argv, progname);
683 argv = &argv[optind];
684 while (*argv) {
685 add_target(*argv);
686 argv++;
689 if (!targets) {
690 errno = 0;
691 crash("No hosts to check");
694 // add_target might change address_family
695 switch (address_family) {
696 case AF_INET:
697 icmp_proto = IPPROTO_ICMP;
698 break;
699 case AF_INET6:
700 icmp_proto = IPPROTO_ICMPV6;
701 break;
702 default:
703 crash("Address family not supported");
705 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
706 sockets |= HAVE_ICMP;
707 else
708 icmp_sockerrno = errno;
710 if (source_ip)
711 set_source_ip(source_ip);
713 #ifdef SO_TIMESTAMP
714 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
715 if (debug)
716 printf("Warning: no SO_TIMESTAMP support\n");
717 #endif // SO_TIMESTAMP
719 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
720 if (setuid(getuid()) == -1) {
721 printf("ERROR: Failed to drop privileges\n");
722 return 1;
725 if (!sockets) {
726 if (icmp_sock == -1) {
727 errno = icmp_sockerrno;
728 crash("Failed to obtain ICMP socket");
729 return -1;
731 /* if(udp_sock == -1) { */
732 /* errno = icmp_sockerrno; */
733 /* crash("Failed to obtain UDP socket"); */
734 /* return -1; */
735 /* } */
736 /* if(tcp_sock == -1) { */
737 /* errno = icmp_sockerrno; */
738 /* crash("Failed to obtain TCP socker"); */
739 /* return -1; */
740 /* } */
742 if (!ttl)
743 ttl = 64;
745 if (icmp_sock) {
746 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
747 if (debug) {
748 if (result == -1)
749 printf("setsockopt failed\n");
750 else
751 printf("ttl set to %u\n", ttl);
755 /* stupid users should be able to give whatever thresholds they want
756 * (nothing will break if they do), but some anal plugin maintainer
757 * will probably add some printf() thing here later, so it might be
758 * best to at least show them where to do it. ;) */
759 if (warn.pl > crit.pl)
760 warn.pl = crit.pl;
761 if (warn.rta > crit.rta)
762 warn.rta = crit.rta;
763 if (warn_down > crit_down)
764 crit_down = warn_down;
765 if (warn.jitter > crit.jitter)
766 crit.jitter = warn.jitter;
767 if (warn.mos < crit.mos)
768 warn.mos = crit.mos;
769 if (warn.score < crit.score)
770 warn.score = crit.score;
772 #ifdef HAVE_SIGACTION
773 sig_action.sa_sigaction = NULL;
774 sig_action.sa_handler = finish;
775 sigfillset(&sig_action.sa_mask);
776 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
777 sigaction(SIGINT, &sig_action, NULL);
778 sigaction(SIGHUP, &sig_action, NULL);
779 sigaction(SIGTERM, &sig_action, NULL);
780 sigaction(SIGALRM, &sig_action, NULL);
781 #else /* HAVE_SIGACTION */
782 signal(SIGINT, finish);
783 signal(SIGHUP, finish);
784 signal(SIGTERM, finish);
785 signal(SIGALRM, finish);
786 #endif /* HAVE_SIGACTION */
787 if (debug)
788 printf("Setting alarm timeout to %u seconds\n", timeout);
789 alarm(timeout);
791 /* make sure we don't wait any longer than necessary */
792 gettimeofday(&prog_start, &tz);
793 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta;
795 if (debug) {
796 printf("packets: %u, targets: %u\n"
797 "target_interval: %0.3f, pkt_interval %0.3f\n"
798 "crit.rta: %0.3f\n"
799 "max_completion_time: %0.3f\n",
800 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, (float)max_completion_time / 1000);
803 if (debug) {
804 if (max_completion_time > (u_int)timeout * 1000000) {
805 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout);
806 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1);
810 if (debug) {
811 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl);
812 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval);
813 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout);
816 if (packets > 20) {
817 errno = 0;
818 crash("packets is > 20 (%d)", packets);
821 if (min_hosts_alive < -1) {
822 errno = 0;
823 crash("minimum alive hosts is negative (%i)", min_hosts_alive);
826 host = list;
827 table = malloc(sizeof(struct rta_host *) * targets);
828 if (!table) {
829 crash("main(): malloc failed for host table");
832 i = 0;
833 while (host) {
834 host->id = i * packets;
835 table[i] = host;
836 host = host->next;
837 i++;
840 run_checks();
842 errno = 0;
843 finish(0);
845 return (0);
848 static void run_checks(void) {
849 u_int i, t;
850 u_int final_wait, time_passed;
852 /* this loop might actually violate the pkt_interval or target_interval
853 * settings, but only if there aren't any packets on the wire which
854 * indicates that the target can handle an increased packet rate */
855 for (i = 0; i < packets; i++) {
856 for (t = 0; t < targets; t++) {
857 /* don't send useless packets */
858 if (!targets_alive)
859 finish(0);
860 if (table[t]->flags & FLAG_LOST_CAUSE) {
861 if (debug)
862 printf("%s is a lost cause. not sending any more\n", table[t]->name);
863 continue;
866 /* we're still in the game, so send next packet */
867 (void)send_icmp_ping(icmp_sock, table[t]);
868 wait_for_reply(icmp_sock, target_interval);
870 wait_for_reply(icmp_sock, pkt_interval * targets);
873 if (icmp_pkts_en_route && targets_alive) {
874 time_passed = get_timevaldiff(NULL, NULL);
875 final_wait = max_completion_time - time_passed;
877 if (debug) {
878 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time);
880 if (time_passed > max_completion_time) {
881 if (debug)
882 printf("Time passed. Finishing up\n");
883 finish(0);
886 /* catch the packets that might come in within the timeframe, but
887 * haven't yet */
888 if (debug)
889 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000);
890 wait_for_reply(icmp_sock, final_wait);
894 /* response structure:
895 * IPv4:
896 * ip header : 20 bytes
897 * icmp header : 28 bytes
898 * IPv6:
899 * ip header : 40 bytes
900 * icmp header : 28 bytes
901 * both:
902 * icmp echo reply : the rest
904 static int wait_for_reply(int sock, u_int t) {
905 int n, hlen;
906 static unsigned char buf[65536];
907 struct sockaddr_storage resp_addr;
908 union ip_hdr *ip;
909 union icmp_packet packet;
910 struct rta_host *host;
911 struct icmp_ping_data data;
912 struct timeval wait_start, now;
913 u_int tdiff, i, per_pkt_wait;
914 double jitter_tmp;
916 if (!(packet.buf = malloc(icmp_pkt_size))) {
917 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
918 return -1; /* might be reached if we're in debug mode */
921 memset(packet.buf, 0, icmp_pkt_size);
923 /* if we can't listen or don't have anything to listen to, just return */
924 if (!t || !icmp_pkts_en_route) {
925 free(packet.buf);
926 return 0;
929 gettimeofday(&wait_start, &tz);
931 i = t;
932 per_pkt_wait = t / icmp_pkts_en_route;
933 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
934 t = per_pkt_wait;
936 /* wrap up if all targets are declared dead */
937 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) {
938 finish(0);
941 /* reap responses until we hit a timeout */
942 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now);
943 if (!n) {
944 if (debug > 1) {
945 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait);
947 continue; /* timeout for this one, so keep trying */
949 if (n < 0) {
950 if (debug)
951 printf("recvfrom_wto() returned errors\n");
952 free(packet.buf);
953 return n;
956 // FIXME: with ipv6 we don't have an ip header here
957 if (address_family != AF_INET6) {
958 ip = (union ip_hdr *)buf;
960 if (debug > 1) {
961 char address[INET6_ADDRSTRLEN];
962 parse_address(&resp_addr, address, sizeof(address));
963 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address);
967 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
968 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
969 /* alpha headers are decidedly broken. Using an ansi compiler,
970 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
971 * off the bottom 4 bits */
972 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
973 /* #else */
974 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
975 /* #endif */
977 if (n < (hlen + ICMP_MINLEN)) {
978 char address[INET6_ADDRSTRLEN];
979 parse_address(&resp_addr, address, sizeof(address));
980 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address);
982 /* else if(debug) { */
983 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
984 /* hlen, ntohs(ip->ip_len) - hlen, */
985 /* sizeof(struct ip), icmp_pkt_size); */
986 /* } */
988 /* check the response */
990 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
991 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
992 : sizeof(struct icmp));*/
994 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || ntohs(packet.icp->icmp_seq) >= targets * packets)) ||
995 (address_family == PF_INET6 &&
996 (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
997 if (debug > 2)
998 printf("not a proper ICMP_ECHOREPLY\n");
999 handle_random_icmp(buf + hlen, &resp_addr);
1000 continue;
1003 /* this is indeed a valid response */
1004 if (address_family == PF_INET) {
1005 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1006 if (debug > 2)
1007 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1008 packet.icp->icmp_cksum);
1009 host = table[ntohs(packet.icp->icmp_seq) / packets];
1010 } else {
1011 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1012 if (debug > 2)
1013 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1014 packet.icp6->icmp6_cksum);
1015 host = table[ntohs(packet.icp6->icmp6_seq) / packets];
1018 tdiff = get_timevaldiff(&data.stime, &now);
1020 if (host->last_tdiff > 0) {
1021 /* Calculate jitter */
1022 if (host->last_tdiff > tdiff) {
1023 jitter_tmp = host->last_tdiff - tdiff;
1024 } else {
1025 jitter_tmp = tdiff - host->last_tdiff;
1028 if (host->jitter == 0) {
1029 host->jitter = jitter_tmp;
1030 host->jitter_max = jitter_tmp;
1031 host->jitter_min = jitter_tmp;
1032 } else {
1033 host->jitter += jitter_tmp;
1035 if (jitter_tmp < host->jitter_min) {
1036 host->jitter_min = jitter_tmp;
1039 if (jitter_tmp > host->jitter_max) {
1040 host->jitter_max = jitter_tmp;
1044 /* Check if packets in order */
1045 if (host->last_icmp_seq >= packet.icp->icmp_seq)
1046 host->order_status = STATE_CRITICAL;
1048 host->last_tdiff = tdiff;
1050 host->last_icmp_seq = packet.icp->icmp_seq;
1052 host->time_waited += tdiff;
1053 host->icmp_recv++;
1054 icmp_recv++;
1055 if (tdiff > (unsigned int)host->rtmax)
1056 host->rtmax = tdiff;
1057 if (tdiff < (unsigned int)host->rtmin)
1058 host->rtmin = tdiff;
1060 if (debug) {
1061 char address[INET6_ADDRSTRLEN];
1062 parse_address(&resp_addr, address, sizeof(address));
1064 switch (address_family) {
1065 case AF_INET: {
1066 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, ip->ip.ip_ttl,
1067 (float)host->rtmax / 1000, (float)host->rtmin / 1000);
1068 break;
1070 case AF_INET6: {
1071 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000);
1076 /* if we're in hostcheck mode, exit with limited printouts */
1077 if (mode == MODE_HOSTCHECK) {
1078 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1079 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1080 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000);
1081 exit(STATE_OK);
1085 free(packet.buf);
1086 return 0;
1089 /* the ping functions */
1090 static int send_icmp_ping(int sock, struct rta_host *host) {
1091 long int len;
1092 size_t addrlen;
1093 struct icmp_ping_data data;
1094 struct msghdr hdr;
1095 struct iovec iov;
1096 struct timeval tv;
1097 void *buf = NULL;
1099 if (sock == -1) {
1100 errno = 0;
1101 crash("Attempt to send on bogus socket");
1102 return -1;
1105 if (!buf) {
1106 if (!(buf = malloc(icmp_pkt_size))) {
1107 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
1108 return -1; /* might be reached if we're in debug mode */
1111 memset(buf, 0, icmp_pkt_size);
1113 if ((gettimeofday(&tv, &tz)) == -1) {
1114 free(buf);
1115 return -1;
1118 data.ping_id = 10; /* host->icmp.icmp_sent; */
1119 memcpy(&data.stime, &tv, sizeof(tv));
1121 if (address_family == AF_INET) {
1122 struct icmp *icp = (struct icmp *)buf;
1123 addrlen = sizeof(struct sockaddr_in);
1125 memcpy(&icp->icmp_data, &data, sizeof(data));
1127 icp->icmp_type = ICMP_ECHO;
1128 icp->icmp_code = 0;
1129 icp->icmp_cksum = 0;
1130 icp->icmp_id = htons(pid);
1131 icp->icmp_seq = htons(host->id++);
1132 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1134 if (debug > 2)
1135 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq),
1136 icp->icmp_cksum, host->name);
1137 } else {
1138 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1139 addrlen = sizeof(struct sockaddr_in6);
1141 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
1143 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1144 icp6->icmp6_code = 0;
1145 icp6->icmp6_cksum = 0;
1146 icp6->icmp6_id = htons(pid);
1147 icp6->icmp6_seq = htons(host->id++);
1148 // let checksum be calculated automatically
1150 if (debug > 2) {
1151 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq),
1152 icp6->icmp6_cksum, host->name);
1156 memset(&iov, 0, sizeof(iov));
1157 iov.iov_base = buf;
1158 iov.iov_len = icmp_pkt_size;
1160 memset(&hdr, 0, sizeof(hdr));
1161 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
1162 hdr.msg_namelen = addrlen;
1163 hdr.msg_iov = &iov;
1164 hdr.msg_iovlen = 1;
1166 errno = 0;
1168 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1169 #ifdef MSG_CONFIRM
1170 len = sendmsg(sock, &hdr, MSG_CONFIRM);
1171 #else
1172 len = sendmsg(sock, &hdr, 0);
1173 #endif
1175 free(buf);
1177 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1178 if (debug) {
1179 char address[INET6_ADDRSTRLEN];
1180 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1181 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1183 errno = 0;
1184 return -1;
1187 icmp_sent++;
1188 host->icmp_sent++;
1190 return 0;
1193 static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) {
1194 u_int slen;
1195 int n, ret;
1196 struct timeval to, then, now;
1197 fd_set rd, wr;
1198 #ifdef HAVE_MSGHDR_MSG_CONTROL
1199 char ans_data[4096];
1200 #endif // HAVE_MSGHDR_MSG_CONTROL
1201 struct msghdr hdr;
1202 struct iovec iov;
1203 #ifdef SO_TIMESTAMP
1204 struct cmsghdr *chdr;
1205 #endif
1207 if (!*timo) {
1208 if (debug)
1209 printf("*timo is not\n");
1210 return 0;
1213 to.tv_sec = *timo / 1000000;
1214 to.tv_usec = (*timo - (to.tv_sec * 1000000));
1216 FD_ZERO(&rd);
1217 FD_ZERO(&wr);
1218 FD_SET(sock, &rd);
1219 errno = 0;
1220 gettimeofday(&then, &tz);
1221 n = select(sock + 1, &rd, &wr, NULL, &to);
1222 if (n < 0)
1223 crash("select() in recvfrom_wto");
1224 gettimeofday(&now, &tz);
1225 *timo = get_timevaldiff(&then, &now);
1227 if (!n)
1228 return 0; /* timeout */
1230 slen = sizeof(struct sockaddr_storage);
1232 memset(&iov, 0, sizeof(iov));
1233 iov.iov_base = buf;
1234 iov.iov_len = len;
1236 memset(&hdr, 0, sizeof(hdr));
1237 hdr.msg_name = saddr;
1238 hdr.msg_namelen = slen;
1239 hdr.msg_iov = &iov;
1240 hdr.msg_iovlen = 1;
1241 #ifdef HAVE_MSGHDR_MSG_CONTROL
1242 hdr.msg_control = ans_data;
1243 hdr.msg_controllen = sizeof(ans_data);
1244 #endif
1246 ret = recvmsg(sock, &hdr, 0);
1247 #ifdef SO_TIMESTAMP
1248 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1249 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1250 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv));
1251 break;
1255 if (!chdr)
1256 #endif // SO_TIMESTAMP
1257 gettimeofday(tv, &tz);
1258 return (ret);
1261 static void finish(int sig) {
1262 u_int i = 0;
1263 unsigned char pl;
1264 double rta;
1265 struct rta_host *host;
1266 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1267 int hosts_ok = 0;
1268 int hosts_warn = 0;
1269 int this_status;
1270 double R;
1272 alarm(0);
1273 if (debug > 1)
1274 printf("finish(%d) called\n", sig);
1276 if (icmp_sock != -1)
1277 close(icmp_sock);
1278 if (udp_sock != -1)
1279 close(udp_sock);
1280 if (tcp_sock != -1)
1281 close(tcp_sock);
1283 if (debug) {
1284 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost);
1285 printf("targets: %u targets_alive: %u\n", targets, targets_alive);
1288 /* iterate thrice to calculate values, give output, and print perfparse */
1289 status = STATE_OK;
1290 host = list;
1292 while (host) {
1293 this_status = STATE_OK;
1295 if (!host->icmp_recv) {
1296 /* rta 0 is ofcourse not entirely correct, but will still show up
1297 * conspicuously as missing entries in perfparse and cacti */
1298 pl = 100;
1299 rta = 0;
1300 status = STATE_CRITICAL;
1301 /* up the down counter if not already counted */
1302 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive)
1303 targets_down++;
1304 } else {
1305 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1306 rta = (double)host->time_waited / host->icmp_recv;
1309 if (host->icmp_recv > 1) {
1311 * This algorithm is probably pretty much blindly copied from
1312 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1313 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1314 * According to some quick research MOS originates from the Audio/Video transport network area.
1315 * Whether it can and should be computed from ICMP data, I can not say.
1317 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1319 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1321 * More links:
1322 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1324 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1327 * Take the average round trip latency (in milliseconds), add
1328 * round trip jitter, but double the impact to latency
1329 * then add 10 for protocol latencies (in milliseconds).
1331 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1333 if (host->EffectiveLatency < 160) {
1334 R = 93.2 - (host->EffectiveLatency / 40);
1335 } else {
1336 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1339 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1340 // loss of 5% will be entered as 5).
1341 R = R - (pl * 2.5);
1343 if (R < 0) {
1344 R = 0;
1347 host->score = R;
1348 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1349 } else {
1350 host->jitter = 0;
1351 host->jitter_min = 0;
1352 host->jitter_max = 0;
1353 host->mos = 0;
1356 host->pl = pl;
1357 host->rta = rta;
1359 /* if no new mode selected, use old schema */
1360 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1361 rta_mode = true;
1362 pl_mode = true;
1365 /* Check which mode is on and do the warn / Crit stuff */
1366 if (rta_mode) {
1367 if (rta >= crit.rta) {
1368 this_status = STATE_CRITICAL;
1369 status = STATE_CRITICAL;
1370 host->rta_status = STATE_CRITICAL;
1371 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1372 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1373 status = STATE_WARNING;
1374 host->rta_status = STATE_WARNING;
1378 if (pl_mode) {
1379 if (pl >= crit.pl) {
1380 this_status = STATE_CRITICAL;
1381 status = STATE_CRITICAL;
1382 host->pl_status = STATE_CRITICAL;
1383 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1384 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1385 status = STATE_WARNING;
1386 host->pl_status = STATE_WARNING;
1390 if (jitter_mode) {
1391 if (host->jitter >= crit.jitter) {
1392 this_status = STATE_CRITICAL;
1393 status = STATE_CRITICAL;
1394 host->jitter_status = STATE_CRITICAL;
1395 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1396 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1397 status = STATE_WARNING;
1398 host->jitter_status = STATE_WARNING;
1402 if (mos_mode) {
1403 if (host->mos <= crit.mos) {
1404 this_status = STATE_CRITICAL;
1405 status = STATE_CRITICAL;
1406 host->mos_status = STATE_CRITICAL;
1407 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1408 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1409 status = STATE_WARNING;
1410 host->mos_status = STATE_WARNING;
1414 if (score_mode) {
1415 if (host->score <= crit.score) {
1416 this_status = STATE_CRITICAL;
1417 status = STATE_CRITICAL;
1418 host->score_status = STATE_CRITICAL;
1419 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1420 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1421 status = STATE_WARNING;
1422 host->score_status = STATE_WARNING;
1426 if (this_status == STATE_WARNING) {
1427 hosts_warn++;
1428 } else if (this_status == STATE_OK) {
1429 hosts_ok++;
1432 host = host->next;
1435 /* this is inevitable */
1436 if (!targets_alive)
1437 status = STATE_CRITICAL;
1438 if (min_hosts_alive > -1) {
1439 if (hosts_ok >= min_hosts_alive)
1440 status = STATE_OK;
1441 else if ((hosts_ok + hosts_warn) >= min_hosts_alive)
1442 status = STATE_WARNING;
1444 printf("%s - ", status_string[status]);
1446 host = list;
1447 while (host) {
1449 if (debug)
1450 puts("");
1451 if (i) {
1452 if (i < targets)
1453 printf(" :: ");
1454 else
1455 printf("\n");
1457 i++;
1458 if (!host->icmp_recv) {
1459 status = STATE_CRITICAL;
1460 host->rtmin = 0;
1461 host->jitter_min = 0;
1462 if (host->flags & FLAG_LOST_CAUSE) {
1463 char address[INET6_ADDRSTRLEN];
1464 parse_address(&host->error_addr, address, sizeof(address));
1465 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1466 } else { /* not marked as lost cause, so we have no flags for it */
1467 printf("%s: rta nan, lost 100%%", host->name);
1469 } else { /* !icmp_recv */
1470 printf("%s", host->name);
1471 /* rta text output */
1472 if (rta_mode) {
1473 if (status == STATE_OK)
1474 printf(" rta %0.3fms", host->rta / 1000);
1475 else if (status == STATE_WARNING && host->rta_status == status)
1476 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1477 else if (status == STATE_CRITICAL && host->rta_status == status)
1478 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1480 /* pl text output */
1481 if (pl_mode) {
1482 if (status == STATE_OK)
1483 printf(" lost %u%%", host->pl);
1484 else if (status == STATE_WARNING && host->pl_status == status)
1485 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1486 else if (status == STATE_CRITICAL && host->pl_status == status)
1487 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1489 /* jitter text output */
1490 if (jitter_mode) {
1491 if (status == STATE_OK)
1492 printf(" jitter %0.3fms", (float)host->jitter);
1493 else if (status == STATE_WARNING && host->jitter_status == status)
1494 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1495 else if (status == STATE_CRITICAL && host->jitter_status == status)
1496 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1498 /* mos text output */
1499 if (mos_mode) {
1500 if (status == STATE_OK)
1501 printf(" MOS %0.1f", (float)host->mos);
1502 else if (status == STATE_WARNING && host->mos_status == status)
1503 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1504 else if (status == STATE_CRITICAL && host->mos_status == status)
1505 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1507 /* score text output */
1508 if (score_mode) {
1509 if (status == STATE_OK)
1510 printf(" Score %u", (int)host->score);
1511 else if (status == STATE_WARNING && host->score_status == status)
1512 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1513 else if (status == STATE_CRITICAL && host->score_status == status)
1514 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1516 /* order statis text output */
1517 if (order_mode) {
1518 if (status == STATE_OK)
1519 printf(" Packets in order");
1520 else if (status == STATE_CRITICAL && host->order_status == status)
1521 printf(" Packets out of order");
1524 host = host->next;
1527 /* iterate once more for pretty perfparse output */
1528 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1529 printf("|");
1531 i = 0;
1532 host = list;
1533 while (host) {
1534 if (debug)
1535 puts("");
1537 if (rta_mode) {
1538 if (host->pl < 100) {
1539 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "", host->rta / 1000, (float)warn.rta / 1000,
1540 (float)crit.rta / 1000, (targets > 1) ? host->name : "", (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1541 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1542 } else {
1543 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1547 if (pl_mode) {
1548 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1551 if (jitter_mode) {
1552 if (host->pl < 100) {
1553 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ", (targets > 1) ? host->name : "", (float)host->jitter,
1554 (float)warn.jitter, (float)crit.jitter, (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1555 (float)host->jitter_min / 1000);
1556 } else {
1557 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1558 (targets > 1) ? host->name : "");
1562 if (mos_mode) {
1563 if (host->pl < 100) {
1564 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1565 } else {
1566 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1570 if (score_mode) {
1571 if (host->pl < 100) {
1572 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1573 } else {
1574 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1578 host = host->next;
1581 if (min_hosts_alive > -1) {
1582 if (hosts_ok >= min_hosts_alive)
1583 status = STATE_OK;
1584 else if ((hosts_ok + hosts_warn) >= min_hosts_alive)
1585 status = STATE_WARNING;
1588 /* finish with an empty line */
1589 puts("");
1590 if (debug)
1591 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, hosts_warn, min_hosts_alive);
1593 exit(status);
1596 static u_int get_timevaldiff(struct timeval *early, struct timeval *later) {
1597 u_int ret;
1598 struct timeval now;
1600 if (!later) {
1601 gettimeofday(&now, &tz);
1602 later = &now;
1604 if (!early)
1605 early = &prog_start;
1607 /* if early > later we return 0 so as to indicate a timeout */
1608 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) {
1609 return 0;
1611 ret = (later->tv_sec - early->tv_sec) * 1000000;
1612 ret += later->tv_usec - early->tv_usec;
1614 return ret;
1617 static int add_target_ip(char *arg, struct sockaddr_storage *in) {
1618 struct rta_host *host;
1619 struct sockaddr_in *sin, *host_sin;
1620 struct sockaddr_in6 *sin6, *host_sin6;
1622 if (address_family == AF_INET)
1623 sin = (struct sockaddr_in *)in;
1624 else
1625 sin6 = (struct sockaddr_in6 *)in;
1627 /* disregard obviously stupid addresses
1628 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1629 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1630 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1631 return -1;
1634 /* no point in adding two identical IP's, so don't. ;) */
1635 host = list;
1636 while (host) {
1637 host_sin = (struct sockaddr_in *)&host->saddr_in;
1638 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1640 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1641 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1642 if (debug)
1643 printf("Identical IP already exists. Not adding %s\n", arg);
1644 return -1;
1646 host = host->next;
1649 /* add the fresh ip */
1650 host = (struct rta_host *)malloc(sizeof(struct rta_host));
1651 if (!host) {
1652 char straddr[INET6_ADDRSTRLEN];
1653 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1654 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1656 memset(host, 0, sizeof(struct rta_host));
1658 /* set the values. use calling name for output */
1659 host->name = strdup(arg);
1661 /* fill out the sockaddr_storage struct */
1662 if (address_family == AF_INET) {
1663 host_sin = (struct sockaddr_in *)&host->saddr_in;
1664 host_sin->sin_family = AF_INET;
1665 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1666 } else {
1667 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1668 host_sin6->sin6_family = AF_INET6;
1669 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1672 /* fill out the sockaddr_in struct */
1673 host->rtmin = INFINITY;
1674 host->rtmax = 0;
1675 host->jitter = 0;
1676 host->jitter_max = 0;
1677 host->jitter_min = INFINITY;
1678 host->last_tdiff = 0;
1679 host->order_status = STATE_OK;
1680 host->last_icmp_seq = 0;
1681 host->rta_status = 0;
1682 host->pl_status = 0;
1683 host->jitter_status = 0;
1684 host->mos_status = 0;
1685 host->score_status = 0;
1686 host->pl_status = 0;
1688 if (!list)
1689 list = cursor = host;
1690 else
1691 cursor->next = host;
1693 cursor = host;
1694 targets++;
1696 return 0;
1699 /* wrapper for add_target_ip */
1700 static int add_target(char *arg) {
1701 int error, result = -1;
1702 struct sockaddr_storage ip;
1703 struct addrinfo hints, *res, *p;
1704 struct sockaddr_in *sin;
1705 struct sockaddr_in6 *sin6;
1707 switch (address_family) {
1708 case -1:
1709 /* -4 and -6 are not specified on cmdline */
1710 address_family = AF_INET;
1711 sin = (struct sockaddr_in *)&ip;
1712 result = inet_pton(address_family, arg, &sin->sin_addr);
1713 #ifdef USE_IPV6
1714 if (result != 1) {
1715 address_family = AF_INET6;
1716 sin6 = (struct sockaddr_in6 *)&ip;
1717 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1719 #endif
1720 /* If we don't find any valid addresses, we still don't know the address_family */
1721 if (result != 1) {
1722 address_family = -1;
1724 break;
1725 case AF_INET:
1726 sin = (struct sockaddr_in *)&ip;
1727 result = inet_pton(address_family, arg, &sin->sin_addr);
1728 break;
1729 case AF_INET6:
1730 sin6 = (struct sockaddr_in6 *)&ip;
1731 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1732 break;
1733 default:
1734 crash("Address family not supported");
1737 /* don't resolve if we don't have to */
1738 if (result == 1) {
1739 /* don't add all ip's if we were given a specific one */
1740 return add_target_ip(arg, &ip);
1741 } else {
1742 errno = 0;
1743 memset(&hints, 0, sizeof(hints));
1744 if (address_family == -1) {
1745 hints.ai_family = AF_UNSPEC;
1746 } else {
1747 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1749 hints.ai_socktype = SOCK_RAW;
1750 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1751 errno = 0;
1752 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1753 return -1;
1755 address_family = res->ai_family;
1758 /* possibly add all the IP's as targets */
1759 for (p = res; p != NULL; p = p->ai_next) {
1760 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1761 add_target_ip(arg, &ip);
1763 /* this is silly, but it works */
1764 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
1765 if (debug > 2)
1766 printf("mode: %d\n", mode);
1767 continue;
1769 break;
1771 freeaddrinfo(res);
1773 return 0;
1776 static void set_source_ip(char *arg) {
1777 struct sockaddr_in src;
1779 memset(&src, 0, sizeof(src));
1780 src.sin_family = address_family;
1781 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1782 src.sin_addr.s_addr = get_ip_address(arg);
1783 if (bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
1784 crash("Cannot bind to IP address %s", arg);
1787 /* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1788 static in_addr_t get_ip_address(const char *ifname) {
1789 // TODO: Rewrite this so the function return an error and we exit somewhere else
1790 struct sockaddr_in ip;
1791 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1792 #if defined(SIOCGIFADDR)
1793 struct ifreq ifr;
1795 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
1797 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
1799 if (ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1)
1800 crash("Cannot determine IP address of interface %s", ifname);
1802 memcpy(&ip, &ifr.ifr_addr, sizeof(ip));
1803 #else
1804 (void)ifname;
1805 errno = 0;
1806 crash("Cannot get interface IP address on this platform.");
1807 #endif
1808 return ip.sin_addr.s_addr;
1812 * u = micro
1813 * m = milli
1814 * s = seconds
1815 * return value is in microseconds
1817 static u_int get_timevar(const char *str) {
1818 char p, u, *ptr;
1819 size_t len;
1820 u_int i, d; /* integer and decimal, respectively */
1821 u_int factor = 1000; /* default to milliseconds */
1823 if (!str)
1824 return 0;
1825 len = strlen(str);
1826 if (!len)
1827 return 0;
1829 /* unit might be given as ms|m (millisec),
1830 * us|u (microsec) or just plain s, for seconds */
1831 p = '\0';
1832 u = str[len - 1];
1833 if (len >= 2 && !isdigit((int)str[len - 2]))
1834 p = str[len - 2];
1835 if (p && u == 's')
1836 u = p;
1837 else if (!p)
1838 p = u;
1839 if (debug > 2)
1840 printf("evaluating %s, u: %c, p: %c\n", str, u, p);
1842 if (u == 'u')
1843 factor = 1; /* microseconds */
1844 else if (u == 'm')
1845 factor = 1000; /* milliseconds */
1846 else if (u == 's')
1847 factor = 1000000; /* seconds */
1848 if (debug > 2)
1849 printf("factor is %u\n", factor);
1851 i = strtoul(str, &ptr, 0);
1852 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
1853 return i * factor;
1855 /* time specified in usecs can't have decimal points, so ignore them */
1856 if (factor == 1)
1857 return i;
1859 d = strtoul(ptr + 1, NULL, 0);
1861 /* d is decimal, so get rid of excess digits */
1862 while (d >= factor)
1863 d /= 10;
1865 /* the last parenthesis avoids floating point exceptions. */
1866 return ((i * factor) + (d * (factor / 10)));
1869 /* not too good at checking errors, but it'll do (main() should barfe on -1) */
1870 static int get_threshold(char *str, threshold *th) {
1871 char *p = NULL, i = 0;
1873 if (!str || !strlen(str) || !th)
1874 return -1;
1876 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1877 p = &str[strlen(str) - 1];
1878 while (p != &str[1]) {
1879 if (*p == '%')
1880 *p = '\0';
1881 else if (*p == ',' && i) {
1882 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1883 th->pl = (unsigned char)strtoul(p + 1, NULL, 0);
1884 break;
1886 i = 1;
1887 p--;
1889 th->rta = get_timevar(str);
1891 if (!th->rta)
1892 return -1;
1894 if (th->rta > MAXTTL * 1000000)
1895 th->rta = MAXTTL * 1000000;
1896 if (th->pl > 100)
1897 th->pl = 100;
1899 return 0;
1903 * This functions receives a pointer to a string which should contain a threshold for the
1904 * rta, packet_loss, jitter, mos or score mode in the form number,number[m|%]* assigns the
1905 * parsed number to the corresponding threshold variable.
1906 * @param[in,out] str String containing the given threshold values
1907 * @param[in] length strlen(str)
1908 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
1909 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
1910 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively)
1912 static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) {
1913 if (!str || !length || !warn || !crit)
1914 return false;
1916 // p points to the last char in str
1917 char *p = &str[length - 1];
1919 // first_iteration is bof-stop on stupid libc's
1920 bool first_iteration = true;
1922 while (p != &str[0]) {
1923 if ((*p == 'm') || (*p == '%')) {
1924 *p = '\0';
1925 } else if (*p == ',' && !first_iteration) {
1926 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1928 char *start_of_value = p + 1;
1930 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) {
1931 return false;
1934 first_iteration = false;
1935 p--;
1938 return parse_threshold2_helper(p, strlen(p), warn, mode);
1941 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) {
1942 char *resultChecker = {0};
1944 switch (mode) {
1945 case const_rta_mode:
1946 thr->rta = strtod(s, &resultChecker) * 1000;
1947 break;
1948 case const_packet_loss_mode:
1949 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0);
1950 break;
1951 case const_jitter_mode:
1952 thr->jitter = strtod(s, &resultChecker);
1954 break;
1955 case const_mos_mode:
1956 thr->mos = strtod(s, &resultChecker);
1957 break;
1958 case const_score_mode:
1959 thr->score = strtod(s, &resultChecker);
1960 break;
1963 if (resultChecker == s) {
1964 // Failed to parse
1965 return false;
1968 if (resultChecker != (s + length)) {
1969 // Trailing symbols
1970 return false;
1973 return true;
1976 unsigned short icmp_checksum(uint16_t *p, size_t n) {
1977 unsigned short cksum;
1978 long sum = 0;
1980 /* sizeof(uint16_t) == 2 */
1981 while (n >= 2) {
1982 sum += *(p++);
1983 n -= 2;
1986 /* mop up the occasional odd byte */
1987 if (n == 1)
1988 sum += *((uint8_t *)p - 1);
1990 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1991 sum += (sum >> 16); /* add carry */
1992 cksum = ~sum; /* ones-complement, trunc to 16 bits */
1994 return cksum;
1997 void print_help(void) {
1998 /*print_revision (progname);*/ /* FIXME: Why? */
1999 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2001 printf(COPYRIGHT, copyright, email);
2003 printf("\n\n");
2005 print_usage();
2007 printf(UT_HELP_VRSN);
2008 printf(UT_EXTRA_OPTS);
2010 printf(" %s\n", "-H");
2011 printf(" %s\n", _("specify a target"));
2012 printf(" %s\n", "[-4|-6]");
2013 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
2014 printf(" %s\n", "-w");
2015 printf(" %s", _("warning threshold (currently "));
2016 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);
2017 printf(" %s\n", "-c");
2018 printf(" %s", _("critical threshold (currently "));
2019 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl);
2021 printf(" %s\n", "-R");
2022 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms"));
2023 printf(" %s\n", "-P");
2024 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2025 printf(" %s\n", "-J");
2026 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2027 printf(" %s\n", "-M");
2028 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2029 printf(" %s\n", "-S");
2030 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2031 printf(" %s\n", "-O");
2032 printf(" %s\n", _("detect out of order ICMP packts "));
2033 printf(" %s\n", "-H");
2034 printf(" %s\n", _("specify a target"));
2035 printf(" %s\n", "-s");
2036 printf(" %s\n", _("specify a source IP address or device name"));
2037 printf(" %s\n", "-n");
2038 printf(" %s", _("number of packets to send (currently "));
2039 printf("%u)\n", packets);
2040 printf(" %s\n", "-p");
2041 printf(" %s", _("number of packets to send (currently "));
2042 printf("%u)\n", packets);
2043 printf(" %s\n", "-i");
2044 printf(" %s", _("max packet interval (currently "));
2045 printf("%0.3fms)\n", (float)pkt_interval / 1000);
2046 printf(" %s\n", "-I");
2047 printf(" %s", _("max target interval (currently "));
2048 printf("%0.3fms)\n", (float)target_interval / 1000);
2049 printf(" %s\n", "-m");
2050 printf(" %s", _("number of alive hosts required for success"));
2051 printf("\n");
2052 printf(" %s\n", "-l");
2053 printf(" %s", _("TTL on outgoing packets (currently "));
2054 printf("%u)\n", ttl);
2055 printf(" %s\n", "-t");
2056 printf(" %s", _("timeout value (seconds, currently "));
2057 printf("%u)\n", timeout);
2058 printf(" %s\n", "-b");
2059 printf(" %s\n", _("Number of icmp data bytes to send"));
2060 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN);
2061 printf(" %s\n", "-v");
2062 printf(" %s\n", _("verbose"));
2063 printf("\n");
2064 printf("%s\n", _("Notes:"));
2065 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2066 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not."));
2067 printf("\n");
2068 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2069 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2070 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations"));
2071 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2072 /* -d not yet implemented */
2073 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops"));
2074 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2075 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2076 printf("\n");
2077 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2078 /* printf ("%s\n", _("Long options are currently unsupported."));
2079 printf ("%s\n", _("Options marked with * require an argument"));
2082 printf(UT_SUPPORT);
2085 void print_usage(void) {
2086 printf("%s\n", _("Usage:"));
2087 printf(" %s [options] [-H] host1 host2 hostN\n", progname);