summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/check_dhcp.c1261
-rw-r--r--plugins/check_icmp.c1199
2 files changed, 0 insertions, 2460 deletions
diff --git a/plugins/check_dhcp.c b/plugins/check_dhcp.c
deleted file mode 100644
index a3e0c53..0000000
--- a/plugins/check_dhcp.c
+++ /dev/null
@@ -1,1261 +0,0 @@
1/******************************************************************************
2*
3* CHECK_DHCP.C
4*
5* Program: DHCP plugin for Nagios
6* License: GPL
7* Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
8*
9* License Information:
10*
11* This program is free software; you can redistribute it and/or modify
12* it under the terms of the GNU General Public License as published by
13* the Free Software Foundation; either version 2 of the License, or
14* (at your option) any later version.
15*
16* This program is distributed in the hope that it will be useful,
17* but WITHOUT ANY WARRANTY; without even the implied warranty of
18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19* GNU General Public License for more details.
20*
21* You should have received a copy of the GNU General Public License
22* along with this program; if not, write to the Free Software
23* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24*
25* $Id$
26*
27*****************************************************************************/
28
29const char *progname = "check_dhcp";
30const char *revision = "$Revision$";
31const char *copyright = "2001-2004";
32const char *email = "nagiosplug-devel@lists.sourceforge.net";
33
34#include "common.h"
35#include "netutils.h"
36#include "utils.h"
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <errno.h>
42#include <unistd.h>
43#include <sys/time.h>
44#include <sys/ioctl.h>
45#include <fcntl.h>
46#include <getopt.h>
47#include <sys/socket.h>
48#include <sys/types.h>
49#include <netdb.h>
50#include <netinet/in.h>
51#include <net/if.h>
52#include <arpa/inet.h>
53
54#if defined( __linux__ )
55
56#include <linux/if_ether.h>
57#include <features.h>
58
59#elif defined (__bsd__)
60
61#include <netinet/if_ether.h>
62#include <sys/sysctl.h>
63#include <net/if_dl.h>
64
65#elif defined(__sun__) || defined(__solaris__) || defined(__hpux__)
66
67#define INSAP 22
68#define OUTSAP 24
69
70#include <signal.h>
71#include <ctype.h>
72#include <sys/stropts.h>
73#include <sys/poll.h>
74#include <sys/dlpi.h>
75
76#define bcopy(source, destination, length) memcpy(destination, source, length)
77
78#define AREA_SZ 5000 /* buffer length in bytes */
79static u_long ctl_area[AREA_SZ];
80static u_long dat_area[AREA_SZ];
81static struct strbuf ctl = {AREA_SZ, 0, (char *)ctl_area};
82static struct strbuf dat = {AREA_SZ, 0, (char *)dat_area};
83
84#define GOT_CTRL 1
85#define GOT_DATA 2
86#define GOT_BOTH 3
87#define GOT_INTR 4
88#define GOT_ERR 128
89
90#define u_int8_t uint8_t
91#define u_int16_t uint16_t
92#define u_int32_t uint32_t
93
94static int get_msg(int);
95static int check_ctrl(int);
96static int put_ctrl(int, int, int);
97static int put_both(int, int, int, int);
98static int dl_open(const char *, int, int *);
99static int dl_bind(int, int, u_char *);
100long mac_addr_dlpi( const char *, int, u_char *);
101
102#endif
103
104#define HAVE_GETOPT_H
105
106
107/**** Common definitions ****/
108
109#define STATE_OK 0
110#define STATE_WARNING 1
111#define STATE_CRITICAL 2
112#define STATE_UNKNOWN -1
113
114#define OK 0
115#define ERROR -1
116
117#define FALSE 0
118#define TRUE 1
119
120
121/**** DHCP definitions ****/
122
123#define MAX_DHCP_CHADDR_LENGTH 16
124#define MAX_DHCP_SNAME_LENGTH 64
125#define MAX_DHCP_FILE_LENGTH 128
126#define MAX_DHCP_OPTIONS_LENGTH 312
127
128
129typedef struct dhcp_packet_struct{
130 u_int8_t op; /* packet type */
131 u_int8_t htype; /* type of hardware address for this machine (Ethernet, etc) */
132 u_int8_t hlen; /* length of hardware address (of this machine) */
133 u_int8_t hops; /* hops */
134 u_int32_t xid; /* random transaction id number - chosen by this machine */
135 u_int16_t secs; /* seconds used in timing */
136 u_int16_t flags; /* flags */
137 struct in_addr ciaddr; /* IP address of this machine (if we already have one) */
138 struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */
139 struct in_addr siaddr; /* IP address of DHCP server */
140 struct in_addr giaddr; /* IP address of DHCP relay */
141 unsigned char chaddr [MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */
142 char sname [MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */
143 char file [MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */
144 char options[MAX_DHCP_OPTIONS_LENGTH]; /* options */
145 }dhcp_packet;
146
147
148typedef struct dhcp_offer_struct{
149 struct in_addr server_address; /* address of DHCP server that sent this offer */
150 struct in_addr offered_address; /* the IP address that was offered to us */
151 u_int32_t lease_time; /* lease time in seconds */
152 u_int32_t renewal_time; /* renewal time in seconds */
153 u_int32_t rebinding_time; /* rebinding time in seconds */
154 struct dhcp_offer_struct *next;
155 }dhcp_offer;
156
157
158typedef struct requested_server_struct{
159 struct in_addr server_address;
160 struct requested_server_struct *next;
161 }requested_server;
162
163
164#define BOOTREQUEST 1
165#define BOOTREPLY 2
166
167#define DHCPDISCOVER 1
168#define DHCPOFFER 2
169#define DHCPREQUEST 3
170#define DHCPDECLINE 4
171#define DHCPACK 5
172#define DHCPNACK 6
173#define DHCPRELEASE 7
174
175#define DHCP_OPTION_MESSAGE_TYPE 53
176#define DHCP_OPTION_HOST_NAME 12
177#define DHCP_OPTION_BROADCAST_ADDRESS 28
178#define DHCP_OPTION_REQUESTED_ADDRESS 50
179#define DHCP_OPTION_LEASE_TIME 51
180#define DHCP_OPTION_RENEWAL_TIME 58
181#define DHCP_OPTION_REBINDING_TIME 59
182
183#define DHCP_INFINITE_TIME 0xFFFFFFFF
184
185#define DHCP_BROADCAST_FLAG 32768
186
187#define DHCP_SERVER_PORT 67
188#define DHCP_CLIENT_PORT 68
189
190#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
191#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
192
193unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]="";
194
195char network_interface_name[8]="eth0";
196
197u_int32_t packet_xid=0;
198
199u_int32_t dhcp_lease_time=0;
200u_int32_t dhcp_renewal_time=0;
201u_int32_t dhcp_rebinding_time=0;
202
203int dhcpoffer_timeout=2;
204
205dhcp_offer *dhcp_offer_list=NULL;
206requested_server *requested_server_list=NULL;
207
208int valid_responses=0; /* number of valid DHCPOFFERs we received */
209int requested_servers=0;
210int requested_responses=0;
211
212int request_specific_address=FALSE;
213int received_requested_address=FALSE;
214int verbose=0;
215struct in_addr requested_address;
216
217
218int process_arguments(int, char **);
219int call_getopt(int, char **);
220int validate_arguments(void);
221void print_usage(void);
222void print_help(void);
223
224int get_hardware_address(int,char *);
225
226int send_dhcp_discover(int);
227int get_dhcp_offer(int);
228
229int get_results(void);
230
231int add_dhcp_offer(struct in_addr,dhcp_packet *);
232int free_dhcp_offer_list(void);
233int free_requested_server_list(void);
234
235int create_dhcp_socket(void);
236int close_dhcp_socket(int);
237int send_dhcp_packet(void *,int,int,struct sockaddr_in *);
238int receive_dhcp_packet(void *,int,int,int,struct sockaddr_in *);
239
240
241
242int main(int argc, char **argv){
243 int dhcp_socket;
244 int result;
245
246 setlocale (LC_ALL, "");
247 bindtextdomain (PACKAGE, LOCALEDIR);
248 textdomain (PACKAGE);
249
250 if(process_arguments(argc,argv)!=OK){
251 usage4 (_("Could not parse arguments"));
252 }
253
254 /* create socket for DHCP communications */
255 dhcp_socket=create_dhcp_socket();
256
257 /* get hardware address of client machine */
258 get_hardware_address(dhcp_socket,network_interface_name);
259
260 /* send DHCPDISCOVER packet */
261 send_dhcp_discover(dhcp_socket);
262
263 /* wait for a DHCPOFFER packet */
264 get_dhcp_offer(dhcp_socket);
265
266 /* close socket we created */
267 close_dhcp_socket(dhcp_socket);
268
269 /* determine state/plugin output to return */
270 result=get_results();
271
272 /* free allocated memory */
273 free_dhcp_offer_list();
274 free_requested_server_list();
275
276 return result;
277 }
278
279
280
281/* determines hardware address on client machine */
282int get_hardware_address(int sock,char *interface_name){
283
284 int i;
285
286#if defined(__linux__)
287 struct ifreq ifr;
288
289 strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name));
290
291 /* try and grab hardware address of requested interface */
292 if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){
293 printf(_("Error: Could not get hardware address of interface '%s'\n"),interface_name);
294 exit(STATE_UNKNOWN);
295 }
296
297 memcpy(&client_hardware_address[0],&ifr.ifr_hwaddr.sa_data,6);
298
299#elif defined(__bsd__)
300 /* King 2004 see ACKNOWLEDGEMENTS */
301
302 int mib[6], len;
303 char *buf;
304 unsigned char *ptr;
305 struct if_msghdr *ifm;
306 struct sockaddr_dl *sdl;
307
308 mib[0] = CTL_NET;
309 mib[1] = AF_ROUTE;
310 mib[2] = 0;
311 mib[3] = AF_LINK;
312 mib[4] = NET_RT_IFLIST;
313
314 if ((mib[5] = if_nametoindex(interface_name)) == 0) {
315 printf(_("Error: if_nametoindex error - %s.\n"), strerror(errno));
316 exit(STATE_UNKNOWN);
317 }
318
319 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
320 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno));
321 exit(STATE_UNKNOWN);
322 }
323
324 if ((buf = malloc(len)) == NULL) {
325 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno));
326 exit(4);
327 }
328
329 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
330 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno));
331 exit(STATE_UNKNOWN);
332 }
333
334 ifm = (struct if_msghdr *)buf;
335 sdl = (struct sockaddr_dl *)(ifm + 1);
336 ptr = (unsigned char *)LLADDR(sdl);
337 memcpy(&client_hardware_address[0], ptr, 6) ;
338 /* King 2004 */
339
340#elif defined(__sun__) || defined(__solaris__)
341
342 /* Kompf 2000-2003 see ACKNOWLEDGEMENTS */
343 long stat;
344 char dev[20] = "/dev/";
345 char *p;
346 int unit;
347
348 for (p = interface_name; *p && isalpha(*p); p++)
349 /* no-op */ ;
350 if ( p != '\0' ) {
351 unit = atoi(p) ;
352 *p = '\0' ;
353 strncat(dev, interface_name, 6) ;
354 } else {
355 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name);
356 exit(STATE_UNKNOWN);
357 }
358 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
359 if (stat != 0) {
360 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit);
361 exit(STATE_UNKNOWN);
362 }
363
364#elif defined(__hpux__)
365
366 long stat;
367 char dev[20] = "/dev/dlpi" ;
368 int unit = 0;
369
370 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
371 if (stat != 0) {
372 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit);
373 exit(STATE_UNKNOWN);
374 }
375 /* Kompf 2000-2003 */
376
377#else
378 printf(_("Error: can't get MAC address for this architecture.\n"));
379 exit(STATE_UNKNOWN);
380#endif
381
382 if (verbose) {
383 printf(_("Hardware address: "));
384 for (i=0; i<6; ++i)
385 printf("%2.2x", client_hardware_address[i]);
386 printf( "\n");
387 }
388
389 return OK;
390 }
391
392
393/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
394int send_dhcp_discover(int sock){
395 dhcp_packet discover_packet;
396 struct sockaddr_in sockaddr_broadcast;
397
398
399 /* clear the packet data structure */
400 bzero(&discover_packet,sizeof(discover_packet));
401
402
403 /* boot request flag (backward compatible with BOOTP servers) */
404 discover_packet.op=BOOTREQUEST;
405
406 /* hardware address type */
407 discover_packet.htype=ETHERNET_HARDWARE_ADDRESS;
408
409 /* length of our hardware address */
410 discover_packet.hlen=ETHERNET_HARDWARE_ADDRESS_LENGTH;
411
412 discover_packet.hops=0;
413
414 /* transaction id is supposed to be random */
415 srand(time(NULL));
416 packet_xid=random();
417 discover_packet.xid=htonl(packet_xid);
418
419 /**** WHAT THE HECK IS UP WITH THIS?!? IF I DON'T MAKE THIS CALL, ONLY ONE SERVER RESPONSE IS PROCESSED!!!! ****/
420 /* downright bizzarre... */
421 ntohl(discover_packet.xid);
422
423 /*discover_packet.secs=htons(65535);*/
424 discover_packet.secs=0xFF;
425
426 /* tell server it should broadcast its response */
427 discover_packet.flags=htons(DHCP_BROADCAST_FLAG);
428
429 /* our hardware address */
430 memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH);
431
432 /* first four bytes of options field is magic cookie (as per RFC 2132) */
433 discover_packet.options[0]='\x63';
434 discover_packet.options[1]='\x82';
435 discover_packet.options[2]='\x53';
436 discover_packet.options[3]='\x63';
437
438 /* DHCP message type is embedded in options field */
439 discover_packet.options[4]=DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
440 discover_packet.options[5]='\x01'; /* DHCP message option length in bytes */
441 discover_packet.options[6]=DHCPDISCOVER;
442
443 /* the IP address we're requesting */
444 if(request_specific_address==TRUE){
445 discover_packet.options[7]=DHCP_OPTION_REQUESTED_ADDRESS;
446 discover_packet.options[8]='\x04';
447 memcpy(&discover_packet.options[9],&requested_address,sizeof(requested_address));
448 }
449
450 /* send the DHCPDISCOVER packet to broadcast address */
451 sockaddr_broadcast.sin_family=AF_INET;
452 sockaddr_broadcast.sin_port=htons(DHCP_SERVER_PORT);
453 sockaddr_broadcast.sin_addr.s_addr=INADDR_BROADCAST;
454 bzero(&sockaddr_broadcast.sin_zero,sizeof(sockaddr_broadcast.sin_zero));
455
456
457 if (verbose) {
458 printf(_("DHCPDISCOVER to %s port %d\n"),inet_ntoa(sockaddr_broadcast.sin_addr),ntohs(sockaddr_broadcast.sin_port));
459 printf("DHCPDISCOVER XID: %lu (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid));
460 printf("DHCDISCOVER ciaddr: %s\n",inet_ntoa(discover_packet.ciaddr));
461 printf("DHCDISCOVER yiaddr: %s\n",inet_ntoa(discover_packet.yiaddr));
462 printf("DHCDISCOVER siaddr: %s\n",inet_ntoa(discover_packet.siaddr));
463 printf("DHCDISCOVER giaddr: %s\n",inet_ntoa(discover_packet.giaddr));
464 }
465
466 /* send the DHCPDISCOVER packet out */
467 send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast);
468
469 if (verbose)
470 printf("\n\n");
471
472 return OK;
473 }
474
475
476
477
478/* waits for a DHCPOFFER message from one or more DHCP servers */
479int get_dhcp_offer(int sock){
480 dhcp_packet offer_packet;
481 struct sockaddr_in source;
482 int result=OK;
483 int timeout=1;
484 int responses=0;
485 int x;
486 time_t start_time;
487 time_t current_time;
488
489 time(&start_time);
490
491 /* receive as many responses as we can */
492 for(responses=0,valid_responses=0;;){
493
494 time(&current_time);
495 if((current_time-start_time)>=dhcpoffer_timeout)
496 break;
497
498 if (verbose)
499 printf("\n\n");
500
501 bzero(&source,sizeof(source));
502 bzero(&offer_packet,sizeof(offer_packet));
503
504 result=OK;
505 result=receive_dhcp_packet(&offer_packet,sizeof(offer_packet),sock,dhcpoffer_timeout,&source);
506
507 if(result!=OK){
508 if (verbose)
509 printf(_("Result=ERROR\n"));
510
511 continue;
512 }
513 else{
514 if (verbose)
515 printf(_("Result=OK\n"));
516
517 responses++;
518 }
519
520 if (verbose) {
521 printf(_("DHCPOFFER from IP address %s\n"),inet_ntoa(source.sin_addr));
522 printf("DHCPOFFER XID: %lu (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid));
523 }
524
525 /* check packet xid to see if its the same as the one we used in the discover packet */
526 if(ntohl(offer_packet.xid)!=packet_xid){
527 if (verbose)
528 printf(_("DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - ignoring packet\n"),ntohl(offer_packet.xid),packet_xid);
529
530 continue;
531 }
532
533 /* check hardware address */
534 result=OK;
535 if (verbose)
536 printf("DHCPOFFER chaddr: ");
537
538 for(x=0;x<ETHERNET_HARDWARE_ADDRESS_LENGTH;x++){
539 if (verbose)
540 printf("%02X",(unsigned char)offer_packet.chaddr[x]);
541
542 if(offer_packet.chaddr[x]!=client_hardware_address[x])
543 result=ERROR;
544 }
545 if (verbose)
546 printf("\n");
547
548 if(result==ERROR){
549 if (verbose)
550 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
551
552 continue;
553 }
554
555 if (verbose) {
556 printf("DHCPOFFER ciaddr: %s\n",inet_ntoa(offer_packet.ciaddr));
557 printf("DHCPOFFER yiaddr: %s\n",inet_ntoa(offer_packet.yiaddr));
558 printf("DHCPOFFER siaddr: %s\n",inet_ntoa(offer_packet.siaddr));
559 printf("DHCPOFFER giaddr: %s\n",inet_ntoa(offer_packet.giaddr));
560 }
561
562 add_dhcp_offer(source.sin_addr,&offer_packet);
563
564 valid_responses++;
565 }
566
567 if (verbose) {
568 printf(_("Total responses seen on the wire: %d\n"),responses);
569 printf(_("Valid responses for this machine: %d\n"),valid_responses);
570 }
571
572 return OK;
573 }
574
575
576
577/* sends a DHCP packet */
578int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest){
579 struct sockaddr_in myname;
580 int result;
581
582 result=sendto(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)dest,sizeof(*dest));
583
584 if (verbose)
585 printf(_("send_dhcp_packet result: %d\n"),result);
586
587 if(result<0)
588 return ERROR;
589
590 return OK;
591 }
592
593
594
595/* receives a DHCP packet */
596int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address){
597 struct timeval tv;
598 fd_set readfds;
599 int recv_result;
600 socklen_t address_size;
601 struct sockaddr_in source_address;
602
603
604 /* wait for data to arrive (up time timeout) */
605 tv.tv_sec=timeout;
606 tv.tv_usec=0;
607 FD_ZERO(&readfds);
608 FD_SET(sock,&readfds);
609 select(sock+1,&readfds,NULL,NULL,&tv);
610
611 /* make sure some data has arrived */
612 if(!FD_ISSET(sock,&readfds)){
613 if (verbose)
614 printf(_("No (more) data received\n"));
615 return ERROR;
616 }
617
618 else{
619
620 /* why do we need to peek first? i don't know, its a hack. without it, the source address of the first packet received was
621 not being interpreted correctly. sigh... */
622 bzero(&source_address,sizeof(source_address));
623 address_size=sizeof(source_address);
624 recv_result=recvfrom(sock,(char *)buffer,buffer_size,MSG_PEEK,(struct sockaddr *)&source_address,&address_size);
625 if (verbose)
626 printf("recv_result_1: %d\n",recv_result);
627 recv_result=recvfrom(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)&source_address,&address_size);
628 if (verbose)
629 printf("recv_result_2: %d\n",recv_result);
630
631 if(recv_result==-1){
632 if (verbose) {
633 printf(_("recvfrom() failed, "));
634 printf("errno: (%d) -> %s\n",errno,strerror(errno));
635 }
636 return ERROR;
637 }
638 else{
639 if (verbose) {
640 printf(_("receive_dhcp_packet() result: %d\n"),recv_result);
641 printf(_("receive_dhcp_packet() source: %s\n"),inet_ntoa(source_address.sin_addr));
642 }
643
644 memcpy(address,&source_address,sizeof(source_address));
645 return OK;
646 }
647 }
648
649 return OK;
650 }
651
652
653/* creates a socket for DHCP communication */
654int create_dhcp_socket(void){
655 struct sockaddr_in myname;
656 struct ifreq interface;
657 int sock;
658 int flag=1;
659
660 /* Set up the address we're going to bind to. */
661 bzero(&myname,sizeof(myname));
662 myname.sin_family=AF_INET;
663 myname.sin_port=htons(DHCP_CLIENT_PORT);
664 myname.sin_addr.s_addr=INADDR_ANY; /* listen on any address */
665 bzero(&myname.sin_zero,sizeof(myname.sin_zero));
666
667 /* create a socket for DHCP communications */
668 sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
669 if(sock<0){
670 printf(_("Error: Could not create socket!\n"));
671 exit(STATE_UNKNOWN);
672 }
673
674 if (verbose)
675 printf("DHCP socket: %d\n",sock);
676
677 /* set the reuse address flag so we don't get errors when restarting */
678 flag=1;
679 if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
680 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
681 exit(STATE_UNKNOWN);
682 }
683
684 /* set the broadcast option - we need this to listen to DHCP broadcast messages */
685 if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
686 printf(_("Error: Could not set broadcast option on DHCP socket!\n"));
687 exit(STATE_UNKNOWN);
688 }
689
690 /* bind socket to interface */
691#if defined(__linux__)
692 strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ);
693 if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){
694 printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"),network_interface_name);
695 exit(STATE_UNKNOWN);
696 }
697
698#else
699 strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ);
700#endif
701
702 /* bind the socket */
703 if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
704 printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"),DHCP_CLIENT_PORT);
705 exit(STATE_UNKNOWN);
706 }
707
708 return sock;
709 }
710
711
712/* closes DHCP socket */
713int close_dhcp_socket(int sock){
714
715 close(sock);
716
717 return OK;
718 }
719
720
721/* adds a requested server address to list in memory */
722int add_requested_server(struct in_addr server_address){
723 requested_server *new_server;
724
725 new_server=(requested_server *)malloc(sizeof(requested_server));
726 if(new_server==NULL)
727 return ERROR;
728
729 new_server->server_address=server_address;
730
731 new_server->next=requested_server_list;
732 requested_server_list=new_server;
733
734 requested_servers++;
735
736 if (verbose)
737 printf(_("Requested server address: %s\n"),inet_ntoa(new_server->server_address));
738
739 return OK;
740 }
741
742
743
744
745/* adds a DHCP OFFER to list in memory */
746int add_dhcp_offer(struct in_addr source,dhcp_packet *offer_packet){
747 dhcp_offer *new_offer;
748 int x;
749 int y;
750 unsigned option_type;
751 unsigned option_length;
752
753 if(offer_packet==NULL)
754 return ERROR;
755
756 /* process all DHCP options present in the packet */
757 for(x=4;x<MAX_DHCP_OPTIONS_LENGTH;){
758
759 /* end of options (0 is really just a pad, but bail out anyway) */
760 if((int)offer_packet->options[x]==-1 || (int)offer_packet->options[x]==0)
761 break;
762
763 /* get option type */
764 option_type=offer_packet->options[x++];
765
766 /* get option length */
767 option_length=offer_packet->options[x++];
768
769 if (verbose)
770 printf("Option: %d (0x%02X)\n",option_type,option_length);
771
772 /* get option data */
773 if(option_type==DHCP_OPTION_LEASE_TIME) {
774 memcpy(&dhcp_lease_time, &offer_packet->options[x],
775 sizeof(dhcp_lease_time));
776 dhcp_lease_time = ntohl(dhcp_lease_time);
777 }
778 if(option_type==DHCP_OPTION_RENEWAL_TIME) {
779 memcpy(&dhcp_renewal_time, &offer_packet->options[x],
780 sizeof(dhcp_renewal_time));
781 dhcp_renewal_time = ntohl(dhcp_renewal_time);
782 }
783 if(option_type==DHCP_OPTION_REBINDING_TIME) {
784 memcpy(&dhcp_rebinding_time, &offer_packet->options[x],
785 sizeof(dhcp_rebinding_time));
786 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
787 }
788
789 /* skip option data we're ignoring */
790 else
791 for(y=0;y<option_length;y++,x++);
792 }
793
794 if (verbose) {
795 if(dhcp_lease_time==DHCP_INFINITE_TIME)
796 printf(_("Lease Time: Infinite\n"));
797 else
798 printf(_("Lease Time: %lu seconds\n"),(unsigned long)dhcp_lease_time);
799 if(dhcp_renewal_time==DHCP_INFINITE_TIME)
800 printf(_("Renewal Time: Infinite\n"));
801 else
802 printf(_("Renewal Time: %lu seconds\n"),(unsigned long)dhcp_renewal_time);
803 if(dhcp_rebinding_time==DHCP_INFINITE_TIME)
804 printf(_("Rebinding Time: Infinite\n"));
805 printf(_("Rebinding Time: %lu seconds\n"),(unsigned long)dhcp_rebinding_time);
806 }
807
808 new_offer=(dhcp_offer *)malloc(sizeof(dhcp_offer));
809
810 if(new_offer==NULL)
811 return ERROR;
812
813 new_offer->server_address=source;
814 new_offer->offered_address=offer_packet->yiaddr;
815 new_offer->lease_time=dhcp_lease_time;
816 new_offer->renewal_time=dhcp_renewal_time;
817 new_offer->rebinding_time=dhcp_rebinding_time;
818
819
820 if (verbose) {
821 printf(_("Added offer from server @ %s"),inet_ntoa(new_offer->server_address));
822 printf(_(" of IP address %s\n"),inet_ntoa(new_offer->offered_address));
823 }
824
825 /* add new offer to head of list */
826 new_offer->next=dhcp_offer_list;
827 dhcp_offer_list=new_offer;
828
829 return OK;
830 }
831
832
833/* frees memory allocated to DHCP OFFER list */
834int free_dhcp_offer_list(void){
835 dhcp_offer *this_offer;
836 dhcp_offer *next_offer;
837
838 for(this_offer=dhcp_offer_list;this_offer!=NULL;this_offer=next_offer){
839 next_offer=this_offer->next;
840 free(this_offer);
841 }
842
843 return OK;
844 }
845
846
847/* frees memory allocated to requested server list */
848int free_requested_server_list(void){
849 requested_server *this_server;
850 requested_server *next_server;
851
852 for(this_server=requested_server_list;this_server!=NULL;this_server=next_server){
853 next_server=this_server->next;
854 free(this_server);
855 }
856
857 return OK;
858 }
859
860
861/* gets state and plugin output to return */
862int get_results(void){
863 dhcp_offer *temp_offer;
864 requested_server *temp_server;
865 int result;
866 u_int32_t max_lease_time=0;
867
868 received_requested_address=FALSE;
869
870 /* checks responses from requested servers */
871 requested_responses=0;
872 if(requested_servers>0){
873
874 for(temp_server=requested_server_list;temp_server!=NULL;temp_server=temp_server->next){
875
876 for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
877
878 /* get max lease time we were offered */
879 if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
880 max_lease_time=temp_offer->lease_time;
881
882 /* see if we got the address we requested */
883 if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
884 received_requested_address=TRUE;
885
886 /* see if the servers we wanted a response from talked to us or not */
887 if(!memcmp(&temp_offer->server_address,&temp_server->server_address,sizeof(temp_server->server_address))){
888 if (verbose) {
889 printf(_("DHCP Server Match: Offerer=%s"),inet_ntoa(temp_offer->server_address));
890 printf(_(" Requested=%s\n"),inet_ntoa(temp_server->server_address));
891 }
892 requested_responses++;
893 }
894 }
895 }
896
897 }
898
899 /* else check and see if we got our requested address from any server */
900 else{
901
902 for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
903
904 /* get max lease time we were offered */
905 if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
906 max_lease_time=temp_offer->lease_time;
907
908 /* see if we got the address we requested */
909 if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
910 received_requested_address=TRUE;
911 }
912 }
913
914 result=STATE_OK;
915 if(valid_responses==0)
916 result=STATE_CRITICAL;
917 else if(requested_servers>0 && requested_responses==0)
918 result=STATE_CRITICAL;
919 else if(requested_responses<requested_servers)
920 result=STATE_WARNING;
921 else if(request_specific_address==TRUE && received_requested_address==FALSE)
922 result=STATE_WARNING;
923
924
925 printf("DHCP %s: ",(result==STATE_OK)?"ok":"problem");
926
927 /* we didn't receive any DHCPOFFERs */
928 if(dhcp_offer_list==NULL){
929 printf(_("No DHCPOFFERs were received.\n"));
930 return result;
931 }
932
933 printf(_("Received %d DHCPOFFER(s)"),valid_responses);
934
935 if(requested_servers>0)
936 printf(_(", %s%d of %d requested servers responded"),((requested_responses<requested_servers) && requested_responses>0)?"only ":"",requested_responses,requested_servers);
937
938 if(request_specific_address==TRUE)
939 printf(_(", requested address (%s) was %soffered"),inet_ntoa(requested_address),(received_requested_address==TRUE)?"":_("not "));
940
941 printf(_(", max lease time = "));
942 if(max_lease_time==DHCP_INFINITE_TIME)
943 printf(_("Infinity"));
944 else
945 printf("%lu sec",(unsigned long)max_lease_time);
946
947 printf(".\n");
948
949 return result;
950 }
951
952
953/* process command-line arguments */
954int process_arguments(int argc, char **argv){
955 int c;
956
957 if(argc<1)
958 return ERROR;
959
960 c=0;
961 while((c+=(call_getopt(argc-c,&argv[c])))<argc){
962
963 /*
964 if(is_option(argv[c]))
965 continue;
966 */
967 }
968
969 return validate_arguments();
970 }
971
972
973
974int call_getopt(int argc, char **argv){
975 int c=0;
976 int i=0;
977 struct in_addr ipaddress;
978
979#ifdef HAVE_GETOPT_H
980 int option_index = 0;
981 static struct option long_options[] =
982 {
983 {"serverip", required_argument,0,'s'},
984 {"requestedip", required_argument,0,'r'},
985 {"timeout", required_argument,0,'t'},
986 {"interface", required_argument,0,'i'},
987 {"verbose", no_argument, 0,'v'},
988 {"version", no_argument, 0,'V'},
989 {"help", no_argument, 0,'h'},
990 {0,0,0,0}
991 };
992#endif
993
994 while(1){
995#ifdef HAVE_GETOPT_H
996 c=getopt_long(argc,argv,"+hVvt:s:r:t:i:",long_options,&option_index);
997#else
998 c=getopt(argc,argv,"+?hVvt:s:r:t:i:");
999#endif
1000
1001 i++;
1002
1003 if(c==-1||c==EOF||c==1)
1004 break;
1005
1006 switch(c){
1007 case 'w':
1008 case 'r':
1009 case 't':
1010 case 'i':
1011 i++;
1012 break;
1013 default:
1014 break;
1015 }
1016
1017 switch(c){
1018
1019 case 's': /* DHCP server address */
1020 if(inet_aton(optarg,&ipaddress))
1021 add_requested_server(ipaddress);
1022 /*
1023 else
1024 usage("Invalid server IP address\n");
1025 */
1026 break;
1027
1028 case 'r': /* address we are requested from DHCP servers */
1029 if(inet_aton(optarg,&ipaddress)){
1030 requested_address=ipaddress;
1031 request_specific_address=TRUE;
1032 }
1033 /*
1034 else
1035 usage("Invalid requested IP address\n");
1036 */
1037 break;
1038
1039 case 't': /* timeout */
1040
1041 /*
1042 if(is_intnonneg(optarg))
1043 */
1044 if(atoi(optarg)>0)
1045 dhcpoffer_timeout=atoi(optarg);
1046 /*
1047 else
1048 usage("Time interval must be a nonnegative integer\n");
1049 */
1050 break;
1051
1052 case 'i': /* interface name */
1053
1054 strncpy(network_interface_name,optarg,sizeof(network_interface_name)-1);
1055 network_interface_name[sizeof(network_interface_name)-1]='\x0';
1056
1057 break;
1058
1059 case 'V': /* version */
1060 print_revision(progname,revision);
1061 exit(STATE_OK);
1062
1063 case 'h': /* help */
1064 print_help();
1065 exit(STATE_OK);
1066
1067 case 'v': /* verbose */
1068 verbose=1;
1069 break;
1070
1071 case '?': /* help */
1072 usage2 (_("Unknown argument"), optarg);
1073 break;
1074
1075 default:
1076 break;
1077 }
1078 }
1079
1080 return i;
1081 }
1082
1083
1084int validate_arguments(void){
1085
1086 return OK;
1087 }
1088
1089#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
1090
1091 /* Kompf 2000-2003 see ACKNOWLEDGEMENTS */
1092
1093/* get a message from a stream; return type of message */
1094static int get_msg(int fd)
1095{
1096 int flags = 0;
1097 int res, ret;
1098 ctl_area[0] = 0;
1099 dat_area[0] = 0;
1100 ret = 0;
1101 res = getmsg(fd, &ctl, &dat, &flags);
1102
1103 if(res < 0) {
1104 if(errno == EINTR) {
1105 return(GOT_INTR);
1106 } else {
1107 printf("%s\n", "get_msg FAILED.");
1108 return(GOT_ERR);
1109 }
1110 }
1111 if(ctl.len > 0) {
1112 ret |= GOT_CTRL;
1113 }
1114 if(dat.len > 0) {
1115 ret |= GOT_DATA;
1116 }
1117 return(ret);
1118}
1119
1120/* verify that dl_primitive in ctl_area = prim */
1121static int check_ctrl(int prim)
1122{
1123 dl_error_ack_t *err_ack = (dl_error_ack_t *)ctl_area;
1124 if(err_ack->dl_primitive != prim) {
1125 printf(_("Error: DLPI stream API failed to get MAC in check_ctrl: %s.\n"), strerror(errno));
1126 exit(STATE_UNKNOWN);
1127 }
1128 return 0;
1129}
1130
1131/* put a control message on a stream */
1132static int put_ctrl(int fd, int len, int pri)
1133{
1134 ctl.len = len;
1135 if(putmsg(fd, &ctl, 0, pri) < 0) {
1136 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno));
1137 exit(STATE_UNKNOWN);
1138 }
1139 return 0;
1140}
1141
1142/* put a control + data message on a stream */
1143static int put_both(int fd, int clen, int dlen, int pri)
1144{
1145 ctl.len = clen;
1146 dat.len = dlen;
1147 if(putmsg(fd, &ctl, &dat, pri) < 0) {
1148 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno));
1149 exit(STATE_UNKNOWN);
1150 }
1151 return 0;
1152}
1153
1154/* open file descriptor and attach */
1155static int dl_open(const char *dev, int unit, int *fd)
1156{
1157 dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area;
1158 if((*fd = open(dev, O_RDWR)) == -1) {
1159 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno));
1160 exit(STATE_UNKNOWN);
1161 }
1162 attach_req->dl_primitive = DL_ATTACH_REQ;
1163 attach_req->dl_ppa = unit;
1164 put_ctrl(*fd, sizeof(dl_attach_req_t), 0);
1165 get_msg(*fd);
1166 return check_ctrl(DL_OK_ACK);
1167}
1168
1169/* send DL_BIND_REQ */
1170static int dl_bind(int fd, int sap, u_char *addr)
1171{
1172 dl_bind_req_t *bind_req = (dl_bind_req_t *)ctl_area;
1173 dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)ctl_area;
1174 bind_req->dl_primitive = DL_BIND_REQ;
1175 bind_req->dl_sap = sap;
1176 bind_req->dl_max_conind = 1;
1177 bind_req->dl_service_mode = DL_CLDLS;
1178 bind_req->dl_conn_mgmt = 0;
1179 bind_req->dl_xidtest_flg = 0;
1180 put_ctrl(fd, sizeof(dl_bind_req_t), 0);
1181 get_msg(fd);
1182 if (GOT_ERR == check_ctrl(DL_BIND_ACK)) {
1183 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno));
1184 exit(STATE_UNKNOWN);
1185 }
1186 bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr,
1187 bind_ack->dl_addr_length);
1188 return 0;
1189}
1190
1191/***********************************************************************
1192 * interface:
1193 * function mac_addr_dlpi - get the mac address of the interface with
1194 * type dev (eg lnc, hme) and unit (0, 1 ..)
1195 *
1196 * parameter: addr: an array of six bytes, has to be allocated by the caller
1197 *
1198 * return: 0 if OK, -1 if the address could not be determined
1199 *
1200 *
1201 ***********************************************************************/
1202
1203long mac_addr_dlpi( const char *dev, int unit, u_char *addr) {
1204
1205 int fd;
1206 u_char mac_addr[25];
1207
1208 if (GOT_ERR != dl_open(dev, unit, &fd)) {
1209 if (GOT_ERR != dl_bind(fd, INSAP, mac_addr)) {
1210 bcopy( mac_addr, addr, 6);
1211 return 0;
1212 }
1213 }
1214 close(fd);
1215 return -1;
1216}
1217
1218 /* Kompf 2000-2003 */
1219
1220#endif
1221
1222
1223/* print usage help */
1224void print_help(void){
1225
1226 print_revision(progname,revision);
1227
1228 printf("Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n\n");
1229 printf (COPYRIGHT, copyright, email);
1230
1231 printf(_("This plugin tests the availability of DHCP servers on a network.\n\n"));
1232
1233 print_usage();
1234
1235 printf(_("\
1236 -s, --serverip=IPADDRESS\n\
1237 IP address of DHCP server that we must hear from\n\
1238 -r, --requestedip=IPADDRESS\n\
1239 IP address that should be offered by at least one DHCP server\n\
1240 -t, --timeout=INTEGER\n\
1241 Seconds to wait for DHCPOFFER before timeout occurs\n\
1242 -i, --interface=STRING\n\
1243 Interface to to use for listening (i.e. eth0)\n\
1244 -v, --verbose\n\
1245 Print extra information (command-line use only)\n\
1246 -h, --help\n\
1247 Print detailed help screen\n\
1248 -V, --version\n\
1249 Print version information\n"));
1250}
1251
1252
1253void print_usage(void)
1254{
1255 printf("\
1256Usage: %s [-s serverip] [-r requestedip] [-t timeout] [-i interface]\n\
1257 [-v]",progname);
1258}
1259
1260
1261
diff --git a/plugins/check_icmp.c b/plugins/check_icmp.c
deleted file mode 100644
index 2f03552..0000000
--- a/plugins/check_icmp.c
+++ /dev/null
@@ -1,1199 +0,0 @@
1/*
2 * $Id$
3 *
4 * Author: Andreas Ericsson <ae@op5.se>
5 *
6 * License: GNU GPL 2.0 or any later version.
7 *
8 * Relevant RFC's: 792 (ICMP), 791 (IP)
9 *
10 * This program was modeled somewhat after the check_icmp program,
11 * which was in turn a hack of fping (www.fping.org) but has been
12 * completely rewritten since to generate higher precision rta values,
13 * and support several different modes as well as setting ttl to control.
14 * redundant routes. The only remainders of fping is currently a few
15 * function names.
16 *
17 */
18
19#include <sys/time.h>
20#include <sys/types.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <stdarg.h>
24#include <unistd.h>
25#include <stddef.h>
26#include <errno.h>
27#include <string.h>
28#include <ctype.h>
29#include <netdb.h>
30#include <sys/socket.h>
31#include <netinet/in_systm.h>
32#include <netinet/in.h>
33#include <netinet/ip.h>
34#include <netinet/ip_icmp.h>
35#include <arpa/inet.h>
36#include <signal.h>
37
38/** sometimes undefined system macros (quite a few, actually) **/
39#ifndef MAXTTL
40# define MAXTTL 255
41#endif
42#ifndef INADDR_NONE
43# define INADDR_NONE 0xffffffU
44#endif
45
46#ifndef SOL_IP
47#define SOL_IP 0
48#endif
49
50/* we bundle these in one #ifndef, since they're all from BSD
51 * Put individual #ifndef's around those that bother you */
52#ifndef ICMP_UNREACH_NET_UNKNOWN
53# define ICMP_UNREACH_NET_UNKNOWN 6
54# define ICMP_UNREACH_HOST_UNKNOWN 7
55# define ICMP_UNREACH_ISOLATED 8
56# define ICMP_UNREACH_NET_PROHIB 9
57# define ICMP_UNREACH_HOST_PROHIB 10
58# define ICMP_UNREACH_TOSNET 11
59# define ICMP_UNREACH_TOSHOST 12
60#endif
61/* tru64 has the ones above, but not these */
62#ifndef ICMP_UNREACH_FILTER_PROHIB
63# define ICMP_UNREACH_FILTER_PROHIB 13
64# define ICMP_UNREACH_HOST_PRECEDENCE 14
65# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
66#endif
67
68
69/** typedefs and such **/
70enum states {
71 STATE_OK = 0,
72 STATE_WARNING,
73 STATE_CRITICAL,
74 STATE_UNKNOWN,
75 STATE_DEPENDENT,
76 STATE_OOB
77};
78
79typedef unsigned short range_t; /* type for get_range() -- unimplemented */
80
81typedef struct rta_host {
82 unsigned short id; /* id in **table, and icmp pkts */
83 char *name; /* arg used for adding this host */
84 char *msg; /* icmp error message, if any */
85 struct sockaddr_in saddr_in; /* the address of this host */
86 struct in_addr error_addr; /* stores address of error replies */
87 unsigned long long time_waited; /* total time waited, in usecs */
88 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
89 unsigned char icmp_type, icmp_code; /* type and code from errors */
90 unsigned short flags; /* control/status flags */
91 double rta; /* measured RTA */
92 unsigned char pl; /* measured packet loss */
93 struct rta_host *next; /* linked list */
94} rta_host;
95
96#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
97
98/* threshold structure. all values are maximum allowed, exclusive */
99typedef struct threshold {
100 unsigned char pl; /* max allowed packet loss in percent */
101 unsigned int rta; /* roundtrip time average, microseconds */
102} threshold;
103
104/* the data structure */
105typedef struct icmp_ping_data {
106 struct timeval stime; /* timestamp (saved in protocol struct as well) */
107 unsigned short ping_id;
108} icmp_ping_data;
109
110/* the different modes of this program are as follows:
111 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
112 * MODE_HOSTCHECK: Return immediately upon any sign of life
113 * In addition, sends packets to ALL addresses assigned
114 * to this host (as returned by gethostbyname() or
115 * gethostbyaddr() and expects one host only to be checked at
116 * a time. Therefore, any packet response what so ever will
117 * count as a sign of life, even when received outside
118 * crit.rta limit. Do not misspell any additional IP's.
119 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
120 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
121 * tcp and udp args does this)
122 */
123#define MODE_RTA 0
124#define MODE_HOSTCHECK 1
125#define MODE_ALL 2
126#define MODE_ICMP 3
127
128/* the different ping types we can do
129 * TODO: investigate ARP ping as well */
130#define HAVE_ICMP 1
131#define HAVE_UDP 2
132#define HAVE_TCP 4
133#define HAVE_ARP 8
134
135#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
136#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
137#define IP_HDR_SIZE 20
138#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
139#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
140
141/* various target states */
142#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
143#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
144#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
145#define TSTATE_UNREACH 0x08
146
147/** prototypes **/
148static void usage(unsigned char, char *);
149static u_int get_timevar(const char *);
150static u_int get_timevaldiff(struct timeval *, struct timeval *);
151static int wait_for_reply(int, u_int);
152static int recvfrom_wto(int, char *, unsigned int, struct sockaddr *, u_int *);
153static int send_icmp_ping(int, struct rta_host *);
154static int get_threshold(char *str, threshold *th);
155static void run_checks(void);
156static int add_target(char *);
157static int add_target_ip(char *, struct in_addr *);
158static int handle_random_icmp(struct icmp *, struct sockaddr_in *);
159static unsigned short icmp_checksum(unsigned short *, int);
160static void finish(int);
161static void crash(const char *, ...);
162
163/** external **/
164extern int optind, opterr, optopt;
165extern char *optarg;
166extern char **environ;
167
168/** global variables **/
169static char *progname;
170static struct rta_host **table, *cursor, *list;
171static threshold crit = {80, 500000}, warn = {40, 200000};
172static int mode, protocols, sockets, debug = 0, timeout = 10;
173static unsigned short icmp_pkt_size, icmp_data_size = DEFAULT_PING_DATA_SIZE;
174static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0;
175#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost))
176static unsigned short targets_down = 0, targets = 0, packets = 0;
177#define targets_alive (targets - targets_down)
178static unsigned int retry_interval, pkt_interval, target_interval;
179static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK;
180static pid_t pid;
181static struct timezone tz;
182static struct timeval prog_start;
183static unsigned long long max_completion_time = 0;
184static unsigned char ttl = 0; /* outgoing ttl */
185static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */
186float pkt_backoff_factor = 1.5;
187float target_backoff_factor = 1.5;
188
189/** code start **/
190static void
191crash(const char *fmt, ...)
192{
193 va_list ap;
194
195 printf("%s: ", progname);
196
197 va_start(ap, fmt);
198 vprintf(fmt, ap);
199 va_end(ap);
200
201 if(errno) printf(": %s", strerror(errno));
202 puts("");
203
204 exit(3);
205}
206
207
208static char *
209get_icmp_error_msg(unsigned char icmp_type, unsigned char icmp_code)
210{
211 char *msg = "unreachable";
212
213 if(debug > 1) printf("get_icmp_error_msg(%u, %u)\n", icmp_type, icmp_code);
214 switch(icmp_type) {
215 case ICMP_UNREACH:
216 switch(icmp_code) {
217 case ICMP_UNREACH_NET: msg = "Net unreachable"; break;
218 case ICMP_UNREACH_HOST: msg = "Host unreachable"; break;
219 case ICMP_UNREACH_PROTOCOL: msg = "Protocol unreachable (firewall?)"; break;
220 case ICMP_UNREACH_PORT: msg = "Port unreachable (firewall?)"; break;
221 case ICMP_UNREACH_NEEDFRAG: msg = "Fragmentation needed"; break;
222 case ICMP_UNREACH_SRCFAIL: msg = "Source route failed"; break;
223 case ICMP_UNREACH_ISOLATED: msg = "Source host isolated"; break;
224 case ICMP_UNREACH_NET_UNKNOWN: msg = "Unknown network"; break;
225 case ICMP_UNREACH_HOST_UNKNOWN: msg = "Unknown host"; break;
226 case ICMP_UNREACH_NET_PROHIB: msg = "Network denied (firewall?)"; break;
227 case ICMP_UNREACH_HOST_PROHIB: msg = "Host denied (firewall?)"; break;
228 case ICMP_UNREACH_TOSNET: msg = "Bad TOS for network (firewall?)"; break;
229 case ICMP_UNREACH_TOSHOST: msg = "Bad TOS for host (firewall?)"; break;
230 case ICMP_UNREACH_FILTER_PROHIB: msg = "Prohibited by filter (firewall)"; break;
231 case ICMP_UNREACH_HOST_PRECEDENCE: msg = "Host precedence violation"; break;
232 case ICMP_UNREACH_PRECEDENCE_CUTOFF: msg = "Precedence cutoff"; break;
233 default: msg = "Invalid code"; break;
234 }
235 break;
236
237 case ICMP_TIMXCEED:
238 /* really 'out of reach', or non-existant host behind a router serving
239 * two different subnets */
240 switch(icmp_code) {
241 case ICMP_TIMXCEED_INTRANS: msg = "Time to live exceeded in transit"; break;
242 case ICMP_TIMXCEED_REASS: msg = "Fragment reassembly time exceeded"; break;
243 default: msg = "Invalid code"; break;
244 }
245 break;
246
247 case ICMP_SOURCEQUENCH: msg = "Transmitting too fast"; break;
248 case ICMP_REDIRECT: msg = "Redirect (change route)"; break;
249 case ICMP_PARAMPROB: msg = "Bad IP header (required option absent)"; break;
250
251 /* the following aren't error messages, so ignore */
252 case ICMP_TSTAMP:
253 case ICMP_TSTAMPREPLY:
254 case ICMP_IREQ:
255 case ICMP_IREQREPLY:
256 case ICMP_MASKREQ:
257 case ICMP_MASKREPLY:
258 default: msg = ""; break;
259 }
260
261 return msg;
262}
263
264static int
265handle_random_icmp(struct icmp *p, struct sockaddr_in *addr)
266{
267 struct icmp *sent_icmp = NULL;
268 struct rta_host *host = NULL;
269 unsigned char *ptr;
270
271 if(p->icmp_type == ICMP_ECHO && p->icmp_id == pid) {
272 /* echo request from us to us (pinging localhost) */
273 return 0;
274 }
275
276 ptr = (unsigned char *)p;
277 if(debug) printf("handle_random_icmp(%p, %p)\n", (void *)p, (void *)addr);
278
279 /* only handle a few types, since others can't possibly be replies to
280 * us in a sane network (if it is anyway, it will be counted as lost
281 * at summary time, but not as quickly as a proper response */
282 /* TIMXCEED can be an unreach from a router with multiple IP's which
283 * serves two different subnets on the same interface and a dead host
284 * on one net is pinged from the other. The router will respond to
285 * itself and thus set TTL=0 so as to not loop forever. Even when
286 * TIMXCEED actually sends a proper icmp response we will have passed
287 * too many hops to have a hope of reaching it later, in which case it
288 * indicates overconfidence in the network, poor routing or both. */
289 if(p->icmp_type != ICMP_UNREACH && p->icmp_type != ICMP_TIMXCEED &&
290 p->icmp_type != ICMP_SOURCEQUENCH && p->icmp_type != ICMP_PARAMPROB)
291 {
292 return 0;
293 }
294
295 /* might be for us. At least it holds the original package (according
296 * to RFC 792). If it isn't, just ignore it */
297 sent_icmp = (struct icmp *)(ptr + 28);
298 if(sent_icmp->icmp_type != ICMP_ECHO || sent_icmp->icmp_id != pid ||
299 sent_icmp->icmp_seq >= targets)
300 {
301 if(debug) printf("Packet is no response to a packet we sent\n");
302 return 0;
303 }
304
305 /* it is indeed a response for us */
306 host = table[sent_icmp->icmp_seq];
307 if(debug) {
308 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n",
309 get_icmp_error_msg(p->icmp_type, p->icmp_code),
310 inet_ntoa(addr->sin_addr), host->name);
311 }
312
313 icmp_lost++;
314 host->icmp_lost++;
315 /* don't spend time on lost hosts any more */
316 if(host->flags & FLAG_LOST_CAUSE) return 0;
317
318 /* source quench means we're sending too fast, so increase the
319 * interval and mark this packet lost */
320 if(p->icmp_type == ICMP_SOURCEQUENCH) {
321 pkt_interval *= pkt_backoff_factor;
322 target_interval *= target_backoff_factor;
323 }
324 else {
325 targets_down++;
326 host->flags |= FLAG_LOST_CAUSE;
327 }
328 host->icmp_type = p->icmp_type;
329 host->icmp_code = p->icmp_code;
330 host->error_addr.s_addr = addr->sin_addr.s_addr;
331
332 return 0;
333}
334
335int
336main(int argc, char **argv)
337{
338 int i;
339 char *ptr;
340 long int arg;
341 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
342 int result;
343 struct rta_host *host;
344
345 /* we only need to be setsuid when we get the sockets, so do
346 * that before pointer magic (esp. on network data) */
347 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
348
349 if((icmp_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1)
350 sockets |= HAVE_ICMP;
351 else icmp_sockerrno = errno;
352
353 /* if((udp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1) */
354 /* sockets |= HAVE_UDP; */
355 /* else udp_sockerrno = errno; */
356
357 /* if((tcp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) */
358 /* sockets |= HAVE_TCP; */
359 /* else tcp_sockerrno = errno; */
360
361 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
362 setuid(getuid());
363
364 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
365 environ = NULL;
366
367 /* use the pid to mark packets as ours */
368 pid = getpid();
369 /* printf("pid = %u\n", pid); */
370
371 /* get calling name the old-fashioned way for portability instead
372 * of relying on the glibc-ism __progname */
373 ptr = strrchr(argv[0], '/');
374 if(ptr) progname = &ptr[1];
375 else progname = argv[0];
376
377 /* now set defaults. Use progname to set them initially (allows for
378 * superfast check_host program when target host is up */
379 cursor = list = NULL;
380 table = NULL;
381
382 mode = MODE_RTA;
383 crit.rta = 500000;
384 crit.pl = 80;
385 warn.rta = 200000;
386 warn.pl = 40;
387 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
388 pkt_interval = 80000; /* 80 msec packet interval by default */
389 packets = 5;
390
391 if(!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
392 mode = MODE_ICMP;
393 protocols = HAVE_ICMP;
394 }
395 else if(!strcmp(progname, "check_host")) {
396 mode = MODE_HOSTCHECK;
397 pkt_interval = 1000000;
398 packets = 5;
399 crit.rta = warn.rta = 1000000;
400 crit.pl = warn.pl = 100;
401 }
402 else if(!strcmp(progname, "check_rta_multi")) {
403 mode = MODE_ALL;
404 target_interval = 0;
405 pkt_interval = 50000;
406 packets = 5;
407 }
408
409 /* parse the arguments */
410 for(i = 1; i < argc; i++) {
411 while((arg = getopt(argc, argv, "vhVw:c:n:p:t:H:i:b:I:l:")) != EOF) {
412 switch(arg) {
413 case 'v':
414 debug++;
415 break;
416 case 'b':
417 /* silently ignored for now */
418 break;
419 case 'i':
420 pkt_interval = get_timevar(optarg);
421 break;
422 case 'I':
423 target_interval = get_timevar(optarg);
424 break;
425 case 'w':
426 get_threshold(optarg, &warn);
427 break;
428 case 'c':
429 get_threshold(optarg, &crit);
430 break;
431 case 'n':
432 case 'p':
433 packets = strtoul(optarg, NULL, 0);
434 break;
435 case 't':
436 timeout = strtoul(optarg, NULL, 0);
437 if(!timeout) timeout = 10;
438 break;
439 case 'H':
440 add_target(optarg);
441 break;
442 case 'l':
443 ttl = (unsigned char)strtoul(optarg, NULL, 0);
444 break;
445 case 'd': /* implement later, for cluster checks */
446 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
447 if(ptr) {
448 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
449 }
450 break;
451 case 'h': case 'V': default:
452 usage(arg, NULL);
453 break;
454 }
455 }
456 }
457
458 argv = &argv[optind];
459 while(*argv) {
460 add_target(*argv);
461 argv++;
462 }
463 if(!targets) {
464 errno = 0;
465 crash("No hosts to check");
466 exit(3);
467 }
468
469 if(!sockets) {
470 if(icmp_sock == -1) {
471 errno = icmp_sockerrno;
472 crash("Failed to obtain ICMP socket");
473 return -1;
474 }
475 /* if(udp_sock == -1) { */
476 /* errno = icmp_sockerrno; */
477 /* crash("Failed to obtain UDP socket"); */
478 /* return -1; */
479 /* } */
480 /* if(tcp_sock == -1) { */
481 /* errno = icmp_sockerrno; */
482 /* crash("Failed to obtain TCP socker"); */
483 /* return -1; */
484 /* } */
485 }
486 if(!ttl) ttl = 64;
487
488 if(icmp_sock) {
489 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
490 if(debug) {
491 if(result == -1) printf("setsockopt failed\n");
492 else printf("ttl set to %u\n", ttl);
493 }
494 }
495
496 /* stupid users should be able to give whatever thresholds they want
497 * (nothing will break if they do), but some anal plugin maintainer
498 * will probably add some printf() thing here later, so it might be
499 * best to at least show them where to do it. ;) */
500 if(warn.pl > crit.pl) warn.pl = crit.pl;
501 if(warn.rta > crit.rta) warn.rta = crit.rta;
502 if(warn_down > crit_down) crit_down = warn_down;
503
504 signal(SIGINT, finish);
505 signal(SIGHUP, finish);
506 signal(SIGTERM, finish);
507 signal(SIGALRM, finish);
508 if(debug) printf("Setting alarm timeout to %u seconds\n", timeout);
509 alarm(timeout);
510
511 /* make sure we don't wait any longer than necessary */
512 gettimeofday(&prog_start, &tz);
513 max_completion_time =
514 ((targets * packets * pkt_interval) + (targets * target_interval)) +
515 (targets * packets * crit.rta) + crit.rta;
516
517 if(debug) {
518 printf("packets: %u, targets: %u\n"
519 "target_interval: %0.3f, pkt_interval %0.3f\n"
520 "crit.rta: %0.3f\n"
521 "max_completion_time: %0.3f\n",
522 packets, targets,
523 (float)target_interval / 1000, (float)pkt_interval / 1000,
524 (float)crit.rta / 1000,
525 (float)max_completion_time / 1000);
526 }
527
528 if(debug) {
529 if(max_completion_time > (u_int)timeout * 1000000) {
530 printf("max_completion_time: %llu timeout: %u\n",
531 max_completion_time, timeout);
532 printf("Timout must be at lest %llu\n",
533 max_completion_time / 1000000 + 1);
534 }
535 }
536
537 icmp_pkt_size = icmp_data_size + ICMP_MINLEN;
538 if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
539 if(icmp_pkt_size < sizeof(struct icmp) + sizeof(struct icmp_ping_data)) {
540 icmp_pkt_size = sizeof(struct icmp) + sizeof(struct icmp_ping_data);
541 }
542 if(debug > 2) printf("icmp_pkt_size = %u\n", icmp_pkt_size);
543
544 if(debug) {
545 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n",
546 crit.rta, crit.pl, warn.rta, warn.pl);
547 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n",
548 pkt_interval, target_interval, retry_interval);
549 printf("icmp_pkt_size: %u timeout: %u\n",
550 icmp_pkt_size, timeout);
551 }
552
553 if(packets > 20) {
554 errno = 0;
555 crash("packets is > 20 (%d)", packets);
556 }
557
558 host = list;
559 table = malloc(sizeof(struct rta_host **) * (argc - 1));
560 i = 0;
561 while(host) {
562 host->id = i;
563 table[i] = host;
564 host = host->next;
565 i++;
566 }
567
568 run_checks();
569
570 errno = 0;
571 finish(0);
572
573 return(0);
574}
575
576static void
577run_checks()
578{
579 u_int i, t, result;
580 u_int final_wait, time_passed;
581
582 /* this loop might actually violate the pkt_interval or target_interval
583 * settings, but only if there aren't any packets on the wire which
584 * indicates that the target can handle an increased packet rate */
585 for(i = 0; i < packets; i++) {
586 for(t = 0; t < targets; t++) {
587 /* don't send useless packets */
588 if(!targets_alive) finish(0);
589 if(table[t]->flags & FLAG_LOST_CAUSE) {
590 if(debug) printf("%s is a lost cause. not sending any more\n",
591 table[t]->name);
592 continue;
593 }
594
595 /* we're still in the game, so send next packet */
596 (void)send_icmp_ping(icmp_sock, table[t]);
597 result = wait_for_reply(icmp_sock, target_interval);
598 }
599 result = wait_for_reply(icmp_sock, pkt_interval * targets);
600 }
601
602 if(icmp_pkts_en_route && targets_alive) {
603 time_passed = get_timevaldiff(NULL, NULL);
604 final_wait = max_completion_time - time_passed;
605
606 if(debug) {
607 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n",
608 time_passed, final_wait, max_completion_time);
609 }
610 if(time_passed > max_completion_time) {
611 if(debug) printf("Time passed. Finishing up\n");
612 finish(0);
613 }
614
615 /* catch the packets that might come in within the timeframe, but
616 * haven't yet */
617 if(debug) printf("Waiting for %u micro-seconds (%0.3f msecs)\n",
618 final_wait, (float)final_wait / 1000);
619 result = wait_for_reply(icmp_sock, final_wait);
620 }
621}
622
623/* response structure:
624 * ip header : 20 bytes
625 * icmp header : 28 bytes
626 * icmp echo reply : the rest
627 */
628static int
629wait_for_reply(int sock, u_int t)
630{
631 int n, hlen;
632 static char buf[4096];
633 struct sockaddr_in resp_addr;
634 struct ip *ip;
635 struct icmp *icp, *sent_icmp;
636 struct rta_host *host;
637 struct icmp_ping_data *data;
638 struct timeval wait_start, now;
639 u_int tdiff, i, per_pkt_wait;
640
641 /* if we can't listen or don't have anything to listen to, just return */
642 if(!t || !icmp_pkts_en_route) return 0;
643
644 gettimeofday(&wait_start, &tz);
645
646 i = t;
647 per_pkt_wait = t / icmp_pkts_en_route;
648 while(icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
649 t = per_pkt_wait;
650
651 /* wrap up if all targets are declared dead */
652 if(!targets_alive ||
653 get_timevaldiff(&prog_start, NULL) >= max_completion_time ||
654 (mode == MODE_HOSTCHECK && targets_down))
655 {
656 finish(0);
657 }
658
659 /* reap responses until we hit a timeout */
660 n = recvfrom_wto(sock, buf, sizeof(buf),
661 (struct sockaddr *)&resp_addr, &t);
662 if(!n) {
663 if(debug > 1) {
664 printf("recvfrom_wto() timed out during a %u usecs wait\n",
665 per_pkt_wait);
666 }
667 continue; /* timeout for this one, so keep trying */
668 }
669 if(n < 0) {
670 if(debug) printf("recvfrom_wto() returned errors\n");
671 return n;
672 }
673
674 ip = (struct ip *)buf;
675 if(debug > 1) printf("received %u bytes from %s\n",
676 ntohs(ip->ip_len), inet_ntoa(resp_addr.sin_addr));
677
678/* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */
679/* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */
680 /* alpha headers are decidedly broken. Using an ansi compiler,
681 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
682 * off the bottom 4 bits */
683/* hlen = (ip->ip_vhl & 0x0f) << 2; */
684/* #else */
685 hlen = ip->ip_hl << 2;
686/* #endif */
687
688 if(n < (hlen + ICMP_MINLEN)) {
689 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n",
690 n, hlen + icmp_pkt_size, inet_ntoa(resp_addr.sin_addr));
691 }
692 /* else if(debug) { */
693 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
694 /* hlen, ntohs(ip->ip_len) - hlen, */
695 /* sizeof(struct ip), icmp_pkt_size); */
696 /* } */
697
698 /* check the response */
699 icp = (struct icmp *)(buf + hlen);
700 sent_icmp = (struct icmp *)(buf + hlen + ICMP_MINLEN);
701 /* printf("buf: %p, icp: %p, distance: %u (expected %u)\n", */
702 /* buf, icp, */
703 /* (u_int)icp - (u_int)buf, hlen); */
704 /* printf("buf: %p, sent_icmp: %p, distance: %u (expected %u)\n", */
705 /* buf, sent_icmp, */
706 /* (u_int)sent_icmp - (u_int)buf, hlen + ICMP_MINLEN); */
707
708 if(icp->icmp_id != pid) {
709 handle_random_icmp(icp, &resp_addr);
710 continue;
711 }
712
713 if(icp->icmp_type != ICMP_ECHOREPLY || icp->icmp_seq >= targets) {
714 if(debug > 2) printf("not a proper ICMP_ECHOREPLY\n");
715 handle_random_icmp(icp, &resp_addr);
716 continue;
717 }
718
719 /* this is indeed a valid response */
720 data = (struct icmp_ping_data *)(icp->icmp_data);
721
722 host = table[icp->icmp_seq];
723 gettimeofday(&now, &tz);
724 tdiff = get_timevaldiff(&data->stime, &now);
725
726 host->time_waited += tdiff;
727 host->icmp_recv++;
728 icmp_recv++;
729
730 if(debug) {
731 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u\n",
732 (float)tdiff / 1000, inet_ntoa(resp_addr.sin_addr),
733 ttl, ip->ip_ttl);
734 }
735
736 /* if we're in hostcheck mode, exit with limited printouts */
737 if(mode == MODE_HOSTCHECK) {
738 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
739 "pkt=%u;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
740 host->name, icmp_recv, (float)tdiff / 1000,
741 icmp_recv, packets, (float)tdiff / 1000,
742 (float)warn.rta / 1000, (float)crit.rta / 1000);
743 exit(STATE_OK);
744 }
745 }
746
747 return 0;
748}
749
750/* the ping functions */
751static int
752send_icmp_ping(int sock, struct rta_host *host)
753{
754 static char *buf = NULL; /* re-use so we prevent leaks */
755 long int len;
756 struct icmp *icp;
757 struct icmp_ping_data *data;
758 struct timeval tv;
759 struct sockaddr *addr;
760
761
762 if(sock == -1) {
763 errno = 0;
764 crash("Attempt to send on bogus socket");
765 return -1;
766 }
767 addr = (struct sockaddr *)&host->saddr_in;
768
769 if(!buf) {
770 buf = (char *)malloc(icmp_pkt_size + sizeof(struct ip));
771 if(!buf) {
772 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer",
773 icmp_pkt_size);
774 return -1; /* might be reached if we're in debug mode */
775 }
776 }
777 memset(buf, 0, icmp_pkt_size + sizeof(struct ip));
778
779 if((gettimeofday(&tv, &tz)) == -1) return -1;
780
781 icp = (struct icmp *)buf;
782 icp->icmp_type = ICMP_ECHO;
783 icp->icmp_code = 0;
784 icp->icmp_cksum = 0;
785 icp->icmp_id = pid;
786 icp->icmp_seq = host->id;
787 data = (struct icmp_ping_data *)icp->icmp_data;
788 data->ping_id = 10; /* host->icmp.icmp_sent; */
789 memcpy(&data->stime, &tv, sizeof(struct timeval));
790 icp->icmp_cksum = icmp_checksum((u_short *)icp, icmp_pkt_size);
791
792 len = sendto(sock, buf, icmp_pkt_size, 0, (struct sockaddr *)addr,
793 sizeof(struct sockaddr));
794
795 if(len < 0 || (unsigned int)len != icmp_pkt_size) {
796 if(debug) printf("Failed to send ping to %s\n",
797 inet_ntoa(host->saddr_in.sin_addr));
798 return -1;
799 }
800
801 icmp_sent++;
802 host->icmp_sent++;
803
804 return 0;
805}
806
807static int
808recvfrom_wto(int sock, char *buf, unsigned int len, struct sockaddr *saddr,
809 u_int *timo)
810{
811 u_int slen;
812 int n;
813 struct timeval to, then, now;
814 fd_set rd, wr;
815
816 if(!*timo) {
817 if(debug) printf("*timo is not\n");
818 return 0;
819 }
820
821 to.tv_sec = *timo / 1000000;
822 to.tv_usec = (*timo - (to.tv_sec * 1000000));
823
824 FD_ZERO(&rd);
825 FD_ZERO(&wr);
826 FD_SET(sock, &rd);
827 errno = 0;
828 gettimeofday(&then, &tz);
829 n = select(sock + 1, &rd, &wr, NULL, &to);
830 if(n < 0) crash("select() in recvfrom_wto");
831 gettimeofday(&now, &tz);
832 *timo = get_timevaldiff(&then, &now);
833
834 if(!n) return 0; /* timeout */
835
836 slen = sizeof(struct sockaddr);
837
838 return recvfrom(sock, buf, len, 0, saddr, &slen);
839}
840
841static void
842finish(int sig)
843{
844 u_int i = 0;
845 unsigned char pl;
846 double rta;
847 struct rta_host *host;
848 char *status_string[] =
849 {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
850
851 alarm(0);
852 if(debug > 1) printf("finish(%d) called\n", sig);
853
854 if(icmp_sock != -1) close(icmp_sock);
855 if(udp_sock != -1) close(udp_sock);
856 if(tcp_sock != -1) close(tcp_sock);
857
858 if(debug) {
859 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n",
860 icmp_sent, icmp_recv, icmp_lost);
861 printf("targets: %u targets_alive: %u\n", targets, targets_alive);
862 }
863
864 /* iterate thrice to calculate values, give output, and print perfparse */
865 host = list;
866 while(host) {
867 if(!host->icmp_recv) {
868 /* rta 0 is ofcourse not entirely correct, but will still show up
869 * conspicuosly as missing entries in perfparse and cacti */
870 pl = 100;
871 rta = 0;
872 status = STATE_CRITICAL;
873 /* up the down counter if not already counted */
874 if(!(host->flags & FLAG_LOST_CAUSE) && targets_alive) targets_down++;
875 }
876 else {
877 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
878 rta = (double)host->time_waited / host->icmp_recv;
879 }
880 host->pl = pl;
881 host->rta = rta;
882 if(!status && (pl >= warn.pl || rta >= warn.rta)) status = STATE_WARNING;
883 if(pl >= crit.pl || rta >= crit.rta) status = STATE_CRITICAL;
884
885 host = host->next;
886 }
887 /* this is inevitable */
888 if(!targets_alive) status = STATE_CRITICAL;
889 printf("%s - ", status_string[status]);
890
891 host = list;
892 while(host) {
893 if(debug) puts("");
894 if(i) {
895 if(i < targets) printf(" :: ");
896 else printf("\n");
897 }
898 i++;
899 if(!host->icmp_recv) {
900 status = STATE_CRITICAL;
901 if(host->flags & FLAG_LOST_CAUSE) {
902 printf("%s: %s @ %s. rta nan, lost %d%%",
903 host->name,
904 get_icmp_error_msg(host->icmp_type, host->icmp_code),
905 inet_ntoa(host->error_addr),
906 100);
907 }
908 else { /* not marked as lost cause, so we have no flags for it */
909 printf("%s: rta nan, lost 100%%", host->name);
910 }
911 }
912 else { /* !icmp_recv */
913 printf("%s: rta %0.3fms, lost %u%%",
914 host->name, host->rta / 1000, host->pl);
915 }
916
917 host = host->next;
918 }
919
920 /* iterate once more for pretty perfparse output */
921 printf("|");
922 i = 0;
923 host = list;
924 while(host) {
925 if(debug) puts("");
926 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %spl=%u%%;%u;%u;; ",
927 (targets > 1) ? host->name : "",
928 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000,
929 (targets > 1) ? host->name : "",
930 host->pl, warn.pl, crit.pl);
931
932 host = host->next;
933 }
934
935 /* finish with an empty line */
936 puts("");
937 if(debug) printf("targets: %u, targets_alive: %u\n",
938 targets, targets_alive);
939
940 exit(status);
941}
942
943static u_int
944get_timevaldiff(struct timeval *early, struct timeval *later)
945{
946 u_int ret;
947 struct timeval now;
948
949 if(!later) {
950 gettimeofday(&now, &tz);
951 later = &now;
952 }
953 if(!early) early = &prog_start;
954
955 /* if early > later we return 0 so as to indicate a timeout */
956 if(early->tv_sec > early->tv_sec ||
957 (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec))
958 {
959 return 0;
960 }
961
962 ret = (later->tv_sec - early->tv_sec) * 1000000;
963 ret += later->tv_usec - early->tv_usec;
964
965 return ret;
966}
967
968static int
969add_target_ip(char *arg, struct in_addr *in)
970{
971 struct rta_host *host;
972
973 /* disregard obviously stupid addresses */
974 if(in->s_addr == INADDR_NONE || in->s_addr == INADDR_ANY)
975 return -1;
976
977 /* no point in adding two identical IP's, so don't. ;) */
978 host = list;
979 while(host) {
980 if(host->saddr_in.sin_addr.s_addr == in->s_addr) {
981 if(debug) printf("Identical IP already exists. Not adding %s\n", arg);
982 return -1;
983 }
984 host = host->next;
985 }
986
987 /* add the fresh ip */
988 host = malloc(sizeof(struct rta_host));
989 if(!host) {
990 crash("add_target_ip(%s, %s): malloc(%d) failed",
991 arg, inet_ntoa(*in), sizeof(struct rta_host));
992 }
993 memset(host, 0, sizeof(struct rta_host));
994
995 /* set the values. use calling name for output */
996 host->name = strdup(arg);
997
998 /* fill out the sockaddr_in struct */
999 host->saddr_in.sin_family = AF_INET;
1000 host->saddr_in.sin_addr.s_addr = in->s_addr;
1001
1002 if(!list) list = cursor = host;
1003 else cursor->next = host;
1004
1005 cursor = host;
1006 targets++;
1007
1008 return 0;
1009}
1010
1011/* wrapper for add_target_ip */
1012static int
1013add_target(char *arg)
1014{
1015 int i;
1016 struct hostent *he;
1017 struct in_addr *in, ip;
1018
1019 /* don't resolve if we don't have to */
1020 if((ip.s_addr = inet_addr(arg)) != INADDR_NONE) {
1021 /* don't add all ip's if we were given a specific one */
1022 return add_target_ip(arg, &ip);
1023 /* he = gethostbyaddr((char *)in, sizeof(struct in_addr), AF_INET); */
1024 /* if(!he) return add_target_ip(arg, in); */
1025 }
1026 else {
1027 errno = 0;
1028 he = gethostbyname(arg);
1029 if(!he) {
1030 errno = 0;
1031 crash("Failed to resolve %s", arg);
1032 return -1;
1033 }
1034 }
1035
1036 /* possibly add all the IP's as targets */
1037 for(i = 0; he->h_addr_list[i]; i++) {
1038 in = (struct in_addr *)he->h_addr_list[i];
1039 add_target_ip(arg, in);
1040
1041 /* this is silly, but it works */
1042 if(mode == MODE_HOSTCHECK || mode == MODE_ALL) {
1043 printf("mode: %d\n", mode);
1044 continue;
1045 }
1046 break;
1047 }
1048
1049 return 0;
1050}
1051/*
1052 * u = micro
1053 * m = milli
1054 * s = seconds
1055 * return value is in microseconds
1056 */
1057static u_int
1058get_timevar(const char *str)
1059{
1060 char p, u, *ptr;
1061 unsigned int len;
1062 u_int i, d; /* integer and decimal, respectively */
1063 u_int factor = 1000; /* default to milliseconds */
1064
1065 if(!str) return 0;
1066 len = strlen(str);
1067 if(!len) return 0;
1068
1069 /* unit might be given as ms|m (millisec),
1070 * us|u (microsec) or just plain s, for seconds */
1071 u = p = '\0';
1072 u = str[len - 1];
1073 if(len >= 2 && !isdigit((int)str[len - 2])) p = str[len - 2];
1074 if(p && u == 's') u = p;
1075 else if(!p) p = u;
1076 if(debug > 2) printf("evaluating %s, u: %c, p: %c\n", str, u, p);
1077
1078 if(u == 'u') factor = 1; /* microseconds */
1079 else if(u == 'm') factor = 1000; /* milliseconds */
1080 else if(u == 's') factor = 1000000; /* seconds */
1081 if(debug > 2) printf("factor is %u\n", factor);
1082
1083 i = strtoul(str, &ptr, 0);
1084 if(!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1)
1085 return i * factor;
1086
1087 /* time specified in usecs can't have decimal points, so ignore them */
1088 if(factor == 1) return i;
1089
1090 d = strtoul(ptr + 1, NULL, 0);
1091
1092 /* d is decimal, so get rid of excess digits */
1093 while(d >= factor) d /= 10;
1094
1095 /* the last parenthesis avoids floating point exceptions. */
1096 return ((i * factor) + (d * (factor / 10)));
1097}
1098
1099/* not too good at checking errors, but it'll do (main() should barfe on -1) */
1100static int
1101get_threshold(char *str, threshold *th)
1102{
1103 char *p = NULL, i = 0;
1104
1105 if(!str || !strlen(str) || !th) return -1;
1106
1107 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1108 p = &str[strlen(str) - 1];
1109 while(p != &str[1]) {
1110 if(*p == '%') *p = '\0';
1111 else if(*p == ',' && i) {
1112 *p = '\0'; /* reset it so get_timevar(str) works nicely later */
1113 th->pl = (unsigned char)strtoul(p+1, NULL, 0);
1114 break;
1115 }
1116 i = 1;
1117 p--;
1118 }
1119 th->rta = get_timevar(str);
1120
1121 if(!th->rta) return -1;
1122
1123 if(th->rta > MAXTTL * 1000000) th->rta = MAXTTL * 1000000;
1124 if(th->pl > 100) th->pl = 100;
1125
1126 return 0;
1127}
1128
1129unsigned short
1130icmp_checksum(unsigned short *p, int n)
1131{
1132 register unsigned short cksum;
1133 register long sum = 0;
1134
1135 while(n > 1) {
1136 sum += *p++;
1137 n -= 2;
1138 }
1139
1140 /* mop up the occasional odd byte */
1141 if(n == 1) sum += (unsigned char)*p;
1142
1143 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
1144 sum += (sum >> 16); /* add carry */
1145 cksum = ~sum; /* ones-complement, trunc to 16 bits */
1146
1147 return cksum;
1148}
1149
1150/* make core plugin developers happy (silly, really) */
1151static void
1152usage(unsigned char arg, char *msg)
1153{
1154 if(msg) printf("%s: %s\n", progname, msg);
1155
1156 if(arg == 'V') {
1157 printf("$Id$\n");
1158 exit(STATE_UNKNOWN);
1159 }
1160
1161 printf("Usage: %s [options] [-H] host1 host2 hostn\n\n", progname);
1162
1163 if(arg != 'h') exit(3);
1164
1165 printf("Where options are any combination of:\n"
1166 " * -H | --host specify a target\n"
1167 " * -w | --warn warning threshold (currently %0.3fms,%u%%)\n"
1168 " * -c | --crit critical threshold (currently %0.3fms,%u%%)\n"
1169 " * -n | --packets number of packets to send (currently %u)\n"
1170 " * -i | --interval max packet interval (currently %0.3fms)\n"
1171 " * -I | --hostint max target interval (currently %0.3fms)\n"
1172 " * -l | --ttl TTL on outgoing packets (currently %u)\n"
1173 " * -t | --timeout timeout value (seconds, currently %u)\n"
1174 " * -b | --bytes icmp packet size (currenly ignored)\n"
1175 " -v | --verbose verbosity++\n"
1176 " -h | --help this cruft\n",
1177 (float)warn.rta / 1000, warn.pl, (float)crit.rta / 1000, crit.pl,
1178 packets,
1179 (float)pkt_interval / 1000, (float)target_interval / 1000,
1180 ttl, timeout);
1181
1182 puts("\nThe -H switch is optional. Naming a host (or several) to check is not.\n\n"
1183 "Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%\n"
1184 "packet loss. The default values should work well for most users.\n"
1185 "You can specify different RTA factors using the standardized abbreviations\n"
1186 "us (microseconds), ms (milliseconds, default) or just plain s for seconds.\n\n"
1187 "Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops\n"
1188 "are spent and CRITICAL if >= 14 hops are spent.\n"
1189 "NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not.\n\n"
1190 "The -v switch can be specified several times for increased verbosity.\n\n"
1191 "Long options are currently unsupported.\n\n"
1192 "Options marked with * require an argument\n");
1193
1194 puts("The latest version of this plugin can be found at http://oss.op5.se/nagios\n"
1195 "or https://devel.op5.se/oss until the day it is included in the official\n"
1196 "plugin distribution.\n");
1197
1198 exit(3);
1199}