summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins-root/check_dhcp.c741
-rw-r--r--plugins-root/check_dhcp.d/config.h50
2 files changed, 448 insertions, 343 deletions
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c
index 70809956..3732d970 100644
--- a/plugins-root/check_dhcp.c
+++ b/plugins-root/check_dhcp.c
@@ -4,7 +4,7 @@
4 * 4 *
5 * License: GPL 5 * License: GPL
6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org) 6 * Copyright (c) 2001-2004 Ethan Galstad (nagios@nagios.org)
7 * Copyright (c) 2001-2023 Monitoring Plugins Development Team 7 * Copyright (c) 2001-2025 Monitoring Plugins Development Team
8 * 8 *
9 * Description: 9 * Description:
10 * 10 *
@@ -34,13 +34,17 @@
34 *****************************************************************************/ 34 *****************************************************************************/
35 35
36const char *progname = "check_dhcp"; 36const char *progname = "check_dhcp";
37const char *copyright = "2001-2024"; 37const char *copyright = "2001-2025";
38const char *email = "devel@monitoring-plugins.org"; 38const char *email = "devel@monitoring-plugins.org";
39 39
40#include "common.h" 40#include "../plugins/common.h"
41#include "netutils.h" 41#include "../plugins/utils.h"
42#include "utils.h" 42#include "./check_dhcp.d/config.h"
43#include "../lib/output.h"
44#include "../lib/utils_base.h"
43 45
46#include "states.h"
47#include <stdint.h>
44#include <ctype.h> 48#include <ctype.h>
45#include <stdio.h> 49#include <stdio.h>
46#include <stdlib.h> 50#include <stdlib.h>
@@ -111,8 +115,9 @@ static long mac_addr_dlpi(const char *, int, u_char *);
111 115
112/**** Common definitions ****/ 116/**** Common definitions ****/
113 117
114#define OK 0 118#define OK 0
115#define ERROR -1 119#define ERROR -1
120#define MAC_ADDR_LEN 6
116 121
117/**** DHCP definitions ****/ 122/**** DHCP definitions ****/
118 123
@@ -149,12 +154,6 @@ typedef struct dhcp_offer_struct {
149 struct dhcp_offer_struct *next; 154 struct dhcp_offer_struct *next;
150} dhcp_offer; 155} dhcp_offer;
151 156
152typedef struct requested_server_struct {
153 struct in_addr server_address;
154 bool answered;
155 struct requested_server_struct *next;
156} requested_server;
157
158#define BOOTREQUEST 1 157#define BOOTREQUEST 1
159#define BOOTREPLY 2 158#define BOOTREPLY 2
160 159
@@ -186,65 +185,60 @@ typedef struct requested_server_struct {
186#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */ 185#define ETHERNET_HARDWARE_ADDRESS 1 /* used in htype field of dhcp packet */
187#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */ 186#define ETHERNET_HARDWARE_ADDRESS_LENGTH 6 /* length of Ethernet hardware addresses */
188 187
189static bool unicast = false; /* unicast mode: mimic a DHCP relay */
190static bool exclusive = false; /* exclusive mode aka "rogue DHCP server detection" */
191static struct in_addr my_ip; /* our address (required for relay) */
192static struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
193static unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
194static unsigned char *user_specified_mac = NULL;
195
196static char network_interface_name[IFNAMSIZ] = "eth0";
197
198static uint32_t packet_xid = 0;
199
200static uint32_t dhcp_lease_time = 0;
201static uint32_t dhcp_renewal_time = 0;
202static uint32_t dhcp_rebinding_time = 0;
203
204static int dhcpoffer_timeout = 2;
205
206static dhcp_offer *dhcp_offer_list = NULL;
207static requested_server *requested_server_list = NULL;
208
209static int valid_responses = 0; /* number of valid DHCPOFFERs we received */
210static int requested_servers = 0;
211static int requested_responses = 0;
212
213static bool request_specific_address = false;
214static bool received_requested_address = false;
215static int verbose = 0; 188static int verbose = 0;
216static struct in_addr requested_address;
217 189
218static int process_arguments(int, char **); 190typedef struct process_arguments_wrapper {
219static int call_getopt(int, char **); 191 int error;
220static int validate_arguments(int); 192 check_dhcp_config config;
193} process_arguments_wrapper;
194
195static process_arguments_wrapper process_arguments(int /*argc*/, char ** /*argv*/);
221void print_usage(void); 196void print_usage(void);
222static void print_help(void); 197static void print_help(void);
223 198
224static void resolve_host(const char *in, struct in_addr *out); 199static void resolve_host(const char * /*in*/, struct in_addr * /*out*/);
225static unsigned char *mac_aton(const char *); 200static unsigned char *mac_aton(const char * /*string*/);
226static void print_hardware_address(const unsigned char *); 201static void print_hardware_address(const unsigned char * /*address*/);
227static int get_hardware_address(int, char *); 202static int get_hardware_address(int /*sock*/, char * /*interface_name*/, unsigned char *client_hardware_address);
228static int get_ip_address(int, char *); 203
229 204typedef struct get_ip_address_wrapper {
230static int send_dhcp_discover(int); 205 int error;
231static int get_dhcp_offer(int); 206 struct in_addr my_ip;
232 207} get_ip_address_wrapper;
233static int get_results(void); 208static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/);
234 209
235static int add_dhcp_offer(struct in_addr, dhcp_packet *); 210typedef struct send_dhcp_discover_wrapper {
236static int free_dhcp_offer_list(void); 211 int error;
237static int free_requested_server_list(void); 212 uint32_t packet_xid;
238 213} send_dhcp_discover_wrapper;
239static int create_dhcp_socket(void); 214static send_dhcp_discover_wrapper send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
240static int close_dhcp_socket(int); 215 bool request_specific_address, struct in_addr my_ip,
241static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); 216 unsigned char *client_hardware_address);
242static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); 217typedef struct get_dhcp_offer_wrapper {
218 int error;
219 int valid_responses;
220 dhcp_offer *dhcp_offer_list;
221} get_dhcp_offer_wrapper;
222static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
223 const unsigned char *client_hardware_address);
224
225static mp_subcheck get_results(bool exclusive, int requested_servers, struct in_addr requested_address, bool request_specific_address,
226 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list);
227
228typedef struct add_dhcp_offer_wrapper {
229 int error;
230 dhcp_offer *dhcp_offer_list;
231} add_dhcp_offer_wrapper;
232static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/, dhcp_packet * /*offer_packet*/, dhcp_offer *dhcp_offer_list);
233static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list);
234static int free_requested_server_list(requested_server *requested_server_list);
235
236static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name);
237static int close_dhcp_socket(int /*sock*/);
238static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, struct sockaddr_in * /*dest*/);
239static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/, int /*timeout*/, struct sockaddr_in * /*address*/);
243 240
244int main(int argc, char **argv) { 241int main(int argc, char **argv) {
245 int dhcp_socket;
246 int result = STATE_UNKNOWN;
247
248 setlocale(LC_ALL, ""); 242 setlocale(LC_ALL, "");
249 bindtextdomain(PACKAGE, LOCALEDIR); 243 bindtextdomain(PACKAGE, LOCALEDIR);
250 textdomain(PACKAGE); 244 textdomain(PACKAGE);
@@ -252,45 +246,80 @@ int main(int argc, char **argv) {
252 /* Parse extra opts if any */ 246 /* Parse extra opts if any */
253 argv = np_extra_opts(&argc, argv, progname); 247 argv = np_extra_opts(&argc, argv, progname);
254 248
255 if (process_arguments(argc, argv) != OK) { 249 process_arguments_wrapper tmp = process_arguments(argc, argv);
250
251 if (tmp.error != OK) {
256 usage4(_("Could not parse arguments")); 252 usage4(_("Could not parse arguments"));
257 } 253 }
258 254
255 check_dhcp_config config = tmp.config;
256 if (config.output_format_is_set) {
257 mp_set_format(config.output_format);
258 }
259
259 /* create socket for DHCP communications */ 260 /* create socket for DHCP communications */
260 dhcp_socket = create_dhcp_socket(); 261 int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name);
261 262
262 /* get hardware address of client machine */ 263 /* get hardware address of client machine */
263 if (user_specified_mac != NULL) { 264 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
264 memcpy(client_hardware_address, user_specified_mac, 6); 265 if (config.user_specified_mac != NULL) {
266 memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN);
265 } else { 267 } else {
266 get_hardware_address(dhcp_socket, network_interface_name); 268 get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address);
267 } 269 }
268 270
269 if (unicast) { /* get IP address of client machine */ 271 struct in_addr my_ip = {0};
270 get_ip_address(dhcp_socket, network_interface_name); 272
273 if (config.unicast_mode) { /* get IP address of client machine */
274 get_ip_address_wrapper tmp_get_ip = get_ip_address(dhcp_socket, config.network_interface_name);
275 if (tmp_get_ip.error == OK) {
276 my_ip = tmp_get_ip.my_ip;
277 } else {
278 // TODO failed to get own IP
279 die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode");
280 }
271 } 281 }
272 282
273 /* send DHCPDISCOVER packet */ 283 /* send DHCPDISCOVER packet */
274 send_dhcp_discover(dhcp_socket); 284 send_dhcp_discover_wrapper disco_res = send_dhcp_discover(dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address,
285 config.request_specific_address, my_ip, client_hardware_address);
286
287 if (disco_res.error != OK) {
288 // DO something?
289 die(STATE_UNKNOWN, "Failed to send DHCP discover");
290 }
275 291
276 /* wait for a DHCPOFFER packet */ 292 /* wait for a DHCPOFFER packet */
277 get_dhcp_offer(dhcp_socket); 293 get_dhcp_offer_wrapper offer_res =
294 get_dhcp_offer(dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address);
295
296 int valid_responses = 0;
297 dhcp_offer *dhcp_offer_list = NULL;
298 if (offer_res.error == OK) {
299 valid_responses = offer_res.valid_responses;
300 dhcp_offer_list = offer_res.dhcp_offer_list;
301 } else {
302 die(STATE_UNKNOWN, "Failed to get DHCP offers");
303 }
278 304
279 /* close socket we created */ 305 /* close socket we created */
280 close_dhcp_socket(dhcp_socket); 306 close_dhcp_socket(dhcp_socket);
281 307
282 /* determine state/plugin output to return */ 308 mp_check overall = mp_check_init();
283 result = get_results();
284 309
310 /* determine state/plugin output to return */
311 mp_subcheck sc_res = get_results(config.exclusive_mode, config.num_of_requested_servers, config.requested_address,
312 config.request_specific_address, config.requested_server_list, valid_responses, dhcp_offer_list);
313 mp_add_subcheck_to_check(&overall, sc_res);
285 /* free allocated memory */ 314 /* free allocated memory */
286 free_dhcp_offer_list(); 315 free_dhcp_offer_list(dhcp_offer_list);
287 free_requested_server_list(); 316 free_requested_server_list(config.requested_server_list);
288 317
289 return result; 318 mp_exit(overall);
290} 319}
291 320
292/* determines hardware address on client machine */ 321/* determines hardware address on client machine */
293static int get_hardware_address(int sock, char *interface_name) { 322int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) {
294 323
295#if defined(__linux__) 324#if defined(__linux__)
296 struct ifreq ifr; 325 struct ifreq ifr;
@@ -304,7 +333,7 @@ static int get_hardware_address(int sock, char *interface_name) {
304 exit(STATE_UNKNOWN); 333 exit(STATE_UNKNOWN);
305 } 334 }
306 335
307 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); 336 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
308 337
309#elif defined(__bsd__) 338#elif defined(__bsd__)
310 /* King 2004 see ACKNOWLEDGEMENTS */ 339 /* King 2004 see ACKNOWLEDGEMENTS */
@@ -404,7 +433,7 @@ static int get_hardware_address(int sock, char *interface_name) {
404} 433}
405 434
406/* determines IP address of the client interface */ 435/* determines IP address of the client interface */
407static int get_ip_address(int sock, char *interface_name) { 436get_ip_address_wrapper get_ip_address(int sock, char *interface_name) {
408#if defined(SIOCGIFADDR) 437#if defined(SIOCGIFADDR)
409 struct ifreq ifr; 438 struct ifreq ifr;
410 439
@@ -416,29 +445,28 @@ static int get_ip_address(int sock, char *interface_name) {
416 exit(STATE_UNKNOWN); 445 exit(STATE_UNKNOWN);
417 } 446 }
418 447
419 my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
420
421#else 448#else
422 printf(_("Error: Cannot get interface IP address on this platform.\n")); 449 printf(_("Error: Cannot get interface IP address on this platform.\n"));
423 exit(STATE_UNKNOWN); 450 exit(STATE_UNKNOWN);
424#endif 451#endif
425 452
453 get_ip_address_wrapper result = {
454 .error = OK,
455 .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,
456 };
457
426 if (verbose) { 458 if (verbose) {
427 printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); 459 printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip));
428 } 460 }
429 461
430 return OK; 462 return result;
431} 463}
432 464
433/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ 465/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
434static int send_dhcp_discover(int sock) { 466static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip, struct in_addr requested_address,
435 dhcp_packet discover_packet; 467 bool request_specific_address, struct in_addr my_ip,
436 struct sockaddr_in sockaddr_broadcast; 468 unsigned char *client_hardware_address) {
437 unsigned short opts; 469 dhcp_packet discover_packet = {0};
438
439 /* clear the packet data structure */
440 bzero(&discover_packet, sizeof(discover_packet));
441
442 /* boot request flag (backward compatible with BOOTP servers) */ 470 /* boot request flag (backward compatible with BOOTP servers) */
443 discover_packet.op = BOOTREQUEST; 471 discover_packet.op = BOOTREQUEST;
444 472
@@ -448,12 +476,15 @@ static int send_dhcp_discover(int sock) {
448 /* length of our hardware address */ 476 /* length of our hardware address */
449 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; 477 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
450 478
479 send_dhcp_discover_wrapper result = {
480 .error = OK,
481 };
451 /* 482 /*
452 * transaction ID is supposed to be random. 483 * transaction ID is supposed to be random.
453 */ 484 */
454 srand(time(NULL) ^ getpid()); 485 srand(time(NULL) ^ getpid());
455 packet_xid = random(); 486 result.packet_xid = random();
456 discover_packet.xid = htonl(packet_xid); 487 discover_packet.xid = htonl(result.packet_xid);
457 488
458 /*discover_packet.secs=htons(65535);*/ 489 /*discover_packet.secs=htons(65535);*/
459 discover_packet.secs = 0xFF; 490 discover_packet.secs = 0xFF;
@@ -473,7 +504,7 @@ static int send_dhcp_discover(int sock) {
473 discover_packet.options[2] = '\x53'; 504 discover_packet.options[2] = '\x53';
474 discover_packet.options[3] = '\x63'; 505 discover_packet.options[3] = '\x63';
475 506
476 opts = 4; 507 unsigned short opts = 4;
477 /* DHCP message type is embedded in options field */ 508 /* DHCP message type is embedded in options field */
478 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ 509 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
479 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ 510 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */
@@ -497,10 +528,11 @@ static int send_dhcp_discover(int sock) {
497 discover_packet.hops = unicast ? 1 : 0; 528 discover_packet.hops = unicast ? 1 : 0;
498 529
499 /* send the DHCPDISCOVER packet to broadcast address */ 530 /* send the DHCPDISCOVER packet to broadcast address */
500 sockaddr_broadcast.sin_family = AF_INET; 531 struct sockaddr_in sockaddr_broadcast = {
501 sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); 532 .sin_family = AF_INET,
502 sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; 533 .sin_port = htons(DHCP_SERVER_PORT),
503 bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); 534 .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST,
535 };
504 536
505 if (verbose) { 537 if (verbose) {
506 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); 538 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port));
@@ -518,25 +550,21 @@ static int send_dhcp_discover(int sock) {
518 printf("\n\n"); 550 printf("\n\n");
519 } 551 }
520 552
521 return OK; 553 return result;
522} 554}
523 555
524/* waits for a DHCPOFFER message from one or more DHCP servers */ 556/* waits for a DHCPOFFER message from one or more DHCP servers */
525static int get_dhcp_offer(int sock) { 557get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
526 dhcp_packet offer_packet; 558 const unsigned char *client_hardware_address) {
527 struct sockaddr_in source;
528 struct sockaddr_in via;
529 int result = OK;
530 int responses = 0;
531 int x;
532 time_t start_time; 559 time_t start_time;
533 time_t current_time;
534
535 time(&start_time); 560 time(&start_time);
536 561
562 int result = OK;
563 int responses = 0;
564 int valid_responses = 0;
537 /* receive as many responses as we can */ 565 /* receive as many responses as we can */
538 for (responses = 0, valid_responses = 0;;) { 566 for (;;) {
539 567 time_t current_time;
540 time(&current_time); 568 time(&current_time);
541 if ((current_time - start_time) >= dhcpoffer_timeout) { 569 if ((current_time - start_time) >= dhcpoffer_timeout) {
542 break; 570 break;
@@ -546,9 +574,8 @@ static int get_dhcp_offer(int sock) {
546 printf("\n\n"); 574 printf("\n\n");
547 } 575 }
548 576
549 bzero(&source, sizeof(source)); 577 struct sockaddr_in source = {0};
550 bzero(&via, sizeof(via)); 578 dhcp_packet offer_packet = {0};
551 bzero(&offer_packet, sizeof(offer_packet));
552 579
553 result = OK; 580 result = OK;
554 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); 581 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source);
@@ -559,16 +586,16 @@ static int get_dhcp_offer(int sock) {
559 } 586 }
560 587
561 continue; 588 continue;
562 } else {
563 if (verbose) {
564 printf(_("Result=OK\n"));
565 }
566
567 responses++;
568 } 589 }
590 if (verbose) {
591 printf(_("Result=OK\n"));
592 }
593
594 responses++;
569 595
570 /* The "source" is either a server or a relay. */ 596 /* The "source" is either a server or a relay. */
571 /* Save a copy of "source" into "via" even if it's via itself */ 597 /* Save a copy of "source" into "via" even if it's via itself */
598 struct sockaddr_in via = {0};
572 memcpy(&via, &source, sizeof(source)); 599 memcpy(&via, &source, sizeof(source));
573 600
574 if (verbose) { 601 if (verbose) {
@@ -593,12 +620,12 @@ static int get_dhcp_offer(int sock) {
593 printf("DHCPOFFER chaddr: "); 620 printf("DHCPOFFER chaddr: ");
594 } 621 }
595 622
596 for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { 623 for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) {
597 if (verbose) { 624 if (verbose) {
598 printf("%02X", (unsigned char)offer_packet.chaddr[x]); 625 printf("%02X", offer_packet.chaddr[i]);
599 } 626 }
600 627
601 if (offer_packet.chaddr[x] != client_hardware_address[x]) { 628 if (offer_packet.chaddr[i] != client_hardware_address[i]) {
602 result = ERROR; 629 result = ERROR;
603 } 630 }
604 } 631 }
@@ -621,7 +648,12 @@ static int get_dhcp_offer(int sock) {
621 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); 648 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr));
622 } 649 }
623 650
624 add_dhcp_offer(source.sin_addr, &offer_packet); 651 add_dhcp_offer_wrapper add_res = add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list);
652 if (add_res.error != OK) {
653 // TODO
654 } else {
655 dhcp_offer_list = add_res.dhcp_offer_list;
656 }
625 657
626 valid_responses++; 658 valid_responses++;
627 } 659 }
@@ -631,14 +663,17 @@ static int get_dhcp_offer(int sock) {
631 printf(_("Valid responses for this machine: %d\n"), valid_responses); 663 printf(_("Valid responses for this machine: %d\n"), valid_responses);
632 } 664 }
633 665
634 return OK; 666 get_dhcp_offer_wrapper ret_val = {
667 .error = OK,
668 .valid_responses = valid_responses,
669 .dhcp_offer_list = dhcp_offer_list,
670 };
671 return ret_val;
635} 672}
636 673
637/* sends a DHCP packet */ 674/* sends a DHCP packet */
638static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { 675int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) {
639 int result; 676 int result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
640
641 result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
642 677
643 if (verbose) { 678 if (verbose) {
644 printf(_("send_dhcp_packet result: %d\n"), result); 679 printf(_("send_dhcp_packet result: %d\n"), result);
@@ -652,23 +687,19 @@ static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sock
652} 687}
653 688
654/* receives a DHCP packet */ 689/* receives a DHCP packet */
655static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { 690int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) {
656 struct timeval tv;
657 fd_set readfds;
658 fd_set oobfds;
659 int recv_result;
660 socklen_t address_size;
661 struct sockaddr_in source_address;
662 int nfound;
663
664 /* wait for data to arrive (up time timeout) */ 691 /* wait for data to arrive (up time timeout) */
665 tv.tv_sec = timeout; 692 struct timeval timeout_val = {
666 tv.tv_usec = 0; 693 .tv_sec = timeout,
694 .tv_usec = 0,
695 };
696 fd_set readfds;
667 FD_ZERO(&readfds); 697 FD_ZERO(&readfds);
698 fd_set oobfds;
668 FD_ZERO(&oobfds); 699 FD_ZERO(&oobfds);
669 FD_SET(sock, &readfds); 700 FD_SET(sock, &readfds);
670 FD_SET(sock, &oobfds); 701 FD_SET(sock, &oobfds);
671 nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); 702 int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val);
672 703
673 /* make sure some data has arrived */ 704 /* make sure some data has arrived */
674 if (!FD_ISSET(sock, &readfds)) { 705 if (!FD_ISSET(sock, &readfds)) {
@@ -678,51 +709,44 @@ static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int time
678 return ERROR; 709 return ERROR;
679 } 710 }
680 711
681 else { 712 struct sockaddr_in source_address = {0};
682 bzero(&source_address, sizeof(source_address)); 713 socklen_t address_size = sizeof(source_address);
683 address_size = sizeof(source_address); 714 int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size);
684 recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); 715 if (verbose) {
685 if (verbose) { 716 printf("recv_result: %d\n", recv_result);
686 printf("recv_result: %d\n", recv_result); 717 }
687 }
688
689 if (recv_result == -1) {
690 if (verbose) {
691 printf(_("recvfrom() failed, "));
692 printf("errno: (%d) -> %s\n", errno, strerror(errno));
693 }
694 return ERROR;
695 } else {
696 if (verbose) {
697 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
698 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
699 }
700 718
701 memcpy(address, &source_address, sizeof(source_address)); 719 if (recv_result == -1) {
702 return OK; 720 if (verbose) {
721 printf(_("recvfrom() failed, "));
722 printf("errno: (%d) -> %s\n", errno, strerror(errno));
703 } 723 }
724 return ERROR;
725 }
726 if (verbose) {
727 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
728 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
704 } 729 }
705 730
731 memcpy(address, &source_address, sizeof(source_address));
706 return OK; 732 return OK;
707} 733}
708 734
709/* creates a socket for DHCP communication */ 735/* creates a socket for DHCP communication */
710static int create_dhcp_socket(void) { 736int create_dhcp_socket(bool unicast, char *network_interface_name) {
711 struct sockaddr_in myname;
712 struct ifreq interface;
713 int sock;
714 int flag = 1;
715
716 /* Set up the address we're going to bind to. */ 737 /* Set up the address we're going to bind to. */
717 bzero(&myname, sizeof(myname));
718 myname.sin_family = AF_INET;
719 /* listen to DHCP server port if we're in unicast mode */ 738 /* listen to DHCP server port if we're in unicast mode */
720 myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); 739 struct sockaddr_in myname = {
721 myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; 740 .sin_family = AF_INET,
722 bzero(&myname.sin_zero, sizeof(myname.sin_zero)); 741 .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT),
742 // TODO previously the next line was trying to use our own IP, we was not set
743 // until some point later, so it was removed. Recheck whether it is actually
744 // necessary/useful
745 .sin_addr.s_addr = INADDR_ANY,
746 };
723 747
724 /* create a socket for DHCP communications */ 748 /* create a socket for DHCP communications */
725 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 749 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
726 if (sock < 0) { 750 if (sock < 0) {
727 printf(_("Error: Could not create socket!\n")); 751 printf(_("Error: Could not create socket!\n"));
728 exit(STATE_UNKNOWN); 752 exit(STATE_UNKNOWN);
@@ -733,7 +757,7 @@ static int create_dhcp_socket(void) {
733 } 757 }
734 758
735 /* set the reuse address flag so we don't get errors when restarting */ 759 /* set the reuse address flag so we don't get errors when restarting */
736 flag = 1; 760 int flag = 1;
737 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 761 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
738 printf(_("Error: Could not set reuse address option on DHCP socket!\n")); 762 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
739 exit(STATE_UNKNOWN); 763 exit(STATE_UNKNOWN);
@@ -745,6 +769,7 @@ static int create_dhcp_socket(void) {
745 exit(STATE_UNKNOWN); 769 exit(STATE_UNKNOWN);
746 } 770 }
747 771
772 struct ifreq interface;
748 /* bind socket to interface */ 773 /* bind socket to interface */
749#if defined(__linux__) 774#if defined(__linux__)
750 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); 775 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1);
@@ -769,18 +794,14 @@ static int create_dhcp_socket(void) {
769} 794}
770 795
771/* closes DHCP socket */ 796/* closes DHCP socket */
772static int close_dhcp_socket(int sock) { 797int close_dhcp_socket(int sock) {
773
774 close(sock); 798 close(sock);
775
776 return OK; 799 return OK;
777} 800}
778 801
779/* adds a requested server address to list in memory */ 802/* adds a requested server address to list in memory */
780static int add_requested_server(struct in_addr server_address) { 803int add_requested_server(struct in_addr server_address, int *requested_servers, requested_server **requested_server_list) {
781 requested_server *new_server; 804 requested_server *new_server = (requested_server *)malloc(sizeof(requested_server));
782
783 new_server = (requested_server *)malloc(sizeof(requested_server));
784 if (new_server == NULL) { 805 if (new_server == NULL) {
785 return ERROR; 806 return ERROR;
786 } 807 }
@@ -788,10 +809,10 @@ static int add_requested_server(struct in_addr server_address) {
788 new_server->server_address = server_address; 809 new_server->server_address = server_address;
789 new_server->answered = false; 810 new_server->answered = false;
790 811
791 new_server->next = requested_server_list; 812 new_server->next = *requested_server_list;
792 requested_server_list = new_server; 813 *requested_server_list = new_server;
793 814
794 requested_servers++; 815 *requested_servers += 1;
795 816
796 if (verbose) { 817 if (verbose) {
797 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); 818 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address));
@@ -801,29 +822,31 @@ static int add_requested_server(struct in_addr server_address) {
801} 822}
802 823
803/* adds a DHCP OFFER to list in memory */ 824/* adds a DHCP OFFER to list in memory */
804static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { 825add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet, dhcp_offer *dhcp_offer_list) {
805 dhcp_offer *new_offer;
806 int x;
807 unsigned option_type;
808 unsigned option_length;
809 struct in_addr serv_ident = {0};
810
811 if (offer_packet == NULL) { 826 if (offer_packet == NULL) {
812 return ERROR; 827 add_dhcp_offer_wrapper tmp = {
828 .error = ERROR,
829 };
830 return tmp;
813 } 831 }
814 832
833 uint32_t dhcp_lease_time = 0;
834 uint32_t dhcp_renewal_time = 0;
835 uint32_t dhcp_rebinding_time = 0;
836 dhcp_offer *new_offer;
837 struct in_addr serv_ident = {0};
815 /* process all DHCP options present in the packet */ 838 /* process all DHCP options present in the packet */
816 for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { 839 for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) {
817 840
818 if ((int)offer_packet->options[x] == -1) { 841 if ((int)offer_packet->options[dchp_opt_idx] == -1) {
819 break; 842 break;
820 } 843 }
821 844
822 /* get option type */ 845 /* get option type */
823 option_type = offer_packet->options[x++]; 846 unsigned option_type = offer_packet->options[dchp_opt_idx++];
824 847
825 /* get option length */ 848 /* get option length */
826 option_length = offer_packet->options[x++]; 849 unsigned option_length = offer_packet->options[dchp_opt_idx++];
827 850
828 if (verbose) { 851 if (verbose) {
829 printf("Option: %d (0x%02X)\n", option_type, option_length); 852 printf("Option: %d (0x%02X)\n", option_type, option_length);
@@ -832,27 +855,27 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
832 /* get option data */ 855 /* get option data */
833 switch (option_type) { 856 switch (option_type) {
834 case DHCP_OPTION_LEASE_TIME: 857 case DHCP_OPTION_LEASE_TIME:
835 memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); 858 memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time));
836 dhcp_lease_time = ntohl(dhcp_lease_time); 859 dhcp_lease_time = ntohl(dhcp_lease_time);
837 break; 860 break;
838 case DHCP_OPTION_RENEWAL_TIME: 861 case DHCP_OPTION_RENEWAL_TIME:
839 memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); 862 memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_renewal_time));
840 dhcp_renewal_time = ntohl(dhcp_renewal_time); 863 dhcp_renewal_time = ntohl(dhcp_renewal_time);
841 break; 864 break;
842 case DHCP_OPTION_REBINDING_TIME: 865 case DHCP_OPTION_REBINDING_TIME:
843 memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); 866 memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_rebinding_time));
844 dhcp_rebinding_time = ntohl(dhcp_rebinding_time); 867 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
845 break; 868 break;
846 case DHCP_OPTION_SERVER_IDENTIFIER: 869 case DHCP_OPTION_SERVER_IDENTIFIER:
847 memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); 870 memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx], sizeof(serv_ident.s_addr));
848 break; 871 break;
849 } 872 }
850 873
851 /* skip option data we're ignoring */ 874 /* skip option data we're ignoring */
852 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */ 875 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */
853 x += 1; 876 dchp_opt_idx += 1;
854 } else { 877 } else {
855 x += option_length; 878 dchp_opt_idx += option_length;
856 } 879 }
857 } 880 }
858 881
@@ -876,7 +899,10 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
876 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); 899 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer));
877 900
878 if (new_offer == NULL) { 901 if (new_offer == NULL) {
879 return ERROR; 902 add_dhcp_offer_wrapper tmp = {
903 .error = ERROR,
904 };
905 return tmp;
880 } 906 }
881 907
882 /* 908 /*
@@ -907,15 +933,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
907 new_offer->next = dhcp_offer_list; 933 new_offer->next = dhcp_offer_list;
908 dhcp_offer_list = new_offer; 934 dhcp_offer_list = new_offer;
909 935
910 return OK; 936 add_dhcp_offer_wrapper result = {
937 .error = OK,
938 .dhcp_offer_list = dhcp_offer_list,
939 };
940
941 return result;
911} 942}
912 943
913/* frees memory allocated to DHCP OFFER list */ 944/* frees memory allocated to DHCP OFFER list */
914static int free_dhcp_offer_list(void) { 945int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) {
915 dhcp_offer *this_offer;
916 dhcp_offer *next_offer; 946 dhcp_offer *next_offer;
917 947 for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
918 for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
919 next_offer = this_offer->next; 948 next_offer = this_offer->next;
920 free(this_offer); 949 free(this_offer);
921 } 950 }
@@ -924,11 +953,9 @@ static int free_dhcp_offer_list(void) {
924} 953}
925 954
926/* frees memory allocated to requested server list */ 955/* frees memory allocated to requested server list */
927static int free_requested_server_list(void) { 956int free_requested_server_list(requested_server *requested_server_list) {
928 requested_server *this_server;
929 requested_server *next_server; 957 requested_server *next_server;
930 958 for (requested_server *this_server = requested_server_list; this_server != NULL; this_server = next_server) {
931 for (this_server = requested_server_list; this_server != NULL; this_server = next_server) {
932 next_server = this_server->next; 959 next_server = this_server->next;
933 free(this_server); 960 free(this_server);
934 } 961 }
@@ -937,22 +964,39 @@ static int free_requested_server_list(void) {
937} 964}
938 965
939/* gets state and plugin output to return */ 966/* gets state and plugin output to return */
940static int get_results(void) { 967mp_subcheck get_results(bool exclusive, const int requested_servers, const struct in_addr requested_address, bool request_specific_address,
941 dhcp_offer *temp_offer, *undesired_offer = NULL; 968 requested_server *requested_server_list, int valid_responses, dhcp_offer *dhcp_offer_list) {
942 requested_server *temp_server; 969 mp_subcheck sc_dhcp_results = mp_subcheck_init();
943 int result; 970 sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK);
944 uint32_t max_lease_time = 0;
945
946 received_requested_address = false;
947 971
948 /* checks responses from requested servers */ 972 /* we didn't receive any DHCPOFFERs */
949 requested_responses = 0; 973 if (dhcp_offer_list == NULL) {
950 if (requested_servers > 0) { 974 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
975 xasprintf(&sc_dhcp_results.output, "%s", "No DHCP offers were received");
976 return sc_dhcp_results;
977 }
951 978
952 for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { 979 if (valid_responses == 0) {
980 // No valid responses at all, so early exit here
981 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
982 xasprintf(&sc_dhcp_results.output, "No valid responses received");
983 return sc_dhcp_results;
984 }
953 985
954 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 986 if (valid_responses == 1) {
987 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses);
988 } else {
989 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses);
990 }
955 991
992 bool received_requested_address = false;
993 dhcp_offer *undesired_offer = NULL;
994 uint32_t max_lease_time = 0;
995 /* checks responses from requested servers */
996 int requested_responses = 0;
997 if (requested_servers > 0) {
998 for (requested_server *temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) {
999 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
956 /* get max lease time we were offered */ 1000 /* get max lease time we were offered */
957 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { 1001 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
958 max_lease_time = temp_offer->lease_time; 1002 max_lease_time = temp_offer->lease_time;
@@ -963,7 +1007,7 @@ static int get_results(void) {
963 received_requested_address = true; 1007 received_requested_address = true;
964 } 1008 }
965 1009
966 /* see if the servers we wanted a response from talked to us or not */ 1010 /* see if the servers we wanted a response from, talked to us or not */
967 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { 1011 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) {
968 if (verbose) { 1012 if (verbose) {
969 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); 1013 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address));
@@ -973,6 +1017,7 @@ static int get_results(void) {
973 } 1017 }
974 printf(_("\n")); 1018 printf(_("\n"));
975 } 1019 }
1020
976 if (!temp_server->answered) { 1021 if (!temp_server->answered) {
977 requested_responses++; 1022 requested_responses++;
978 temp_server->answered = true; 1023 temp_server->answered = true;
@@ -983,19 +1028,32 @@ static int get_results(void) {
983 } 1028 }
984 1029
985 /* exclusive mode: check for undesired offers */ 1030 /* exclusive mode: check for undesired offers */
986 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1031 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
987 if (!temp_offer->desired) { 1032 if (!temp_offer->desired) {
988 undesired_offer = temp_offer; /* Checks only for the first undesired offer */ 1033 undesired_offer = temp_offer; /* Checks only for the first undesired offer */
989 break; /* no further checks needed */ 1034 break; /* no further checks needed */
990 } 1035 }
991 } 1036 }
992 }
993 1037
994 /* else check and see if we got our requested address from any server */ 1038 mp_subcheck sc_rqust_srvs = mp_subcheck_init();
995 else { 1039 xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded", requested_responses, requested_servers);
996 1040
997 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1041 if (requested_responses == requested_servers) {
1042 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK);
1043 } else if (requested_responses == 0) {
1044 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL);
1045 } else if (requested_responses < requested_servers) {
1046 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1047 } else {
1048 // We received more(!) responses than we asked for?
1049 // This case shouldn't happen, but is here for completion
1050 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1051 }
1052 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs);
998 1053
1054 } else {
1055 /* else check and see if we got our requested address from any server */
1056 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) {
999 /* get max lease time we were offered */ 1057 /* get max lease time we were offered */
1000 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) { 1058 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) {
1001 max_lease_time = temp_offer->lease_time; 1059 max_lease_time = temp_offer->lease_time;
@@ -1008,79 +1066,77 @@ static int get_results(void) {
1008 } 1066 }
1009 } 1067 }
1010 1068
1011 result = STATE_OK; 1069 if (max_lease_time == DHCP_INFINITE_TIME) {
1012 if (valid_responses == 0) { 1070 xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output);
1013 result = STATE_CRITICAL; 1071 } else {
1014 } else if (requested_servers > 0 && requested_responses == 0) { 1072 xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds", sc_dhcp_results.output, max_lease_time);
1015 result = STATE_CRITICAL;
1016 } else if (requested_responses < requested_servers) {
1017 result = STATE_WARNING;
1018 } else if (request_specific_address && !received_requested_address) {
1019 result = STATE_WARNING;
1020 } 1073 }
1021 1074
1022 if (exclusive && undesired_offer) { 1075 if (exclusive) {
1023 result = STATE_CRITICAL; 1076 mp_subcheck sc_rogue_server = mp_subcheck_init();
1024 }
1025 1077
1026 if (result == 0) { /* garrett honeycutt 2005 */ 1078 if (undesired_offer != NULL) {
1027 printf("OK: "); 1079 // We wanted to get a DHCPOFFER exclusively from one machine, but another one
1028 } else if (result == 1) { 1080 // sent one (too)
1029 printf("WARNING: "); 1081 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL);
1030 } else if (result == 2) {
1031 printf("CRITICAL: ");
1032 } else if (result == 3) {
1033 printf("UNKNOWN: ");
1034 }
1035 1082
1036 /* we didn't receive any DHCPOFFERs */ 1083 // Get the addresses for printout
1037 if (dhcp_offer_list == NULL) { 1084 // 1.address of the sending server
1038 printf(_("No DHCPOFFERs were received.\n")); 1085 char server_address[INET_ADDRSTRLEN];
1039 return result; 1086 const char *server_address_transformed =
1040 } 1087 inet_ntop(AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address));
1088
1089 if (server_address != server_address_transformed) {
1090 die(STATE_UNKNOWN, "inet_ntop failed");
1091 }
1041 1092
1042 printf(_("Received %d DHCPOFFER(s)"), valid_responses); 1093 // 2.address offered
1094 char offered_address[INET_ADDRSTRLEN];
1095 const char *offered_address_transformed =
1096 inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address, sizeof(offered_address));
1043 1097
1044 if (exclusive && undesired_offer) { 1098 if (offered_address != offered_address_transformed) {
1045 printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); 1099 die(STATE_UNKNOWN, "inet_ntop failed");
1046 printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); 1100 }
1047 return result;
1048 }
1049 1101
1050 if (requested_servers > 0) { 1102 xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s", server_address, offered_address);
1051 printf(_(", %s%d of %d requested servers responded"), 1103 } else {
1052 ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, 1104 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK);
1053 requested_servers); 1105 xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected");
1106 }
1107 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server);
1054 } 1108 }
1055 1109
1056 if (request_specific_address) { 1110 if (request_specific_address) {
1057 printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); 1111 mp_subcheck sc_rqustd_addr = mp_subcheck_init();
1058 }
1059 1112
1060 printf(_(", max lease time = ")); 1113 if (received_requested_address) {
1061 if (max_lease_time == DHCP_INFINITE_TIME) { 1114 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK);
1062 printf(_("Infinity")); 1115 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered", inet_ntoa(requested_address));
1063 } else { 1116 } else {
1064 printf("%lu sec", (unsigned long)max_lease_time); 1117 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING);
1065 } 1118 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered", inet_ntoa(requested_address));
1119 }
1066 1120
1067 printf(".\n"); 1121 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr);
1122 }
1068 1123
1069 return result; 1124 return sc_dhcp_results;
1070} 1125}
1071 1126
1072/* process command-line arguments */ 1127/* process command-line arguments */
1073static int process_arguments(int argc, char **argv) { 1128process_arguments_wrapper process_arguments(int argc, char **argv) {
1074 if (argc < 1) { 1129 if (argc < 1) {
1075 return ERROR; 1130 process_arguments_wrapper tmp = {
1131 .error = ERROR,
1132 };
1133 return tmp;
1076 } 1134 }
1077 1135
1078 call_getopt(argc, argv); 1136 enum {
1079 return validate_arguments(argc); 1137 output_format_index = CHAR_MAX + 1,
1080} 1138 };
1081 1139
1082static int call_getopt(int argc, char **argv) {
1083 extern int optind;
1084 int option_index = 0; 1140 int option_index = 0;
1085 static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, 1141 static struct option long_options[] = {{"serverip", required_argument, 0, 's'},
1086 {"requestedip", required_argument, 0, 'r'}, 1142 {"requestedip", required_argument, 0, 'r'},
@@ -1092,65 +1148,55 @@ static int call_getopt(int argc, char **argv) {
1092 {"verbose", no_argument, 0, 'v'}, 1148 {"verbose", no_argument, 0, 'v'},
1093 {"version", no_argument, 0, 'V'}, 1149 {"version", no_argument, 0, 'V'},
1094 {"help", no_argument, 0, 'h'}, 1150 {"help", no_argument, 0, 'h'},
1151 {"output-format", required_argument, 0, output_format_index},
1095 {0, 0, 0, 0}}; 1152 {0, 0, 0, 0}};
1096 1153
1097 int c = 0; 1154 check_dhcp_config config = check_dhcp_config_init();
1155 int option_char = 0;
1098 while (true) { 1156 while (true) {
1099 c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); 1157 option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index);
1100 1158
1101 if (c == -1 || c == EOF || c == 1) { 1159 if (option_char == -1 || option_char == EOF || option_char == 1) {
1102 break; 1160 break;
1103 } 1161 }
1104 1162
1105 switch (c) { 1163 switch (option_char) {
1106
1107 case 's': /* DHCP server address */ 1164 case 's': /* DHCP server address */
1108 resolve_host(optarg, &dhcp_ip); 1165 resolve_host(optarg, &config.dhcp_ip);
1109 add_requested_server(dhcp_ip); 1166 add_requested_server(config.dhcp_ip, &config.num_of_requested_servers, &config.requested_server_list);
1110 break; 1167 break;
1111 1168
1112 case 'r': /* address we are requested from DHCP servers */ 1169 case 'r': /* address we are requested from DHCP servers */
1113 resolve_host(optarg, &requested_address); 1170 resolve_host(optarg, &config.requested_address);
1114 request_specific_address = true; 1171 config.request_specific_address = true;
1115 break; 1172 break;
1116 1173
1117 case 't': /* timeout */ 1174 case 't': /* timeout */
1118
1119 /*
1120 if(is_intnonneg(optarg))
1121 */
1122 if (atoi(optarg) > 0) { 1175 if (atoi(optarg) > 0) {
1123 dhcpoffer_timeout = atoi(optarg); 1176 config.dhcpoffer_timeout = atoi(optarg);
1124 } 1177 }
1125 /*
1126 else
1127 usage("Time interval must be a nonnegative integer\n");
1128 */
1129 break; 1178 break;
1130 1179
1131 case 'm': /* MAC address */ 1180 case 'm': /* MAC address */
1132 1181 if ((config.user_specified_mac = mac_aton(optarg)) == NULL) {
1133 if ((user_specified_mac = mac_aton(optarg)) == NULL) {
1134 usage("Cannot parse MAC address.\n"); 1182 usage("Cannot parse MAC address.\n");
1135 } 1183 }
1136 if (verbose) { 1184 if (verbose) {
1137 print_hardware_address(user_specified_mac); 1185 print_hardware_address(config.user_specified_mac);
1138 } 1186 }
1139
1140 break; 1187 break;
1141 1188
1142 case 'i': /* interface name */ 1189 case 'i': /* interface name */
1143 1190 strncpy(config.network_interface_name, optarg, sizeof(config.network_interface_name) - 1);
1144 strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); 1191 config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0';
1145 network_interface_name[sizeof(network_interface_name) - 1] = '\x0';
1146
1147 break; 1192 break;
1148 1193
1149 case 'u': /* unicast testing */ 1194 case 'u': /* unicast testing */
1150 unicast = true; 1195 config.unicast_mode = true;
1151 break; 1196 break;
1197
1152 case 'x': /* exclusive testing aka "rogue DHCP server detection" */ 1198 case 'x': /* exclusive testing aka "rogue DHCP server detection" */
1153 exclusive = true; 1199 config.exclusive_mode = true;
1154 break; 1200 break;
1155 1201
1156 case 'V': /* version */ 1202 case 'V': /* version */
@@ -1164,6 +1210,18 @@ static int call_getopt(int argc, char **argv) {
1164 case 'v': /* verbose */ 1210 case 'v': /* verbose */
1165 verbose = 1; 1211 verbose = 1;
1166 break; 1212 break;
1213 case output_format_index: {
1214 parsed_output_format parser = mp_parse_output_format(optarg);
1215 if (!parser.parsing_success) {
1216 // TODO List all available formats here, maybe add anothoer usage function
1217 printf("Invalid output format: %s\n", optarg);
1218 exit(STATE_UNKNOWN);
1219 }
1220
1221 config.output_format_is_set = true;
1222 config.output_format = parser.output_format;
1223 break;
1224 }
1167 case '?': /* help */ 1225 case '?': /* help */
1168 usage5(); 1226 usage5();
1169 break; 1227 break;
@@ -1172,16 +1230,16 @@ static int call_getopt(int argc, char **argv) {
1172 break; 1230 break;
1173 } 1231 }
1174 } 1232 }
1175 return optind;
1176}
1177
1178static int validate_arguments(int argc) {
1179 1233
1180 if (argc - optind > 0) { 1234 if (argc - optind > 0) {
1181 usage(_("Got unexpected non-option argument")); 1235 usage(_("Got unexpected non-option argument"));
1182 } 1236 }
1183 1237
1184 return OK; 1238 process_arguments_wrapper result = {
1239 .config = config,
1240 .error = OK,
1241 };
1242 return result;
1185} 1243}
1186 1244
1187#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) 1245#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
@@ -1300,7 +1358,7 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1300 * 1358 *
1301 ***********************************************************************/ 1359 ***********************************************************************/
1302 1360
1303static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { 1361long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1304 int fd; 1362 int fd;
1305 u_char mac_addr[25]; 1363 u_char mac_addr[25];
1306 1364
@@ -1319,26 +1377,27 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1319#endif 1377#endif
1320 1378
1321/* resolve host name or die (TODO: move this to netutils.c!) */ 1379/* resolve host name or die (TODO: move this to netutils.c!) */
1322static void resolve_host(const char *in, struct in_addr *out) { 1380void resolve_host(const char *name, struct in_addr *out) {
1323 struct addrinfo hints, *ai; 1381 struct addrinfo hints = {
1382 .ai_family = PF_INET,
1383 };
1384 struct addrinfo *addr_info;
1324 1385
1325 memset(&hints, 0, sizeof(hints)); 1386 if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) {
1326 hints.ai_family = PF_INET;
1327 if (getaddrinfo(in, NULL, &hints, &ai) != 0) {
1328 usage_va(_("Invalid hostname/address - %s"), optarg); 1387 usage_va(_("Invalid hostname/address - %s"), optarg);
1329 } 1388 }
1330 1389
1331 memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); 1390 memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out));
1332 freeaddrinfo(ai); 1391 freeaddrinfo(addr_info);
1333} 1392}
1334 1393
1335/* parse MAC address string, return 6 bytes (unterminated) or NULL */ 1394/* parse MAC address string, return 6 bytes (unterminated) or NULL */
1336static unsigned char *mac_aton(const char *string) { 1395unsigned char *mac_aton(const char *string) {
1337 static unsigned char result[6]; 1396 static unsigned char result[MAC_ADDR_LEN];
1338 char tmp[3]; 1397 char tmp[3];
1339 unsigned i, j; 1398 unsigned byte_counter = 0;
1340 1399
1341 for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { 1400 for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) {
1342 /* ignore ':' and any other non-hex character */ 1401 /* ignore ':' and any other non-hex character */
1343 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) { 1402 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) {
1344 continue; 1403 continue;
@@ -1346,27 +1405,25 @@ static unsigned char *mac_aton(const char *string) {
1346 tmp[0] = string[i]; 1405 tmp[0] = string[i];
1347 tmp[1] = string[i + 1]; 1406 tmp[1] = string[i + 1];
1348 tmp[2] = '\0'; 1407 tmp[2] = '\0';
1349 result[j] = strtol(tmp, (char **)NULL, 16); 1408 result[byte_counter] = strtol(tmp, (char **)NULL, 16);
1350 i++; 1409 i++;
1351 j++; 1410 byte_counter++;
1352 } 1411 }
1353 1412
1354 return (j == 6) ? result : NULL; 1413 return (byte_counter == MAC_ADDR_LEN) ? result : NULL;
1355} 1414}
1356 1415
1357static void print_hardware_address(const unsigned char *address) { 1416void print_hardware_address(const unsigned char *address) {
1358 int i;
1359 1417
1360 printf(_("Hardware address: ")); 1418 printf(_("Hardware address: "));
1361 for (i = 0; i < 5; i++) { 1419 for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) {
1362 printf("%2.2x:", address[i]); 1420 printf("%2.2x:", address[addr_idx]);
1363 } 1421 }
1364 printf("%2.2x", address[i]);
1365 putchar('\n'); 1422 putchar('\n');
1366} 1423}
1367 1424
1368/* print usage help */ 1425/* print usage help */
1369static void print_help(void) { 1426void print_help(void) {
1370 1427
1371 print_revision(progname, NP_VERSION); 1428 print_revision(progname, NP_VERSION);
1372 1429
@@ -1382,6 +1439,7 @@ static void print_help(void) {
1382 printf(UT_HELP_VRSN); 1439 printf(UT_HELP_VRSN);
1383 printf(UT_EXTRA_OPTS); 1440 printf(UT_EXTRA_OPTS);
1384 1441
1442 printf(UT_OUTPUT_FORMAT);
1385 printf(UT_VERBOSE); 1443 printf(UT_VERBOSE);
1386 1444
1387 printf(" %s\n", "-s, --serverip=IPADDRESS"); 1445 printf(" %s\n", "-s, --serverip=IPADDRESS");
@@ -1400,7 +1458,6 @@ static void print_help(void) {
1400 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); 1458 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s"));
1401 1459
1402 printf(UT_SUPPORT); 1460 printf(UT_SUPPORT);
1403 return;
1404} 1461}
1405 1462
1406void print_usage(void) { 1463void print_usage(void) {
@@ -1408,6 +1465,4 @@ void print_usage(void) {
1408 printf("%s\n", _("Usage:")); 1465 printf("%s\n", _("Usage:"));
1409 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); 1466 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname);
1410 printf(" [-i interface] [-m mac]\n"); 1467 printf(" [-i interface] [-m mac]\n");
1411
1412 return;
1413} 1468}
diff --git a/plugins-root/check_dhcp.d/config.h b/plugins-root/check_dhcp.d/config.h
new file mode 100644
index 00000000..f189068b
--- /dev/null
+++ b/plugins-root/check_dhcp.d/config.h
@@ -0,0 +1,50 @@
1#pragma once
2
3#include "../../config.h"
4#include "../lib/states.h"
5#include <stdbool.h>
6#include <netinet/in.h>
7#include "net/if.h"
8#include "output.h"
9
10typedef struct requested_server_struct {
11 struct in_addr server_address;
12 bool answered;
13 struct requested_server_struct *next;
14} requested_server;
15
16typedef struct check_dhcp_config {
17 bool unicast_mode; /* unicast mode: mimic a DHCP relay */
18 bool exclusive_mode; /* exclusive mode aka "rogue DHCP server detection" */
19 int num_of_requested_servers;
20 struct in_addr dhcp_ip; /* server to query (if in unicast mode) */
21 struct in_addr requested_address;
22 bool request_specific_address;
23
24 int dhcpoffer_timeout;
25 unsigned char *user_specified_mac;
26 char network_interface_name[IFNAMSIZ];
27 requested_server *requested_server_list;
28
29 mp_output_format output_format;
30 bool output_format_is_set;
31} check_dhcp_config;
32
33check_dhcp_config check_dhcp_config_init(void) {
34 check_dhcp_config tmp = {
35 .unicast_mode = false,
36 .exclusive_mode = false,
37 .num_of_requested_servers = 0,
38 .dhcp_ip = {0},
39 .requested_address = {0},
40 .request_specific_address = false,
41
42 .dhcpoffer_timeout = 2,
43 .user_specified_mac = NULL,
44 .network_interface_name = "eth0",
45 .requested_server_list = NULL,
46
47 .output_format_is_set = false,
48 };
49 return tmp;
50}