Merge pull request #2046 from RincewindsHat/fix/format_string
[monitoring-plugins.git] / plugins-root / check_icmp.c
blob960ab8f1b1c5eba429d6e35e4b377fc8fa129f33
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 {
179 const_rta_mode,
180 const_packet_loss_mode,
181 const_jitter_mode,
182 const_mos_mode,
183 const_score_mode
186 typedef enum enum_threshold_mode threshold_mode;
188 /* the different ping types we can do
189 * TODO: investigate ARP ping as well */
190 #define HAVE_ICMP 1
191 #define HAVE_UDP 2
192 #define HAVE_TCP 4
193 #define HAVE_ARP 8
195 #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
196 #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
197 #define IP_HDR_SIZE 20
198 #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
199 #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
201 /* various target states */
202 #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
203 #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
204 #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
205 #define TSTATE_UNREACH 0x08
207 /** prototypes **/
208 void print_help(void);
209 void print_usage(void);
210 static u_int get_timevar(const char *);
211 static u_int get_timevaldiff(struct timeval *, struct timeval *);
212 static in_addr_t get_ip_address(const char *);
213 static int wait_for_reply(int, u_int);
214 static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *);
215 static int send_icmp_ping(int, struct rta_host *);
216 static int get_threshold(char *str, threshold *th);
217 static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode);
218 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode);
219 static void run_checks(void);
220 static void set_source_ip(char *);
221 static int add_target(char *);
222 static int add_target_ip(char *, struct sockaddr_storage *);
223 static int handle_random_icmp(unsigned char *, struct sockaddr_storage *);
224 static void parse_address(struct sockaddr_storage *, char *, int);
225 static unsigned short icmp_checksum(uint16_t *, size_t);
226 static void finish(int);
227 static void crash(const char *, ...);
229 /** external **/
230 extern int optind;
231 extern char *optarg;
232 extern char **environ;
234 /** global variables **/
235 static struct rta_host **table, *cursor, *list;
237 static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0};
238 static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0};
240 static int mode, protocols, sockets, debug = 0, timeout = 10;
241 static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE;
242 static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN;
244 static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0;
245 #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
246 static unsigned short targets_down = 0, targets = 0, packets = 0;
247 #define targets_alive (targets - targets_down)
248 static unsigned int retry_interval, pkt_interval, target_interval;
249 static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
250 static pid_t pid;
251 static struct timezone tz;
252 static struct timeval prog_start;
253 static unsigned long long max_completion_time = 0;
254 static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
255 static int min_hosts_alive = -1;
256 static float pkt_backoff_factor = 1.5;
257 static float target_backoff_factor = 1.5;
258 static bool rta_mode = false;
259 static bool pl_mode = false;
260 static bool jitter_mode = false;
261 static bool score_mode = false;
262 static bool mos_mode = false;
263 static bool order_mode = false;
265 /** code start **/
266 static void crash(const char *fmt, ...) {
267 va_list ap;
269 printf("%s: ", progname);
271 va_start(ap, fmt);
272 vprintf(fmt, ap);
273 va_end(ap);
275 if (errno)
276 printf(": %s", strerror(errno));
277 puts("");
279 exit(3);
282 static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code) {
283 const char *msg = "unreachable";
285 if (debug > 1)
286 printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
287 switch (icmp_type) {
288 case ICMP_UNREACH:
289 switch (icmp_code) {
290 case ICMP_UNREACH_NET:
291 msg = "Net unreachable";
292 break;
293 case ICMP_UNREACH_HOST:
294 msg = "Host unreachable";
295 break;
296 case ICMP_UNREACH_PROTOCOL:
297 msg = "Protocol unreachable (firewall?)";
298 break;
299 case ICMP_UNREACH_PORT:
300 msg = "Port unreachable (firewall?)";
301 break;
302 case ICMP_UNREACH_NEEDFRAG:
303 msg = "Fragmentation needed";
304 break;
305 case ICMP_UNREACH_SRCFAIL:
306 msg = "Source route failed";
307 break;
308 case ICMP_UNREACH_ISOLATED:
309 msg = "Source host isolated";
310 break;
311 case ICMP_UNREACH_NET_UNKNOWN:
312 msg = "Unknown network";
313 break;
314 case ICMP_UNREACH_HOST_UNKNOWN:
315 msg = "Unknown host";
316 break;
317 case ICMP_UNREACH_NET_PROHIB:
318 msg = "Network denied (firewall?)";
319 break;
320 case ICMP_UNREACH_HOST_PROHIB:
321 msg = "Host denied (firewall?)";
322 break;
323 case ICMP_UNREACH_TOSNET:
324 msg = "Bad TOS for network (firewall?)";
325 break;
326 case ICMP_UNREACH_TOSHOST:
327 msg = "Bad TOS for host (firewall?)";
328 break;
329 case ICMP_UNREACH_FILTER_PROHIB:
330 msg = "Prohibited by filter (firewall)";
331 break;
332 case ICMP_UNREACH_HOST_PRECEDENCE:
333 msg = "Host precedence violation";
334 break;
335 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
336 msg = "Precedence cutoff";
337 break;
338 default:
339 msg = "Invalid code";
340 break;
342 break;
344 case ICMP_TIMXCEED:
345 /* really 'out of reach', or non-existent host behind a router serving
346 * two different subnets */
347 switch (icmp_code) {
348 case ICMP_TIMXCEED_INTRANS:
349 msg = "Time to live exceeded in transit";
350 break;
351 case ICMP_TIMXCEED_REASS:
352 msg = "Fragment reassembly time exceeded";
353 break;
354 default:
355 msg = "Invalid code";
356 break;
358 break;
360 case ICMP_SOURCEQUENCH:
361 msg = "Transmitting too fast";
362 break;
363 case ICMP_REDIRECT:
364 msg = "Redirect (change route)";
365 break;
366 case ICMP_PARAMPROB:
367 msg = "Bad IP header (required option absent)";
368 break;
370 /* the following aren't error messages, so ignore */
371 case ICMP_TSTAMP:
372 case ICMP_TSTAMPREPLY:
373 case ICMP_IREQ:
374 case ICMP_IREQREPLY:
375 case ICMP_MASKREQ:
376 case ICMP_MASKREPLY:
377 default:
378 msg = "";
379 break;
382 return msg;
385 static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) {
386 struct icmp p, sent_icmp;
387 struct rta_host *host = NULL;
389 memcpy(&p, packet, sizeof(p));
390 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) {
391 /* echo request from us to us (pinging localhost) */
392 return 0;
395 if (debug)
396 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr);
398 /* only handle a few types, since others can't possibly be replies to
399 * us in a sane network (if it is anyway, it will be counted as lost
400 * at summary time, but not as quickly as a proper response */
401 /* TIMXCEED can be an unreach from a router with multiple IP's which
402 * serves two different subnets on the same interface and a dead host
403 * on one net is pinged from the other. The router will respond to
404 * itself and thus set TTL=0 so as to not loop forever. Even when
405 * TIMXCEED actually sends a proper icmp response we will have passed
406 * too many hops to have a hope of reaching it later, in which case it
407 * indicates overconfidence in the network, poor routing or both. */
408 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) {
409 return 0;
412 /* might be for us. At least it holds the original package (according
413 * to RFC 792). If it isn't, just ignore it */
414 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
415 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) {
416 if (debug)
417 printf("Packet is no response to a packet we sent\n");
418 return 0;
421 /* it is indeed a response for us */
422 host = table[ntohs(sent_icmp.icmp_seq) / packets];
423 if (debug) {
424 char address[INET6_ADDRSTRLEN];
425 parse_address(addr, address, sizeof(address));
426 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);
429 icmp_lost++;
430 host->icmp_lost++;
431 /* don't spend time on lost hosts any more */
432 if (host->flags & FLAG_LOST_CAUSE)
433 return 0;
435 /* source quench means we're sending too fast, so increase the
436 * interval and mark this packet lost */
437 if (p.icmp_type == ICMP_SOURCEQUENCH) {
438 pkt_interval *= pkt_backoff_factor;
439 target_interval *= target_backoff_factor;
440 } else {
441 targets_down++;
442 host->flags |= FLAG_LOST_CAUSE;
444 host->icmp_type = p.icmp_type;
445 host->icmp_code = p.icmp_code;
446 host->error_addr = *addr;
448 return 0;
451 void parse_address(struct sockaddr_storage *addr, char *address, int size) {
452 switch (address_family) {
453 case AF_INET:
454 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size);
455 break;
456 case AF_INET6:
457 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size);
458 break;
462 int main(int argc, char **argv) {
463 int i;
464 char *ptr;
465 long int arg;
466 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
467 int result;
468 struct rta_host *host;
469 #ifdef HAVE_SIGACTION
470 struct sigaction sig_action;
471 #endif
472 #ifdef SO_TIMESTAMP
473 int on = 1;
474 #endif
475 char *source_ip = NULL;
476 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
477 setlocale(LC_ALL, "");
478 bindtextdomain(PACKAGE, LOCALEDIR);
479 textdomain(PACKAGE);
481 /* we only need to be setsuid when we get the sockets, so do
482 * that before pointer magic (esp. on network data) */
483 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
485 address_family = -1;
486 int icmp_proto = IPPROTO_ICMP;
488 /* get calling name the old-fashioned way for portability instead
489 * of relying on the glibc-ism __progname */
490 ptr = strrchr(argv[0], '/');
491 if (ptr)
492 progname = &ptr[1];
493 else
494 progname = argv[0];
496 /* now set defaults. Use progname to set them initially (allows for
497 * superfast check_host program when target host is up */
498 cursor = list = NULL;
499 table = NULL;
501 mode = MODE_RTA;
502 /* Default critical thresholds */
503 crit.rta = 500000;
504 crit.pl = 80;
505 crit.jitter = 50;
506 crit.mos = 3;
507 crit.score = 70;
508 /* Default warning thresholds */
509 warn.rta = 200000;
510 warn.pl = 40;
511 warn.jitter = 40;
512 warn.mos = 3.5;
513 warn.score = 80;
515 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
516 pkt_interval = 80000; /* 80 msec packet interval by default */
517 packets = 5;
519 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
520 mode = MODE_ICMP;
521 protocols = HAVE_ICMP;
522 } else if (!strcmp(progname, "check_host")) {
523 mode = MODE_HOSTCHECK;
524 pkt_interval = 1000000;
525 packets = 5;
526 crit.rta = warn.rta = 1000000;
527 crit.pl = warn.pl = 100;
528 } else if (!strcmp(progname, "check_rta_multi")) {
529 mode = MODE_ALL;
530 target_interval = 0;
531 pkt_interval = 50000;
532 packets = 5;
535 /* support "--help" and "--version" */
536 if (argc == 2) {
537 if (!strcmp(argv[1], "--help"))
538 strcpy(argv[1], "-h");
539 if (!strcmp(argv[1], "--version"))
540 strcpy(argv[1], "-V");
543 /* Parse protocol arguments first */
544 for (i = 1; i < argc; i++) {
545 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
546 switch (arg) {
547 case '4':
548 if (address_family != -1)
549 crash("Multiple protocol versions not supported");
550 address_family = AF_INET;
551 break;
552 case '6':
553 #ifdef USE_IPV6
554 if (address_family != -1)
555 crash("Multiple protocol versions not supported");
556 address_family = AF_INET6;
557 #else
558 usage(_("IPv6 support not available\n"));
559 #endif
560 break;
565 /* Reset argument scanning */
566 optind = 1;
568 unsigned long size;
569 bool err;
570 /* parse the arguments */
571 for (i = 1; i < argc; i++) {
572 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
573 switch (arg) {
574 case 'v':
575 debug++;
576 break;
577 case 'b':
578 size = strtol(optarg, NULL, 0);
579 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
580 icmp_data_size = size;
581 icmp_pkt_size = size + ICMP_MINLEN;
582 } else
583 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data),
584 MAX_PING_DATA - 1);
585 break;
586 case 'i':
587 pkt_interval = get_timevar(optarg);
588 break;
589 case 'I':
590 target_interval = get_timevar(optarg);
591 break;
592 case 'w':
593 get_threshold(optarg, &warn);
594 break;
595 case 'c':
596 get_threshold(optarg, &crit);
597 break;
598 case 'n':
599 case 'p':
600 packets = strtoul(optarg, NULL, 0);
601 break;
602 case 't':
603 timeout = strtoul(optarg, NULL, 0);
604 if (!timeout)
605 timeout = 10;
606 break;
607 case 'H':
608 add_target(optarg);
609 break;
610 case 'l':
611 ttl = (int)strtoul(optarg, NULL, 0);
612 break;
613 case 'm':
614 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
615 break;
616 case 'd': /* implement later, for cluster checks */
617 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
618 if (ptr) {
619 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
621 break;
622 case 's': /* specify source IP address */
623 source_ip = optarg;
624 break;
625 case 'V': /* version */
626 print_revision(progname, NP_VERSION);
627 exit(STATE_UNKNOWN);
628 case 'h': /* help */
629 print_help();
630 exit(STATE_UNKNOWN);
631 break;
632 case 'R': /* RTA mode */
633 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
634 if (!err) {
635 crash("Failed to parse RTA threshold");
638 rta_mode = true;
639 break;
640 case 'P': /* packet loss mode */
641 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
642 if (!err) {
643 crash("Failed to parse packet loss threshold");
646 pl_mode = true;
647 break;
648 case 'J': /* jitter mode */
649 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode);
650 if (!err) {
651 crash("Failed to parse jitter threshold");
654 jitter_mode = true;
655 break;
656 case 'M': /* MOS mode */
657 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode);
658 if (!err) {
659 crash("Failed to parse MOS threshold");
662 mos_mode = true;
663 break;
664 case 'S': /* score mode */
665 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode);
666 if (!err) {
667 crash("Failed to parse score threshold");
670 score_mode = true;
671 break;
672 case 'O': /* out of order mode */
673 order_mode = true;
674 break;
679 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
680 environ = NULL;
682 /* use the pid to mark packets as ours */
683 /* Some systems have 32-bit pid_t so mask off only 16 bits */
684 pid = getpid() & 0xffff;
685 /* printf("pid = %u\n", pid); */
687 /* Parse extra opts if any */
688 argv = np_extra_opts(&argc, argv, progname);
690 argv = &argv[optind];
691 while (*argv) {
692 add_target(*argv);
693 argv++;
696 if (!targets) {
697 errno = 0;
698 crash("No hosts to check");
701 // add_target might change address_family
702 switch (address_family) {
703 case AF_INET:
704 icmp_proto = IPPROTO_ICMP;
705 break;
706 case AF_INET6:
707 icmp_proto = IPPROTO_ICMPV6;
708 break;
709 default:
710 crash("Address family not supported");
712 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1)
713 sockets |= HAVE_ICMP;
714 else
715 icmp_sockerrno = errno;
717 if (source_ip)
718 set_source_ip(source_ip);
720 #ifdef SO_TIMESTAMP
721 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
722 if (debug)
723 printf("Warning: no SO_TIMESTAMP support\n");
724 #endif // SO_TIMESTAMP
726 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
727 if (setuid(getuid()) == -1) {
728 printf("ERROR: Failed to drop privileges\n");
729 return 1;
732 if (!sockets) {
733 if (icmp_sock == -1) {
734 errno = icmp_sockerrno;
735 crash("Failed to obtain ICMP socket");
736 return -1;
738 /* if(udp_sock == -1) { */
739 /* errno = icmp_sockerrno; */
740 /* crash("Failed to obtain UDP socket"); */
741 /* return -1; */
742 /* } */
743 /* if(tcp_sock == -1) { */
744 /* errno = icmp_sockerrno; */
745 /* crash("Failed to obtain TCP socker"); */
746 /* return -1; */
747 /* } */
749 if (!ttl)
750 ttl = 64;
752 if (icmp_sock) {
753 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
754 if (debug) {
755 if (result == -1)
756 printf("setsockopt failed\n");
757 else
758 printf("ttl set to %u\n", ttl);
762 /* stupid users should be able to give whatever thresholds they want
763 * (nothing will break if they do), but some anal plugin maintainer
764 * will probably add some printf() thing here later, so it might be
765 * best to at least show them where to do it. ;) */
766 if (warn.pl > crit.pl)
767 warn.pl = crit.pl;
768 if (warn.rta > crit.rta)
769 warn.rta = crit.rta;
770 if (warn_down > crit_down)
771 crit_down = warn_down;
772 if (warn.jitter > crit.jitter)
773 crit.jitter = warn.jitter;
774 if (warn.mos < crit.mos)
775 warn.mos = crit.mos;
776 if (warn.score < crit.score)
777 warn.score = crit.score;
779 #ifdef HAVE_SIGACTION
780 sig_action.sa_sigaction = NULL;
781 sig_action.sa_handler = finish;
782 sigfillset(&sig_action.sa_mask);
783 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
784 sigaction(SIGINT, &sig_action, NULL);
785 sigaction(SIGHUP, &sig_action, NULL);
786 sigaction(SIGTERM, &sig_action, NULL);
787 sigaction(SIGALRM, &sig_action, NULL);
788 #else /* HAVE_SIGACTION */
789 signal(SIGINT, finish);
790 signal(SIGHUP, finish);
791 signal(SIGTERM, finish);
792 signal(SIGALRM, finish);
793 #endif /* HAVE_SIGACTION */
794 if (debug)
795 printf("Setting alarm timeout to %u seconds\n", timeout);
796 alarm(timeout);
798 /* make sure we don't wait any longer than necessary */
799 gettimeofday(&prog_start, &tz);
800 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta;
802 if (debug) {
803 printf("packets: %u, targets: %u\n"
804 "target_interval: %0.3f, pkt_interval %0.3f\n"
805 "crit.rta: %0.3f\n"
806 "max_completion_time: %0.3f\n",
807 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000,
808 (float)max_completion_time / 1000);
811 if (debug) {
812 if (max_completion_time > (u_int)timeout * 1000000) {
813 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout);
814 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1);
818 if (debug) {
819 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl);
820 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval);
821 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout);
824 if (packets > 20) {
825 errno = 0;
826 crash("packets is > 20 (%d)", packets);
829 if (min_hosts_alive < -1) {
830 errno = 0;
831 crash("minimum alive hosts is negative (%i)", min_hosts_alive);
834 host = list;
835 table = malloc(sizeof(struct rta_host *) * targets);
836 if (!table) {
837 crash("main(): malloc failed for host table");
840 i = 0;
841 while (host) {
842 host->id = i * packets;
843 table[i] = host;
844 host = host->next;
845 i++;
848 run_checks();
850 errno = 0;
851 finish(0);
853 return (0);
856 static void run_checks(void) {
857 u_int i, t;
858 u_int final_wait, time_passed;
860 /* this loop might actually violate the pkt_interval or target_interval
861 * settings, but only if there aren't any packets on the wire which
862 * indicates that the target can handle an increased packet rate */
863 for (i = 0; i < packets; i++) {
864 for (t = 0; t < targets; t++) {
865 /* don't send useless packets */
866 if (!targets_alive)
867 finish(0);
868 if (table[t]->flags & FLAG_LOST_CAUSE) {
869 if (debug)
870 printf("%s is a lost cause. not sending any more\n", table[t]->name);
871 continue;
874 /* we're still in the game, so send next packet */
875 (void)send_icmp_ping(icmp_sock, table[t]);
876 wait_for_reply(icmp_sock, target_interval);
878 wait_for_reply(icmp_sock, pkt_interval * targets);
881 if (icmp_pkts_en_route && targets_alive) {
882 time_passed = get_timevaldiff(NULL, NULL);
883 final_wait = max_completion_time - time_passed;
885 if (debug) {
886 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time);
888 if (time_passed > max_completion_time) {
889 if (debug)
890 printf("Time passed. Finishing up\n");
891 finish(0);
894 /* catch the packets that might come in within the timeframe, but
895 * haven't yet */
896 if (debug)
897 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000);
898 wait_for_reply(icmp_sock, final_wait);
902 /* response structure:
903 * IPv4:
904 * ip header : 20 bytes
905 * icmp header : 28 bytes
906 * IPv6:
907 * ip header : 40 bytes
908 * icmp header : 28 bytes
909 * both:
910 * icmp echo reply : the rest
912 static int wait_for_reply(int sock, u_int t) {
913 int n, hlen;
914 static unsigned char buf[65536];
915 struct sockaddr_storage resp_addr;
916 union ip_hdr *ip;
917 union icmp_packet packet;
918 struct rta_host *host;
919 struct icmp_ping_data data;
920 struct timeval wait_start, now;
921 u_int tdiff, i, per_pkt_wait;
922 double jitter_tmp;
924 if (!(packet.buf = malloc(icmp_pkt_size))) {
925 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
926 return -1; /* might be reached if we're in debug mode */
929 memset(packet.buf, 0, icmp_pkt_size);
931 /* if we can't listen or don't have anything to listen to, just return */
932 if (!t || !icmp_pkts_en_route) {
933 free(packet.buf);
934 return 0;
937 gettimeofday(&wait_start, &tz);
939 i = t;
940 per_pkt_wait = t / icmp_pkts_en_route;
941 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
942 t = per_pkt_wait;
944 /* wrap up if all targets are declared dead */
945 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) {
946 finish(0);
949 /* reap responses until we hit a timeout */
950 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now);
951 if (!n) {
952 if (debug > 1) {
953 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait);
955 continue; /* timeout for this one, so keep trying */
957 if (n < 0) {
958 if (debug)
959 printf("recvfrom_wto() returned errors\n");
960 free(packet.buf);
961 return n;
964 // FIXME: with ipv6 we don't have an ip header here
965 if (address_family != AF_INET6) {
966 ip = (union ip_hdr *)buf;
968 if (debug > 1) {
969 char address[INET6_ADDRSTRLEN];
970 parse_address(&resp_addr, address, sizeof(address));
971 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address);
975 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
976 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
977 /* alpha headers are decidedly broken. Using an ansi compiler,
978 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
979 * off the bottom 4 bits */
980 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
981 /* #else */
982 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
983 /* #endif */
985 if (n < (hlen + ICMP_MINLEN)) {
986 char address[INET6_ADDRSTRLEN];
987 parse_address(&resp_addr, address, sizeof(address));
988 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address);
990 /* else if(debug) { */
991 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
992 /* hlen, ntohs(ip->ip_len) - hlen, */
993 /* sizeof(struct ip), icmp_pkt_size); */
994 /* } */
996 /* check the response */
998 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
999 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1000 : sizeof(struct icmp));*/
1002 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY ||
1003 ntohs(packet.icp->icmp_seq) >= targets * packets)) ||
1004 (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1005 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) {
1006 if (debug > 2)
1007 printf("not a proper ICMP_ECHOREPLY\n");
1008 handle_random_icmp(buf + hlen, &resp_addr);
1009 continue;
1012 /* this is indeed a valid response */
1013 if (address_family == PF_INET) {
1014 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1015 if (debug > 2)
1016 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id),
1017 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum);
1018 host = table[ntohs(packet.icp->icmp_seq) / packets];
1019 } else {
1020 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1021 if (debug > 2)
1022 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id),
1023 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum);
1024 host = table[ntohs(packet.icp6->icmp6_seq) / packets];
1027 tdiff = get_timevaldiff(&data.stime, &now);
1029 if (host->last_tdiff > 0) {
1030 /* Calculate jitter */
1031 if (host->last_tdiff > tdiff) {
1032 jitter_tmp = host->last_tdiff - tdiff;
1033 } else {
1034 jitter_tmp = tdiff - host->last_tdiff;
1037 if (host->jitter == 0) {
1038 host->jitter = jitter_tmp;
1039 host->jitter_max = jitter_tmp;
1040 host->jitter_min = jitter_tmp;
1041 } else {
1042 host->jitter += jitter_tmp;
1044 if (jitter_tmp < host->jitter_min) {
1045 host->jitter_min = jitter_tmp;
1048 if (jitter_tmp > host->jitter_max) {
1049 host->jitter_max = jitter_tmp;
1053 /* Check if packets in order */
1054 if (host->last_icmp_seq >= packet.icp->icmp_seq)
1055 host->order_status = STATE_CRITICAL;
1057 host->last_tdiff = tdiff;
1059 host->last_icmp_seq = packet.icp->icmp_seq;
1061 host->time_waited += tdiff;
1062 host->icmp_recv++;
1063 icmp_recv++;
1064 if (tdiff > (unsigned int)host->rtmax)
1065 host->rtmax = tdiff;
1066 if (tdiff < (unsigned int)host->rtmin)
1067 host->rtmin = tdiff;
1069 if (debug) {
1070 char address[INET6_ADDRSTRLEN];
1071 parse_address(&resp_addr, address, sizeof(address));
1073 switch (address_family) {
1074 case AF_INET: {
1075 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address,
1076 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000);
1077 break;
1079 case AF_INET6: {
1080 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl,
1081 (float)host->rtmax / 1000, (float)host->rtmin / 1000);
1086 /* if we're in hostcheck mode, exit with limited printouts */
1087 if (mode == MODE_HOSTCHECK) {
1088 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1089 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1090 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000,
1091 (float)crit.rta / 1000);
1092 exit(STATE_OK);
1096 free(packet.buf);
1097 return 0;
1100 /* the ping functions */
1101 static int 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", icmp_pkt_size);
1119 return -1; /* might be reached if we're in debug mode */
1122 memset(buf, 0, icmp_pkt_size);
1124 if ((gettimeofday(&tv, &tz)) == -1) {
1125 free(buf);
1126 return -1;
1129 data.ping_id = 10; /* host->icmp.icmp_sent; */
1130 memcpy(&data.stime, &tv, sizeof(tv));
1132 if (address_family == AF_INET) {
1133 struct icmp *icp = (struct icmp *)buf;
1134 addrlen = sizeof(struct sockaddr_in);
1136 memcpy(&icp->icmp_data, &data, sizeof(data));
1138 icp->icmp_type = ICMP_ECHO;
1139 icp->icmp_code = 0;
1140 icp->icmp_cksum = 0;
1141 icp->icmp_id = htons(pid);
1142 icp->icmp_seq = htons(host->id++);
1143 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1145 if (debug > 2)
1146 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data),
1147 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name);
1148 } else {
1149 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1150 addrlen = sizeof(struct sockaddr_in6);
1152 memcpy(&icp6->icmp6_dataun.icmp6_un_data8[4], &data, sizeof(data));
1154 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1155 icp6->icmp6_code = 0;
1156 icp6->icmp6_cksum = 0;
1157 icp6->icmp6_id = htons(pid);
1158 icp6->icmp6_seq = htons(host->id++);
1159 // let checksum be calculated automatically
1161 if (debug > 2) {
1162 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data),
1163 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name);
1167 memset(&iov, 0, sizeof(iov));
1168 iov.iov_base = buf;
1169 iov.iov_len = icmp_pkt_size;
1171 memset(&hdr, 0, sizeof(hdr));
1172 hdr.msg_name = (struct sockaddr *)&host->saddr_in;
1173 hdr.msg_namelen = addrlen;
1174 hdr.msg_iov = &iov;
1175 hdr.msg_iovlen = 1;
1177 errno = 0;
1179 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1180 #ifdef MSG_CONFIRM
1181 len = sendmsg(sock, &hdr, MSG_CONFIRM);
1182 #else
1183 len = sendmsg(sock, &hdr, 0);
1184 #endif
1186 free(buf);
1188 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1189 if (debug) {
1190 char address[INET6_ADDRSTRLEN];
1191 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address));
1192 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1194 errno = 0;
1195 return -1;
1198 icmp_sent++;
1199 host->icmp_sent++;
1201 return 0;
1204 static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) {
1205 u_int slen;
1206 int n, ret;
1207 struct timeval to, then, now;
1208 fd_set rd, wr;
1209 #ifdef HAVE_MSGHDR_MSG_CONTROL
1210 char ans_data[4096];
1211 #endif // HAVE_MSGHDR_MSG_CONTROL
1212 struct msghdr hdr;
1213 struct iovec iov;
1214 #ifdef SO_TIMESTAMP
1215 struct cmsghdr *chdr;
1216 #endif
1218 if (!*timo) {
1219 if (debug)
1220 printf("*timo is not\n");
1221 return 0;
1224 to.tv_sec = *timo / 1000000;
1225 to.tv_usec = (*timo - (to.tv_sec * 1000000));
1227 FD_ZERO(&rd);
1228 FD_ZERO(&wr);
1229 FD_SET(sock, &rd);
1230 errno = 0;
1231 gettimeofday(&then, &tz);
1232 n = select(sock + 1, &rd, &wr, NULL, &to);
1233 if (n < 0)
1234 crash("select() in recvfrom_wto");
1235 gettimeofday(&now, &tz);
1236 *timo = get_timevaldiff(&then, &now);
1238 if (!n)
1239 return 0; /* timeout */
1241 slen = sizeof(struct sockaddr_storage);
1243 memset(&iov, 0, sizeof(iov));
1244 iov.iov_base = buf;
1245 iov.iov_len = len;
1247 memset(&hdr, 0, sizeof(hdr));
1248 hdr.msg_name = saddr;
1249 hdr.msg_namelen = slen;
1250 hdr.msg_iov = &iov;
1251 hdr.msg_iovlen = 1;
1252 #ifdef HAVE_MSGHDR_MSG_CONTROL
1253 hdr.msg_control = ans_data;
1254 hdr.msg_controllen = sizeof(ans_data);
1255 #endif
1257 ret = recvmsg(sock, &hdr, 0);
1258 #ifdef SO_TIMESTAMP
1259 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1260 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1261 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv));
1262 break;
1266 if (!chdr)
1267 #endif // SO_TIMESTAMP
1268 gettimeofday(tv, &tz);
1269 return (ret);
1272 static void finish(int sig) {
1273 u_int i = 0;
1274 unsigned char pl;
1275 double rta;
1276 struct rta_host *host;
1277 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1278 int hosts_ok = 0;
1279 int hosts_warn = 0;
1280 int this_status;
1281 double R;
1283 alarm(0);
1284 if (debug > 1)
1285 printf("finish(%d) called\n", sig);
1287 if (icmp_sock != -1)
1288 close(icmp_sock);
1289 if (udp_sock != -1)
1290 close(udp_sock);
1291 if (tcp_sock != -1)
1292 close(tcp_sock);
1294 if (debug) {
1295 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost);
1296 printf("targets: %u targets_alive: %u\n", targets, targets_alive);
1299 /* iterate thrice to calculate values, give output, and print perfparse */
1300 status = STATE_OK;
1301 host = list;
1303 while (host) {
1304 this_status = STATE_OK;
1306 if (!host->icmp_recv) {
1307 /* rta 0 is ofcourse not entirely correct, but will still show up
1308 * conspicuously as missing entries in perfparse and cacti */
1309 pl = 100;
1310 rta = 0;
1311 status = STATE_CRITICAL;
1312 /* up the down counter if not already counted */
1313 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive)
1314 targets_down++;
1315 } else {
1316 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1317 rta = (double)host->time_waited / host->icmp_recv;
1320 if (host->icmp_recv > 1) {
1322 * This algorithm is probably pretty much blindly copied from
1323 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1324 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1325 * According to some quick research MOS originates from the Audio/Video transport network area.
1326 * Whether it can and should be computed from ICMP data, I can not say.
1328 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1330 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1332 * More links:
1333 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1335 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1338 * Take the average round trip latency (in milliseconds), add
1339 * round trip jitter, but double the impact to latency
1340 * then add 10 for protocol latencies (in milliseconds).
1342 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1344 if (host->EffectiveLatency < 160) {
1345 R = 93.2 - (host->EffectiveLatency / 40);
1346 } else {
1347 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1350 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1351 // loss of 5% will be entered as 5).
1352 R = R - (pl * 2.5);
1354 if (R < 0) {
1355 R = 0;
1358 host->score = R;
1359 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1360 } else {
1361 host->jitter = 0;
1362 host->jitter_min = 0;
1363 host->jitter_max = 0;
1364 host->mos = 0;
1367 host->pl = pl;
1368 host->rta = rta;
1370 /* if no new mode selected, use old schema */
1371 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1372 rta_mode = true;
1373 pl_mode = true;
1376 /* Check which mode is on and do the warn / Crit stuff */
1377 if (rta_mode) {
1378 if (rta >= crit.rta) {
1379 this_status = STATE_CRITICAL;
1380 status = STATE_CRITICAL;
1381 host->rta_status = STATE_CRITICAL;
1382 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1383 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1384 status = STATE_WARNING;
1385 host->rta_status = STATE_WARNING;
1389 if (pl_mode) {
1390 if (pl >= crit.pl) {
1391 this_status = STATE_CRITICAL;
1392 status = STATE_CRITICAL;
1393 host->pl_status = STATE_CRITICAL;
1394 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1395 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1396 status = STATE_WARNING;
1397 host->pl_status = STATE_WARNING;
1401 if (jitter_mode) {
1402 if (host->jitter >= crit.jitter) {
1403 this_status = STATE_CRITICAL;
1404 status = STATE_CRITICAL;
1405 host->jitter_status = STATE_CRITICAL;
1406 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1407 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1408 status = STATE_WARNING;
1409 host->jitter_status = STATE_WARNING;
1413 if (mos_mode) {
1414 if (host->mos <= crit.mos) {
1415 this_status = STATE_CRITICAL;
1416 status = STATE_CRITICAL;
1417 host->mos_status = STATE_CRITICAL;
1418 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1419 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1420 status = STATE_WARNING;
1421 host->mos_status = STATE_WARNING;
1425 if (score_mode) {
1426 if (host->score <= crit.score) {
1427 this_status = STATE_CRITICAL;
1428 status = STATE_CRITICAL;
1429 host->score_status = STATE_CRITICAL;
1430 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1431 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1432 status = STATE_WARNING;
1433 host->score_status = STATE_WARNING;
1437 if (this_status == STATE_WARNING) {
1438 hosts_warn++;
1439 } else if (this_status == STATE_OK) {
1440 hosts_ok++;
1443 host = host->next;
1446 /* this is inevitable */
1447 if (!targets_alive)
1448 status = STATE_CRITICAL;
1449 if (min_hosts_alive > -1) {
1450 if (hosts_ok >= min_hosts_alive)
1451 status = STATE_OK;
1452 else if ((hosts_ok + hosts_warn) >= min_hosts_alive)
1453 status = STATE_WARNING;
1455 printf("%s - ", status_string[status]);
1457 host = list;
1458 while (host) {
1460 if (debug)
1461 puts("");
1462 if (i) {
1463 if (i < targets)
1464 printf(" :: ");
1465 else
1466 printf("\n");
1468 i++;
1469 if (!host->icmp_recv) {
1470 status = STATE_CRITICAL;
1471 host->rtmin = 0;
1472 host->jitter_min = 0;
1473 if (host->flags & FLAG_LOST_CAUSE) {
1474 char address[INET6_ADDRSTRLEN];
1475 parse_address(&host->error_addr, address, sizeof(address));
1476 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1477 } else { /* not marked as lost cause, so we have no flags for it */
1478 printf("%s: rta nan, lost 100%%", host->name);
1480 } else { /* !icmp_recv */
1481 printf("%s", host->name);
1482 /* rta text output */
1483 if (rta_mode) {
1484 if (status == STATE_OK)
1485 printf(" rta %0.3fms", host->rta / 1000);
1486 else if (status == STATE_WARNING && host->rta_status == status)
1487 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1488 else if (status == STATE_CRITICAL && host->rta_status == status)
1489 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1491 /* pl text output */
1492 if (pl_mode) {
1493 if (status == STATE_OK)
1494 printf(" lost %u%%", host->pl);
1495 else if (status == STATE_WARNING && host->pl_status == status)
1496 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1497 else if (status == STATE_CRITICAL && host->pl_status == status)
1498 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1500 /* jitter text output */
1501 if (jitter_mode) {
1502 if (status == STATE_OK)
1503 printf(" jitter %0.3fms", (float)host->jitter);
1504 else if (status == STATE_WARNING && host->jitter_status == status)
1505 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1506 else if (status == STATE_CRITICAL && host->jitter_status == status)
1507 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1509 /* mos text output */
1510 if (mos_mode) {
1511 if (status == STATE_OK)
1512 printf(" MOS %0.1f", (float)host->mos);
1513 else if (status == STATE_WARNING && host->mos_status == status)
1514 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1515 else if (status == STATE_CRITICAL && host->mos_status == status)
1516 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1518 /* score text output */
1519 if (score_mode) {
1520 if (status == STATE_OK)
1521 printf(" Score %u", (int)host->score);
1522 else if (status == STATE_WARNING && host->score_status == status)
1523 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1524 else if (status == STATE_CRITICAL && host->score_status == status)
1525 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1527 /* order statis text output */
1528 if (order_mode) {
1529 if (status == STATE_OK)
1530 printf(" Packets in order");
1531 else if (status == STATE_CRITICAL && host->order_status == status)
1532 printf(" Packets out of order");
1535 host = host->next;
1538 /* iterate once more for pretty perfparse output */
1539 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1540 printf("|");
1542 i = 0;
1543 host = list;
1544 while (host) {
1545 if (debug)
1546 puts("");
1548 if (rta_mode) {
1549 if (host->pl < 100) {
1550 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "",
1551 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "",
1552 (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1553 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1554 } else {
1555 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1556 (targets > 1) ? host->name : "");
1560 if (pl_mode) {
1561 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1564 if (jitter_mode) {
1565 if (host->pl < 100) {
1566 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1567 (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter,
1568 (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1569 (float)host->jitter_min / 1000);
1570 } else {
1571 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "",
1572 (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1576 if (mos_mode) {
1577 if (host->pl < 100) {
1578 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1579 } else {
1580 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1584 if (score_mode) {
1585 if (host->pl < 100) {
1586 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1587 } else {
1588 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1592 host = host->next;
1595 if (min_hosts_alive > -1) {
1596 if (hosts_ok >= min_hosts_alive)
1597 status = STATE_OK;
1598 else if ((hosts_ok + hosts_warn) >= min_hosts_alive)
1599 status = STATE_WARNING;
1602 /* finish with an empty line */
1603 puts("");
1604 if (debug)
1605 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok,
1606 hosts_warn, min_hosts_alive);
1608 exit(status);
1611 static u_int get_timevaldiff(struct timeval *early, struct timeval *later) {
1612 u_int ret;
1613 struct timeval now;
1615 if (!later) {
1616 gettimeofday(&now, &tz);
1617 later = &now;
1619 if (!early)
1620 early = &prog_start;
1622 /* if early > later we return 0 so as to indicate a timeout */
1623 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) {
1624 return 0;
1626 ret = (later->tv_sec - early->tv_sec) * 1000000;
1627 ret += later->tv_usec - early->tv_usec;
1629 return ret;
1632 static int add_target_ip(char *arg, struct sockaddr_storage *in) {
1633 struct rta_host *host;
1634 struct sockaddr_in *sin, *host_sin;
1635 struct sockaddr_in6 *sin6, *host_sin6;
1637 if (address_family == AF_INET)
1638 sin = (struct sockaddr_in *)in;
1639 else
1640 sin6 = (struct sockaddr_in6 *)in;
1642 /* disregard obviously stupid addresses
1643 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1644 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1645 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1646 return -1;
1649 /* no point in adding two identical IP's, so don't. ;) */
1650 host = list;
1651 while (host) {
1652 host_sin = (struct sockaddr_in *)&host->saddr_in;
1653 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1655 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1656 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1657 if (debug)
1658 printf("Identical IP already exists. Not adding %s\n", arg);
1659 return -1;
1661 host = host->next;
1664 /* add the fresh ip */
1665 host = (struct rta_host *)malloc(sizeof(struct rta_host));
1666 if (!host) {
1667 char straddr[INET6_ADDRSTRLEN];
1668 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1669 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1671 memset(host, 0, sizeof(struct rta_host));
1673 /* set the values. use calling name for output */
1674 host->name = strdup(arg);
1676 /* fill out the sockaddr_storage struct */
1677 if (address_family == AF_INET) {
1678 host_sin = (struct sockaddr_in *)&host->saddr_in;
1679 host_sin->sin_family = AF_INET;
1680 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1681 } else {
1682 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1683 host_sin6->sin6_family = AF_INET6;
1684 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1687 /* fill out the sockaddr_in struct */
1688 host->rtmin = INFINITY;
1689 host->rtmax = 0;
1690 host->jitter = 0;
1691 host->jitter_max = 0;
1692 host->jitter_min = INFINITY;
1693 host->last_tdiff = 0;
1694 host->order_status = STATE_OK;
1695 host->last_icmp_seq = 0;
1696 host->rta_status = 0;
1697 host->pl_status = 0;
1698 host->jitter_status = 0;
1699 host->mos_status = 0;
1700 host->score_status = 0;
1701 host->pl_status = 0;
1703 if (!list)
1704 list = cursor = host;
1705 else
1706 cursor->next = host;
1708 cursor = host;
1709 targets++;
1711 return 0;
1714 /* wrapper for add_target_ip */
1715 static int add_target(char *arg) {
1716 int error, result = -1;
1717 struct sockaddr_storage ip;
1718 struct addrinfo hints, *res, *p;
1719 struct sockaddr_in *sin;
1720 struct sockaddr_in6 *sin6;
1722 switch (address_family) {
1723 case -1:
1724 /* -4 and -6 are not specified on cmdline */
1725 address_family = AF_INET;
1726 sin = (struct sockaddr_in *)&ip;
1727 result = inet_pton(address_family, arg, &sin->sin_addr);
1728 #ifdef USE_IPV6
1729 if (result != 1) {
1730 address_family = AF_INET6;
1731 sin6 = (struct sockaddr_in6 *)&ip;
1732 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1734 #endif
1735 /* If we don't find any valid addresses, we still don't know the address_family */
1736 if (result != 1) {
1737 address_family = -1;
1739 break;
1740 case AF_INET:
1741 sin = (struct sockaddr_in *)&ip;
1742 result = inet_pton(address_family, arg, &sin->sin_addr);
1743 break;
1744 case AF_INET6:
1745 sin6 = (struct sockaddr_in6 *)&ip;
1746 result = inet_pton(address_family, arg, &sin6->sin6_addr);
1747 break;
1748 default:
1749 crash("Address family not supported");
1752 /* don't resolve if we don't have to */
1753 if (result == 1) {
1754 /* don't add all ip's if we were given a specific one */
1755 return add_target_ip(arg, &ip);
1756 } else {
1757 errno = 0;
1758 memset(&hints, 0, sizeof(hints));
1759 if (address_family == -1) {
1760 hints.ai_family = AF_UNSPEC;
1761 } else {
1762 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1764 hints.ai_socktype = SOCK_RAW;
1765 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1766 errno = 0;
1767 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1768 return -1;
1770 address_family = res->ai_family;
1773 /* possibly add all the IP's as targets */
1774 for (p = res; p != NULL; p = p->ai_next) {
1775 memcpy(&ip, p->ai_addr, p->ai_addrlen);
1776 add_target_ip(arg, &ip);
1778 /* this is silly, but it works */
1779 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
1780 if (debug > 2)
1781 printf("mode: %d\n", mode);
1782 continue;
1784 break;
1786 freeaddrinfo(res);
1788 return 0;
1791 static void set_source_ip(char *arg) {
1792 struct sockaddr_in src;
1794 memset(&src, 0, sizeof(src));
1795 src.sin_family = address_family;
1796 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE)
1797 src.sin_addr.s_addr = get_ip_address(arg);
1798 if (bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1)
1799 crash("Cannot bind to IP address %s", arg);
1802 /* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1803 static in_addr_t get_ip_address(const char *ifname) {
1804 // TODO: Rewrite this so the function return an error and we exit somewhere else
1805 struct sockaddr_in ip;
1806 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1807 #if defined(SIOCGIFADDR)
1808 struct ifreq ifr;
1810 strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
1812 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
1814 if (ioctl(icmp_sock, SIOCGIFADDR, &ifr) == -1)
1815 crash("Cannot determine IP address of interface %s", ifname);
1817 memcpy(&ip, &ifr.ifr_addr, sizeof(ip));
1818 #else
1819 (void)ifname;
1820 errno = 0;
1821 crash("Cannot get interface IP address on this platform.");
1822 #endif
1823 return ip.sin_addr.s_addr;
1827 * u = micro
1828 * m = milli
1829 * s = seconds
1830 * return value is in microseconds
1832 static u_int get_timevar(const char *str) {
1833 char p, u, *ptr;
1834 size_t len;
1835 u_int i, d; /* integer and decimal, respectively */
1836 u_int factor = 1000; /* default to milliseconds */
1838 if (!str)
1839 return 0;
1840 len = strlen(str);
1841 if (!len)
1842 return 0;
1844 /* unit might be given as ms|m (millisec),
1845 * us|u (microsec) or just plain s, for seconds */
1846 p = '\0';
1847 u = str[len - 1];
1848 if (len >= 2 && !isdigit((int)str[len - 2]))
1849 p = str[len - 2];
1850 if (p && u == 's')
1851 u = p;
1852 else if (!p)
1853 p = u;
1854 if (debug > 2)
1855 printf("evaluating %s, u: %c, p: %c\n", str, u, p);
1857 if (u == 'u')
1858 factor = 1; /* microseconds */
1859 else if (u == 'm')
1860 factor = 1000; /* milliseconds */
1861 else if (u == 's')
1862 factor = 1000000; /* seconds */
1863 if (debug > 2)
1864 printf("factor is %u\n", factor);
1866 i = strtoul(str, &ptr, 0);
1867 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
1868 return i * factor;
1870 /* time specified in usecs can't have decimal points, so ignore them */
1871 if (factor == 1)
1872 return i;
1874 d = strtoul(ptr + 1, NULL, 0);
1876 /* d is decimal, so get rid of excess digits */
1877 while (d >= factor)
1878 d /= 10;
1880 /* the last parenthesis avoids floating point exceptions. */
1881 return ((i * factor) + (d * (factor / 10)));
1884 /* not too good at checking errors, but it'll do (main() should barfe on -1) */
1885 static int get_threshold(char *str, threshold *th) {
1886 char *p = NULL, i = 0;
1888 if (!str || !strlen(str) || !th)
1889 return -1;
1891 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1892 p = &str[strlen(str) - 1];
1893 while (p != &str[1]) {
1894 if (*p == '%')
1895 *p = '\0';
1896 else if (*p == ',' && i) {
1897 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1898 th->pl = (unsigned char)strtoul(p + 1, NULL, 0);
1899 break;
1901 i = 1;
1902 p--;
1904 th->rta = get_timevar(str);
1906 if (!th->rta)
1907 return -1;
1909 if (th->rta > MAXTTL * 1000000)
1910 th->rta = MAXTTL * 1000000;
1911 if (th->pl > 100)
1912 th->pl = 100;
1914 return 0;
1918 * This functions receives a pointer to a string which should contain a threshold for the
1919 * rta, packet_loss, jitter, mos or score mode in the form number,number[m|%]* assigns the
1920 * parsed number to the corresponding threshold variable.
1921 * @param[in,out] str String containing the given threshold values
1922 * @param[in] length strlen(str)
1923 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
1924 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
1925 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively)
1927 static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) {
1928 if (!str || !length || !warn || !crit)
1929 return false;
1931 // p points to the last char in str
1932 char *p = &str[length - 1];
1934 // first_iteration is bof-stop on stupid libc's
1935 bool first_iteration = true;
1937 while (p != &str[0]) {
1938 if ((*p == 'm') || (*p == '%')) {
1939 *p = '\0';
1940 } else if (*p == ',' && !first_iteration) {
1941 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1943 char *start_of_value = p + 1;
1945 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) {
1946 return false;
1949 first_iteration = false;
1950 p--;
1953 return parse_threshold2_helper(p, strlen(p), warn, mode);
1956 static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) {
1957 char *resultChecker = {0};
1959 switch (mode) {
1960 case const_rta_mode:
1961 thr->rta = strtod(s, &resultChecker) * 1000;
1962 break;
1963 case const_packet_loss_mode:
1964 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0);
1965 break;
1966 case const_jitter_mode:
1967 thr->jitter = strtod(s, &resultChecker);
1969 break;
1970 case const_mos_mode:
1971 thr->mos = strtod(s, &resultChecker);
1972 break;
1973 case const_score_mode:
1974 thr->score = strtod(s, &resultChecker);
1975 break;
1978 if (resultChecker == s) {
1979 // Failed to parse
1980 return false;
1983 if (resultChecker != (s + length)) {
1984 // Trailing symbols
1985 return false;
1988 return true;
1991 unsigned short icmp_checksum(uint16_t *p, size_t n) {
1992 unsigned short cksum;
1993 long sum = 0;
1995 /* sizeof(uint16_t) == 2 */
1996 while (n >= 2) {
1997 sum += *(p++);
1998 n -= 2;
2001 /* mop up the occasional odd byte */
2002 if (n == 1)
2003 sum += *((uint8_t *)p - 1);
2005 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2006 sum += (sum >> 16); /* add carry */
2007 cksum = ~sum; /* ones-complement, trunc to 16 bits */
2009 return cksum;
2012 void print_help(void) {
2013 /*print_revision (progname);*/ /* FIXME: Why? */
2014 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2016 printf(COPYRIGHT, copyright, email);
2018 printf("\n\n");
2020 print_usage();
2022 printf(UT_HELP_VRSN);
2023 printf(UT_EXTRA_OPTS);
2025 printf(" %s\n", "-H");
2026 printf(" %s\n", _("specify a target"));
2027 printf(" %s\n", "[-4|-6]");
2028 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
2029 printf(" %s\n", "-w");
2030 printf(" %s", _("warning threshold (currently "));
2031 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl);
2032 printf(" %s\n", "-c");
2033 printf(" %s", _("critical threshold (currently "));
2034 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl);
2036 printf(" %s\n", "-R");
2037 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms"));
2038 printf(" %s\n", "-P");
2039 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2040 printf(" %s\n", "-J");
2041 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2042 printf(" %s\n", "-M");
2043 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2044 printf(" %s\n", "-S");
2045 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2046 printf(" %s\n", "-O");
2047 printf(" %s\n", _("detect out of order ICMP packts "));
2048 printf(" %s\n", "-H");
2049 printf(" %s\n", _("specify a target"));
2050 printf(" %s\n", "-s");
2051 printf(" %s\n", _("specify a source IP address or device name"));
2052 printf(" %s\n", "-n");
2053 printf(" %s", _("number of packets to send (currently "));
2054 printf("%u)\n", packets);
2055 printf(" %s\n", "-p");
2056 printf(" %s", _("number of packets to send (currently "));
2057 printf("%u)\n", packets);
2058 printf(" %s\n", "-i");
2059 printf(" %s", _("max packet interval (currently "));
2060 printf("%0.3fms)\n", (float)pkt_interval / 1000);
2061 printf(" %s\n", "-I");
2062 printf(" %s", _("max target interval (currently "));
2063 printf("%0.3fms)\n", (float)target_interval / 1000);
2064 printf(" %s\n", "-m");
2065 printf(" %s", _("number of alive hosts required for success"));
2066 printf("\n");
2067 printf(" %s\n", "-l");
2068 printf(" %s", _("TTL on outgoing packets (currently "));
2069 printf("%u)\n", ttl);
2070 printf(" %s\n", "-t");
2071 printf(" %s", _("timeout value (seconds, currently "));
2072 printf("%u)\n", timeout);
2073 printf(" %s\n", "-b");
2074 printf(" %s\n", _("Number of icmp data bytes to send"));
2075 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN);
2076 printf(" %s\n", "-v");
2077 printf(" %s\n", _("verbose"));
2078 printf("\n");
2079 printf("%s\n", _("Notes:"));
2080 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2081 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not."));
2082 printf("\n");
2083 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2084 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2085 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations"));
2086 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2087 /* -d not yet implemented */
2088 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops"));
2089 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2090 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2091 printf("\n");
2092 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2093 /* printf ("%s\n", _("Long options are currently unsupported."));
2094 printf ("%s\n", _("Options marked with * require an argument"));
2097 printf(UT_SUPPORT);
2100 void print_usage(void) {
2101 printf("%s\n", _("Usage:"));
2102 printf(" %s [options] [-H] host1 host2 hostN\n", progname);