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 */
51 #include <sys/sockio.h>
53 #include <sys/ioctl.h>
55 #include <sys/types.h>
65 #include <sys/socket.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in.h>
69 #include <netinet/ip.h>
70 #include <netinet/ip6.h>
71 #include <netinet/ip_icmp.h>
72 #include <netinet/icmp6.h>
73 #include <arpa/inet.h>
78 /** sometimes undefined system macros (quite a few, actually) **/
83 # define INADDR_NONE (in_addr_t)(-1)
90 /* we bundle these in one #ifndef, since they're all from BSD
91 * Put individual #ifndef's around those that bother you */
92 #ifndef ICMP_UNREACH_NET_UNKNOWN
93 # define ICMP_UNREACH_NET_UNKNOWN 6
94 # define ICMP_UNREACH_HOST_UNKNOWN 7
95 # define ICMP_UNREACH_ISOLATED 8
96 # define ICMP_UNREACH_NET_PROHIB 9
97 # define ICMP_UNREACH_HOST_PROHIB 10
98 # define ICMP_UNREACH_TOSNET 11
99 # define ICMP_UNREACH_TOSHOST 12
101 /* tru64 has the ones above, but not these */
102 #ifndef ICMP_UNREACH_FILTER_PROHIB
103 # define ICMP_UNREACH_FILTER_PROHIB 13
104 # define ICMP_UNREACH_HOST_PRECEDENCE 14
105 # define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
109 # define DBL_MAX 9.9999999999e999
112 typedef unsigned short range_t
; /* type for get_range() -- unimplemented */
114 typedef struct rta_host
{
115 unsigned short id
; /* id in **table, and icmp pkts */
116 char *name
; /* arg used for adding this host */
117 char *msg
; /* icmp error message, if any */
118 struct sockaddr_storage saddr_in
; /* the address of this host */
119 struct sockaddr_storage error_addr
; /* stores address of error replies */
120 unsigned long long time_waited
; /* total time waited, in usecs */
121 unsigned int icmp_sent
, icmp_recv
, icmp_lost
; /* counters */
122 unsigned char icmp_type
, icmp_code
; /* type and code from errors */
123 unsigned short flags
; /* control/status flags */
124 double rta
; /* measured RTA */
125 double rtmax
; /* max rtt */
126 double rtmin
; /* min rtt */
127 unsigned char pl
; /* measured packet loss */
128 struct rta_host
*next
; /* linked list */
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 */
139 /* the data structure */
140 typedef struct icmp_ping_data
{
141 struct timeval stime
; /* timestamp (saved in protocol struct as well) */
142 unsigned short ping_id
;
145 typedef union ip_hdr
{
150 typedef union icmp_packet
{
153 struct icmp6_hdr
*icp6
;
157 /* the different modes of this program are as follows:
158 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
159 * MODE_HOSTCHECK: Return immediately upon any sign of life
160 * In addition, sends packets to ALL addresses assigned
161 * to this host (as returned by gethostbyname() or
162 * gethostbyaddr() and expects one host only to be checked at
163 * a time. Therefore, any packet response what so ever will
164 * count as a sign of life, even when received outside
165 * crit.rta limit. Do not misspell any additional IP's.
166 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
167 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
168 * tcp and udp args does this)
171 #define MODE_HOSTCHECK 1
175 /* the different ping types we can do
176 * TODO: investigate ARP ping as well */
182 #define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
183 #define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
184 #define IP_HDR_SIZE 20
185 #define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
186 #define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
188 /* various target states */
189 #define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
190 #define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
191 #define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
192 #define TSTATE_UNREACH 0x08
195 void print_help (void);
196 void print_usage (void);
197 static u_int
get_timevar(const char *);
198 static u_int
get_timevaldiff(struct timeval
*, struct timeval
*);
199 static in_addr_t
get_ip_address(const char *);
200 static int wait_for_reply(int, u_int
);
201 static int recvfrom_wto(int, void *, unsigned int, struct sockaddr
*, u_int
*, struct timeval
*);
202 static int send_icmp_ping(int, struct rta_host
*);
203 static int get_threshold(char *str
, threshold
*th
);
204 static void run_checks(void);
205 static void set_source_ip(char *);
206 static int add_target(char *);
207 static int add_target_ip(char *, struct sockaddr_storage
*);
208 static int handle_random_icmp(unsigned char *, struct sockaddr_storage
*);
209 static void parse_address(struct sockaddr_storage
*, char *, int);
210 static unsigned short icmp_checksum(unsigned short *, int);
211 static void finish(int);
212 static void crash(const char *, ...);
215 extern int optind
, opterr
, optopt
;
217 extern char **environ
;
219 /** global variables **/
220 static struct rta_host
**table
, *cursor
, *list
;
221 static threshold crit
= {80, 500000}, warn
= {40, 200000};
222 static int mode
, protocols
, sockets
, debug
= 0, timeout
= 10;
223 static unsigned short icmp_data_size
= DEFAULT_PING_DATA_SIZE
;
224 static unsigned short icmp_pkt_size
= DEFAULT_PING_DATA_SIZE
+ ICMP_MINLEN
;
226 static unsigned int icmp_sent
= 0, icmp_recv
= 0, icmp_lost
= 0;
227 #define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
228 static unsigned short targets_down
= 0, targets
= 0, packets
= 0;
229 #define targets_alive (targets - targets_down)
230 static unsigned int retry_interval
, pkt_interval
, target_interval
;
231 static int icmp_sock
, tcp_sock
, udp_sock
, status
= STATE_OK
;
233 static struct timezone tz
;
234 static struct timeval prog_start
;
235 static unsigned long long max_completion_time
= 0;
236 static unsigned char ttl
= 0; /* outgoing ttl */
237 static unsigned int warn_down
= 1, crit_down
= 1; /* host down threshold values */
238 static int min_hosts_alive
= -1;
239 float pkt_backoff_factor
= 1.5;
240 float target_backoff_factor
= 1.5;
244 crash(const char *fmt
, ...)
248 printf("%s: ", progname
);
254 if(errno
) printf(": %s", strerror(errno
));
262 get_icmp_error_msg(unsigned char icmp_type
, unsigned char icmp_code
)
264 const char *msg
= "unreachable";
266 if(debug
> 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type
, icmp_code
);
270 case ICMP_UNREACH_NET
: msg
= "Net unreachable"; break;
271 case ICMP_UNREACH_HOST
: msg
= "Host unreachable"; break;
272 case ICMP_UNREACH_PROTOCOL
: msg
= "Protocol unreachable (firewall?)"; break;
273 case ICMP_UNREACH_PORT
: msg
= "Port unreachable (firewall?)"; break;
274 case ICMP_UNREACH_NEEDFRAG
: msg
= "Fragmentation needed"; break;
275 case ICMP_UNREACH_SRCFAIL
: msg
= "Source route failed"; break;
276 case ICMP_UNREACH_ISOLATED
: msg
= "Source host isolated"; break;
277 case ICMP_UNREACH_NET_UNKNOWN
: msg
= "Unknown network"; break;
278 case ICMP_UNREACH_HOST_UNKNOWN
: msg
= "Unknown host"; break;
279 case ICMP_UNREACH_NET_PROHIB
: msg
= "Network denied (firewall?)"; break;
280 case ICMP_UNREACH_HOST_PROHIB
: msg
= "Host denied (firewall?)"; break;
281 case ICMP_UNREACH_TOSNET
: msg
= "Bad TOS for network (firewall?)"; break;
282 case ICMP_UNREACH_TOSHOST
: msg
= "Bad TOS for host (firewall?)"; break;
283 case ICMP_UNREACH_FILTER_PROHIB
: msg
= "Prohibited by filter (firewall)"; break;
284 case ICMP_UNREACH_HOST_PRECEDENCE
: msg
= "Host precedence violation"; break;
285 case ICMP_UNREACH_PRECEDENCE_CUTOFF
: msg
= "Precedence cutoff"; break;
286 default: msg
= "Invalid code"; break;
291 /* really 'out of reach', or non-existant host behind a router serving
292 * two different subnets */
294 case ICMP_TIMXCEED_INTRANS
: msg
= "Time to live exceeded in transit"; break;
295 case ICMP_TIMXCEED_REASS
: msg
= "Fragment reassembly time exceeded"; break;
296 default: msg
= "Invalid code"; break;
300 case ICMP_SOURCEQUENCH
: msg
= "Transmitting too fast"; break;
301 case ICMP_REDIRECT
: msg
= "Redirect (change route)"; break;
302 case ICMP_PARAMPROB
: msg
= "Bad IP header (required option absent)"; break;
304 /* the following aren't error messages, so ignore */
306 case ICMP_TSTAMPREPLY
:
311 default: msg
= ""; break;
318 handle_random_icmp(unsigned char *packet
, struct sockaddr_storage
*addr
)
320 struct icmp p
, sent_icmp
;
321 struct rta_host
*host
= NULL
;
323 memcpy(&p
, packet
, sizeof(p
));
324 if(p
.icmp_type
== ICMP_ECHO
&& ntohs(p
.icmp_id
) == pid
) {
325 /* echo request from us to us (pinging localhost) */
329 if(debug
) printf("handle_random_icmp(%p, %p)\n", (void *)&p
, (void *)addr
);
331 /* only handle a few types, since others can't possibly be replies to
332 * us in a sane network (if it is anyway, it will be counted as lost
333 * at summary time, but not as quickly as a proper response */
334 /* TIMXCEED can be an unreach from a router with multiple IP's which
335 * serves two different subnets on the same interface and a dead host
336 * on one net is pinged from the other. The router will respond to
337 * itself and thus set TTL=0 so as to not loop forever. Even when
338 * TIMXCEED actually sends a proper icmp response we will have passed
339 * too many hops to have a hope of reaching it later, in which case it
340 * indicates overconfidence in the network, poor routing or both. */
341 if(p
.icmp_type
!= ICMP_UNREACH
&& p
.icmp_type
!= ICMP_TIMXCEED
&&
342 p
.icmp_type
!= ICMP_SOURCEQUENCH
&& p
.icmp_type
!= ICMP_PARAMPROB
)
347 /* might be for us. At least it holds the original package (according
348 * to RFC 792). If it isn't, just ignore it */
349 memcpy(&sent_icmp
, packet
+ 28, sizeof(sent_icmp
));
350 if(sent_icmp
.icmp_type
!= ICMP_ECHO
|| ntohs(sent_icmp
.icmp_id
) != pid
||
351 ntohs(sent_icmp
.icmp_seq
) >= targets
*packets
)
353 if(debug
) printf("Packet is no response to a packet we sent\n");
357 /* it is indeed a response for us */
358 host
= table
[ntohs(sent_icmp
.icmp_seq
)/packets
];
360 char address
[INET6_ADDRSTRLEN
];
361 parse_address(addr
, address
, sizeof(address
));
362 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
363 get_icmp_error_msg(p
.icmp_type
, p
.icmp_code
),
364 address
, host
->name
);
369 /* don't spend time on lost hosts any more */
370 if(host
->flags
& FLAG_LOST_CAUSE
) return 0;
372 /* source quench means we're sending too fast, so increase the
373 * interval and mark this packet lost */
374 if(p
.icmp_type
== ICMP_SOURCEQUENCH
) {
375 pkt_interval
*= pkt_backoff_factor
;
376 target_interval
*= target_backoff_factor
;
380 host
->flags
|= FLAG_LOST_CAUSE
;
382 host
->icmp_type
= p
.icmp_type
;
383 host
->icmp_code
= p
.icmp_code
;
384 host
->error_addr
= *addr
;
389 void parse_address(struct sockaddr_storage
*addr
, char *address
, int size
)
391 switch (address_family
) {
393 inet_ntop(address_family
, &((struct sockaddr_in
*)addr
)->sin_addr
, address
, size
);
396 inet_ntop(address_family
, &((struct sockaddr_in6
*)addr
)->sin6_addr
, address
, size
);
402 main(int argc
, char **argv
)
407 int icmp_sockerrno
, udp_sockerrno
, tcp_sockerrno
;
409 struct rta_host
*host
;
413 char * opts_str
= "vhVw:c:n:p:t:H:s:i:b:I:l:m:64";
415 setlocale (LC_ALL
, "");
416 bindtextdomain (PACKAGE
, LOCALEDIR
);
417 textdomain (PACKAGE
);
419 /* we only need to be setsuid when we get the sockets, so do
420 * that before pointer magic (esp. on network data) */
421 icmp_sockerrno
= udp_sockerrno
= tcp_sockerrno
= sockets
= 0;
424 int icmp_proto
= IPPROTO_ICMP
;
426 /* get calling name the old-fashioned way for portability instead
427 * of relying on the glibc-ism __progname */
428 ptr
= strrchr(argv
[0], '/');
429 if(ptr
) progname
= &ptr
[1];
430 else progname
= argv
[0];
432 /* now set defaults. Use progname to set them initially (allows for
433 * superfast check_host program when target host is up */
434 cursor
= list
= NULL
;
442 protocols
= HAVE_ICMP
| HAVE_UDP
| HAVE_TCP
;
443 pkt_interval
= 80000; /* 80 msec packet interval by default */
446 if(!strcmp(progname
, "check_icmp") || !strcmp(progname
, "check_ping")) {
448 protocols
= HAVE_ICMP
;
450 else if(!strcmp(progname
, "check_host")) {
451 mode
= MODE_HOSTCHECK
;
452 pkt_interval
= 1000000;
454 crit
.rta
= warn
.rta
= 1000000;
455 crit
.pl
= warn
.pl
= 100;
457 else if(!strcmp(progname
, "check_rta_multi")) {
460 pkt_interval
= 50000;
464 /* Parse protocol arguments first */
465 for(i
= 1; i
< argc
; i
++) {
466 while((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
470 if (address_family
!= -1)
471 crash("Multiple protocol versions not supported");
472 address_family
= AF_INET
;
476 if (address_family
!= -1)
477 crash("Multiple protocol versions not supported");
478 address_family
= AF_INET6
;
480 usage (_("IPv6 support not available\n"));
487 /* Reset argument scanning */
490 /* parse the arguments */
491 for(i
= 1; i
< argc
; i
++) {
492 while((arg
= getopt(argc
, argv
, opts_str
)) != EOF
) {
499 size
= (unsigned short)strtol(optarg
,NULL
,0);
500 if (size
>= (sizeof(struct icmp
) + sizeof(struct icmp_ping_data
)) &&
501 size
< MAX_PING_DATA
) {
502 icmp_data_size
= size
;
503 icmp_pkt_size
= size
+ ICMP_MINLEN
;
505 usage_va("ICMP data length must be between: %d and %d",
506 sizeof(struct icmp
) + sizeof(struct icmp_ping_data
),
510 pkt_interval
= get_timevar(optarg
);
513 target_interval
= get_timevar(optarg
);
516 get_threshold(optarg
, &warn
);
519 get_threshold(optarg
, &crit
);
523 packets
= strtoul(optarg
, NULL
, 0);
526 timeout
= strtoul(optarg
, NULL
, 0);
527 if(!timeout
) timeout
= 10;
533 ttl
= (unsigned char)strtoul(optarg
, NULL
, 0);
536 min_hosts_alive
= (int)strtoul(optarg
, NULL
, 0);
538 case 'd': /* implement later, for cluster checks */
539 warn_down
= (unsigned char)strtoul(optarg
, &ptr
, 0);
541 crit_down
= (unsigned char)strtoul(ptr
+ 1, NULL
, 0);
544 case 's': /* specify source IP address */
545 set_source_ip(optarg
);
547 case 'V': /* version */
548 print_revision (progname
, NP_VERSION
);
549 exit (STATE_UNKNOWN
);
552 exit (STATE_UNKNOWN
);
558 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
561 /* use the pid to mark packets as ours */
562 /* Some systems have 32-bit pid_t so mask off only 16 bits */
563 pid
= getpid() & 0xffff;
564 /* printf("pid = %u\n", pid); */
566 /* Parse extra opts if any */
567 argv
=np_extra_opts(&argc
, argv
, progname
);
569 /* support "--help" and "--version" */
571 if(!strcmp(argv
[1], "--help"))
572 strcpy(argv
[1], "-h");
573 if(!strcmp(argv
[1], "--version"))
574 strcpy(argv
[1], "-V");
577 argv
= &argv
[optind
];
584 crash("No hosts to check");
588 // add_target might change address_family
589 switch ( address_family
){
590 case AF_INET
: icmp_proto
= IPPROTO_ICMP
;
592 case AF_INET6
: icmp_proto
= IPPROTO_ICMPV6
;
594 default: crash("Address family not supported");
596 if((icmp_sock
= socket(address_family
, SOCK_RAW
, icmp_proto
)) != -1)
597 sockets
|= HAVE_ICMP
;
598 else icmp_sockerrno
= errno
;
602 if(setsockopt(icmp_sock
, SOL_SOCKET
, SO_TIMESTAMP
, &on
, sizeof(on
)))
603 if(debug
) printf("Warning: no SO_TIMESTAMP support\n");
604 #endif // SO_TIMESTAMP
606 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
607 if (setuid(getuid()) == -1) {
608 printf("ERROR: Failed to drop privileges\n");
613 if(icmp_sock
== -1) {
614 errno
= icmp_sockerrno
;
615 crash("Failed to obtain ICMP socket");
618 /* if(udp_sock == -1) { */
619 /* errno = icmp_sockerrno; */
620 /* crash("Failed to obtain UDP socket"); */
623 /* if(tcp_sock == -1) { */
624 /* errno = icmp_sockerrno; */
625 /* crash("Failed to obtain TCP socker"); */
632 result
= setsockopt(icmp_sock
, SOL_IP
, IP_TTL
, &ttl
, sizeof(ttl
));
634 if(result
== -1) printf("setsockopt failed\n");
635 else printf("ttl set to %u\n", ttl
);
639 /* stupid users should be able to give whatever thresholds they want
640 * (nothing will break if they do), but some anal plugin maintainer
641 * will probably add some printf() thing here later, so it might be
642 * best to at least show them where to do it. ;) */
643 if(warn
.pl
> crit
.pl
) warn
.pl
= crit
.pl
;
644 if(warn
.rta
> crit
.rta
) warn
.rta
= crit
.rta
;
645 if(warn_down
> crit_down
) crit_down
= warn_down
;
647 signal(SIGINT
, finish
);
648 signal(SIGHUP
, finish
);
649 signal(SIGTERM
, finish
);
650 signal(SIGALRM
, finish
);
651 if(debug
) printf("Setting alarm timeout to %u seconds\n", timeout
);
654 /* make sure we don't wait any longer than necessary */
655 gettimeofday(&prog_start
, &tz
);
656 max_completion_time
=
657 ((targets
* packets
* pkt_interval
) + (targets
* target_interval
)) +
658 (targets
* packets
* crit
.rta
) + crit
.rta
;
661 printf("packets: %u, targets: %u\n"
662 "target_interval: %0.3f, pkt_interval %0.3f\n"
664 "max_completion_time: %0.3f\n",
666 (float)target_interval
/ 1000, (float)pkt_interval
/ 1000,
667 (float)crit
.rta
/ 1000,
668 (float)max_completion_time
/ 1000);
672 if(max_completion_time
> (u_int
)timeout
* 1000000) {
673 printf("max_completion_time: %llu timeout: %u\n",
674 max_completion_time
, timeout
);
675 printf("Timeout must be at least %llu\n",
676 max_completion_time
/ 1000000 + 1);
681 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
682 crit
.rta
, crit
.pl
, warn
.rta
, warn
.pl
);
683 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
684 pkt_interval
, target_interval
, retry_interval
);
685 printf("icmp_pkt_size: %u timeout: %u\n",
686 icmp_pkt_size
, timeout
);
691 crash("packets is > 20 (%d)", packets
);
694 if(min_hosts_alive
< -1) {
696 crash("minimum alive hosts is negative (%i)", min_hosts_alive
);
700 table
= (struct rta_host
**)malloc(sizeof(struct rta_host
**) * targets
);
703 host
->id
= i
*packets
;
721 u_int final_wait
, time_passed
;
723 /* this loop might actually violate the pkt_interval or target_interval
724 * settings, but only if there aren't any packets on the wire which
725 * indicates that the target can handle an increased packet rate */
726 for(i
= 0; i
< packets
; i
++) {
727 for(t
= 0; t
< targets
; t
++) {
728 /* don't send useless packets */
729 if(!targets_alive
) finish(0);
730 if(table
[t
]->flags
& FLAG_LOST_CAUSE
) {
731 if(debug
) printf("%s is a lost cause. not sending any more\n",
736 /* we're still in the game, so send next packet */
737 (void)send_icmp_ping(icmp_sock
, table
[t
]);
738 result
= wait_for_reply(icmp_sock
, target_interval
);
740 result
= wait_for_reply(icmp_sock
, pkt_interval
* targets
);
743 if(icmp_pkts_en_route
&& targets_alive
) {
744 time_passed
= get_timevaldiff(NULL
, NULL
);
745 final_wait
= max_completion_time
- time_passed
;
748 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
749 time_passed
, final_wait
, max_completion_time
);
751 if(time_passed
> max_completion_time
) {
752 if(debug
) printf("Time passed. Finishing up\n");
756 /* catch the packets that might come in within the timeframe, but
758 if(debug
) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
759 final_wait
, (float)final_wait
/ 1000);
760 result
= wait_for_reply(icmp_sock
, final_wait
);
765 /* response structure:
767 * ip header : 20 bytes
768 * icmp header : 28 bytes
770 * ip header : 40 bytes
771 * icmp header : 28 bytes
773 * icmp echo reply : the rest
776 wait_for_reply(int sock
, u_int t
)
779 static unsigned char buf
[4096];
780 struct sockaddr_storage resp_addr
;
782 union icmp_packet packet
;
783 struct rta_host
*host
;
784 struct icmp_ping_data data
;
785 struct timeval wait_start
, now
;
786 u_int tdiff
, i
, per_pkt_wait
;
788 if (!(packet
.buf
= malloc(icmp_pkt_size
))) {
789 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
791 return -1; /* might be reached if we're in debug mode */
794 memset(packet
.buf
, 0, icmp_pkt_size
);
796 /* if we can't listen or don't have anything to listen to, just return */
797 if(!t
|| !icmp_pkts_en_route
) {
802 gettimeofday(&wait_start
, &tz
);
805 per_pkt_wait
= t
/ icmp_pkts_en_route
;
806 while(icmp_pkts_en_route
&& get_timevaldiff(&wait_start
, NULL
) < i
) {
809 /* wrap up if all targets are declared dead */
811 get_timevaldiff(&prog_start
, NULL
) >= max_completion_time
||
812 (mode
== MODE_HOSTCHECK
&& targets_down
))
817 /* reap responses until we hit a timeout */
818 n
= recvfrom_wto(sock
, buf
, sizeof(buf
),
819 (struct sockaddr
*)&resp_addr
, &t
, &now
);
822 printf("recvfrom_wto() timed out during a %u usecs wait\n",
825 continue; /* timeout for this one, so keep trying */
828 if(debug
) printf("recvfrom_wto() returned errors\n");
833 // FIXME: with ipv6 we don't have an ip header here
834 if (address_family
!= AF_INET6
) {
835 ip
= (union ip_hdr
*)buf
;
838 char address
[INET6_ADDRSTRLEN
];
839 parse_address(&resp_addr
, address
, sizeof(address
));
840 printf("received %u bytes from %s\n",
841 address_family
== AF_INET6
? ntohs(ip
->ip6
.ip6_plen
)
842 : ntohs(ip
->ip
.ip_len
),
847 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
848 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
849 /* alpha headers are decidedly broken. Using an ansi compiler,
850 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
851 * off the bottom 4 bits */
852 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
854 hlen
= (address_family
== AF_INET6
) ? 0 : ip
->ip
.ip_hl
<< 2;
857 if(n
< (hlen
+ ICMP_MINLEN
)) {
858 char address
[INET6_ADDRSTRLEN
];
859 parse_address(&resp_addr
, address
, sizeof(address
));
860 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
861 n
, hlen
+ icmp_pkt_size
, address
);
863 /* else if(debug) { */
864 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
865 /* hlen, ntohs(ip->ip_len) - hlen, */
866 /* sizeof(struct ip), icmp_pkt_size); */
869 /* check the response */
871 memcpy(packet
.buf
, buf
+ hlen
, icmp_pkt_size
);
872 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
873 : sizeof(struct icmp));*/
875 if( (address_family
== PF_INET
&&
876 (ntohs(packet
.icp
->icmp_id
) != pid
|| packet
.icp
->icmp_type
!= ICMP_ECHOREPLY
877 || ntohs(packet
.icp
->icmp_seq
) >= targets
* packets
))
878 || (address_family
== PF_INET6
&&
879 (ntohs(packet
.icp6
->icmp6_id
) != pid
|| packet
.icp6
->icmp6_type
!= ICMP6_ECHO_REPLY
880 || ntohs(packet
.icp6
->icmp6_seq
) >= targets
* packets
))) {
881 if(debug
> 2) printf("not a proper ICMP_ECHOREPLY\n");
882 handle_random_icmp(buf
+ hlen
, &resp_addr
);
886 /* this is indeed a valid response */
887 if (address_family
== PF_INET
) {
888 memcpy(&data
, packet
.icp
->icmp_data
, sizeof(data
));
890 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
891 (unsigned long)sizeof(data
), ntohs(packet
.icp
->icmp_id
),
892 ntohs(packet
.icp
->icmp_seq
), packet
.icp
->icmp_cksum
);
893 host
= table
[ntohs(packet
.icp
->icmp_seq
)/packets
];
895 memcpy(&data
, &packet
.icp6
->icmp6_dataun
.icmp6_un_data8
[4], sizeof(data
));
897 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n",
898 (unsigned long)sizeof(data
), ntohs(packet
.icp6
->icmp6_id
),
899 ntohs(packet
.icp6
->icmp6_seq
), packet
.icp6
->icmp6_cksum
);
900 host
= table
[ntohs(packet
.icp6
->icmp6_seq
)/packets
];
903 tdiff
= get_timevaldiff(&data
.stime
, &now
);
905 host
->time_waited
+= tdiff
;
908 if (tdiff
> host
->rtmax
)
910 if (tdiff
< host
->rtmin
)
914 char address
[INET6_ADDRSTRLEN
];
915 parse_address(&resp_addr
, address
, sizeof(address
));
916 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
917 (float)tdiff
/ 1000, address
,
918 ttl
, ip
->ip
.ip_ttl
, (float)host
->rtmax
/ 1000, (float)host
->rtmin
/ 1000);
921 /* if we're in hostcheck mode, exit with limited printouts */
922 if(mode
== MODE_HOSTCHECK
) {
923 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
924 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
925 host
->name
, icmp_recv
, (float)tdiff
/ 1000,
926 icmp_recv
, packets
, (float)tdiff
/ 1000,
927 (float)warn
.rta
/ 1000, (float)crit
.rta
/ 1000);
936 /* the ping functions */
938 send_icmp_ping(int sock
, struct rta_host
*host
)
941 struct icmp_ping_data data
;
949 crash("Attempt to send on bogus socket");
954 if (!(buf
= malloc(icmp_pkt_size
))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
957 return -1; /* might be reached if we're in debug mode */
960 memset(buf
, 0, icmp_pkt_size
);
962 if((gettimeofday(&tv
, &tz
)) == -1) {
967 data
.ping_id
= 10; /* host->icmp.icmp_sent; */
968 memcpy(&data
.stime
, &tv
, sizeof(tv
));
970 if (address_family
== AF_INET
) {
971 struct icmp
*icp
= (struct icmp
*)buf
;
973 memcpy(&icp
->icmp_data
, &data
, sizeof(data
));
975 icp
->icmp_type
= ICMP_ECHO
;
978 icp
->icmp_id
= htons(pid
);
979 icp
->icmp_seq
= htons(host
->id
++);
980 icp
->icmp_cksum
= icmp_checksum((unsigned short*)buf
, icmp_pkt_size
);
983 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
984 (unsigned long)sizeof(data
), ntohs(icp
->icmp_id
), ntohs(icp
->icmp_seq
), icp
->icmp_cksum
, host
->name
);
987 struct icmp6_hdr
*icp6
= (struct icmp6_hdr
*)buf
;
988 memcpy(&icp6
->icmp6_dataun
.icmp6_un_data8
[4], &data
, sizeof(data
));
989 icp6
->icmp6_type
= ICMP6_ECHO_REQUEST
;
990 icp6
->icmp6_code
= 0;
991 icp6
->icmp6_cksum
= 0;
992 icp6
->icmp6_id
= htons(pid
);
993 icp6
->icmp6_seq
= htons(host
->id
++);
994 // let checksum be calculated automatically
997 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
998 (unsigned long)sizeof(data
), ntohs(icp6
->icmp6_id
),
999 ntohs(icp6
->icmp6_seq
), icp6
->icmp6_cksum
, host
->name
);
1003 memset(&iov
, 0, sizeof(iov
));
1005 iov
.iov_len
= icmp_pkt_size
;
1007 memset(&hdr
, 0, sizeof(hdr
));
1008 hdr
.msg_name
= (struct sockaddr
*)&host
->saddr_in
;
1009 hdr
.msg_namelen
= sizeof(struct sockaddr_storage
);
1015 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1017 len
= sendmsg(sock
, &hdr
, MSG_CONFIRM
);
1019 len
= sendmsg(sock
, &hdr
, 0);
1024 if(len
< 0 || (unsigned int)len
!= icmp_pkt_size
) {
1026 char address
[INET6_ADDRSTRLEN
];
1027 parse_address((struct sockaddr_storage
*)&host
->saddr_in
, address
, sizeof(address
));
1028 printf("Failed to send ping to %s: %s\n", address
, strerror(errno
));
1041 recvfrom_wto(int sock
, void *buf
, unsigned int len
, struct sockaddr
*saddr
,
1042 u_int
*timo
, struct timeval
* tv
)
1046 struct timeval to
, then
, now
;
1048 char ans_data
[4096];
1052 struct cmsghdr
* chdr
;
1056 if(debug
) printf("*timo is not\n");
1060 to
.tv_sec
= *timo
/ 1000000;
1061 to
.tv_usec
= (*timo
- (to
.tv_sec
* 1000000));
1067 gettimeofday(&then
, &tz
);
1068 n
= select(sock
+ 1, &rd
, &wr
, NULL
, &to
);
1069 if(n
< 0) crash("select() in recvfrom_wto");
1070 gettimeofday(&now
, &tz
);
1071 *timo
= get_timevaldiff(&then
, &now
);
1073 if(!n
) return 0; /* timeout */
1075 slen
= sizeof(struct sockaddr_storage
);
1077 memset(&iov
, 0, sizeof(iov
));
1081 memset(&hdr
, 0, sizeof(hdr
));
1082 hdr
.msg_name
= saddr
;
1083 hdr
.msg_namelen
= slen
;
1086 hdr
.msg_control
= ans_data
;
1087 hdr
.msg_controllen
= sizeof(ans_data
);
1089 ret
= recvmsg(sock
, &hdr
, 0);
1091 for(chdr
= CMSG_FIRSTHDR(&hdr
); chdr
; chdr
= CMSG_NXTHDR(&hdr
, chdr
)) {
1092 if(chdr
->cmsg_level
== SOL_SOCKET
1093 && chdr
->cmsg_type
== SO_TIMESTAMP
1094 && chdr
->cmsg_len
>= CMSG_LEN(sizeof(struct timeval
))) {
1095 memcpy(tv
, CMSG_DATA(chdr
), sizeof(*tv
));
1101 #endif // SO_TIMESTAMP
1102 gettimeofday(tv
, &tz
);
1112 struct rta_host
*host
;
1113 const char *status_string
[] =
1114 {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1119 if(debug
> 1) printf("finish(%d) called\n", sig
);
1121 if(icmp_sock
!= -1) close(icmp_sock
);
1122 if(udp_sock
!= -1) close(udp_sock
);
1123 if(tcp_sock
!= -1) close(tcp_sock
);
1126 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
1127 icmp_sent
, icmp_recv
, icmp_lost
);
1128 printf("targets: %u targets_alive: %u\n", targets
, targets_alive
);
1131 /* iterate thrice to calculate values, give output, and print perfparse */
1135 if(!host
->icmp_recv
) {
1136 /* rta 0 is ofcourse not entirely correct, but will still show up
1137 * conspicuously as missing entries in perfparse and cacti */
1140 status
= STATE_CRITICAL
;
1141 /* up the down counter if not already counted */
1142 if(!(host
->flags
& FLAG_LOST_CAUSE
) && targets_alive
) targets_down
++;
1145 pl
= ((host
->icmp_sent
- host
->icmp_recv
) * 100) / host
->icmp_sent
;
1146 rta
= (double)host
->time_waited
/ host
->icmp_recv
;
1150 if(pl
>= crit
.pl
|| rta
>= crit
.rta
) {
1151 status
= STATE_CRITICAL
;
1153 else if(!status
&& (pl
>= warn
.pl
|| rta
>= warn
.rta
)) {
1154 status
= STATE_WARNING
;
1163 /* this is inevitable */
1164 if(!targets_alive
) status
= STATE_CRITICAL
;
1165 if(min_hosts_alive
> -1) {
1166 if(hosts_ok
>= min_hosts_alive
) status
= STATE_OK
;
1167 else if((hosts_ok
+ hosts_warn
) >= min_hosts_alive
) status
= STATE_WARNING
;
1169 printf("%s - ", status_string
[status
]);
1175 if(i
< targets
) printf(" :: ");
1179 if(!host
->icmp_recv
) {
1180 status
= STATE_CRITICAL
;
1181 if(host
->flags
& FLAG_LOST_CAUSE
) {
1182 char address
[INET6_ADDRSTRLEN
];
1183 parse_address(&host
->error_addr
, address
, sizeof(address
));
1184 printf("%s: %s @ %s. rta nan, lost %d%%",
1186 get_icmp_error_msg(host
->icmp_type
, host
->icmp_code
),
1190 else { /* not marked as lost cause, so we have no flags for it */
1191 printf("%s: rta nan, lost 100%%", host
->name
);
1194 else { /* !icmp_recv */
1195 printf("%s: rta %0.3fms, lost %u%%",
1196 host
->name
, host
->rta
/ 1000, host
->pl
);
1202 /* iterate once more for pretty perfparse output */
1208 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ",
1209 (targets
> 1) ? host
->name
: "",
1210 host
->rta
/ 1000, (float)warn
.rta
/ 1000, (float)crit
.rta
/ 1000,
1211 (targets
> 1) ? host
->name
: "", host
->pl
, warn
.pl
, crit
.pl
,
1212 (targets
> 1) ? host
->name
: "", (float)host
->rtmax
/ 1000,
1213 (targets
> 1) ? host
->name
: "", (host
->rtmin
< DBL_MAX
) ? (float)host
->rtmin
/ 1000 : (float)0);
1218 if(min_hosts_alive
> -1) {
1219 if(hosts_ok
>= min_hosts_alive
) status
= STATE_OK
;
1220 else if((hosts_ok
+ hosts_warn
) >= min_hosts_alive
) status
= STATE_WARNING
;
1223 /* finish with an empty line */
1225 if(debug
) printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1226 targets
, targets_alive
, hosts_ok
, hosts_warn
, min_hosts_alive
);
1232 get_timevaldiff(struct timeval
*early
, struct timeval
*later
)
1238 gettimeofday(&now
, &tz
);
1241 if(!early
) early
= &prog_start
;
1243 /* if early > later we return 0 so as to indicate a timeout */
1244 if(early
->tv_sec
> later
->tv_sec
||
1245 (early
->tv_sec
== later
->tv_sec
&& early
->tv_usec
> later
->tv_usec
))
1249 ret
= (later
->tv_sec
- early
->tv_sec
) * 1000000;
1250 ret
+= later
->tv_usec
- early
->tv_usec
;
1256 add_target_ip(char *arg
, struct sockaddr_storage
*in
)
1258 struct rta_host
*host
;
1259 struct sockaddr_in
*sin
, *host_sin
;
1260 struct sockaddr_in6
*sin6
, *host_sin6
;
1262 if (address_family
== AF_INET
)
1263 sin
= (struct sockaddr_in
*)in
;
1265 sin6
= (struct sockaddr_in6
*)in
;
1269 /* disregard obviously stupid addresses
1270 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1271 if (((address_family
== AF_INET
&& (sin
->sin_addr
.s_addr
== INADDR_NONE
1272 || sin
->sin_addr
.s_addr
== INADDR_ANY
)))
1273 || (address_family
== AF_INET6
&& (sin6
->sin6_addr
.s6_addr
== in6addr_any
.s6_addr
))) {
1277 /* no point in adding two identical IP's, so don't. ;) */
1280 host_sin
= (struct sockaddr_in
*)&host
->saddr_in
;
1281 host_sin6
= (struct sockaddr_in6
*)&host
->saddr_in
;
1283 if( (address_family
== AF_INET
&& host_sin
->sin_addr
.s_addr
== sin
->sin_addr
.s_addr
)
1284 || (address_family
== AF_INET6
&& host_sin6
->sin6_addr
.s6_addr
== sin6
->sin6_addr
.s6_addr
)) {
1285 if(debug
) printf("Identical IP already exists. Not adding %s\n", arg
);
1291 /* add the fresh ip */
1292 host
= (struct rta_host
*)malloc(sizeof(struct rta_host
));
1294 char straddr
[INET6_ADDRSTRLEN
];
1295 parse_address((struct sockaddr_storage
*)&in
, straddr
, sizeof(straddr
));
1296 crash("add_target_ip(%s, %s): malloc(%d) failed",
1297 arg
, straddr
, sizeof(struct rta_host
));
1299 memset(host
, 0, sizeof(struct rta_host
));
1301 /* set the values. use calling name for output */
1302 host
->name
= strdup(arg
);
1304 /* fill out the sockaddr_storage struct */
1305 if(address_family
== AF_INET
) {
1306 host_sin
= (struct sockaddr_in
*)&host
->saddr_in
;
1307 host_sin
->sin_family
= AF_INET
;
1308 host_sin
->sin_addr
.s_addr
= sin
->sin_addr
.s_addr
;
1311 host_sin6
= (struct sockaddr_in6
*)&host
->saddr_in
;
1312 host_sin6
->sin6_family
= AF_INET6
;
1313 memcpy(host_sin6
->sin6_addr
.s6_addr
, sin6
->sin6_addr
.s6_addr
, sizeof host_sin6
->sin6_addr
.s6_addr
);
1316 host
->rtmin
= DBL_MAX
;
1318 if(!list
) list
= cursor
= host
;
1319 else cursor
->next
= host
;
1327 /* wrapper for add_target_ip */
1329 add_target(char *arg
)
1332 struct sockaddr_storage ip
;
1333 struct addrinfo hints
, *res
, *p
;
1334 struct sockaddr_in
*sin
;
1335 struct sockaddr_in6
*sin6
;
1337 switch (address_family
) {
1339 /* -4 and -6 are not specified on cmdline */
1340 address_family
= AF_INET
;
1341 sin
= (struct sockaddr_in
*)&ip
;
1342 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1345 address_family
= AF_INET6
;
1346 sin6
= (struct sockaddr_in6
*)&ip
;
1347 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1350 /* If we don't find any valid addresses, we still don't know the address_family */
1352 address_family
= -1;
1356 sin
= (struct sockaddr_in
*)&ip
;
1357 result
= inet_pton(address_family
, arg
, &sin
->sin_addr
);
1360 sin6
= (struct sockaddr_in6
*)&ip
;
1361 result
= inet_pton(address_family
, arg
, &sin6
->sin6_addr
);
1363 default: crash("Address family not supported");
1366 /* don't resolve if we don't have to */
1368 /* don't add all ip's if we were given a specific one */
1369 return add_target_ip(arg
, &ip
);
1373 memset(&hints
, 0, sizeof(hints
));
1374 if (address_family
== -1) {
1375 hints
.ai_family
= AF_UNSPEC
;
1377 hints
.ai_family
= address_family
== AF_INET
? PF_INET
: PF_INET6
;
1379 hints
.ai_socktype
= SOCK_RAW
;
1380 if((error
= getaddrinfo(arg
, NULL
, &hints
, &res
)) != 0) {
1382 crash("Failed to resolve %s: %s", arg
, gai_strerror(error
));
1385 address_family
= res
->ai_family
;
1388 /* possibly add all the IP's as targets */
1389 for(p
= res
; p
!= NULL
; p
= p
->ai_next
) {
1390 memcpy(&ip
, p
->ai_addr
, p
->ai_addrlen
);
1391 add_target_ip(arg
, &ip
);
1393 /* this is silly, but it works */
1394 if(mode
== MODE_HOSTCHECK
|| mode
== MODE_ALL
) {
1395 if(debug
> 2) printf("mode: %d\n", mode
);
1406 set_source_ip(char *arg
)
1408 struct sockaddr_in src
;
1410 memset(&src
, 0, sizeof(src
));
1411 src
.sin_family
= address_family
;
1412 if((src
.sin_addr
.s_addr
= inet_addr(arg
)) == INADDR_NONE
)
1413 src
.sin_addr
.s_addr
= get_ip_address(arg
);
1414 if(bind(icmp_sock
, (struct sockaddr
*)&src
, sizeof(src
)) == -1)
1415 crash("Cannot bind to IP address %s", arg
);
1418 /* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1420 get_ip_address(const char *ifname
)
1422 #if defined(SIOCGIFADDR)
1424 struct sockaddr_in ip
;
1426 strncpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
) - 1);
1427 ifr
.ifr_name
[sizeof(ifr
.ifr_name
) - 1] = '\0';
1428 if(ioctl(icmp_sock
, SIOCGIFADDR
, &ifr
) == -1)
1429 crash("Cannot determine IP address of interface %s", ifname
);
1430 memcpy(&ip
, &ifr
.ifr_addr
, sizeof(ip
));
1431 return ip
.sin_addr
.s_addr
;
1434 crash("Cannot get interface IP address on this platform.");
1442 * return value is in microseconds
1445 get_timevar(const char *str
)
1449 u_int i
, d
; /* integer and decimal, respectively */
1450 u_int factor
= 1000; /* default to milliseconds */
1456 /* unit might be given as ms|m (millisec),
1457 * us|u (microsec) or just plain s, for seconds */
1460 if(len
>= 2 && !isdigit((int)str
[len
- 2])) p
= str
[len
- 2];
1461 if(p
&& u
== 's') u
= p
;
1463 if(debug
> 2) printf("evaluating %s, u: %c, p: %c\n", str
, u
, p
);
1465 if(u
== 'u') factor
= 1; /* microseconds */
1466 else if(u
== 'm') factor
= 1000; /* milliseconds */
1467 else if(u
== 's') factor
= 1000000; /* seconds */
1468 if(debug
> 2) printf("factor is %u\n", factor
);
1470 i
= strtoul(str
, &ptr
, 0);
1471 if(!ptr
|| *ptr
!= '.' || strlen(ptr
) < 2 || factor
== 1)
1474 /* time specified in usecs can't have decimal points, so ignore them */
1475 if(factor
== 1) return i
;
1477 d
= strtoul(ptr
+ 1, NULL
, 0);
1479 /* d is decimal, so get rid of excess digits */
1480 while(d
>= factor
) d
/= 10;
1482 /* the last parenthesis avoids floating point exceptions. */
1483 return ((i
* factor
) + (d
* (factor
/ 10)));
1486 /* not too good at checking errors, but it'll do (main() should barfe on -1) */
1488 get_threshold(char *str
, threshold
*th
)
1490 char *p
= NULL
, i
= 0;
1492 if(!str
|| !strlen(str
) || !th
) return -1;
1494 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1495 p
= &str
[strlen(str
) - 1];
1496 while(p
!= &str
[1]) {
1497 if(*p
== '%') *p
= '\0';
1498 else if(*p
== ',' && i
) {
1499 *p
= '\0'; /* reset it so get_timevar(str) works nicely later */
1500 th
->pl
= (unsigned char)strtoul(p
+1, NULL
, 0);
1506 th
->rta
= get_timevar(str
);
1508 if(!th
->rta
) return -1;
1510 if(th
->rta
> MAXTTL
* 1000000) th
->rta
= MAXTTL
* 1000000;
1511 if(th
->pl
> 100) th
->pl
= 100;
1517 icmp_checksum(unsigned short *p
, int n
)
1519 unsigned short cksum
;
1524 n
-= sizeof(unsigned short);
1527 /* mop up the occasional odd byte */
1528 if(n
== 1) sum
+= (unsigned char)*p
;
1530 sum
= (sum
>> 16) + (sum
& 0xffff); /* add hi 16 to low 16 */
1531 sum
+= (sum
>> 16); /* add carry */
1532 cksum
= ~sum
; /* ones-complement, trunc to 16 bits */
1541 /*print_revision (progname);*/ /* FIXME: Why? */
1543 printf ("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
1544 printf (COPYRIGHT
, copyright
, email
);
1550 printf (UT_HELP_VRSN
);
1551 printf (UT_EXTRA_OPTS
);
1553 printf (" %s\n", "-H");
1554 printf (" %s\n", _("specify a target"));
1555 printf (" %s\n", "[-4|-6]");
1556 printf (" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
1557 printf (" %s\n", "-w");
1558 printf (" %s", _("warning threshold (currently "));
1559 printf ("%0.3fms,%u%%)\n", (float)warn
.rta
/ 1000, warn
.pl
);
1560 printf (" %s\n", "-c");
1561 printf (" %s", _("critical threshold (currently "));
1562 printf ("%0.3fms,%u%%)\n", (float)crit
.rta
/ 1000, crit
.pl
);
1563 printf (" %s\n", "-s");
1564 printf (" %s\n", _("specify a source IP address or device name"));
1565 printf (" %s\n", "-n");
1566 printf (" %s", _("number of packets to send (currently "));
1567 printf ("%u)\n",packets
);
1568 printf (" %s\n", "-i");
1569 printf (" %s", _("max packet interval (currently "));
1570 printf ("%0.3fms)\n",(float)pkt_interval
/ 1000);
1571 printf (" %s\n", "-I");
1572 printf (" %s", _("max target interval (currently "));
1573 printf ("%0.3fms)\n", (float)target_interval
/ 1000);
1574 printf (" %s\n", "-m");
1575 printf (" %s",_("number of alive hosts required for success"));
1577 printf (" %s\n", "-l");
1578 printf (" %s", _("TTL on outgoing packets (currently "));
1579 printf ("%u)\n", ttl
);
1580 printf (" %s\n", "-t");
1581 printf (" %s",_("timeout value (seconds, currently "));
1582 printf ("%u)\n", timeout
);
1583 printf (" %s\n", "-b");
1584 printf (" %s\n", _("Number of icmp data bytes to send"));
1585 printf (" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"),icmp_data_size
, ICMP_MINLEN
);
1586 printf (" %s\n", "-v");
1587 printf (" %s\n", _("verbose"));
1590 printf ("%s\n", _("Notes:"));
1591 printf (" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not."));
1593 printf (" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
1594 printf (" %s\n", _("packet loss. The default values should work well for most users."));
1595 printf (" %s\n", _("You can specify different RTA factors using the standardized abbreviations"));
1596 printf (" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
1597 /* -d not yet implemented */
1598 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops"));
1599 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
1600 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
1602 printf (" %s\n", _("The -v switch can be specified several times for increased verbosity."));
1603 /* printf ("%s\n", _("Long options are currently unsupported."));
1604 printf ("%s\n", _("Options marked with * require an argument"));
1607 printf (UT_SUPPORT
);
1615 printf ("%s\n", _("Usage:"));
1616 printf(" %s [options] [-H] host1 host2 hostN\n", progname
);