Code

Applied patch for responses from helpers/relays, cleaned up different coding styles...
[nagiosplug.git] / plugins-root / check_dhcp.c
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 * Copyright (c) 2001-2006 Nagios Plugin Development Team
9 *
10 * License Information:
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 * $Id$
27 *
28 *****************************************************************************/
30 const char *progname = "check_dhcp";
31 const char *revision = "$Revision$";
32 const char *copyright = "2001-2006";
33 const char *email = "nagiosplug-devel@lists.sourceforge.net";
35 #include "common.h"
36 #include "netutils.h"
37 #include "utils.h"
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <sys/time.h>
45 #include <sys/ioctl.h>
46 #include <fcntl.h>
47 #include <getopt.h>
48 #include <sys/socket.h>
49 #include <sys/types.h>
50 #include <netdb.h>
51 #include <netinet/in.h>
52 #include <net/if.h>
53 #include <arpa/inet.h>
55 #if defined( __linux__ )
57 #include <linux/if_ether.h>
58 #include <features.h>
60 #elif defined (__bsd__)
62 #include <netinet/if_ether.h>
63 #include <sys/sysctl.h>
64 #include <net/if_dl.h>
66 #elif defined(__sun__) || defined(__solaris__) || defined(__hpux__)
68 #define INSAP 22 
69 #define OUTSAP 24 
71 #include <signal.h>
72 #include <ctype.h>
73 #include <sys/stropts.h>
74 #include <sys/poll.h>
75 #include <sys/dlpi.h>
77 #define bcopy(source, destination, length) memcpy(destination, source, length)
79 #define AREA_SZ 5000            /* buffer length in bytes */ 
80 static u_long ctl_area[AREA_SZ];
81 static u_long dat_area[AREA_SZ];
82 static struct strbuf ctl = {AREA_SZ, 0, (char *)ctl_area};
83 static struct strbuf dat = {AREA_SZ, 0, (char *)dat_area};
85 #define GOT_CTRL 1 
86 #define GOT_DATA 2 
87 #define GOT_BOTH 3 
88 #define GOT_INTR 4 
89 #define GOT_ERR 128 
91 #define u_int8_t         uint8_t
92 #define u_int16_t       uint16_t
93 #define u_int32_t       uint32_t
95 static int get_msg(int);
96 static int check_ctrl(int);
97 static int put_ctrl(int, int, int);
98 static int put_both(int, int, int, int);
99 static int dl_open(const char *, int, int *);
100 static int dl_bind(int, int, u_char *);
101 long mac_addr_dlpi( const char *, int, u_char *);
103 #endif
107 /**** Common definitions ****/
109 #define OK                0
110 #define ERROR             -1
112 #define FALSE             0
113 #define TRUE              1
116 /**** DHCP definitions ****/
118 #define MAX_DHCP_CHADDR_LENGTH           16
119 #define MAX_DHCP_SNAME_LENGTH            64
120 #define MAX_DHCP_FILE_LENGTH             128
121 #define MAX_DHCP_OPTIONS_LENGTH          312
124 typedef struct dhcp_packet_struct{
125         u_int8_t  op;                   /* packet type */
126         u_int8_t  htype;                /* type of hardware address for this machine (Ethernet, etc) */
127         u_int8_t  hlen;                 /* length of hardware address (of this machine) */
128         u_int8_t  hops;                 /* hops */
129         u_int32_t xid;                  /* random transaction id number - chosen by this machine */
130         u_int16_t secs;                 /* seconds used in timing */
131         u_int16_t flags;                /* flags */
132         struct in_addr ciaddr;          /* IP address of this machine (if we already have one) */
133         struct in_addr yiaddr;          /* IP address of this machine (offered by the DHCP server) */
134         struct in_addr siaddr;          /* IP address of DHCP server */
135         struct in_addr giaddr;          /* IP address of DHCP relay */
136         unsigned char chaddr [MAX_DHCP_CHADDR_LENGTH];      /* hardware address of this machine */
137         char sname [MAX_DHCP_SNAME_LENGTH];    /* name of DHCP server */
138         char file [MAX_DHCP_FILE_LENGTH];      /* boot file name (used for diskless booting?) */
139         char options[MAX_DHCP_OPTIONS_LENGTH];  /* options */
140         }dhcp_packet;
143 typedef struct dhcp_offer_struct{
144         struct in_addr server_address;   /* address of DHCP server that sent this offer */
145         struct in_addr offered_address;  /* the IP address that was offered to us */
146         u_int32_t lease_time;            /* lease time in seconds */
147         u_int32_t renewal_time;          /* renewal time in seconds */
148         u_int32_t rebinding_time;        /* rebinding time in seconds */
149         struct dhcp_offer_struct *next;
150         }dhcp_offer;
153 typedef struct requested_server_struct{
154         struct in_addr server_address;
155         int answered;
156         struct requested_server_struct *next;
157         }requested_server;
160 #define BOOTREQUEST     1
161 #define BOOTREPLY       2
163 #define DHCPDISCOVER    1
164 #define DHCPOFFER       2
165 #define DHCPREQUEST     3
166 #define DHCPDECLINE     4
167 #define DHCPACK         5
168 #define DHCPNACK        6
169 #define DHCPRELEASE     7
171 #define DHCP_OPTION_MESSAGE_TYPE        53
172 #define DHCP_OPTION_HOST_NAME           12
173 #define DHCP_OPTION_BROADCAST_ADDRESS   28
174 #define DHCP_OPTION_REQUESTED_ADDRESS   50
175 #define DHCP_OPTION_LEASE_TIME          51
176 #define DHCP_OPTION_RENEWAL_TIME        58
177 #define DHCP_OPTION_REBINDING_TIME      59
179 #define DHCP_INFINITE_TIME              0xFFFFFFFF
181 #define DHCP_BROADCAST_FLAG 32768
183 #define DHCP_SERVER_PORT   67
184 #define DHCP_CLIENT_PORT   68
186 #define ETHERNET_HARDWARE_ADDRESS            1     /* used in htype field of dhcp packet */
187 #define ETHERNET_HARDWARE_ADDRESS_LENGTH     6     /* length of Ethernet hardware addresses */
189 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]="";
191 char network_interface_name[IFNAMSIZ]="eth0";
193 u_int32_t packet_xid=0;
195 u_int32_t dhcp_lease_time=0;
196 u_int32_t dhcp_renewal_time=0;
197 u_int32_t dhcp_rebinding_time=0;
199 int dhcpoffer_timeout=2;
201 dhcp_offer *dhcp_offer_list=NULL;
202 requested_server *requested_server_list=NULL;
204 int valid_responses=0;     /* number of valid DHCPOFFERs we received */
205 int requested_servers=0;   
206 int requested_responses=0;
208 int request_specific_address=FALSE;
209 int received_requested_address=FALSE;
210 int verbose=0;
211 struct in_addr requested_address;
214 int process_arguments(int, char **);
215 int call_getopt(int, char **);
216 int validate_arguments(void);
217 void print_usage(void);
218 void print_help(void);
220 int get_hardware_address(int,char *);
222 int send_dhcp_discover(int);
223 int get_dhcp_offer(int);
225 int get_results(void);
227 int add_dhcp_offer(struct in_addr,dhcp_packet *);
228 int free_dhcp_offer_list(void);
229 int free_requested_server_list(void);
231 int create_dhcp_socket(void);
232 int close_dhcp_socket(int);
233 int send_dhcp_packet(void *,int,int,struct sockaddr_in *);
234 int receive_dhcp_packet(void *,int,int,int,struct sockaddr_in *);
238 int main(int argc, char **argv){
239         int dhcp_socket;
240         int result = STATE_UNKNOWN;
242         setlocale (LC_ALL, "");
243         bindtextdomain (PACKAGE, LOCALEDIR);
244         textdomain (PACKAGE);
245         
246         if(process_arguments(argc,argv)!=OK){
247                 usage4 (_("Could not parse arguments"));
248                 }
250         /* create socket for DHCP communications */
251         dhcp_socket=create_dhcp_socket();
253         /* get hardware address of client machine */
254         get_hardware_address(dhcp_socket,network_interface_name);
256         /* send DHCPDISCOVER packet */
257         send_dhcp_discover(dhcp_socket);
259         /* wait for a DHCPOFFER packet */
260         get_dhcp_offer(dhcp_socket);
262         /* close socket we created */
263         close_dhcp_socket(dhcp_socket);
265         /* determine state/plugin output to return */
266         result=get_results();
268         /* free allocated memory */
269         free_dhcp_offer_list();
270         free_requested_server_list();
272         return result;
273         }
277 /* determines hardware address on client machine */
278 int get_hardware_address(int sock,char *interface_name){
280         int i;
282 #if defined(__linux__)
283         struct ifreq ifr;
285         strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name));
286         
287         /* try and grab hardware address of requested interface */
288         if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){
289                 printf(_("Error: Could not get hardware address of interface '%s'\n"),interface_name);
290                 exit(STATE_UNKNOWN);
291                 }
293         memcpy(&client_hardware_address[0],&ifr.ifr_hwaddr.sa_data,6);
295 #elif defined(__bsd__)
296                                                 /* King 2004    see ACKNOWLEDGEMENTS */
298         int                     mib[6], len;
299         char                    *buf;
300         unsigned char           *ptr;
301         struct if_msghdr        *ifm;
302         struct sockaddr_dl      *sdl;
304         mib[0] = CTL_NET;
305         mib[1] = AF_ROUTE;
306         mib[2] = 0;
307         mib[3] = AF_LINK;
308         mib[4] = NET_RT_IFLIST;
310         if((mib[5] = if_nametoindex(interface_name)) == 0){
311                 printf(_("Error: if_nametoindex error - %s.\n"), strerror(errno));
312                 exit(STATE_UNKNOWN);
313                 }
315         if(sysctl(mib, 6, NULL, &len, NULL, 0) < 0){
316                 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno));
317                 exit(STATE_UNKNOWN);
318                 }
320         if((buf = malloc(len)) == NULL){
321                 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno));
322                 exit(4);
323                 }
325         if(sysctl(mib, 6, buf, &len, NULL, 0) < 0){
326                 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno));
327                 exit(STATE_UNKNOWN);
328                 }
330         ifm = (struct if_msghdr *)buf;
331         sdl = (struct sockaddr_dl *)(ifm + 1);
332         ptr = (unsigned char *)LLADDR(sdl);
333         memcpy(&client_hardware_address[0], ptr, 6) ;
334                                                 /* King 2004 */
336 #elif defined(__sun__) || defined(__solaris__)
338                                                 /* Kompf 2000-2003      see ACKNOWLEDGEMENTS */
339         long stat;
340         char dev[20] = "/dev/";
341         char *p;
342         int unit;
344         for(p = interface_name; *p && isalpha(*p); p++)
345                 /* no-op */ ;
346         if( p != '\0' ){
347                 unit = atoi(p) ;
348                 *p = '\0' ;
349                 strncat(dev, interface_name, 6) ;
350                 } 
351         else{
352                 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name);
353                 exit(STATE_UNKNOWN);
354                 }
355         stat = mac_addr_dlpi(dev, unit, client_hardware_address);
356         if(stat != 0){
357                 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit);
358                 exit(STATE_UNKNOWN);
359                 }
361 #elif defined(__hpux__)
363         long stat;
364         char dev[20] = "/dev/dlpi" ;
365         int unit = 0;
367         stat = mac_addr_dlpi(dev, unit, client_hardware_address);
368         if(stat != 0){
369                 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit);
370                 exit(STATE_UNKNOWN);
371                 }
372                                                 /* Kompf 2000-2003 */
374 #else
375         printf(_("Error: can't get MAC address for this architecture.\n"));
376         exit(STATE_UNKNOWN);
377 #endif
379         if(verbose){ 
380                 printf(_("Hardware address: "));
381                 for (i=0; i<6; ++i)
382                         printf("%2.2x", client_hardware_address[i]);
383                 printf( "\n");
384                 }
386         return OK;
387         }
390 /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
391 int send_dhcp_discover(int sock){
392         dhcp_packet discover_packet;
393         struct sockaddr_in sockaddr_broadcast;
396         /* clear the packet data structure */
397         bzero(&discover_packet,sizeof(discover_packet));
400         /* boot request flag (backward compatible with BOOTP servers) */
401         discover_packet.op=BOOTREQUEST;
403         /* hardware address type */
404         discover_packet.htype=ETHERNET_HARDWARE_ADDRESS;
406         /* length of our hardware address */
407         discover_packet.hlen=ETHERNET_HARDWARE_ADDRESS_LENGTH;
409         discover_packet.hops=0;
411         /* transaction id is supposed to be random */
412         srand(time(NULL));
413         packet_xid=random();
414         discover_packet.xid=htonl(packet_xid);
416         /**** WHAT THE HECK IS UP WITH THIS?!?  IF I DON'T MAKE THIS CALL, ONLY ONE SERVER RESPONSE IS PROCESSED!!!! ****/
417         /* downright bizzarre... */
418         ntohl(discover_packet.xid);
420         /*discover_packet.secs=htons(65535);*/
421         discover_packet.secs=0xFF;
423         /* tell server it should broadcast its response */ 
424         discover_packet.flags=htons(DHCP_BROADCAST_FLAG);
426         /* our hardware address */
427         memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH);
429         /* first four bytes of options field is magic cookie (as per RFC 2132) */
430         discover_packet.options[0]='\x63';
431         discover_packet.options[1]='\x82';
432         discover_packet.options[2]='\x53';
433         discover_packet.options[3]='\x63';
435         /* DHCP message type is embedded in options field */
436         discover_packet.options[4]=DHCP_OPTION_MESSAGE_TYPE;    /* DHCP message type option identifier */
437         discover_packet.options[5]='\x01';               /* DHCP message option length in bytes */
438         discover_packet.options[6]=DHCPDISCOVER;
440         /* the IP address we're requesting */
441         if(request_specific_address==TRUE){
442                 discover_packet.options[7]=DHCP_OPTION_REQUESTED_ADDRESS;
443                 discover_packet.options[8]='\x04';
444                 memcpy(&discover_packet.options[9],&requested_address,sizeof(requested_address));
445                 }
446         
447         /* send the DHCPDISCOVER packet to broadcast address */
448         sockaddr_broadcast.sin_family=AF_INET;
449         sockaddr_broadcast.sin_port=htons(DHCP_SERVER_PORT);
450         sockaddr_broadcast.sin_addr.s_addr=INADDR_BROADCAST;
451         bzero(&sockaddr_broadcast.sin_zero,sizeof(sockaddr_broadcast.sin_zero));
454         if(verbose){
455                 printf(_("DHCPDISCOVER to %s port %d\n"),inet_ntoa(sockaddr_broadcast.sin_addr),ntohs(sockaddr_broadcast.sin_port));
456                 printf("DHCPDISCOVER XID: %lu (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid));
457                 printf("DHCDISCOVER ciaddr:  %s\n",inet_ntoa(discover_packet.ciaddr));
458                 printf("DHCDISCOVER yiaddr:  %s\n",inet_ntoa(discover_packet.yiaddr));
459                 printf("DHCDISCOVER siaddr:  %s\n",inet_ntoa(discover_packet.siaddr));
460                 printf("DHCDISCOVER giaddr:  %s\n",inet_ntoa(discover_packet.giaddr));
461                 }
463         /* send the DHCPDISCOVER packet out */
464         send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast);
466         if(verbose) 
467                 printf("\n\n");
469         return OK;
470         }
475 /* waits for a DHCPOFFER message from one or more DHCP servers */
476 int get_dhcp_offer(int sock){
477         dhcp_packet offer_packet;
478         struct sockaddr_in source;
479         struct sockaddr_in via;
480         int result=OK;
481         int timeout=1;
482         int responses=0;
483         int x;
484         time_t start_time;
485         time_t current_time;
487         time(&start_time);
489         /* receive as many responses as we can */
490         for(responses=0,valid_responses=0;;){
492                 time(&current_time);
493                 if((current_time-start_time)>=dhcpoffer_timeout)
494                         break;
496                 if(verbose) 
497                         printf("\n\n");
499                 bzero(&source,sizeof(source));
500                 bzero(&via,sizeof(via));
501                 bzero(&offer_packet,sizeof(offer_packet));
503                 result=OK;
504                 result=receive_dhcp_packet(&offer_packet,sizeof(offer_packet),sock,dhcpoffer_timeout,&source);
505                 
506                 if(result!=OK){
507                         if(verbose)
508                                 printf(_("Result=ERROR\n"));
510                         continue;
511                         }
512                 else{
513                         if(verbose) 
514                                 printf(_("Result=OK\n"));
516                         responses++;
517                         }
519                 /* The "source" is either a server or a relay. */
520                 /* Save a copy of "source" into "via" even if it's via itself */
521                 memcpy(&via,&source,sizeof(source)) ;
523                 /* If siaddr is non-zero, set "source" to siaddr */
524                 if(offer_packet.siaddr.s_addr != 0L){
525                         source.sin_addr.s_addr = offer_packet.siaddr.s_addr ;
526                         }
528                 if(verbose){
529                         printf(_("DHCPOFFER from IP address %s"),inet_ntoa(source.sin_addr));
530                         printf(_(" via %s\n"),inet_ntoa(via.sin_addr));
531                         printf("DHCPOFFER XID: %lu (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid));
532                         }
534                 /* check packet xid to see if its the same as the one we used in the discover packet */
535                 if(ntohl(offer_packet.xid)!=packet_xid){
536                         if(verbose)
537                                 printf(_("DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - ignoring packet\n"),ntohl(offer_packet.xid),packet_xid);
539                         continue;
540                         }
542                 /* check hardware address */
543                 result=OK;
544                 if(verbose)
545                         printf("DHCPOFFER chaddr: ");
547                 for(x=0;x<ETHERNET_HARDWARE_ADDRESS_LENGTH;x++){
548                         if(verbose)
549                                 printf("%02X",(unsigned char)offer_packet.chaddr[x]);
551                         if(offer_packet.chaddr[x]!=client_hardware_address[x])
552                                 result=ERROR;
553                         }
554                 if(verbose)
555                         printf("\n");
557                 if(result==ERROR){
558                         if(verbose) 
559                                 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
561                         continue;
562                         }
564                 if(verbose){
565                         printf("DHCPOFFER ciaddr: %s\n",inet_ntoa(offer_packet.ciaddr));
566                         printf("DHCPOFFER yiaddr: %s\n",inet_ntoa(offer_packet.yiaddr));
567                         printf("DHCPOFFER siaddr: %s\n",inet_ntoa(offer_packet.siaddr));
568                         printf("DHCPOFFER giaddr: %s\n",inet_ntoa(offer_packet.giaddr));
569                         }
571                 add_dhcp_offer(source.sin_addr,&offer_packet);
573                 valid_responses++;
574                 }
576         if(verbose){
577                 printf(_("Total responses seen on the wire: %d\n"),responses);
578                 printf(_("Valid responses for this machine: %d\n"),valid_responses);
579                 }
581         return OK;
582         }
586 /* sends a DHCP packet */
587 int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest){
588         struct sockaddr_in myname;
589         int result;
591         result=sendto(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)dest,sizeof(*dest));
593         if(verbose) 
594                 printf(_("send_dhcp_packet result: %d\n"),result);
596         if(result<0)
597                 return ERROR;
599         return OK;
600         }
604 /* receives a DHCP packet */
605 int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address){
606         struct timeval tv;
607         fd_set readfds;
608         int recv_result;
609         socklen_t address_size;
610         struct sockaddr_in source_address;
613         /* wait for data to arrive (up time timeout) */
614         tv.tv_sec=timeout;
615         tv.tv_usec=0;
616         FD_ZERO(&readfds);
617         FD_SET(sock,&readfds);
618         select(sock+1,&readfds,NULL,NULL,&tv);
620         /* make sure some data has arrived */
621         if(!FD_ISSET(sock,&readfds)){
622                 if(verbose)
623                         printf(_("No (more) data received\n"));
624                 return ERROR;
625                 }
627         else{
629                 /* 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
630                    not being interpreted correctly.  sigh... */
631                 bzero(&source_address,sizeof(source_address));
632                 address_size=sizeof(source_address);
633                 recv_result=recvfrom(sock,(char *)buffer,buffer_size,MSG_PEEK,(struct sockaddr *)&source_address,&address_size);
634                 if(verbose)
635                         printf("recv_result_1: %d\n",recv_result);
636                 recv_result=recvfrom(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)&source_address,&address_size);
637                 if(verbose)
638                         printf("recv_result_2: %d\n",recv_result);
640                 if(recv_result==-1){
641                         if(verbose){
642                                 printf(_("recvfrom() failed, "));
643                                 printf("errno: (%d) -> %s\n",errno,strerror(errno));
644                                 }
645                         return ERROR;
646                         }
647                 else{
648                         if(verbose){
649                                 printf(_("receive_dhcp_packet() result: %d\n"),recv_result);
650                                 printf(_("receive_dhcp_packet() source: %s\n"),inet_ntoa(source_address.sin_addr));
651                                 }
653                         memcpy(address,&source_address,sizeof(source_address));
654                         return OK;
655                         }
656                 }
658         return OK;
659         }
662 /* creates a socket for DHCP communication */
663 int create_dhcp_socket(void){
664         struct sockaddr_in myname;
665         struct ifreq interface;
666         int sock;
667         int flag=1;
669         /* Set up the address we're going to bind to. */
670         bzero(&myname,sizeof(myname));
671         myname.sin_family=AF_INET;
672         myname.sin_port=htons(DHCP_CLIENT_PORT);
673         myname.sin_addr.s_addr=INADDR_ANY;                 /* listen on any address */
674         bzero(&myname.sin_zero,sizeof(myname.sin_zero));
676         /* create a socket for DHCP communications */
677         sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
678         if(sock<0){
679                 printf(_("Error: Could not create socket!\n"));
680                 exit(STATE_UNKNOWN);
681                 }
683         if(verbose)
684                 printf("DHCP socket: %d\n",sock);
686         /* set the reuse address flag so we don't get errors when restarting */
687         flag=1;
688         if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
689                 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
690                 exit(STATE_UNKNOWN);
691                 }
693         /* set the broadcast option - we need this to listen to DHCP broadcast messages */
694         if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
695                 printf(_("Error: Could not set broadcast option on DHCP socket!\n"));
696                 exit(STATE_UNKNOWN);
697                 }
699         /* bind socket to interface */
700 #if defined(__linux__)
701         strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ);
702         if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){
703                 printf(_("Error: Could not bind socket to interface %s.  Check your privileges...\n"),network_interface_name);
704                 exit(STATE_UNKNOWN);
705                 }
707 #else
708         strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ);
709 #endif
711         /* bind the socket */
712         if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
713                 printf(_("Error: Could not bind to DHCP socket (port %d)!  Check your privileges...\n"),DHCP_CLIENT_PORT);
714                 exit(STATE_UNKNOWN);
715                 }
717         return sock;
718         }
721 /* closes DHCP socket */
722 int close_dhcp_socket(int sock){
724         close(sock);
726         return OK;
727         }
730 /* adds a requested server address to list in memory */
731 int add_requested_server(struct in_addr server_address){
732         requested_server *new_server;
734         new_server=(requested_server *)malloc(sizeof(requested_server));
735         if(new_server==NULL)
736                 return ERROR;
738         new_server->server_address=server_address;
739         new_server->answered=FALSE;
741         new_server->next=requested_server_list;
742         requested_server_list=new_server;
744         requested_servers++;
746         if(verbose)
747                 printf(_("Requested server address: %s\n"),inet_ntoa(new_server->server_address));
749         return OK;
750         }
755 /* adds a DHCP OFFER to list in memory */
756 int add_dhcp_offer(struct in_addr source,dhcp_packet *offer_packet){
757         dhcp_offer *new_offer;
758         int x;
759         int y;
760         unsigned option_type;
761         unsigned option_length;
763         if(offer_packet==NULL)
764                 return ERROR;
766         /* process all DHCP options present in the packet */
767         for(x=4;x<MAX_DHCP_OPTIONS_LENGTH;){
769                 /* end of options (0 is really just a pad, but bail out anyway) */
770                 if((int)offer_packet->options[x]==-1 || (int)offer_packet->options[x]==0)
771                         break;
773                 /* get option type */
774                 option_type=offer_packet->options[x++];
776                 /* get option length */
777                 option_length=offer_packet->options[x++];
779                 if(verbose) 
780                         printf("Option: %d (0x%02X)\n",option_type,option_length);
782                 /* get option data */
783                 if(option_type==DHCP_OPTION_LEASE_TIME){
784                         memcpy(&dhcp_lease_time, &offer_packet->options[x],sizeof(dhcp_lease_time));
785                         dhcp_lease_time = ntohl(dhcp_lease_time);
786                         }
787                 if(option_type==DHCP_OPTION_RENEWAL_TIME){
788                         memcpy(&dhcp_renewal_time, &offer_packet->options[x],sizeof(dhcp_renewal_time));
789                         dhcp_renewal_time = ntohl(dhcp_renewal_time);
790                         }
791                 if(option_type==DHCP_OPTION_REBINDING_TIME){
792                         memcpy(&dhcp_rebinding_time, &offer_packet->options[x],sizeof(dhcp_rebinding_time));
793                         dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
794                         }
796                 /* skip option data we're ignoring */
797                 else
798                         for(y=0;y<option_length;y++,x++);
799                 }
801         if(verbose){
802                 if(dhcp_lease_time==DHCP_INFINITE_TIME)
803                         printf(_("Lease Time: Infinite\n"));
804                 else
805                         printf(_("Lease Time: %lu seconds\n"),(unsigned long)dhcp_lease_time);
806                 if(dhcp_renewal_time==DHCP_INFINITE_TIME)
807                         printf(_("Renewal Time: Infinite\n"));
808                 else
809                         printf(_("Renewal Time: %lu seconds\n"),(unsigned long)dhcp_renewal_time);
810                 if(dhcp_rebinding_time==DHCP_INFINITE_TIME)
811                         printf(_("Rebinding Time: Infinite\n"));
812                 printf(_("Rebinding Time: %lu seconds\n"),(unsigned long)dhcp_rebinding_time);
813                 }
815         new_offer=(dhcp_offer *)malloc(sizeof(dhcp_offer));
817         if(new_offer==NULL)
818                 return ERROR;
820         new_offer->server_address=source;
821         new_offer->offered_address=offer_packet->yiaddr;
822         new_offer->lease_time=dhcp_lease_time;
823         new_offer->renewal_time=dhcp_renewal_time;
824         new_offer->rebinding_time=dhcp_rebinding_time;
827         if(verbose){
828                 printf(_("Added offer from server @ %s"),inet_ntoa(new_offer->server_address));
829                 printf(_(" of IP address %s\n"),inet_ntoa(new_offer->offered_address));
830                 }
832         /* add new offer to head of list */
833         new_offer->next=dhcp_offer_list;
834         dhcp_offer_list=new_offer;
836         return OK;
837         }
840 /* frees memory allocated to DHCP OFFER list */
841 int free_dhcp_offer_list(void){
842         dhcp_offer *this_offer;
843         dhcp_offer *next_offer;
845         for(this_offer=dhcp_offer_list;this_offer!=NULL;this_offer=next_offer){
846                 next_offer=this_offer->next;
847                 free(this_offer);
848                 }
850         return OK;
851         }
854 /* frees memory allocated to requested server list */
855 int free_requested_server_list(void){
856         requested_server *this_server;
857         requested_server *next_server;
859         for(this_server=requested_server_list;this_server!=NULL;this_server=next_server){
860                 next_server=this_server->next;
861                 free(this_server);
862                 }
863         
864         return OK;
865         }
868 /* gets state and plugin output to return */
869 int get_results(void){
870         dhcp_offer *temp_offer;
871         requested_server *temp_server;
872         int result;
873         u_int32_t max_lease_time=0;
875         received_requested_address=FALSE;
877         /* checks responses from requested servers */
878         requested_responses=0;
879         if(requested_servers>0){
881                 for(temp_server=requested_server_list;temp_server!=NULL;temp_server=temp_server->next){
883                         for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
885                                 /* get max lease time we were offered */
886                                 if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
887                                         max_lease_time=temp_offer->lease_time;
888                                 
889                                 /* see if we got the address we requested */
890                                 if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
891                                         received_requested_address=TRUE;
893                                 /* see if the servers we wanted a response from talked to us or not */
894                                 if(!memcmp(&temp_offer->server_address,&temp_server->server_address,sizeof(temp_server->server_address))){
895                                         if(verbose){
896                                                 printf(_("DHCP Server Match: Offerer=%s"),inet_ntoa(temp_offer->server_address));
897                                                 printf(_(" Requested=%s"),inet_ntoa(temp_server->server_address));
898                                                 if(temp_server->answered) 
899                                                         printf(_(" (duplicate)"));
900                                                 printf(_("\n"));
901                                                 }
902                                         if(temp_server->answered == FALSE){
903                                                 requested_responses++;
904                                                 temp_server->answered=TRUE;
905                                                 }
906                                         }
907                                 }
908                         }
910                 }
912         /* else check and see if we got our requested address from any server */
913         else{
915                 for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
917                         /* get max lease time we were offered */
918                         if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
919                                 max_lease_time=temp_offer->lease_time;
920                                 
921                         /* see if we got the address we requested */
922                         if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
923                                 received_requested_address=TRUE;
924                         }
925                 }
927         result=STATE_OK;
928         if(valid_responses==0)
929                 result=STATE_CRITICAL;
930         else if(requested_servers>0 && requested_responses==0)
931                 result=STATE_CRITICAL;
932         else if(requested_responses<requested_servers)
933                 result=STATE_WARNING;
934         else if(request_specific_address==TRUE && received_requested_address==FALSE)
935                 result=STATE_WARNING;
937         if(result==0)               /* garrett honeycutt 2005 */
938                 printf("OK: ");
939         else if(result==1)
940                 printf("WARNING: ");
941         else if(result==2)
942                 printf("CRITICAL: ");
943         else if(result==3)
944                 printf("UNKNOWN: ");
946         /* we didn't receive any DHCPOFFERs */
947         if(dhcp_offer_list==NULL){
948                 printf(_("No DHCPOFFERs were received.\n"));
949                 return result;
950                 }
952         printf(_("Received %d DHCPOFFER(s)"),valid_responses);
954         if(requested_servers>0)
955                 printf(_(", %s%d of %d requested servers responded"),((requested_responses<requested_servers) && requested_responses>0)?"only ":"",requested_responses,requested_servers);
957         if(request_specific_address==TRUE)
958                 printf(_(", requested address (%s) was %soffered"),inet_ntoa(requested_address),(received_requested_address==TRUE)?"":_("not "));
960         printf(_(", max lease time = "));
961         if(max_lease_time==DHCP_INFINITE_TIME)
962                 printf(_("Infinity"));
963         else
964                 printf("%lu sec",(unsigned long)max_lease_time);
966         printf(".\n");
968         return result;
969         }
972 /* process command-line arguments */
973 int process_arguments(int argc, char **argv){
974         int c;
976         if(argc<1)
977                 return ERROR;
979         c=0;
980         while((c+=(call_getopt(argc-c,&argv[c])))<argc){
982                 /*
983                 if(is_option(argv[c]))
984                         continue;
985                 */
986                 }
988         return validate_arguments();
989         }
993 int call_getopt(int argc, char **argv){
994         int c=0;
995         int i=0;
996         struct in_addr ipaddress;
998         int option_index = 0;
999         static struct option long_options[] =
1000         { 
1001                 {"serverip",       required_argument,0,'s'},
1002                 {"requestedip",    required_argument,0,'r'},
1003                 {"timeout",        required_argument,0,'t'},
1004                 {"interface",      required_argument,0,'i'},
1005                 {"verbose",        no_argument,      0,'v'},
1006                 {"version",        no_argument,      0,'V'},
1007                 {"help",           no_argument,      0,'h'},
1008                 {0,0,0,0}
1009         };
1011         while(1){
1012                 c=getopt_long(argc,argv,"+hVvt:s:r:t:i:",long_options,&option_index);
1014                 i++;
1016                 if(c==-1||c==EOF||c==1)
1017                         break;
1019                 switch(c){
1020                 case 'w':
1021                 case 'r':
1022                 case 't':
1023                 case 'i':
1024                         i++;
1025                         break;
1026                 default:
1027                         break;
1028                         }
1030                 switch(c){
1032                 case 's': /* DHCP server address */
1033                         if(inet_aton(optarg,&ipaddress))
1034                                 add_requested_server(ipaddress);
1035                         /*
1036                         else
1037                                 usage("Invalid server IP address\n");
1038                         */
1039                         break;
1041                 case 'r': /* address we are requested from DHCP servers */
1042                         if(inet_aton(optarg,&ipaddress)){
1043                                 requested_address=ipaddress;
1044                                 request_specific_address=TRUE;
1045                                 }
1046                         /*
1047                         else
1048                                 usage("Invalid requested IP address\n");
1049                         */
1050                         break;
1052                 case 't': /* timeout */
1054                         /*
1055                         if(is_intnonneg(optarg))
1056                         */
1057                         if(atoi(optarg)>0)
1058                                 dhcpoffer_timeout=atoi(optarg);
1059                         /*
1060                         else
1061                                 usage("Time interval must be a nonnegative integer\n");
1062                         */
1063                         break;
1065                 case 'i': /* interface name */
1067                         strncpy(network_interface_name,optarg,sizeof(network_interface_name)-1);
1068                         network_interface_name[sizeof(network_interface_name)-1]='\x0';
1070                         break;
1072                 case 'V': /* version */
1073                         print_revision(progname,revision);
1074                         exit(STATE_OK);
1076                 case 'h': /* help */
1077                         print_help();
1078                         exit(STATE_OK);
1080                 case 'v': /* verbose */
1081                         verbose=1;
1082                         break;
1084                 case '?': /* help */
1085                         usage2 (_("Unknown argument"), optarg);
1086                         break;
1088                 default:
1089                         break;
1090                         }
1091                 }
1093         return i;
1094         }
1097 int validate_arguments(void){
1099         return OK;
1100         }
1103 #if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
1104 /* Kompf 2000-2003      see ACKNOWLEDGEMENTS */
1106 /* get a message from a stream; return type of message */
1107 static int get_msg(int fd){
1108         int flags = 0;
1109         int res, ret;
1110         ctl_area[0] = 0;
1111         dat_area[0] = 0;
1112         ret = 0;
1113         res = getmsg(fd, &ctl, &dat, &flags);
1115         if(res < 0){
1116                 if(errno == EINTR){
1117                         return(GOT_INTR);
1118                         }
1119                 else{
1120                         printf("%s\n", "get_msg FAILED.");
1121                         return(GOT_ERR);
1122                         }
1123                 }
1124         if(ctl.len > 0){
1125                 ret |= GOT_CTRL;
1126                 }
1127         if(dat.len > 0){
1128                 ret |= GOT_DATA;
1129                 }
1131         return(ret);
1132         }
1134 /* verify that dl_primitive in ctl_area = prim */
1135 static int check_ctrl(int prim){
1136         dl_error_ack_t *err_ack = (dl_error_ack_t *)ctl_area;
1138         if(err_ack->dl_primitive != prim){
1139                 printf(_("Error: DLPI stream API failed to get MAC in check_ctrl: %s.\n"), strerror(errno));
1140                 exit(STATE_UNKNOWN);
1141                 }
1143         return 0;
1144         }
1146 /* put a control message on a stream */
1147 static int put_ctrl(int fd, int len, int pri){
1149         ctl.len = len;
1150         if(putmsg(fd, &ctl, 0, pri) < 0){
1151                 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno));
1152                 exit(STATE_UNKNOWN);
1153                 }
1155         return  0;
1156         }
1158 /* put a control + data message on a stream */
1159 static int put_both(int fd, int clen, int dlen, int pri){
1161         ctl.len = clen;
1162         dat.len = dlen;
1163         if(putmsg(fd, &ctl, &dat, pri) < 0){
1164                 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno));
1165                 exit(STATE_UNKNOWN);
1166                 }
1168         return  0;
1169         }
1171 /* open file descriptor and attach */
1172 static int dl_open(const char *dev, int unit, int *fd){
1173         dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area;
1175         if((*fd = open(dev, O_RDWR)) == -1){
1176                 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno));
1177                 exit(STATE_UNKNOWN);
1178                 }
1179         attach_req->dl_primitive = DL_ATTACH_REQ;
1180         attach_req->dl_ppa = unit;
1181         put_ctrl(*fd, sizeof(dl_attach_req_t), 0);
1182         get_msg(*fd);
1183         return check_ctrl(DL_OK_ACK);
1184         }
1186 /* send DL_BIND_REQ */
1187 static int dl_bind(int fd, int sap, u_char *addr){
1188         dl_bind_req_t *bind_req = (dl_bind_req_t *)ctl_area;
1189         dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)ctl_area;
1191         bind_req->dl_primitive = DL_BIND_REQ;
1192         bind_req->dl_sap = sap;
1193         bind_req->dl_max_conind = 1;
1194         bind_req->dl_service_mode = DL_CLDLS;
1195         bind_req->dl_conn_mgmt = 0;
1196         bind_req->dl_xidtest_flg = 0;
1197         put_ctrl(fd, sizeof(dl_bind_req_t), 0);
1198         get_msg(fd);
1199         if GOT_ERR == check_ctrl(DL_BIND_ACK)){
1200                 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno));
1201                 exit(STATE_UNKNOWN);
1202                 }
1203         bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr,
1204               bind_ack->dl_addr_length);
1206         return 0;
1207         }
1209 /***********************************************************************
1210  * interface:
1211  * function mac_addr_dlpi - get the mac address of the interface with 
1212  *                          type dev (eg lnc, hme) and unit (0, 1 ..)
1213  *
1214  * parameter: addr: an array of six bytes, has to be allocated by the caller
1215  *
1216  * return: 0 if OK, -1 if the address could not be determined
1217  *
1218  *
1219  ***********************************************************************/
1221 long mac_addr_dlpi( const char *dev, int unit, u_char  *addr){
1222         int fd;
1223         u_char mac_addr[25];
1225         if(GOT_ERR != dl_open(dev, unit, &fd)){
1226                 if(GOT_ERR != dl_bind(fd, INSAP, mac_addr)){
1227                         bcopy( mac_addr, addr, 6);
1228                         return 0;
1229                         }
1230                 }
1231         close(fd);
1233         return -1;
1234         }
1236 /* Kompf 2000-2003 */
1237 #endif
1240 /* print usage help */
1241 void print_help(void){
1243         print_revision(progname,revision);
1245         printf("Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n");
1246         printf (COPYRIGHT, copyright, email);
1247         
1248         printf(_("This plugin tests the availability of DHCP servers on a network.\n\n"));
1250         print_usage();
1252         printf (_(UT_HELP_VRSN));
1254         printf (_(UT_VERBOSE));
1256         printf(_("\
1257  -s, --serverip=IPADDRESS\n\
1258    IP address of DHCP server that we must hear from\n\
1259  -r, --requestedip=IPADDRESS\n\
1260    IP address that should be offered by at least one DHCP server\n\
1261  -t, --timeout=INTEGER\n\
1262    Seconds to wait for DHCPOFFER before timeout occurs\n\
1263  -i, --interface=STRING\n\
1264    Interface to to use for listening (i.e. eth0)\n"));
1266         return;
1267         }
1270 void print_usage(void){
1271         printf("\
1272 Usage: %s [-s serverip] [-r requestedip] [-t timeout] [-i interface] [-v]\n",progname);
1274         return;
1275         }