diff options
author | Stanley Hopcroft <stanleyhopcroft@users.sourceforge.net> | 2004-12-07 02:33:17 +0000 |
---|---|---|
committer | Stanley Hopcroft <stanleyhopcroft@users.sourceforge.net> | 2004-12-07 02:33:17 +0000 |
commit | d50a9917ef38c44ac2067dba508cf2e690ff1412 (patch) | |
tree | ce8f21a473e9339ae1424418ad31cdc483c0f462 /plugins/check_dhcp.c | |
parent | 8ce197c306c62402aa20ea6d821462062bd1843e (diff) | |
download | monitoring-plugins-d50a9917ef38c44ac2067dba508cf2e690ff1412.tar.gz |
New plugin: check_dhcp.c. Attempt to port to non Linux platform.
git-svn-id: https://nagiosplug.svn.sourceforge.net/svnroot/nagiosplug/nagiosplug/trunk@1008 f882894a-f735-0410-b71e-b25c423dba1c
Diffstat (limited to 'plugins/check_dhcp.c')
-rw-r--r-- | plugins/check_dhcp.c | 1061 |
1 files changed, 1061 insertions, 0 deletions
diff --git a/plugins/check_dhcp.c b/plugins/check_dhcp.c new file mode 100644 index 00000000..ee893cd2 --- /dev/null +++ b/plugins/check_dhcp.c | |||
@@ -0,0 +1,1061 @@ | |||
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 | *****************************************************************************/ | ||
26 | |||
27 | #include "common.h" | ||
28 | #include "netutils.h" | ||
29 | #include "utils.h" | ||
30 | |||
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> | ||
46 | |||
47 | #if defined( __linux__ ) | ||
48 | #include <linux/if_ether.h> | ||
49 | #include <features.h> | ||
50 | #else | ||
51 | #include <netinet/if_ether.h> | ||
52 | #include <sys/sysctl.h> | ||
53 | #include <net/if_dl.h> | ||
54 | #endif | ||
55 | |||
56 | const char *progname = "check_dhcp"; | ||
57 | |||
58 | /*#define DEBUG*/ | ||
59 | #define HAVE_GETOPT_H | ||
60 | |||
61 | |||
62 | /**** Common definitions ****/ | ||
63 | |||
64 | #define STATE_OK 0 | ||
65 | #define STATE_WARNING 1 | ||
66 | #define STATE_CRITICAL 2 | ||
67 | #define STATE_UNKNOWN -1 | ||
68 | |||
69 | #define OK 0 | ||
70 | #define ERROR -1 | ||
71 | |||
72 | #define FALSE 0 | ||
73 | #define TRUE 1 | ||
74 | |||
75 | |||
76 | /**** DHCP definitions ****/ | ||
77 | |||
78 | #define MAX_DHCP_CHADDR_LENGTH 16 | ||
79 | #define MAX_DHCP_SNAME_LENGTH 64 | ||
80 | #define MAX_DHCP_FILE_LENGTH 128 | ||
81 | #define MAX_DHCP_OPTIONS_LENGTH 312 | ||
82 | |||
83 | |||
84 | typedef struct dhcp_packet_struct{ | ||
85 | u_int8_t op; /* packet type */ | ||
86 | u_int8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ | ||
87 | u_int8_t hlen; /* length of hardware address (of this machine) */ | ||
88 | u_int8_t hops; /* hops */ | ||
89 | u_int32_t xid; /* random transaction id number - chosen by this machine */ | ||
90 | u_int16_t secs; /* seconds used in timing */ | ||
91 | u_int16_t flags; /* flags */ | ||
92 | struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ | ||
93 | struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ | ||
94 | struct in_addr siaddr; /* IP address of DHCP server */ | ||
95 | struct in_addr giaddr; /* IP address of DHCP relay */ | ||
96 | unsigned char chaddr [MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ | ||
97 | char sname [MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ | ||
98 | char file [MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ | ||
99 | char options[MAX_DHCP_OPTIONS_LENGTH]; /* options */ | ||
100 | }dhcp_packet; | ||
101 | |||
102 | |||
103 | typedef struct dhcp_offer_struct{ | ||
104 | struct in_addr server_address; /* address of DHCP server that sent this offer */ | ||
105 | struct in_addr offered_address; /* the IP address that was offered to us */ | ||
106 | u_int32_t lease_time; /* lease time in seconds */ | ||
107 | u_int32_t renewal_time; /* renewal time in seconds */ | ||
108 | u_int32_t rebinding_time; /* rebinding time in seconds */ | ||
109 | struct dhcp_offer_struct *next; | ||
110 | }dhcp_offer; | ||
111 | |||
112 | |||
113 | typedef struct requested_server_struct{ | ||
114 | struct in_addr server_address; | ||
115 | struct requested_server_struct *next; | ||
116 | }requested_server; | ||
117 | |||
118 | |||
119 | #define BOOTREQUEST 1 | ||
120 | #define BOOTREPLY 2 | ||
121 | |||
122 | #define DHCPDISCOVER 1 | ||
123 | #define DHCPOFFER 2 | ||
124 | #define DHCPREQUEST 3 | ||
125 | #define DHCPDECLINE 4 | ||
126 | #define DHCPACK 5 | ||
127 | #define DHCPNACK 6 | ||
128 | #define DHCPRELEASE 7 | ||
129 | |||
130 | #define DHCP_OPTION_MESSAGE_TYPE 53 | ||
131 | #define DHCP_OPTION_HOST_NAME 12 | ||
132 | #define DHCP_OPTION_BROADCAST_ADDRESS 28 | ||
133 | #define DHCP_OPTION_REQUESTED_ADDRESS 50 | ||
134 | #define DHCP_OPTION_LEASE_TIME 51 | ||
135 | #define DHCP_OPTION_RENEWAL_TIME 58 | ||
136 | #define DHCP_OPTION_REBINDING_TIME 59 | ||
137 | |||
138 | #define DHCP_INFINITE_TIME 0xFFFFFFFF | ||
139 | |||
140 | #define DHCP_BROADCAST_FLAG 32768 | ||
141 | |||
142 | #define DHCP_SERVER_PORT 67 | ||
143 | #define DHCP_CLIENT_PORT 68 | ||
144 | |||
145 | #define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ | ||
146 | #define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ | ||
147 | |||
148 | unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH]=""; | ||
149 | |||
150 | char network_interface_name[8]="eth0"; | ||
151 | |||
152 | u_int32_t packet_xid=0; | ||
153 | |||
154 | u_int32_t dhcp_lease_time=0; | ||
155 | u_int32_t dhcp_renewal_time=0; | ||
156 | u_int32_t dhcp_rebinding_time=0; | ||
157 | |||
158 | int dhcpoffer_timeout=2; | ||
159 | |||
160 | dhcp_offer *dhcp_offer_list=NULL; | ||
161 | requested_server *requested_server_list=NULL; | ||
162 | |||
163 | int valid_responses=0; /* number of valid DHCPOFFERs we received */ | ||
164 | int requested_servers=0; | ||
165 | int requested_responses=0; | ||
166 | |||
167 | int request_specific_address=FALSE; | ||
168 | int received_requested_address=FALSE; | ||
169 | struct in_addr requested_address; | ||
170 | |||
171 | |||
172 | int process_arguments(int, char **); | ||
173 | int call_getopt(int, char **); | ||
174 | int validate_arguments(void); | ||
175 | void print_usage(void); | ||
176 | void print_help(void); | ||
177 | |||
178 | int get_hardware_address(int,char *); | ||
179 | |||
180 | int send_dhcp_discover(int); | ||
181 | int get_dhcp_offer(int); | ||
182 | |||
183 | int get_results(void); | ||
184 | |||
185 | int add_dhcp_offer(struct in_addr,dhcp_packet *); | ||
186 | int free_dhcp_offer_list(void); | ||
187 | int free_requested_server_list(void); | ||
188 | |||
189 | int create_dhcp_socket(void); | ||
190 | int close_dhcp_socket(int); | ||
191 | int send_dhcp_packet(void *,int,int,struct sockaddr_in *); | ||
192 | int receive_dhcp_packet(void *,int,int,int,struct sockaddr_in *); | ||
193 | |||
194 | |||
195 | |||
196 | int main(int argc, char **argv){ | ||
197 | int dhcp_socket; | ||
198 | int result; | ||
199 | |||
200 | if(process_arguments(argc,argv)!=OK){ | ||
201 | /*usage("Invalid command arguments supplied\n");*/ | ||
202 | printf("Invalid command arguments supplied\n"); | ||
203 | exit(STATE_UNKNOWN); | ||
204 | } | ||
205 | |||
206 | |||
207 | /* create socket for DHCP communications */ | ||
208 | dhcp_socket=create_dhcp_socket(); | ||
209 | |||
210 | /* get hardware address of client machine */ | ||
211 | get_hardware_address(dhcp_socket,network_interface_name); | ||
212 | |||
213 | /* send DHCPDISCOVER packet */ | ||
214 | send_dhcp_discover(dhcp_socket); | ||
215 | |||
216 | /* wait for a DHCPOFFER packet */ | ||
217 | get_dhcp_offer(dhcp_socket); | ||
218 | |||
219 | /* close socket we created */ | ||
220 | close_dhcp_socket(dhcp_socket); | ||
221 | |||
222 | /* determine state/plugin output to return */ | ||
223 | result=get_results(); | ||
224 | |||
225 | /* free allocated memory */ | ||
226 | free_dhcp_offer_list(); | ||
227 | free_requested_server_list(); | ||
228 | |||
229 | return result; | ||
230 | } | ||
231 | |||
232 | |||
233 | |||
234 | /* determines hardware address on client machine */ | ||
235 | int get_hardware_address(int sock,char *interface_name){ | ||
236 | #if defined(__linux__) | ||
237 | struct ifreq ifr; | ||
238 | |||
239 | strncpy((char *)&ifr.ifr_name,interface_name,sizeof(ifr.ifr_name)); | ||
240 | |||
241 | /* try and grab hardware address of requested interface */ | ||
242 | if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){ | ||
243 | printf("Error: Could not get hardware address of interface '%s'\n",interface_name); | ||
244 | exit(STATE_UNKNOWN); | ||
245 | } | ||
246 | |||
247 | memcpy(&client_hardware_address[0],&ifr.ifr_hwaddr.sa_data,6); | ||
248 | #else | ||
249 | /* Code from getmac.c posted at http://lists.freebsd.org/pipermail/freebsd-hackers/2004-June/007415.html | ||
250 | * by Alecs King based on Unix Network programming Ch 17 | ||
251 | */ | ||
252 | |||
253 | int mib[6], len; | ||
254 | char *buf; | ||
255 | unsigned char *ptr; | ||
256 | struct if_msghdr *ifm; | ||
257 | struct sockaddr_dl *sdl; | ||
258 | |||
259 | mib[0] = CTL_NET; | ||
260 | mib[1] = AF_ROUTE; | ||
261 | mib[2] = 0; | ||
262 | mib[3] = AF_LINK; | ||
263 | mib[4] = NET_RT_IFLIST; | ||
264 | |||
265 | if ((mib[5] = if_nametoindex(interface_name)) == 0) { | ||
266 | perror("if_nametoindex error"); | ||
267 | exit(2); | ||
268 | } | ||
269 | |||
270 | if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { | ||
271 | perror("sysctl 1 error"); | ||
272 | exit(3); | ||
273 | } | ||
274 | |||
275 | if ((buf = malloc(len)) == NULL) { | ||
276 | perror("malloc error"); | ||
277 | exit(4); | ||
278 | } | ||
279 | |||
280 | if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { | ||
281 | perror("sysctl 2 error"); | ||
282 | exit(5); | ||
283 | } | ||
284 | |||
285 | ifm = (struct if_msghdr *)buf; | ||
286 | sdl = (struct sockaddr_dl *)(ifm + 1); | ||
287 | ptr = (unsigned char *)LLADDR(sdl); | ||
288 | memcpy(&client_hardware_address[0], ptr, 6) ; | ||
289 | #endif | ||
290 | |||
291 | |||
292 | #ifdef DEBUG | ||
293 | printf("Hardware address: %02x:%02x:%02x:",client_hardware_address[0],client_hardware_address[1],client_hardware_address[2]); | ||
294 | printf("%02x:",client_hardware_address[3]); | ||
295 | printf("%02x:%02x\n",client_hardware_address[4],client_hardware_address[5]); | ||
296 | printf("\n"); | ||
297 | #endif | ||
298 | |||
299 | return OK; | ||
300 | } | ||
301 | |||
302 | |||
303 | /* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ | ||
304 | int send_dhcp_discover(int sock){ | ||
305 | dhcp_packet discover_packet; | ||
306 | struct sockaddr_in sockaddr_broadcast; | ||
307 | |||
308 | |||
309 | /* clear the packet data structure */ | ||
310 | bzero(&discover_packet,sizeof(discover_packet)); | ||
311 | |||
312 | |||
313 | /* boot request flag (backward compatible with BOOTP servers) */ | ||
314 | discover_packet.op=BOOTREQUEST; | ||
315 | |||
316 | /* hardware address type */ | ||
317 | discover_packet.htype=ETHERNET_HARDWARE_ADDRESS; | ||
318 | |||
319 | /* length of our hardware address */ | ||
320 | discover_packet.hlen=ETHERNET_HARDWARE_ADDRESS_LENGTH; | ||
321 | |||
322 | discover_packet.hops=0; | ||
323 | |||
324 | /* transaction id is supposed to be random */ | ||
325 | srand(time(NULL)); | ||
326 | packet_xid=random(); | ||
327 | discover_packet.xid=htonl(packet_xid); | ||
328 | |||
329 | /**** WHAT THE HECK IS UP WITH THIS?!? IF I DON'T MAKE THIS CALL, ONLY ONE SERVER RESPONSE IS PROCESSED!!!! ****/ | ||
330 | /* downright bizzarre... */ | ||
331 | ntohl(discover_packet.xid); | ||
332 | |||
333 | /*discover_packet.secs=htons(65535);*/ | ||
334 | discover_packet.secs=0xFF; | ||
335 | |||
336 | /* tell server it should broadcast its response */ | ||
337 | discover_packet.flags=htons(DHCP_BROADCAST_FLAG); | ||
338 | |||
339 | /* our hardware address */ | ||
340 | memcpy(discover_packet.chaddr,client_hardware_address,ETHERNET_HARDWARE_ADDRESS_LENGTH); | ||
341 | |||
342 | /* first four bytes of options field is magic cookie (as per RFC 2132) */ | ||
343 | discover_packet.options[0]='\x63'; | ||
344 | discover_packet.options[1]='\x82'; | ||
345 | discover_packet.options[2]='\x53'; | ||
346 | discover_packet.options[3]='\x63'; | ||
347 | |||
348 | /* DHCP message type is embedded in options field */ | ||
349 | discover_packet.options[4]=DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ | ||
350 | discover_packet.options[5]='\x01'; /* DHCP message option length in bytes */ | ||
351 | discover_packet.options[6]=DHCPDISCOVER; | ||
352 | |||
353 | /* the IP address we're requesting */ | ||
354 | if(request_specific_address==TRUE){ | ||
355 | discover_packet.options[7]=DHCP_OPTION_REQUESTED_ADDRESS; | ||
356 | discover_packet.options[8]='\x04'; | ||
357 | memcpy(&discover_packet.options[9],&requested_address,sizeof(requested_address)); | ||
358 | } | ||
359 | |||
360 | /* send the DHCPDISCOVER packet to broadcast address */ | ||
361 | sockaddr_broadcast.sin_family=AF_INET; | ||
362 | sockaddr_broadcast.sin_port=htons(DHCP_SERVER_PORT); | ||
363 | sockaddr_broadcast.sin_addr.s_addr=INADDR_BROADCAST; | ||
364 | bzero(&sockaddr_broadcast.sin_zero,sizeof(sockaddr_broadcast.sin_zero)); | ||
365 | |||
366 | |||
367 | #ifdef DEBUG | ||
368 | printf("DHCPDISCOVER to %s port %d\n",inet_ntoa(sockaddr_broadcast.sin_addr),ntohs(sockaddr_broadcast.sin_port)); | ||
369 | printf("DHCPDISCOVER XID: %lu (0x%X)\n",ntohl(discover_packet.xid),ntohl(discover_packet.xid)); | ||
370 | printf("DHCDISCOVER ciaddr: %s\n",inet_ntoa(discover_packet.ciaddr)); | ||
371 | printf("DHCDISCOVER yiaddr: %s\n",inet_ntoa(discover_packet.yiaddr)); | ||
372 | printf("DHCDISCOVER siaddr: %s\n",inet_ntoa(discover_packet.siaddr)); | ||
373 | printf("DHCDISCOVER giaddr: %s\n",inet_ntoa(discover_packet.giaddr)); | ||
374 | #endif | ||
375 | |||
376 | /* send the DHCPDISCOVER packet out */ | ||
377 | send_dhcp_packet(&discover_packet,sizeof(discover_packet),sock,&sockaddr_broadcast); | ||
378 | |||
379 | #ifdef DEBUG | ||
380 | printf("\n\n"); | ||
381 | #endif | ||
382 | |||
383 | return OK; | ||
384 | } | ||
385 | |||
386 | |||
387 | |||
388 | |||
389 | /* waits for a DHCPOFFER message from one or more DHCP servers */ | ||
390 | int get_dhcp_offer(int sock){ | ||
391 | dhcp_packet offer_packet; | ||
392 | struct sockaddr_in source; | ||
393 | int result=OK; | ||
394 | int timeout=1; | ||
395 | int responses=0; | ||
396 | int x; | ||
397 | time_t start_time; | ||
398 | time_t current_time; | ||
399 | |||
400 | time(&start_time); | ||
401 | |||
402 | /* receive as many responses as we can */ | ||
403 | for(responses=0,valid_responses=0;;){ | ||
404 | |||
405 | time(¤t_time); | ||
406 | if((current_time-start_time)>=dhcpoffer_timeout) | ||
407 | break; | ||
408 | |||
409 | #ifdef DEBUG | ||
410 | printf("\n\n"); | ||
411 | #endif | ||
412 | |||
413 | bzero(&source,sizeof(source)); | ||
414 | bzero(&offer_packet,sizeof(offer_packet)); | ||
415 | |||
416 | result=OK; | ||
417 | result=receive_dhcp_packet(&offer_packet,sizeof(offer_packet),sock,dhcpoffer_timeout,&source); | ||
418 | |||
419 | if(result!=OK){ | ||
420 | #ifdef DEBUG | ||
421 | printf("Result=ERROR\n"); | ||
422 | #endif | ||
423 | continue; | ||
424 | } | ||
425 | else{ | ||
426 | #ifdef DEBUG | ||
427 | printf("Result=OK\n"); | ||
428 | #endif | ||
429 | responses++; | ||
430 | } | ||
431 | |||
432 | #ifdef DEBUG | ||
433 | printf("DHCPOFFER from IP address %s\n",inet_ntoa(source.sin_addr)); | ||
434 | printf("DHCPOFFER XID: %lu (0x%X)\n",ntohl(offer_packet.xid),ntohl(offer_packet.xid)); | ||
435 | #endif | ||
436 | |||
437 | /* check packet xid to see if its the same as the one we used in the discover packet */ | ||
438 | if(ntohl(offer_packet.xid)!=packet_xid){ | ||
439 | #ifdef DEBUG | ||
440 | printf("DHCPOFFER XID (%lu) did not match DHCPDISCOVER XID (%lu) - ignoring packet\n",ntohl(offer_packet.xid),packet_xid); | ||
441 | #endif | ||
442 | continue; | ||
443 | } | ||
444 | |||
445 | /* check hardware address */ | ||
446 | result=OK; | ||
447 | #ifdef DEBUG | ||
448 | printf("DHCPOFFER chaddr: "); | ||
449 | #endif | ||
450 | for(x=0;x<ETHERNET_HARDWARE_ADDRESS_LENGTH;x++){ | ||
451 | #ifdef DEBUG | ||
452 | printf("%02X",(unsigned char)offer_packet.chaddr[x]); | ||
453 | #endif | ||
454 | if(offer_packet.chaddr[x]!=client_hardware_address[x]){ | ||
455 | result=ERROR; | ||
456 | } | ||
457 | } | ||
458 | #ifdef DEBUG | ||
459 | printf("\n"); | ||
460 | #endif | ||
461 | if(result==ERROR){ | ||
462 | #ifdef DEBUG | ||
463 | printf("DHCPOFFER hardware address did not match our own - ignoring packet\n"); | ||
464 | #endif | ||
465 | continue; | ||
466 | } | ||
467 | |||
468 | #ifdef DEBUG | ||
469 | printf("DHCPOFFER ciaddr: %s\n",inet_ntoa(offer_packet.ciaddr)); | ||
470 | printf("DHCPOFFER yiaddr: %s\n",inet_ntoa(offer_packet.yiaddr)); | ||
471 | printf("DHCPOFFER siaddr: %s\n",inet_ntoa(offer_packet.siaddr)); | ||
472 | printf("DHCPOFFER giaddr: %s\n",inet_ntoa(offer_packet.giaddr)); | ||
473 | #endif | ||
474 | |||
475 | add_dhcp_offer(source.sin_addr,&offer_packet); | ||
476 | |||
477 | valid_responses++; | ||
478 | } | ||
479 | |||
480 | #ifdef DEBUG | ||
481 | printf("Total responses seen on the wire: %d\n",responses); | ||
482 | printf("Valid responses for this machine: %d\n",valid_responses); | ||
483 | #endif | ||
484 | |||
485 | return OK; | ||
486 | } | ||
487 | |||
488 | |||
489 | |||
490 | /* sends a DHCP packet */ | ||
491 | int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest){ | ||
492 | struct sockaddr_in myname; | ||
493 | int result; | ||
494 | |||
495 | result=sendto(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)dest,sizeof(*dest)); | ||
496 | |||
497 | #ifdef DEBUG | ||
498 | printf("send_dhcp_packet result: %d\n",result); | ||
499 | #endif | ||
500 | |||
501 | if(result<0) | ||
502 | return ERROR; | ||
503 | |||
504 | return OK; | ||
505 | } | ||
506 | |||
507 | |||
508 | |||
509 | /* receives a DHCP packet */ | ||
510 | int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address){ | ||
511 | struct timeval tv; | ||
512 | fd_set readfds; | ||
513 | int recv_result; | ||
514 | socklen_t address_size; | ||
515 | struct sockaddr_in source_address; | ||
516 | |||
517 | |||
518 | /* wait for data to arrive (up time timeout) */ | ||
519 | tv.tv_sec=timeout; | ||
520 | tv.tv_usec=0; | ||
521 | FD_ZERO(&readfds); | ||
522 | FD_SET(sock,&readfds); | ||
523 | select(sock+1,&readfds,NULL,NULL,&tv); | ||
524 | |||
525 | /* make sure some data has arrived */ | ||
526 | if(!FD_ISSET(sock,&readfds)){ | ||
527 | #ifdef DEBUG | ||
528 | printf("No (more) data received\n"); | ||
529 | #endif | ||
530 | return ERROR; | ||
531 | } | ||
532 | |||
533 | else{ | ||
534 | |||
535 | /* 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 | ||
536 | not being interpreted correctly. sigh... */ | ||
537 | bzero(&source_address,sizeof(source_address)); | ||
538 | address_size=sizeof(source_address); | ||
539 | recv_result=recvfrom(sock,(char *)buffer,buffer_size,MSG_PEEK,(struct sockaddr *)&source_address,&address_size); | ||
540 | #ifdef DEBUG | ||
541 | printf("recv_result_1: %d\n",recv_result); | ||
542 | #endif | ||
543 | recv_result=recvfrom(sock,(char *)buffer,buffer_size,0,(struct sockaddr *)&source_address,&address_size); | ||
544 | #ifdef DEBUG | ||
545 | printf("recv_result_2: %d\n",recv_result); | ||
546 | #endif | ||
547 | |||
548 | if(recv_result==-1){ | ||
549 | #ifdef DEBUG | ||
550 | printf("recvfrom() failed, "); | ||
551 | printf("errno: (%d) -> %s\n",errno,strerror(errno)); | ||
552 | #endif | ||
553 | return ERROR; | ||
554 | } | ||
555 | else{ | ||
556 | #ifdef DEBUG | ||
557 | printf("receive_dhcp_packet() result: %d\n",recv_result); | ||
558 | printf("receive_dhcp_packet() source: %s\n",inet_ntoa(source_address.sin_addr)); | ||
559 | #endif | ||
560 | |||
561 | memcpy(address,&source_address,sizeof(source_address)); | ||
562 | return OK; | ||
563 | } | ||
564 | } | ||
565 | |||
566 | return OK; | ||
567 | } | ||
568 | |||
569 | |||
570 | |||
571 | /* creates a socket for DHCP communication */ | ||
572 | int create_dhcp_socket(void){ | ||
573 | struct sockaddr_in myname; | ||
574 | struct ifreq interface; | ||
575 | int sock; | ||
576 | int flag=1; | ||
577 | |||
578 | /* Set up the address we're going to bind to. */ | ||
579 | bzero(&myname,sizeof(myname)); | ||
580 | myname.sin_family=AF_INET; | ||
581 | myname.sin_port=htons(DHCP_CLIENT_PORT); | ||
582 | myname.sin_addr.s_addr=INADDR_ANY; /* listen on any address */ | ||
583 | bzero(&myname.sin_zero,sizeof(myname.sin_zero)); | ||
584 | |||
585 | /* create a socket for DHCP communications */ | ||
586 | sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); | ||
587 | if(sock<0){ | ||
588 | printf("Error: Could not create socket!\n"); | ||
589 | exit(STATE_UNKNOWN); | ||
590 | } | ||
591 | |||
592 | #ifdef DEBUG | ||
593 | printf("DHCP socket: %d\n",sock); | ||
594 | #endif | ||
595 | |||
596 | /* set the reuse address flag so we don't get errors when restarting */ | ||
597 | flag=1; | ||
598 | if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){ | ||
599 | printf("Error: Could not set reuse address option on DHCP socket!\n"); | ||
600 | exit(STATE_UNKNOWN); | ||
601 | } | ||
602 | |||
603 | /* set the broadcast option - we need this to listen to DHCP broadcast messages */ | ||
604 | if(setsockopt(sock,SOL_SOCKET,SO_BROADCAST,(char *)&flag,sizeof flag)<0){ | ||
605 | printf("Error: Could not set broadcast option on DHCP socket!\n"); | ||
606 | exit(STATE_UNKNOWN); | ||
607 | } | ||
608 | |||
609 | /* bind socket to interface */ | ||
610 | #if defined(__linux__) | ||
611 | strncpy(interface.ifr_ifrn.ifrn_name,network_interface_name,IFNAMSIZ); | ||
612 | if(setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE,(char *)&interface,sizeof(interface))<0){ | ||
613 | printf("Error: Could not bind socket to interface %s. Check your privileges...\n",network_interface_name); | ||
614 | exit(STATE_UNKNOWN); | ||
615 | } | ||
616 | |||
617 | #else | ||
618 | strncpy(interface.ifr_name,network_interface_name,IFNAMSIZ); | ||
619 | #endif | ||
620 | |||
621 | /* bind the socket */ | ||
622 | if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){ | ||
623 | printf("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n",DHCP_CLIENT_PORT); | ||
624 | exit(STATE_UNKNOWN); | ||
625 | } | ||
626 | |||
627 | return sock; | ||
628 | } | ||
629 | |||
630 | |||
631 | |||
632 | |||
633 | |||
634 | /* closes DHCP socket */ | ||
635 | int close_dhcp_socket(int sock){ | ||
636 | |||
637 | close(sock); | ||
638 | |||
639 | return OK; | ||
640 | } | ||
641 | |||
642 | |||
643 | |||
644 | |||
645 | /* adds a requested server address to list in memory */ | ||
646 | int add_requested_server(struct in_addr server_address){ | ||
647 | requested_server *new_server; | ||
648 | |||
649 | new_server=(requested_server *)malloc(sizeof(requested_server)); | ||
650 | if(new_server==NULL) | ||
651 | return ERROR; | ||
652 | |||
653 | new_server->server_address=server_address; | ||
654 | |||
655 | new_server->next=requested_server_list; | ||
656 | requested_server_list=new_server; | ||
657 | |||
658 | requested_servers++; | ||
659 | |||
660 | #ifdef DEBUG | ||
661 | printf("Requested server address: %s\n",inet_ntoa(new_server->server_address)); | ||
662 | #endif | ||
663 | |||
664 | return OK; | ||
665 | } | ||
666 | |||
667 | |||
668 | |||
669 | |||
670 | /* adds a DHCP OFFER to list in memory */ | ||
671 | int add_dhcp_offer(struct in_addr source,dhcp_packet *offer_packet){ | ||
672 | dhcp_offer *new_offer; | ||
673 | int x; | ||
674 | int y; | ||
675 | unsigned option_type; | ||
676 | unsigned option_length; | ||
677 | |||
678 | if(offer_packet==NULL) | ||
679 | return ERROR; | ||
680 | |||
681 | /* process all DHCP options present in the packet */ | ||
682 | for(x=4;x<MAX_DHCP_OPTIONS_LENGTH;){ | ||
683 | |||
684 | /* end of options (0 is really just a pad, but bail out anyway) */ | ||
685 | if((int)offer_packet->options[x]==-1 || (int)offer_packet->options[x]==0) | ||
686 | break; | ||
687 | |||
688 | /* get option type */ | ||
689 | option_type=offer_packet->options[x++]; | ||
690 | |||
691 | /* get option length */ | ||
692 | option_length=offer_packet->options[x++]; | ||
693 | |||
694 | #ifdef DEBUG | ||
695 | printf("Option: %d (0x%02X)\n",option_type,option_length); | ||
696 | #endif | ||
697 | |||
698 | /* get option data */ | ||
699 | if(option_type==DHCP_OPTION_LEASE_TIME) | ||
700 | dhcp_lease_time=ntohl(*((u_int32_t *)&offer_packet->options[x])); | ||
701 | if(option_type==DHCP_OPTION_RENEWAL_TIME) | ||
702 | dhcp_renewal_time=ntohl(*((u_int32_t *)&offer_packet->options[x])); | ||
703 | if(option_type==DHCP_OPTION_REBINDING_TIME) | ||
704 | dhcp_rebinding_time=ntohl(*((u_int32_t *)&offer_packet->options[x])); | ||
705 | |||
706 | /* skip option data we're ignoring */ | ||
707 | else | ||
708 | for(y=0;y<option_length;y++,x++); | ||
709 | } | ||
710 | |||
711 | #ifdef DEBUG | ||
712 | if(dhcp_lease_time==DHCP_INFINITE_TIME) | ||
713 | printf("Lease Time: Infinite\n"); | ||
714 | else | ||
715 | printf("Lease Time: %lu seconds\n",(unsigned long)dhcp_lease_time); | ||
716 | if(dhcp_renewal_time==DHCP_INFINITE_TIME) | ||
717 | printf("Renewal Time: Infinite\n"); | ||
718 | else | ||
719 | printf("Renewal Time: %lu seconds\n",(unsigned long)dhcp_renewal_time); | ||
720 | if(dhcp_rebinding_time==DHCP_INFINITE_TIME) | ||
721 | printf("Rebinding Time: Infinite\n"); | ||
722 | printf("Rebinding Time: %lu seconds\n",(unsigned long)dhcp_rebinding_time); | ||
723 | #endif | ||
724 | |||
725 | new_offer=(dhcp_offer *)malloc(sizeof(dhcp_offer)); | ||
726 | |||
727 | if(new_offer==NULL) | ||
728 | return ERROR; | ||
729 | |||
730 | |||
731 | new_offer->server_address=source; | ||
732 | new_offer->offered_address=offer_packet->yiaddr; | ||
733 | new_offer->lease_time=dhcp_lease_time; | ||
734 | new_offer->renewal_time=dhcp_renewal_time; | ||
735 | new_offer->rebinding_time=dhcp_rebinding_time; | ||
736 | |||
737 | |||
738 | #ifdef DEBUG | ||
739 | printf("Added offer from server @ %s",inet_ntoa(new_offer->server_address)); | ||
740 | printf(" of IP address %s\n",inet_ntoa(new_offer->offered_address)); | ||
741 | #endif | ||
742 | |||
743 | /* add new offer to head of list */ | ||
744 | new_offer->next=dhcp_offer_list; | ||
745 | dhcp_offer_list=new_offer; | ||
746 | |||
747 | return OK; | ||
748 | } | ||
749 | |||
750 | |||
751 | |||
752 | |||
753 | /* frees memory allocated to DHCP OFFER list */ | ||
754 | int free_dhcp_offer_list(void){ | ||
755 | dhcp_offer *this_offer; | ||
756 | dhcp_offer *next_offer; | ||
757 | |||
758 | for(this_offer=dhcp_offer_list;this_offer!=NULL;this_offer=next_offer){ | ||
759 | next_offer=this_offer->next; | ||
760 | free(this_offer); | ||
761 | } | ||
762 | |||
763 | return OK; | ||
764 | } | ||
765 | |||
766 | |||
767 | |||
768 | |||
769 | /* frees memory allocated to requested server list */ | ||
770 | int free_requested_server_list(void){ | ||
771 | requested_server *this_server; | ||
772 | requested_server *next_server; | ||
773 | |||
774 | for(this_server=requested_server_list;this_server!=NULL;this_server=next_server){ | ||
775 | next_server=this_server->next; | ||
776 | free(this_server); | ||
777 | } | ||
778 | |||
779 | return OK; | ||
780 | } | ||
781 | |||
782 | |||
783 | /* gets state and plugin output to return */ | ||
784 | int get_results(void){ | ||
785 | dhcp_offer *temp_offer; | ||
786 | requested_server *temp_server; | ||
787 | int result; | ||
788 | u_int32_t max_lease_time=0; | ||
789 | |||
790 | received_requested_address=FALSE; | ||
791 | |||
792 | /* checks responses from requested servers */ | ||
793 | requested_responses=0; | ||
794 | if(requested_servers>0){ | ||
795 | |||
796 | for(temp_server=requested_server_list;temp_server!=NULL;temp_server=temp_server->next){ | ||
797 | |||
798 | for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){ | ||
799 | |||
800 | /* get max lease time we were offered */ | ||
801 | if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME) | ||
802 | max_lease_time=temp_offer->lease_time; | ||
803 | |||
804 | /* see if we got the address we requested */ | ||
805 | if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address))) | ||
806 | received_requested_address=TRUE; | ||
807 | |||
808 | /* see if the servers we wanted a response from talked to us or not */ | ||
809 | if(!memcmp(&temp_offer->server_address,&temp_server->server_address,sizeof(temp_server->server_address))){ | ||
810 | #ifdef DEBUG | ||
811 | printf("DHCP Server Match: Offerer=%s",inet_ntoa(temp_offer->server_address)); | ||
812 | printf(" Requested=%s\n",inet_ntoa(temp_server->server_address)); | ||
813 | #endif | ||
814 | requested_responses++; | ||
815 | } | ||
816 | } | ||
817 | } | ||
818 | |||
819 | } | ||
820 | |||
821 | /* else check and see if we got our requested address from any server */ | ||
822 | else{ | ||
823 | |||
824 | for(temp_offer=dhcp_offer_list;temp_offer!=NULL;temp_offer=temp_offer->next){ | ||
825 | |||
826 | /* get max lease time we were offered */ | ||
827 | if(temp_offer->lease_time>max_lease_time || temp_offer->lease_time==DHCP_INFINITE_TIME) | ||
828 | max_lease_time=temp_offer->lease_time; | ||
829 | |||
830 | /* see if we got the address we requested */ | ||
831 | if(!memcmp(&requested_address,&temp_offer->offered_address,sizeof(requested_address))) | ||
832 | received_requested_address=TRUE; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | result=STATE_OK; | ||
837 | if(valid_responses==0) | ||
838 | result=STATE_CRITICAL; | ||
839 | else if(requested_servers>0 && requested_responses==0) | ||
840 | result=STATE_CRITICAL; | ||
841 | else if(requested_responses<requested_servers) | ||
842 | result=STATE_WARNING; | ||
843 | else if(request_specific_address==TRUE && received_requested_address==FALSE) | ||
844 | result=STATE_WARNING; | ||
845 | |||
846 | |||
847 | printf("DHCP %s: ",(result==STATE_OK)?"ok":"problem"); | ||
848 | |||
849 | /* we didn't receive any DHCPOFFERs */ | ||
850 | if(dhcp_offer_list==NULL){ | ||
851 | printf("No DHCPOFFERs were received.\n"); | ||
852 | return result; | ||
853 | } | ||
854 | |||
855 | printf("Received %d DHCPOFFER(s)",valid_responses); | ||
856 | |||
857 | if(requested_servers>0) | ||
858 | printf(", %s%d of %d requested servers responded",((requested_responses<requested_servers) && requested_responses>0)?"only ":"",requested_responses,requested_servers); | ||
859 | |||
860 | if(request_specific_address==TRUE) | ||
861 | printf(", requested address (%s) was %soffered",inet_ntoa(requested_address),(received_requested_address==TRUE)?"":"not "); | ||
862 | |||
863 | printf(", max lease time = "); | ||
864 | if(max_lease_time==DHCP_INFINITE_TIME) | ||
865 | printf("Infinity"); | ||
866 | else | ||
867 | printf("%lu sec",(unsigned long)max_lease_time); | ||
868 | |||
869 | printf(".\n"); | ||
870 | |||
871 | return result; | ||
872 | } | ||
873 | |||
874 | |||
875 | |||
876 | |||
877 | |||
878 | |||
879 | /* print usage help */ | ||
880 | void print_help(void){ | ||
881 | |||
882 | /*print_revision(progname,"$Revision$");*/ | ||
883 | |||
884 | printf("Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)\n\n"); | ||
885 | printf("This plugin tests the availability of DHCP servers on a network.\n\n"); | ||
886 | |||
887 | print_usage(); | ||
888 | |||
889 | printf | ||
890 | ("\nOptions:\n" | ||
891 | " -s, --serverip=IPADDRESS\n" | ||
892 | " IP address of DHCP server that we must hear from\n" | ||
893 | " -r, --requestedip=IPADDRESS\n" | ||
894 | " IP address that should be offered by at least one DHCP server\n" | ||
895 | " -t, --timeout=INTEGER\n" | ||
896 | " Seconds to wait for DHCPOFFER before timeout occurs\n" | ||
897 | " -i, --interface=STRING\n" | ||
898 | " Interface to to use for listening (i.e. eth0)\n" | ||
899 | " -v, --verbose\n" | ||
900 | " Print extra information (command-line use only)\n" | ||
901 | " -h, --help\n" | ||
902 | " Print detailed help screen\n" | ||
903 | " -V, --version\n" | ||
904 | " Print version information\n\n" | ||
905 | ); | ||
906 | |||
907 | /*support();*/ | ||
908 | |||
909 | return; | ||
910 | } | ||
911 | |||
912 | |||
913 | /* prints usage information */ | ||
914 | void print_usage(void){ | ||
915 | |||
916 | printf("Usage: %s [-s serverip] [-r requestedip] [-t timeout] [-i interface]\n",progname); | ||
917 | printf(" %s --help\n",progname); | ||
918 | printf(" %s --version\n",progname); | ||
919 | |||
920 | return; | ||
921 | } | ||
922 | |||
923 | |||
924 | |||
925 | |||
926 | /* process command-line arguments */ | ||
927 | int process_arguments(int argc, char **argv){ | ||
928 | int c; | ||
929 | |||
930 | if(argc<1) | ||
931 | return ERROR; | ||
932 | |||
933 | c=0; | ||
934 | while((c+=(call_getopt(argc-c,&argv[c])))<argc){ | ||
935 | |||
936 | /* | ||
937 | if(is_option(argv[c])) | ||
938 | continue; | ||
939 | */ | ||
940 | } | ||
941 | |||
942 | return validate_arguments(); | ||
943 | } | ||
944 | |||
945 | |||
946 | |||
947 | int call_getopt(int argc, char **argv){ | ||
948 | int c=0; | ||
949 | int i=0; | ||
950 | struct in_addr ipaddress; | ||
951 | |||
952 | #ifdef HAVE_GETOPT_H | ||
953 | int option_index = 0; | ||
954 | static struct option long_options[] = | ||
955 | { | ||
956 | {"serverip", required_argument,0,'s'}, | ||
957 | {"requestedip", required_argument,0,'r'}, | ||
958 | {"timeout", required_argument,0,'t'}, | ||
959 | {"interface", required_argument,0,'i'}, | ||
960 | {"verbose", no_argument, 0,'v'}, | ||
961 | {"version", no_argument, 0,'V'}, | ||
962 | {"help", no_argument, 0,'h'}, | ||
963 | {0,0,0,0} | ||
964 | }; | ||
965 | #endif | ||
966 | |||
967 | while(1){ | ||
968 | #ifdef HAVE_GETOPT_H | ||
969 | c=getopt_long(argc,argv,"+hVvt:s:r:t:i:",long_options,&option_index); | ||
970 | #else | ||
971 | c=getopt(argc,argv,"+?hVvt:s:r:t:i:"); | ||
972 | #endif | ||
973 | |||
974 | i++; | ||
975 | |||
976 | if(c==-1||c==EOF||c==1) | ||
977 | break; | ||
978 | |||
979 | switch(c){ | ||
980 | case 'w': | ||
981 | case 'r': | ||
982 | case 't': | ||
983 | case 'i': | ||
984 | i++; | ||
985 | break; | ||
986 | default: | ||
987 | break; | ||
988 | } | ||
989 | |||
990 | switch(c){ | ||
991 | |||
992 | case 's': /* DHCP server address */ | ||
993 | if(inet_aton(optarg,&ipaddress)) | ||
994 | add_requested_server(ipaddress); | ||
995 | /* | ||
996 | else | ||
997 | usage("Invalid server IP address\n"); | ||
998 | */ | ||
999 | break; | ||
1000 | |||
1001 | case 'r': /* address we are requested from DHCP servers */ | ||
1002 | if(inet_aton(optarg,&ipaddress)){ | ||
1003 | requested_address=ipaddress; | ||
1004 | request_specific_address=TRUE; | ||
1005 | } | ||
1006 | /* | ||
1007 | else | ||
1008 | usage("Invalid requested IP address\n"); | ||
1009 | */ | ||
1010 | break; | ||
1011 | |||
1012 | case 't': /* timeout */ | ||
1013 | |||
1014 | /* | ||
1015 | if(is_intnonneg(optarg)) | ||
1016 | */ | ||
1017 | if(atoi(optarg)>0) | ||
1018 | dhcpoffer_timeout=atoi(optarg); | ||
1019 | /* | ||
1020 | else | ||
1021 | usage("Time interval must be a nonnegative integer\n"); | ||
1022 | */ | ||
1023 | break; | ||
1024 | |||
1025 | case 'i': /* interface name */ | ||
1026 | |||
1027 | strncpy(network_interface_name,optarg,sizeof(network_interface_name)-1); | ||
1028 | network_interface_name[sizeof(network_interface_name)-1]='\x0'; | ||
1029 | |||
1030 | break; | ||
1031 | |||
1032 | case 'V': /* version */ | ||
1033 | |||
1034 | /*print_revision(progname,"$Revision$");*/ | ||
1035 | exit(STATE_OK); | ||
1036 | |||
1037 | case 'h': /* help */ | ||
1038 | |||
1039 | print_help(); | ||
1040 | exit(STATE_OK); | ||
1041 | |||
1042 | case '?': /* help */ | ||
1043 | |||
1044 | /*usage("Invalid argument\n");*/ | ||
1045 | break; | ||
1046 | |||
1047 | default: | ||
1048 | break; | ||
1049 | } | ||
1050 | } | ||
1051 | |||
1052 | return i; | ||
1053 | } | ||
1054 | |||
1055 | |||
1056 | |||
1057 | int validate_arguments(void){ | ||
1058 | |||
1059 | return OK; | ||
1060 | } | ||
1061 | |||