1 /*****************************************************************************
3 * Monitoring check_icmp plugin
6 * Copyright (c) 2005-2024 Monitoring Plugins Development Team
7 * Original Author : Andreas Ericsson <ae@op5.se>
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
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"; */
42 const char *copyright
= "2005-2024";
43 const char *email
= "devel@monitoring-plugins.org";
45 /** Monitoring Plugins basic includes */
46 #include "../plugins/common.h"
51 # include <sys/sockio.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) **/
73 # define INADDR_NONE (in_addr_t)(-1)
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
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
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
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
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 */
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
;
148 typedef union ip_hdr
{
153 typedef union icmp_packet
{
156 struct icmp6_hdr
*icp6
;
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)
174 #define MODE_HOSTCHECK 1
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 */
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
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 *, ...);
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
;
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;
260 static void crash(const char *fmt
, ...) {
263 printf("%s: ", progname
);
270 printf(": %s", strerror(errno
));
276 static const char *get_icmp_error_msg(unsigned char icmp_type
, unsigned char icmp_code
) {
277 const char *msg
= "unreachable";
280 printf("get_icmp_error_msg(%u, %u)\n", icmp_type
, icmp_code
);
284 case ICMP_UNREACH_NET
:
285 msg
= "Net unreachable";
287 case ICMP_UNREACH_HOST
:
288 msg
= "Host unreachable";
290 case ICMP_UNREACH_PROTOCOL
:
291 msg
= "Protocol unreachable (firewall?)";
293 case ICMP_UNREACH_PORT
:
294 msg
= "Port unreachable (firewall?)";
296 case ICMP_UNREACH_NEEDFRAG
:
297 msg
= "Fragmentation needed";
299 case ICMP_UNREACH_SRCFAIL
:
300 msg
= "Source route failed";
302 case ICMP_UNREACH_ISOLATED
:
303 msg
= "Source host isolated";
305 case ICMP_UNREACH_NET_UNKNOWN
:
306 msg
= "Unknown network";
308 case ICMP_UNREACH_HOST_UNKNOWN
:
309 msg
= "Unknown host";
311 case ICMP_UNREACH_NET_PROHIB
:
312 msg
= "Network denied (firewall?)";
314 case ICMP_UNREACH_HOST_PROHIB
:
315 msg
= "Host denied (firewall?)";
317 case ICMP_UNREACH_TOSNET
:
318 msg
= "Bad TOS for network (firewall?)";
320 case ICMP_UNREACH_TOSHOST
:
321 msg
= "Bad TOS for host (firewall?)";
323 case ICMP_UNREACH_FILTER_PROHIB
:
324 msg
= "Prohibited by filter (firewall)";
326 case ICMP_UNREACH_HOST_PRECEDENCE
:
327 msg
= "Host precedence violation";
329 case ICMP_UNREACH_PRECEDENCE_CUTOFF
:
330 msg
= "Precedence cutoff";
333 msg
= "Invalid code";
339 /* really 'out of reach', or non-existent host behind a router serving
340 * two different subnets */
342 case ICMP_TIMXCEED_INTRANS
:
343 msg
= "Time to live exceeded in transit";
345 case ICMP_TIMXCEED_REASS
:
346 msg
= "Fragment reassembly time exceeded";
349 msg
= "Invalid code";
354 case ICMP_SOURCEQUENCH
:
355 msg
= "Transmitting too fast";
358 msg
= "Redirect (change route)";
361 msg
= "Bad IP header (required option absent)";
364 /* the following aren't error messages, so ignore */
366 case ICMP_TSTAMPREPLY
:
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) */
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
) {
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
) {
411 printf("Packet is no response to a packet we sent\n");
415 /* it is indeed a response for us */
416 host
= table
[ntohs(sent_icmp
.icmp_seq
) / packets
];
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
);
425 /* don't spend time on lost hosts any more */
426 if (host
->flags
& FLAG_LOST_CAUSE
)
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
;
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
;
445 void parse_address(struct sockaddr_storage
*addr
, char *address
, int size
) {
446 switch (address_family
) {
448 inet_ntop(address_family
, &((struct sockaddr_in
*)addr
)->sin_addr
, address
, size
);
451 inet_ntop(address_family
, &((struct sockaddr_in6
*)addr
)->sin6_addr
, address
, size
);
456 int main(int argc
, char **argv
) {
460 int icmp_sockerrno
, udp_sockerrno
, tcp_sockerrno
;
462 struct rta_host
*host
;
463 #ifdef HAVE_SIGACTION
464 struct sigaction sig_action
;
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
);
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;
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], '/');
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
;
496 /* Default critical thresholds */
502 /* Default warning thresholds */
509 protocols
= HAVE_ICMP
| HAVE_UDP
| HAVE_TCP
;
510 pkt_interval
= 80000; /* 80 msec packet interval by default */
513 if (!strcmp(progname
, "check_icmp") || !strcmp(progname
, "check_ping")) {
515 protocols
= HAVE_ICMP
;
516 } else if (!strcmp(progname
, "check_host")) {
517 mode
= MODE_HOSTCHECK
;
518 pkt_interval
= 1000000;
520 crit
.rta
= warn
.rta
= 1000000;
521 crit
.pl
= warn
.pl
= 100;
522 } else if (!strcmp(progname
, "check_rta_multi")) {
525 pkt_interval
= 50000;
529 /* support "--help" and "--version" */
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
) {
542 if (address_family
!= -1)
543 crash("Multiple protocol versions not supported");
544 address_family
= AF_INET
;
548 if (address_family
!= -1)
549 crash("Multiple protocol versions not supported");
550 address_family
= AF_INET6
;
552 usage(_("IPv6 support not available\n"));
559 /* Reset argument scanning */
564 /* parse the arguments */
565 for (i
= 1; i
< argc
; i
++) {
566 while ((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
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
;
577 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp
) + sizeof(struct icmp_ping_data
), MAX_PING_DATA
- 1);
580 pkt_interval
= get_timevar(optarg
);
583 target_interval
= get_timevar(optarg
);
586 get_threshold(optarg
, &warn
);
589 get_threshold(optarg
, &crit
);
593 packets
= strtoul(optarg
, NULL
, 0);
596 timeout
= strtoul(optarg
, NULL
, 0);
604 ttl
= (int)strtoul(optarg
, NULL
, 0);
607 min_hosts_alive
= (int)strtoul(optarg
, NULL
, 0);
609 case 'd': /* implement later, for cluster checks */
610 warn_down
= (unsigned char)strtoul(optarg
, &ptr
, 0);
612 crit_down
= (unsigned char)strtoul(ptr
+ 1, NULL
, 0);
615 case 's': /* specify source IP address */
618 case 'V': /* version */
619 print_revision(progname
, NP_VERSION
);
625 case 'R': /* RTA mode */
626 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_rta_mode
);
628 crash("Failed to parse RTA threshold");
633 case 'P': /* packet loss mode */
634 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_packet_loss_mode
);
636 crash("Failed to parse packet loss threshold");
641 case 'J': /* jitter mode */
642 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_jitter_mode
);
644 crash("Failed to parse jitter threshold");
649 case 'M': /* MOS mode */
650 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_mos_mode
);
652 crash("Failed to parse MOS threshold");
657 case 'S': /* score mode */
658 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_score_mode
);
660 crash("Failed to parse score threshold");
665 case 'O': /* out of order mode */
672 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
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
];
691 crash("No hosts to check");
694 // add_target might change address_family
695 switch (address_family
) {
697 icmp_proto
= IPPROTO_ICMP
;
700 icmp_proto
= IPPROTO_ICMPV6
;
703 crash("Address family not supported");
705 if ((icmp_sock
= socket(address_family
, SOCK_RAW
, icmp_proto
)) != -1)
706 sockets
|= HAVE_ICMP
;
708 icmp_sockerrno
= errno
;
711 set_source_ip(source_ip
);
714 if (setsockopt(icmp_sock
, SOL_SOCKET
, SO_TIMESTAMP
, &on
, sizeof(on
)))
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");
726 if (icmp_sock
== -1) {
727 errno
= icmp_sockerrno
;
728 crash("Failed to obtain ICMP socket");
731 /* if(udp_sock == -1) { */
732 /* errno = icmp_sockerrno; */
733 /* crash("Failed to obtain UDP socket"); */
736 /* if(tcp_sock == -1) { */
737 /* errno = icmp_sockerrno; */
738 /* crash("Failed to obtain TCP socker"); */
746 result
= setsockopt(icmp_sock
, SOL_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
749 printf("setsockopt failed\n");
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
)
761 if (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
)
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 */
788 printf("Setting alarm timeout to %u seconds\n", 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
;
796 printf("packets: %u, targets: %u\n"
797 "target_interval: %0.3f, pkt_interval %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);
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);
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
);
818 crash("packets is > 20 (%d)", packets
);
821 if (min_hosts_alive
< -1) {
823 crash("minimum alive hosts is negative (%i)", min_hosts_alive
);
827 table
= malloc(sizeof(struct rta_host
*) * targets
);
829 crash("main(): malloc failed for host table");
834 host
->id
= i
* packets
;
848 static void run_checks(void) {
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 */
860 if (table
[t
]->flags
& FLAG_LOST_CAUSE
) {
862 printf("%s is a lost cause. not sending any more\n", table
[t
]->name
);
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
;
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
) {
882 printf("Time passed. Finishing up\n");
886 /* catch the packets that might come in within the timeframe, but
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:
896 * ip header : 20 bytes
897 * icmp header : 28 bytes
899 * ip header : 40 bytes
900 * icmp header : 28 bytes
902 * icmp echo reply : the rest
904 static int wait_for_reply(int sock
, u_int t
) {
906 static unsigned char buf
[65536];
907 struct sockaddr_storage resp_addr
;
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
;
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
) {
929 gettimeofday(&wait_start
, &tz
);
932 per_pkt_wait
= t
/ icmp_pkts_en_route
;
933 while (icmp_pkts_en_route
&& get_timevaldiff(&wait_start
, NULL
) < i
) {
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
)) {
941 /* reap responses until we hit a timeout */
942 n
= recvfrom_wto(sock
, buf
, sizeof(buf
), (struct sockaddr
*)&resp_addr
, &t
, &now
);
945 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait
);
947 continue; /* timeout for this one, so keep trying */
951 printf("recvfrom_wto() returned errors\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
;
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; */
974 hlen
= (address_family
== AF_INET6
) ? 0 : ip
->ip
.ip_hl
<< 2;
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); */
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
))) {
998 printf("not a proper ICMP_ECHOREPLY\n");
999 handle_random_icmp(buf
+ hlen
, &resp_addr
);
1003 /* this is indeed a valid response */
1004 if (address_family
== PF_INET
) {
1005 memcpy(&data
, packet
.icp
->icmp_data
, sizeof(data
));
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
];
1011 memcpy(&data
, &packet
.icp6
->icmp6_dataun
.icmp6_un_data8
[4], sizeof(data
));
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
;
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
;
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
;
1055 if (tdiff
> (unsigned int)host
->rtmax
)
1056 host
->rtmax
= tdiff
;
1057 if (tdiff
< (unsigned int)host
->rtmin
)
1058 host
->rtmin
= tdiff
;
1061 char address
[INET6_ADDRSTRLEN
];
1062 parse_address(&resp_addr
, address
, sizeof(address
));
1064 switch (address_family
) {
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);
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);
1089 /* the ping functions */
1090 static int send_icmp_ping(int sock
, struct rta_host
*host
) {
1093 struct icmp_ping_data data
;
1101 crash("Attempt to send on bogus socket");
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) {
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
;
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
);
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
);
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
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
));
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
;
1168 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1170 len
= sendmsg(sock
, &hdr
, MSG_CONFIRM
);
1172 len
= sendmsg(sock
, &hdr
, 0);
1177 if (len
< 0 || (unsigned int)len
!= icmp_pkt_size
) {
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
));
1193 static int recvfrom_wto(int sock
, void *buf
, unsigned int len
, struct sockaddr
*saddr
, u_int
*timo
, struct timeval
*tv
) {
1196 struct timeval to
, then
, now
;
1198 #ifdef HAVE_MSGHDR_MSG_CONTROL
1199 char ans_data
[4096];
1200 #endif // HAVE_MSGHDR_MSG_CONTROL
1204 struct cmsghdr
*chdr
;
1209 printf("*timo is not\n");
1213 to
.tv_sec
= *timo
/ 1000000;
1214 to
.tv_usec
= (*timo
- (to
.tv_sec
* 1000000));
1220 gettimeofday(&then
, &tz
);
1221 n
= select(sock
+ 1, &rd
, &wr
, NULL
, &to
);
1223 crash("select() in recvfrom_wto");
1224 gettimeofday(&now
, &tz
);
1225 *timo
= get_timevaldiff(&then
, &now
);
1228 return 0; /* timeout */
1230 slen
= sizeof(struct sockaddr_storage
);
1232 memset(&iov
, 0, sizeof(iov
));
1236 memset(&hdr
, 0, sizeof(hdr
));
1237 hdr
.msg_name
= saddr
;
1238 hdr
.msg_namelen
= slen
;
1241 #ifdef HAVE_MSGHDR_MSG_CONTROL
1242 hdr
.msg_control
= ans_data
;
1243 hdr
.msg_controllen
= sizeof(ans_data
);
1246 ret
= recvmsg(sock
, &hdr
, 0);
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
));
1256 #endif // SO_TIMESTAMP
1257 gettimeofday(tv
, &tz
);
1261 static void finish(int sig
) {
1265 struct rta_host
*host
;
1266 const char *status_string
[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1274 printf("finish(%d) called\n", sig
);
1276 if (icmp_sock
!= -1)
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 */
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 */
1300 status
= STATE_CRITICAL
;
1301 /* up the down counter if not already counted */
1302 if (!(host
->flags
& FLAG_LOST_CAUSE
) && targets_alive
)
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 )
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);
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).
1348 host
->mos
= 1 + ((0.035) * R
) + ((.000007) * R
* (R
- 60) * (100 - R
));
1351 host
->jitter_min
= 0;
1352 host
->jitter_max
= 0;
1359 /* if no new mode selected, use old schema */
1360 if (!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& !order_mode
) {
1365 /* Check which mode is on and do the warn / Crit stuff */
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
;
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
;
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
;
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
;
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
) {
1428 } else if (this_status
== STATE_OK
) {
1435 /* this is inevitable */
1437 status
= STATE_CRITICAL
;
1438 if (min_hosts_alive
> -1) {
1439 if (hosts_ok
>= min_hosts_alive
)
1441 else if ((hosts_ok
+ hosts_warn
) >= min_hosts_alive
)
1442 status
= STATE_WARNING
;
1444 printf("%s - ", status_string
[status
]);
1458 if (!host
->icmp_recv
) {
1459 status
= STATE_CRITICAL
;
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 */
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 */
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 */
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 */
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 */
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 */
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");
1527 /* iterate once more for pretty perfparse output */
1528 if (!(!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& order_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);
1543 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets
> 1) ? host
->name
: "", (targets
> 1) ? host
->name
: "", (targets
> 1) ? host
->name
: "");
1548 printf("%spl=%u%%;%u;%u;0;100 ", (targets
> 1) ? host
->name
: "", host
->pl
, warn
.pl
, crit
.pl
);
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);
1557 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets
> 1) ? host
->name
: "", (targets
> 1) ? host
->name
: "",
1558 (targets
> 1) ? host
->name
: "");
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
);
1566 printf("%smos=U;;;; ", (targets
> 1) ? host
->name
: "");
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
);
1574 printf("%sscore=U;;;; ", (targets
> 1) ? host
->name
: "");
1581 if (min_hosts_alive
> -1) {
1582 if (hosts_ok
>= min_hosts_alive
)
1584 else if ((hosts_ok
+ hosts_warn
) >= min_hosts_alive
)
1585 status
= STATE_WARNING
;
1588 /* finish with an empty line */
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
);
1596 static u_int
get_timevaldiff(struct timeval
*early
, struct timeval
*later
) {
1601 gettimeofday(&now
, &tz
);
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
)) {
1611 ret
= (later
->tv_sec
- early
->tv_sec
) * 1000000;
1612 ret
+= later
->tv_usec
- early
->tv_usec
;
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
;
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
))) {
1634 /* no point in adding two identical IP's, so don't. ;) */
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
)) {
1643 printf("Identical IP already exists. Not adding %s\n", arg
);
1649 /* add the fresh ip */
1650 host
= (struct rta_host
*)malloc(sizeof(struct rta_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
;
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
;
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;
1689 list
= cursor
= host
;
1691 cursor
->next
= host
;
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
) {
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
);
1715 address_family
= AF_INET6
;
1716 sin6
= (struct sockaddr_in6
*)&ip
;
1717 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1720 /* If we don't find any valid addresses, we still don't know the address_family */
1722 address_family
= -1;
1726 sin
= (struct sockaddr_in
*)&ip
;
1727 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1730 sin6
= (struct sockaddr_in6
*)&ip
;
1731 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1734 crash("Address family not supported");
1737 /* don't resolve if we don't have to */
1739 /* don't add all ip's if we were given a specific one */
1740 return add_target_ip(arg
, &ip
);
1743 memset(&hints
, 0, sizeof(hints
));
1744 if (address_family
== -1) {
1745 hints
.ai_family
= AF_UNSPEC
;
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) {
1752 crash("Failed to resolve %s: %s", arg
, gai_strerror(error
));
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
) {
1766 printf("mode: %d\n", mode
);
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)
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
));
1806 crash("Cannot get interface IP address on this platform.");
1808 return ip
.sin_addr
.s_addr
;
1815 * return value is in microseconds
1817 static u_int
get_timevar(const char *str
) {
1820 u_int i
, d
; /* integer and decimal, respectively */
1821 u_int factor
= 1000; /* default to milliseconds */
1829 /* unit might be given as ms|m (millisec),
1830 * us|u (microsec) or just plain s, for seconds */
1833 if (len
>= 2 && !isdigit((int)str
[len
- 2]))
1840 printf("evaluating %s, u: %c, p: %c\n", str
, u
, p
);
1843 factor
= 1; /* microseconds */
1845 factor
= 1000; /* milliseconds */
1847 factor
= 1000000; /* seconds */
1849 printf("factor is %u\n", factor
);
1851 i
= strtoul(str
, &ptr
, 0);
1852 if (!ptr
|| *ptr
!= '.' || strlen(ptr
) < 2 || factor
== 1)
1855 /* time specified in usecs can't have decimal points, so ignore them */
1859 d
= strtoul(ptr
+ 1, NULL
, 0);
1861 /* d is decimal, so get rid of excess digits */
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
)
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]) {
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);
1889 th
->rta
= get_timevar(str
);
1894 if (th
->rta
> MAXTTL
* 1000000)
1895 th
->rta
= MAXTTL
* 1000000;
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
)
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
== '%')) {
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
)) {
1934 first_iteration
= false;
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};
1945 case const_rta_mode
:
1946 thr
->rta
= strtod(s
, &resultChecker
) * 1000;
1948 case const_packet_loss_mode
:
1949 thr
->pl
= (unsigned char)strtoul(s
, &resultChecker
, 0);
1951 case const_jitter_mode
:
1952 thr
->jitter
= strtod(s
, &resultChecker
);
1955 case const_mos_mode
:
1956 thr
->mos
= strtod(s
, &resultChecker
);
1958 case const_score_mode
:
1959 thr
->score
= strtod(s
, &resultChecker
);
1963 if (resultChecker
== s
) {
1968 if (resultChecker
!= (s
+ length
)) {
1976 unsigned short icmp_checksum(uint16_t *p
, size_t n
) {
1977 unsigned short cksum
;
1980 /* sizeof(uint16_t) == 2 */
1986 /* mop up the occasional odd byte */
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 */
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
);
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"));
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"));
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."));
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."));*/
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"));
2085 void print_usage(void) {
2086 printf("%s\n", _("Usage:"));
2087 printf(" %s [options] [-H] host1 host2 hostN\n", progname
);