index 72405feac1a3a80c4a7676d3f51c9d9a488265c4..bdd348f20dc50c54a3e0872ba0d1e6cfdf21c6b2 100644 (file)
-/******************************************************************************
-*
+/*****************************************************************************
+*
* Nagios check_dhcp plugin
-*
+*
* License: GPL
* Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
-* Copyright (c) 2001-2006 Nagios Plugin Development Team
-*
-* Last Modified: $Date$
-*
+* Copyright (c) 2001-2007 Nagios Plugin Development Team
+*
* Description:
-*
+*
* This file contains the check_dhcp plugin
-*
-* This plugin tests the availability of DHCP servers on a network.
-*
-*
-* License Information:
-*
-* This program is free software; you can redistribute it and/or modify
+*
+* This plugin tests the availability of DHCP servers on a network.
+*
+* Unicast mode was originally implemented by Heiti of Boras Kommun with
+* general improvements as well as usability fixes and "forward"-porting by
+* Andreas Ericsson of OP5 AB.
+*
+*
+* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 2 of the License, or
+* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
-*
+*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
-*
+*
* You should have received a copy of the GNU General Public License
-* along with this program; if not, write to the Free Software
-* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* $Id$
-*
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+*
*****************************************************************************/
const char *progname = "check_dhcp";
-const char *revision = "$Revision$";
-const char *copyright = "2001-2006";
+const char *copyright = "2001-2007";
const char *email = "nagiosplug-devel@lists.sourceforge.net";
#include "common.h"
#include "netutils.h"
#include "utils.h"
+#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
+#if HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
#if defined( __linux__ )
#elif defined(__sun__) || defined(__solaris__) || defined(__hpux__)
-#define INSAP 22
-#define OUTSAP 24
+#define INSAP 22
+#define OUTSAP 24
#include <signal.h>
#include <ctype.h>
#define bcopy(source, destination, length) memcpy(destination, source, length)
-#define AREA_SZ 5000 /* buffer length in bytes */
+#define AREA_SZ 5000 /* buffer length in bytes */
static u_long ctl_area[AREA_SZ];
static u_long dat_area[AREA_SZ];
static struct strbuf ctl = {AREA_SZ, 0, (char *)ctl_area};
static struct strbuf dat = {AREA_SZ, 0, (char *)dat_area};
-#define GOT_CTRL 1
-#define GOT_DATA 2
-#define GOT_BOTH 3
-#define GOT_INTR 4
-#define GOT_ERR 128
+#define GOT_CTRL 1
+#define GOT_DATA 2
+#define GOT_BOTH 3
+#define GOT_INTR 4
+#define GOT_ERR 128
#define u_int8_t uint8_t
#define u_int16_t uint16_t
#define DHCP_OPTION_SERVER_IDENTIFIER 54
#define DHCP_OPTION_RENEWAL_TIME 58
#define DHCP_OPTION_REBINDING_TIME 59
+#define DHCP_OPTION_END 255
#define DHCP_INFINITE_TIME 0xFFFFFFFF
#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
+u_int8_t unicast = 0; /* unicast mode: mimic a DHCP relay */
+struct in_addr my_ip; /* our address (required for relay) */
+struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]="";
+unsigned char *user_specified_mac=NULL;
char network_interface_name[IFNAMSIZ]="eth0";
requested_server *requested_server_list=NULL;
int valid_responses=0; /* number of valid DHCPOFFERs we received */
-int requested_servers=0;
+int requested_servers=0;
int requested_responses=0;
int request_specific_address=FALSE;
void print_usage(void);
void print_help(void);
+void resolve_host(const char *in,struct in_addr *out);
+unsigned char *mac_aton(const char *);
+void print_hardware_address(const unsigned char *);
int get_hardware_address(int,char *);
+int get_ip_address(int,char *);
int send_dhcp_discover(int);
int get_dhcp_offer(int);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
-
+
+ /* Parse extra opts if any */
+ argv=np_extra_opts(&argc, argv, progname);
+
if(process_arguments(argc,argv)!=OK){
usage4 (_("Could not parse arguments"));
}
+ /* this plugin almost certainly needs root permissions. */
+ np_warn_if_not_root();
+
/* create socket for DHCP communications */
dhcp_socket=create_dhcp_socket();
/* get hardware address of client machine */
- get_hardware_address(dhcp_socket,network_interface_name);
+ if(user_specified_mac!=NULL)
+ memcpy(client_hardware_address,user_specified_mac,6);
+ else
+ get_hardware_address(dhcp_socket,network_interface_name);
+
+ if(unicast) /* get IP address of client machine */
+ get_ip_address(dhcp_socket,network_interface_name);
/* send DHCPDISCOVER packet */
send_dhcp_discover(dhcp_socket);
/* determines hardware address on client machine */
int get_hardware_address(int sock,char *interface_name){
- int i;
-
#if defined(__linux__)
struct ifreq ifr;
- strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name));
-
+ strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name)-1);
+ ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';
+
/* try and grab hardware address of requested interface */
if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){
printf(_("Error: Could not get hardware address of interface '%s'\n"),interface_name);
unit = atoi(p) ;
*p = '\0' ;
strncat(dev, interface_name, 6) ;
- }
+ }
else{
printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name);
exit(STATE_UNKNOWN);
/* Kompf 2000-2003 */
#else
- printf(_("Error: can't get MAC address for this architecture.\n"));
+ printf(_("Error: can't get MAC address for this architecture. Use the --mac option.\n"));
exit(STATE_UNKNOWN);
#endif
- if(verbose){
- printf(_("Hardware address: "));
- for (i=0; i<6; ++i)
- printf("%2.2x", client_hardware_address[i]);
- printf( "\n");
- }
+ if(verbose)
+ print_hardware_address(client_hardware_address);
return OK;
}
+/* determines IP address of the client interface */
+int get_ip_address(int sock,char *interface_name){
+#if defined(SIOCGIFADDR)
+ struct ifreq ifr;
+
+ strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name)-1);
+ ifr.ifr_name[sizeof(ifr.ifr_name)-1]='\0';
+
+ if(ioctl(sock,SIOCGIFADDR,&ifr)<0){
+ printf(_("Error: Cannot determine IP address of interface %s\n"),
+ interface_name);
+ exit(STATE_UNKNOWN);
+ }
+
+ my_ip=((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+
+#else
+ printf(_("Error: Cannot get interface IP address on this platform.\n"));
+ exit(STATE_UNKNOWN);
+#endif
+
+ if(verbose)
+ printf(_("Pretending to be relay client %s\n"),inet_ntoa(my_ip));
+
+ return OK;
+ }
/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
int send_dhcp_discover(int sock){
dhcp_packet discover_packet;
struct sockaddr_in sockaddr_broadcast;
+ unsigned short opts;
/* clear the packet data structure */
/* length of our hardware address */
discover_packet.hlen=ETHERNET_HARDWARE_ADDRESS_LENGTH;
- discover_packet.hops=0;
-
- /* transaction id is supposed to be random */
+ /*
+ * transaction ID is supposed to be random. We won't use the address so
+ * we don't care about high entropy here. time(2) is good enough.
+ */
srand(time(NULL));
packet_xid=random();
discover_packet.xid=htonl(packet_xid);
/*discover_packet.secs=htons(65535);*/
discover_packet.secs=0xFF;
- /* tell server it should broadcast its response */
- discover_packet.flags=htons(DHCP_BROADCAST_FLAG);
+ /*
+ * server needs to know if it should broadcast or unicast its response:
+ * 0x8000L == 32768 == 1 << 15 == broadcast, 0 == unicast
+ */
+ discover_packet.flags = unicast ? 0 : htons(DHCP_BROADCAST_FLAG);
/* our hardware address */
memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH);
discover_packet.options[2]='\x53';
discover_packet.options[3]='\x63';
+ opts = 4;
/* DHCP message type is embedded in options field */
- discover_packet.options[4]=DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
- discover_packet.options[5]='\x01'; /* DHCP message option length in bytes */
- discover_packet.options[6]=DHCPDISCOVER;
+ discover_packet.options[opts++]=DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
+ discover_packet.options[opts++]='\x01'; /* DHCP message option length in bytes */
+ discover_packet.options[opts++]=DHCPDISCOVER;
/* the IP address we're requesting */
if(request_specific_address==TRUE){
- discover_packet.options[7]=DHCP_OPTION_REQUESTED_ADDRESS;
- discover_packet.options[8]='\x04';
- memcpy(&discover_packet.options[9],&requested_address,sizeof(requested_address));
+ discover_packet.options[opts++]=DHCP_OPTION_REQUESTED_ADDRESS;
+ discover_packet.options[opts++]='\x04';
+ memcpy(&discover_packet.options[opts],&requested_address,sizeof(requested_address));
+ opts += sizeof(requested_address);
}
-
+ discover_packet.options[opts++]=DHCP_OPTION_END;
+
+ /* unicast fields */
+ if(unicast)
+ discover_packet.giaddr.s_addr = my_ip.s_addr;
+
+ /* see RFC 1542, 4.1.1 */
+ discover_packet.hops = unicast ? 1 : 0;
+
/* send the DHCPDISCOVER packet to broadcast address */
sockaddr_broadcast.sin_family=AF_INET;
sockaddr_broadcast.sin_port=htons(DHCP_SERVER_PORT);
- sockaddr_broadcast.sin_addr.s_addr=INADDR_BROADCAST;
+ sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST;
bzero(&sockaddr_broadcast.sin_zero,sizeof(sockaddr_broadcast.sin_zero));
if(verbose){
printf(_("DHCPDISCOVER to %s port %d\n"),inet_ntoa(sockaddr_broadcast.sin_addr),ntohs(sockaddr_broadcast.sin_port));
- printf("DHCPDISCOVER XID: %lu (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid));
+ printf("DHCPDISCOVER XID: %u (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid));
printf("DHCDISCOVER ciaddr: %s\n",inet_ntoa(discover_packet.ciaddr));
printf("DHCDISCOVER yiaddr: %s\n",inet_ntoa(discover_packet.yiaddr));
printf("DHCDISCOVER siaddr: %s\n",inet_ntoa(discover_packet.siaddr));
/* send the DHCPDISCOVER packet out */
send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast);
- if(verbose)
+ if(verbose)
printf("\n\n");
return OK;
if((current_time-start_time)>=dhcpoffer_timeout)
break;
- if(verbose)
+ if(verbose)
printf("\n\n");
bzero(&source,sizeof(source));
result=OK;
result=receive_dhcp_packet(&offer_packet,sizeof(offer_packet),sock,dhcpoffer_timeout,&source);
-
+
if(result!=OK){
if(verbose)
printf(_("Result=ERROR\n"));
continue;
}
else{
- if(verbose)
+ if(verbose)
printf(_("Result=OK\n"));
responses++;
if(verbose){
printf(_("DHCPOFFER from IP address %s"),inet_ntoa(source.sin_addr));
printf(_(" via %s\n"),inet_ntoa(via.sin_addr));
- printf("DHCPOFFER XID: %lu (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid));
+ printf("DHCPOFFER XID: %u (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid));
}
/* check packet xid to see if its the same as the one we used in the discover packet */
if(ntohl(offer_packet.xid)!=packet_xid){
if(verbose)
- printf(_("DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - ignoring packet\n"),ntohl(offer_packet.xid),packet_xid);
+ printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"),ntohl(offer_packet.xid),packet_xid);
continue;
}
printf("\n");
if(result==ERROR){
- if(verbose)
+ if(verbose)
printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
continue;
@@ -598,7 +656,7 @@ int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in
result=sendto(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)dest,sizeof(*dest));
- if(verbose)
+ if(verbose)
printf(_("send_dhcp_packet result: %d\n"),result);
if(result<0)
@@ -613,22 +671,26 @@ int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in
int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address){
struct timeval tv;
fd_set readfds;
+ fd_set oobfds;
int recv_result;
socklen_t address_size;
struct sockaddr_in source_address;
+ int nfound;
/* wait for data to arrive (up time timeout) */
tv.tv_sec=timeout;
tv.tv_usec=0;
FD_ZERO(&readfds);
+ FD_ZERO(&oobfds);
FD_SET(sock,&readfds);
- select(sock+1,&readfds,NULL,NULL,&tv);
+ FD_SET(sock,&oobfds);
+ nfound = select(sock+1,&readfds,NULL,&oobfds,&tv);
/* make sure some data has arrived */
if(!FD_ISSET(sock,&readfds)){
if(verbose)
- printf(_("No (more) data received\n"));
+ printf(_("No (more) data received (nfound: %d)\n"), nfound);
return ERROR;
}
/* Set up the address we're going to bind to. */
bzero(&myname,sizeof(myname));
myname.sin_family=AF_INET;
- myname.sin_port=htons(DHCP_CLIENT_PORT);
- myname.sin_addr.s_addr=INADDR_ANY; /* listen on any address */
+ /* listen to DHCP server port if we're in unicast mode */
+ myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT);
+ myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY;
bzero(&myname.sin_zero,sizeof(myname.sin_zero));
/* create a socket for DHCP communications */
}
/* set the broadcast option - we need this to listen to DHCP broadcast messages */
- if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
+ if(!unicast && setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){
printf(_("Error: Could not set broadcast option on DHCP socket!\n"));
exit(STATE_UNKNOWN);
}
/* bind socket to interface */
#if defined(__linux__)
- strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ);
+ strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ-1);
+ interface.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0';
if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){
printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"),network_interface_name);
exit(STATE_UNKNOWN);
}
#else
- strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ);
+ strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ-1);
+ interface.ifr_name[IFNAMSIZ-1]='\0';
#endif
/* bind the socket */
/* get option length */
option_length=offer_packet->options[x++];
- if(verbose)
+ if(verbose)
printf("Option: %d (0x%02X)\n",option_type,option_length);
/* get option data */
next_server=this_server->next;
free(this_server);
}
-
+
return OK;
}
/* get max lease time we were offered */
if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
max_lease_time=temp_offer->lease_time;
-
+
/* see if we got the address we requested */
if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
received_requested_address=TRUE;
if(verbose){
printf(_("DHCP Server Match: Offerer=%s"),inet_ntoa(temp_offer->server_address));
printf(_(" Requested=%s"),inet_ntoa(temp_server->server_address));
- if(temp_server->answered)
+ if(temp_server->answered)
printf(_(" (duplicate)"));
printf(_("\n"));
}
/* get max lease time we were offered */
if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME)
max_lease_time=temp_offer->lease_time;
-
+
/* see if we got the address we requested */
if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address)))
received_requested_address=TRUE;
int call_getopt(int argc, char **argv){
int c=0;
int i=0;
- struct in_addr ipaddress;
int option_index = 0;
static struct option long_options[] =
- {
+ {
{"serverip", required_argument,0,'s'},
{"requestedip", required_argument,0,'r'},
{"timeout", required_argument,0,'t'},
{"interface", required_argument,0,'i'},
+ {"mac", required_argument,0,'m'},
+ {"unicast", no_argument, 0,'u'},
{"verbose", no_argument, 0,'v'},
{"version", no_argument, 0,'V'},
{"help", no_argument, 0,'h'},
};
while(1){
- c=getopt_long(argc,argv,"+hVvt:s:r:t:i:",long_options,&option_index);
+ c=getopt_long(argc,argv,"+hVvt:s:r:t:i:m:u",long_options,&option_index);
i++;
switch(c){
case 's': /* DHCP server address */
- if(inet_aton(optarg,&ipaddress))
- add_requested_server(ipaddress);
- /*
- else
- usage("Invalid server IP address\n");
- */
+ resolve_host(optarg,&dhcp_ip);
+ add_requested_server(dhcp_ip);
break;
case 'r': /* address we are requested from DHCP servers */
- if(inet_aton(optarg,&ipaddress)){
- requested_address=ipaddress;
- request_specific_address=TRUE;
- }
- /*
- else
- usage("Invalid requested IP address\n");
- */
+ resolve_host(optarg,&requested_address);
+ request_specific_address=TRUE;
break;
case 't': /* timeout */
*/
break;
+ case 'm': /* MAC address */
+
+ if((user_specified_mac=mac_aton(optarg)) == NULL)
+ usage("Cannot parse MAC address.\n");
+ if(verbose)
+ print_hardware_address(user_specified_mac);
+
+ break;
+
case 'i': /* interface name */
strncpy(network_interface_name,optarg,sizeof(network_interface_name)-1);
break;
+ case 'u': /* unicast testing */
+ unicast=1;
+ break;
+
case 'V': /* version */
- print_revision(progname,revision);
+ print_revision(progname, NP_VERSION);
exit(STATE_OK);
case 'h': /* help */
/***********************************************************************
* interface:
- * function mac_addr_dlpi - get the mac address of the interface with
+ * function mac_addr_dlpi - get the mac address of the interface with
* type dev (eg lnc, hme) and unit (0, 1 ..)
*
* parameter: addr: an array of six bytes, has to be allocated by the caller
#endif
+/* resolve host name or die (TODO: move this to netutils.c!) */
+void resolve_host(const char *in,struct in_addr *out){
+ struct addrinfo hints, *ai;
+
+ memset(&hints,0,sizeof(hints));
+ hints.ai_family=PF_INET;
+ if (getaddrinfo(in,NULL,&hints,&ai) != 0)
+ usage_va(_("Invalid hostname/address - %s"),optarg);
+
+ memcpy(out,&((struct sockaddr_in *)ai->ai_addr)->sin_addr,sizeof(*out));
+ freeaddrinfo(ai);
+ }
+
+
+/* parse MAC address string, return 6 bytes (unterminated) or NULL */
+unsigned char *mac_aton(const char *string){
+ static unsigned char result[6];
+ char tmp[3];
+ unsigned i, j;
+
+ for(i=0, j=0; string[i] != '\0' && j < sizeof(result); i++){
+ /* ignore ':' and any other non-hex character */
+ if(!isxdigit(string[i]) || !isxdigit(string[i+1]))
+ continue;
+ tmp[0]=string[i];
+ tmp[1]=string[i+1];
+ tmp[2]='\0';
+ result[j]=strtol(tmp,(char **)NULL,16);
+ i++;
+ j++;
+ }
+
+ return (j==6) ? result : NULL;
+ }
+
+
+void print_hardware_address(const unsigned char *address){
+ int i;
+
+ printf(_("Hardware address: "));
+ for (i=0; i<5; i++)
+ printf("%2.2x:", address[i]);
+ printf("%2.2x", address[i]);
+ putchar('\n');
+ }
+
+
/* print usage help */
void print_help(void){
- print_revision(progname,revision);
+ print_revision(progname, NP_VERSION);
printf("Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n");
printf (COPYRIGHT, copyright, email);
-
+
printf("%s\n", _("This plugin tests the availability of DHCP servers on a network."));
printf ("\n\n");
print_usage();
printf (_(UT_HELP_VRSN));
+ printf (_(UT_EXTRA_OPTS));
printf (_(UT_VERBOSE));
printf (" %s\n", _("Seconds to wait for DHCPOFFER before timeout occurs"));
printf (" %s\n", "-i, --interface=STRING");
printf (" %s\n", _("Interface to to use for listening (i.e. eth0)"));
+ printf (" %s\n", "-m, --mac=STRING");
+ printf (" %s\n", _("MAC address to use in the DHCP request"));
+ printf (" %s\n", "-u, --unicast");
+ printf (" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s"));
+
+#ifdef NP_EXTRA_OPTS
+ printf ("\n");
+ printf ("%s\n", _("Notes:"));
+ printf (_(UT_EXTRA_OPTS_NOTES));
+#endif
+ printf (_(UT_SUPPORT));
return;
}
void
print_usage(void){
-
+
printf (_("Usage:"));
- printf ("%s [-s serverip] [-r requestedip] [-t timeout] [-i interface] [-v]\n",progname);
-
+ printf (" %s [-v] [-u] [-s serverip] [-r requestedip] [-t timeout]\n",progname);
+ printf (" [-i interface] [-m mac]\n");
+
return;
}