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
{
180 const_packet_loss_mode
,
186 typedef enum enum_threshold_mode threshold_mode
;
188 /* the different ping types we can do
189 * TODO: investigate ARP ping as well */
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
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 *, ...);
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
;
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;
266 static void crash(const char *fmt
, ...) {
269 printf("%s: ", progname
);
276 printf(": %s", strerror(errno
));
282 static const char *get_icmp_error_msg(unsigned char icmp_type
, unsigned char icmp_code
) {
283 const char *msg
= "unreachable";
286 printf("get_icmp_error_msg(%u, %u)\n", icmp_type
, icmp_code
);
290 case ICMP_UNREACH_NET
:
291 msg
= "Net unreachable";
293 case ICMP_UNREACH_HOST
:
294 msg
= "Host unreachable";
296 case ICMP_UNREACH_PROTOCOL
:
297 msg
= "Protocol unreachable (firewall?)";
299 case ICMP_UNREACH_PORT
:
300 msg
= "Port unreachable (firewall?)";
302 case ICMP_UNREACH_NEEDFRAG
:
303 msg
= "Fragmentation needed";
305 case ICMP_UNREACH_SRCFAIL
:
306 msg
= "Source route failed";
308 case ICMP_UNREACH_ISOLATED
:
309 msg
= "Source host isolated";
311 case ICMP_UNREACH_NET_UNKNOWN
:
312 msg
= "Unknown network";
314 case ICMP_UNREACH_HOST_UNKNOWN
:
315 msg
= "Unknown host";
317 case ICMP_UNREACH_NET_PROHIB
:
318 msg
= "Network denied (firewall?)";
320 case ICMP_UNREACH_HOST_PROHIB
:
321 msg
= "Host denied (firewall?)";
323 case ICMP_UNREACH_TOSNET
:
324 msg
= "Bad TOS for network (firewall?)";
326 case ICMP_UNREACH_TOSHOST
:
327 msg
= "Bad TOS for host (firewall?)";
329 case ICMP_UNREACH_FILTER_PROHIB
:
330 msg
= "Prohibited by filter (firewall)";
332 case ICMP_UNREACH_HOST_PRECEDENCE
:
333 msg
= "Host precedence violation";
335 case ICMP_UNREACH_PRECEDENCE_CUTOFF
:
336 msg
= "Precedence cutoff";
339 msg
= "Invalid code";
345 /* really 'out of reach', or non-existent host behind a router serving
346 * two different subnets */
348 case ICMP_TIMXCEED_INTRANS
:
349 msg
= "Time to live exceeded in transit";
351 case ICMP_TIMXCEED_REASS
:
352 msg
= "Fragment reassembly time exceeded";
355 msg
= "Invalid code";
360 case ICMP_SOURCEQUENCH
:
361 msg
= "Transmitting too fast";
364 msg
= "Redirect (change route)";
367 msg
= "Bad IP header (required option absent)";
370 /* the following aren't error messages, so ignore */
372 case ICMP_TSTAMPREPLY
:
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) */
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
) {
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
) {
417 printf("Packet is no response to a packet we sent\n");
421 /* it is indeed a response for us */
422 host
= table
[ntohs(sent_icmp
.icmp_seq
) / packets
];
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
);
431 /* don't spend time on lost hosts any more */
432 if (host
->flags
& FLAG_LOST_CAUSE
)
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
;
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
;
451 void parse_address(struct sockaddr_storage
*addr
, char *address
, int size
) {
452 switch (address_family
) {
454 inet_ntop(address_family
, &((struct sockaddr_in
*)addr
)->sin_addr
, address
, size
);
457 inet_ntop(address_family
, &((struct sockaddr_in6
*)addr
)->sin6_addr
, address
, size
);
462 int main(int argc
, char **argv
) {
466 int icmp_sockerrno
, udp_sockerrno
, tcp_sockerrno
;
468 struct rta_host
*host
;
469 #ifdef HAVE_SIGACTION
470 struct sigaction sig_action
;
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
);
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;
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], '/');
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
;
502 /* Default critical thresholds */
508 /* Default warning thresholds */
515 protocols
= HAVE_ICMP
| HAVE_UDP
| HAVE_TCP
;
516 pkt_interval
= 80000; /* 80 msec packet interval by default */
519 if (!strcmp(progname
, "check_icmp") || !strcmp(progname
, "check_ping")) {
521 protocols
= HAVE_ICMP
;
522 } else if (!strcmp(progname
, "check_host")) {
523 mode
= MODE_HOSTCHECK
;
524 pkt_interval
= 1000000;
526 crit
.rta
= warn
.rta
= 1000000;
527 crit
.pl
= warn
.pl
= 100;
528 } else if (!strcmp(progname
, "check_rta_multi")) {
531 pkt_interval
= 50000;
535 /* support "--help" and "--version" */
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
) {
548 if (address_family
!= -1)
549 crash("Multiple protocol versions not supported");
550 address_family
= AF_INET
;
554 if (address_family
!= -1)
555 crash("Multiple protocol versions not supported");
556 address_family
= AF_INET6
;
558 usage(_("IPv6 support not available\n"));
565 /* Reset argument scanning */
570 /* parse the arguments */
571 for (i
= 1; i
< argc
; i
++) {
572 while ((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
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
;
583 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp
) + sizeof(struct icmp_ping_data
),
587 pkt_interval
= get_timevar(optarg
);
590 target_interval
= get_timevar(optarg
);
593 get_threshold(optarg
, &warn
);
596 get_threshold(optarg
, &crit
);
600 packets
= strtoul(optarg
, NULL
, 0);
603 timeout
= strtoul(optarg
, NULL
, 0);
611 ttl
= (int)strtoul(optarg
, NULL
, 0);
614 min_hosts_alive
= (int)strtoul(optarg
, NULL
, 0);
616 case 'd': /* implement later, for cluster checks */
617 warn_down
= (unsigned char)strtoul(optarg
, &ptr
, 0);
619 crit_down
= (unsigned char)strtoul(ptr
+ 1, NULL
, 0);
622 case 's': /* specify source IP address */
625 case 'V': /* version */
626 print_revision(progname
, NP_VERSION
);
632 case 'R': /* RTA mode */
633 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_rta_mode
);
635 crash("Failed to parse RTA threshold");
640 case 'P': /* packet loss mode */
641 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_packet_loss_mode
);
643 crash("Failed to parse packet loss threshold");
648 case 'J': /* jitter mode */
649 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_jitter_mode
);
651 crash("Failed to parse jitter threshold");
656 case 'M': /* MOS mode */
657 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_mos_mode
);
659 crash("Failed to parse MOS threshold");
664 case 'S': /* score mode */
665 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_score_mode
);
667 crash("Failed to parse score threshold");
672 case 'O': /* out of order mode */
679 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
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
];
698 crash("No hosts to check");
701 // add_target might change address_family
702 switch (address_family
) {
704 icmp_proto
= IPPROTO_ICMP
;
707 icmp_proto
= IPPROTO_ICMPV6
;
710 crash("Address family not supported");
712 if ((icmp_sock
= socket(address_family
, SOCK_RAW
, icmp_proto
)) != -1)
713 sockets
|= HAVE_ICMP
;
715 icmp_sockerrno
= errno
;
718 set_source_ip(source_ip
);
721 if (setsockopt(icmp_sock
, SOL_SOCKET
, SO_TIMESTAMP
, &on
, sizeof(on
)))
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");
733 if (icmp_sock
== -1) {
734 errno
= icmp_sockerrno
;
735 crash("Failed to obtain ICMP socket");
738 /* if(udp_sock == -1) { */
739 /* errno = icmp_sockerrno; */
740 /* crash("Failed to obtain UDP socket"); */
743 /* if(tcp_sock == -1) { */
744 /* errno = icmp_sockerrno; */
745 /* crash("Failed to obtain TCP socker"); */
753 result
= setsockopt(icmp_sock
, SOL_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
756 printf("setsockopt failed\n");
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
)
768 if (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
)
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 */
795 printf("Setting alarm timeout to %u seconds\n", 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
;
803 printf("packets: %u, targets: %u\n"
804 "target_interval: %0.3f, pkt_interval %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);
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);
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
);
826 crash("packets is > 20 (%d)", packets
);
829 if (min_hosts_alive
< -1) {
831 crash("minimum alive hosts is negative (%i)", min_hosts_alive
);
835 table
= malloc(sizeof(struct rta_host
*) * targets
);
837 crash("main(): malloc failed for host table");
842 host
->id
= i
* packets
;
856 static void run_checks(void) {
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 */
868 if (table
[t
]->flags
& FLAG_LOST_CAUSE
) {
870 printf("%s is a lost cause. not sending any more\n", table
[t
]->name
);
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
;
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
) {
890 printf("Time passed. Finishing up\n");
894 /* catch the packets that might come in within the timeframe, but
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:
904 * ip header : 20 bytes
905 * icmp header : 28 bytes
907 * ip header : 40 bytes
908 * icmp header : 28 bytes
910 * icmp echo reply : the rest
912 static int wait_for_reply(int sock
, u_int t
) {
914 static unsigned char buf
[65536];
915 struct sockaddr_storage resp_addr
;
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
;
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
) {
937 gettimeofday(&wait_start
, &tz
);
940 per_pkt_wait
= t
/ icmp_pkts_en_route
;
941 while (icmp_pkts_en_route
&& get_timevaldiff(&wait_start
, NULL
) < i
) {
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
)) {
949 /* reap responses until we hit a timeout */
950 n
= recvfrom_wto(sock
, buf
, sizeof(buf
), (struct sockaddr
*)&resp_addr
, &t
, &now
);
953 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait
);
955 continue; /* timeout for this one, so keep trying */
959 printf("recvfrom_wto() returned errors\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
;
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; */
982 hlen
= (address_family
== AF_INET6
) ? 0 : ip
->ip
.ip_hl
<< 2;
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); */
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
))) {
1007 printf("not a proper ICMP_ECHOREPLY\n");
1008 handle_random_icmp(buf
+ hlen
, &resp_addr
);
1012 /* this is indeed a valid response */
1013 if (address_family
== PF_INET
) {
1014 memcpy(&data
, packet
.icp
->icmp_data
, sizeof(data
));
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
];
1020 memcpy(&data
, &packet
.icp6
->icmp6_dataun
.icmp6_un_data8
[4], sizeof(data
));
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
;
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
;
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
;
1064 if (tdiff
> (unsigned int)host
->rtmax
)
1065 host
->rtmax
= tdiff
;
1066 if (tdiff
< (unsigned int)host
->rtmin
)
1067 host
->rtmin
= tdiff
;
1070 char address
[INET6_ADDRSTRLEN
];
1071 parse_address(&resp_addr
, address
, sizeof(address
));
1073 switch (address_family
) {
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);
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);
1100 /* the ping functions */
1101 static int send_icmp_ping(int sock
, struct rta_host
*host
) {
1104 struct icmp_ping_data data
;
1112 crash("Attempt to send on bogus socket");
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) {
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
;
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
);
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
);
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
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
));
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
;
1179 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1181 len
= sendmsg(sock
, &hdr
, MSG_CONFIRM
);
1183 len
= sendmsg(sock
, &hdr
, 0);
1188 if (len
< 0 || (unsigned int)len
!= icmp_pkt_size
) {
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
));
1204 static int recvfrom_wto(int sock
, void *buf
, unsigned int len
, struct sockaddr
*saddr
, u_int
*timo
, struct timeval
*tv
) {
1207 struct timeval to
, then
, now
;
1209 #ifdef HAVE_MSGHDR_MSG_CONTROL
1210 char ans_data
[4096];
1211 #endif // HAVE_MSGHDR_MSG_CONTROL
1215 struct cmsghdr
*chdr
;
1220 printf("*timo is not\n");
1224 to
.tv_sec
= *timo
/ 1000000;
1225 to
.tv_usec
= (*timo
- (to
.tv_sec
* 1000000));
1231 gettimeofday(&then
, &tz
);
1232 n
= select(sock
+ 1, &rd
, &wr
, NULL
, &to
);
1234 crash("select() in recvfrom_wto");
1235 gettimeofday(&now
, &tz
);
1236 *timo
= get_timevaldiff(&then
, &now
);
1239 return 0; /* timeout */
1241 slen
= sizeof(struct sockaddr_storage
);
1243 memset(&iov
, 0, sizeof(iov
));
1247 memset(&hdr
, 0, sizeof(hdr
));
1248 hdr
.msg_name
= saddr
;
1249 hdr
.msg_namelen
= slen
;
1252 #ifdef HAVE_MSGHDR_MSG_CONTROL
1253 hdr
.msg_control
= ans_data
;
1254 hdr
.msg_controllen
= sizeof(ans_data
);
1257 ret
= recvmsg(sock
, &hdr
, 0);
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
));
1267 #endif // SO_TIMESTAMP
1268 gettimeofday(tv
, &tz
);
1272 static void finish(int sig
) {
1276 struct rta_host
*host
;
1277 const char *status_string
[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1285 printf("finish(%d) called\n", sig
);
1287 if (icmp_sock
!= -1)
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 */
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 */
1311 status
= STATE_CRITICAL
;
1312 /* up the down counter if not already counted */
1313 if (!(host
->flags
& FLAG_LOST_CAUSE
) && targets_alive
)
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 )
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);
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).
1359 host
->mos
= 1 + ((0.035) * R
) + ((.000007) * R
* (R
- 60) * (100 - R
));
1362 host
->jitter_min
= 0;
1363 host
->jitter_max
= 0;
1370 /* if no new mode selected, use old schema */
1371 if (!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& !order_mode
) {
1376 /* Check which mode is on and do the warn / Crit stuff */
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
;
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
;
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
;
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
;
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
) {
1439 } else if (this_status
== STATE_OK
) {
1446 /* this is inevitable */
1448 status
= STATE_CRITICAL
;
1449 if (min_hosts_alive
> -1) {
1450 if (hosts_ok
>= min_hosts_alive
)
1452 else if ((hosts_ok
+ hosts_warn
) >= min_hosts_alive
)
1453 status
= STATE_WARNING
;
1455 printf("%s - ", status_string
[status
]);
1469 if (!host
->icmp_recv
) {
1470 status
= STATE_CRITICAL
;
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 */
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 */
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 */
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 */
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 */
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 */
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");
1538 /* iterate once more for pretty perfparse output */
1539 if (!(!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& order_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);
1555 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets
> 1) ? host
->name
: "", (targets
> 1) ? host
->name
: "",
1556 (targets
> 1) ? host
->name
: "");
1561 printf("%spl=%u%%;%u;%u;0;100 ", (targets
> 1) ? host
->name
: "", host
->pl
, warn
.pl
, crit
.pl
);
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);
1571 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets
> 1) ? host
->name
: "",
1572 (targets
> 1) ? host
->name
: "", (targets
> 1) ? host
->name
: "");
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
);
1580 printf("%smos=U;;;; ", (targets
> 1) ? host
->name
: "");
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
);
1588 printf("%sscore=U;;;; ", (targets
> 1) ? host
->name
: "");
1595 if (min_hosts_alive
> -1) {
1596 if (hosts_ok
>= min_hosts_alive
)
1598 else if ((hosts_ok
+ hosts_warn
) >= min_hosts_alive
)
1599 status
= STATE_WARNING
;
1602 /* finish with an empty line */
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
);
1611 static u_int
get_timevaldiff(struct timeval
*early
, struct timeval
*later
) {
1616 gettimeofday(&now
, &tz
);
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
)) {
1626 ret
= (later
->tv_sec
- early
->tv_sec
) * 1000000;
1627 ret
+= later
->tv_usec
- early
->tv_usec
;
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
;
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
))) {
1649 /* no point in adding two identical IP's, so don't. ;) */
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
)) {
1658 printf("Identical IP already exists. Not adding %s\n", arg
);
1664 /* add the fresh ip */
1665 host
= (struct rta_host
*)malloc(sizeof(struct rta_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
;
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
;
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;
1704 list
= cursor
= host
;
1706 cursor
->next
= host
;
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
) {
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
);
1730 address_family
= AF_INET6
;
1731 sin6
= (struct sockaddr_in6
*)&ip
;
1732 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1735 /* If we don't find any valid addresses, we still don't know the address_family */
1737 address_family
= -1;
1741 sin
= (struct sockaddr_in
*)&ip
;
1742 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1745 sin6
= (struct sockaddr_in6
*)&ip
;
1746 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1749 crash("Address family not supported");
1752 /* don't resolve if we don't have to */
1754 /* don't add all ip's if we were given a specific one */
1755 return add_target_ip(arg
, &ip
);
1758 memset(&hints
, 0, sizeof(hints
));
1759 if (address_family
== -1) {
1760 hints
.ai_family
= AF_UNSPEC
;
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) {
1767 crash("Failed to resolve %s: %s", arg
, gai_strerror(error
));
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
) {
1781 printf("mode: %d\n", mode
);
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)
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
));
1821 crash("Cannot get interface IP address on this platform.");
1823 return ip
.sin_addr
.s_addr
;
1830 * return value is in microseconds
1832 static u_int
get_timevar(const char *str
) {
1835 u_int i
, d
; /* integer and decimal, respectively */
1836 u_int factor
= 1000; /* default to milliseconds */
1844 /* unit might be given as ms|m (millisec),
1845 * us|u (microsec) or just plain s, for seconds */
1848 if (len
>= 2 && !isdigit((int)str
[len
- 2]))
1855 printf("evaluating %s, u: %c, p: %c\n", str
, u
, p
);
1858 factor
= 1; /* microseconds */
1860 factor
= 1000; /* milliseconds */
1862 factor
= 1000000; /* seconds */
1864 printf("factor is %u\n", factor
);
1866 i
= strtoul(str
, &ptr
, 0);
1867 if (!ptr
|| *ptr
!= '.' || strlen(ptr
) < 2 || factor
== 1)
1870 /* time specified in usecs can't have decimal points, so ignore them */
1874 d
= strtoul(ptr
+ 1, NULL
, 0);
1876 /* d is decimal, so get rid of excess digits */
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
)
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]) {
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);
1904 th
->rta
= get_timevar(str
);
1909 if (th
->rta
> MAXTTL
* 1000000)
1910 th
->rta
= MAXTTL
* 1000000;
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
)
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
== '%')) {
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
)) {
1949 first_iteration
= false;
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};
1960 case const_rta_mode
:
1961 thr
->rta
= strtod(s
, &resultChecker
) * 1000;
1963 case const_packet_loss_mode
:
1964 thr
->pl
= (unsigned char)strtoul(s
, &resultChecker
, 0);
1966 case const_jitter_mode
:
1967 thr
->jitter
= strtod(s
, &resultChecker
);
1970 case const_mos_mode
:
1971 thr
->mos
= strtod(s
, &resultChecker
);
1973 case const_score_mode
:
1974 thr
->score
= strtod(s
, &resultChecker
);
1978 if (resultChecker
== s
) {
1983 if (resultChecker
!= (s
+ length
)) {
1991 unsigned short icmp_checksum(uint16_t *p
, size_t n
) {
1992 unsigned short cksum
;
1995 /* sizeof(uint16_t) == 2 */
2001 /* mop up the occasional odd byte */
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 */
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
);
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"));
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"));
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."));
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."));*/
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"));
2100 void print_usage(void) {
2101 printf("%s\n", _("Usage:"));
2102 printf(" %s [options] [-H] host1 host2 hostN\n", progname
);