Code

f668a8806029a5b4b0c557a786d97ef70dd1fd2b
[nagiosplug.git] / plugins / 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 *
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 *****************************************************************************/
27 #include "common.h"
28 #include "netutils.h"
29 #include "utils.h"
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <sys/time.h>
37 #include <sys/ioctl.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <sys/socket.h>
41 #include <sys/types.h>
42 #include <netdb.h>
43 #include <netinet/in.h>
44 #include <net/if.h>
45 #include <arpa/inet.h>
47 #if defined( __linux__ )
49 #include <linux/if_ether.h>
50 #include <features.h>
52 #elif defined (__bsd__)
54 #include <netinet/if_ether.h>
55 #include <sys/sysctl.h>
56 #include <net/if_dl.h>
58 #elif defined(__sun__) || defined(__solaris__) || defined(__hpux__)
60 #define INSAP 22 
61 #define OUTSAP 24 
63 #include <signal.h>
64 #include <ctype.h>
65 #include <sys/stropts.h>
66 #include <sys/poll.h>
67 #include <sys/dlpi.h>
69 #define bcopy(source, destination, length) memcpy(destination, source, length)
71 #define AREA_SZ 5000            /* buffer length in bytes */ 
72 static u_long ctl_area[AREA_SZ];
73 static u_long dat_area[AREA_SZ];
74 static struct strbuf ctl = {AREA_SZ, 0, (char *)ctl_area};
75 static struct strbuf dat = {AREA_SZ, 0, (char *)dat_area};
77 #define GOT_CTRL 1 
78 #define GOT_DATA 2 
79 #define GOT_BOTH 3 
80 #define GOT_INTR 4 
81 #define GOT_ERR 128 
83 #define u_int8_t         uint8_t
84 #define u_int16_t       uint16_t
85 #define u_int32_t       uint32_t
87 static int get_msg(int);
88 static int check_ctrl(int);
89 static int put_ctrl(int, int, int);
90 static int put_both(int, int, int, int);
91 static int dl_open(const char *, int, int *);
92 static int dl_bind(int, int, u_char *);
93 long mac_addr_dlpi( const char *, int, u_char *);
95 #endif
97 const char *progname = "check_dhcp";
99 #define HAVE_GETOPT_H
102 /**** Common definitions ****/
104 #define STATE_OK          0
105 #define STATE_WARNING     1
106 #define STATE_CRITICAL    2
107 #define STATE_UNKNOWN     -1
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         struct requested_server_struct *next;
156         }requested_server;
159 #define BOOTREQUEST     1
160 #define BOOTREPLY       2
162 #define DHCPDISCOVER    1
163 #define DHCPOFFER       2
164 #define DHCPREQUEST     3
165 #define DHCPDECLINE     4
166 #define DHCPACK         5
167 #define DHCPNACK        6
168 #define DHCPRELEASE     7
170 #define DHCP_OPTION_MESSAGE_TYPE        53
171 #define DHCP_OPTION_HOST_NAME           12
172 #define DHCP_OPTION_BROADCAST_ADDRESS   28
173 #define DHCP_OPTION_REQUESTED_ADDRESS   50
174 #define DHCP_OPTION_LEASE_TIME          51
175 #define DHCP_OPTION_RENEWAL_TIME        58
176 #define DHCP_OPTION_REBINDING_TIME      59
178 #define DHCP_INFINITE_TIME              0xFFFFFFFF
180 #define DHCP_BROADCAST_FLAG 32768
182 #define DHCP_SERVER_PORT   67
183 #define DHCP_CLIENT_PORT   68
185 #define ETHERNET_HARDWARE_ADDRESS            1     /* used in htype field of dhcp packet */
186 #define ETHERNET_HARDWARE_ADDRESS_LENGTH     6     /* length of Ethernet hardware addresses */
188 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]="";
190 char network_interface_name[8]="eth0";
192 u_int32_t packet_xid=0;
194 u_int32_t dhcp_lease_time=0;
195 u_int32_t dhcp_renewal_time=0;
196 u_int32_t dhcp_rebinding_time=0;
198 int dhcpoffer_timeout=2;
200 dhcp_offer *dhcp_offer_list=NULL;
201 requested_server *requested_server_list=NULL;
203 int valid_responses=0;     /* number of valid DHCPOFFERs we received */
204 int requested_servers=0;   
205 int requested_responses=0;
207 int request_specific_address=FALSE;
208 int received_requested_address=FALSE;
209 int verbose=0;
210 struct in_addr requested_address;
213 int process_arguments(int, char **);
214 int call_getopt(int, char **);
215 int validate_arguments(void);
216 void print_usage(void);
217 void print_help(void);
219 int get_hardware_address(int,char *);
221 int send_dhcp_discover(int);
222 int get_dhcp_offer(int);
224 int get_results(void);
226 int add_dhcp_offer(struct in_addr,dhcp_packet *);
227 int free_dhcp_offer_list(void);
228 int free_requested_server_list(void);
230 int create_dhcp_socket(void);
231 int close_dhcp_socket(int);
232 int send_dhcp_packet(void *,int,int,struct sockaddr_in *);
233 int receive_dhcp_packet(void *,int,int,int,struct sockaddr_in *);
237 int main(int argc, char **argv){
238         int dhcp_socket;
239         int result;
241         if(process_arguments(argc,argv)!=OK){
242                 /*usage("Invalid command arguments supplied\n");*/
243                 printf("Invalid command arguments supplied\n");
244                 exit(STATE_UNKNOWN);
245                 }
248         /* create socket for DHCP communications */
249         dhcp_socket=create_dhcp_socket();
251         /* get hardware address of client machine */
252         get_hardware_address(dhcp_socket,network_interface_name);
254         /* send DHCPDISCOVER packet */
255         send_dhcp_discover(dhcp_socket);
257         /* wait for a DHCPOFFER packet */
258         get_dhcp_offer(dhcp_socket);
260         /* close socket we created */
261         close_dhcp_socket(dhcp_socket);
263         /* determine state/plugin output to return */
264         result=get_results();
266         /* free allocated memory */
267         free_dhcp_offer_list();
268         free_requested_server_list();
270         return result;
271         }
275 /* determines hardware address on client machine */
276 int get_hardware_address(int sock,char *interface_name){
278         int i;
280 #if defined(__linux__)
281         struct ifreq ifr;
283         strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name));
284         
285         /* try and grab hardware address of requested interface */
286         if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){
287                 printf("Error: Could not get hardware address of interface '%s'\n",interface_name);
288                 exit(STATE_UNKNOWN);
289                 }
291         memcpy(&client_hardware_address[0],&ifr.ifr_hwaddr.sa_data,6);
293 #elif defined(__bsd__)
295         /* Code from getmac.c posted at http://lists.freebsd.org/pipermail/freebsd-hackers/2004-June/007415.html
296          * by Alecs King based on Unix Network programming Ch 17
297          */
299         int                     mib[6], len;
300         char                    *buf;
301         unsigned char           *ptr;
302         struct if_msghdr        *ifm;
303         struct sockaddr_dl      *sdl;
305         mib[0] = CTL_NET;
306         mib[1] = AF_ROUTE;
307         mib[2] = 0;
308         mib[3] = AF_LINK;
309         mib[4] = NET_RT_IFLIST;
311         if ((mib[5] = if_nametoindex(interface_name)) == 0) {
312                 printf("Error: if_nametoindex error - %s.\n", strerror(errno));
313                 exit(STATE_UNKNOWN);
314         }
316         if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
317                 printf("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n", interface_name, strerror(errno));
318                 exit(STATE_UNKNOWN);
319         }
321         if ((buf = malloc(len)) == NULL) {
322                 printf("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n", interface_name, strerror(errno));
323                 exit(4);
324         }
326         if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
327                 printf("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n", interface_name, strerror(errno));
328                 exit(STATE_UNKNOWN);
329         }
331         ifm = (struct if_msghdr *)buf;
332         sdl = (struct sockaddr_dl *)(ifm + 1);
333         ptr = (unsigned char *)LLADDR(sdl);
334         memcpy(&client_hardware_address[0], ptr, 6) ;
336 #elif defined(__sun__) || defined(__solaris__)
338         /*
339         * Lifted from
340         *
341         * mac_addr_dlpi.c
342         *
343         * Copyright @2000, 2003 Martin Kompf, martin@kompf.de
344         *
345         * Return the MAC (ie, ethernet hardware) address by using the dlpi api.
346         *
347         */
349         long stat;
350         char dev[20] = "/dev/";
351         char *p;
352         int unit;
354         for (p = interface_name; *p && isalpha(*p); p++)
355                 /* no-op */ ;
356         if ( p != '\0' ) {
357                 unit = atoi(p) ;
358                 *p = '\0' ;
359                 strncat(dev, interface_name, 6) ;
360         } else {
361                 printf("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n", interface_name);
362                 exit(STATE_UNKNOWN);
363         }
364         stat = mac_addr_dlpi(dev, unit, client_hardware_address);
365         if (stat != 0) {
366                 printf("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n", dev, unit);
367                 exit(STATE_UNKNOWN);
368         }
370 #elif defined(__hpux__)
372         /* Martin Kompf again
373          *
374          * Nagios plugins thank you sincerely
375          */
377         long stat;
378         char dev[20] = "/dev/dlpi" ;
379         int unit = 0;
381         stat = mac_addr_dlpi(dev, unit, client_hardware_address);
382         if (stat != 0) {
383                 printf("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n", dev, unit);
384                 exit(STATE_UNKNOWN);
385         }
387 #else
388         printf("Error: can't get MAC address for this architecture.\n");
389         exit(STATE_UNKNOWN);
390 #endif
392         if (verbose) { 
393                 printf( "Hardware address: ");
394                 for (i=0; i<6; ++i)
395                         printf("%2.2x", client_hardware_address[i]);
396                 printf( "\n");
397         }
399         return OK;
400         }
403 /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
404 int send_dhcp_discover(int sock){
405         dhcp_packet discover_packet;
406         struct sockaddr_in sockaddr_broadcast;
409         /* clear the packet data structure */
410         bzero(&discover_packet,sizeof(discover_packet));
413         /* boot request flag (backward compatible with BOOTP servers) */
414         discover_packet.op=BOOTREQUEST;
416         /* hardware address type */
417         discover_packet.htype=ETHERNET_HARDWARE_ADDRESS;
419         /* length of our hardware address */
420         discover_packet.hlen=ETHERNET_HARDWARE_ADDRESS_LENGTH;
422         discover_packet.hops=0;
424         /* transaction id is supposed to be random */
425         srand(time(NULL));
426         packet_xid=random();
427         discover_packet.xid=htonl(packet_xid);
429         /**** WHAT THE HECK IS UP WITH THIS?!?  IF I DON'T MAKE THIS CALL, ONLY ONE SERVER RESPONSE IS PROCESSED!!!! ****/
430         /* downright bizzarre... */
431         ntohl(discover_packet.xid);
433         /*discover_packet.secs=htons(65535);*/
434         discover_packet.secs=0xFF;
436         /* tell server it should broadcast its response */ 
437         discover_packet.flags=htons(DHCP_BROADCAST_FLAG);
439         /* our hardware address */
440         memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH);
442         /* first four bytes of options field is magic cookie (as per RFC 2132) */
443         discover_packet.options[0]='\x63';
444         discover_packet.options[1]='\x82';
445         discover_packet.options[2]='\x53';
446         discover_packet.options[3]='\x63';
448         /* DHCP message type is embedded in options field */
449         discover_packet.options[4]=DHCP_OPTION_MESSAGE_TYPE;    /* DHCP message type option identifier */
450         discover_packet.options[5]='\x01';               /* DHCP message option length in bytes */
451         discover_packet.options[6]=DHCPDISCOVER;
453         /* the IP address we're requesting */
454         if(request_specific_address==TRUE){
455                 discover_packet.options[7]=DHCP_OPTION_REQUESTED_ADDRESS;
456                 discover_packet.options[8]='\x04';
457                 memcpy(&discover_packet.options[9],&requested_address,sizeof(requested_address));
458                 }
459         
460         /* send the DHCPDISCOVER packet to broadcast address */
461         sockaddr_broadcast.sin_family=AF_INET;
462         sockaddr_broadcast.sin_port=htons(DHCP_SERVER_PORT);
463         sockaddr_broadcast.sin_addr.s_addr=INADDR_BROADCAST;
464         bzero(&sockaddr_broadcast.sin_zero,sizeof(sockaddr_broadcast.sin_zero));
467         if (verbose) {
468                 printf("DHCPDISCOVER to %s port %d\n",inet_ntoa(sockaddr_broadcast.sin_addr),ntohs(sockaddr_broadcast.sin_port));
469                 printf("DHCPDISCOVER XID: %lu (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid));
470                 printf("DHCDISCOVER ciaddr:  %s\n",inet_ntoa(discover_packet.ciaddr));
471                 printf("DHCDISCOVER yiaddr:  %s\n",inet_ntoa(discover_packet.yiaddr));
472                 printf("DHCDISCOVER siaddr:  %s\n",inet_ntoa(discover_packet.siaddr));
473                 printf("DHCDISCOVER giaddr:  %s\n",inet_ntoa(discover_packet.giaddr));
474         }
476         /* send the DHCPDISCOVER packet out */
477         send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast);
479         if (verbose) 
480                 printf("\n\n");
482         return OK;
483         }
488 /* waits for a DHCPOFFER message from one or more DHCP servers */
489 int get_dhcp_offer(int sock){
490         dhcp_packet offer_packet;
491         struct sockaddr_in source;
492         int result=OK;
493         int timeout=1;
494         int responses=0;
495         int x;
496         time_t start_time;
497         time_t current_time;
499         time(&start_time);
501         /* receive as many responses as we can */
502         for(responses=0,valid_responses=0;;){
504                 time(&current_time);
505                 if((current_time-start_time)>=dhcpoffer_timeout)
506                         break;
508                 if (verbose) 
509                         printf("\n\n");
511                 bzero(&source,sizeof(source));
512                 bzero(&offer_packet,sizeof(offer_packet));
514                 result=OK;
515                 result=receive_dhcp_packet(&offer_packet,sizeof(offer_packet),sock,dhcpoffer_timeout,&source);
516                 
517                 if(result!=OK){
518                         if (verbose)
519                                 printf("Result=ERROR\n");
521                         continue;
522                         }
523                 else{
524                         if (verbose) 
525                                 printf("Result=OK\n");
527                         responses++;
528                         }
530                 if (verbose) {
531                         printf("DHCPOFFER from IP address %s\n",inet_ntoa(source.sin_addr));
532                         printf("DHCPOFFER XID: %lu (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid));
533                 }
535                 /* check packet xid to see if its the same as the one we used in the discover packet */
536                 if(ntohl(offer_packet.xid)!=packet_xid){
537                         if (verbose)
538                                 printf("DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - ignoring packet\n",ntohl(offer_packet.xid),packet_xid);
540                         continue;
541                         }
543                 /* check hardware address */
544                 result=OK;
545                 if (verbose)
546                         printf("DHCPOFFER chaddr: ");
548                 for(x=0;x<ETHERNET_HARDWARE_ADDRESS_LENGTH;x++){
549                         if (verbose)
550                                 printf("%02X",(unsigned char)offer_packet.chaddr[x]);
552                         if(offer_packet.chaddr[x]!=client_hardware_address[x])
553                                 result=ERROR;
554                 }
555                 if (verbose)
556                         printf("\n");
558                 if(result==ERROR){
559                         if (verbose) 
560                                 printf("DHCPOFFER hardware address did not match our own - ignoring packet\n");
562                         continue;
563                         }
565                 if (verbose) {
566                         printf("DHCPOFFER ciaddr: %s\n",inet_ntoa(offer_packet.ciaddr));
567                         printf("DHCPOFFER yiaddr: %s\n",inet_ntoa(offer_packet.yiaddr));
568                         printf("DHCPOFFER siaddr: %s\n",inet_ntoa(offer_packet.siaddr));
569                         printf("DHCPOFFER giaddr: %s\n",inet_ntoa(offer_packet.giaddr));
570                 }
572                 add_dhcp_offer(source.sin_addr,&offer_packet);
574                 valid_responses++;
575                 }
577         if (verbose) {
578                 printf("Total responses seen on the wire: %d\n",responses);
579                 printf("Valid responses for this machine: %d\n",valid_responses);
580         }
582         return OK;
583         }
587 /* sends a DHCP packet */
588 int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest){
589         struct sockaddr_in myname;
590         int result;
592         result=sendto(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)dest,sizeof(*dest));
594         if (verbose) 
595                 printf("send_dhcp_packet result: %d\n",result);
597         if(result<0)
598                 return ERROR;
600         return OK;
601         }
605 /* receives a DHCP packet */
606 int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address){
607         struct timeval tv;
608         fd_set readfds;
609         int recv_result;
610         socklen_t address_size;
611         struct sockaddr_in source_address;
614         /* wait for data to arrive (up time timeout) */
615         tv.tv_sec=timeout;
616         tv.tv_usec=0;
617         FD_ZERO(&readfds);
618         FD_SET(sock,&readfds);
619         select(sock+1,&readfds,NULL,NULL,&tv);
621         /* make sure some data has arrived */
622         if(!FD_ISSET(sock,&readfds)){
623                 if (verbose)
624                         printf("No (more) data received\n");
625                 return ERROR;
626                 }
628         else{
630                 /* 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
631                    not being interpreted correctly.  sigh... */
632                 bzero(&source_address,sizeof(source_address));
633                 address_size=sizeof(source_address);
634                 recv_result=recvfrom(sock,(char *)buffer,buffer_size,MSG_PEEK,(struct sockaddr *)&source_address,&address_size);
635                 if (verbose)
636                         printf("recv_result_1: %d\n",recv_result);
637                 recv_result=recvfrom(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)&source_address,&address_size);
638                 if (verbose)
639                         printf("recv_result_2: %d\n",recv_result);
641                 if(recv_result==-1){
642                         if (verbose) {
643                                 printf("recvfrom() failed, ");
644                                 printf("errno: (%d) -> %s\n",errno,strerror(errno));
645                         }
646                         return ERROR;
647                         }
648                 else{
649                         if (verbose) {
650                                 printf("receive_dhcp_packet() result: %d\n",recv_result);
651                                 printf("receive_dhcp_packet() source: %s\n",inet_ntoa(source_address.sin_addr));
652                         }
654                         memcpy(address,&source_address,sizeof(source_address));
655                         return OK;
656                         }
657                 }
659         return OK;
660         }
664 /* creates a socket for DHCP communication */
665 int create_dhcp_socket(void){
666         struct sockaddr_in myname;
667         struct ifreq interface;
668         int sock;
669         int flag=1;
671         /* Set up the address we're going to bind to. */
672         bzero(&myname,sizeof(myname));
673         myname.sin_family=AF_INET;
674         myname.sin_port=htons(DHCP_CLIENT_PORT);
675         myname.sin_addr.s_addr=INADDR_ANY;                 /* listen on any address */
676         bzero(&myname.sin_zero,sizeof(myname.sin_zero));
678         /* create a socket for DHCP communications */
679         sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
680         if(sock<0){
681                 printf("Error: Could not create socket!\n");
682                 exit(STATE_UNKNOWN);
683                 }
685         if (verbose)
686                 printf("DHCP socket: %d\n",sock);
688         /* set the reuse address flag so we don't get errors when restarting */
689         flag=1;
690         if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
691                 printf("Error: Could not set reuse address option on DHCP socket!\n");
692                 exit(STATE_UNKNOWN);
693                 }
695         /* set the broadcast option - we need this to listen to DHCP broadcast messages */
696         if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
697                 printf("Error: Could not set broadcast option on DHCP socket!\n");
698                 exit(STATE_UNKNOWN);
699                 }
701         /* bind socket to interface */
702 #if defined(__linux__)
703         strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ);
704         if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){
705                 printf("Error: Could not bind socket to interface %s.  Check your privileges...\n",network_interface_name);
706                 exit(STATE_UNKNOWN);
707                 }
709 #else
710         strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ);
711 #endif
713         /* bind the socket */
714         if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
715                 printf("Error: Could not bind to DHCP socket (port %d)!  Check your privileges...\n",DHCP_CLIENT_PORT);
716                 exit(STATE_UNKNOWN);
717                 }
719         return sock;
720         }
726 /* closes DHCP socket */
727 int close_dhcp_socket(int sock){
729         close(sock);
731         return OK;
732         }
737 /* adds a requested server address to list in memory */
738 int add_requested_server(struct in_addr server_address){
739         requested_server *new_server;
741         new_server=(requested_server *)malloc(sizeof(requested_server));
742         if(new_server==NULL)
743                 return ERROR;
745         new_server->server_address=server_address;
747         new_server->next=requested_server_list;
748         requested_server_list=new_server;
750         requested_servers++;
752         if (verbose)
753                 printf("Requested server address: %s\n",inet_ntoa(new_server->server_address));
755         return OK;
756         }
761 /* adds a DHCP OFFER to list in memory */
762 int add_dhcp_offer(struct in_addr source,dhcp_packet *offer_packet){
763         dhcp_offer *new_offer;
764         int x;
765         int y;
766         unsigned option_type;
767         unsigned option_length;
769         if(offer_packet==NULL)
770                 return ERROR;
772         /* process all DHCP options present in the packet */
773         for(x=4;x<MAX_DHCP_OPTIONS_LENGTH;){
775                 /* end of options (0 is really just a pad, but bail out anyway) */
776                 if((int)offer_packet->options[x]==-1 || (int)offer_packet->options[x]==0)
777                         break;
779                 /* get option type */
780                 option_type=offer_packet->options[x++];
782                 /* get option length */
783                 option_length=offer_packet->options[x++];
785                 if (verbose) 
786                         printf("Option: %d (0x%02X)\n",option_type,option_length);
788                 /* get option data */
789                 if(option_type==DHCP_OPTION_LEASE_TIME)
790                         dhcp_lease_time=ntohl(*((u_int32_t *)&offer_packet->options[x]));
791                 if(option_type==DHCP_OPTION_RENEWAL_TIME)
792                         dhcp_renewal_time=ntohl(*((u_int32_t *)&offer_packet->options[x]));
793                 if(option_type==DHCP_OPTION_REBINDING_TIME)
794                         dhcp_rebinding_time=ntohl(*((u_int32_t *)&offer_packet->options[x]));
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;
821         new_offer->server_address=source;
822         new_offer->offered_address=offer_packet->yiaddr;
823         new_offer->lease_time=dhcp_lease_time;
824         new_offer->renewal_time=dhcp_renewal_time;
825         new_offer->rebinding_time=dhcp_rebinding_time;
828         if (verbose) {
829                 printf("Added offer from server @ %s",inet_ntoa(new_offer->server_address));
830                 printf(" of IP address %s\n",inet_ntoa(new_offer->offered_address));
831         }
833         /* add new offer to head of list */
834         new_offer->next=dhcp_offer_list;
835         dhcp_offer_list=new_offer;
837         return OK;
838         }
843 /* frees memory allocated to DHCP OFFER list */
844 int free_dhcp_offer_list(void){
845         dhcp_offer *this_offer;
846         dhcp_offer *next_offer;
848         for(this_offer=dhcp_offer_list;this_offer!=NULL;this_offer=next_offer){
849                 next_offer=this_offer->next;
850                 free(this_offer);
851                 }
853         return OK;
854         }
859 /* frees memory allocated to requested server list */
860 int free_requested_server_list(void){
861         requested_server *this_server;
862         requested_server *next_server;
864         for(this_server=requested_server_list;this_server!=NULL;this_server=next_server){
865                 next_server=this_server->next;
866                 free(this_server);
867                 }
868         
869         return OK;
870         }
873 /* gets state and plugin output to return */
874 int get_results(void){
875         dhcp_offer *temp_offer;
876         requested_server *temp_server;
877         int result;
878         u_int32_t max_lease_time=0;
880         received_requested_address=FALSE;
882         /* checks responses from requested servers */
883         requested_responses=0;
884         if(requested_servers>0){
886                 for(temp_server=requested_server_list;temp_server!=NULL;temp_server=temp_server->next){
888                         for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
890                                 /* get max lease time we were offered */
891                                 if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
892                                         max_lease_time=temp_offer->lease_time;
893                                 
894                                 /* see if we got the address we requested */
895                                 if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
896                                         received_requested_address=TRUE;
898                                 /* see if the servers we wanted a response from talked to us or not */
899                                 if(!memcmp(&temp_offer->server_address,&temp_server->server_address,sizeof(temp_server->server_address))){
900         if (verbose) {
901                                         printf("DHCP Server Match: Offerer=%s",inet_ntoa(temp_offer->server_address));
902                                         printf(" Requested=%s\n",inet_ntoa(temp_server->server_address));
903         }                                      
904                                         requested_responses++;
905                                         }
906                                 }
907                         }
909                 }
911         /* else check and see if we got our requested address from any server */
912         else{
914                 for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){
916                         /* get max lease time we were offered */
917                         if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
918                                 max_lease_time=temp_offer->lease_time;
919                                 
920                         /* see if we got the address we requested */
921                         if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
922                                 received_requested_address=TRUE;
923                         }
924                 }
926         result=STATE_OK;
927         if(valid_responses==0)
928                 result=STATE_CRITICAL;
929         else if(requested_servers>0 && requested_responses==0)
930                 result=STATE_CRITICAL;
931         else if(requested_responses<requested_servers)
932                 result=STATE_WARNING;
933         else if(request_specific_address==TRUE && received_requested_address==FALSE)
934                 result=STATE_WARNING;
937         printf("DHCP %s: ",(result==STATE_OK)?"ok":"problem");
939         /* we didn't receive any DHCPOFFERs */
940         if(dhcp_offer_list==NULL){
941                 printf("No DHCPOFFERs were received.\n");
942                 return result;
943                 }
945         printf("Received %d DHCPOFFER(s)",valid_responses);
947         if(requested_servers>0)
948                 printf(", %s%d of %d requested servers responded",((requested_responses<requested_servers) && requested_responses>0)?"only ":"",requested_responses,requested_servers);
950         if(request_specific_address==TRUE)
951                 printf(", requested address (%s) was %soffered",inet_ntoa(requested_address),(received_requested_address==TRUE)?"":"not ");
953         printf(", max lease time = ");
954         if(max_lease_time==DHCP_INFINITE_TIME)
955                 printf("Infinity");
956         else
957                 printf("%lu sec",(unsigned long)max_lease_time);
959         printf(".\n");
961         return result;
962         }
969 /* print usage help */
970 void print_help(void){
972         /*print_revision(progname,"$Revision$");*/
974         printf("Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n\n");
975         printf("This plugin tests the availability of DHCP servers on a network.\n\n");
977         print_usage();
979         printf
980                 ("\nOptions:\n"
981                  " -s, --serverip=IPADDRESS\n"
982                  "   IP address of DHCP server that we must hear from\n"
983                  " -r, --requestedip=IPADDRESS\n"
984                  "   IP address that should be offered by at least one DHCP server\n"
985                  " -t, --timeout=INTEGER\n"
986                  "   Seconds to wait for DHCPOFFER before timeout occurs\n"
987                  " -i, --interface=STRING\n"
988                  "   Interface to to use for listening (i.e. eth0)\n"
989                  " -v, --verbose\n"
990                  "   Print extra information (command-line use only)\n"
991                  " -h, --help\n"
992                  "   Print detailed help screen\n"
993                  " -V, --version\n"
994                  "   Print version information\n\n"
995                  );
997         /*support();*/
999         return;
1000         }
1003 /* prints usage information */
1004 void print_usage(void){
1006         printf("Usage: %s [-s serverip] [-r requestedip] [-t timeout] [-i interface]\n",progname);
1007         printf("       %s --help\n",progname);
1008         printf("       %s --version\n",progname);
1010         return;
1011         }
1016 /* process command-line arguments */
1017 int process_arguments(int argc, char **argv){
1018         int c;
1020         if(argc<1)
1021                 return ERROR;
1023         c=0;
1024         while((c+=(call_getopt(argc-c,&argv[c])))<argc){
1026                 /*
1027                 if(is_option(argv[c]))
1028                         continue;
1029                 */
1030                 }
1032         return validate_arguments();
1033         }
1037 int call_getopt(int argc, char **argv){
1038         int c=0;
1039         int i=0;
1040         struct in_addr ipaddress;
1042 #ifdef HAVE_GETOPT_H
1043         int option_index = 0;
1044         static struct option long_options[] =
1045         { 
1046                 {"serverip",       required_argument,0,'s'},
1047                 {"requestedip",    required_argument,0,'r'},
1048                 {"timeout",        required_argument,0,'t'},
1049                 {"interface",      required_argument,0,'i'},
1050                 {"verbose",        no_argument,      0,'v'},
1051                 {"version",        no_argument,      0,'V'},
1052                 {"help",           no_argument,      0,'h'},
1053                 {0,0,0,0}
1054         };
1055 #endif
1057         while(1){
1058 #ifdef HAVE_GETOPT_H
1059                 c=getopt_long(argc,argv,"+hVvt:s:r:t:i:",long_options,&option_index);
1060 #else
1061                 c=getopt(argc,argv,"+?hVvt:s:r:t:i:");
1062 #endif
1064                 i++;
1066                 if(c==-1||c==EOF||c==1)
1067                         break;
1069                 switch(c){
1070                 case 'w':
1071                 case 'r':
1072                 case 't':
1073                 case 'i':
1074                         i++;
1075                         break;
1076                 default:
1077                         break;
1078                         }
1080                 switch(c){
1082                 case 's': /* DHCP server address */
1083                         if(inet_aton(optarg,&ipaddress))
1084                                 add_requested_server(ipaddress);
1085                         /*
1086                         else
1087                                 usage("Invalid server IP address\n");
1088                         */
1089                         break;
1091                 case 'r': /* address we are requested from DHCP servers */
1092                         if(inet_aton(optarg,&ipaddress)){
1093                                 requested_address=ipaddress;
1094                                 request_specific_address=TRUE;
1095                                 }
1096                         /*
1097                         else
1098                                 usage("Invalid requested IP address\n");
1099                         */
1100                         break;
1102                 case 't': /* timeout */
1104                         /*
1105                         if(is_intnonneg(optarg))
1106                         */
1107                         if(atoi(optarg)>0)
1108                                 dhcpoffer_timeout=atoi(optarg);
1109                         /*
1110                         else
1111                                 usage("Time interval must be a nonnegative integer\n");
1112                         */
1113                         break;
1115                 case 'i': /* interface name */
1117                         strncpy(network_interface_name,optarg,sizeof(network_interface_name)-1);
1118                         network_interface_name[sizeof(network_interface_name)-1]='\x0';
1120                         break;
1122                 case 'V': /* version */
1124                         /*print_revision(progname,"$Revision$");*/
1125                         exit(STATE_OK);
1127                 case 'h': /* help */
1129                         print_help();
1130                         exit(STATE_OK);
1132                 case 'v': /* verbose */
1134                         verbose=1;
1135                         break;
1137                 case '?': /* help */
1139                         /*usage("Invalid argument\n");*/
1140                         break;
1142                 default:
1143                         break;
1144                         }
1145                 }
1147         return i;
1148         }
1152 int validate_arguments(void){
1154         return OK;
1155         }
1157 #if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
1160 /*
1161  * Copyright @2000, 2003 Martin Kompf, martin@kompf.de
1162  * 
1163  * Nagios plugins thanks Martin for this code.
1164  */
1166 /* get a message from a stream; return type of message */
1167 static int get_msg(int fd)
1169     int flags = 0;
1170     int res, ret;
1171     ctl_area[0] = 0;
1172     dat_area[0] = 0;
1173     ret = 0;
1174     res = getmsg(fd, &ctl, &dat, &flags);
1176     if(res < 0) {
1177         if(errno == EINTR) {
1178             return(GOT_INTR);
1179         } else {
1180             printf("%s\n", "get_msg FAILED.");
1181             return(GOT_ERR);
1182         }
1183     }
1184     if(ctl.len > 0) {
1185         ret |= GOT_CTRL;
1186     }
1187     if(dat.len > 0) {
1188         ret |= GOT_DATA;
1189     }
1190     return(ret);
1193 /* verify that dl_primitive in ctl_area = prim */
1194 static int check_ctrl(int prim)
1196     dl_error_ack_t *err_ack = (dl_error_ack_t *)ctl_area;
1197     if(err_ack->dl_primitive != prim) {
1198         printf("Error: DLPI stream API failed to get MAC in check_ctrl: %s.\n", strerror(errno));
1199         exit(STATE_UNKNOWN);
1200     }
1201     return 0;
1204 /* put a control message on a stream */
1205 static int put_ctrl(int fd, int len, int pri)
1207     ctl.len = len;
1208     if(putmsg(fd, &ctl, 0, pri) < 0) {
1209         printf("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n", strerror(errno));
1210         exit(STATE_UNKNOWN);
1211     }
1212     return  0;
1215 /* put a control + data message on a stream */
1216 static int put_both(int fd, int clen, int dlen, int pri)
1218     ctl.len = clen;
1219     dat.len = dlen;
1220     if(putmsg(fd, &ctl, &dat, pri) < 0) {
1221         printf("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n", strerror(errno));
1222         exit(STATE_UNKNOWN);
1223     }
1224     return  0;
1227 /* open file descriptor and attach */
1228 static int dl_open(const char *dev, int unit, int *fd)
1230     dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area;
1231     if((*fd = open(dev, O_RDWR)) == -1) {
1232         printf("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n", dev, strerror(errno));
1233         exit(STATE_UNKNOWN);
1234     }
1235     attach_req->dl_primitive = DL_ATTACH_REQ;
1236     attach_req->dl_ppa = unit;
1237     put_ctrl(*fd, sizeof(dl_attach_req_t), 0);
1238     get_msg(*fd);
1239     return check_ctrl(DL_OK_ACK);
1242 /* send DL_BIND_REQ */
1243 static int dl_bind(int fd, int sap, u_char *addr)
1245     dl_bind_req_t *bind_req = (dl_bind_req_t *)ctl_area;
1246     dl_bind_ack_t *bind_ack = (dl_bind_ack_t *)ctl_area;
1247     bind_req->dl_primitive = DL_BIND_REQ;
1248     bind_req->dl_sap = sap;
1249     bind_req->dl_max_conind = 1;
1250     bind_req->dl_service_mode = DL_CLDLS;
1251     bind_req->dl_conn_mgmt = 0;
1252     bind_req->dl_xidtest_flg = 0;
1253     put_ctrl(fd, sizeof(dl_bind_req_t), 0);
1254     get_msg(fd);
1255     if (GOT_ERR == check_ctrl(DL_BIND_ACK)) {
1256         printf("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n", strerror(errno));
1257         exit(STATE_UNKNOWN);
1258     }
1259     bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr,
1260         bind_ack->dl_addr_length);
1261     return 0;
1264 /***********************************************************************
1265  * interface:
1266  * function mac_addr_dlpi - get the mac address of the interface with 
1267  *                          type dev (eg lnc, hme) and unit (0, 1 ..)
1268  *
1269  * parameter: addr: an array of six bytes, has to be allocated by the caller
1270  *
1271  * return: 0 if OK, -1 if the address could not be determined
1272  *
1273  *
1274  ***********************************************************************/
1276 long mac_addr_dlpi( const char *dev, int unit, u_char  *addr) {
1278         int fd;
1279         u_char mac_addr[25];
1281         if (GOT_ERR != dl_open(dev, unit, &fd)) {
1282                 if (GOT_ERR != dl_bind(fd, INSAP, mac_addr)) {
1283                         bcopy( mac_addr, addr, 6);
1284                         return 0;
1285                 }
1286         }
1287         close(fd);
1288         return -1;
1291 #endif