1 /*****************************************************************************
3 * Monitoring check_icmp plugin
6 * Copyright (c) 2005-2008 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-2008";
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>
69 /** sometimes undefined system macros (quite a few, actually) **/
74 # define INADDR_NONE (in_addr_t)(-1)
81 /* we bundle these in one #ifndef, since they're all from BSD
82 * Put individual #ifndef's around those that bother you */
83 #ifndef ICMP_UNREACH_NET_UNKNOWN
84 # define ICMP_UNREACH_NET_UNKNOWN 6
85 # define ICMP_UNREACH_HOST_UNKNOWN 7
86 # define ICMP_UNREACH_ISOLATED 8
87 # define ICMP_UNREACH_NET_PROHIB 9
88 # define ICMP_UNREACH_HOST_PROHIB 10
89 # define ICMP_UNREACH_TOSNET 11
90 # define ICMP_UNREACH_TOSHOST 12
92 /* tru64 has the ones above, but not these */
93 #ifndef ICMP_UNREACH_FILTER_PROHIB
94 # define ICMP_UNREACH_FILTER_PROHIB 13
95 # define ICMP_UNREACH_HOST_PRECEDENCE 14
96 # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
99 typedef unsigned short range_t
; /* type for get_range() -- unimplemented */
101 typedef struct rta_host
{
102 unsigned short id
; /* id in **table, and icmp pkts */
103 char *name
; /* arg used for adding this host */
104 char *msg
; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in
; /* the address of this host */
106 struct sockaddr_storage error_addr
; /* stores address of error replies */
107 unsigned long long time_waited
; /* total time waited, in usecs */
108 unsigned int icmp_sent
, icmp_recv
, icmp_lost
; /* counters */
109 unsigned char icmp_type
, icmp_code
; /* type and code from errors */
110 unsigned short flags
; /* control/status flags */
111 double rta
; /* measured RTA */
112 int rta_status
; // check result for RTA checks
113 double rtmax
; /* max rtt */
114 double rtmin
; /* min rtt */
115 double jitter
; /* measured jitter */
116 int jitter_status
; // check result for Jitter checks
117 double jitter_max
; /* jitter rtt maximum */
118 double jitter_min
; /* jitter rtt minimum */
119 double EffectiveLatency
;
120 double mos
; /* Mean opnion score */
121 int mos_status
; // check result for MOS checks
122 double score
; /* score */
123 int score_status
; // check result for score checks
125 u_int last_icmp_seq
; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl
; /* measured packet loss */
127 int pl_status
; // check result for packet loss checks
128 struct rta_host
*next
; /* linked list */
129 int order_status
; // check result for packet order checks
132 #define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
134 /* threshold structure. all values are maximum allowed, exclusive */
135 typedef struct threshold
{
136 unsigned char pl
; /* max allowed packet loss in percent */
137 unsigned int rta
; /* roundtrip time average, microseconds */
138 double jitter
; /* jitter time average, microseconds */
139 double mos
; /* MOS */
140 double score
; /* Score */
143 /* the data structure */
144 typedef struct icmp_ping_data
{
145 struct timeval stime
; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id
;
149 typedef union ip_hdr
{
154 typedef union icmp_packet
{
157 struct icmp6_hdr
*icp6
;
161 /* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
175 #define MODE_HOSTCHECK 1
179 enum enum_threshold_mode
{
181 const_packet_loss_mode
,
187 typedef enum enum_threshold_mode threshold_mode
;
189 /* the different ping types we can do
190 * TODO: investigate ARP ping as well */
196 #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197 #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198 #define IP_HDR_SIZE 20
199 #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200 #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
202 /* various target states */
203 #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204 #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205 #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206 #define TSTATE_UNREACH 0x08
209 void print_help (void);
210 void print_usage (void);
211 static u_int
get_timevar(const char *);
212 static u_int
get_timevaldiff(struct timeval
*, struct timeval
*);
213 static in_addr_t
get_ip_address(const char *);
214 static int wait_for_reply(int, u_int
);
215 static int recvfrom_wto(int, void *, unsigned int, struct sockaddr
*, u_int
*, struct timeval
*);
216 static int send_icmp_ping(int, struct rta_host
*);
217 static int get_threshold(char *str
, threshold
*th
);
218 static bool get_threshold2(char *str
, size_t length
, threshold
*, threshold
*, threshold_mode mode
);
219 static bool parse_threshold2_helper(char *s
, size_t length
, threshold
*thr
, threshold_mode mode
);
220 static void run_checks(void);
221 static void set_source_ip(char *);
222 static int add_target(char *);
223 static int add_target_ip(char *, struct sockaddr_storage
*);
224 static int handle_random_icmp(unsigned char *, struct sockaddr_storage
*);
225 static void parse_address(struct sockaddr_storage
*, char *, int);
226 static unsigned short icmp_checksum(uint16_t *, size_t);
227 static void finish(int);
228 static void crash(const char *, ...);
233 extern char **environ
;
235 /** global variables **/
236 static struct rta_host
**table
, *cursor
, *list
;
238 static threshold crit
= {
245 static threshold warn
= {
253 static int mode
, protocols
, sockets
, debug
= 0, timeout
= 10;
254 static unsigned short icmp_data_size
= DEFAULT_PING_DATA_SIZE
;
255 static unsigned short icmp_pkt_size
= DEFAULT_PING_DATA_SIZE
+ ICMP_MINLEN
;
257 static unsigned int icmp_sent
= 0, icmp_recv
= 0, icmp_lost
= 0, ttl
= 0;
258 #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
259 static unsigned short targets_down
= 0, targets
= 0, packets
= 0;
260 #define targets_alive (targets - targets_down)
261 static unsigned int retry_interval
, pkt_interval
, target_interval
;
262 static int icmp_sock
, tcp_sock
, udp_sock
, status
= STATE_OK
;
264 static struct timezone tz
;
265 static struct timeval prog_start
;
266 static unsigned long long max_completion_time
= 0;
267 static unsigned int warn_down
= 1, crit_down
= 1; /* host down threshold values */
268 static int min_hosts_alive
= -1;
269 float pkt_backoff_factor
= 1.5;
270 float target_backoff_factor
= 1.5;
273 bool jitter_mode
=false;
274 bool score_mode
=false;
276 bool order_mode
=false;
280 crash(const char *fmt
, ...)
284 printf("%s: ", progname
);
290 if(errno
) printf(": %s", strerror(errno
));
298 get_icmp_error_msg(unsigned char icmp_type
, unsigned char icmp_code
)
300 const char *msg
= "unreachable";
302 if(debug
> 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type
, icmp_code
);
306 case ICMP_UNREACH_NET
: msg
= "Net unreachable"; break;
307 case ICMP_UNREACH_HOST
: msg
= "Host unreachable"; break;
308 case ICMP_UNREACH_PROTOCOL
: msg
= "Protocol unreachable (firewall?)"; break;
309 case ICMP_UNREACH_PORT
: msg
= "Port unreachable (firewall?)"; break;
310 case ICMP_UNREACH_NEEDFRAG
: msg
= "Fragmentation needed"; break;
311 case ICMP_UNREACH_SRCFAIL
: msg
= "Source route failed"; break;
312 case ICMP_UNREACH_ISOLATED
: msg
= "Source host isolated"; break;
313 case ICMP_UNREACH_NET_UNKNOWN
: msg
= "Unknown network"; break;
314 case ICMP_UNREACH_HOST_UNKNOWN
: msg
= "Unknown host"; break;
315 case ICMP_UNREACH_NET_PROHIB
: msg
= "Network denied (firewall?)"; break;
316 case ICMP_UNREACH_HOST_PROHIB
: msg
= "Host denied (firewall?)"; break;
317 case ICMP_UNREACH_TOSNET
: msg
= "Bad TOS for network (firewall?)"; break;
318 case ICMP_UNREACH_TOSHOST
: msg
= "Bad TOS for host (firewall?)"; break;
319 case ICMP_UNREACH_FILTER_PROHIB
: msg
= "Prohibited by filter (firewall)"; break;
320 case ICMP_UNREACH_HOST_PRECEDENCE
: msg
= "Host precedence violation"; break;
321 case ICMP_UNREACH_PRECEDENCE_CUTOFF
: msg
= "Precedence cutoff"; break;
322 default: msg
= "Invalid code"; break;
327 /* really 'out of reach', or non-existent host behind a router serving
328 * two different subnets */
330 case ICMP_TIMXCEED_INTRANS
: msg
= "Time to live exceeded in transit"; break;
331 case ICMP_TIMXCEED_REASS
: msg
= "Fragment reassembly time exceeded"; break;
332 default: msg
= "Invalid code"; break;
336 case ICMP_SOURCEQUENCH
: msg
= "Transmitting too fast"; break;
337 case ICMP_REDIRECT
: msg
= "Redirect (change route)"; break;
338 case ICMP_PARAMPROB
: msg
= "Bad IP header (required option absent)"; break;
340 /* the following aren't error messages, so ignore */
342 case ICMP_TSTAMPREPLY
:
347 default: msg
= ""; break;
354 handle_random_icmp(unsigned char *packet
, struct sockaddr_storage
*addr
)
356 struct icmp p
, sent_icmp
;
357 struct rta_host
*host
= NULL
;
359 memcpy(&p
, packet
, sizeof(p
));
360 if(p
.icmp_type
== ICMP_ECHO
&& ntohs(p
.icmp_id
) == pid
) {
361 /* echo request from us to us (pinging localhost) */
365 if(debug
) printf("handle_random_icmp(%p, %p)\n", (void *)&p
, (void *)addr
);
367 /* only handle a few types, since others can't possibly be replies to
368 * us in a sane network (if it is anyway, it will be counted as lost
369 * at summary time, but not as quickly as a proper response */
370 /* TIMXCEED can be an unreach from a router with multiple IP's which
371 * serves two different subnets on the same interface and a dead host
372 * on one net is pinged from the other. The router will respond to
373 * itself and thus set TTL=0 so as to not loop forever. Even when
374 * TIMXCEED actually sends a proper icmp response we will have passed
375 * too many hops to have a hope of reaching it later, in which case it
376 * indicates overconfidence in the network, poor routing or both. */
377 if(p
.icmp_type
!= ICMP_UNREACH
&& p
.icmp_type
!= ICMP_TIMXCEED
&&
378 p
.icmp_type
!= ICMP_SOURCEQUENCH
&& p
.icmp_type
!= ICMP_PARAMPROB
)
383 /* might be for us. At least it holds the original package (according
384 * to RFC 792). If it isn't, just ignore it */
385 memcpy(&sent_icmp
, packet
+ 28, sizeof(sent_icmp
));
386 if(sent_icmp
.icmp_type
!= ICMP_ECHO
|| ntohs(sent_icmp
.icmp_id
) != pid
||
387 ntohs(sent_icmp
.icmp_seq
) >= targets
*packets
)
389 if(debug
) printf("Packet is no response to a packet we sent\n");
393 /* it is indeed a response for us */
394 host
= table
[ntohs(sent_icmp
.icmp_seq
)/packets
];
396 char address
[INET6_ADDRSTRLEN
];
397 parse_address(addr
, address
, sizeof(address
));
398 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
399 get_icmp_error_msg(p
.icmp_type
, p
.icmp_code
),
400 address
, host
->name
);
405 /* don't spend time on lost hosts any more */
406 if(host
->flags
& FLAG_LOST_CAUSE
) return 0;
408 /* source quench means we're sending too fast, so increase the
409 * interval and mark this packet lost */
410 if(p
.icmp_type
== ICMP_SOURCEQUENCH
) {
411 pkt_interval
*= pkt_backoff_factor
;
412 target_interval
*= target_backoff_factor
;
416 host
->flags
|= FLAG_LOST_CAUSE
;
418 host
->icmp_type
= p
.icmp_type
;
419 host
->icmp_code
= p
.icmp_code
;
420 host
->error_addr
= *addr
;
425 void parse_address(struct sockaddr_storage
*addr
, char *address
, int size
)
427 switch (address_family
) {
429 inet_ntop(address_family
, &((struct sockaddr_in
*)addr
)->sin_addr
, address
, size
);
432 inet_ntop(address_family
, &((struct sockaddr_in6
*)addr
)->sin6_addr
, address
, size
);
438 main(int argc
, char **argv
)
443 int icmp_sockerrno
, udp_sockerrno
, tcp_sockerrno
;
445 struct rta_host
*host
;
446 #ifdef HAVE_SIGACTION
447 struct sigaction sig_action
;
452 char *source_ip
= NULL
;
453 char * opts_str
= "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
454 setlocale (LC_ALL
, "");
455 bindtextdomain (PACKAGE
, LOCALEDIR
);
456 textdomain (PACKAGE
);
458 /* we only need to be setsuid when we get the sockets, so do
459 * that before pointer magic (esp. on network data) */
460 icmp_sockerrno
= udp_sockerrno
= tcp_sockerrno
= sockets
= 0;
463 int icmp_proto
= IPPROTO_ICMP
;
465 /* get calling name the old-fashioned way for portability instead
466 * of relying on the glibc-ism __progname */
467 ptr
= strrchr(argv
[0], '/');
468 if(ptr
) progname
= &ptr
[1];
469 else progname
= argv
[0];
471 /* now set defaults. Use progname to set them initially (allows for
472 * superfast check_host program when target host is up */
473 cursor
= list
= NULL
;
477 /* Default critical thresholds */
483 /* Default warning thresholds */
490 protocols
= HAVE_ICMP
| HAVE_UDP
| HAVE_TCP
;
491 pkt_interval
= 80000; /* 80 msec packet interval by default */
494 if(!strcmp(progname
, "check_icmp") || !strcmp(progname
, "check_ping")) {
496 protocols
= HAVE_ICMP
;
498 else if(!strcmp(progname
, "check_host")) {
499 mode
= MODE_HOSTCHECK
;
500 pkt_interval
= 1000000;
502 crit
.rta
= warn
.rta
= 1000000;
503 crit
.pl
= warn
.pl
= 100;
505 else if(!strcmp(progname
, "check_rta_multi")) {
508 pkt_interval
= 50000;
512 /* support "--help" and "--version" */
514 if(!strcmp(argv
[1], "--help"))
515 strcpy(argv
[1], "-h");
516 if(!strcmp(argv
[1], "--version"))
517 strcpy(argv
[1], "-V");
520 /* Parse protocol arguments first */
521 for(i
= 1; i
< argc
; i
++) {
522 while((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
525 if (address_family
!= -1)
526 crash("Multiple protocol versions not supported");
527 address_family
= AF_INET
;
531 if (address_family
!= -1)
532 crash("Multiple protocol versions not supported");
533 address_family
= AF_INET6
;
535 usage (_("IPv6 support not available\n"));
542 /* Reset argument scanning */
547 /* parse the arguments */
548 for(i
= 1; i
< argc
; i
++) {
549 while((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
555 size
= strtol(optarg
,NULL
,0);
556 if (size
>= (sizeof(struct icmp
) + sizeof(struct icmp_ping_data
)) &&
557 size
< MAX_PING_DATA
) {
558 icmp_data_size
= size
;
559 icmp_pkt_size
= size
+ ICMP_MINLEN
;
561 usage_va("ICMP data length must be between: %lu and %lu",
562 sizeof(struct icmp
) + sizeof(struct icmp_ping_data
),
566 pkt_interval
= get_timevar(optarg
);
569 target_interval
= get_timevar(optarg
);
572 get_threshold(optarg
, &warn
);
575 get_threshold(optarg
, &crit
);
579 packets
= strtoul(optarg
, NULL
, 0);
582 timeout
= strtoul(optarg
, NULL
, 0);
583 if(!timeout
) timeout
= 10;
589 ttl
= (int)strtoul(optarg
, NULL
, 0);
592 min_hosts_alive
= (int)strtoul(optarg
, NULL
, 0);
594 case 'd': /* implement later, for cluster checks */
595 warn_down
= (unsigned char)strtoul(optarg
, &ptr
, 0);
597 crit_down
= (unsigned char)strtoul(ptr
+ 1, NULL
, 0);
600 case 's': /* specify source IP address */
603 case 'V': /* version */
604 print_revision (progname
, NP_VERSION
);
605 exit (STATE_UNKNOWN
);
608 exit (STATE_UNKNOWN
);
610 case 'R': /* RTA mode */
611 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_rta_mode
);
613 crash("Failed to parse RTA threshold");
618 case 'P': /* packet loss mode */
619 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_packet_loss_mode
);
621 crash("Failed to parse packet loss threshold");
626 case 'J': /* jitter mode */
627 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_jitter_mode
);
629 crash("Failed to parse jitter threshold");
634 case 'M': /* MOS mode */
635 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_mos_mode
);
637 crash("Failed to parse MOS threshold");
642 case 'S': /* score mode */
643 err
= get_threshold2(optarg
, strlen(optarg
), &warn
, &crit
, const_score_mode
);
645 crash("Failed to parse score threshold");
650 case 'O': /* out of order mode */
657 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
660 /* use the pid to mark packets as ours */
661 /* Some systems have 32-bit pid_t so mask off only 16 bits */
662 pid
= getpid() & 0xffff;
663 /* printf("pid = %u\n", pid); */
665 /* Parse extra opts if any */
666 argv
=np_extra_opts(&argc
, argv
, progname
);
668 argv
= &argv
[optind
];
676 crash("No hosts to check");
679 // add_target might change address_family
680 switch ( address_family
){
681 case AF_INET
: icmp_proto
= IPPROTO_ICMP
;
683 case AF_INET6
: icmp_proto
= IPPROTO_ICMPV6
;
685 default: crash("Address family not supported");
687 if((icmp_sock
= socket(address_family
, SOCK_RAW
, icmp_proto
)) != -1)
688 sockets
|= HAVE_ICMP
;
689 else icmp_sockerrno
= errno
;
692 set_source_ip(source_ip
);
695 if(setsockopt(icmp_sock
, SOL_SOCKET
, SO_TIMESTAMP
, &on
, sizeof(on
)))
696 if(debug
) printf("Warning: no SO_TIMESTAMP support\n");
697 #endif // SO_TIMESTAMP
699 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
700 if (setuid(getuid()) == -1) {
701 printf("ERROR: Failed to drop privileges\n");
706 if(icmp_sock
== -1) {
707 errno
= icmp_sockerrno
;
708 crash("Failed to obtain ICMP socket");
711 /* if(udp_sock == -1) { */
712 /* errno = icmp_sockerrno; */
713 /* crash("Failed to obtain UDP socket"); */
716 /* if(tcp_sock == -1) { */
717 /* errno = icmp_sockerrno; */
718 /* crash("Failed to obtain TCP socker"); */
725 result
= setsockopt(icmp_sock
, SOL_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
727 if(result
== -1) printf("setsockopt failed\n");
728 else printf("ttl set to %u\n", ttl
);
732 /* stupid users should be able to give whatever thresholds they want
733 * (nothing will break if they do), but some anal plugin maintainer
734 * will probably add some printf() thing here later, so it might be
735 * best to at least show them where to do it. ;) */
736 if(warn
.pl
> crit
.pl
) warn
.pl
= crit
.pl
;
737 if(warn
.rta
> crit
.rta
) warn
.rta
= crit
.rta
;
738 if(warn_down
> crit_down
) crit_down
= warn_down
;
739 if(warn
.jitter
> crit
.jitter
) crit
.jitter
= warn
.jitter
;
740 if(warn
.mos
< crit
.mos
) warn
.mos
= crit
.mos
;
741 if(warn
.score
< crit
.score
) warn
.score
= crit
.score
;
743 #ifdef HAVE_SIGACTION
744 sig_action
.sa_sigaction
= NULL
;
745 sig_action
.sa_handler
= finish
;
746 sigfillset(&sig_action
.sa_mask
);
747 sig_action
.sa_flags
= SA_NODEFER
|SA_RESTART
;
748 sigaction(SIGINT
, &sig_action
, NULL
);
749 sigaction(SIGHUP
, &sig_action
, NULL
);
750 sigaction(SIGTERM
, &sig_action
, NULL
);
751 sigaction(SIGALRM
, &sig_action
, NULL
);
752 #else /* HAVE_SIGACTION */
753 signal(SIGINT
, finish
);
754 signal(SIGHUP
, finish
);
755 signal(SIGTERM
, finish
);
756 signal(SIGALRM
, finish
);
757 #endif /* HAVE_SIGACTION */
758 if(debug
) printf("Setting alarm timeout to %u seconds\n", timeout
);
761 /* make sure we don't wait any longer than necessary */
762 gettimeofday(&prog_start
, &tz
);
763 max_completion_time
=
764 ((targets
* packets
* pkt_interval
) + (targets
* target_interval
)) +
765 (targets
* packets
* crit
.rta
) + crit
.rta
;
768 printf("packets: %u, targets: %u\n"
769 "target_interval: %0.3f, pkt_interval %0.3f\n"
771 "max_completion_time: %0.3f\n",
773 (float)target_interval
/ 1000, (float)pkt_interval
/ 1000,
774 (float)crit
.rta
/ 1000,
775 (float)max_completion_time
/ 1000);
779 if(max_completion_time
> (u_int
)timeout
* 1000000) {
780 printf("max_completion_time: %llu timeout: %u\n",
781 max_completion_time
, timeout
);
782 printf("Timeout must be at least %llu\n",
783 max_completion_time
/ 1000000 + 1);
788 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
789 crit
.rta
, crit
.pl
, warn
.rta
, warn
.pl
);
790 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
791 pkt_interval
, target_interval
, retry_interval
);
792 printf("icmp_pkt_size: %u timeout: %u\n",
793 icmp_pkt_size
, timeout
);
798 crash("packets is > 20 (%d)", packets
);
801 if(min_hosts_alive
< -1) {
803 crash("minimum alive hosts is negative (%i)", min_hosts_alive
);
807 table
= malloc(sizeof(struct rta_host
*) * targets
);
809 crash("main(): malloc failed for host table");
814 host
->id
= i
*packets
;
832 u_int final_wait
, time_passed
;
834 /* this loop might actually violate the pkt_interval or target_interval
835 * settings, but only if there aren't any packets on the wire which
836 * indicates that the target can handle an increased packet rate */
837 for(i
= 0; i
< packets
; i
++) {
838 for(t
= 0; t
< targets
; t
++) {
839 /* don't send useless packets */
840 if(!targets_alive
) finish(0);
841 if(table
[t
]->flags
& FLAG_LOST_CAUSE
) {
842 if(debug
) printf("%s is a lost cause. not sending any more\n",
847 /* we're still in the game, so send next packet */
848 (void)send_icmp_ping(icmp_sock
, table
[t
]);
849 wait_for_reply(icmp_sock
, target_interval
);
851 wait_for_reply(icmp_sock
, pkt_interval
* targets
);
854 if(icmp_pkts_en_route
&& targets_alive
) {
855 time_passed
= get_timevaldiff(NULL
, NULL
);
856 final_wait
= max_completion_time
- time_passed
;
859 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
860 time_passed
, final_wait
, max_completion_time
);
862 if(time_passed
> max_completion_time
) {
863 if(debug
) printf("Time passed. Finishing up\n");
867 /* catch the packets that might come in within the timeframe, but
869 if(debug
) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
870 final_wait
, (float)final_wait
/ 1000);
871 wait_for_reply(icmp_sock
, final_wait
);
876 /* response structure:
878 * ip header : 20 bytes
879 * icmp header : 28 bytes
881 * ip header : 40 bytes
882 * icmp header : 28 bytes
884 * icmp echo reply : the rest
887 wait_for_reply(int sock
, u_int t
)
890 static unsigned char buf
[65536];
891 struct sockaddr_storage resp_addr
;
893 union icmp_packet packet
;
894 struct rta_host
*host
;
895 struct icmp_ping_data data
;
896 struct timeval wait_start
, now
;
897 u_int tdiff
, i
, per_pkt_wait
;
900 if (!(packet
.buf
= malloc(icmp_pkt_size
))) {
901 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
903 return -1; /* might be reached if we're in debug mode */
906 memset(packet
.buf
, 0, icmp_pkt_size
);
908 /* if we can't listen or don't have anything to listen to, just return */
909 if(!t
|| !icmp_pkts_en_route
) {
914 gettimeofday(&wait_start
, &tz
);
917 per_pkt_wait
= t
/ icmp_pkts_en_route
;
918 while(icmp_pkts_en_route
&& get_timevaldiff(&wait_start
, NULL
) < i
) {
921 /* wrap up if all targets are declared dead */
923 get_timevaldiff(&prog_start
, NULL
) >= max_completion_time
||
924 (mode
== MODE_HOSTCHECK
&& targets_down
))
929 /* reap responses until we hit a timeout */
930 n
= recvfrom_wto(sock
, buf
, sizeof(buf
),
931 (struct sockaddr
*)&resp_addr
, &t
, &now
);
934 printf("recvfrom_wto() timed out during a %u usecs wait\n",
937 continue; /* timeout for this one, so keep trying */
940 if(debug
) printf("recvfrom_wto() returned errors\n");
945 // FIXME: with ipv6 we don't have an ip header here
946 if (address_family
!= AF_INET6
) {
947 ip
= (union ip_hdr
*)buf
;
950 char address
[INET6_ADDRSTRLEN
];
951 parse_address(&resp_addr
, address
, sizeof(address
));
952 printf("received %u bytes from %s\n",
953 address_family
== AF_INET6
? ntohs(ip
->ip6
.ip6_plen
)
954 : ntohs(ip
->ip
.ip_len
),
959 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
960 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
961 /* alpha headers are decidedly broken. Using an ansi compiler,
962 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
963 * off the bottom 4 bits */
964 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
966 hlen
= (address_family
== AF_INET6
) ? 0 : ip
->ip
.ip_hl
<< 2;
969 if(n
< (hlen
+ ICMP_MINLEN
)) {
970 char address
[INET6_ADDRSTRLEN
];
971 parse_address(&resp_addr
, address
, sizeof(address
));
972 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
973 n
, hlen
+ icmp_pkt_size
, address
);
975 /* else if(debug) { */
976 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
977 /* hlen, ntohs(ip->ip_len) - hlen, */
978 /* sizeof(struct ip), icmp_pkt_size); */
981 /* check the response */
983 memcpy(packet
.buf
, buf
+ hlen
, icmp_pkt_size
);
984 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
985 : sizeof(struct icmp));*/
987 if( (address_family
== PF_INET
&&
988 (ntohs(packet
.icp
->icmp_id
) != pid
|| packet
.icp
->icmp_type
!= ICMP_ECHOREPLY
989 || ntohs(packet
.icp
->icmp_seq
) >= targets
* packets
))
990 || (address_family
== PF_INET6
&&
991 (ntohs(packet
.icp6
->icmp6_id
) != pid
|| packet
.icp6
->icmp6_type
!= ICMP6_ECHO_REPLY
992 || ntohs(packet
.icp6
->icmp6_seq
) >= targets
* packets
))) {
993 if(debug
> 2) printf("not a proper ICMP_ECHOREPLY\n");
994 handle_random_icmp(buf
+ hlen
, &resp_addr
);
998 /* this is indeed a valid response */
999 if (address_family
== PF_INET
) {
1000 memcpy(&data
, packet
.icp
->icmp_data
, sizeof(data
));
1002 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
1003 (unsigned long)sizeof(data
), ntohs(packet
.icp
->icmp_id
),
1004 ntohs(packet
.icp
->icmp_seq
), packet
.icp
->icmp_cksum
);
1005 host
= table
[ntohs(packet
.icp
->icmp_seq
)/packets
];
1007 memcpy(&data
, &packet
.icp6
->icmp6_dataun
.icmp6_un_data8
[4], sizeof(data
));
1009 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
1010 (unsigned long)sizeof(data
), ntohs(packet
.icp6
->icmp6_id
),
1011 ntohs(packet
.icp6
->icmp6_seq
), packet
.icp6
->icmp6_cksum
);
1012 host
= table
[ntohs(packet
.icp6
->icmp6_seq
)/packets
];
1015 tdiff
= get_timevaldiff(&data
.stime
, &now
);
1017 if (host
->last_tdiff
>0) {
1018 /* Calculate jitter */
1019 if (host
->last_tdiff
> tdiff
) {
1020 jitter_tmp
= host
->last_tdiff
- tdiff
;
1022 jitter_tmp
= tdiff
- host
->last_tdiff
;
1025 if (host
->jitter
==0) {
1026 host
->jitter
=jitter_tmp
;
1027 host
->jitter_max
=jitter_tmp
;
1028 host
->jitter_min
=jitter_tmp
;
1030 host
->jitter
+=jitter_tmp
;
1032 if (jitter_tmp
< host
->jitter_min
) {
1033 host
->jitter_min
=jitter_tmp
;
1036 if (jitter_tmp
> host
->jitter_max
) {
1037 host
->jitter_max
=jitter_tmp
;
1041 /* Check if packets in order */
1042 if (host
->last_icmp_seq
>= packet
.icp
->icmp_seq
)
1043 host
->order_status
=STATE_CRITICAL
;
1045 host
->last_tdiff
=tdiff
;
1047 host
->last_icmp_seq
=packet
.icp
->icmp_seq
;
1049 host
->time_waited
+= tdiff
;
1052 if (tdiff
> (unsigned int)host
->rtmax
)
1053 host
->rtmax
= tdiff
;
1054 if (tdiff
< (unsigned int)host
->rtmin
)
1055 host
->rtmin
= tdiff
;
1058 char address
[INET6_ADDRSTRLEN
];
1059 parse_address(&resp_addr
, address
, sizeof(address
));
1061 switch(address_family
) {
1063 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1064 (float)tdiff
/ 1000,
1068 (float)host
->rtmax
/ 1000,
1069 (float)host
->rtmin
/ 1000);
1073 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n",
1074 (float)tdiff
/ 1000,
1077 (float)host
->rtmax
/ 1000,
1078 (float)host
->rtmin
/ 1000);
1083 /* if we're in hostcheck mode, exit with limited printouts */
1084 if(mode
== MODE_HOSTCHECK
) {
1085 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1086 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1087 host
->name
, icmp_recv
, (float)tdiff
/ 1000,
1088 icmp_recv
, packets
, (float)tdiff
/ 1000,
1089 (float)warn
.rta
/ 1000, (float)crit
.rta
/ 1000);
1098 /* the ping functions */
1100 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",
1120 return -1; /* might be reached if we're in debug mode */
1123 memset(buf
, 0, icmp_pkt_size
);
1125 if((gettimeofday(&tv
, &tz
)) == -1) {
1130 data
.ping_id
= 10; /* host->icmp.icmp_sent; */
1131 memcpy(&data
.stime
, &tv
, sizeof(tv
));
1133 if (address_family
== AF_INET
) {
1134 struct icmp
*icp
= (struct icmp
*)buf
;
1135 addrlen
= sizeof(struct sockaddr_in
);
1137 memcpy(&icp
->icmp_data
, &data
, sizeof(data
));
1139 icp
->icmp_type
= ICMP_ECHO
;
1141 icp
->icmp_cksum
= 0;
1142 icp
->icmp_id
= htons(pid
);
1143 icp
->icmp_seq
= htons(host
->id
++);
1144 icp
->icmp_cksum
= icmp_checksum((uint16_t*)buf
, (size_t)icmp_pkt_size
);
1147 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1148 (unsigned long)sizeof(data
), ntohs(icp
->icmp_id
), ntohs(icp
->icmp_seq
), icp
->icmp_cksum
, host
->name
);
1151 struct icmp6_hdr
*icp6
= (struct icmp6_hdr
*)buf
;
1152 addrlen
= sizeof(struct sockaddr_in6
);
1154 memcpy(&icp6
->icmp6_dataun
.icmp6_un_data8
[4], &data
, sizeof(data
));
1156 icp6
->icmp6_type
= ICMP6_ECHO_REQUEST
;
1157 icp6
->icmp6_code
= 0;
1158 icp6
->icmp6_cksum
= 0;
1159 icp6
->icmp6_id
= htons(pid
);
1160 icp6
->icmp6_seq
= htons(host
->id
++);
1161 // let checksum be calculated automatically
1164 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1165 (unsigned long)sizeof(data
), ntohs(icp6
->icmp6_id
),
1166 ntohs(icp6
->icmp6_seq
), icp6
->icmp6_cksum
, host
->name
);
1170 memset(&iov
, 0, sizeof(iov
));
1172 iov
.iov_len
= icmp_pkt_size
;
1174 memset(&hdr
, 0, sizeof(hdr
));
1175 hdr
.msg_name
= (struct sockaddr
*)&host
->saddr_in
;
1176 hdr
.msg_namelen
= addrlen
;
1182 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1184 len
= sendmsg(sock
, &hdr
, MSG_CONFIRM
);
1186 len
= sendmsg(sock
, &hdr
, 0);
1191 if(len
< 0 || (unsigned int)len
!= icmp_pkt_size
) {
1193 char address
[INET6_ADDRSTRLEN
];
1194 parse_address((struct sockaddr_storage
*)&host
->saddr_in
, address
, sizeof(address
));
1195 printf("Failed to send ping to %s: %s\n", address
, strerror(errno
));
1208 recvfrom_wto(int sock
, void *buf
, unsigned int len
, struct sockaddr
*saddr
,
1209 u_int
*timo
, struct timeval
* tv
)
1213 struct timeval to
, then
, now
;
1215 #ifdef HAVE_MSGHDR_MSG_CONTROL
1216 char ans_data
[4096];
1217 #endif // HAVE_MSGHDR_MSG_CONTROL
1221 struct cmsghdr
* chdr
;
1225 if(debug
) printf("*timo is not\n");
1229 to
.tv_sec
= *timo
/ 1000000;
1230 to
.tv_usec
= (*timo
- (to
.tv_sec
* 1000000));
1236 gettimeofday(&then
, &tz
);
1237 n
= select(sock
+ 1, &rd
, &wr
, NULL
, &to
);
1238 if(n
< 0) crash("select() in recvfrom_wto");
1239 gettimeofday(&now
, &tz
);
1240 *timo
= get_timevaldiff(&then
, &now
);
1242 if(!n
) return 0; /* timeout */
1244 slen
= sizeof(struct sockaddr_storage
);
1246 memset(&iov
, 0, sizeof(iov
));
1250 memset(&hdr
, 0, sizeof(hdr
));
1251 hdr
.msg_name
= saddr
;
1252 hdr
.msg_namelen
= slen
;
1255 #ifdef HAVE_MSGHDR_MSG_CONTROL
1256 hdr
.msg_control
= ans_data
;
1257 hdr
.msg_controllen
= sizeof(ans_data
);
1260 ret
= recvmsg(sock
, &hdr
, 0);
1262 for(chdr
= CMSG_FIRSTHDR(&hdr
); chdr
; chdr
= CMSG_NXTHDR(&hdr
, chdr
)) {
1263 if(chdr
->cmsg_level
== SOL_SOCKET
1264 && chdr
->cmsg_type
== SO_TIMESTAMP
1265 && chdr
->cmsg_len
>= CMSG_LEN(sizeof(struct timeval
))) {
1266 memcpy(tv
, CMSG_DATA(chdr
), sizeof(*tv
));
1272 #endif // SO_TIMESTAMP
1273 gettimeofday(tv
, &tz
);
1283 struct rta_host
*host
;
1284 const char *status_string
[] =
1285 {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1292 if(debug
> 1) printf("finish(%d) called\n", sig
);
1294 if(icmp_sock
!= -1) close(icmp_sock
);
1295 if(udp_sock
!= -1) close(udp_sock
);
1296 if(tcp_sock
!= -1) close(tcp_sock
);
1299 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
1300 icmp_sent
, icmp_recv
, icmp_lost
);
1301 printf("targets: %u targets_alive: %u\n", targets
, targets_alive
);
1304 /* iterate thrice to calculate values, give output, and print perfparse */
1309 this_status
= STATE_OK
;
1311 if(!host
->icmp_recv
) {
1312 /* rta 0 is ofcourse not entirely correct, but will still show up
1313 * conspicuously as missing entries in perfparse and cacti */
1316 status
= STATE_CRITICAL
;
1317 /* up the down counter if not already counted */
1318 if(!(host
->flags
& FLAG_LOST_CAUSE
) && targets_alive
) targets_down
++;
1320 pl
= ((host
->icmp_sent
- host
->icmp_recv
) * 100) / host
->icmp_sent
;
1321 rta
= (double)host
->time_waited
/ host
->icmp_recv
;
1324 if (host
->icmp_recv
>1) {
1326 * This algorithm is probably pretty much blindly copied from
1327 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1328 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1329 * According to some quick research MOS originates from the Audio/Video transport network area.
1330 * Whether it can and should be computed from ICMP data, I can not say.
1332 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1334 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1337 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1339 host
->jitter
=(host
->jitter
/ (host
->icmp_recv
- 1)/1000);
1342 * Take the average round trip latency (in milliseconds), add
1343 * round trip jitter, but double the impact to latency
1344 * then add 10 for protocol latencies (in milliseconds).
1346 host
->EffectiveLatency
= (rta
/1000) + host
->jitter
* 2 + 10;
1348 if (host
->EffectiveLatency
< 160) {
1349 R
= 93.2 - (host
->EffectiveLatency
/ 40);
1351 R
= 93.2 - ((host
->EffectiveLatency
- 120) / 10);
1354 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1355 // loss of 5% will be entered as 5).
1363 host
->mos
= 1 + ((0.035) * R
) + ((.000007) * R
* (R
-60) * (100-R
));
1374 /* if no new mode selected, use old schema */
1375 if (!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& !order_mode
) {
1380 /* Check which mode is on and do the warn / Crit stuff */
1382 if(rta
>= crit
.rta
) {
1383 this_status
= STATE_CRITICAL
;
1384 status
= STATE_CRITICAL
;
1385 host
->rta_status
=STATE_CRITICAL
;
1386 } else if(status
!=STATE_CRITICAL
&& (rta
>= warn
.rta
)) {
1387 this_status
= (this_status
<= STATE_WARNING
? STATE_WARNING
: this_status
);
1388 status
= STATE_WARNING
;
1389 host
->rta_status
=STATE_WARNING
;
1395 this_status
= STATE_CRITICAL
;
1396 status
= STATE_CRITICAL
;
1397 host
->pl_status
=STATE_CRITICAL
;
1398 } else if(status
!=STATE_CRITICAL
&& (pl
>= warn
.pl
)) {
1399 this_status
= (this_status
<= STATE_WARNING
? STATE_WARNING
: this_status
);
1400 status
= STATE_WARNING
;
1401 host
->pl_status
=STATE_WARNING
;
1406 if(host
->jitter
>= crit
.jitter
) {
1407 this_status
= STATE_CRITICAL
;
1408 status
= STATE_CRITICAL
;
1409 host
->jitter_status
=STATE_CRITICAL
;
1410 } else if(status
!=STATE_CRITICAL
&& (host
->jitter
>= warn
.jitter
)) {
1411 this_status
= (this_status
<= STATE_WARNING
? STATE_WARNING
: this_status
);
1412 status
= STATE_WARNING
;
1413 host
->jitter_status
=STATE_WARNING
;
1418 if(host
->mos
<= crit
.mos
) {
1419 this_status
= STATE_CRITICAL
;
1420 status
= STATE_CRITICAL
;
1421 host
->mos_status
=STATE_CRITICAL
;
1422 } else if(status
!=STATE_CRITICAL
&& (host
->mos
<= warn
.mos
)) {
1423 this_status
= (this_status
<= STATE_WARNING
? STATE_WARNING
: this_status
);
1424 status
= STATE_WARNING
;
1425 host
->mos_status
=STATE_WARNING
;
1430 if(host
->score
<= crit
.score
) {
1431 this_status
= STATE_CRITICAL
;
1432 status
= STATE_CRITICAL
;
1433 host
->score_status
=STATE_CRITICAL
;
1434 } else if(status
!=STATE_CRITICAL
&& (host
->score
<= warn
.score
)) {
1435 this_status
= (this_status
<= STATE_WARNING
? STATE_WARNING
: this_status
);
1436 status
= STATE_WARNING
;
1437 host
->score_status
=STATE_WARNING
;
1441 if (this_status
== STATE_WARNING
) {
1443 } else if (this_status
== STATE_OK
) {
1451 /* this is inevitable */
1452 if(!targets_alive
) status
= STATE_CRITICAL
;
1453 if(min_hosts_alive
> -1) {
1454 if(hosts_ok
>= min_hosts_alive
) status
= STATE_OK
;
1455 else if((hosts_ok
+ hosts_warn
) >= min_hosts_alive
) status
= STATE_WARNING
;
1457 printf("%s - ", status_string
[status
]);
1464 if(i
< targets
) printf(" :: ");
1468 if(!host
->icmp_recv
) {
1469 status
= STATE_CRITICAL
;
1472 if(host
->flags
& FLAG_LOST_CAUSE
) {
1473 char address
[INET6_ADDRSTRLEN
];
1474 parse_address(&host
->error_addr
, address
, sizeof(address
));
1475 printf("%s: %s @ %s. rta nan, lost %d%%",
1477 get_icmp_error_msg(host
->icmp_type
, host
->icmp_code
),
1480 } else { /* not marked as lost cause, so we have no flags for it */
1481 printf("%s: rta nan, lost 100%%", host
->name
);
1483 } else { /* !icmp_recv */
1484 printf("%s", host
->name
);
1485 /* rta text output */
1487 if (status
== STATE_OK
)
1488 printf(" rta %0.3fms", host
->rta
/ 1000);
1489 else if (status
==STATE_WARNING
&& host
->rta_status
==status
)
1490 printf(" rta %0.3fms > %0.3fms", (float)host
->rta
/ 1000, (float)warn
.rta
/1000);
1491 else if (status
==STATE_CRITICAL
&& host
->rta_status
==status
)
1492 printf(" rta %0.3fms > %0.3fms", (float)host
->rta
/ 1000, (float)crit
.rta
/1000);
1494 /* pl text output */
1496 if (status
== STATE_OK
)
1497 printf(" lost %u%%", host
->pl
);
1498 else if (status
==STATE_WARNING
&& host
->pl_status
==status
)
1499 printf(" lost %u%% > %u%%", host
->pl
, warn
.pl
);
1500 else if (status
==STATE_CRITICAL
&& host
->pl_status
==status
)
1501 printf(" lost %u%% > %u%%", host
->pl
, crit
.pl
);
1503 /* jitter text output */
1505 if (status
== STATE_OK
)
1506 printf(" jitter %0.3fms", (float)host
->jitter
);
1507 else if (status
==STATE_WARNING
&& host
->jitter_status
==status
)
1508 printf(" jitter %0.3fms > %0.3fms", (float)host
->jitter
, warn
.jitter
);
1509 else if (status
==STATE_CRITICAL
&& host
->jitter_status
==status
)
1510 printf(" jitter %0.3fms > %0.3fms", (float)host
->jitter
, crit
.jitter
);
1512 /* mos text output */
1514 if (status
== STATE_OK
)
1515 printf(" MOS %0.1f", (float)host
->mos
);
1516 else if (status
==STATE_WARNING
&& host
->mos_status
==status
)
1517 printf(" MOS %0.1f < %0.1f", (float)host
->mos
, (float)warn
.mos
);
1518 else if (status
==STATE_CRITICAL
&& host
->mos_status
==status
)
1519 printf(" MOS %0.1f < %0.1f", (float)host
->mos
, (float)crit
.mos
);
1521 /* score text output */
1523 if (status
== STATE_OK
)
1524 printf(" Score %u", (int)host
->score
);
1525 else if (status
==STATE_WARNING
&& host
->score_status
==status
)
1526 printf(" Score %u < %u", (int)host
->score
, (int)warn
.score
);
1527 else if (status
==STATE_CRITICAL
&& host
->score_status
==status
)
1528 printf(" Score %u < %u", (int)host
->score
, (int)crit
.score
);
1530 /* order statis text output */
1532 if (status
== STATE_OK
)
1533 printf(" Packets in order");
1534 else if (status
==STATE_CRITICAL
&& host
->order_status
==status
)
1535 printf(" Packets out of order");
1541 /* iterate once more for pretty perfparse output */
1542 if (!(!rta_mode
&& !pl_mode
&& !jitter_mode
&& !score_mode
&& !mos_mode
&& order_mode
)) {
1552 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ",
1553 (targets
> 1) ? host
->name
: "",
1554 host
->rta
/ 1000, (float)warn
.rta
/ 1000, (float)crit
.rta
/ 1000,
1555 (targets
> 1) ? host
->name
: "", (float)host
->rtmax
/ 1000,
1556 (targets
> 1) ? host
->name
: "", (host
->rtmin
< INFINITY
) ? (float)host
->rtmin
/ 1000 : (float)0);
1558 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ",
1559 (targets
> 1) ? host
->name
: "",
1560 (targets
> 1) ? host
->name
: "",
1561 (targets
> 1) ? host
->name
: "");
1566 printf("%spl=%u%%;%u;%u;0;100 ", (targets
> 1) ? host
->name
: "", host
->pl
, warn
.pl
, crit
.pl
);
1571 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1572 (targets
> 1) ? host
->name
: "",
1573 (float)host
->jitter
,
1576 (targets
> 1) ? host
->name
: "",
1577 (float)host
->jitter_max
/ 1000, (targets
> 1) ? host
->name
: "",
1578 (float)host
->jitter_min
/ 1000
1581 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ",
1582 (targets
> 1) ? host
->name
: "",
1583 (targets
> 1) ? host
->name
: "",
1584 (targets
> 1) ? host
->name
: "");
1590 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ",
1591 (targets
> 1) ? host
->name
: "",
1596 printf("%smos=U;;;; ", (targets
> 1) ? host
->name
: "");
1602 printf("%sscore=%u;%u;%u;0;100 ",
1603 (targets
> 1) ? host
->name
: "",
1608 printf("%sscore=U;;;; ", (targets
> 1) ? host
->name
: "");
1615 if(min_hosts_alive
> -1) {
1616 if(hosts_ok
>= min_hosts_alive
) status
= STATE_OK
;
1617 else if((hosts_ok
+ hosts_warn
) >= min_hosts_alive
) status
= STATE_WARNING
;
1620 /* finish with an empty line */
1622 if(debug
) printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1623 targets
, targets_alive
, hosts_ok
, hosts_warn
, min_hosts_alive
);
1629 get_timevaldiff(struct timeval
*early
, struct timeval
*later
)
1635 gettimeofday(&now
, &tz
);
1638 if(!early
) early
= &prog_start
;
1640 /* if early > later we return 0 so as to indicate a timeout */
1641 if(early
->tv_sec
> later
->tv_sec
||
1642 (early
->tv_sec
== later
->tv_sec
&& early
->tv_usec
> later
->tv_usec
))
1646 ret
= (later
->tv_sec
- early
->tv_sec
) * 1000000;
1647 ret
+= later
->tv_usec
- early
->tv_usec
;
1653 add_target_ip(char *arg
, struct sockaddr_storage
*in
)
1655 struct rta_host
*host
;
1656 struct sockaddr_in
*sin
, *host_sin
;
1657 struct sockaddr_in6
*sin6
, *host_sin6
;
1659 if (address_family
== AF_INET
)
1660 sin
= (struct sockaddr_in
*)in
;
1662 sin6
= (struct sockaddr_in6
*)in
;
1666 /* disregard obviously stupid addresses
1667 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1668 if (((address_family
== AF_INET
&& (sin
->sin_addr
.s_addr
== INADDR_NONE
1669 || sin
->sin_addr
.s_addr
== INADDR_ANY
)))
1670 || (address_family
== AF_INET6
&& (sin6
->sin6_addr
.s6_addr
== in6addr_any
.s6_addr
))) {
1674 /* no point in adding two identical IP's, so don't. ;) */
1677 host_sin
= (struct sockaddr_in
*)&host
->saddr_in
;
1678 host_sin6
= (struct sockaddr_in6
*)&host
->saddr_in
;
1680 if( (address_family
== AF_INET
&& host_sin
->sin_addr
.s_addr
== sin
->sin_addr
.s_addr
)
1681 || (address_family
== AF_INET6
&& host_sin6
->sin6_addr
.s6_addr
== sin6
->sin6_addr
.s6_addr
)) {
1682 if(debug
) printf("Identical IP already exists. Not adding %s\n", arg
);
1688 /* add the fresh ip */
1689 host
= (struct rta_host
*)malloc(sizeof(struct rta_host
));
1691 char straddr
[INET6_ADDRSTRLEN
];
1692 parse_address((struct sockaddr_storage
*)&in
, straddr
, sizeof(straddr
));
1693 crash("add_target_ip(%s, %s): malloc(%lu) failed",
1694 arg
, straddr
, sizeof(struct rta_host
));
1696 memset(host
, 0, sizeof(struct rta_host
));
1698 /* set the values. use calling name for output */
1699 host
->name
= strdup(arg
);
1702 /* fill out the sockaddr_storage struct */
1703 if(address_family
== AF_INET
) {
1704 host_sin
= (struct sockaddr_in
*)&host
->saddr_in
;
1705 host_sin
->sin_family
= AF_INET
;
1706 host_sin
->sin_addr
.s_addr
= sin
->sin_addr
.s_addr
;
1709 host_sin6
= (struct sockaddr_in6
*)&host
->saddr_in
;
1710 host_sin6
->sin6_family
= AF_INET6
;
1711 memcpy(host_sin6
->sin6_addr
.s6_addr
, sin6
->sin6_addr
.s6_addr
, sizeof host_sin6
->sin6_addr
.s6_addr
);
1714 /* fill out the sockaddr_in struct */
1715 host
->rtmin
= INFINITY
;
1719 host
->jitter_min
=INFINITY
;
1721 host
->order_status
=STATE_OK
;
1722 host
->last_icmp_seq
=0;
1725 host
->jitter_status
=0;
1727 host
->score_status
=0;
1731 if(!list
) list
= cursor
= host
;
1732 else cursor
->next
= host
;
1740 /* wrapper for add_target_ip */
1742 add_target(char *arg
)
1744 int error
, result
= -1;
1745 struct sockaddr_storage ip
;
1746 struct addrinfo hints
, *res
, *p
;
1747 struct sockaddr_in
*sin
;
1748 struct sockaddr_in6
*sin6
;
1750 switch (address_family
) {
1752 /* -4 and -6 are not specified on cmdline */
1753 address_family
= AF_INET
;
1754 sin
= (struct sockaddr_in
*)&ip
;
1755 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1758 address_family
= AF_INET6
;
1759 sin6
= (struct sockaddr_in6
*)&ip
;
1760 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1763 /* If we don't find any valid addresses, we still don't know the address_family */
1765 address_family
= -1;
1769 sin
= (struct sockaddr_in
*)&ip
;
1770 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1773 sin6
= (struct sockaddr_in6
*)&ip
;
1774 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1776 default: crash("Address family not supported");
1779 /* don't resolve if we don't have to */
1781 /* don't add all ip's if we were given a specific one */
1782 return add_target_ip(arg
, &ip
);
1786 memset(&hints
, 0, sizeof(hints
));
1787 if (address_family
== -1) {
1788 hints
.ai_family
= AF_UNSPEC
;
1790 hints
.ai_family
= address_family
== AF_INET
? PF_INET
: PF_INET6
;
1792 hints
.ai_socktype
= SOCK_RAW
;
1793 if((error
= getaddrinfo(arg
, NULL
, &hints
, &res
)) != 0) {
1795 crash("Failed to resolve %s: %s", arg
, gai_strerror(error
));
1798 address_family
= res
->ai_family
;
1801 /* possibly add all the IP's as targets */
1802 for(p
= res
; p
!= NULL
; p
= p
->ai_next
) {
1803 memcpy(&ip
, p
->ai_addr
, p
->ai_addrlen
);
1804 add_target_ip(arg
, &ip
);
1806 /* this is silly, but it works */
1807 if(mode
== MODE_HOSTCHECK
|| mode
== MODE_ALL
) {
1808 if(debug
> 2) printf("mode: %d\n", mode
);
1819 set_source_ip(char *arg
)
1821 struct sockaddr_in src
;
1823 memset(&src
, 0, sizeof(src
));
1824 src
.sin_family
= address_family
;
1825 if((src
.sin_addr
.s_addr
= inet_addr(arg
)) == INADDR_NONE
)
1826 src
.sin_addr
.s_addr
= get_ip_address(arg
);
1827 if(bind(icmp_sock
, (struct sockaddr
*)&src
, sizeof(src
)) == -1)
1828 crash("Cannot bind to IP address %s", arg
);
1831 /* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1833 get_ip_address(const char *ifname
)
1835 // TODO: Rewrite this so the function return an error and we exit somewhere else
1836 struct sockaddr_in ip
;
1837 ip
.sin_addr
.s_addr
= 0; // Fake initialization to make compiler happy
1838 #if defined(SIOCGIFADDR)
1841 strncpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
) - 1);
1843 ifr
.ifr_name
[sizeof(ifr
.ifr_name
) - 1] = '\0';
1845 if(ioctl(icmp_sock
, SIOCGIFADDR
, &ifr
) == -1)
1846 crash("Cannot determine IP address of interface %s", ifname
);
1848 memcpy(&ip
, &ifr
.ifr_addr
, sizeof(ip
));
1852 crash("Cannot get interface IP address on this platform.");
1854 return ip
.sin_addr
.s_addr
;
1861 * return value is in microseconds
1864 get_timevar(const char *str
)
1868 u_int i
, d
; /* integer and decimal, respectively */
1869 u_int factor
= 1000; /* default to milliseconds */
1875 /* unit might be given as ms|m (millisec),
1876 * us|u (microsec) or just plain s, for seconds */
1879 if(len
>= 2 && !isdigit((int)str
[len
- 2])) p
= str
[len
- 2];
1880 if(p
&& u
== 's') u
= p
;
1882 if(debug
> 2) printf("evaluating %s, u: %c, p: %c\n", str
, u
, p
);
1884 if(u
== 'u') factor
= 1; /* microseconds */
1885 else if(u
== 'm') factor
= 1000; /* milliseconds */
1886 else if(u
== 's') factor
= 1000000; /* seconds */
1887 if(debug
> 2) printf("factor is %u\n", factor
);
1889 i
= strtoul(str
, &ptr
, 0);
1890 if(!ptr
|| *ptr
!= '.' || strlen(ptr
) < 2 || factor
== 1)
1893 /* time specified in usecs can't have decimal points, so ignore them */
1894 if(factor
== 1) return i
;
1896 d
= strtoul(ptr
+ 1, NULL
, 0);
1898 /* d is decimal, so get rid of excess digits */
1899 while(d
>= factor
) d
/= 10;
1901 /* the last parenthesis avoids floating point exceptions. */
1902 return ((i
* factor
) + (d
* (factor
/ 10)));
1905 /* not too good at checking errors, but it'll do (main() should barfe on -1) */
1907 get_threshold(char *str
, threshold
*th
)
1909 char *p
= NULL
, i
= 0;
1911 if(!str
|| !strlen(str
) || !th
) return -1;
1913 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1914 p
= &str
[strlen(str
) - 1];
1915 while(p
!= &str
[1]) {
1916 if(*p
== '%') *p
= '\0';
1917 else if(*p
== ',' && i
) {
1918 *p
= '\0'; /* reset it so get_timevar(str) works nicely later */
1919 th
->pl
= (unsigned char)strtoul(p
+1, NULL
, 0);
1925 th
->rta
= get_timevar(str
);
1927 if(!th
->rta
) return -1;
1929 if(th
->rta
> MAXTTL
* 1000000) th
->rta
= MAXTTL
* 1000000;
1930 if(th
->pl
> 100) th
->pl
= 100;
1936 * This functions receives a pointer to a string which should contain a threshold for the
1937 * rta, packet_loss, jitter, mos or score mode in the form number,number[m|%]* assigns the
1938 * parsed number to the corresponding threshold variable.
1939 * @param[in,out] str String containing the given threshold values
1940 * @param[in] length strlen(str)
1941 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
1942 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
1943 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively)
1945 static bool get_threshold2(char *str
, size_t length
, threshold
*warn
, threshold
*crit
, threshold_mode mode
) {
1946 if (!str
|| !length
|| !warn
|| !crit
) return false;
1949 // p points to the last char in str
1950 char *p
= &str
[length
- 1];
1952 // first_iteration is bof-stop on stupid libc's
1953 bool first_iteration
= true;
1955 while(p
!= &str
[0]) {
1956 if( (*p
== 'm') || (*p
== '%') ) {
1958 } else if(*p
== ',' && !first_iteration
) {
1959 *p
= '\0'; /* reset it so get_timevar(str) works nicely later */
1961 char *start_of_value
= p
+ 1;
1963 if (!parse_threshold2_helper(start_of_value
, strlen(start_of_value
), crit
, mode
)){
1968 first_iteration
= false;
1972 return parse_threshold2_helper(p
, strlen(p
), warn
, mode
);
1975 static bool parse_threshold2_helper(char *s
, size_t length
, threshold
*thr
, threshold_mode mode
) {
1976 char *resultChecker
= {0};
1979 case const_rta_mode
:
1980 thr
->rta
= strtod(s
, &resultChecker
) * 1000;
1982 case const_packet_loss_mode
:
1983 thr
->pl
= (unsigned char)strtoul(s
, &resultChecker
, 0);
1985 case const_jitter_mode
:
1986 thr
->jitter
= strtod(s
, &resultChecker
);
1989 case const_mos_mode
:
1990 thr
->mos
= strtod(s
, &resultChecker
);
1992 case const_score_mode
:
1993 thr
->score
= strtod(s
, &resultChecker
);
1997 if (resultChecker
== s
) {
2002 if (resultChecker
!= (s
+ length
)) {
2011 icmp_checksum(uint16_t *p
, size_t n
)
2013 unsigned short cksum
;
2016 /* sizeof(uint16_t) == 2 */
2022 /* mop up the occasional odd byte */
2023 if(n
== 1) sum
+= *((uint8_t *)p
-1);
2025 sum
= (sum
>> 16) + (sum
& 0xffff); /* add hi 16 to low 16 */
2026 sum
+= (sum
>> 16); /* add carry */
2027 cksum
= ~sum
; /* ones-complement, trunc to 16 bits */
2035 /*print_revision (progname);*/ /* FIXME: Why? */
2036 printf ("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2038 printf (COPYRIGHT
, copyright
, email
);
2044 printf (UT_HELP_VRSN
);
2045 printf (UT_EXTRA_OPTS
);
2047 printf (" %s\n", "-H");
2048 printf (" %s\n", _("specify a target"));
2049 printf (" %s\n", "[-4|-6]");
2050 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
2051 printf (" %s\n", "-w");
2052 printf (" %s", _("warning threshold (currently "));
2053 printf ("%0.3fms,%u%%)\n", (float)warn
.rta
/ 1000, warn
.pl
);
2054 printf (" %s\n", "-c");
2055 printf (" %s", _("critical threshold (currently "));
2056 printf ("%0.3fms,%u%%)\n", (float)crit
.rta
/ 1000, crit
.pl
);
2058 printf (" %s\n", "-R");
2059 printf (" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms"));
2060 printf (" %s\n", "-P");
2061 printf (" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2062 printf (" %s\n", "-J");
2063 printf (" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2064 printf (" %s\n", "-M");
2065 printf (" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2066 printf (" %s\n", "-S");
2067 printf (" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2068 printf (" %s\n", "-O");
2069 printf (" %s\n", _("detect out of order ICMP packts "));
2070 printf (" %s\n", "-H");
2071 printf (" %s\n", _("specify a target"));
2072 printf (" %s\n", "-s");
2073 printf (" %s\n", _("specify a source IP address or device name"));
2074 printf (" %s\n", "-n");
2075 printf (" %s", _("number of packets to send (currently "));
2076 printf ("%u)\n",packets
);
2077 printf (" %s\n", "-p");
2078 printf (" %s", _("number of packets to send (currently "));
2079 printf ("%u)\n",packets
);
2080 printf (" %s\n", "-i");
2081 printf (" %s", _("max packet interval (currently "));
2082 printf ("%0.3fms)\n",(float)pkt_interval
/ 1000);
2083 printf (" %s\n", "-I");
2084 printf (" %s", _("max target interval (currently "));
2085 printf ("%0.3fms)\n", (float)target_interval
/ 1000);
2086 printf (" %s\n", "-m");
2087 printf (" %s",_("number of alive hosts required for success"));
2089 printf (" %s\n", "-l");
2090 printf (" %s", _("TTL on outgoing packets (currently "));
2091 printf ("%u)\n", ttl
);
2092 printf (" %s\n", "-t");
2093 printf (" %s",_("timeout value (seconds, currently "));
2094 printf ("%u)\n", timeout
);
2095 printf (" %s\n", "-b");
2096 printf (" %s\n", _("Number of icmp data bytes to send"));
2097 printf (" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"),icmp_data_size
, ICMP_MINLEN
);
2098 printf (" %s\n", "-v");
2099 printf (" %s\n", _("verbose"));
2101 printf ("%s\n", _("Notes:"));
2102 printf (" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2103 printf (" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not."));
2105 printf (" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2106 printf (" %s\n", _("packet loss. The default values should work well for most users."));
2107 printf (" %s\n", _("You can specify different RTA factors using the standardized abbreviations"));
2108 printf (" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2109 /* -d not yet implemented */
2110 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops"));
2111 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2112 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2114 printf (" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2115 /* printf ("%s\n", _("Long options are currently unsupported."));
2116 printf ("%s\n", _("Options marked with * require an argument"));
2119 printf (UT_SUPPORT
);
2127 printf ("%s\n", _("Usage:"));
2128 printf(" %s [options] [-H] host1 host2 hostN\n", progname
);