summaryrefslogtreecommitdiffstats
path: root/plugins-root
diff options
context:
space:
mode:
Diffstat (limited to 'plugins-root')
-rw-r--r--plugins-root/Makefile.am7
-rw-r--r--plugins-root/check_dhcp.c1021
-rw-r--r--plugins-root/check_dhcp.d/config.h50
-rw-r--r--plugins-root/check_icmp.c2931
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.c134
-rw-r--r--plugins-root/check_icmp.d/check_icmp_helpers.h68
-rw-r--r--plugins-root/check_icmp.d/config.h115
-rw-r--r--plugins-root/pst3.c441
-rw-r--r--plugins-root/t/check_dhcp.t24
-rw-r--r--plugins-root/t/check_icmp.t58
10 files changed, 2821 insertions, 2028 deletions
diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am
index a80229e2..b6342909 100644
--- a/plugins-root/Makefile.am
+++ b/plugins-root/Makefile.am
@@ -24,7 +24,9 @@ noinst_PROGRAMS = check_dhcp check_icmp @EXTRAS_ROOT@
24 24
25EXTRA_PROGRAMS = pst3 25EXTRA_PROGRAMS = pst3
26 26
27EXTRA_DIST = t pst3.c 27EXTRA_DIST = t pst3.c \
28 check_icmp.d \
29 check_dhcp.d
28 30
29BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a 31BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a
30NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) 32NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS)
@@ -82,6 +84,7 @@ install-exec-local: $(noinst_PROGRAMS)
82# the actual targets 84# the actual targets
83check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) 85check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO)
84check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) 86check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO)
87check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c
85 88
86# -m64 needed at compiler and linker phase 89# -m64 needed at compiler and linker phase
87pst3_CFLAGS = @PST3CFLAGS@ 90pst3_CFLAGS = @PST3CFLAGS@
@@ -89,7 +92,7 @@ pst3_LDFLAGS = @PST3CFLAGS@
89# pst3 must not use monitoringplug/gnulib includes! 92# pst3 must not use monitoringplug/gnulib includes!
90pst3_CPPFLAGS = 93pst3_CPPFLAGS =
91 94
92check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) 95check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS)
93check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) 96check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS)
94 97
95clean-local: 98clean-local:
diff --git a/plugins-root/check_dhcp.c b/plugins-root/check_dhcp.c
index 6802232e..9a96547f 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
@@ -122,17 +127,17 @@ static long mac_addr_dlpi(const char *, int, u_char *);
122#define MAX_DHCP_OPTIONS_LENGTH 312 127#define MAX_DHCP_OPTIONS_LENGTH 312
123 128
124typedef struct dhcp_packet_struct { 129typedef struct dhcp_packet_struct {
125 uint8_t op; /* packet type */ 130 uint8_t op; /* packet type */
126 uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */ 131 uint8_t htype; /* type of hardware address for this machine (Ethernet, etc) */
127 uint8_t hlen; /* length of hardware address (of this machine) */ 132 uint8_t hlen; /* length of hardware address (of this machine) */
128 uint8_t hops; /* hops */ 133 uint8_t hops; /* hops */
129 uint32_t xid; /* random transaction id number - chosen by this machine */ 134 uint32_t xid; /* random transaction id number - chosen by this machine */
130 uint16_t secs; /* seconds used in timing */ 135 uint16_t secs; /* seconds used in timing */
131 uint16_t flags; /* flags */ 136 uint16_t flags; /* flags */
132 struct in_addr ciaddr; /* IP address of this machine (if we already have one) */ 137 struct in_addr ciaddr; /* IP address of this machine (if we already have one) */
133 struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */ 138 struct in_addr yiaddr; /* IP address of this machine (offered by the DHCP server) */
134 struct in_addr siaddr; /* IP address of next server */ 139 struct in_addr siaddr; /* IP address of next server */
135 struct in_addr giaddr; /* IP address of DHCP relay */ 140 struct in_addr giaddr; /* IP address of DHCP relay */
136 unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */ 141 unsigned char chaddr[MAX_DHCP_CHADDR_LENGTH]; /* hardware address of this machine */
137 char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */ 142 char sname[MAX_DHCP_SNAME_LENGTH]; /* name of DHCP server */
138 char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */ 143 char file[MAX_DHCP_FILE_LENGTH]; /* boot file name (used for diskless booting?) */
@@ -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,69 @@ 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*/,
228static int get_ip_address(int, char *); 203 unsigned char *client_hardware_address);
229 204
230static int send_dhcp_discover(int); 205typedef struct get_ip_address_wrapper {
231static int get_dhcp_offer(int); 206 int error;
232 207 struct in_addr my_ip;
233static int get_results(void); 208} get_ip_address_wrapper;
234 209static get_ip_address_wrapper get_ip_address(int /*sock*/, char * /*interface_name*/);
235static int add_dhcp_offer(struct in_addr, dhcp_packet *); 210
236static int free_dhcp_offer_list(void); 211typedef struct send_dhcp_discover_wrapper {
237static int free_requested_server_list(void); 212 int error;
238 213 uint32_t packet_xid;
239static int create_dhcp_socket(void); 214} send_dhcp_discover_wrapper;
240static int close_dhcp_socket(int); 215static send_dhcp_discover_wrapper
241static int send_dhcp_packet(void *, int, int, struct sockaddr_in *); 216send_dhcp_discover(int socket, bool unicast, struct in_addr dhcp_ip,
242static int receive_dhcp_packet(void *, int, int, int, struct sockaddr_in *); 217 struct in_addr requested_address, bool request_specific_address,
218 struct in_addr my_ip, unsigned char *client_hardware_address);
219typedef struct get_dhcp_offer_wrapper {
220 int error;
221 int valid_responses;
222 dhcp_offer *dhcp_offer_list;
223} get_dhcp_offer_wrapper;
224static get_dhcp_offer_wrapper get_dhcp_offer(int /*sock*/, int dhcpoffer_timeout,
225 uint32_t packet_xid, dhcp_offer *dhcp_offer_list,
226 const unsigned char *client_hardware_address);
227
228static mp_subcheck get_results(bool exclusive, int requested_servers,
229 struct in_addr requested_address, bool request_specific_address,
230 requested_server *requested_server_list, int valid_responses,
231 dhcp_offer *dhcp_offer_list);
232
233typedef struct add_dhcp_offer_wrapper {
234 int error;
235 dhcp_offer *dhcp_offer_list;
236} add_dhcp_offer_wrapper;
237static add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr /*source*/,
238 dhcp_packet * /*offer_packet*/,
239 dhcp_offer *dhcp_offer_list);
240static int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list);
241static int free_requested_server_list(requested_server *requested_server_list);
242
243static int create_dhcp_socket(bool /*unicast*/, char *network_interface_name);
244static int close_dhcp_socket(int /*sock*/);
245static int send_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/,
246 struct sockaddr_in * /*dest*/);
247static int receive_dhcp_packet(void * /*buffer*/, int /*buffer_size*/, int /*sock*/,
248 int /*timeout*/, struct sockaddr_in * /*address*/);
243 249
244int main(int argc, char **argv) { 250int main(int argc, char **argv) {
245 int dhcp_socket;
246 int result = STATE_UNKNOWN;
247
248 setlocale(LC_ALL, ""); 251 setlocale(LC_ALL, "");
249 bindtextdomain(PACKAGE, LOCALEDIR); 252 bindtextdomain(PACKAGE, LOCALEDIR);
250 textdomain(PACKAGE); 253 textdomain(PACKAGE);
@@ -252,43 +255,84 @@ int main(int argc, char **argv) {
252 /* Parse extra opts if any */ 255 /* Parse extra opts if any */
253 argv = np_extra_opts(&argc, argv, progname); 256 argv = np_extra_opts(&argc, argv, progname);
254 257
255 if (process_arguments(argc, argv) != OK) { 258 process_arguments_wrapper tmp = process_arguments(argc, argv);
259
260 if (tmp.error != OK) {
256 usage4(_("Could not parse arguments")); 261 usage4(_("Could not parse arguments"));
257 } 262 }
258 263
264 check_dhcp_config config = tmp.config;
265 if (config.output_format_is_set) {
266 mp_set_format(config.output_format);
267 }
268
259 /* create socket for DHCP communications */ 269 /* create socket for DHCP communications */
260 dhcp_socket = create_dhcp_socket(); 270 int dhcp_socket = create_dhcp_socket(config.unicast_mode, config.network_interface_name);
261 271
262 /* get hardware address of client machine */ 272 /* get hardware address of client machine */
263 if (user_specified_mac != NULL) 273 unsigned char client_hardware_address[MAX_DHCP_CHADDR_LENGTH] = "";
264 memcpy(client_hardware_address, user_specified_mac, 6); 274 if (config.user_specified_mac != NULL) {
265 else 275 memcpy(client_hardware_address, config.user_specified_mac, MAC_ADDR_LEN);
266 get_hardware_address(dhcp_socket, network_interface_name); 276 } else {
277 get_hardware_address(dhcp_socket, config.network_interface_name, client_hardware_address);
278 }
267 279
268 if (unicast) /* get IP address of client machine */ 280 struct in_addr my_ip = {0};
269 get_ip_address(dhcp_socket, network_interface_name); 281
282 if (config.unicast_mode) { /* get IP address of client machine */
283 get_ip_address_wrapper tmp_get_ip =
284 get_ip_address(dhcp_socket, config.network_interface_name);
285 if (tmp_get_ip.error == OK) {
286 my_ip = tmp_get_ip.my_ip;
287 } else {
288 // TODO failed to get own IP
289 die(STATE_UNKNOWN, "Failed to retrieve my own IP address in unicast mode");
290 }
291 }
270 292
271 /* send DHCPDISCOVER packet */ 293 /* send DHCPDISCOVER packet */
272 send_dhcp_discover(dhcp_socket); 294 send_dhcp_discover_wrapper disco_res = send_dhcp_discover(
295 dhcp_socket, config.unicast_mode, config.dhcp_ip, config.requested_address,
296 config.request_specific_address, my_ip, client_hardware_address);
297
298 if (disco_res.error != OK) {
299 // DO something?
300 die(STATE_UNKNOWN, "Failed to send DHCP discover");
301 }
273 302
274 /* wait for a DHCPOFFER packet */ 303 /* wait for a DHCPOFFER packet */
275 get_dhcp_offer(dhcp_socket); 304 get_dhcp_offer_wrapper offer_res = get_dhcp_offer(
305 dhcp_socket, config.dhcpoffer_timeout, disco_res.packet_xid, NULL, client_hardware_address);
306
307 int valid_responses = 0;
308 dhcp_offer *dhcp_offer_list = NULL;
309 if (offer_res.error == OK) {
310 valid_responses = offer_res.valid_responses;
311 dhcp_offer_list = offer_res.dhcp_offer_list;
312 } else {
313 die(STATE_UNKNOWN, "Failed to get DHCP offers");
314 }
276 315
277 /* close socket we created */ 316 /* close socket we created */
278 close_dhcp_socket(dhcp_socket); 317 close_dhcp_socket(dhcp_socket);
279 318
280 /* determine state/plugin output to return */ 319 mp_check overall = mp_check_init();
281 result = get_results();
282 320
321 /* determine state/plugin output to return */
322 mp_subcheck sc_res =
323 get_results(config.exclusive_mode, config.num_of_requested_servers,
324 config.requested_address, config.request_specific_address,
325 config.requested_server_list, valid_responses, dhcp_offer_list);
326 mp_add_subcheck_to_check(&overall, sc_res);
283 /* free allocated memory */ 327 /* free allocated memory */
284 free_dhcp_offer_list(); 328 free_dhcp_offer_list(dhcp_offer_list);
285 free_requested_server_list(); 329 free_requested_server_list(config.requested_server_list);
286 330
287 return result; 331 mp_exit(overall);
288} 332}
289 333
290/* determines hardware address on client machine */ 334/* determines hardware address on client machine */
291static int get_hardware_address(int sock, char *interface_name) { 335int get_hardware_address(int sock, char *interface_name, unsigned char *client_hardware_address) {
292 336
293#if defined(__linux__) 337#if defined(__linux__)
294 struct ifreq ifr; 338 struct ifreq ifr;
@@ -302,7 +346,7 @@ static int get_hardware_address(int sock, char *interface_name) {
302 exit(STATE_UNKNOWN); 346 exit(STATE_UNKNOWN);
303 } 347 }
304 348
305 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, 6); 349 memcpy(&client_hardware_address[0], &ifr.ifr_hwaddr.sa_data, MAC_ADDR_LEN);
306 350
307#elif defined(__bsd__) 351#elif defined(__bsd__)
308 /* King 2004 see ACKNOWLEDGEMENTS */ 352 /* King 2004 see ACKNOWLEDGEMENTS */
@@ -326,17 +370,20 @@ static int get_hardware_address(int sock, char *interface_name) {
326 } 370 }
327 371
328 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { 372 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
329 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"), interface_name, strerror(errno)); 373 printf(_("Error: Couldn't get hardware address from %s. sysctl 1 error - %s.\n"),
374 interface_name, strerror(errno));
330 exit(STATE_UNKNOWN); 375 exit(STATE_UNKNOWN);
331 } 376 }
332 377
333 if ((buf = malloc(len)) == NULL) { 378 if ((buf = malloc(len)) == NULL) {
334 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"), interface_name, strerror(errno)); 379 printf(_("Error: Couldn't get hardware address from interface %s. malloc error - %s.\n"),
380 interface_name, strerror(errno));
335 exit(4); 381 exit(4);
336 } 382 }
337 383
338 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 384 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
339 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"), interface_name, strerror(errno)); 385 printf(_("Error: Couldn't get hardware address from %s. sysctl 2 error - %s.\n"),
386 interface_name, strerror(errno));
340 exit(STATE_UNKNOWN); 387 exit(STATE_UNKNOWN);
341 } 388 }
342 389
@@ -358,20 +405,25 @@ static int get_hardware_address(int sock, char *interface_name) {
358 int i; 405 int i;
359 p = interface_name + strlen(interface_name) - 1; 406 p = interface_name + strlen(interface_name) - 1;
360 for (i = strlen(interface_name) - 1; i > 0; p--) { 407 for (i = strlen(interface_name) - 1; i > 0; p--) {
361 if (isalpha(*p)) 408 if (isalpha(*p)) {
362 break; 409 break;
410 }
363 } 411 }
364 p++; 412 p++;
365 if (p != interface_name) { 413 if (p != interface_name) {
366 unit = atoi(p); 414 unit = atoi(p);
367 strncat(dev, interface_name, 6); 415 strncat(dev, interface_name, 6);
368 } else { 416 } else {
369 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg lnc0.\n"), interface_name); 417 printf(_("Error: can't find unit number in interface_name (%s) - expecting TypeNumber eg "
418 "lnc0.\n"),
419 interface_name);
370 exit(STATE_UNKNOWN); 420 exit(STATE_UNKNOWN);
371 } 421 }
372 stat = mac_addr_dlpi(dev, unit, client_hardware_address); 422 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
373 if (stat != 0) { 423 if (stat != 0) {
374 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); 424 printf(
425 _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"),
426 dev, unit);
375 exit(STATE_UNKNOWN); 427 exit(STATE_UNKNOWN);
376 } 428 }
377 429
@@ -383,7 +435,9 @@ static int get_hardware_address(int sock, char *interface_name) {
383 435
384 stat = mac_addr_dlpi(dev, unit, client_hardware_address); 436 stat = mac_addr_dlpi(dev, unit, client_hardware_address);
385 if (stat != 0) { 437 if (stat != 0) {
386 printf(_("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"), dev, unit); 438 printf(
439 _("Error: can't read MAC address from DLPI streams interface for device %s unit %d.\n"),
440 dev, unit);
387 exit(STATE_UNKNOWN); 441 exit(STATE_UNKNOWN);
388 } 442 }
389 /* Kompf 2000-2003 */ 443 /* Kompf 2000-2003 */
@@ -393,14 +447,15 @@ static int get_hardware_address(int sock, char *interface_name) {
393 exit(STATE_UNKNOWN); 447 exit(STATE_UNKNOWN);
394#endif 448#endif
395 449
396 if (verbose) 450 if (verbose) {
397 print_hardware_address(client_hardware_address); 451 print_hardware_address(client_hardware_address);
452 }
398 453
399 return OK; 454 return OK;
400} 455}
401 456
402/* determines IP address of the client interface */ 457/* determines IP address of the client interface */
403static int get_ip_address(int sock, char *interface_name) { 458get_ip_address_wrapper get_ip_address(int sock, char *interface_name) {
404#if defined(SIOCGIFADDR) 459#if defined(SIOCGIFADDR)
405 struct ifreq ifr; 460 struct ifreq ifr;
406 461
@@ -412,28 +467,30 @@ static int get_ip_address(int sock, char *interface_name) {
412 exit(STATE_UNKNOWN); 467 exit(STATE_UNKNOWN);
413 } 468 }
414 469
415 my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
416
417#else 470#else
418 printf(_("Error: Cannot get interface IP address on this platform.\n")); 471 printf(_("Error: Cannot get interface IP address on this platform.\n"));
419 exit(STATE_UNKNOWN); 472 exit(STATE_UNKNOWN);
420#endif 473#endif
421 474
422 if (verbose) 475 get_ip_address_wrapper result = {
423 printf(_("Pretending to be relay client %s\n"), inet_ntoa(my_ip)); 476 .error = OK,
477 .my_ip = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr,
478 };
424 479
425 return OK; 480 if (verbose) {
481 printf(_("Pretending to be relay client %s\n"), inet_ntoa(result.my_ip));
482 }
483
484 return result;
426} 485}
427 486
428/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */ 487/* sends a DHCPDISCOVER broadcast message in an attempt to find DHCP servers */
429static int send_dhcp_discover(int sock) { 488static send_dhcp_discover_wrapper send_dhcp_discover(int sock, bool unicast, struct in_addr dhcp_ip,
430 dhcp_packet discover_packet; 489 struct in_addr requested_address,
431 struct sockaddr_in sockaddr_broadcast; 490 bool request_specific_address,
432 unsigned short opts; 491 struct in_addr my_ip,
433 492 unsigned char *client_hardware_address) {
434 /* clear the packet data structure */ 493 dhcp_packet discover_packet = {0};
435 bzero(&discover_packet, sizeof(discover_packet));
436
437 /* boot request flag (backward compatible with BOOTP servers) */ 494 /* boot request flag (backward compatible with BOOTP servers) */
438 discover_packet.op = BOOTREQUEST; 495 discover_packet.op = BOOTREQUEST;
439 496
@@ -443,12 +500,15 @@ static int send_dhcp_discover(int sock) {
443 /* length of our hardware address */ 500 /* length of our hardware address */
444 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH; 501 discover_packet.hlen = ETHERNET_HARDWARE_ADDRESS_LENGTH;
445 502
503 send_dhcp_discover_wrapper result = {
504 .error = OK,
505 };
446 /* 506 /*
447 * transaction ID is supposed to be random. 507 * transaction ID is supposed to be random.
448 */ 508 */
449 srand(time(NULL) ^ getpid()); 509 srand(time(NULL) ^ getpid());
450 packet_xid = random(); 510 result.packet_xid = random();
451 discover_packet.xid = htonl(packet_xid); 511 discover_packet.xid = htonl(result.packet_xid);
452 512
453 /*discover_packet.secs=htons(65535);*/ 513 /*discover_packet.secs=htons(65535);*/
454 discover_packet.secs = 0xFF; 514 discover_packet.secs = 0xFF;
@@ -468,10 +528,11 @@ static int send_dhcp_discover(int sock) {
468 discover_packet.options[2] = '\x53'; 528 discover_packet.options[2] = '\x53';
469 discover_packet.options[3] = '\x63'; 529 discover_packet.options[3] = '\x63';
470 530
471 opts = 4; 531 unsigned short opts = 4;
472 /* DHCP message type is embedded in options field */ 532 /* DHCP message type is embedded in options field */
473 discover_packet.options[opts++] = DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */ 533 discover_packet.options[opts++] =
474 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */ 534 DHCP_OPTION_MESSAGE_TYPE; /* DHCP message type option identifier */
535 discover_packet.options[opts++] = '\x01'; /* DHCP message option length in bytes */
475 discover_packet.options[opts++] = DHCPDISCOVER; 536 discover_packet.options[opts++] = DHCPDISCOVER;
476 537
477 /* the IP address we're requesting */ 538 /* the IP address we're requesting */
@@ -484,21 +545,25 @@ static int send_dhcp_discover(int sock) {
484 discover_packet.options[opts++] = (char)DHCP_OPTION_END; 545 discover_packet.options[opts++] = (char)DHCP_OPTION_END;
485 546
486 /* unicast fields */ 547 /* unicast fields */
487 if (unicast) 548 if (unicast) {
488 discover_packet.giaddr.s_addr = my_ip.s_addr; 549 discover_packet.giaddr.s_addr = my_ip.s_addr;
550 }
489 551
490 /* see RFC 1542, 4.1.1 */ 552 /* see RFC 1542, 4.1.1 */
491 discover_packet.hops = unicast ? 1 : 0; 553 discover_packet.hops = unicast ? 1 : 0;
492 554
493 /* send the DHCPDISCOVER packet to broadcast address */ 555 /* send the DHCPDISCOVER packet to broadcast address */
494 sockaddr_broadcast.sin_family = AF_INET; 556 struct sockaddr_in sockaddr_broadcast = {
495 sockaddr_broadcast.sin_port = htons(DHCP_SERVER_PORT); 557 .sin_family = AF_INET,
496 sockaddr_broadcast.sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST; 558 .sin_port = htons(DHCP_SERVER_PORT),
497 bzero(&sockaddr_broadcast.sin_zero, sizeof(sockaddr_broadcast.sin_zero)); 559 .sin_addr.s_addr = unicast ? dhcp_ip.s_addr : INADDR_BROADCAST,
560 };
498 561
499 if (verbose) { 562 if (verbose) {
500 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr), ntohs(sockaddr_broadcast.sin_port)); 563 printf(_("DHCPDISCOVER to %s port %d\n"), inet_ntoa(sockaddr_broadcast.sin_addr),
501 printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid), ntohl(discover_packet.xid)); 564 ntohs(sockaddr_broadcast.sin_port));
565 printf("DHCPDISCOVER XID: %u (0x%X)\n", ntohl(discover_packet.xid),
566 ntohl(discover_packet.xid));
502 printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr)); 567 printf("DHCDISCOVER ciaddr: %s\n", inet_ntoa(discover_packet.ciaddr));
503 printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr)); 568 printf("DHCDISCOVER yiaddr: %s\n", inet_ntoa(discover_packet.yiaddr));
504 printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr)); 569 printf("DHCDISCOVER siaddr: %s\n", inet_ntoa(discover_packet.siaddr));
@@ -508,56 +573,58 @@ static int send_dhcp_discover(int sock) {
508 /* send the DHCPDISCOVER packet out */ 573 /* send the DHCPDISCOVER packet out */
509 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast); 574 send_dhcp_packet(&discover_packet, sizeof(discover_packet), sock, &sockaddr_broadcast);
510 575
511 if (verbose) 576 if (verbose) {
512 printf("\n\n"); 577 printf("\n\n");
578 }
513 579
514 return OK; 580 return result;
515} 581}
516 582
517/* waits for a DHCPOFFER message from one or more DHCP servers */ 583/* waits for a DHCPOFFER message from one or more DHCP servers */
518static int get_dhcp_offer(int sock) { 584get_dhcp_offer_wrapper get_dhcp_offer(int sock, int dhcpoffer_timeout, uint32_t packet_xid,
519 dhcp_packet offer_packet; 585 dhcp_offer *dhcp_offer_list,
520 struct sockaddr_in source; 586 const unsigned char *client_hardware_address) {
521 struct sockaddr_in via;
522 int result = OK;
523 int responses = 0;
524 int x;
525 time_t start_time; 587 time_t start_time;
526 time_t current_time;
527
528 time(&start_time); 588 time(&start_time);
529 589
590 int result = OK;
591 int responses = 0;
592 int valid_responses = 0;
530 /* receive as many responses as we can */ 593 /* receive as many responses as we can */
531 for (responses = 0, valid_responses = 0;;) { 594 for (;;) {
532 595 time_t current_time;
533 time(&current_time); 596 time(&current_time);
534 if ((current_time - start_time) >= dhcpoffer_timeout) 597 if ((current_time - start_time) >= dhcpoffer_timeout) {
535 break; 598 break;
599 }
536 600
537 if (verbose) 601 if (verbose) {
538 printf("\n\n"); 602 printf("\n\n");
603 }
539 604
540 bzero(&source, sizeof(source)); 605 struct sockaddr_in source = {0};
541 bzero(&via, sizeof(via)); 606 dhcp_packet offer_packet = {0};
542 bzero(&offer_packet, sizeof(offer_packet));
543 607
544 result = OK; 608 result = OK;
545 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout, &source); 609 result = receive_dhcp_packet(&offer_packet, sizeof(offer_packet), sock, dhcpoffer_timeout,
610 &source);
546 611
547 if (result != OK) { 612 if (result != OK) {
548 if (verbose) 613 if (verbose) {
549 printf(_("Result=ERROR\n")); 614 printf(_("Result=ERROR\n"));
615 }
550 616
551 continue; 617 continue;
552 } else { 618 }
553 if (verbose) 619 if (verbose) {
554 printf(_("Result=OK\n")); 620 printf(_("Result=OK\n"));
555
556 responses++;
557 } 621 }
558 622
623 responses++;
624
559 /* The "source" is either a server or a relay. */ 625 /* The "source" is either a server or a relay. */
560 /* Save a copy of "source" into "via" even if it's via itself */ 626 /* Save a copy of "source" into "via" even if it's via itself */
627 struct sockaddr_in via = {0};
561 memcpy(&via, &source, sizeof(source)); 628 memcpy(&via, &source, sizeof(source));
562 629
563 if (verbose) { 630 if (verbose) {
@@ -568,30 +635,38 @@ static int get_dhcp_offer(int sock) {
568 635
569 /* check packet xid to see if its the same as the one we used in the discover packet */ 636 /* check packet xid to see if its the same as the one we used in the discover packet */
570 if (ntohl(offer_packet.xid) != packet_xid) { 637 if (ntohl(offer_packet.xid) != packet_xid) {
571 if (verbose) 638 if (verbose) {
572 printf(_("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"), ntohl(offer_packet.xid), packet_xid); 639 printf(
640 _("DHCPOFFER XID (%u) did not match DHCPDISCOVER XID (%u) - ignoring packet\n"),
641 ntohl(offer_packet.xid), packet_xid);
642 }
573 643
574 continue; 644 continue;
575 } 645 }
576 646
577 /* check hardware address */ 647 /* check hardware address */
578 result = OK; 648 result = OK;
579 if (verbose) 649 if (verbose) {
580 printf("DHCPOFFER chaddr: "); 650 printf("DHCPOFFER chaddr: ");
651 }
581 652
582 for (x = 0; x < ETHERNET_HARDWARE_ADDRESS_LENGTH; x++) { 653 for (int i = 0; i < ETHERNET_HARDWARE_ADDRESS_LENGTH; i++) {
583 if (verbose) 654 if (verbose) {
584 printf("%02X", (unsigned char)offer_packet.chaddr[x]); 655 printf("%02X", offer_packet.chaddr[i]);
656 }
585 657
586 if (offer_packet.chaddr[x] != client_hardware_address[x]) 658 if (offer_packet.chaddr[i] != client_hardware_address[i]) {
587 result = ERROR; 659 result = ERROR;
660 }
588 } 661 }
589 if (verbose) 662 if (verbose) {
590 printf("\n"); 663 printf("\n");
664 }
591 665
592 if (result == ERROR) { 666 if (result == ERROR) {
593 if (verbose) 667 if (verbose) {
594 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n")); 668 printf(_("DHCPOFFER hardware address did not match our own - ignoring packet\n"));
669 }
595 670
596 continue; 671 continue;
597 } 672 }
@@ -603,7 +678,13 @@ static int get_dhcp_offer(int sock) {
603 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr)); 678 printf("DHCPOFFER giaddr: %s\n", inet_ntoa(offer_packet.giaddr));
604 } 679 }
605 680
606 add_dhcp_offer(source.sin_addr, &offer_packet); 681 add_dhcp_offer_wrapper add_res =
682 add_dhcp_offer(source.sin_addr, &offer_packet, dhcp_offer_list);
683 if (add_res.error != OK) {
684 // TODO
685 } else {
686 dhcp_offer_list = add_res.dhcp_offer_list;
687 }
607 688
608 valid_responses++; 689 valid_responses++;
609 } 690 }
@@ -613,104 +694,104 @@ static int get_dhcp_offer(int sock) {
613 printf(_("Valid responses for this machine: %d\n"), valid_responses); 694 printf(_("Valid responses for this machine: %d\n"), valid_responses);
614 } 695 }
615 696
616 return OK; 697 get_dhcp_offer_wrapper ret_val = {
698 .error = OK,
699 .valid_responses = valid_responses,
700 .dhcp_offer_list = dhcp_offer_list,
701 };
702 return ret_val;
617} 703}
618 704
619/* sends a DHCP packet */ 705/* sends a DHCP packet */
620static int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) { 706int send_dhcp_packet(void *buffer, int buffer_size, int sock, struct sockaddr_in *dest) {
621 int result; 707 int result =
622 708 sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
623 result = sendto(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)dest, sizeof(*dest));
624 709
625 if (verbose) 710 if (verbose) {
626 printf(_("send_dhcp_packet result: %d\n"), result); 711 printf(_("send_dhcp_packet result: %d\n"), result);
712 }
627 713
628 if (result < 0) 714 if (result < 0) {
629 return ERROR; 715 return ERROR;
716 }
630 717
631 return OK; 718 return OK;
632} 719}
633 720
634/* receives a DHCP packet */ 721/* receives a DHCP packet */
635static int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout, struct sockaddr_in *address) { 722int receive_dhcp_packet(void *buffer, int buffer_size, int sock, int timeout,
636 struct timeval tv; 723 struct sockaddr_in *address) {
637 fd_set readfds;
638 fd_set oobfds;
639 int recv_result;
640 socklen_t address_size;
641 struct sockaddr_in source_address;
642 int nfound;
643
644 /* wait for data to arrive (up time timeout) */ 724 /* wait for data to arrive (up time timeout) */
645 tv.tv_sec = timeout; 725 struct timeval timeout_val = {
646 tv.tv_usec = 0; 726 .tv_sec = timeout,
727 .tv_usec = 0,
728 };
729 fd_set readfds;
647 FD_ZERO(&readfds); 730 FD_ZERO(&readfds);
731 fd_set oobfds;
648 FD_ZERO(&oobfds); 732 FD_ZERO(&oobfds);
649 FD_SET(sock, &readfds); 733 FD_SET(sock, &readfds);
650 FD_SET(sock, &oobfds); 734 FD_SET(sock, &oobfds);
651 nfound = select(sock + 1, &readfds, NULL, &oobfds, &tv); 735 int nfound = select(sock + 1, &readfds, NULL, &oobfds, &timeout_val);
652 736
653 /* make sure some data has arrived */ 737 /* make sure some data has arrived */
654 if (!FD_ISSET(sock, &readfds)) { 738 if (!FD_ISSET(sock, &readfds)) {
655 if (verbose) 739 if (verbose) {
656 printf(_("No (more) data received (nfound: %d)\n"), nfound); 740 printf(_("No (more) data received (nfound: %d)\n"), nfound);
741 }
657 return ERROR; 742 return ERROR;
658 } 743 }
659 744
660 else { 745 struct sockaddr_in source_address = {0};
661 bzero(&source_address, sizeof(source_address)); 746 socklen_t address_size = sizeof(source_address);
662 address_size = sizeof(source_address); 747 int recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0,
663 recv_result = recvfrom(sock, (char *)buffer, buffer_size, 0, (struct sockaddr *)&source_address, &address_size); 748 (struct sockaddr *)&source_address, &address_size);
664 if (verbose) 749 if (verbose) {
665 printf("recv_result: %d\n", recv_result); 750 printf("recv_result: %d\n", recv_result);
666 751 }
667 if (recv_result == -1) {
668 if (verbose) {
669 printf(_("recvfrom() failed, "));
670 printf("errno: (%d) -> %s\n", errno, strerror(errno));
671 }
672 return ERROR;
673 } else {
674 if (verbose) {
675 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
676 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
677 }
678 752
679 memcpy(address, &source_address, sizeof(source_address)); 753 if (recv_result == -1) {
680 return OK; 754 if (verbose) {
755 printf(_("recvfrom() failed, "));
756 printf("errno: (%d) -> %s\n", errno, strerror(errno));
681 } 757 }
758 return ERROR;
759 }
760 if (verbose) {
761 printf(_("receive_dhcp_packet() result: %d\n"), recv_result);
762 printf(_("receive_dhcp_packet() source: %s\n"), inet_ntoa(source_address.sin_addr));
682 } 763 }
683 764
765 memcpy(address, &source_address, sizeof(source_address));
684 return OK; 766 return OK;
685} 767}
686 768
687/* creates a socket for DHCP communication */ 769/* creates a socket for DHCP communication */
688static int create_dhcp_socket(void) { 770int create_dhcp_socket(bool unicast, char *network_interface_name) {
689 struct sockaddr_in myname;
690 struct ifreq interface;
691 int sock;
692 int flag = 1;
693
694 /* Set up the address we're going to bind to. */ 771 /* Set up the address we're going to bind to. */
695 bzero(&myname, sizeof(myname));
696 myname.sin_family = AF_INET;
697 /* listen to DHCP server port if we're in unicast mode */ 772 /* listen to DHCP server port if we're in unicast mode */
698 myname.sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT); 773 struct sockaddr_in myname = {
699 myname.sin_addr.s_addr = unicast ? my_ip.s_addr : INADDR_ANY; 774 .sin_family = AF_INET,
700 bzero(&myname.sin_zero, sizeof(myname.sin_zero)); 775 .sin_port = htons(unicast ? DHCP_SERVER_PORT : DHCP_CLIENT_PORT),
776 // TODO previously the next line was trying to use our own IP, we was not set
777 // until some point later, so it was removed. Recheck whether it is actually
778 // necessary/useful
779 .sin_addr.s_addr = INADDR_ANY,
780 };
701 781
702 /* create a socket for DHCP communications */ 782 /* create a socket for DHCP communications */
703 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 783 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
704 if (sock < 0) { 784 if (sock < 0) {
705 printf(_("Error: Could not create socket!\n")); 785 printf(_("Error: Could not create socket!\n"));
706 exit(STATE_UNKNOWN); 786 exit(STATE_UNKNOWN);
707 } 787 }
708 788
709 if (verbose) 789 if (verbose) {
710 printf("DHCP socket: %d\n", sock); 790 printf("DHCP socket: %d\n", sock);
791 }
711 792
712 /* set the reuse address flag so we don't get errors when restarting */ 793 /* set the reuse address flag so we don't get errors when restarting */
713 flag = 1; 794 int flag = 1;
714 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) { 795 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)) < 0) {
715 printf(_("Error: Could not set reuse address option on DHCP socket!\n")); 796 printf(_("Error: Could not set reuse address option on DHCP socket!\n"));
716 exit(STATE_UNKNOWN); 797 exit(STATE_UNKNOWN);
@@ -722,12 +803,14 @@ static int create_dhcp_socket(void) {
722 exit(STATE_UNKNOWN); 803 exit(STATE_UNKNOWN);
723 } 804 }
724 805
806 struct ifreq interface;
725 /* bind socket to interface */ 807 /* bind socket to interface */
726#if defined(__linux__) 808#if defined(__linux__)
727 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1); 809 strncpy(interface.ifr_ifrn.ifrn_name, network_interface_name, IFNAMSIZ - 1);
728 interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; 810 interface.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0';
729 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) { 811 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&interface, sizeof(interface)) < 0) {
730 printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"), network_interface_name); 812 printf(_("Error: Could not bind socket to interface %s. Check your privileges...\n"),
813 network_interface_name);
731 exit(STATE_UNKNOWN); 814 exit(STATE_UNKNOWN);
732 } 815 }
733 816
@@ -738,7 +821,8 @@ static int create_dhcp_socket(void) {
738 821
739 /* bind the socket */ 822 /* bind the socket */
740 if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) { 823 if (bind(sock, (struct sockaddr *)&myname, sizeof(myname)) < 0) {
741 printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"), DHCP_CLIENT_PORT); 824 printf(_("Error: Could not bind to DHCP socket (port %d)! Check your privileges...\n"),
825 DHCP_CLIENT_PORT);
742 exit(STATE_UNKNOWN); 826 exit(STATE_UNKNOWN);
743 } 827 }
744 828
@@ -746,105 +830,121 @@ static int create_dhcp_socket(void) {
746} 830}
747 831
748/* closes DHCP socket */ 832/* closes DHCP socket */
749static int close_dhcp_socket(int sock) { 833int close_dhcp_socket(int sock) {
750
751 close(sock); 834 close(sock);
752
753 return OK; 835 return OK;
754} 836}
755 837
756/* adds a requested server address to list in memory */ 838/* adds a requested server address to list in memory */
757static int add_requested_server(struct in_addr server_address) { 839int add_requested_server(struct in_addr server_address, int *requested_servers,
758 requested_server *new_server; 840 requested_server **requested_server_list) {
759 841 requested_server *new_server = (requested_server *)malloc(sizeof(requested_server));
760 new_server = (requested_server *)malloc(sizeof(requested_server)); 842 if (new_server == NULL) {
761 if (new_server == NULL)
762 return ERROR; 843 return ERROR;
844 }
763 845
764 new_server->server_address = server_address; 846 new_server->server_address = server_address;
765 new_server->answered = false; 847 new_server->answered = false;
766 848
767 new_server->next = requested_server_list; 849 new_server->next = *requested_server_list;
768 requested_server_list = new_server; 850 *requested_server_list = new_server;
769 851
770 requested_servers++; 852 *requested_servers += 1;
771 853
772 if (verbose) 854 if (verbose) {
773 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address)); 855 printf(_("Requested server address: %s\n"), inet_ntoa(new_server->server_address));
856 }
774 857
775 return OK; 858 return OK;
776} 859}
777 860
778/* adds a DHCP OFFER to list in memory */ 861/* adds a DHCP OFFER to list in memory */
779static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) { 862add_dhcp_offer_wrapper add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet,
863 dhcp_offer *dhcp_offer_list) {
864 if (offer_packet == NULL) {
865 add_dhcp_offer_wrapper tmp = {
866 .error = ERROR,
867 };
868 return tmp;
869 }
870
871 uint32_t dhcp_lease_time = 0;
872 uint32_t dhcp_renewal_time = 0;
873 uint32_t dhcp_rebinding_time = 0;
780 dhcp_offer *new_offer; 874 dhcp_offer *new_offer;
781 int x;
782 unsigned option_type;
783 unsigned option_length;
784 struct in_addr serv_ident = {0}; 875 struct in_addr serv_ident = {0};
785
786 if (offer_packet == NULL)
787 return ERROR;
788
789 /* process all DHCP options present in the packet */ 876 /* process all DHCP options present in the packet */
790 for (x = 4; x < MAX_DHCP_OPTIONS_LENGTH - 1;) { 877 for (int dchp_opt_idx = 4; dchp_opt_idx < MAX_DHCP_OPTIONS_LENGTH - 1;) {
791 878
792 if ((int)offer_packet->options[x] == -1) 879 if ((int)offer_packet->options[dchp_opt_idx] == -1) {
793 break; 880 break;
881 }
794 882
795 /* get option type */ 883 /* get option type */
796 option_type = offer_packet->options[x++]; 884 unsigned option_type = offer_packet->options[dchp_opt_idx++];
797 885
798 /* get option length */ 886 /* get option length */
799 option_length = offer_packet->options[x++]; 887 unsigned option_length = offer_packet->options[dchp_opt_idx++];
800 888
801 if (verbose) 889 if (verbose) {
802 printf("Option: %d (0x%02X)\n", option_type, option_length); 890 printf("Option: %d (0x%02X)\n", option_type, option_length);
891 }
803 892
804 /* get option data */ 893 /* get option data */
805 switch (option_type) { 894 switch (option_type) {
806 case DHCP_OPTION_LEASE_TIME: 895 case DHCP_OPTION_LEASE_TIME:
807 memcpy(&dhcp_lease_time, &offer_packet->options[x], sizeof(dhcp_lease_time)); 896 memcpy(&dhcp_lease_time, &offer_packet->options[dchp_opt_idx], sizeof(dhcp_lease_time));
808 dhcp_lease_time = ntohl(dhcp_lease_time); 897 dhcp_lease_time = ntohl(dhcp_lease_time);
809 break; 898 break;
810 case DHCP_OPTION_RENEWAL_TIME: 899 case DHCP_OPTION_RENEWAL_TIME:
811 memcpy(&dhcp_renewal_time, &offer_packet->options[x], sizeof(dhcp_renewal_time)); 900 memcpy(&dhcp_renewal_time, &offer_packet->options[dchp_opt_idx],
901 sizeof(dhcp_renewal_time));
812 dhcp_renewal_time = ntohl(dhcp_renewal_time); 902 dhcp_renewal_time = ntohl(dhcp_renewal_time);
813 break; 903 break;
814 case DHCP_OPTION_REBINDING_TIME: 904 case DHCP_OPTION_REBINDING_TIME:
815 memcpy(&dhcp_rebinding_time, &offer_packet->options[x], sizeof(dhcp_rebinding_time)); 905 memcpy(&dhcp_rebinding_time, &offer_packet->options[dchp_opt_idx],
906 sizeof(dhcp_rebinding_time));
816 dhcp_rebinding_time = ntohl(dhcp_rebinding_time); 907 dhcp_rebinding_time = ntohl(dhcp_rebinding_time);
817 break; 908 break;
818 case DHCP_OPTION_SERVER_IDENTIFIER: 909 case DHCP_OPTION_SERVER_IDENTIFIER:
819 memcpy(&serv_ident.s_addr, &offer_packet->options[x], sizeof(serv_ident.s_addr)); 910 memcpy(&serv_ident.s_addr, &offer_packet->options[dchp_opt_idx],
911 sizeof(serv_ident.s_addr));
820 break; 912 break;
821 } 913 }
822 914
823 /* skip option data we're ignoring */ 915 /* skip option data we're ignoring */
824 if (option_type == 0) /* "pad" option, see RFC 2132 (3.1) */ 916 if (option_type == 0) { /* "pad" option, see RFC 2132 (3.1) */
825 x += 1; 917 dchp_opt_idx += 1;
826 else 918 } else {
827 x += option_length; 919 dchp_opt_idx += option_length;
920 }
828 } 921 }
829 922
830 if (verbose) { 923 if (verbose) {
831 if (dhcp_lease_time == DHCP_INFINITE_TIME) 924 if (dhcp_lease_time == DHCP_INFINITE_TIME) {
832 printf(_("Lease Time: Infinite\n")); 925 printf(_("Lease Time: Infinite\n"));
833 else 926 } else {
834 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time); 927 printf(_("Lease Time: %lu seconds\n"), (unsigned long)dhcp_lease_time);
835 if (dhcp_renewal_time == DHCP_INFINITE_TIME) 928 }
929 if (dhcp_renewal_time == DHCP_INFINITE_TIME) {
836 printf(_("Renewal Time: Infinite\n")); 930 printf(_("Renewal Time: Infinite\n"));
837 else 931 } else {
838 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time); 932 printf(_("Renewal Time: %lu seconds\n"), (unsigned long)dhcp_renewal_time);
839 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) 933 }
934 if (dhcp_rebinding_time == DHCP_INFINITE_TIME) {
840 printf(_("Rebinding Time: Infinite\n")); 935 printf(_("Rebinding Time: Infinite\n"));
936 }
841 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time); 937 printf(_("Rebinding Time: %lu seconds\n"), (unsigned long)dhcp_rebinding_time);
842 } 938 }
843 939
844 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer)); 940 new_offer = (dhcp_offer *)malloc(sizeof(dhcp_offer));
845 941
846 if (new_offer == NULL) 942 if (new_offer == NULL) {
847 return ERROR; 943 add_dhcp_offer_wrapper tmp = {
944 .error = ERROR,
945 };
946 return tmp;
947 }
848 948
849 /* 949 /*
850 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the 950 * RFC 2131 (2.) says: "DHCP clarifies the interpretation of the
@@ -874,15 +974,18 @@ static int add_dhcp_offer(struct in_addr source, dhcp_packet *offer_packet) {
874 new_offer->next = dhcp_offer_list; 974 new_offer->next = dhcp_offer_list;
875 dhcp_offer_list = new_offer; 975 dhcp_offer_list = new_offer;
876 976
877 return OK; 977 add_dhcp_offer_wrapper result = {
978 .error = OK,
979 .dhcp_offer_list = dhcp_offer_list,
980 };
981
982 return result;
878} 983}
879 984
880/* frees memory allocated to DHCP OFFER list */ 985/* frees memory allocated to DHCP OFFER list */
881static int free_dhcp_offer_list(void) { 986int free_dhcp_offer_list(dhcp_offer *dhcp_offer_list) {
882 dhcp_offer *this_offer;
883 dhcp_offer *next_offer; 987 dhcp_offer *next_offer;
884 988 for (dhcp_offer *this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
885 for (this_offer = dhcp_offer_list; this_offer != NULL; this_offer = next_offer) {
886 next_offer = this_offer->next; 989 next_offer = this_offer->next;
887 free(this_offer); 990 free(this_offer);
888 } 991 }
@@ -891,11 +994,10 @@ static int free_dhcp_offer_list(void) {
891} 994}
892 995
893/* frees memory allocated to requested server list */ 996/* frees memory allocated to requested server list */
894static int free_requested_server_list(void) { 997int free_requested_server_list(requested_server *requested_server_list) {
895 requested_server *this_server;
896 requested_server *next_server; 998 requested_server *next_server;
897 999 for (requested_server *this_server = requested_server_list; this_server != NULL;
898 for (this_server = requested_server_list; this_server != NULL; this_server = next_server) { 1000 this_server = next_server) {
899 next_server = this_server->next; 1001 next_server = this_server->next;
900 free(this_server); 1002 free(this_server);
901 } 1003 }
@@ -904,39 +1006,68 @@ static int free_requested_server_list(void) {
904} 1006}
905 1007
906/* gets state and plugin output to return */ 1008/* gets state and plugin output to return */
907static int get_results(void) { 1009mp_subcheck get_results(bool exclusive, const int requested_servers,
908 dhcp_offer *temp_offer, *undesired_offer = NULL; 1010 const struct in_addr requested_address, bool request_specific_address,
909 requested_server *temp_server; 1011 requested_server *requested_server_list, int valid_responses,
910 int result; 1012 dhcp_offer *dhcp_offer_list) {
911 uint32_t max_lease_time = 0; 1013 mp_subcheck sc_dhcp_results = mp_subcheck_init();
1014 sc_dhcp_results = mp_set_subcheck_default_state(sc_dhcp_results, STATE_OK);
912 1015
913 received_requested_address = false; 1016 /* we didn't receive any DHCPOFFERs */
914 1017 if (dhcp_offer_list == NULL) {
915 /* checks responses from requested servers */ 1018 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
916 requested_responses = 0; 1019 xasprintf(&sc_dhcp_results.output, "%s", "No DHCPOFFERs were received");
917 if (requested_servers > 0) { 1020 return sc_dhcp_results;
1021 }
918 1022
919 for (temp_server = requested_server_list; temp_server != NULL; temp_server = temp_server->next) { 1023 if (valid_responses == 0) {
1024 // No valid responses at all, so early exit here
1025 sc_dhcp_results = mp_set_subcheck_state(sc_dhcp_results, STATE_CRITICAL);
1026 xasprintf(&sc_dhcp_results.output, "No valid responses received");
1027 return sc_dhcp_results;
1028 }
920 1029
921 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1030 if (valid_responses == 1) {
1031 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFER", valid_responses);
1032 } else {
1033 xasprintf(&sc_dhcp_results.output, "Received %d DHCPOFFERs", valid_responses);
1034 }
922 1035
1036 bool received_requested_address = false;
1037 dhcp_offer *undesired_offer = NULL;
1038 uint32_t max_lease_time = 0;
1039 /* checks responses from requested servers */
1040 int requested_responses = 0;
1041 if (requested_servers > 0) {
1042 for (requested_server *temp_server = requested_server_list; temp_server != NULL;
1043 temp_server = temp_server->next) {
1044 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1045 temp_offer = temp_offer->next) {
923 /* get max lease time we were offered */ 1046 /* get max lease time we were offered */
924 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1047 if (temp_offer->lease_time > max_lease_time ||
1048 temp_offer->lease_time == DHCP_INFINITE_TIME) {
925 max_lease_time = temp_offer->lease_time; 1049 max_lease_time = temp_offer->lease_time;
1050 }
926 1051
927 /* see if we got the address we requested */ 1052 /* see if we got the address we requested */
928 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1053 if (!memcmp(&requested_address, &temp_offer->offered_address,
1054 sizeof(requested_address))) {
929 received_requested_address = true; 1055 received_requested_address = true;
1056 }
930 1057
931 /* see if the servers we wanted a response from talked to us or not */ 1058 /* see if the servers we wanted a response from, talked to us or not */
932 if (!memcmp(&temp_offer->server_address, &temp_server->server_address, sizeof(temp_server->server_address))) { 1059 if (!memcmp(&temp_offer->server_address, &temp_server->server_address,
1060 sizeof(temp_server->server_address))) {
933 if (verbose) { 1061 if (verbose) {
934 printf(_("DHCP Server Match: Offerer=%s"), inet_ntoa(temp_offer->server_address)); 1062 printf(_("DHCP Server Match: Offerer=%s"),
1063 inet_ntoa(temp_offer->server_address));
935 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address)); 1064 printf(_(" Requested=%s"), inet_ntoa(temp_server->server_address));
936 if (temp_server->answered) 1065 if (temp_server->answered) {
937 printf(_(" (duplicate)")); 1066 printf(_(" (duplicate)"));
1067 }
938 printf(_("\n")); 1068 printf(_("\n"));
939 } 1069 }
1070
940 if (!temp_server->answered) { 1071 if (!temp_server->answered) {
941 requested_responses++; 1072 requested_responses++;
942 temp_server->answered = true; 1073 temp_server->answered = true;
@@ -947,160 +1078,188 @@ static int get_results(void) {
947 } 1078 }
948 1079
949 /* exclusive mode: check for undesired offers */ 1080 /* exclusive mode: check for undesired offers */
950 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1081 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1082 temp_offer = temp_offer->next) {
951 if (!temp_offer->desired) { 1083 if (!temp_offer->desired) {
952 undesired_offer = temp_offer; /* Checks only for the first undesired offer */ 1084 undesired_offer = temp_offer; /* Checks only for the first undesired offer */
953 break; /* no further checks needed */ 1085 break; /* no further checks needed */
954 } 1086 }
955 } 1087 }
956 }
957 1088
958 /* else check and see if we got our requested address from any server */ 1089 mp_subcheck sc_rqust_srvs = mp_subcheck_init();
959 else { 1090 xasprintf(&sc_rqust_srvs.output, "%d of %d requested servers responded",
1091 requested_responses, requested_servers);
960 1092
961 for (temp_offer = dhcp_offer_list; temp_offer != NULL; temp_offer = temp_offer->next) { 1093 if (requested_responses == requested_servers) {
1094 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_OK);
1095 } else if (requested_responses == 0) {
1096 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_CRITICAL);
1097 } else if (requested_responses < requested_servers) {
1098 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1099 } else {
1100 // We received more(!) responses than we asked for?
1101 // This case shouldn't happen, but is here for completion
1102 sc_rqust_srvs = mp_set_subcheck_state(sc_rqust_srvs, STATE_WARNING);
1103 }
1104 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqust_srvs);
962 1105
1106 } else {
1107 /* else check and see if we got our requested address from any server */
1108 for (dhcp_offer *temp_offer = dhcp_offer_list; temp_offer != NULL;
1109 temp_offer = temp_offer->next) {
963 /* get max lease time we were offered */ 1110 /* get max lease time we were offered */
964 if (temp_offer->lease_time > max_lease_time || temp_offer->lease_time == DHCP_INFINITE_TIME) 1111 if (temp_offer->lease_time > max_lease_time ||
1112 temp_offer->lease_time == DHCP_INFINITE_TIME) {
965 max_lease_time = temp_offer->lease_time; 1113 max_lease_time = temp_offer->lease_time;
1114 }
966 1115
967 /* see if we got the address we requested */ 1116 /* see if we got the address we requested */
968 if (!memcmp(&requested_address, &temp_offer->offered_address, sizeof(requested_address))) 1117 if (!memcmp(&requested_address, &temp_offer->offered_address,
1118 sizeof(requested_address))) {
969 received_requested_address = true; 1119 received_requested_address = true;
1120 }
970 } 1121 }
971 } 1122 }
972 1123
973 result = STATE_OK; 1124 if (max_lease_time == DHCP_INFINITE_TIME) {
974 if (valid_responses == 0) 1125 xasprintf(&sc_dhcp_results.output, "%s, max lease time = Infinity", sc_dhcp_results.output);
975 result = STATE_CRITICAL; 1126 } else {
976 else if (requested_servers > 0 && requested_responses == 0) 1127 xasprintf(&sc_dhcp_results.output, "%s, max lease time = %" PRIu32 " seconds",
977 result = STATE_CRITICAL; 1128 sc_dhcp_results.output, max_lease_time);
978 else if (requested_responses < requested_servers)
979 result = STATE_WARNING;
980 else if (request_specific_address && !received_requested_address)
981 result = STATE_WARNING;
982
983 if (exclusive && undesired_offer)
984 result = STATE_CRITICAL;
985
986 if (result == 0) /* garrett honeycutt 2005 */
987 printf("OK: ");
988 else if (result == 1)
989 printf("WARNING: ");
990 else if (result == 2)
991 printf("CRITICAL: ");
992 else if (result == 3)
993 printf("UNKNOWN: ");
994
995 /* we didn't receive any DHCPOFFERs */
996 if (dhcp_offer_list == NULL) {
997 printf(_("No DHCPOFFERs were received.\n"));
998 return result;
999 } 1129 }
1000 1130
1001 printf(_("Received %d DHCPOFFER(s)"), valid_responses); 1131 if (exclusive) {
1132 mp_subcheck sc_rogue_server = mp_subcheck_init();
1002 1133
1003 if (exclusive && undesired_offer) { 1134 if (undesired_offer != NULL) {
1004 printf(_(", Rogue DHCP Server detected! Server %s"), inet_ntoa(undesired_offer->server_address)); 1135 // We wanted to get a DHCPOFFER exclusively from one machine, but another one
1005 printf(_(" offered %s \n"), inet_ntoa(undesired_offer->offered_address)); 1136 // sent one (too)
1006 return result; 1137 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_CRITICAL);
1007 }
1008 1138
1009 if (requested_servers > 0) 1139 // Get the addresses for printout
1010 printf(_(", %s%d of %d requested servers responded"), ((requested_responses < requested_servers) && requested_responses > 0) ? "only " : "", requested_responses, 1140 // 1.address of the sending server
1011 requested_servers); 1141 char server_address[INET_ADDRSTRLEN];
1142 const char *server_address_transformed = inet_ntop(
1143 AF_INET, &undesired_offer->server_address, server_address, sizeof(server_address));
1012 1144
1013 if (request_specific_address) 1145 if (server_address != server_address_transformed) {
1014 printf(_(", requested address (%s) was %soffered"), inet_ntoa(requested_address), (received_requested_address) ? "" : _("not ")); 1146 die(STATE_UNKNOWN, "inet_ntop failed");
1147 }
1015 1148
1016 printf(_(", max lease time = ")); 1149 // 2.address offered
1017 if (max_lease_time == DHCP_INFINITE_TIME) 1150 char offered_address[INET_ADDRSTRLEN];
1018 printf(_("Infinity")); 1151 const char *offered_address_transformed =
1019 else 1152 inet_ntop(AF_INET, &undesired_offer->offered_address, offered_address,
1020 printf("%lu sec", (unsigned long)max_lease_time); 1153 sizeof(offered_address));
1021 1154
1022 printf(".\n"); 1155 if (offered_address != offered_address_transformed) {
1156 die(STATE_UNKNOWN, "inet_ntop failed");
1157 }
1023 1158
1024 return result; 1159 xasprintf(&sc_rogue_server.output, "Rogue DHCP Server detected! Server %s offered %s",
1160 server_address, offered_address);
1161 } else {
1162 sc_rogue_server = mp_set_subcheck_state(sc_rogue_server, STATE_OK);
1163 xasprintf(&sc_rogue_server.output, "No Rogue DHCP Server detected");
1164 }
1165 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rogue_server);
1166 }
1167
1168 if (request_specific_address) {
1169 mp_subcheck sc_rqustd_addr = mp_subcheck_init();
1170
1171 if (received_requested_address) {
1172 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_OK);
1173 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was offered",
1174 inet_ntoa(requested_address));
1175 } else {
1176 sc_rqustd_addr = mp_set_subcheck_state(sc_rqustd_addr, STATE_WARNING);
1177 xasprintf(&sc_rqustd_addr.output, "Requested address (%s) was NOT offered",
1178 inet_ntoa(requested_address));
1179 }
1180
1181 mp_add_subcheck_to_subcheck(&sc_dhcp_results, sc_rqustd_addr);
1182 }
1183
1184 return sc_dhcp_results;
1025} 1185}
1026 1186
1027/* process command-line arguments */ 1187/* process command-line arguments */
1028static int process_arguments(int argc, char **argv) { 1188process_arguments_wrapper process_arguments(int argc, char **argv) {
1029 if (argc < 1) 1189 if (argc < 1) {
1030 return ERROR; 1190 process_arguments_wrapper tmp = {
1191 .error = ERROR,
1192 };
1193 return tmp;
1194 }
1031 1195
1032 call_getopt(argc, argv); 1196 enum {
1033 return validate_arguments(argc); 1197 output_format_index = CHAR_MAX + 1,
1034} 1198 };
1035 1199
1036static int call_getopt(int argc, char **argv) {
1037 extern int optind;
1038 int option_index = 0; 1200 int option_index = 0;
1039 static struct option long_options[] = {{"serverip", required_argument, 0, 's'}, 1201 static struct option long_options[] = {
1040 {"requestedip", required_argument, 0, 'r'}, 1202 {"serverip", required_argument, 0, 's'},
1041 {"timeout", required_argument, 0, 't'}, 1203 {"requestedip", required_argument, 0, 'r'},
1042 {"interface", required_argument, 0, 'i'}, 1204 {"timeout", required_argument, 0, 't'},
1043 {"mac", required_argument, 0, 'm'}, 1205 {"interface", required_argument, 0, 'i'},
1044 {"unicast", no_argument, 0, 'u'}, 1206 {"mac", required_argument, 0, 'm'},
1045 {"exclusive", no_argument, 0, 'x'}, 1207 {"unicast", no_argument, 0, 'u'},
1046 {"verbose", no_argument, 0, 'v'}, 1208 {"exclusive", no_argument, 0, 'x'},
1047 {"version", no_argument, 0, 'V'}, 1209 {"verbose", no_argument, 0, 'v'},
1048 {"help", no_argument, 0, 'h'}, 1210 {"version", no_argument, 0, 'V'},
1049 {0, 0, 0, 0}}; 1211 {"help", no_argument, 0, 'h'},
1050 1212 {"output-format", required_argument, 0, output_format_index},
1051 int c = 0; 1213 {0, 0, 0, 0}};
1214
1215 check_dhcp_config config = check_dhcp_config_init();
1216 int option_char = 0;
1052 while (true) { 1217 while (true) {
1053 c = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index); 1218 option_char = getopt_long(argc, argv, "+hVvxt:s:r:t:i:m:u", long_options, &option_index);
1054 1219
1055 if (c == -1 || c == EOF || c == 1) 1220 if (option_char == -1 || option_char == EOF || option_char == 1) {
1056 break; 1221 break;
1222 }
1057 1223
1058 switch (c) { 1224 switch (option_char) {
1059
1060 case 's': /* DHCP server address */ 1225 case 's': /* DHCP server address */
1061 resolve_host(optarg, &dhcp_ip); 1226 resolve_host(optarg, &config.dhcp_ip);
1062 add_requested_server(dhcp_ip); 1227 add_requested_server(config.dhcp_ip, &config.num_of_requested_servers,
1228 &config.requested_server_list);
1063 break; 1229 break;
1064 1230
1065 case 'r': /* address we are requested from DHCP servers */ 1231 case 'r': /* address we are requested from DHCP servers */
1066 resolve_host(optarg, &requested_address); 1232 resolve_host(optarg, &config.requested_address);
1067 request_specific_address = true; 1233 config.request_specific_address = true;
1068 break; 1234 break;
1069 1235
1070 case 't': /* timeout */ 1236 case 't': /* timeout */
1071 1237 if (atoi(optarg) > 0) {
1072 /* 1238 config.dhcpoffer_timeout = atoi(optarg);
1073 if(is_intnonneg(optarg)) 1239 }
1074 */
1075 if (atoi(optarg) > 0)
1076 dhcpoffer_timeout = atoi(optarg);
1077 /*
1078 else
1079 usage("Time interval must be a nonnegative integer\n");
1080 */
1081 break; 1240 break;
1082 1241
1083 case 'm': /* MAC address */ 1242 case 'm': /* MAC address */
1084 1243 if ((config.user_specified_mac = mac_aton(optarg)) == NULL) {
1085 if ((user_specified_mac = mac_aton(optarg)) == NULL)
1086 usage("Cannot parse MAC address.\n"); 1244 usage("Cannot parse MAC address.\n");
1087 if (verbose) 1245 }
1088 print_hardware_address(user_specified_mac); 1246 if (verbose) {
1089 1247 print_hardware_address(config.user_specified_mac);
1248 }
1090 break; 1249 break;
1091 1250
1092 case 'i': /* interface name */ 1251 case 'i': /* interface name */
1093 1252 strncpy(config.network_interface_name, optarg,
1094 strncpy(network_interface_name, optarg, sizeof(network_interface_name) - 1); 1253 sizeof(config.network_interface_name) - 1);
1095 network_interface_name[sizeof(network_interface_name) - 1] = '\x0'; 1254 config.network_interface_name[sizeof(config.network_interface_name) - 1] = '\x0';
1096
1097 break; 1255 break;
1098 1256
1099 case 'u': /* unicast testing */ 1257 case 'u': /* unicast testing */
1100 unicast = true; 1258 config.unicast_mode = true;
1101 break; 1259 break;
1260
1102 case 'x': /* exclusive testing aka "rogue DHCP server detection" */ 1261 case 'x': /* exclusive testing aka "rogue DHCP server detection" */
1103 exclusive = true; 1262 config.exclusive_mode = true;
1104 break; 1263 break;
1105 1264
1106 case 'V': /* version */ 1265 case 'V': /* version */
@@ -1114,6 +1273,18 @@ static int call_getopt(int argc, char **argv) {
1114 case 'v': /* verbose */ 1273 case 'v': /* verbose */
1115 verbose = 1; 1274 verbose = 1;
1116 break; 1275 break;
1276 case output_format_index: {
1277 parsed_output_format parser = mp_parse_output_format(optarg);
1278 if (!parser.parsing_success) {
1279 // TODO List all available formats here, maybe add anothoer usage function
1280 printf("Invalid output format: %s\n", optarg);
1281 exit(STATE_UNKNOWN);
1282 }
1283
1284 config.output_format_is_set = true;
1285 config.output_format = parser.output_format;
1286 break;
1287 }
1117 case '?': /* help */ 1288 case '?': /* help */
1118 usage5(); 1289 usage5();
1119 break; 1290 break;
@@ -1122,15 +1293,16 @@ static int call_getopt(int argc, char **argv) {
1122 break; 1293 break;
1123 } 1294 }
1124 } 1295 }
1125 return optind;
1126}
1127 1296
1128static int validate_arguments(int argc) { 1297 if (argc - optind > 0) {
1129
1130 if (argc - optind > 0)
1131 usage(_("Got unexpected non-option argument")); 1298 usage(_("Got unexpected non-option argument"));
1299 }
1132 1300
1133 return OK; 1301 process_arguments_wrapper result = {
1302 .config = config,
1303 .error = OK,
1304 };
1305 return result;
1134} 1306}
1135 1307
1136#if defined(__sun__) || defined(__solaris__) || defined(__hpux__) 1308#if defined(__sun__) || defined(__solaris__) || defined(__hpux__)
@@ -1180,7 +1352,8 @@ static int put_ctrl(int fd, int len, int pri) {
1180 1352
1181 ctl.len = len; 1353 ctl.len = len;
1182 if (putmsg(fd, &ctl, 0, pri) < 0) { 1354 if (putmsg(fd, &ctl, 0, pri) < 0) {
1183 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"), strerror(errno)); 1355 printf(_("Error: DLPI stream API failed to get MAC in put_ctrl/putmsg(): %s.\n"),
1356 strerror(errno));
1184 exit(STATE_UNKNOWN); 1357 exit(STATE_UNKNOWN);
1185 } 1358 }
1186 1359
@@ -1193,7 +1366,8 @@ static int put_both(int fd, int clen, int dlen, int pri) {
1193 ctl.len = clen; 1366 ctl.len = clen;
1194 dat.len = dlen; 1367 dat.len = dlen;
1195 if (putmsg(fd, &ctl, &dat, pri) < 0) { 1368 if (putmsg(fd, &ctl, &dat, pri) < 0) {
1196 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"), strerror(errno)); 1369 printf(_("Error: DLPI stream API failed to get MAC in put_both/putmsg().\n"),
1370 strerror(errno));
1197 exit(STATE_UNKNOWN); 1371 exit(STATE_UNKNOWN);
1198 } 1372 }
1199 1373
@@ -1205,7 +1379,8 @@ static int dl_open(const char *dev, int unit, int *fd) {
1205 dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area; 1379 dl_attach_req_t *attach_req = (dl_attach_req_t *)ctl_area;
1206 1380
1207 if ((*fd = open(dev, O_RDWR)) == -1) { 1381 if ((*fd = open(dev, O_RDWR)) == -1) {
1208 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"), dev, strerror(errno)); 1382 printf(_("Error: DLPI stream API failed to get MAC in dl_attach_req/open(%s..): %s.\n"),
1383 dev, strerror(errno));
1209 exit(STATE_UNKNOWN); 1384 exit(STATE_UNKNOWN);
1210 } 1385 }
1211 attach_req->dl_primitive = DL_ATTACH_REQ; 1386 attach_req->dl_primitive = DL_ATTACH_REQ;
@@ -1229,7 +1404,8 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1229 put_ctrl(fd, sizeof(dl_bind_req_t), 0); 1404 put_ctrl(fd, sizeof(dl_bind_req_t), 0);
1230 get_msg(fd); 1405 get_msg(fd);
1231 if (GOT_ERR == check_ctrl(DL_BIND_ACK)) { 1406 if (GOT_ERR == check_ctrl(DL_BIND_ACK)) {
1232 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"), strerror(errno)); 1407 printf(_("Error: DLPI stream API failed to get MAC in dl_bind/check_ctrl(): %s.\n"),
1408 strerror(errno));
1233 exit(STATE_UNKNOWN); 1409 exit(STATE_UNKNOWN);
1234 } 1410 }
1235 bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length); 1411 bcopy((u_char *)bind_ack + bind_ack->dl_addr_offset, addr, bind_ack->dl_addr_length);
@@ -1249,7 +1425,7 @@ static int dl_bind(int fd, int sap, u_char *addr) {
1249 * 1425 *
1250 ***********************************************************************/ 1426 ***********************************************************************/
1251 1427
1252static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) { 1428long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1253 int fd; 1429 int fd;
1254 u_char mac_addr[25]; 1430 u_char mac_addr[25];
1255 1431
@@ -1268,51 +1444,53 @@ static long mac_addr_dlpi(const char *dev, int unit, u_char *addr) {
1268#endif 1444#endif
1269 1445
1270/* resolve host name or die (TODO: move this to netutils.c!) */ 1446/* resolve host name or die (TODO: move this to netutils.c!) */
1271static void resolve_host(const char *in, struct in_addr *out) { 1447void resolve_host(const char *name, struct in_addr *out) {
1272 struct addrinfo hints, *ai; 1448 struct addrinfo hints = {
1449 .ai_family = PF_INET,
1450 };
1451 struct addrinfo *addr_info;
1273 1452
1274 memset(&hints, 0, sizeof(hints)); 1453 if (getaddrinfo(name, NULL, &hints, &addr_info) != 0) {
1275 hints.ai_family = PF_INET;
1276 if (getaddrinfo(in, NULL, &hints, &ai) != 0)
1277 usage_va(_("Invalid hostname/address - %s"), optarg); 1454 usage_va(_("Invalid hostname/address - %s"), optarg);
1455 }
1278 1456
1279 memcpy(out, &((struct sockaddr_in *)ai->ai_addr)->sin_addr, sizeof(*out)); 1457 memcpy(out, &((struct sockaddr_in *)addr_info->ai_addr)->sin_addr, sizeof(*out));
1280 freeaddrinfo(ai); 1458 freeaddrinfo(addr_info);
1281} 1459}
1282 1460
1283/* parse MAC address string, return 6 bytes (unterminated) or NULL */ 1461/* parse MAC address string, return 6 bytes (unterminated) or NULL */
1284static unsigned char *mac_aton(const char *string) { 1462unsigned char *mac_aton(const char *string) {
1285 static unsigned char result[6]; 1463 static unsigned char result[MAC_ADDR_LEN];
1286 char tmp[3]; 1464 char tmp[3];
1287 unsigned i, j; 1465 unsigned byte_counter = 0;
1288 1466
1289 for (i = 0, j = 0; string[i] != '\0' && j < sizeof(result); i++) { 1467 for (int i = 0; string[i] != '\0' && byte_counter < sizeof(result); i++) {
1290 /* ignore ':' and any other non-hex character */ 1468 /* ignore ':' and any other non-hex character */
1291 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) 1469 if (!isxdigit(string[i]) || !isxdigit(string[i + 1])) {
1292 continue; 1470 continue;
1471 }
1293 tmp[0] = string[i]; 1472 tmp[0] = string[i];
1294 tmp[1] = string[i + 1]; 1473 tmp[1] = string[i + 1];
1295 tmp[2] = '\0'; 1474 tmp[2] = '\0';
1296 result[j] = strtol(tmp, (char **)NULL, 16); 1475 result[byte_counter] = strtol(tmp, (char **)NULL, 16);
1297 i++; 1476 i++;
1298 j++; 1477 byte_counter++;
1299 } 1478 }
1300 1479
1301 return (j == 6) ? result : NULL; 1480 return (byte_counter == MAC_ADDR_LEN) ? result : NULL;
1302} 1481}
1303 1482
1304static void print_hardware_address(const unsigned char *address) { 1483void print_hardware_address(const unsigned char *address) {
1305 int i;
1306 1484
1307 printf(_("Hardware address: ")); 1485 printf(_("Hardware address: "));
1308 for (i = 0; i < 5; i++) 1486 for (int addr_idx = 0; addr_idx < MAC_ADDR_LEN; addr_idx++) {
1309 printf("%2.2x:", address[i]); 1487 printf("%2.2x:", address[addr_idx]);
1310 printf("%2.2x", address[i]); 1488 }
1311 putchar('\n'); 1489 putchar('\n');
1312} 1490}
1313 1491
1314/* print usage help */ 1492/* print usage help */
1315static void print_help(void) { 1493void print_help(void) {
1316 1494
1317 print_revision(progname, NP_VERSION); 1495 print_revision(progname, NP_VERSION);
1318 1496
@@ -1328,6 +1506,7 @@ static void print_help(void) {
1328 printf(UT_HELP_VRSN); 1506 printf(UT_HELP_VRSN);
1329 printf(UT_EXTRA_OPTS); 1507 printf(UT_EXTRA_OPTS);
1330 1508
1509 printf(UT_OUTPUT_FORMAT);
1331 printf(UT_VERBOSE); 1510 printf(UT_VERBOSE);
1332 1511
1333 printf(" %s\n", "-s, --serverip=IPADDRESS"); 1512 printf(" %s\n", "-s, --serverip=IPADDRESS");
@@ -1343,10 +1522,10 @@ static void print_help(void) {
1343 printf(" %s\n", "-u, --unicast"); 1522 printf(" %s\n", "-u, --unicast");
1344 printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s")); 1523 printf(" %s\n", _("Unicast testing: mimic a DHCP relay, requires -s"));
1345 printf(" %s\n", "-x, --exclusive"); 1524 printf(" %s\n", "-x, --exclusive");
1346 printf(" %s\n", _("Only requested DHCP server may response (rogue DHCP server detection), requires -s")); 1525 printf(" %s\n",
1526 _("Only requested DHCP server may response (rogue DHCP server detection), requires -s"));
1347 1527
1348 printf(UT_SUPPORT); 1528 printf(UT_SUPPORT);
1349 return;
1350} 1529}
1351 1530
1352void print_usage(void) { 1531void print_usage(void) {
@@ -1354,6 +1533,4 @@ void print_usage(void) {
1354 printf("%s\n", _("Usage:")); 1533 printf("%s\n", _("Usage:"));
1355 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname); 1534 printf(" %s [-v] [-u] [-x] [-s serverip] [-r requestedip] [-t timeout]\n", progname);
1356 printf(" [-i interface] [-m mac]\n"); 1535 printf(" [-i interface] [-m mac]\n");
1357
1358 return;
1359} 1536}
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}
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index dcaceddb..35cae3ed 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -46,12 +46,17 @@ const char *email = "devel@monitoring-plugins.org";
46#include "../plugins/common.h" 46#include "../plugins/common.h"
47#include "netutils.h" 47#include "netutils.h"
48#include "utils.h" 48#include "utils.h"
49#include "output.h"
50#include "perfdata.h"
49 51
50#if HAVE_SYS_SOCKIO_H 52#if HAVE_SYS_SOCKIO_H
51# include <sys/sockio.h> 53# include <sys/sockio.h>
52#endif 54#endif
53 55
54#include <sys/time.h> 56#include <sys/time.h>
57#if defined(SIOCGIFADDR)
58#include <sys/ioctl.h>
59#endif /* SIOCGIFADDR */
55#include <errno.h> 60#include <errno.h>
56#include <signal.h> 61#include <signal.h>
57#include <ctype.h> 62#include <ctype.h>
@@ -65,6 +70,17 @@ const char *email = "devel@monitoring-plugins.org";
65#include <netinet/icmp6.h> 70#include <netinet/icmp6.h>
66#include <arpa/inet.h> 71#include <arpa/inet.h>
67#include <math.h> 72#include <math.h>
73#include <netdb.h>
74#include <sys/types.h>
75#include <unistd.h>
76#include <stdint.h>
77#include <sys/socket.h>
78#include <assert.h>
79#include <sys/select.h>
80
81#include "../lib/states.h"
82#include "./check_icmp.d/config.h"
83#include "./check_icmp.d/check_icmp_helpers.h"
68 84
69/** sometimes undefined system macros (quite a few, actually) **/ 85/** sometimes undefined system macros (quite a few, actually) **/
70#ifndef MAXTTL 86#ifndef MAXTTL
@@ -96,56 +112,8 @@ const char *email = "devel@monitoring-plugins.org";
96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 112# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97#endif 113#endif
98 114
99typedef unsigned short range_t; /* type for get_range() -- unimplemented */
100
101typedef struct rta_host {
102 unsigned short id; /* id in **table, and icmp pkts */
103 char *name; /* arg used for adding this host */
104 char *msg; /* icmp error message, if any */
105 struct sockaddr_storage saddr_in; /* the address of this host */
106 struct sockaddr_storage error_addr; /* stores address of error replies */
107 unsigned long long time_waited; /* total time waited, in usecs */
108 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
109 unsigned char icmp_type, icmp_code; /* type and code from errors */
110 unsigned short flags; /* control/status flags */
111 double rta; /* measured RTA */
112 int rta_status; // check result for RTA checks
113 double rtmax; /* max rtt */
114 double rtmin; /* min rtt */
115 double jitter; /* measured jitter */
116 int jitter_status; // check result for Jitter checks
117 double jitter_max; /* jitter rtt maximum */
118 double jitter_min; /* jitter rtt minimum */
119 double EffectiveLatency;
120 double mos; /* Mean opnion score */
121 int mos_status; // check result for MOS checks
122 double score; /* score */
123 int score_status; // check result for score checks
124 u_int last_tdiff;
125 u_int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
126 unsigned char pl; /* measured packet loss */
127 int pl_status; // check result for packet loss checks
128 struct rta_host *next; /* linked list */
129 int order_status; // check result for packet order checks
130} rta_host;
131
132#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */ 115#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 116
134/* threshold structure. all values are maximum allowed, exclusive */
135typedef struct threshold {
136 unsigned char pl; /* max allowed packet loss in percent */
137 unsigned int rta; /* roundtrip time average, microseconds */
138 double jitter; /* jitter time average, microseconds */
139 double mos; /* MOS */
140 double score; /* Score */
141} threshold;
142
143/* the data structure */
144typedef struct icmp_ping_data {
145 struct timeval stime; /* timestamp (saved in protocol struct as well) */
146 unsigned short ping_id;
147} icmp_ping_data;
148
149typedef union ip_hdr { 117typedef union ip_hdr {
150 struct ip ip; 118 struct ip ip;
151 struct ip6_hdr ip6; 119 struct ip6_hdr ip6;
@@ -158,24 +126,6 @@ typedef union icmp_packet {
158 u_short *cksum_in; 126 u_short *cksum_in;
159} icmp_packet; 127} icmp_packet;
160 128
161/* the different modes of this program are as follows:
162 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
163 * MODE_HOSTCHECK: Return immediately upon any sign of life
164 * In addition, sends packets to ALL addresses assigned
165 * to this host (as returned by gethostbyname() or
166 * gethostbyaddr() and expects one host only to be checked at
167 * a time. Therefore, any packet response what so ever will
168 * count as a sign of life, even when received outside
169 * crit.rta limit. Do not misspell any additional IP's.
170 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
171 * MODE_ICMP: implement something similar to check_icmp (MODE_RTA without
172 * tcp and udp args does this)
173 */
174#define MODE_RTA 0
175#define MODE_HOSTCHECK 1
176#define MODE_ALL 2
177#define MODE_ICMP 3
178
179enum enum_threshold_mode { 129enum enum_threshold_mode {
180 const_rta_mode, 130 const_rta_mode,
181 const_packet_loss_mode, 131 const_packet_loss_mode,
@@ -186,89 +136,487 @@ enum enum_threshold_mode {
186 136
187typedef enum enum_threshold_mode threshold_mode; 137typedef enum enum_threshold_mode threshold_mode;
188 138
189/* the different ping types we can do
190 * TODO: investigate ARP ping as well */
191#define HAVE_ICMP 1
192#define HAVE_UDP 2
193#define HAVE_TCP 4
194#define HAVE_ARP 8
195
196#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
197#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
198#define IP_HDR_SIZE 20
199#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
200#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
201
202/* various target states */
203#define TSTATE_INACTIVE 0x01 /* don't ping this host anymore */
204#define TSTATE_WAITING 0x02 /* unanswered packets on the wire */
205#define TSTATE_ALIVE 0x04 /* target is alive (has answered something) */
206#define TSTATE_UNREACH 0x08
207
208/** prototypes **/ 139/** prototypes **/
209void print_help(void); 140void print_help();
210void print_usage(void); 141void print_usage(void);
211static u_int get_timevar(const char *); 142
212static u_int get_timevaldiff(struct timeval *, struct timeval *); 143/* Time related */
213static in_addr_t get_ip_address(const char *); 144typedef struct {
214static int wait_for_reply(int, u_int); 145 int error_code;
215static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); 146 time_t time_range;
216static int send_icmp_ping(int, struct rta_host *); 147} get_timevar_wrapper;
217static int get_threshold(char *str, threshold *th); 148static get_timevar_wrapper get_timevar(const char *str);
218static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); 149static time_t get_timevaldiff(struct timeval earlier, struct timeval later);
219static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode); 150static time_t get_timevaldiff_to_now(struct timeval earlier);
220static void run_checks(void); 151
221static void set_source_ip(char *); 152static in_addr_t get_ip_address(const char *ifname, const int icmp_sock);
222static int add_target(char *); 153static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family);
223static int add_target_ip(char *, struct sockaddr_storage *); 154
224static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); 155/* Receiving data */
225static void parse_address(struct sockaddr_storage *, char *, int); 156static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval,
226static unsigned short icmp_checksum(uint16_t *, size_t); 157 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
227static void finish(int); 158 ping_target **table, unsigned short packets,
228static void crash(const char *, ...); 159 unsigned short number_of_targets, check_icmp_state *program_state);
229 160
230/** external **/ 161typedef struct {
231extern int optind; 162 sa_family_t recv_proto;
232extern char *optarg; 163 ssize_t received;
233extern char **environ; 164} recvfrom_wto_wrapper;
165static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len,
166 struct sockaddr *saddr, time_t *timeout,
167 struct timeval *received_timestamp);
168static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
169 time_t *target_interval, uint16_t sender_id, ping_target **table,
170 unsigned short packets, unsigned short number_of_targets,
171 check_icmp_state *program_state);
172
173/* Sending data */
174static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
175 unsigned short icmp_pkt_size, uint16_t sender_id,
176 check_icmp_state *program_state);
177
178/* Threshold related */
179typedef struct {
180 int errorcode;
181 check_icmp_threshold threshold;
182} get_threshold_wrapper;
183static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold);
184
185typedef struct {
186 int errorcode;
187 check_icmp_threshold warn;
188 check_icmp_threshold crit;
189} get_threshold2_wrapper;
190static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
191 check_icmp_threshold crit, threshold_mode mode);
192
193typedef struct {
194 int errorcode;
195 check_icmp_threshold result;
196} parse_threshold2_helper_wrapper;
197static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
198 size_t length,
199 check_icmp_threshold thr,
200 threshold_mode mode);
201
202/* main test function */
203static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
204 check_icmp_execution_mode mode, time_t max_completion_time,
205 struct timeval prog_start, ping_target **table, unsigned short packets,
206 check_icmp_socket_set sockset, unsigned short number_of_targets,
207 check_icmp_state *program_state);
208mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
209 check_icmp_threshold warn, check_icmp_threshold crit);
210
211typedef struct {
212 int targets_ok;
213 int targets_warn;
214 mp_subcheck sc_host;
215} evaluate_host_wrapper;
216evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
217 check_icmp_mode_switches modes, check_icmp_threshold warn,
218 check_icmp_threshold crit);
219
220/* Target acquisition */
221typedef struct {
222 int error_code;
223 check_icmp_target_container host;
224 bool has_v4;
225 bool has_v6;
226} add_host_wrapper;
227static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
228 sa_family_t enforced_proto);
229
230typedef struct {
231 int error_code;
232 ping_target *targets;
233 unsigned int number_of_targets;
234 bool has_v4;
235 bool has_v6;
236} add_target_wrapper;
237static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode,
238 sa_family_t enforced_proto);
239
240typedef struct {
241 int error_code;
242 ping_target *target;
243} add_target_ip_wrapper;
244static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address);
245
246static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size);
247
248static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size);
249
250/* End of run function */
251static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive,
252 check_icmp_threshold warn, check_icmp_threshold crit,
253 unsigned short number_of_targets, check_icmp_state *program_state,
254 check_icmp_target_container host_list[], unsigned short number_of_hosts,
255 mp_check overall[static 1]);
256
257/* Error exit */
258static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
234 259
235/** global variables **/ 260/** global variables **/
236static struct rta_host **table, *cursor, *list; 261static int debug = 0;
237 262
238static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 263extern unsigned int timeout;
239static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 264
240 265/** the working code **/
241static int mode, protocols, sockets, debug = 0, timeout = 10; 266static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) {
242static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 267 return targets - targets_down;
243static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 268}
244 269static inline unsigned int icmp_pkts_en_route(unsigned int icmp_sent, unsigned int icmp_recv,
245static unsigned int icmp_sent = 0, icmp_recv = 0, icmp_lost = 0, ttl = 0; 270 unsigned int icmp_lost) {
246#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 271 return icmp_sent - (icmp_recv + icmp_lost);
247static unsigned short targets_down = 0, targets = 0, packets = 0; 272}
248#define targets_alive (targets - targets_down) 273
249static unsigned int retry_interval, pkt_interval, target_interval; 274// Create configuration from cli parameters
250static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; 275typedef struct {
251static pid_t pid; 276 int errorcode;
252static struct timezone tz; 277 check_icmp_config config;
253static struct timeval prog_start; 278} check_icmp_config_wrapper;
254static unsigned long long max_completion_time = 0; 279check_icmp_config_wrapper process_arguments(int argc, char **argv) {
255static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 280 /* get calling name the old-fashioned way for portability instead
256static int min_hosts_alive = -1; 281 * of relying on the glibc-ism __progname */
257static float pkt_backoff_factor = 1.5; 282 char *ptr = strrchr(argv[0], '/');
258static float target_backoff_factor = 1.5; 283 if (ptr) {
259static bool rta_mode = false; 284 progname = &ptr[1];
260static bool pl_mode = false; 285 } else {
261static bool jitter_mode = false; 286 progname = argv[0];
262static bool score_mode = false; 287 }
263static bool mos_mode = false; 288
264static bool order_mode = false; 289 check_icmp_config_wrapper result = {
290 .errorcode = OK,
291 .config = check_icmp_config_init(),
292 };
293
294 /* use the pid to mark packets as ours */
295 /* Some systems have 32-bit pid_t so mask off only 16 bits */
296 result.config.sender_id = getpid() & 0xffff;
297
298 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
299 result.config.mode = MODE_ICMP;
300 } else if (!strcmp(progname, "check_host")) {
301 result.config.mode = MODE_HOSTCHECK;
302 result.config.number_of_packets = 5;
303 result.config.crit.rta = result.config.warn.rta = 1000000;
304 result.config.crit.pl = result.config.warn.pl = 100;
305 } else if (!strcmp(progname, "check_rta_multi")) {
306 result.config.mode = MODE_ALL;
307 result.config.target_interval = 0;
308 result.config.number_of_packets = 5;
309 }
310 /* support "--help" and "--version" */
311 if (argc == 2) {
312 if (!strcmp(argv[1], "--help")) {
313 strcpy(argv[1], "-h");
314 }
315 if (!strcmp(argv[1], "--version")) {
316 strcpy(argv[1], "-V");
317 }
318 }
319
320 sa_family_t enforced_ai_family = AF_UNSPEC;
321
322 enum {
323 output_format_index = CHAR_MAX + 1,
324 };
325
326 struct option longopts[] = {
327 {"version", no_argument, 0, 'V'},
328 {"help", no_argument, 0, 'h'},
329 {"verbose", no_argument, 0, 'v'},
330 {"Host", required_argument, 0, 'H'},
331 {"ipv4-only", no_argument, 0, '4'},
332 {"ipv6-only", no_argument, 0, '6'},
333 {"warning", required_argument, 0, 'w'},
334 {"critical", required_argument, 0, 'c'},
335 {"rta-mode-thresholds", required_argument, 0, 'R'},
336 {"packet-loss-mode-thresholds", required_argument, 0, 'P'},
337 {"jitter-mode-thresholds", required_argument, 0, 'J'},
338 {"mos-mode-thresholds", required_argument, 0, 'M'},
339 {"score-mode-thresholds", required_argument, 0, 'S'},
340 {"out-of-order-packets", no_argument, 0, 'O'},
341 {"number-of-packets", required_argument, 0, 'n'},
342 {"number-of-packets", required_argument, 0, 'p'},
343 {"packet-interval", required_argument, 0, 'i'},
344 {"target-interval", required_argument, 0, 'I'},
345 {"minimal-host-alive", required_argument, 0, 'm'},
346 {"outgoing-ttl", required_argument, 0, 'l'},
347 {"size", required_argument, 0, 'b'},
348 {"output-format", required_argument, 0, output_format_index},
349 {},
350 };
351
352 // Parse protocol arguments first
353 // and count hosts here
354 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
355 for (int i = 1; i < argc; i++) {
356 long int arg;
357 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
358 switch (arg) {
359
360 case '4':
361 if (enforced_ai_family != AF_UNSPEC) {
362 crash("Multiple protocol versions not supported");
363 }
364 enforced_ai_family = AF_INET;
365 break;
366 case '6':
367 if (enforced_ai_family != AF_UNSPEC) {
368 crash("Multiple protocol versions not supported");
369 }
370 enforced_ai_family = AF_INET6;
371 break;
372 case 'H': {
373 result.config.number_of_hosts++;
374 break;
375 }
376 case 'h': /* help */
377 // Trigger help here to avoid adding hosts before that (and doing DNS queries)
378 print_help();
379 exit(STATE_UNKNOWN);
380 break;
381 case 'v':
382 debug++;
383 break;
384 }
385 }
386 }
387
388 char **tmp = &argv[optind];
389 while (*tmp) {
390 result.config.number_of_hosts++;
391 tmp++;
392 }
393
394 // Sanity check: if hostmode is selected,only a single host is allowed
395 if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) {
396 usage("check_host only allows a single host");
397 }
398
399 // Allocate hosts
400 result.config.hosts =
401 calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container));
402 if (result.config.hosts == NULL) {
403 crash("failed to allocate memory");
404 }
405
406 /* Reset argument scanning */
407 optind = 1;
408
409 int host_counter = 0;
410 /* parse the arguments */
411 for (int i = 1; i < argc; i++) {
412 long int arg;
413 while ((arg = getopt_long(argc, argv, opts_str, longopts, NULL)) != EOF) {
414 switch (arg) {
415 case 'b': {
416 long size = strtol(optarg, NULL, 0);
417 if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
418 size < MAX_PING_DATA) {
419 result.config.icmp_data_size = (unsigned short)size;
420 } else {
421 usage_va("ICMP data length must be between: %lu and %lu",
422 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
423 MAX_PING_DATA - 1);
424 }
425 } break;
426 case 'i': {
427 // packet_interval was unused and is now removed
428 } break;
429 case 'I': {
430 get_timevar_wrapper parsed_time = get_timevar(optarg);
431
432 if (parsed_time.error_code == OK) {
433 result.config.target_interval = parsed_time.time_range;
434 } else {
435 crash("failed to parse target interval");
436 }
437 } break;
438 case 'w': {
439 get_threshold_wrapper warn = get_threshold(optarg, result.config.warn);
440 if (warn.errorcode == OK) {
441 result.config.warn = warn.threshold;
442 } else {
443 crash("failed to parse warning threshold");
444 }
445 } break;
446 case 'c': {
447 get_threshold_wrapper crit = get_threshold(optarg, result.config.crit);
448 if (crit.errorcode == OK) {
449 result.config.crit = crit.threshold;
450 } else {
451 crash("failed to parse critical threshold");
452 }
453 } break;
454 case 'n':
455 case 'p':
456 result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0);
457 if (result.config.number_of_packets > 20) {
458 errno = 0;
459 crash("packets is > 20 (%d)", result.config.number_of_packets);
460 }
461 break;
462 case 't':
463 // WARNING Deprecated since execution time is determined by the other factors
464 break;
465 case 'H': {
466 add_host_wrapper host_add_result =
467 add_host(optarg, result.config.mode, enforced_ai_family);
468 if (host_add_result.error_code == OK) {
469 result.config.hosts[host_counter] = host_add_result.host;
470 host_counter++;
471
472 if (result.config.targets != NULL) {
473 result.config.number_of_targets += ping_target_list_append(
474 result.config.targets, host_add_result.host.target_list);
475 } else {
476 result.config.targets = host_add_result.host.target_list;
477 result.config.number_of_targets += host_add_result.host.number_of_targets;
478 }
479
480 if (host_add_result.has_v4) {
481 result.config.need_v4 = true;
482 }
483 if (host_add_result.has_v6) {
484 result.config.need_v6 = true;
485 }
486 } else {
487 crash("Failed to add host, unable to parse it correctly");
488 }
489 } break;
490 case 'l':
491 result.config.ttl = strtoul(optarg, NULL, 0);
492 break;
493 case 'm':
494 result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0);
495 break;
496 case 's': /* specify source IP address */
497 result.config.source_ip = optarg;
498 break;
499 case 'V': /* version */
500 print_revision(progname, NP_VERSION);
501 exit(STATE_UNKNOWN);
502 case 'R': /* RTA mode */ {
503 get_threshold2_wrapper rta_th = get_threshold2(
504 optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode);
505
506 if (rta_th.errorcode != OK) {
507 crash("Failed to parse RTA threshold");
508 }
509
510 result.config.warn = rta_th.warn;
511 result.config.crit = rta_th.crit;
512 result.config.modes.rta_mode = true;
513 } break;
514 case 'P': /* packet loss mode */ {
515 get_threshold2_wrapper pl_th =
516 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
517 const_packet_loss_mode);
518 if (pl_th.errorcode != OK) {
519 crash("Failed to parse packet loss threshold");
520 }
521
522 result.config.warn = pl_th.warn;
523 result.config.crit = pl_th.crit;
524 result.config.modes.pl_mode = true;
525 } break;
526 case 'J': /* jitter mode */ {
527 get_threshold2_wrapper jitter_th =
528 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
529 const_jitter_mode);
530 if (jitter_th.errorcode != OK) {
531 crash("Failed to parse jitter threshold");
532 }
533
534 result.config.warn = jitter_th.warn;
535 result.config.crit = jitter_th.crit;
536 result.config.modes.jitter_mode = true;
537 } break;
538 case 'M': /* MOS mode */ {
539 get_threshold2_wrapper mos_th = get_threshold2(
540 optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode);
541 if (mos_th.errorcode != OK) {
542 crash("Failed to parse MOS threshold");
543 }
544
545 result.config.warn = mos_th.warn;
546 result.config.crit = mos_th.crit;
547 result.config.modes.mos_mode = true;
548 } break;
549 case 'S': /* score mode */ {
550 get_threshold2_wrapper score_th =
551 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
552 const_score_mode);
553 if (score_th.errorcode != OK) {
554 crash("Failed to parse score threshold");
555 }
556
557 result.config.warn = score_th.warn;
558 result.config.crit = score_th.crit;
559 result.config.modes.score_mode = true;
560 } break;
561 case 'O': /* out of order mode */
562 result.config.modes.order_mode = true;
563 break;
564 case output_format_index: {
565 parsed_output_format parser = mp_parse_output_format(optarg);
566 if (!parser.parsing_success) {
567 // TODO List all available formats here, maybe add anothoer usage function
568 printf("Invalid output format: %s\n", optarg);
569 exit(STATE_UNKNOWN);
570 }
571
572 result.config.output_format_is_set = true;
573 result.config.output_format = parser.output_format;
574 break;
575 }
576 }
577 }
578 }
579
580 argv = &argv[optind];
581 while (*argv) {
582 add_target(*argv, result.config.mode, enforced_ai_family);
583 argv++;
584 }
585
586 if (!result.config.number_of_targets) {
587 errno = 0;
588 crash("No hosts to check");
589 }
590
591 /* stupid users should be able to give whatever thresholds they want
592 * (nothing will break if they do), but some anal plugin maintainer
593 * will probably add some printf() thing here later, so it might be
594 * best to at least show them where to do it. ;) */
595 if (result.config.warn.pl > result.config.crit.pl) {
596 result.config.warn.pl = result.config.crit.pl;
597 }
598 if (result.config.warn.rta > result.config.crit.rta) {
599 result.config.warn.rta = result.config.crit.rta;
600 }
601 if (result.config.warn.jitter > result.config.crit.jitter) {
602 result.config.crit.jitter = result.config.warn.jitter;
603 }
604 if (result.config.warn.mos < result.config.crit.mos) {
605 result.config.warn.mos = result.config.crit.mos;
606 }
607 if (result.config.warn.score < result.config.crit.score) {
608 result.config.warn.score = result.config.crit.score;
609 }
610
611 return result;
612}
265 613
266/** code start **/ 614/** code start **/
267static void crash(const char *fmt, ...) { 615static void crash(const char *fmt, ...) {
268 va_list ap;
269 616
270 printf("%s: ", progname); 617 printf("%s: ", progname);
271 618
619 va_list ap;
272 va_start(ap, fmt); 620 va_start(ap, fmt);
273 vprintf(fmt, ap); 621 vprintf(fmt, ap);
274 va_end(ap); 622 va_end(ap);
@@ -385,18 +733,20 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm
385 return msg; 733 return msg;
386} 734}
387 735
388static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { 736static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
389 struct icmp p, sent_icmp; 737 time_t *target_interval, const uint16_t sender_id,
390 struct rta_host *host = NULL; 738 ping_target **table, unsigned short packets,
391 739 const unsigned short number_of_targets,
392 memcpy(&p, packet, sizeof(p)); 740 check_icmp_state *program_state) {
393 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { 741 struct icmp icmp_packet;
742 memcpy(&icmp_packet, packet, sizeof(icmp_packet));
743 if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) {
394 /* echo request from us to us (pinging localhost) */ 744 /* echo request from us to us (pinging localhost) */
395 return 0; 745 return 0;
396 } 746 }
397 747
398 if (debug) { 748 if (debug) {
399 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); 749 printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr);
400 } 750 }
401 751
402 /* only handle a few types, since others can't possibly be replies to 752 /* only handle a few types, since others can't possibly be replies to
@@ -409,14 +759,17 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
409 * TIMXCEED actually sends a proper icmp response we will have passed 759 * TIMXCEED actually sends a proper icmp response we will have passed
410 * too many hops to have a hope of reaching it later, in which case it 760 * too many hops to have a hope of reaching it later, in which case it
411 * indicates overconfidence in the network, poor routing or both. */ 761 * indicates overconfidence in the network, poor routing or both. */
412 if (p.icmp_type != ICMP_UNREACH && p.icmp_type != ICMP_TIMXCEED && p.icmp_type != ICMP_SOURCEQUENCH && p.icmp_type != ICMP_PARAMPROB) { 762 if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED &&
763 icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) {
413 return 0; 764 return 0;
414 } 765 }
415 766
416 /* might be for us. At least it holds the original package (according 767 /* might be for us. At least it holds the original package (according
417 * to RFC 792). If it isn't, just ignore it */ 768 * to RFC 792). If it isn't, just ignore it */
769 struct icmp sent_icmp;
418 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); 770 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp));
419 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != pid || ntohs(sent_icmp.icmp_seq) >= targets * packets) { 771 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id ||
772 ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) {
420 if (debug) { 773 if (debug) {
421 printf("Packet is no response to a packet we sent\n"); 774 printf("Packet is no response to a packet we sent\n");
422 } 775 }
@@ -424,14 +777,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
424 } 777 }
425 778
426 /* it is indeed a response for us */ 779 /* it is indeed a response for us */
427 host = table[ntohs(sent_icmp.icmp_seq) / packets]; 780 ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets];
428 if (debug) { 781 if (debug) {
429 char address[INET6_ADDRSTRLEN]; 782 char address[INET6_ADDRSTRLEN];
430 parse_address(addr, address, sizeof(address)); 783 parse_address(addr, address, sizeof(address));
431 printf("Received \"%s\" from %s for ICMP ECHO sent to %s.\n", get_icmp_error_msg(p.icmp_type, p.icmp_code), address, host->name); 784 printf("Received \"%s\" from %s for ICMP ECHO sent.\n",
785 get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address);
432 } 786 }
433 787
434 icmp_lost++; 788 program_state->icmp_lost++;
435 host->icmp_lost++; 789 host->icmp_lost++;
436 /* don't spend time on lost hosts any more */ 790 /* don't spend time on lost hosts any more */
437 if (host->flags & FLAG_LOST_CAUSE) { 791 if (host->flags & FLAG_LOST_CAUSE) {
@@ -440,305 +794,104 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
440 794
441 /* source quench means we're sending too fast, so increase the 795 /* source quench means we're sending too fast, so increase the
442 * interval and mark this packet lost */ 796 * interval and mark this packet lost */
443 if (p.icmp_type == ICMP_SOURCEQUENCH) { 797 if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) {
444 pkt_interval *= pkt_backoff_factor; 798 *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR);
445 target_interval *= target_backoff_factor;
446 } else { 799 } else {
447 targets_down++; 800 program_state->targets_down++;
448 host->flags |= FLAG_LOST_CAUSE; 801 host->flags |= FLAG_LOST_CAUSE;
449 } 802 }
450 host->icmp_type = p.icmp_type; 803 host->icmp_type = icmp_packet.icmp_type;
451 host->icmp_code = p.icmp_code; 804 host->icmp_code = icmp_packet.icmp_code;
452 host->error_addr = *addr; 805 host->error_addr = *addr;
453 806
454 return 0; 807 return 0;
455} 808}
456 809
457void parse_address(struct sockaddr_storage *addr, char *address, int size) { 810void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) {
458 switch (address_family) { 811 switch (addr->ss_family) {
459 case AF_INET: 812 case AF_INET:
460 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); 813 inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size);
461 break; 814 break;
462 case AF_INET6: 815 case AF_INET6:
463 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); 816 inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size);
464 break; 817 break;
818 default:
819 assert(false);
465 } 820 }
466} 821}
467 822
468int main(int argc, char **argv) { 823int main(int argc, char **argv) {
469 int i;
470 char *ptr;
471 long int arg;
472 int icmp_sockerrno, udp_sockerrno, tcp_sockerrno;
473 int result;
474 struct rta_host *host;
475#ifdef HAVE_SIGACTION
476 struct sigaction sig_action;
477#endif
478#ifdef SO_TIMESTAMP
479 int on = 1;
480#endif
481 char *source_ip = NULL;
482 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
483 setlocale(LC_ALL, ""); 824 setlocale(LC_ALL, "");
484 bindtextdomain(PACKAGE, LOCALEDIR); 825 bindtextdomain(PACKAGE, LOCALEDIR);
485 textdomain(PACKAGE); 826 textdomain(PACKAGE);
486 827
487 /* we only need to be setsuid when we get the sockets, so do 828 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
488 * that before pointer magic (esp. on network data) */ 829 environ = NULL;
489 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
490
491 address_family = -1;
492 int icmp_proto = IPPROTO_ICMP;
493 830
494 /* get calling name the old-fashioned way for portability instead 831 /* Parse extra opts if any */
495 * of relying on the glibc-ism __progname */ 832 argv = np_extra_opts(&argc, argv, progname);
496 ptr = strrchr(argv[0], '/');
497 if (ptr) {
498 progname = &ptr[1];
499 } else {
500 progname = argv[0];
501 }
502 833
503 /* now set defaults. Use progname to set them initially (allows for 834 check_icmp_config_wrapper tmp_config = process_arguments(argc, argv);
504 * superfast check_host program when target host is up */
505 cursor = list = NULL;
506 table = NULL;
507
508 mode = MODE_RTA;
509 /* Default critical thresholds */
510 crit.rta = 500000;
511 crit.pl = 80;
512 crit.jitter = 50;
513 crit.mos = 3;
514 crit.score = 70;
515 /* Default warning thresholds */
516 warn.rta = 200000;
517 warn.pl = 40;
518 warn.jitter = 40;
519 warn.mos = 3.5;
520 warn.score = 80;
521
522 protocols = HAVE_ICMP | HAVE_UDP | HAVE_TCP;
523 pkt_interval = 80000; /* 80 msec packet interval by default */
524 packets = 5;
525 835
526 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { 836 if (tmp_config.errorcode != OK) {
527 mode = MODE_ICMP; 837 crash("failed to parse config");
528 protocols = HAVE_ICMP;
529 } else if (!strcmp(progname, "check_host")) {
530 mode = MODE_HOSTCHECK;
531 pkt_interval = 1000000;
532 packets = 5;
533 crit.rta = warn.rta = 1000000;
534 crit.pl = warn.pl = 100;
535 } else if (!strcmp(progname, "check_rta_multi")) {
536 mode = MODE_ALL;
537 target_interval = 0;
538 pkt_interval = 50000;
539 packets = 5;
540 } 838 }
541 839
542 /* support "--help" and "--version" */ 840 const check_icmp_config config = tmp_config.config;
543 if (argc == 2) {
544 if (!strcmp(argv[1], "--help")) {
545 strcpy(argv[1], "-h");
546 }
547 if (!strcmp(argv[1], "--version")) {
548 strcpy(argv[1], "-V");
549 }
550 }
551 841
552 /* Parse protocol arguments first */ 842 if (config.output_format_is_set) {
553 for (i = 1; i < argc; i++) { 843 mp_set_format(config.output_format);
554 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
555 switch (arg) {
556 case '4':
557 if (address_family != -1) {
558 crash("Multiple protocol versions not supported");
559 }
560 address_family = AF_INET;
561 break;
562 case '6':
563#ifdef USE_IPV6
564 if (address_family != -1) {
565 crash("Multiple protocol versions not supported");
566 }
567 address_family = AF_INET6;
568#else
569 usage(_("IPv6 support not available\n"));
570#endif
571 break;
572 }
573 }
574 } 844 }
575 845
576 /* Reset argument scanning */ 846 check_icmp_socket_set sockset = {
577 optind = 1; 847 .socket4 = -1,
848 .socket6 = -1,
849 };
578 850
579 unsigned long size; 851 if (config.need_v4) {
580 bool err; 852 sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
581 /* parse the arguments */ 853 if (sockset.socket4 == -1) {
582 for (i = 1; i < argc; i++) { 854 crash("Failed to obtain ICMP v4 socket");
583 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 855 }
584 switch (arg) {
585 case 'v':
586 debug++;
587 break;
588 case 'b':
589 size = strtol(optarg, NULL, 0);
590 if (size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) && size < MAX_PING_DATA) {
591 icmp_data_size = size;
592 icmp_pkt_size = size + ICMP_MINLEN;
593 } else {
594 usage_va("ICMP data length must be between: %lu and %lu", sizeof(struct icmp) + sizeof(struct icmp_ping_data),
595 MAX_PING_DATA - 1);
596 }
597 break;
598 case 'i':
599 pkt_interval = get_timevar(optarg);
600 break;
601 case 'I':
602 target_interval = get_timevar(optarg);
603 break;
604 case 'w':
605 get_threshold(optarg, &warn);
606 break;
607 case 'c':
608 get_threshold(optarg, &crit);
609 break;
610 case 'n':
611 case 'p':
612 packets = strtoul(optarg, NULL, 0);
613 break;
614 case 't':
615 timeout = strtoul(optarg, NULL, 0);
616 if (!timeout) {
617 timeout = 10;
618 }
619 break;
620 case 'H':
621 add_target(optarg);
622 break;
623 case 'l':
624 ttl = (int)strtoul(optarg, NULL, 0);
625 break;
626 case 'm':
627 min_hosts_alive = (int)strtoul(optarg, NULL, 0);
628 break;
629 case 'd': /* implement later, for cluster checks */
630 warn_down = (unsigned char)strtoul(optarg, &ptr, 0);
631 if (ptr) {
632 crit_down = (unsigned char)strtoul(ptr + 1, NULL, 0);
633 }
634 break;
635 case 's': /* specify source IP address */
636 source_ip = optarg;
637 break;
638 case 'V': /* version */
639 print_revision(progname, NP_VERSION);
640 exit(STATE_UNKNOWN);
641 case 'h': /* help */
642 print_help();
643 exit(STATE_UNKNOWN);
644 break;
645 case 'R': /* RTA mode */
646 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_rta_mode);
647 if (!err) {
648 crash("Failed to parse RTA threshold");
649 }
650 856
651 rta_mode = true; 857 if (config.source_ip) {
652 break;
653 case 'P': /* packet loss mode */
654 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_packet_loss_mode);
655 if (!err) {
656 crash("Failed to parse packet loss threshold");
657 }
658 858
659 pl_mode = true; 859 struct in_addr tmp = {};
660 break; 860 int error_code = inet_pton(AF_INET, config.source_ip, &tmp);
661 case 'J': /* jitter mode */ 861 if (error_code == 1) {
662 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode); 862 set_source_ip(config.source_ip, sockset.socket4, AF_INET);
663 if (!err) { 863 } else {
664 crash("Failed to parse jitter threshold"); 864 // just try this mindlessly if it's not a v4 address
665 } 865 set_source_ip(config.source_ip, sockset.socket6, AF_INET6);
866 }
867 }
666 868
667 jitter_mode = true; 869#ifdef SO_TIMESTAMP
668 break; 870 if (sockset.socket4 != -1) {
669 case 'M': /* MOS mode */ 871 int on = 1;
670 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); 872 if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
671 if (!err) { 873 if (debug) {
672 crash("Failed to parse MOS threshold"); 874 printf("Warning: no SO_TIMESTAMP support\n");
673 } 875 }
674 876 }
675 mos_mode = true; 877 }
676 break; 878 if (sockset.socket6 != -1) {
677 case 'S': /* score mode */ 879 int on = 1;
678 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); 880 if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
679 if (!err) { 881 if (debug) {
680 crash("Failed to parse score threshold"); 882 printf("Warning: no SO_TIMESTAMP support\n");
681 } 883 }
682
683 score_mode = true;
684 break;
685 case 'O': /* out of order mode */
686 order_mode = true;
687 break;
688 } 884 }
689 } 885 }
886#endif // SO_TIMESTAMP
690 } 887 }
691 888
692 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ 889 if (config.need_v6) {
693 environ = NULL; 890 sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
694 891 if (sockset.socket6 == -1) {
695 /* use the pid to mark packets as ours */ 892 crash("Failed to obtain ICMP v6 socket");
696 /* Some systems have 32-bit pid_t so mask off only 16 bits */
697 pid = getpid() & 0xffff;
698 /* printf("pid = %u\n", pid); */
699
700 /* Parse extra opts if any */
701 argv = np_extra_opts(&argc, argv, progname);
702
703 argv = &argv[optind];
704 while (*argv) {
705 add_target(*argv);
706 argv++;
707 }
708
709 if (!targets) {
710 errno = 0;
711 crash("No hosts to check");
712 }
713
714 // add_target might change address_family
715 switch (address_family) {
716 case AF_INET:
717 icmp_proto = IPPROTO_ICMP;
718 break;
719 case AF_INET6:
720 icmp_proto = IPPROTO_ICMPV6;
721 break;
722 default:
723 crash("Address family not supported");
724 }
725 if ((icmp_sock = socket(address_family, SOCK_RAW, icmp_proto)) != -1) {
726 sockets |= HAVE_ICMP;
727 } else {
728 icmp_sockerrno = errno;
729 }
730
731 if (source_ip) {
732 set_source_ip(source_ip);
733 }
734
735#ifdef SO_TIMESTAMP
736 if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
737 if (debug) {
738 printf("Warning: no SO_TIMESTAMP support\n");
739 } 893 }
740 } 894 }
741#endif // SO_TIMESTAMP
742 895
743 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 896 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
744 if (setuid(getuid()) == -1) { 897 if (setuid(getuid()) == -1) {
@@ -746,186 +899,179 @@ int main(int argc, char **argv) {
746 return 1; 899 return 1;
747 } 900 }
748 901
749 if (!sockets) { 902 if (sockset.socket4) {
750 if (icmp_sock == -1) { 903 int result = setsockopt(sockset.socket4, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
751 errno = icmp_sockerrno;
752 crash("Failed to obtain ICMP socket");
753 return -1;
754 }
755 /* if(udp_sock == -1) { */
756 /* errno = icmp_sockerrno; */
757 /* crash("Failed to obtain UDP socket"); */
758 /* return -1; */
759 /* } */
760 /* if(tcp_sock == -1) { */
761 /* errno = icmp_sockerrno; */
762 /* crash("Failed to obtain TCP socker"); */
763 /* return -1; */
764 /* } */
765 }
766 if (!ttl) {
767 ttl = 64;
768 }
769
770 if (icmp_sock) {
771 result = setsockopt(icmp_sock, SOL_IP, IP_TTL, &ttl, sizeof(ttl));
772 if (debug) { 904 if (debug) {
773 if (result == -1) { 905 if (result == -1) {
774 printf("setsockopt failed\n"); 906 printf("setsockopt failed\n");
775 } else { 907 } else {
776 printf("ttl set to %u\n", ttl); 908 printf("ttl set to %lu\n", config.ttl);
777 } 909 }
778 } 910 }
779 } 911 }
780 912
781 /* stupid users should be able to give whatever thresholds they want 913 if (sockset.socket6) {
782 * (nothing will break if they do), but some anal plugin maintainer 914 int result = setsockopt(sockset.socket6, SOL_IP, IP_TTL, &config.ttl, sizeof(config.ttl));
783 * will probably add some printf() thing here later, so it might be 915 if (debug) {
784 * best to at least show them where to do it. ;) */ 916 if (result == -1) {
785 if (warn.pl > crit.pl) { 917 printf("setsockopt failed\n");
786 warn.pl = crit.pl; 918 } else {
787 } 919 printf("ttl set to %lu\n", config.ttl);
788 if (warn.rta > crit.rta) { 920 }
789 warn.rta = crit.rta; 921 }
790 }
791 if (warn_down > crit_down) {
792 crit_down = warn_down;
793 }
794 if (warn.jitter > crit.jitter) {
795 crit.jitter = warn.jitter;
796 }
797 if (warn.mos < crit.mos) {
798 warn.mos = crit.mos;
799 }
800 if (warn.score < crit.score) {
801 warn.score = crit.score;
802 }
803
804#ifdef HAVE_SIGACTION
805 sig_action.sa_sigaction = NULL;
806 sig_action.sa_handler = finish;
807 sigfillset(&sig_action.sa_mask);
808 sig_action.sa_flags = SA_NODEFER | SA_RESTART;
809 sigaction(SIGINT, &sig_action, NULL);
810 sigaction(SIGHUP, &sig_action, NULL);
811 sigaction(SIGTERM, &sig_action, NULL);
812 sigaction(SIGALRM, &sig_action, NULL);
813#else /* HAVE_SIGACTION */
814 signal(SIGINT, finish);
815 signal(SIGHUP, finish);
816 signal(SIGTERM, finish);
817 signal(SIGALRM, finish);
818#endif /* HAVE_SIGACTION */
819 if (debug) {
820 printf("Setting alarm timeout to %u seconds\n", timeout);
821 } 922 }
822 alarm(timeout);
823 923
824 /* make sure we don't wait any longer than necessary */ 924 /* make sure we don't wait any longer than necessary */
825 gettimeofday(&prog_start, &tz); 925 struct timeval prog_start;
826 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; 926 gettimeofday(&prog_start, NULL);
927
928 time_t max_completion_time =
929 (config.target_interval * config.number_of_targets) +
930 (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta;
827 931
828 if (debug) { 932 if (debug) {
829 printf("packets: %u, targets: %u\n" 933 printf("packets: %u, targets: %u\n"
830 "target_interval: %0.3f, pkt_interval %0.3f\n" 934 "target_interval: %0.3f\n"
831 "crit.rta: %0.3f\n" 935 "crit.rta: %0.3f\n"
832 "max_completion_time: %0.3f\n", 936 "max_completion_time: %0.3f\n",
833 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, 937 config.number_of_packets, config.number_of_targets,
938 (float)config.target_interval / 1000, (float)config.crit.rta / 1000,
834 (float)max_completion_time / 1000); 939 (float)max_completion_time / 1000);
835 } 940 }
836 941
837 if (debug) { 942 if (debug) {
838 if (max_completion_time > (u_int)timeout * 1000000) { 943 if (max_completion_time > (timeout * 1000000)) {
839 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); 944 printf("max_completion_time: %ld timeout: %u\n", max_completion_time, timeout);
840 printf("Timeout must be at least %llu\n", max_completion_time / 1000000 + 1); 945 printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1);
841 } 946 }
842 } 947 }
843 948
844 if (debug) { 949 if (debug) {
845 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); 950 printf("crit = {%ld, %u%%}, warn = {%ld, %u%%}\n", config.crit.rta, config.crit.pl,
846 printf("pkt_interval: %u target_interval: %u retry_interval: %u\n", pkt_interval, target_interval, retry_interval); 951 config.warn.rta, config.warn.pl);
847 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); 952 printf("target_interval: %ld\n", config.target_interval);
953 printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_data_size + ICMP_MINLEN, timeout);
848 } 954 }
849 955
850 if (packets > 20) { 956 if (config.min_hosts_alive < -1) {
851 errno = 0; 957 errno = 0;
852 crash("packets is > 20 (%d)", packets); 958 crash("minimum alive hosts is negative (%i)", config.min_hosts_alive);
853 } 959 }
854 960
855 if (min_hosts_alive < -1) { 961 // Build an index table of all targets
856 errno = 0; 962 ping_target *host = config.targets;
857 crash("minimum alive hosts is negative (%i)", min_hosts_alive); 963 ping_target **table = malloc(sizeof(ping_target *) * config.number_of_targets);
858 }
859
860 host = list;
861 table = malloc(sizeof(struct rta_host *) * targets);
862 if (!table) { 964 if (!table) {
863 crash("main(): malloc failed for host table"); 965 crash("main(): malloc failed for host table");
864 } 966 }
865 967
866 i = 0; 968 unsigned short target_index = 0;
867 while (host) { 969 while (host) {
868 host->id = i * packets; 970 host->id = target_index * config.number_of_packets;
869 table[i] = host; 971 table[target_index] = host;
870 host = host->next; 972 host = host->next;
871 i++; 973 target_index++;
872 } 974 }
873 975
874 run_checks(); 976 time_t target_interval = config.target_interval;
977
978 check_icmp_state program_state = check_icmp_state_init();
979
980 run_checks(config.icmp_data_size, &target_interval, config.sender_id, config.mode,
981 max_completion_time, prog_start, table, config.number_of_packets, sockset,
982 config.number_of_targets, &program_state);
875 983
876 errno = 0; 984 errno = 0;
877 finish(0);
878 985
879 return (0); 986 mp_check overall = mp_check_init();
880} 987 finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit,
988 config.number_of_targets, &program_state, config.hosts, config.number_of_hosts,
989 &overall);
990
991 if (sockset.socket4) {
992 close(sockset.socket4);
993 }
994 if (sockset.socket6) {
995 close(sockset.socket6);
996 }
881 997
882static void run_checks(void) { 998 mp_exit(overall);
883 u_int i, t; 999}
884 u_int final_wait, time_passed;
885 1000
1001static void run_checks(unsigned short icmp_pkt_size, time_t *target_interval,
1002 const uint16_t sender_id, const check_icmp_execution_mode mode,
1003 const time_t max_completion_time, const struct timeval prog_start,
1004 ping_target **table, const unsigned short packets,
1005 const check_icmp_socket_set sockset, const unsigned short number_of_targets,
1006 check_icmp_state *program_state) {
886 /* this loop might actually violate the pkt_interval or target_interval 1007 /* this loop might actually violate the pkt_interval or target_interval
887 * settings, but only if there aren't any packets on the wire which 1008 * settings, but only if there aren't any packets on the wire which
888 * indicates that the target can handle an increased packet rate */ 1009 * indicates that the target can handle an increased packet rate */
889 for (i = 0; i < packets; i++) { 1010 for (unsigned int packet_index = 0; packet_index < packets; packet_index++) {
890 for (t = 0; t < targets; t++) { 1011 for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) {
891 /* don't send useless packets */ 1012 /* don't send useless packets */
892 if (!targets_alive) { 1013 if (!targets_alive(number_of_targets, program_state->targets_down)) {
893 finish(0); 1014 return;
894 } 1015 }
895 if (table[t]->flags & FLAG_LOST_CAUSE) { 1016 if (table[target_index]->flags & FLAG_LOST_CAUSE) {
896 if (debug) { 1017 if (debug) {
897 printf("%s is a lost cause. not sending any more\n", table[t]->name); 1018
1019 char address[INET6_ADDRSTRLEN];
1020 parse_address(&table[target_index]->address, address, sizeof(address));
1021 printf("%s is a lost cause. not sending any more\n", address);
898 } 1022 }
899 continue; 1023 continue;
900 } 1024 }
901 1025
902 /* we're still in the game, so send next packet */ 1026 /* we're still in the game, so send next packet */
903 (void)send_icmp_ping(icmp_sock, table[t]); 1027 (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id,
904 wait_for_reply(icmp_sock, target_interval); 1028 program_state);
1029
1030 /* wrap up if all targets are declared dead */
1031 if (targets_alive(number_of_targets, program_state->targets_down) ||
1032 get_timevaldiff(prog_start, prog_start) < max_completion_time ||
1033 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1034 wait_for_reply(sockset, *target_interval, icmp_pkt_size, target_interval, sender_id,
1035 table, packets, number_of_targets, program_state);
1036 }
1037 }
1038 if (targets_alive(number_of_targets, program_state->targets_down) ||
1039 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1040 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1041 wait_for_reply(sockset, number_of_targets, icmp_pkt_size, target_interval, sender_id,
1042 table, packets, number_of_targets, program_state);
905 } 1043 }
906 wait_for_reply(icmp_sock, pkt_interval * targets);
907 } 1044 }
908 1045
909 if (icmp_pkts_en_route && targets_alive) { 1046 if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
910 time_passed = get_timevaldiff(NULL, NULL); 1047 program_state->icmp_lost) &&
911 final_wait = max_completion_time - time_passed; 1048 targets_alive(number_of_targets, program_state->targets_down)) {
1049 time_t time_passed = get_timevaldiff_to_now(prog_start);
1050 time_t final_wait = max_completion_time - time_passed;
912 1051
913 if (debug) { 1052 if (debug) {
914 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); 1053 printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed,
1054 final_wait, max_completion_time);
915 } 1055 }
916 if (time_passed > max_completion_time) { 1056 if (time_passed > max_completion_time) {
917 if (debug) { 1057 if (debug) {
918 printf("Time passed. Finishing up\n"); 1058 printf("Time passed. Finishing up\n");
919 } 1059 }
920 finish(0); 1060 return;
921 } 1061 }
922 1062
923 /* catch the packets that might come in within the timeframe, but 1063 /* catch the packets that might come in within the timeframe, but
924 * haven't yet */ 1064 * haven't yet */
925 if (debug) { 1065 if (debug) {
926 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); 1066 printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait,
1067 (float)final_wait / 1000);
1068 }
1069 if (targets_alive(number_of_targets, program_state->targets_down) ||
1070 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1071 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1072 wait_for_reply(sockset, final_wait, icmp_pkt_size, target_interval, sender_id, table,
1073 packets, number_of_targets, program_state);
927 } 1074 }
928 wait_for_reply(icmp_sock, final_wait);
929 } 1075 }
930} 1076}
931 1077
@@ -939,18 +1085,11 @@ static void run_checks(void) {
939 * both: 1085 * both:
940 * icmp echo reply : the rest 1086 * icmp echo reply : the rest
941 */ 1087 */
942static int wait_for_reply(int sock, u_int t) { 1088static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval,
943 int n, hlen; 1089 unsigned short icmp_pkt_size, time_t *target_interval, uint16_t sender_id,
944 static unsigned char buf[65536]; 1090 ping_target **table, const unsigned short packets,
945 struct sockaddr_storage resp_addr; 1091 const unsigned short number_of_targets, check_icmp_state *program_state) {
946 union ip_hdr *ip;
947 union icmp_packet packet; 1092 union icmp_packet packet;
948 struct rta_host *host;
949 struct icmp_ping_data data;
950 struct timeval wait_start, now;
951 u_int tdiff, i, per_pkt_wait;
952 double jitter_tmp;
953
954 if (!(packet.buf = malloc(icmp_pkt_size))) { 1093 if (!(packet.buf = malloc(icmp_pkt_size))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1094 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
956 return -1; /* might be reached if we're in debug mode */ 1095 return -1; /* might be reached if we're in debug mode */
@@ -959,177 +1098,174 @@ static int wait_for_reply(int sock, u_int t) {
959 memset(packet.buf, 0, icmp_pkt_size); 1098 memset(packet.buf, 0, icmp_pkt_size);
960 1099
961 /* if we can't listen or don't have anything to listen to, just return */ 1100 /* if we can't listen or don't have anything to listen to, just return */
962 if (!t || !icmp_pkts_en_route) { 1101 if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1102 program_state->icmp_lost)) {
963 free(packet.buf); 1103 free(packet.buf);
964 return 0; 1104 return 0;
965 } 1105 }
966 1106
967 gettimeofday(&wait_start, &tz); 1107 // Get current time stamp
968 1108 struct timeval wait_start;
969 i = t; 1109 gettimeofday(&wait_start, NULL);
970 per_pkt_wait = t / icmp_pkts_en_route;
971 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) {
972 t = per_pkt_wait;
973 1110
974 /* wrap up if all targets are declared dead */ 1111 struct sockaddr_storage resp_addr;
975 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { 1112 time_t per_pkt_wait =
976 finish(0); 1113 time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
977 } 1114 program_state->icmp_lost);
1115 static unsigned char buf[65536];
1116 union ip_hdr *ip_header;
1117 struct timeval packet_received_timestamp;
1118 while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1119 program_state->icmp_lost) &&
1120 get_timevaldiff_to_now(wait_start) < time_interval) {
1121 time_t loop_time_interval = per_pkt_wait;
978 1122
979 /* reap responses until we hit a timeout */ 1123 /* reap responses until we hit a timeout */
980 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); 1124 recvfrom_wto_wrapper recv_foo =
981 if (!n) { 1125 recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr,
1126 &loop_time_interval, &packet_received_timestamp);
1127 if (!recv_foo.received) {
982 if (debug > 1) { 1128 if (debug > 1) {
983 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); 1129 printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait);
984 } 1130 }
985 continue; /* timeout for this one, so keep trying */ 1131 continue; /* timeout for this one, so keep trying */
986 } 1132 }
987 if (n < 0) { 1133
1134 if (recv_foo.received < 0) {
988 if (debug) { 1135 if (debug) {
989 printf("recvfrom_wto() returned errors\n"); 1136 printf("recvfrom_wto() returned errors\n");
990 } 1137 }
991 free(packet.buf); 1138 free(packet.buf);
992 return n; 1139 return (int)recv_foo.received;
993 } 1140 }
994 1141
995 // FIXME: with ipv6 we don't have an ip header here 1142 if (recv_foo.recv_proto != AF_INET6) {
996 if (address_family != AF_INET6) { 1143 ip_header = (union ip_hdr *)buf;
997 ip = (union ip_hdr *)buf;
998 1144
999 if (debug > 1) { 1145 if (debug > 1) {
1000 char address[INET6_ADDRSTRLEN]; 1146 char address[INET6_ADDRSTRLEN];
1001 parse_address(&resp_addr, address, sizeof(address)); 1147 parse_address(&resp_addr, address, sizeof(address));
1002 printf("received %u bytes from %s\n", address_family == AF_INET6 ? ntohs(ip->ip6.ip6_plen) : ntohs(ip->ip.ip_len), address); 1148 printf("received %u bytes from %s\n",
1149 address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen)
1150 : ntohs(ip_header->ip.ip_len),
1151 address);
1003 } 1152 }
1004 } 1153 }
1005 1154
1006 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 1155 int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2;
1007 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 1156
1008 /* alpha headers are decidedly broken. Using an ansi compiler, 1157 if (recv_foo.received < (hlen + ICMP_MINLEN)) {
1009 * they provide ip_vhl instead of ip_hl and ip_v, so we mask
1010 * off the bottom 4 bits */
1011 /* hlen = (ip->ip_vhl & 0x0f) << 2; */
1012 /* #else */
1013 hlen = (address_family == AF_INET6) ? 0 : ip->ip.ip_hl << 2;
1014 /* #endif */
1015
1016 if (n < (hlen + ICMP_MINLEN)) {
1017 char address[INET6_ADDRSTRLEN]; 1158 char address[INET6_ADDRSTRLEN];
1018 parse_address(&resp_addr, address, sizeof(address)); 1159 parse_address(&resp_addr, address, sizeof(address));
1019 crash("received packet too short for ICMP (%d bytes, expected %d) from %s\n", n, hlen + icmp_pkt_size, address); 1160 crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n",
1161 recv_foo.received, hlen + icmp_pkt_size, address);
1020 } 1162 }
1021 /* else if(debug) { */
1022 /* printf("ip header size: %u, packet size: %u (expected %u, %u)\n", */
1023 /* hlen, ntohs(ip->ip_len) - hlen, */
1024 /* sizeof(struct ip), icmp_pkt_size); */
1025 /* } */
1026
1027 /* check the response */ 1163 /* check the response */
1028
1029 memcpy(packet.buf, buf + hlen, icmp_pkt_size); 1164 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
1030 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1031 : sizeof(struct icmp));*/
1032 1165
1033 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || 1166 if ((recv_foo.recv_proto == AF_INET &&
1034 ntohs(packet.icp->icmp_seq) >= targets * packets)) || 1167 (ntohs(packet.icp->icmp_id) != sender_id || packet.icp->icmp_type != ICMP_ECHOREPLY ||
1035 (address_family == PF_INET6 && (ntohs(packet.icp6->icmp6_id) != pid || packet.icp6->icmp6_type != ICMP6_ECHO_REPLY || 1168 ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) ||
1036 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { 1169 (recv_foo.recv_proto == AF_INET6 &&
1170 (ntohs(packet.icp6->icmp6_id) != sender_id ||
1171 packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1172 ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) {
1037 if (debug > 2) { 1173 if (debug > 2) {
1038 printf("not a proper ICMP_ECHOREPLY\n"); 1174 printf("not a proper ICMP_ECHOREPLY\n");
1039 } 1175 }
1040 handle_random_icmp(buf + hlen, &resp_addr); 1176
1177 handle_random_icmp(buf + hlen, &resp_addr, target_interval, sender_id, table, packets,
1178 number_of_targets, program_state);
1179
1041 continue; 1180 continue;
1042 } 1181 }
1043 1182
1044 /* this is indeed a valid response */ 1183 /* this is indeed a valid response */
1045 if (address_family == PF_INET) { 1184 ping_target *target;
1185 struct icmp_ping_data data;
1186 if (address_family == AF_INET) {
1046 memcpy(&data, packet.icp->icmp_data, sizeof(data)); 1187 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1047 if (debug > 2) { 1188 if (debug > 2) {
1048 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp->icmp_id), 1189 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1049 ntohs(packet.icp->icmp_seq), packet.icp->icmp_cksum); 1190 ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1191 packet.icp->icmp_cksum);
1050 } 1192 }
1051 host = table[ntohs(packet.icp->icmp_seq) / packets]; 1193 target = table[ntohs(packet.icp->icmp_seq) / packets];
1052 } else { 1194 } else {
1053 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); 1195 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1054 if (debug > 2) { 1196 if (debug > 2) {
1055 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", (unsigned long)sizeof(data), ntohs(packet.icp6->icmp6_id), 1197 printf("ICMP echo-reply of len %lu, id %u, seq %u, cksum 0x%X\n", sizeof(data),
1056 ntohs(packet.icp6->icmp6_seq), packet.icp6->icmp6_cksum); 1198 ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1199 packet.icp6->icmp6_cksum);
1057 } 1200 }
1058 host = table[ntohs(packet.icp6->icmp6_seq) / packets]; 1201 target = table[ntohs(packet.icp6->icmp6_seq) / packets];
1059 } 1202 }
1060 1203
1061 tdiff = get_timevaldiff(&data.stime, &now); 1204 time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
1062 1205
1063 if (host->last_tdiff > 0) { 1206 if (target->last_tdiff > 0) {
1064 /* Calculate jitter */ 1207 /* Calculate jitter */
1065 if (host->last_tdiff > tdiff) { 1208 double jitter_tmp;
1066 jitter_tmp = host->last_tdiff - tdiff; 1209 if (target->last_tdiff > tdiff) {
1210 jitter_tmp = (double)(target->last_tdiff - tdiff);
1067 } else { 1211 } else {
1068 jitter_tmp = tdiff - host->last_tdiff; 1212 jitter_tmp = (double)(tdiff - target->last_tdiff);
1069 } 1213 }
1070 1214
1071 if (host->jitter == 0) { 1215 if (target->jitter == 0) {
1072 host->jitter = jitter_tmp; 1216 target->jitter = jitter_tmp;
1073 host->jitter_max = jitter_tmp; 1217 target->jitter_max = jitter_tmp;
1074 host->jitter_min = jitter_tmp; 1218 target->jitter_min = jitter_tmp;
1075 } else { 1219 } else {
1076 host->jitter += jitter_tmp; 1220 target->jitter += jitter_tmp;
1077 1221
1078 if (jitter_tmp < host->jitter_min) { 1222 if (jitter_tmp < target->jitter_min) {
1079 host->jitter_min = jitter_tmp; 1223 target->jitter_min = jitter_tmp;
1080 } 1224 }
1081 1225
1082 if (jitter_tmp > host->jitter_max) { 1226 if (jitter_tmp > target->jitter_max) {
1083 host->jitter_max = jitter_tmp; 1227 target->jitter_max = jitter_tmp;
1084 } 1228 }
1085 } 1229 }
1086 1230
1087 /* Check if packets in order */ 1231 /* Check if packets in order */
1088 if (host->last_icmp_seq >= packet.icp->icmp_seq) { 1232 if (target->last_icmp_seq >= packet.icp->icmp_seq) {
1089 host->order_status = STATE_CRITICAL; 1233 target->found_out_of_order_packets = true;
1090 } 1234 }
1091 } 1235 }
1092 host->last_tdiff = tdiff; 1236 target->last_tdiff = tdiff;
1093 1237
1094 host->last_icmp_seq = packet.icp->icmp_seq; 1238 target->last_icmp_seq = packet.icp->icmp_seq;
1095 1239
1096 host->time_waited += tdiff; 1240 target->time_waited += tdiff;
1097 host->icmp_recv++; 1241 target->icmp_recv++;
1098 icmp_recv++; 1242 program_state->icmp_recv++;
1099 1243
1100 if (tdiff > (unsigned int)host->rtmax) { 1244 if (tdiff > (unsigned int)target->rtmax) {
1101 host->rtmax = tdiff; 1245 target->rtmax = (double)tdiff;
1102 } 1246 }
1103 1247
1104 if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { 1248 if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) {
1105 host->rtmin = tdiff; 1249 target->rtmin = (double)tdiff;
1106 } 1250 }
1107 1251
1108 if (debug) { 1252 if (debug) {
1109 char address[INET6_ADDRSTRLEN]; 1253 char address[INET6_ADDRSTRLEN];
1110 parse_address(&resp_addr, address, sizeof(address)); 1254 parse_address(&resp_addr, address, sizeof(address));
1111 1255
1112 switch (address_family) { 1256 switch (recv_foo.recv_proto) {
1113 case AF_INET: { 1257 case AF_INET: {
1114 printf("%0.3f ms rtt from %s, outgoing ttl: %u, incoming ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, 1258 printf("%0.3f ms rtt from %s, incoming ttl: %u, max: %0.3f, min: %0.3f\n",
1115 ttl, ip->ip.ip_ttl, (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1259 (float)tdiff / 1000, address, ip_header->ip.ip_ttl,
1260 (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1116 break; 1261 break;
1117 }; 1262 };
1118 case AF_INET6: { 1263 case AF_INET6: {
1119 printf("%0.3f ms rtt from %s, outgoing ttl: %u, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000, address, ttl, 1264 printf("%0.3f ms rtt from %s, max: %0.3f, min: %0.3f\n", (float)tdiff / 1000,
1120 (float)host->rtmax / 1000, (float)host->rtmin / 1000); 1265 address, (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1121 }; 1266 };
1122 } 1267 }
1123 } 1268 }
1124
1125 /* if we're in hostcheck mode, exit with limited printouts */
1126 if (mode == MODE_HOSTCHECK) {
1127 printf("OK - %s responds to ICMP. Packet %u, rta %0.3fms|"
1128 "pkt=%u;;;0;%u rta=%0.3f;%0.3f;%0.3f;;\n",
1129 host->name, icmp_recv, (float)tdiff / 1000, icmp_recv, packets, (float)tdiff / 1000, (float)warn.rta / 1000,
1130 (float)crit.rta / 1000);
1131 exit(STATE_OK);
1132 }
1133 } 1269 }
1134 1270
1135 free(packet.buf); 1271 free(packet.buf);
@@ -1137,38 +1273,28 @@ static int wait_for_reply(int sock, u_int t) {
1137} 1273}
1138 1274
1139/* the ping functions */ 1275/* the ping functions */
1140static int send_icmp_ping(int sock, struct rta_host *host) { 1276static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
1141 long int len; 1277 const unsigned short icmp_pkt_size, const uint16_t sender_id,
1142 size_t addrlen; 1278 check_icmp_state *program_state) {
1143 struct icmp_ping_data data; 1279 void *buf = calloc(1, icmp_pkt_size);
1144 struct msghdr hdr;
1145 struct iovec iov;
1146 struct timeval tv;
1147 void *buf = NULL;
1148
1149 if (sock == -1) {
1150 errno = 0;
1151 crash("Attempt to send on bogus socket");
1152 return -1;
1153 }
1154
1155 if (!buf) { 1280 if (!buf) {
1156 if (!(buf = malloc(icmp_pkt_size))) { 1281 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size);
1157 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1282 return -1; /* might be reached if we're in debug mode */
1158 return -1; /* might be reached if we're in debug mode */
1159 }
1160 } 1283 }
1161 memset(buf, 0, icmp_pkt_size);
1162 1284
1163 if ((gettimeofday(&tv, &tz)) == -1) { 1285 struct timeval current_time;
1286 if ((gettimeofday(&current_time, NULL)) == -1) {
1164 free(buf); 1287 free(buf);
1165 return -1; 1288 return -1;
1166 } 1289 }
1167 1290
1291 struct icmp_ping_data data;
1168 data.ping_id = 10; /* host->icmp.icmp_sent; */ 1292 data.ping_id = 10; /* host->icmp.icmp_sent; */
1169 memcpy(&data.stime, &tv, sizeof(tv)); 1293 memcpy(&data.stime, &current_time, sizeof(current_time));
1170 1294
1171 if (address_family == AF_INET) { 1295 socklen_t addrlen = 0;
1296
1297 if (host->address.ss_family == AF_INET) {
1172 struct icmp *icp = (struct icmp *)buf; 1298 struct icmp *icp = (struct icmp *)buf;
1173 addrlen = sizeof(struct sockaddr_in); 1299 addrlen = sizeof(struct sockaddr_in);
1174 1300
@@ -1177,15 +1303,19 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1177 icp->icmp_type = ICMP_ECHO; 1303 icp->icmp_type = ICMP_ECHO;
1178 icp->icmp_code = 0; 1304 icp->icmp_code = 0;
1179 icp->icmp_cksum = 0; 1305 icp->icmp_cksum = 0;
1180 icp->icmp_id = htons(pid); 1306 icp->icmp_id = htons((uint16_t)sender_id);
1181 icp->icmp_seq = htons(host->id++); 1307 icp->icmp_seq = htons(host->id++);
1182 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); 1308 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1183 1309
1184 if (debug > 2) { 1310 if (debug > 2) {
1185 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1311 char address[INET6_ADDRSTRLEN];
1186 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); 1312 parse_address((&host->address), address, sizeof(address));
1313
1314 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1315 sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum,
1316 address);
1187 } 1317 }
1188 } else { 1318 } else if (host->address.ss_family == AF_INET6) {
1189 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; 1319 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1190 addrlen = sizeof(struct sockaddr_in6); 1320 addrlen = sizeof(struct sockaddr_in6);
1191 1321
@@ -1194,659 +1324,431 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1194 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 1324 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1195 icp6->icmp6_code = 0; 1325 icp6->icmp6_code = 0;
1196 icp6->icmp6_cksum = 0; 1326 icp6->icmp6_cksum = 0;
1197 icp6->icmp6_id = htons(pid); 1327 icp6->icmp6_id = htons((uint16_t)sender_id);
1198 icp6->icmp6_seq = htons(host->id++); 1328 icp6->icmp6_seq = htons(host->id++);
1199 // let checksum be calculated automatically 1329 // let checksum be calculated automatically
1200 1330
1201 if (debug > 2) { 1331 if (debug > 2) {
1202 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n", (unsigned long)sizeof(data), 1332 char address[INET6_ADDRSTRLEN];
1203 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum, host->name); 1333 parse_address((&host->address), address, sizeof(address));
1334
1335 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to target %s\n",
1336 sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum,
1337 address);
1204 } 1338 }
1339 } else {
1340 // unknown address family
1341 crash("unknown address family in %s", __func__);
1205 } 1342 }
1206 1343
1344 struct iovec iov;
1207 memset(&iov, 0, sizeof(iov)); 1345 memset(&iov, 0, sizeof(iov));
1208 iov.iov_base = buf; 1346 iov.iov_base = buf;
1209 iov.iov_len = icmp_pkt_size; 1347 iov.iov_len = icmp_pkt_size;
1210 1348
1349 struct msghdr hdr;
1211 memset(&hdr, 0, sizeof(hdr)); 1350 memset(&hdr, 0, sizeof(hdr));
1212 hdr.msg_name = (struct sockaddr *)&host->saddr_in; 1351 hdr.msg_name = (struct sockaddr *)&host->address;
1213 hdr.msg_namelen = addrlen; 1352 hdr.msg_namelen = addrlen;
1214 hdr.msg_iov = &iov; 1353 hdr.msg_iov = &iov;
1215 hdr.msg_iovlen = 1; 1354 hdr.msg_iovlen = 1;
1216 1355
1217 errno = 0; 1356 errno = 0;
1218 1357
1219/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1358 long int len;
1359 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1360 if (host->address.ss_family == AF_INET) {
1361#ifdef MSG_CONFIRM
1362 len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM);
1363#else
1364 len = sendmsg(sockset.socket4, &hdr, 0);
1365#endif
1366 } else if (host->address.ss_family == AF_INET6) {
1220#ifdef MSG_CONFIRM 1367#ifdef MSG_CONFIRM
1221 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1368 len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM);
1222#else 1369#else
1223 len = sendmsg(sock, &hdr, 0); 1370 len = sendmsg(sockset.socket6, &hdr, 0);
1224#endif 1371#endif
1372 } else {
1373 assert(false);
1374 }
1225 1375
1226 free(buf); 1376 free(buf);
1227 1377
1228 if (len < 0 || (unsigned int)len != icmp_pkt_size) { 1378 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1229 if (debug) { 1379 if (debug) {
1230 char address[INET6_ADDRSTRLEN]; 1380 char address[INET6_ADDRSTRLEN];
1231 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); 1381 parse_address((&host->address), address, sizeof(address));
1232 printf("Failed to send ping to %s: %s\n", address, strerror(errno)); 1382 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1233 } 1383 }
1234 errno = 0; 1384 errno = 0;
1235 return -1; 1385 return -1;
1236 } 1386 }
1237 1387
1238 icmp_sent++; 1388 program_state->icmp_sent++;
1239 host->icmp_sent++; 1389 host->icmp_sent++;
1240 1390
1241 return 0; 1391 return 0;
1242} 1392}
1243 1393
1244static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { 1394static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf,
1245 u_int slen; 1395 const unsigned int len, struct sockaddr *saddr,
1246 int n, ret; 1396 time_t *timeout, struct timeval *received_timestamp) {
1247 struct timeval to, then, now;
1248 fd_set rd, wr;
1249#ifdef HAVE_MSGHDR_MSG_CONTROL 1397#ifdef HAVE_MSGHDR_MSG_CONTROL
1250 char ans_data[4096]; 1398 char ans_data[4096];
1251#endif // HAVE_MSGHDR_MSG_CONTROL 1399#endif // HAVE_MSGHDR_MSG_CONTROL
1252 struct msghdr hdr;
1253 struct iovec iov;
1254#ifdef SO_TIMESTAMP 1400#ifdef SO_TIMESTAMP
1255 struct cmsghdr *chdr; 1401 struct cmsghdr *chdr;
1256#endif 1402#endif
1257 1403
1258 if (!*timo) { 1404 recvfrom_wto_wrapper result = {
1405 .received = 0,
1406 .recv_proto = AF_UNSPEC,
1407 };
1408
1409 if (!*timeout) {
1259 if (debug) { 1410 if (debug) {
1260 printf("*timo is not\n"); 1411 printf("*timeout is not\n");
1261 } 1412 }
1262 return 0; 1413 return result;
1414 }
1415
1416 struct timeval real_timeout;
1417 real_timeout.tv_sec = *timeout / 1000000;
1418 real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000));
1419
1420 // Dummy fds for select
1421 fd_set dummy_write_fds;
1422 FD_ZERO(&dummy_write_fds);
1423
1424 // Read fds for select with the socket
1425 fd_set read_fds;
1426 FD_ZERO(&read_fds);
1427
1428 if (sockset.socket4 != -1) {
1429 FD_SET(sockset.socket4, &read_fds);
1430 }
1431 if (sockset.socket6 != -1) {
1432 FD_SET(sockset.socket6, &read_fds);
1263 } 1433 }
1264 1434
1265 to.tv_sec = *timo / 1000000; 1435 int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1;
1266 to.tv_usec = (*timo - (to.tv_sec * 1000000)); 1436
1437 struct timeval then;
1438 gettimeofday(&then, NULL);
1267 1439
1268 FD_ZERO(&rd);
1269 FD_ZERO(&wr);
1270 FD_SET(sock, &rd);
1271 errno = 0; 1440 errno = 0;
1272 gettimeofday(&then, &tz); 1441 int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout);
1273 n = select(sock + 1, &rd, &wr, NULL, &to); 1442 if (select_return < 0) {
1274 if (n < 0) {
1275 crash("select() in recvfrom_wto"); 1443 crash("select() in recvfrom_wto");
1276 } 1444 }
1277 gettimeofday(&now, &tz);
1278 *timo = get_timevaldiff(&then, &now);
1279 1445
1280 if (!n) { 1446 struct timeval now;
1281 return 0; /* timeout */ 1447 gettimeofday(&now, NULL);
1448 *timeout = get_timevaldiff(then, now);
1449
1450 if (!select_return) {
1451 return result; /* timeout */
1282 } 1452 }
1283 1453
1284 slen = sizeof(struct sockaddr_storage); 1454 unsigned int slen = sizeof(struct sockaddr_storage);
1285 1455
1286 memset(&iov, 0, sizeof(iov)); 1456 struct iovec iov = {
1287 iov.iov_base = buf; 1457 .iov_base = buf,
1288 iov.iov_len = len; 1458 .iov_len = len,
1459 };
1289 1460
1290 memset(&hdr, 0, sizeof(hdr)); 1461 struct msghdr hdr = {
1291 hdr.msg_name = saddr; 1462 .msg_name = saddr,
1292 hdr.msg_namelen = slen; 1463 .msg_namelen = slen,
1293 hdr.msg_iov = &iov; 1464 .msg_iov = &iov,
1294 hdr.msg_iovlen = 1; 1465 .msg_iovlen = 1,
1295#ifdef HAVE_MSGHDR_MSG_CONTROL 1466#ifdef HAVE_MSGHDR_MSG_CONTROL
1296 hdr.msg_control = ans_data; 1467 .msg_control = ans_data,
1297 hdr.msg_controllen = sizeof(ans_data); 1468 .msg_controllen = sizeof(ans_data),
1298#endif 1469#endif
1470 };
1471
1472 ssize_t ret;
1473 if (FD_ISSET(sockset.socket4, &read_fds)) {
1474 ret = recvmsg(sockset.socket4, &hdr, 0);
1475 result.recv_proto = AF_INET;
1476 } else if (FD_ISSET(sockset.socket6, &read_fds)) {
1477 ret = recvmsg(sockset.socket6, &hdr, 0);
1478 result.recv_proto = AF_INET6;
1479 } else {
1480 assert(false);
1481 }
1482
1483 result.received = ret;
1299 1484
1300 ret = recvmsg(sock, &hdr, 0);
1301#ifdef SO_TIMESTAMP 1485#ifdef SO_TIMESTAMP
1302 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { 1486 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) {
1303 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP && chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) { 1487 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP &&
1304 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); 1488 chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1489 memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp));
1305 break; 1490 break;
1306 } 1491 }
1307 } 1492 }
1308 1493
1309 if (!chdr) 1494 if (!chdr) {
1495 gettimeofday(received_timestamp, NULL);
1496 }
1497#else
1498 gettimeofday(tv, NULL);
1310#endif // SO_TIMESTAMP 1499#endif // SO_TIMESTAMP
1311 gettimeofday(tv, &tz);
1312 return (ret);
1313}
1314 1500
1315static void finish(int sig) { 1501 return (result);
1316 u_int i = 0; 1502}
1317 unsigned char pl;
1318 double rta;
1319 struct rta_host *host;
1320 const char *status_string[] = {"OK", "WARNING", "CRITICAL", "UNKNOWN", "DEPENDENT"};
1321 int hosts_ok = 0;
1322 int hosts_warn = 0;
1323 int this_status;
1324 double R;
1325 1503
1504static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive,
1505 check_icmp_threshold warn, check_icmp_threshold crit,
1506 const unsigned short number_of_targets, check_icmp_state *program_state,
1507 check_icmp_target_container host_list[], unsigned short number_of_hosts,
1508 mp_check overall[static 1]) {
1509 // Deactivate alarm
1326 alarm(0); 1510 alarm(0);
1511
1327 if (debug > 1) { 1512 if (debug > 1) {
1328 printf("finish(%d) called\n", sig); 1513 printf("finish(%d) called\n", sig);
1329 } 1514 }
1330 1515
1331 if (icmp_sock != -1) {
1332 close(icmp_sock);
1333 }
1334 if (udp_sock != -1) {
1335 close(udp_sock);
1336 }
1337 if (tcp_sock != -1) {
1338 close(tcp_sock);
1339 }
1340
1341 if (debug) { 1516 if (debug) {
1342 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); 1517 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", program_state->icmp_sent,
1343 printf("targets: %u targets_alive: %u\n", targets, targets_alive); 1518 program_state->icmp_recv, program_state->icmp_lost);
1519 printf("targets: %u targets_alive: %u\n", number_of_targets,
1520 targets_alive(number_of_targets, program_state->targets_down));
1344 } 1521 }
1345 1522
1346 /* iterate thrice to calculate values, give output, and print perfparse */ 1523 // loop over targets to evaluate each one
1347 status = STATE_OK; 1524 int targets_ok = 0;
1348 host = list; 1525 int targets_warn = 0;
1349 1526 for (unsigned short i = 0; i < number_of_hosts; i++) {
1350 while (host) { 1527 evaluate_host_wrapper host_check = evaluate_host(host_list[i], modes, warn, crit);
1351 this_status = STATE_OK;
1352
1353 if (!host->icmp_recv) {
1354 /* rta 0 is ofcourse not entirely correct, but will still show up
1355 * conspicuously as missing entries in perfparse and cacti */
1356 pl = 100;
1357 rta = 0;
1358 status = STATE_CRITICAL;
1359 /* up the down counter if not already counted */
1360 if (!(host->flags & FLAG_LOST_CAUSE) && targets_alive) {
1361 targets_down++;
1362 }
1363 } else {
1364 pl = ((host->icmp_sent - host->icmp_recv) * 100) / host->icmp_sent;
1365 rta = (double)host->time_waited / host->icmp_recv;
1366 }
1367
1368 if (host->icmp_recv > 1) {
1369 /*
1370 * This algorithm is probably pretty much blindly copied from
1371 * locations like this one: https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos
1372 * It calculates a MOS value (range of 1 to 5, where 1 is bad and 5 really good).
1373 * According to some quick research MOS originates from the Audio/Video transport network area.
1374 * Whether it can and should be computed from ICMP data, I can not say.
1375 *
1376 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
1377 *
1378 * MOS stands likely for Mean Opinion Score ( https://en.wikipedia.org/wiki/Mean_Opinion_Score )
1379 *
1380 * More links:
1381 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
1382 */
1383 host->jitter = (host->jitter / (host->icmp_recv - 1) / 1000);
1384
1385 /*
1386 * Take the average round trip latency (in milliseconds), add
1387 * round trip jitter, but double the impact to latency
1388 * then add 10 for protocol latencies (in milliseconds).
1389 */
1390 host->EffectiveLatency = (rta / 1000) + host->jitter * 2 + 10;
1391
1392 if (host->EffectiveLatency < 160) {
1393 R = 93.2 - (host->EffectiveLatency / 40);
1394 } else {
1395 R = 93.2 - ((host->EffectiveLatency - 120) / 10);
1396 }
1397
1398 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
1399 // loss of 5% will be entered as 5).
1400 R = R - (pl * 2.5);
1401 1528
1402 if (R < 0) { 1529 targets_ok += host_check.targets_ok;
1403 R = 0; 1530 targets_warn += host_check.targets_warn;
1404 }
1405 1531
1406 host->score = R; 1532 mp_add_subcheck_to_check(overall, host_check.sc_host);
1407 host->mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
1408 } else {
1409 host->jitter = 0;
1410 host->jitter_min = 0;
1411 host->jitter_max = 0;
1412 host->mos = 0;
1413 }
1414
1415 host->pl = pl;
1416 host->rta = rta;
1417
1418 /* if no new mode selected, use old schema */
1419 if (!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && !order_mode) {
1420 rta_mode = true;
1421 pl_mode = true;
1422 }
1423
1424 /* Check which mode is on and do the warn / Crit stuff */
1425 if (rta_mode) {
1426 if (rta >= crit.rta) {
1427 this_status = STATE_CRITICAL;
1428 status = STATE_CRITICAL;
1429 host->rta_status = STATE_CRITICAL;
1430 } else if (status != STATE_CRITICAL && (rta >= warn.rta)) {
1431 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1432 status = STATE_WARNING;
1433 host->rta_status = STATE_WARNING;
1434 }
1435 }
1436
1437 if (pl_mode) {
1438 if (pl >= crit.pl) {
1439 this_status = STATE_CRITICAL;
1440 status = STATE_CRITICAL;
1441 host->pl_status = STATE_CRITICAL;
1442 } else if (status != STATE_CRITICAL && (pl >= warn.pl)) {
1443 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1444 status = STATE_WARNING;
1445 host->pl_status = STATE_WARNING;
1446 }
1447 }
1448
1449 if (jitter_mode) {
1450 if (host->jitter >= crit.jitter) {
1451 this_status = STATE_CRITICAL;
1452 status = STATE_CRITICAL;
1453 host->jitter_status = STATE_CRITICAL;
1454 } else if (status != STATE_CRITICAL && (host->jitter >= warn.jitter)) {
1455 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1456 status = STATE_WARNING;
1457 host->jitter_status = STATE_WARNING;
1458 }
1459 }
1460
1461 if (mos_mode) {
1462 if (host->mos <= crit.mos) {
1463 this_status = STATE_CRITICAL;
1464 status = STATE_CRITICAL;
1465 host->mos_status = STATE_CRITICAL;
1466 } else if (status != STATE_CRITICAL && (host->mos <= warn.mos)) {
1467 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1468 status = STATE_WARNING;
1469 host->mos_status = STATE_WARNING;
1470 }
1471 }
1472
1473 if (score_mode) {
1474 if (host->score <= crit.score) {
1475 this_status = STATE_CRITICAL;
1476 status = STATE_CRITICAL;
1477 host->score_status = STATE_CRITICAL;
1478 } else if (status != STATE_CRITICAL && (host->score <= warn.score)) {
1479 this_status = (this_status <= STATE_WARNING ? STATE_WARNING : this_status);
1480 status = STATE_WARNING;
1481 host->score_status = STATE_WARNING;
1482 }
1483 }
1484
1485 if (this_status == STATE_WARNING) {
1486 hosts_warn++;
1487 } else if (this_status == STATE_OK) {
1488 hosts_ok++;
1489 }
1490
1491 host = host->next;
1492 } 1533 }
1493 1534
1494 /* this is inevitable */
1495 if (!targets_alive) {
1496 status = STATE_CRITICAL;
1497 }
1498 if (min_hosts_alive > -1) { 1535 if (min_hosts_alive > -1) {
1499 if (hosts_ok >= min_hosts_alive) { 1536 mp_subcheck sc_min_targets_alive = mp_subcheck_init();
1500 status = STATE_OK; 1537 sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK);
1501 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { 1538
1502 status = STATE_WARNING; 1539 if (targets_ok >= min_hosts_alive) {
1503 } 1540 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK);
1504 } 1541 xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok,
1505 printf("%s - ", status_string[status]); 1542 min_hosts_alive);
1506 1543
1507 host = list; 1544 // Overwrite main state here
1508 while (host) { 1545 overall->evaluation_function = &mp_eval_ok;
1509 if (debug) { 1546 } else if ((targets_ok + targets_warn) >= min_hosts_alive) {
1510 puts(""); 1547 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING);
1511 } 1548 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1512 1549 targets_ok + targets_warn, min_hosts_alive);
1513 if (i) { 1550 overall->evaluation_function = &mp_eval_warning;
1514 if (i < targets) { 1551 } else {
1515 printf(" :: "); 1552 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL);
1516 } else { 1553 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1517 printf("\n"); 1554 targets_ok + targets_warn, min_hosts_alive);
1518 } 1555 overall->evaluation_function = &mp_eval_critical;
1519 }
1520
1521 i++;
1522
1523 if (!host->icmp_recv) {
1524 status = STATE_CRITICAL;
1525 host->rtmin = 0;
1526 host->jitter_min = 0;
1527
1528 if (host->flags & FLAG_LOST_CAUSE) {
1529 char address[INET6_ADDRSTRLEN];
1530 parse_address(&host->error_addr, address, sizeof(address));
1531 printf("%s: %s @ %s. rta nan, lost %d%%", host->name, get_icmp_error_msg(host->icmp_type, host->icmp_code), address, 100);
1532 } else { /* not marked as lost cause, so we have no flags for it */
1533 printf("%s: rta nan, lost 100%%", host->name);
1534 }
1535 } else { /* !icmp_recv */
1536 printf("%s", host->name);
1537 /* rta text output */
1538 if (rta_mode) {
1539 if (status == STATE_OK) {
1540 printf(" rta %0.3fms", host->rta / 1000);
1541 } else if (status == STATE_WARNING && host->rta_status == status) {
1542 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)warn.rta / 1000);
1543 } else if (status == STATE_CRITICAL && host->rta_status == status) {
1544 printf(" rta %0.3fms > %0.3fms", (float)host->rta / 1000, (float)crit.rta / 1000);
1545 }
1546 }
1547
1548 /* pl text output */
1549 if (pl_mode) {
1550 if (status == STATE_OK) {
1551 printf(" lost %u%%", host->pl);
1552 } else if (status == STATE_WARNING && host->pl_status == status) {
1553 printf(" lost %u%% > %u%%", host->pl, warn.pl);
1554 } else if (status == STATE_CRITICAL && host->pl_status == status) {
1555 printf(" lost %u%% > %u%%", host->pl, crit.pl);
1556 }
1557 }
1558
1559 /* jitter text output */
1560 if (jitter_mode) {
1561 if (status == STATE_OK) {
1562 printf(" jitter %0.3fms", (float)host->jitter);
1563 } else if (status == STATE_WARNING && host->jitter_status == status) {
1564 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, warn.jitter);
1565 } else if (status == STATE_CRITICAL && host->jitter_status == status) {
1566 printf(" jitter %0.3fms > %0.3fms", (float)host->jitter, crit.jitter);
1567 }
1568 }
1569
1570 /* mos text output */
1571 if (mos_mode) {
1572 if (status == STATE_OK) {
1573 printf(" MOS %0.1f", (float)host->mos);
1574 } else if (status == STATE_WARNING && host->mos_status == status) {
1575 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)warn.mos);
1576 } else if (status == STATE_CRITICAL && host->mos_status == status) {
1577 printf(" MOS %0.1f < %0.1f", (float)host->mos, (float)crit.mos);
1578 }
1579 }
1580
1581 /* score text output */
1582 if (score_mode) {
1583 if (status == STATE_OK) {
1584 printf(" Score %u", (int)host->score);
1585 } else if (status == STATE_WARNING && host->score_status == status) {
1586 printf(" Score %u < %u", (int)host->score, (int)warn.score);
1587 } else if (status == STATE_CRITICAL && host->score_status == status) {
1588 printf(" Score %u < %u", (int)host->score, (int)crit.score);
1589 }
1590 }
1591
1592 /* order statis text output */
1593 if (order_mode) {
1594 if (status == STATE_OK) {
1595 printf(" Packets in order");
1596 } else if (status == STATE_CRITICAL && host->order_status == status) {
1597 printf(" Packets out of order");
1598 }
1599 }
1600 }
1601 host = host->next;
1602 }
1603
1604 /* iterate once more for pretty perfparse output */
1605 if (!(!rta_mode && !pl_mode && !jitter_mode && !score_mode && !mos_mode && order_mode)) {
1606 printf("|");
1607 }
1608 i = 0;
1609 host = list;
1610 while (host) {
1611 if (debug) {
1612 puts("");
1613 }
1614
1615 if (rta_mode) {
1616 if (host->pl < 100) {
1617 printf("%srta=%0.3fms;%0.3f;%0.3f;0; %srtmax=%0.3fms;;;; %srtmin=%0.3fms;;;; ", (targets > 1) ? host->name : "",
1618 host->rta / 1000, (float)warn.rta / 1000, (float)crit.rta / 1000, (targets > 1) ? host->name : "",
1619 (float)host->rtmax / 1000, (targets > 1) ? host->name : "",
1620 (host->rtmin < INFINITY) ? (float)host->rtmin / 1000 : (float)0);
1621 } else {
1622 printf("%srta=U;;;; %srtmax=U;;;; %srtmin=U;;;; ", (targets > 1) ? host->name : "", (targets > 1) ? host->name : "",
1623 (targets > 1) ? host->name : "");
1624 }
1625 }
1626
1627 if (pl_mode) {
1628 printf("%spl=%u%%;%u;%u;0;100 ", (targets > 1) ? host->name : "", host->pl, warn.pl, crit.pl);
1629 }
1630
1631 if (jitter_mode) {
1632 if (host->pl < 100) {
1633 printf("%sjitter_avg=%0.3fms;%0.3f;%0.3f;0; %sjitter_max=%0.3fms;;;; %sjitter_min=%0.3fms;;;; ",
1634 (targets > 1) ? host->name : "", (float)host->jitter, (float)warn.jitter, (float)crit.jitter,
1635 (targets > 1) ? host->name : "", (float)host->jitter_max / 1000, (targets > 1) ? host->name : "",
1636 (float)host->jitter_min / 1000);
1637 } else {
1638 printf("%sjitter_avg=U;;;; %sjitter_max=U;;;; %sjitter_min=U;;;; ", (targets > 1) ? host->name : "",
1639 (targets > 1) ? host->name : "", (targets > 1) ? host->name : "");
1640 }
1641 }
1642
1643 if (mos_mode) {
1644 if (host->pl < 100) {
1645 printf("%smos=%0.1f;%0.1f;%0.1f;0;5 ", (targets > 1) ? host->name : "", (float)host->mos, (float)warn.mos, (float)crit.mos);
1646 } else {
1647 printf("%smos=U;;;; ", (targets > 1) ? host->name : "");
1648 }
1649 }
1650
1651 if (score_mode) {
1652 if (host->pl < 100) {
1653 printf("%sscore=%u;%u;%u;0;100 ", (targets > 1) ? host->name : "", (int)host->score, (int)warn.score, (int)crit.score);
1654 } else {
1655 printf("%sscore=U;;;; ", (targets > 1) ? host->name : "");
1656 }
1657 } 1556 }
1658 1557
1659 host = host->next; 1558 mp_add_subcheck_to_check(overall, sc_min_targets_alive);
1660 }
1661
1662 if (min_hosts_alive > -1) {
1663 if (hosts_ok >= min_hosts_alive) {
1664 status = STATE_OK;
1665 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) {
1666 status = STATE_WARNING;
1667 }
1668 } 1559 }
1669 1560
1670 /* finish with an empty line */ 1561 /* finish with an empty line */
1671 puts("");
1672 if (debug) { 1562 if (debug) {
1673 printf("targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n", targets, targets_alive, hosts_ok, 1563 printf(
1674 hosts_warn, min_hosts_alive); 1564 "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1565 number_of_targets, targets_alive(number_of_targets, program_state->targets_down),
1566 targets_ok, targets_warn, min_hosts_alive);
1675 } 1567 }
1676
1677 exit(status);
1678} 1568}
1679 1569
1680static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { 1570static time_t get_timevaldiff(const struct timeval earlier, const struct timeval later) {
1681 u_int ret;
1682 struct timeval now;
1683
1684 if (!later) {
1685 gettimeofday(&now, &tz);
1686 later = &now;
1687 }
1688 if (!early) {
1689 early = &prog_start;
1690 }
1691
1692 /* if early > later we return 0 so as to indicate a timeout */ 1571 /* if early > later we return 0 so as to indicate a timeout */
1693 if (early->tv_sec > later->tv_sec || (early->tv_sec == later->tv_sec && early->tv_usec > later->tv_usec)) { 1572 if (earlier.tv_sec > later.tv_sec ||
1573 (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) {
1694 return 0; 1574 return 0;
1695 } 1575 }
1696 ret = (later->tv_sec - early->tv_sec) * 1000000; 1576
1697 ret += later->tv_usec - early->tv_usec; 1577 time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000;
1578 ret += later.tv_usec - earlier.tv_usec;
1698 1579
1699 return ret; 1580 return ret;
1700} 1581}
1701 1582
1702static int add_target_ip(char *arg, struct sockaddr_storage *in) { 1583static time_t get_timevaldiff_to_now(struct timeval earlier) {
1703 struct rta_host *host; 1584 struct timeval now;
1704 struct sockaddr_in *sin, *host_sin; 1585 gettimeofday(&now, NULL);
1705 struct sockaddr_in6 *sin6, *host_sin6; 1586
1587 return get_timevaldiff(earlier, now);
1588}
1589
1590static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) {
1591 assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6));
1706 1592
1707 if (address_family == AF_INET) { 1593 if (debug) {
1708 sin = (struct sockaddr_in *)in; 1594 char straddr[INET6_ADDRSTRLEN];
1595 parse_address((&address), straddr, sizeof(straddr));
1596 printf("add_target_ip called with: %s\n", straddr);
1597 }
1598 struct sockaddr_in *sin;
1599 struct sockaddr_in6 *sin6;
1600 if (address.ss_family == AF_INET) {
1601 sin = (struct sockaddr_in *)&address;
1602 } else if (address.ss_family == AF_INET6) {
1603 sin6 = (struct sockaddr_in6 *)&address;
1709 } else { 1604 } else {
1710 sin6 = (struct sockaddr_in6 *)in; 1605 assert(false);
1711 } 1606 }
1712 1607
1608 add_target_ip_wrapper result = {
1609 .error_code = OK,
1610 .target = NULL,
1611 };
1612
1713 /* disregard obviously stupid addresses 1613 /* disregard obviously stupid addresses
1714 * (I didn't find an ipv6 equivalent to INADDR_NONE) */ 1614 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
1715 if (((address_family == AF_INET && (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) || 1615 if (((address.ss_family == AF_INET &&
1716 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { 1616 (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1717 return -1; 1617 (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1618 result.error_code = ERROR;
1619 return result;
1718 } 1620 }
1719 1621
1720 /* no point in adding two identical IP's, so don't. ;) */ 1622 // get string representation of address
1721 host = list; 1623 char straddr[INET6_ADDRSTRLEN];
1722 while (host) { 1624 parse_address((&address), straddr, sizeof(straddr));
1723 host_sin = (struct sockaddr_in *)&host->saddr_in;
1724 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
1725
1726 if ((address_family == AF_INET && host_sin->sin_addr.s_addr == sin->sin_addr.s_addr) ||
1727 (address_family == AF_INET6 && host_sin6->sin6_addr.s6_addr == sin6->sin6_addr.s6_addr)) {
1728 if (debug) {
1729 printf("Identical IP already exists. Not adding %s\n", arg);
1730 }
1731 return -1;
1732 }
1733 host = host->next;
1734 }
1735 1625
1736 /* add the fresh ip */ 1626 /* add the fresh ip */
1737 host = (struct rta_host *)malloc(sizeof(struct rta_host)); 1627 ping_target *target = (ping_target *)calloc(1, sizeof(ping_target));
1738 if (!host) { 1628 if (!target) {
1739 char straddr[INET6_ADDRSTRLEN]; 1629 crash("add_target_ip(%s): malloc(%lu) failed", straddr, sizeof(ping_target));
1740 parse_address((struct sockaddr_storage *)&in, straddr, sizeof(straddr));
1741 crash("add_target_ip(%s, %s): malloc(%lu) failed", arg, straddr, sizeof(struct rta_host));
1742 } 1630 }
1743 memset(host, 0, sizeof(struct rta_host));
1744 1631
1745 /* set the values. use calling name for output */ 1632 ping_target_create_wrapper target_wrapper = ping_target_create(address);
1746 host->name = strdup(arg);
1747 1633
1748 /* fill out the sockaddr_storage struct */ 1634 if (target_wrapper.errorcode == OK) {
1749 if (address_family == AF_INET) { 1635 *target = target_wrapper.host;
1750 host_sin = (struct sockaddr_in *)&host->saddr_in; 1636 result.target = target;
1751 host_sin->sin_family = AF_INET;
1752 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1753 } else { 1637 } else {
1754 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in; 1638 result.error_code = target_wrapper.errorcode;
1755 host_sin6->sin6_family = AF_INET6;
1756 memcpy(host_sin6->sin6_addr.s6_addr, sin6->sin6_addr.s6_addr, sizeof host_sin6->sin6_addr.s6_addr);
1757 }
1758
1759 /* fill out the sockaddr_in struct */
1760 host->rtmin = INFINITY;
1761 host->rtmax = 0;
1762 host->jitter = 0;
1763 host->jitter_max = 0;
1764 host->jitter_min = INFINITY;
1765 host->last_tdiff = 0;
1766 host->order_status = STATE_OK;
1767 host->last_icmp_seq = 0;
1768 host->rta_status = 0;
1769 host->pl_status = 0;
1770 host->jitter_status = 0;
1771 host->mos_status = 0;
1772 host->score_status = 0;
1773 host->pl_status = 0;
1774
1775 if (!list) {
1776 list = cursor = host;
1777 } else {
1778 cursor->next = host;
1779 } 1639 }
1780 1640
1781 cursor = host; 1641 return result;
1782 targets++;
1783
1784 return 0;
1785} 1642}
1786 1643
1787/* wrapper for add_target_ip */ 1644/* wrapper for add_target_ip */
1788static int add_target(char *arg) { 1645static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode,
1789 int error, result = -1; 1646 sa_family_t enforced_proto) {
1790 struct sockaddr_storage ip; 1647 if (debug > 0) {
1791 struct addrinfo hints, *res, *p; 1648 printf("add_target called with argument %s\n", arg);
1792 struct sockaddr_in *sin; 1649 }
1793 struct sockaddr_in6 *sin6; 1650
1794 1651 struct sockaddr_storage address_storage = {};
1795 switch (address_family) { 1652 struct sockaddr_in *sin = NULL;
1796 case -1: 1653 struct sockaddr_in6 *sin6 = NULL;
1797 /* -4 and -6 are not specified on cmdline */ 1654 int error_code = -1;
1798 address_family = AF_INET; 1655
1799 sin = (struct sockaddr_in *)&ip; 1656 switch (enforced_proto) {
1800 result = inet_pton(address_family, arg, &sin->sin_addr); 1657 case AF_UNSPEC:
1801#ifdef USE_IPV6 1658 /*
1802 if (result != 1) { 1659 * no enforced protocol family
1803 address_family = AF_INET6; 1660 * try to parse the address with each one
1804 sin6 = (struct sockaddr_in6 *)&ip; 1661 */
1805 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1662 sin = (struct sockaddr_in *)&address_storage;
1806 } 1663 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1807#endif 1664 address_storage.ss_family = AF_INET;
1808 /* If we don't find any valid addresses, we still don't know the address_family */ 1665
1809 if (result != 1) { 1666 if (error_code != 1) {
1810 address_family = -1; 1667 sin6 = (struct sockaddr_in6 *)&address_storage;
1668 error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr);
1669 address_storage.ss_family = AF_INET6;
1811 } 1670 }
1812 break; 1671 break;
1813 case AF_INET: 1672 case AF_INET:
1814 sin = (struct sockaddr_in *)&ip; 1673 sin = (struct sockaddr_in *)&address_storage;
1815 result = inet_pton(address_family, arg, &sin->sin_addr); 1674 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1675 address_storage.ss_family = AF_INET;
1816 break; 1676 break;
1817 case AF_INET6: 1677 case AF_INET6:
1818 sin6 = (struct sockaddr_in6 *)&ip; 1678 sin6 = (struct sockaddr_in6 *)&address_storage;
1819 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1679 error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr);
1680 address_storage.ss_family = AF_INET6;
1820 break; 1681 break;
1821 default: 1682 default:
1822 crash("Address family not supported"); 1683 crash("Address family not supported");
1823 } 1684 }
1824 1685
1825 /* don't resolve if we don't have to */ 1686 add_target_wrapper result = {
1826 if (result == 1) { 1687 .error_code = OK,
1688 .targets = NULL,
1689 .has_v4 = false,
1690 .has_v6 = false,
1691 };
1692
1693 // if error_code == 1 the address was a valid address parsed above
1694 if (error_code == 1) {
1827 /* don't add all ip's if we were given a specific one */ 1695 /* don't add all ip's if we were given a specific one */
1828 return add_target_ip(arg, &ip); 1696 add_target_ip_wrapper targeted = add_target_ip(address_storage);
1829 } else { 1697
1830 errno = 0; 1698 if (targeted.error_code != OK) {
1831 memset(&hints, 0, sizeof(hints)); 1699 result.error_code = ERROR;
1832 if (address_family == -1) { 1700 return result;
1833 hints.ai_family = AF_UNSPEC;
1834 } else {
1835 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1836 } 1701 }
1837 hints.ai_socktype = SOCK_RAW; 1702
1838 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { 1703 if (targeted.target->address.ss_family == AF_INET) {
1839 errno = 0; 1704 result.has_v4 = true;
1840 crash("Failed to resolve %s: %s", arg, gai_strerror(error)); 1705 } else if (targeted.target->address.ss_family == AF_INET6) {
1841 return -1; 1706 result.has_v6 = true;
1707 } else {
1708 assert(false);
1842 } 1709 }
1843 address_family = res->ai_family; 1710 result.targets = targeted.target;
1711 result.number_of_targets = 1;
1712 return result;
1713 }
1714
1715 struct addrinfo hints = {};
1716 errno = 0;
1717 hints.ai_family = enforced_proto;
1718 hints.ai_socktype = SOCK_RAW;
1719
1720 int error;
1721 struct addrinfo *res;
1722 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1723 errno = 0;
1724 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1725 result.error_code = ERROR;
1726 return result;
1844 } 1727 }
1845 1728
1846 /* possibly add all the IP's as targets */ 1729 /* possibly add all the IP's as targets */
1847 for (p = res; p != NULL; p = p->ai_next) { 1730 for (struct addrinfo *address = res; address != NULL; address = address->ai_next) {
1848 memcpy(&ip, p->ai_addr, p->ai_addrlen); 1731 struct sockaddr_storage temporary_ip_address;
1849 add_target_ip(arg, &ip); 1732 memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen);
1733
1734 add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address);
1735
1736 if (tmp.error_code != OK) {
1737 // No proper error handling
1738 // What to do?
1739 } else {
1740 if (result.targets == NULL) {
1741 result.targets = tmp.target;
1742 result.number_of_targets = 1;
1743 } else {
1744 result.number_of_targets += ping_target_list_append(result.targets, tmp.target);
1745 }
1746 if (address->ai_family == AF_INET) {
1747 result.has_v4 = true;
1748 } else if (address->ai_family == AF_INET6) {
1749 result.has_v6 = true;
1750 }
1751 }
1850 1752
1851 /* this is silly, but it works */ 1753 /* this is silly, but it works */
1852 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1754 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1855,20 +1757,22 @@ static int add_target(char *arg) {
1855 } 1757 }
1856 continue; 1758 continue;
1857 } 1759 }
1760
1761 // Abort after first hit if not in of the modes above
1858 break; 1762 break;
1859 } 1763 }
1860 freeaddrinfo(res); 1764 freeaddrinfo(res);
1861 1765
1862 return 0; 1766 return result;
1863} 1767}
1864 1768
1865static void set_source_ip(char *arg) { 1769static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) {
1866 struct sockaddr_in src; 1770 struct sockaddr_in src;
1867 1771
1868 memset(&src, 0, sizeof(src)); 1772 memset(&src, 0, sizeof(src));
1869 src.sin_family = address_family; 1773 src.sin_family = addr_family;
1870 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { 1774 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) {
1871 src.sin_addr.s_addr = get_ip_address(arg); 1775 src.sin_addr.s_addr = get_ip_address(arg, icmp_sock);
1872 } 1776 }
1873 if (bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) { 1777 if (bind(icmp_sock, (struct sockaddr *)&src, sizeof(src)) == -1) {
1874 crash("Cannot bind to IP address %s", arg); 1778 crash("Cannot bind to IP address %s", arg);
@@ -1876,10 +1780,10 @@ static void set_source_ip(char *arg) {
1876} 1780}
1877 1781
1878/* TODO: Move this to netutils.c and also change check_dhcp to use that. */ 1782/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1879static in_addr_t get_ip_address(const char *ifname) { 1783static in_addr_t get_ip_address(const char *ifname, const int icmp_sock) {
1880 // TODO: Rewrite this so the function return an error and we exit somewhere else 1784 // TODO: Rewrite this so the function return an error and we exit somewhere else
1881 struct sockaddr_in ip; 1785 struct sockaddr_in ip_address;
1882 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy 1786 ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1883#if defined(SIOCGIFADDR) 1787#if defined(SIOCGIFADDR)
1884 struct ifreq ifr; 1788 struct ifreq ifr;
1885 1789
@@ -1891,13 +1795,13 @@ static in_addr_t get_ip_address(const char *ifname) {
1891 crash("Cannot determine IP address of interface %s", ifname); 1795 crash("Cannot determine IP address of interface %s", ifname);
1892 } 1796 }
1893 1797
1894 memcpy(&ip, &ifr.ifr_addr, sizeof(ip)); 1798 memcpy(&ip_address, &ifr.ifr_addr, sizeof(ip_address));
1895#else 1799#else
1896 (void)ifname; 1800 (void)ifname;
1897 errno = 0; 1801 errno = 0;
1898 crash("Cannot get interface IP address on this platform."); 1802 crash("Cannot get interface IP address on this platform.");
1899#endif 1803#endif
1900 return ip.sin_addr.s_addr; 1804 return ip_address.sin_addr.s_addr;
1901} 1805}
1902 1806
1903/* 1807/*
@@ -1906,103 +1810,127 @@ static in_addr_t get_ip_address(const char *ifname) {
1906 * s = seconds 1810 * s = seconds
1907 * return value is in microseconds 1811 * return value is in microseconds
1908 */ 1812 */
1909static u_int get_timevar(const char *str) { 1813static get_timevar_wrapper get_timevar(const char *str) {
1910 char p, u, *ptr; 1814 get_timevar_wrapper result = {
1911 size_t len; 1815 .error_code = OK,
1912 u_int i, d; /* integer and decimal, respectively */ 1816 .time_range = 0,
1913 u_int factor = 1000; /* default to milliseconds */ 1817 };
1914 1818
1915 if (!str) { 1819 if (!str) {
1916 return 0; 1820 result.error_code = ERROR;
1821 return result;
1917 } 1822 }
1918 len = strlen(str); 1823
1824 size_t len = strlen(str);
1919 if (!len) { 1825 if (!len) {
1920 return 0; 1826 result.error_code = ERROR;
1827 return result;
1921 } 1828 }
1922 1829
1923 /* unit might be given as ms|m (millisec), 1830 /* unit might be given as ms|m (millisec),
1924 * us|u (microsec) or just plain s, for seconds */ 1831 * us|u (microsec) or just plain s, for seconds */
1925 p = '\0'; 1832 char tmp = '\0';
1926 u = str[len - 1]; 1833 char unit = str[len - 1];
1927 if (len >= 2 && !isdigit((int)str[len - 2])) { 1834 if (len >= 2 && !isdigit((int)str[len - 2])) {
1928 p = str[len - 2]; 1835 tmp = str[len - 2];
1929 } 1836 }
1930 if (p && u == 's') { 1837
1931 u = p; 1838 if (tmp && unit == 's') {
1932 } else if (!p) { 1839 unit = tmp;
1933 p = u; 1840 } else if (!tmp) {
1841 tmp = unit;
1934 } 1842 }
1843
1935 if (debug > 2) { 1844 if (debug > 2) {
1936 printf("evaluating %s, u: %c, p: %c\n", str, u, p); 1845 printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp);
1937 } 1846 }
1938 1847
1939 if (u == 'u') { 1848 unsigned int factor = 1000; /* default to milliseconds */
1849 if (unit == 'u') {
1940 factor = 1; /* microseconds */ 1850 factor = 1; /* microseconds */
1941 } else if (u == 'm') { 1851 } else if (unit == 'm') {
1942 factor = 1000; /* milliseconds */ 1852 factor = 1000; /* milliseconds */
1943 } else if (u == 's') { 1853 } else if (unit == 's') {
1944 factor = 1000000; /* seconds */ 1854 factor = 1000000; /* seconds */
1945 } 1855 }
1856
1946 if (debug > 2) { 1857 if (debug > 2) {
1947 printf("factor is %u\n", factor); 1858 printf("factor is %u\n", factor);
1948 } 1859 }
1949 1860
1950 i = strtoul(str, &ptr, 0); 1861 char *ptr;
1862 unsigned long pre_radix;
1863 pre_radix = strtoul(str, &ptr, 0);
1951 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { 1864 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) {
1952 return i * factor; 1865 result.time_range = (unsigned int)(pre_radix * factor);
1866 return result;
1953 } 1867 }
1954 1868
1955 /* time specified in usecs can't have decimal points, so ignore them */ 1869 /* time specified in usecs can't have decimal points, so ignore them */
1956 if (factor == 1) { 1870 if (factor == 1) {
1957 return i; 1871 result.time_range = (unsigned int)pre_radix;
1872 return result;
1958 } 1873 }
1959 1874
1960 d = strtoul(ptr + 1, NULL, 0); 1875 /* integer and decimal, respectively */
1876 unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0);
1961 1877
1962 /* d is decimal, so get rid of excess digits */ 1878 /* d is decimal, so get rid of excess digits */
1963 while (d >= factor) { 1879 while (post_radix >= factor) {
1964 d /= 10; 1880 post_radix /= 10;
1965 } 1881 }
1966 1882
1967 /* the last parenthesis avoids floating point exceptions. */ 1883 /* the last parenthesis avoids floating point exceptions. */
1968 return ((i * factor) + (d * (factor / 10))); 1884 result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10)));
1885 return result;
1969} 1886}
1970 1887
1971/* not too good at checking errors, but it'll do (main() should barfe on -1) */ 1888static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) {
1972static int get_threshold(char *str, threshold *th) { 1889 get_threshold_wrapper result = {
1973 char *p = NULL, i = 0; 1890 .errorcode = OK,
1891 .threshold = threshold,
1892 };
1974 1893
1975 if (!str || !strlen(str) || !th) { 1894 if (!str || !strlen(str)) {
1976 return -1; 1895 result.errorcode = ERROR;
1896 return result;
1977 } 1897 }
1978 1898
1979 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ 1899 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1980 p = &str[strlen(str) - 1]; 1900 bool is_at_last_char = false;
1981 while (p != &str[1]) { 1901 char *tmp = &str[strlen(str) - 1];
1982 if (*p == '%') { 1902 while (tmp != &str[1]) {
1983 *p = '\0'; 1903 if (*tmp == '%') {
1984 } else if (*p == ',' && i) { 1904 *tmp = '\0';
1985 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1905 } else if (*tmp == ',' && is_at_last_char) {
1986 th->pl = (unsigned char)strtoul(p + 1, NULL, 0); 1906 *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */
1907 result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0);
1987 break; 1908 break;
1988 } 1909 }
1989 i = 1; 1910 is_at_last_char = true;
1990 p--; 1911 tmp--;
1991 } 1912 }
1992 th->rta = get_timevar(str);
1993 1913
1994 if (!th->rta) { 1914 get_timevar_wrapper parsed_time = get_timevar(str);
1995 return -1; 1915
1916 if (parsed_time.error_code == OK) {
1917 result.threshold.rta = parsed_time.time_range;
1918 } else {
1919 if (debug > 1) {
1920 printf("%s: failed to parse rta threshold\n", __FUNCTION__);
1921 }
1922 result.errorcode = ERROR;
1923 return result;
1996 } 1924 }
1997 1925
1998 if (th->rta > MAXTTL * 1000000) { 1926 if (result.threshold.rta > MAXTTL * 1000000) {
1999 th->rta = MAXTTL * 1000000; 1927 result.threshold.rta = MAXTTL * 1000000;
2000 } 1928 }
2001 if (th->pl > 100) { 1929 if (result.threshold.pl > 100) {
2002 th->pl = 100; 1930 result.threshold.pl = 100;
2003 } 1931 }
2004 1932
2005 return 0; 1933 return result;
2006} 1934}
2007 1935
2008/* 1936/*
@@ -2013,184 +1941,537 @@ static int get_threshold(char *str, threshold *th) {
2013 * @param[in] length strlen(str) 1941 * @param[in] length strlen(str)
2014 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned 1942 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned
2015 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned 1943 * @param[out] crit Pointer to the crit threshold struct to which the values should be assigned
2016 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score (exclusively) 1944 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score
1945 * (exclusively)
2017 */ 1946 */
2018static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { 1947static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
2019 if (!str || !length || !warn || !crit) { 1948 check_icmp_threshold crit, threshold_mode mode) {
2020 return false; 1949 get_threshold2_wrapper result = {
1950 .errorcode = OK,
1951 .warn = warn,
1952 .crit = crit,
1953 };
1954
1955 if (!str || !length) {
1956 result.errorcode = ERROR;
1957 return result;
2021 } 1958 }
2022 1959
2023 // p points to the last char in str 1960 // p points to the last char in str
2024 char *p = &str[length - 1]; 1961 char *work_pointer = &str[length - 1];
2025 1962
2026 // first_iteration is bof-stop on stupid libc's 1963 // first_iteration is bof-stop on stupid libc's
2027 bool first_iteration = true; 1964 bool first_iteration = true;
2028 1965
2029 while (p != &str[0]) { 1966 while (work_pointer != &str[0]) {
2030 if ((*p == 'm') || (*p == '%')) { 1967 if ((*work_pointer == 'm') || (*work_pointer == '%')) {
2031 *p = '\0'; 1968 *work_pointer = '\0';
2032 } else if (*p == ',' && !first_iteration) { 1969 } else if (*work_pointer == ',' && !first_iteration) {
2033 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1970 *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */
2034 1971
2035 char *start_of_value = p + 1; 1972 char *start_of_value = work_pointer + 1;
2036 1973
2037 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { 1974 parse_threshold2_helper_wrapper tmp =
2038 return false; 1975 parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode);
1976 if (tmp.errorcode != OK) {
1977 result.errorcode = ERROR;
1978 return result;
2039 } 1979 }
1980 result.crit = tmp.result;
2040 } 1981 }
2041 first_iteration = false; 1982 first_iteration = false;
2042 p--; 1983 work_pointer--;
2043 } 1984 }
2044 1985
2045 return parse_threshold2_helper(p, strlen(p), warn, mode); 1986 parse_threshold2_helper_wrapper tmp =
1987 parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode);
1988 if (tmp.errorcode != OK) {
1989 result.errorcode = ERROR;
1990 } else {
1991 result.warn = tmp.result;
1992 }
1993 return result;
2046} 1994}
2047 1995
2048static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { 1996static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
1997 size_t length,
1998 check_icmp_threshold thr,
1999 threshold_mode mode) {
2049 char *resultChecker = {0}; 2000 char *resultChecker = {0};
2001 parse_threshold2_helper_wrapper result = {
2002 .result = thr,
2003 .errorcode = OK,
2004 };
2050 2005
2051 switch (mode) { 2006 switch (mode) {
2052 case const_rta_mode: 2007 case const_rta_mode:
2053 thr->rta = strtod(s, &resultChecker) * 1000; 2008 result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000);
2054 break; 2009 break;
2055 case const_packet_loss_mode: 2010 case const_packet_loss_mode:
2056 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); 2011 result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0);
2057 break; 2012 break;
2058 case const_jitter_mode: 2013 case const_jitter_mode:
2059 thr->jitter = strtod(s, &resultChecker); 2014 result.result.jitter = strtod(threshold_string, &resultChecker);
2060
2061 break; 2015 break;
2062 case const_mos_mode: 2016 case const_mos_mode:
2063 thr->mos = strtod(s, &resultChecker); 2017 result.result.mos = strtod(threshold_string, &resultChecker);
2064 break; 2018 break;
2065 case const_score_mode: 2019 case const_score_mode:
2066 thr->score = strtod(s, &resultChecker); 2020 result.result.score = strtod(threshold_string, &resultChecker);
2067 break; 2021 break;
2068 } 2022 }
2069 2023
2070 if (resultChecker == s) { 2024 if (resultChecker == threshold_string) {
2071 // Failed to parse 2025 // Failed to parse
2072 return false; 2026 result.errorcode = ERROR;
2027 return result;
2073 } 2028 }
2074 2029
2075 if (resultChecker != (s + length)) { 2030 if (resultChecker != (threshold_string + length)) {
2076 // Trailing symbols 2031 // Trailing symbols
2077 return false; 2032 result.errorcode = ERROR;
2078 } 2033 }
2079 2034
2080 return true; 2035 return result;
2081} 2036}
2082 2037
2083unsigned short icmp_checksum(uint16_t *p, size_t n) { 2038unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) {
2084 unsigned short cksum;
2085 long sum = 0; 2039 long sum = 0;
2086 2040
2087 /* sizeof(uint16_t) == 2 */ 2041 /* sizeof(uint16_t) == 2 */
2088 while (n >= 2) { 2042 while (packet_size >= 2) {
2089 sum += *(p++); 2043 sum += *(packet++);
2090 n -= 2; 2044 packet_size -= 2;
2091 } 2045 }
2092 2046
2093 /* mop up the occasional odd byte */ 2047 /* mop up the occasional odd byte */
2094 if (n == 1) { 2048 if (packet_size == 1) {
2095 sum += *((uint8_t *)p - 1); 2049 sum += *((uint8_t *)packet - 1);
2096 } 2050 }
2097 2051
2098 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 2052 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2099 sum += (sum >> 16); /* add carry */ 2053 sum += (sum >> 16); /* add carry */
2100 cksum = ~sum; /* ones-complement, trunc to 16 bits */ 2054 unsigned short cksum;
2055 cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */
2101 2056
2102 return cksum; 2057 return cksum;
2103} 2058}
2104 2059
2105void print_help(void) { 2060void print_help(void) {
2106 /*print_revision (progname);*/ /* FIXME: Why? */ 2061 // print_revision (progname); /* FIXME: Why? */
2107 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n"); 2062 printf("Copyright (c) 2005 Andreas Ericsson <ae@op5.se>\n");
2108 2063
2109 printf(COPYRIGHT, copyright, email); 2064 printf(COPYRIGHT, copyright, email);
2110 2065
2111 printf("\n\n");
2112
2113 print_usage(); 2066 print_usage();
2114 2067
2115 printf(UT_HELP_VRSN); 2068 printf(UT_HELP_VRSN);
2116 printf(UT_EXTRA_OPTS); 2069 printf(UT_EXTRA_OPTS);
2117 2070
2118 printf(" %s\n", "-H"); 2071 printf(" -H, --Host=HOST\n");
2119 printf(" %s\n", _("specify a target")); 2072 printf(" %s\n",
2120 printf(" %s\n", "[-4|-6]"); 2073 _("specify a target, might be one of: resolveable name | IPv6 address | IPv4 address\n"
2121 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); 2074 " (required, can be given multiple times)"));
2122 printf(" %s\n", "-w"); 2075 printf(" %s\n", "[-4|-6], [--ipv4-only|--ipv6-only]");
2123 printf(" %s", _("warning threshold (currently ")); 2076 printf(" %s\n", _("Use IPv4 or IPv6 only to communicate with the targets"));
2124 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 2077 printf(" %s\n", "-w, --warning=WARN_VALUE");
2125 printf(" %s\n", "-c"); 2078 printf(" %s", _("warning threshold (default "));
2126 printf(" %s", _("critical threshold (currently ")); 2079 printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL);
2127 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); 2080 printf(" %s\n", "-c, --critical=CRIT_VALUE");
2128 2081 printf(" %s", _("critical threshold (default "));
2129 printf(" %s\n", "-R"); 2082 printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL);
2130 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); 2083
2131 printf(" %s\n", "-P"); 2084 printf(" %s\n", "-R, --rta-mode-thresholds=RTA_THRESHOLDS");
2085 printf(" %s\n",
2086 _("RTA (round trip average) mode warning,critical, ex. 100ms,200ms unit in ms"));
2087 printf(" %s\n", "-P, --packet-loss-mode-thresholds=PACKET_LOSS_THRESHOLD");
2132 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); 2088 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2133 printf(" %s\n", "-J"); 2089 printf(" %s\n", "-J, --jitter-mode-thresholds=JITTER_MODE_THRESHOLD");
2134 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms ")); 2090 printf(" %s\n", _("jitter mode warning,critical, ex. 40.000ms,50.000ms , unit in ms "));
2135 printf(" %s\n", "-M"); 2091 printf(" %s\n", "-M, --mos-mode-thresholds=MOS_MODE_THRESHOLD");
2136 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0")); 2092 printf(" %s\n", _("MOS mode, between 0 and 4.4 warning,critical, ex. 3.5,3.0"));
2137 printf(" %s\n", "-S"); 2093 printf(" %s\n", "-S, --score-mode-thresholds=SCORE_MODE_THRESHOLD");
2138 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 ")); 2094 printf(" %s\n", _("score mode, max value 100 warning,critical, ex. 80,70 "));
2139 printf(" %s\n", "-O"); 2095 printf(" %s\n", "-O, --out-of-order-packets");
2140 printf(" %s\n", _("detect out of order ICMP packts ")); 2096 printf(
2141 printf(" %s\n", "-H"); 2097 " %s\n",
2142 printf(" %s\n", _("specify a target")); 2098 _("detect out of order ICMP packets, if such packets are found, the result is CRITICAL"));
2143 printf(" %s\n", "-s"); 2099 printf(" %s\n", "[-n|-p], --number-of-packets=NUMBER_OF_PACKETS");
2144 printf(" %s\n", _("specify a source IP address or device name")); 2100 printf(" %s", _("number of packets to send (default "));
2145 printf(" %s\n", "-n"); 2101 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2146 printf(" %s", _("number of packets to send (currently ")); 2102
2147 printf("%u)\n", packets);
2148 printf(" %s\n", "-p");
2149 printf(" %s", _("number of packets to send (currently "));
2150 printf("%u)\n", packets);
2151 printf(" %s\n", "-i"); 2103 printf(" %s\n", "-i");
2152 printf(" %s", _("max packet interval (currently ")); 2104 printf(" %s", _("[DEPRECATED] packet interval (default "));
2153 printf("%0.3fms)\n", (float)pkt_interval / 1000); 2105 printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000);
2154 printf(" %s\n", "-I"); 2106 printf(" %s", _("This option was never actually used and is just mentioned here for "
2155 printf(" %s", _("max target interval (currently ")); 2107 "historical purposes\n"));
2156 printf("%0.3fms)\n", (float)target_interval / 1000); 2108
2157 printf(" %s\n", "-m"); 2109 printf(" %s\n", "-I, --target-interval=TARGET_INTERVAL");
2158 printf(" %s", _("number of alive hosts required for success")); 2110 printf(" %s%0.3fms)\n The time interval to wait in between one target and the next\n",
2111 _("max target interval (default "), (float)DEFAULT_TARGET_INTERVAL / 1000);
2112 printf(" %s\n", "-m, --minimal-host-alive=MIN_ALIVE");
2113 printf(" %s", _("number of alive hosts required for success. If less than MIN_ALIVE hosts "
2114 "are OK, but MIN_ALIVE hosts are WARNING or OK, WARNING, else CRITICAL"));
2159 printf("\n"); 2115 printf("\n");
2160 printf(" %s\n", "-l"); 2116 printf(" %s\n", "-l, --outgoing-ttl=OUTGOING_TTL");
2161 printf(" %s", _("TTL on outgoing packets (currently ")); 2117 printf(" %s", _("TTL on outgoing packets (default "));
2162 printf("%u)\n", ttl); 2118 printf("%u)\n", DEFAULT_TTL);
2163 printf(" %s\n", "-t"); 2119 printf(" %s\n", "-b, --size=SIZE");
2164 printf(" %s", _("timeout value (seconds, currently ")); 2120 printf(" %s\n", _("Number of icmp ping data bytes to send"));
2165 printf("%u)\n", timeout); 2121 printf(" %s %lu + %d)\n", _("Packet size will be SIZE + icmp header (default"),
2166 printf(" %s\n", "-b"); 2122 DEFAULT_PING_DATA_SIZE, ICMP_MINLEN);
2167 printf(" %s\n", _("Number of icmp data bytes to send")); 2123 printf(" %s\n", "-v, --verbose");
2168 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); 2124 printf(" %s\n", _("Verbosity, can be given multiple times (for debugging)"));
2169 printf(" %s\n", "-v"); 2125
2170 printf(" %s\n", _("verbose")); 2126 printf(UT_OUTPUT_FORMAT);
2127
2171 printf("\n"); 2128 printf("\n");
2172 printf("%s\n", _("Notes:")); 2129 printf("%s\n", _("Notes:"));
2173 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P")); 2130 printf(" %s\n", _("If none of R,P,J,M,S or O is specified, default behavior is -R -P"));
2174 printf(" %s\n", _("The -H switch is optional. Naming a host (or several) to check is not.")); 2131 printf(" %s\n", _("Naming a host (or several) to check is not."));
2175 printf("\n"); 2132 printf("\n");
2176 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); 2133 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%"));
2177 printf(" %s\n", _("packet loss. The default values should work well for most users.")); 2134 printf(" %s\n", _("packet loss. The default values should work well for most users."));
2178 printf(" %s\n", _("You can specify different RTA factors using the standardized abbreviations")); 2135 printf(" %s\n",
2179 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); 2136 _("You can specify different RTA factors using the standardized abbreviations"));
2180 /* -d not yet implemented */ 2137 printf(" %s\n",
2181 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); 2138 _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2182 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent."));
2183 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/
2184 printf("\n");
2185 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2186 /* printf ("%s\n", _("Long options are currently unsupported."));
2187 printf ("%s\n", _("Options marked with * require an argument"));
2188 */
2189 2139
2190 printf(UT_SUPPORT); 2140 printf(UT_SUPPORT);
2191} 2141}
2192 2142
2193void print_usage(void) { 2143void print_usage(void) {
2194 printf("%s\n", _("Usage:")); 2144 printf("%s\n", _("Usage:"));
2195 printf(" %s [options] [-H] host1 host2 hostN\n", progname); 2145 printf(" %s [options] [-H host1 [-H host2 [-H hostN]]]\n", progname);
2146}
2147
2148static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
2149 sa_family_t enforced_proto) {
2150 if (debug) {
2151 printf("add_host called with argument %s\n", arg);
2152 }
2153
2154 add_host_wrapper result = {
2155 .error_code = OK,
2156 .host = check_icmp_target_container_init(),
2157 .has_v4 = false,
2158 .has_v6 = false,
2159 };
2160
2161 add_target_wrapper targets = add_target(arg, mode, enforced_proto);
2162
2163 if (targets.error_code != OK) {
2164 result.error_code = targets.error_code;
2165 return result;
2166 }
2167
2168 result.has_v4 = targets.has_v4;
2169 result.has_v6 = targets.has_v6;
2170
2171 result.host = check_icmp_target_container_init();
2172
2173 result.host.name = strdup(arg);
2174 result.host.target_list = targets.targets;
2175 result.host.number_of_targets = targets.number_of_targets;
2176
2177 return result;
2178}
2179
2180mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
2181 check_icmp_threshold warn, check_icmp_threshold crit) {
2182 /* if no new mode selected, use old schema */
2183 if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode &&
2184 !modes.mos_mode && !modes.order_mode) {
2185 modes.rta_mode = true;
2186 modes.pl_mode = true;
2187 }
2188
2189 mp_subcheck result = mp_subcheck_init();
2190 result = mp_set_subcheck_default_state(result, STATE_OK);
2191
2192 char address[INET6_ADDRSTRLEN];
2193 memset(address, 0, INET6_ADDRSTRLEN);
2194 parse_address(&target.address, address, sizeof(address));
2195
2196 xasprintf(&result.output, "%s", address);
2197
2198 double packet_loss;
2199 time_t rta;
2200 if (!target.icmp_recv) {
2201 /* rta 0 is of course not entirely correct, but will still show up
2202 * conspicuously as missing entries in perfparse and cacti */
2203 packet_loss = 100;
2204 rta = 0;
2205 result = mp_set_subcheck_state(result, STATE_CRITICAL);
2206 /* up the down counter if not already counted */
2207
2208 if (target.flags & FLAG_LOST_CAUSE) {
2209 xasprintf(&result.output, "%s: %s @ %s", result.output,
2210 get_icmp_error_msg(target.icmp_type, target.icmp_code), address);
2211 } else { /* not marked as lost cause, so we have no flags for it */
2212 xasprintf(&result.output, "%s", result.output);
2213 }
2214 } else {
2215 packet_loss =
2216 (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent;
2217 rta = target.time_waited / target.icmp_recv;
2218 }
2219
2220 double EffectiveLatency;
2221 double mos; /* Mean opinion score */
2222 double score; /* score */
2223
2224 if (target.icmp_recv > 1) {
2225 /*
2226 * This algorithm is probably pretty much blindly copied from
2227 * locations like this one:
2228 * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS
2229 * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick
2230 * research MOS originates from the Audio/Video transport network area. Whether it can
2231 * and should be computed from ICMP data, I can not say.
2232 *
2233 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
2234 *
2235 * MOS stands likely for Mean Opinion Score (
2236 * https://en.wikipedia.org/wiki/Mean_Opinion_Score )
2237 *
2238 * More links:
2239 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
2240 */
2241 target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000);
2242
2243 /*
2244 * Take the average round trip latency (in milliseconds), add
2245 * round trip jitter, but double the impact to latency
2246 * then add 10 for protocol latencies (in milliseconds).
2247 */
2248 EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10;
2249
2250 double R;
2251 if (EffectiveLatency < 160) {
2252 R = 93.2 - (EffectiveLatency / 40);
2253 } else {
2254 R = 93.2 - ((EffectiveLatency - 120) / 10);
2255 }
2256
2257 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
2258 // loss of 5% will be entered as 5).
2259 R = R - (packet_loss * 2.5);
2260
2261 if (R < 0) {
2262 R = 0;
2263 }
2264
2265 score = R;
2266 mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
2267 } else {
2268 target.jitter = 0;
2269 target.jitter_min = 0;
2270 target.jitter_max = 0;
2271 mos = 0;
2272 }
2273
2274 /* Check which mode is on and do the warn / Crit stuff */
2275 if (modes.rta_mode) {
2276 mp_subcheck sc_rta = mp_subcheck_init();
2277 sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK);
2278 xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000);
2279
2280 if (rta >= crit.rta) {
2281 sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL);
2282 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)crit.rta / 1000);
2283 } else if (rta >= warn.rta) {
2284 sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING);
2285 xasprintf(&sc_rta.output, "%s >= %0.3fms", sc_rta.output, (double)warn.rta / 1000);
2286 }
2287
2288 if (packet_loss < 100) {
2289 mp_perfdata pd_rta = perfdata_init();
2290 xasprintf(&pd_rta.label, "%srta", address);
2291 pd_rta.uom = strdup("ms");
2292 pd_rta.value = mp_create_pd_value(rta / 1000);
2293 pd_rta.min = mp_create_pd_value(0);
2294
2295 pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta));
2296 pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta));
2297 mp_add_perfdata_to_subcheck(&sc_rta, pd_rta);
2298
2299 mp_perfdata pd_rt_min = perfdata_init();
2300 xasprintf(&pd_rt_min.label, "%srtmin", address);
2301 pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000);
2302 pd_rt_min.uom = strdup("ms");
2303 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min);
2304
2305 mp_perfdata pd_rt_max = perfdata_init();
2306 xasprintf(&pd_rt_max.label, "%srtmax", address);
2307 pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000);
2308 pd_rt_max.uom = strdup("ms");
2309 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max);
2310 }
2311
2312 mp_add_subcheck_to_subcheck(&result, sc_rta);
2313 }
2314
2315 if (modes.pl_mode) {
2316 mp_subcheck sc_pl = mp_subcheck_init();
2317 sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK);
2318 xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss);
2319
2320 if (packet_loss >= crit.pl) {
2321 sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL);
2322 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, crit.pl);
2323 } else if (packet_loss >= warn.pl) {
2324 sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING);
2325 xasprintf(&sc_pl.output, "%s >= %u%%", sc_pl.output, warn.pl);
2326 }
2327
2328 mp_perfdata pd_pl = perfdata_init();
2329 xasprintf(&pd_pl.label, "%spl", address);
2330 pd_pl.uom = strdup("%");
2331
2332 pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl));
2333 pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl));
2334 pd_pl.value = mp_create_pd_value(packet_loss);
2335
2336 mp_add_perfdata_to_subcheck(&sc_pl, pd_pl);
2337
2338 mp_add_subcheck_to_subcheck(&result, sc_pl);
2339 }
2340
2341 if (modes.jitter_mode) {
2342 mp_subcheck sc_jitter = mp_subcheck_init();
2343 sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK);
2344 xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter);
2345
2346 if (target.jitter >= crit.jitter) {
2347 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL);
2348 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, crit.jitter);
2349 } else if (target.jitter >= warn.jitter) {
2350 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING);
2351 xasprintf(&sc_jitter.output, "%s >= %0.3fms", sc_jitter.output, warn.jitter);
2352 }
2353
2354 if (packet_loss < 100) {
2355 mp_perfdata pd_jitter = perfdata_init();
2356 pd_jitter.uom = strdup("ms");
2357 xasprintf(&pd_jitter.label, "%sjitter_avg", address);
2358 pd_jitter.value = mp_create_pd_value(target.jitter);
2359 pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter));
2360 pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter));
2361 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
2362
2363 mp_perfdata pd_jitter_min = perfdata_init();
2364 pd_jitter_min.uom = strdup("ms");
2365 xasprintf(&pd_jitter_min.label, "%sjitter_min", address);
2366 pd_jitter_min.value = mp_create_pd_value(target.jitter_min);
2367 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min);
2368
2369 mp_perfdata pd_jitter_max = perfdata_init();
2370 pd_jitter_max.uom = strdup("ms");
2371 xasprintf(&pd_jitter_max.label, "%sjitter_max", address);
2372 pd_jitter_max.value = mp_create_pd_value(target.jitter_max);
2373 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max);
2374 }
2375 mp_add_subcheck_to_subcheck(&result, sc_jitter);
2376 }
2377
2378 if (modes.mos_mode) {
2379 mp_subcheck sc_mos = mp_subcheck_init();
2380 sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK);
2381 xasprintf(&sc_mos.output, "MOS %0.1f", mos);
2382
2383 if (mos <= crit.mos) {
2384 sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL);
2385 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, crit.mos);
2386 } else if (mos <= warn.mos) {
2387 sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING);
2388 xasprintf(&sc_mos.output, "%s <= %0.1f", sc_mos.output, warn.mos);
2389 }
2390
2391 if (packet_loss < 100) {
2392 mp_perfdata pd_mos = perfdata_init();
2393 xasprintf(&pd_mos.label, "%smos", address);
2394 pd_mos.value = mp_create_pd_value(mos);
2395 pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos));
2396 pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos));
2397 pd_mos.min = mp_create_pd_value(0); // MOS starts at 0
2398 pd_mos.max = mp_create_pd_value(5); // MOS max is 5, by definition
2399 mp_add_perfdata_to_subcheck(&sc_mos, pd_mos);
2400 }
2401 mp_add_subcheck_to_subcheck(&result, sc_mos);
2402 }
2403
2404 if (modes.score_mode) {
2405 mp_subcheck sc_score = mp_subcheck_init();
2406 sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK);
2407 xasprintf(&sc_score.output, "Score %f", score);
2408
2409 if (score <= crit.score) {
2410 sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL);
2411 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, crit.score);
2412 } else if (score <= warn.score) {
2413 sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING);
2414 xasprintf(&sc_score.output, "%s <= %f", sc_score.output, warn.score);
2415 }
2416
2417 if (packet_loss < 100) {
2418 mp_perfdata pd_score = perfdata_init();
2419 xasprintf(&pd_score.label, "%sscore", address);
2420 pd_score.value = mp_create_pd_value(score);
2421 pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score));
2422 pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score));
2423 pd_score.min = mp_create_pd_value(0);
2424 pd_score.max = mp_create_pd_value(100);
2425 mp_add_perfdata_to_subcheck(&sc_score, pd_score);
2426 }
2427
2428 mp_add_subcheck_to_subcheck(&result, sc_score);
2429 }
2430
2431 if (modes.order_mode) {
2432 mp_subcheck sc_order = mp_subcheck_init();
2433 sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK);
2434
2435 if (target.found_out_of_order_packets) {
2436 mp_set_subcheck_state(sc_order, STATE_CRITICAL);
2437 xasprintf(&sc_order.output, "Packets out of order");
2438 } else {
2439 xasprintf(&sc_order.output, "Packets in order");
2440 }
2441
2442 mp_add_subcheck_to_subcheck(&result, sc_order);
2443 }
2444
2445 return result;
2446}
2447
2448evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
2449 check_icmp_mode_switches modes, check_icmp_threshold warn,
2450 check_icmp_threshold crit) {
2451 evaluate_host_wrapper result = {
2452 .targets_warn = 0,
2453 .targets_ok = 0,
2454 .sc_host = mp_subcheck_init(),
2455 };
2456 result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK);
2457
2458 result.sc_host.output = strdup(host.name);
2459
2460 ping_target *target = host.target_list;
2461 for (unsigned int i = 0; i < host.number_of_targets; i++) {
2462 mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit);
2463
2464 mp_state_enum target_state = mp_compute_subcheck_state(sc_target);
2465
2466 if (target_state == STATE_WARNING) {
2467 result.targets_warn++;
2468 } else if (target_state == STATE_OK) {
2469 result.targets_ok++;
2470 }
2471 mp_add_subcheck_to_subcheck(&result.sc_host, sc_target);
2472
2473 target = target->next;
2474 }
2475
2476 return result;
2196} 2477}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.c b/plugins-root/check_icmp.d/check_icmp_helpers.c
new file mode 100644
index 00000000..1b96a392
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.c
@@ -0,0 +1,134 @@
1#include "./config.h"
2#include <math.h>
3#include <netinet/in.h>
4#include <sys/socket.h>
5#include "./check_icmp_helpers.h"
6#include "../../plugins/netutils.h"
7
8// timeout as a global variable to make it available to the timeout handler
9unsigned int timeout = DEFAULT_TIMEOUT;
10
11check_icmp_config check_icmp_config_init() {
12 check_icmp_config tmp = {
13 .modes =
14 {
15 .order_mode = false,
16 .mos_mode = false,
17 .rta_mode = false,
18 .pl_mode = false,
19 .jitter_mode = false,
20 .score_mode = false,
21 },
22
23 .min_hosts_alive = -1,
24 .crit = {.pl = DEFAULT_CRIT_PL,
25 .rta = DEFAULT_CRIT_RTA,
26 .jitter = 50.0,
27 .mos = 3.0,
28 .score = 70.0},
29 .warn = {.pl = DEFAULT_WARN_PL,
30 .rta = DEFAULT_WARN_RTA,
31 .jitter = 40.0,
32 .mos = 3.5,
33 .score = 80.0},
34
35 .ttl = DEFAULT_TTL,
36 .icmp_data_size = DEFAULT_PING_DATA_SIZE,
37 .target_interval = 0,
38 .number_of_packets = DEFAULT_NUMBER_OF_PACKETS,
39
40 .source_ip = NULL,
41 .need_v4 = false,
42 .need_v6 = false,
43
44 .sender_id = 0,
45
46 .mode = MODE_RTA,
47
48 .number_of_targets = 0,
49 .targets = NULL,
50
51 .number_of_hosts = 0,
52 .hosts = NULL,
53
54 .output_format_is_set = false,
55 };
56 return tmp;
57}
58
59ping_target ping_target_init() {
60 ping_target tmp = {
61 .rtmin = INFINITY,
62
63 .jitter_min = INFINITY,
64
65 .found_out_of_order_packets = false,
66 };
67
68 return tmp;
69}
70
71check_icmp_state check_icmp_state_init() {
72 check_icmp_state tmp = {.icmp_sent = 0, .icmp_lost = 0, .icmp_recv = 0, .targets_down = 0};
73
74 return tmp;
75}
76
77ping_target_create_wrapper ping_target_create(struct sockaddr_storage address) {
78 ping_target_create_wrapper result = {
79 .errorcode = 0,
80 };
81
82 struct sockaddr_storage *tmp_addr = &address;
83
84 /* disregard obviously stupid addresses
85 * (I didn't find an ipv6 equivalent to INADDR_NONE) */
86 if (((tmp_addr->ss_family == AF_INET &&
87 (((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_NONE ||
88 ((struct sockaddr_in *)tmp_addr)->sin_addr.s_addr == INADDR_ANY))) ||
89 (tmp_addr->ss_family == AF_INET6 &&
90 (((struct sockaddr_in6 *)tmp_addr)->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
91 result.errorcode = 1;
92 return result;
93 }
94
95 /* add the fresh ip */
96 ping_target target = ping_target_init();
97
98 /* fill out the sockaddr_storage struct */
99 target.address = address;
100
101 result.host = target;
102
103 return result;
104}
105
106check_icmp_target_container check_icmp_target_container_init() {
107 check_icmp_target_container tmp = {
108 .name = NULL,
109 .number_of_targets = 0,
110 .target_list = NULL,
111 };
112 return tmp;
113}
114
115unsigned int ping_target_list_append(ping_target *list, ping_target *elem) {
116 if (elem == NULL || list == NULL) {
117 return 0;
118 }
119
120 while (list->next != NULL) {
121 list = list->next;
122 }
123
124 list->next = elem;
125
126 unsigned int result = 1;
127
128 while (elem->next != NULL) {
129 result++;
130 elem = elem->next;
131 }
132
133 return result;
134}
diff --git a/plugins-root/check_icmp.d/check_icmp_helpers.h b/plugins-root/check_icmp.d/check_icmp_helpers.h
new file mode 100644
index 00000000..dc6ea40b
--- /dev/null
+++ b/plugins-root/check_icmp.d/check_icmp_helpers.h
@@ -0,0 +1,68 @@
1#pragma once
2
3#include "../../lib/states.h"
4#include <netinet/in_systm.h>
5#include <netinet/in.h>
6#include <netinet/ip.h>
7#include <netinet/ip6.h>
8#include <netinet/ip_icmp.h>
9#include <netinet/icmp6.h>
10#include <arpa/inet.h>
11
12typedef struct ping_target {
13 unsigned short id; /* id in **table, and icmp pkts */
14 char *msg; /* icmp error message, if any */
15
16 struct sockaddr_storage address; /* the address of this host */
17 struct sockaddr_storage error_addr; /* stores address of error replies */
18 time_t time_waited; /* total time waited, in usecs */
19 unsigned int icmp_sent, icmp_recv, icmp_lost; /* counters */
20 unsigned char icmp_type, icmp_code; /* type and code from errors */
21 unsigned short flags; /* control/status flags */
22
23 double rtmax; /* max rtt */
24 double rtmin; /* min rtt */
25
26 double jitter; /* measured jitter */
27 double jitter_max; /* jitter rtt maximum */
28 double jitter_min; /* jitter rtt minimum */
29
30 time_t last_tdiff;
31 unsigned int last_icmp_seq; /* Last ICMP_SEQ to check out of order pkts */
32
33 bool found_out_of_order_packets;
34
35 struct ping_target *next;
36} ping_target;
37
38ping_target ping_target_init();
39
40typedef struct {
41 char *name;
42 ping_target *target_list;
43 unsigned int number_of_targets;
44} check_icmp_target_container;
45
46check_icmp_target_container check_icmp_target_container_init();
47
48typedef struct {
49 unsigned int icmp_sent;
50 unsigned int icmp_recv;
51 unsigned int icmp_lost;
52 unsigned short targets_down;
53} check_icmp_state;
54
55check_icmp_state check_icmp_state_init();
56
57typedef struct {
58 int errorcode;
59 ping_target host;
60} ping_target_create_wrapper;
61
62typedef struct {
63 int socket4;
64 int socket6;
65} check_icmp_socket_set;
66
67ping_target_create_wrapper ping_target_create(struct sockaddr_storage address);
68unsigned int ping_target_list_append(ping_target *list, ping_target *elem);
diff --git a/plugins-root/check_icmp.d/config.h b/plugins-root/check_icmp.d/config.h
new file mode 100644
index 00000000..be388d0a
--- /dev/null
+++ b/plugins-root/check_icmp.d/config.h
@@ -0,0 +1,115 @@
1#pragma once
2
3#include "../../config.h"
4#include "../../lib/states.h"
5#include <stddef.h>
6#include <netinet/in_systm.h>
7#include <netinet/in.h>
8#include <netinet/ip.h>
9#include <netinet/ip6.h>
10#include <netinet/ip_icmp.h>
11#include <netinet/icmp6.h>
12#include <arpa/inet.h>
13#include <stdint.h>
14#include "./check_icmp_helpers.h"
15#include "output.h"
16
17/* threshold structure. all values are maximum allowed, exclusive */
18typedef struct {
19 unsigned char pl; /* max allowed packet loss in percent */
20 time_t rta; /* roundtrip time average, microseconds */
21 double jitter; /* jitter time average, microseconds */
22 double mos; /* MOS */
23 double score; /* Score */
24} check_icmp_threshold;
25
26/* the different modes of this program are as follows:
27 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
28 * MODE_HOSTCHECK: Return immediately upon any sign of life
29 * In addition, sends packets to ALL addresses assigned
30 * to this host (as returned by gethostbyname() or
31 * gethostbyaddr() and expects one host only to be checked at
32 * a time. Therefore, any packet response what so ever will
33 * count as a sign of life, even when received outside
34 * crit.rta limit. Do not misspell any additional IP's.
35 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
36 * MODE_ICMP: Default Mode
37 */
38typedef enum {
39 MODE_RTA,
40 MODE_HOSTCHECK,
41 MODE_ALL,
42 MODE_ICMP,
43} check_icmp_execution_mode;
44
45typedef struct {
46 bool order_mode;
47 bool mos_mode;
48 bool rta_mode;
49 bool pl_mode;
50 bool jitter_mode;
51 bool score_mode;
52} check_icmp_mode_switches;
53
54typedef struct {
55 check_icmp_mode_switches modes;
56
57 int min_hosts_alive;
58 check_icmp_threshold crit;
59 check_icmp_threshold warn;
60
61 unsigned long ttl;
62 unsigned short icmp_data_size;
63 time_t target_interval;
64 unsigned short number_of_packets;
65
66 char *source_ip;
67 bool need_v4;
68 bool need_v6;
69
70 uint16_t sender_id; // PID of the main process, which is used as an ID in packets
71
72 check_icmp_execution_mode mode;
73
74 unsigned short number_of_targets;
75 ping_target *targets;
76
77 unsigned short number_of_hosts;
78 check_icmp_target_container *hosts;
79
80 mp_output_format output_format;
81 bool output_format_is_set;
82} check_icmp_config;
83
84check_icmp_config check_icmp_config_init();
85
86/* the data structure */
87typedef struct icmp_ping_data {
88 struct timeval stime; /* timestamp (saved in protocol struct as well) */
89 unsigned short ping_id;
90} icmp_ping_data;
91
92#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
93#define IP_HDR_SIZE 20
94#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
95#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
96#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
97
98/* 80 msec packet interval by default */
99// DEPRECATED, remove when removing the option
100#define DEFAULT_PKT_INTERVAL 80000
101
102#define DEFAULT_TARGET_INTERVAL 0
103
104#define DEFAULT_WARN_RTA 200000
105#define DEFAULT_CRIT_RTA 500000
106#define DEFAULT_WARN_PL 40
107#define DEFAULT_CRIT_PL 80
108
109#define DEFAULT_TIMEOUT 10
110#define DEFAULT_TTL 64
111
112#define DEFAULT_NUMBER_OF_PACKETS 5
113
114#define PACKET_BACKOFF_FACTOR 1.5
115#define TARGET_BACKOFF_FACTOR 1.5
diff --git a/plugins-root/pst3.c b/plugins-root/pst3.c
index 1f69f3a6..1bfe3d35 100644
--- a/plugins-root/pst3.c
+++ b/plugins-root/pst3.c
@@ -1,45 +1,45 @@
1/***************************************************************************** 1/*****************************************************************************
2* 2 *
3* pst3 3 * pst3
4* 4 *
5* License: GPL 5 * License: GPL
6* Copyright (c) 2008 Monitoring Plugins Development Team 6 * Copyright (c) 2008 Monitoring Plugins Development Team
7* 7 *
8* Description: 8 * Description:
9* 9 *
10* This file contains the pst3 executable. This is a replacement ps command 10 * This file contains the pst3 executable. This is a replacement ps command
11* for Solaris to get output which provides a long argument listing, which 11 * for Solaris to get output which provides a long argument listing, which
12* is not possible with the standard ps command (due to truncation). /usr/ucb/ps 12 * is not possible with the standard ps command (due to truncation). /usr/ucb/ps
13* also has issues where some fields run into each other. 13 * also has issues where some fields run into each other.
14* 14 *
15* This executable works by reading process address structures, so needs 15 * This executable works by reading process address structures, so needs
16* to be executed as root 16 * to be executed as root
17* 17 *
18* Originally written by R.W.Ingraham 18 * Originally written by R.W.Ingraham
19* Rewritten by Duncan Ferguson (Altinity Ltd, June 2008) 19 * Rewritten by Duncan Ferguson (Altinity Ltd, June 2008)
20* The rewrite was necessary as /dev/kmem is not available within 20 * The rewrite was necessary as /dev/kmem is not available within
21* non-global zones on Solaris 10 21 * non-global zones on Solaris 10
22* 22 *
23* Details for rewrite came from 23 * Details for rewrite came from
24* source of /usr/ucb/ps on Solaris: 24 * source of /usr/ucb/ps on Solaris:
25* http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff 25 * http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/ucbcmd/ps/ps.c#argvoff
26* usenet group posting 26 * usenet group posting
27* http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F 27 * http://groups.google.com/group/comp.unix.solaris/tree/browse_frm/month/2001-09/bfa40c08bac819a2?rnum=141&_done=%2Fgroup%2Fcomp.unix.solaris%2Fbrowse_frm%2Fmonth%2F2001-09%3F
28* 28 *
29* This program is free software: you can redistribute it and/or modify 29 * This program is free software: you can redistribute it and/or modify
30* it under the terms of the GNU General Public License as published by 30 * it under the terms of the GNU General Public License as published by
31* the Free Software Foundation, either version 3 of the License, or 31 * the Free Software Foundation, either version 3 of the License, or
32* (at your option) any later version. 32 * (at your option) any later version.
33* 33 *
34* This program is distributed in the hope that it will be useful, 34 * This program is distributed in the hope that it will be useful,
35* but WITHOUT ANY WARRANTY; without even the implied warranty of 35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37* GNU General Public License for more details. 37 * GNU General Public License for more details.
38* 38 *
39* You should have received a copy of the GNU General Public License 39 * You should have received a copy of the GNU General Public License
40* along with this program. If not, see <http://www.gnu.org/licenses/>. 40 * along with this program. If not, see <http://www.gnu.org/licenses/>.
41* 41 *
42*****************************************************************************/ 42 *****************************************************************************/
43 43
44#include <stdio.h> 44#include <stdio.h>
45#include <stdlib.h> 45#include <stdlib.h>
@@ -55,14 +55,14 @@
55 * Constants 55 * Constants
56 */ 56 */
57 57
58#define PROC_DIR "/proc" 58#define PROC_DIR "/proc"
59#define ARGS 30 59#define ARGS 30
60 60
61/* 61/*
62 * Globals 62 * Globals
63 */ 63 */
64 64
65static char * szProg; 65static char *szProg;
66 66
67/* 67/*
68 * Prototypes 68 * Prototypes
@@ -71,192 +71,179 @@ void usage();
71 71
72/*----------------------------------------------------------------------------*/ 72/*----------------------------------------------------------------------------*/
73 73
74int main (int argc, char **argv) 74int main(int argc, char **argv) {
75{ 75 DIR *procdir;
76 DIR *procdir; 76 struct dirent *proc;
77 struct dirent *proc; 77 char ps_name[ARGS];
78 char ps_name[ARGS]; 78 char as_name[ARGS];
79 char as_name[ARGS]; 79 psinfo_t psinfo;
80 psinfo_t psinfo; 80
81 81 /* Set our program name global */
82 /* Set our program name global */ 82 if ((szProg = strrchr(argv[0], '/')) != NULL) {
83 if ((szProg = strrchr(argv[0], '/')) != NULL) 83 szProg++;
84 szProg++; 84 } else {
85 else 85 szProg = argv[0];
86 szProg = argv[0]; 86 }
87 87
88 /* if given any parameters, print out help */ 88 /* if given any parameters, print out help */
89 if(argc > 1) { 89 if (argc > 1) {
90 (void)usage(); 90 (void)usage();
91 exit(1); 91 exit(1);
92 } 92 }
93 93
94 /* Make sure that our euid is root */ 94 /* Make sure that our euid is root */
95 if (geteuid() != 0) 95 if (geteuid() != 0) {
96 { 96 fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg);
97 fprintf(stderr, "%s: This program can only be run by the root user!\n", szProg); 97 exit(1);
98 exit(1); 98 }
99 } 99
100 100 if ((procdir = opendir(PROC_DIR)) == NULL) {
101 if ((procdir = opendir(PROC_DIR)) == NULL) { 101 fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR);
102 fprintf(stderr, "%s: cannot open PROC directory %s\n", szProg, PROC_DIR); 102 exit(1);
103 exit(1); 103 }
104 } 104
105 105 /* Display column headings */
106 /* Display column headings */ 106 printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 'S', "UID", "PID", "PPID", "VSZ", "RSS", "%CPU",
107 printf("%c %5s %5s %5s %6s %6s %4s %s %s\n", 107 "COMMAND", "ARGS");
108 'S', 108
109 "UID", 109 /* Zip through all of the process entries */
110 "PID", 110 while ((proc = readdir(procdir))) {
111 "PPID", 111 int ps_fd;
112 "VSZ", 112 int as_fd;
113 "RSS", 113 off_t argoff;
114 "%CPU", 114 int i;
115 "COMMAND", 115 char *args;
116 "ARGS" 116 char *procname;
117 ); 117 char *ptr;
118 118 int argslen;
119 /* Zip through all of the process entries */ 119 uintptr_t args_addr;
120 while((proc = readdir(procdir))) { 120 ;
121 int ps_fd; 121 uintptr_t *args_vecs;
122 int as_fd; 122 ;
123 off_t argoff; 123 int args_count;
124 int i; 124
125 char *args; 125 if (proc->d_name[0] == '.') {
126 char *procname; 126 continue;
127 char *ptr; 127 }
128 int argslen; 128
129 uintptr_t args_addr;; 129 sprintf(ps_name, "%s/%s/%s", PROC_DIR, proc->d_name, "psinfo");
130 uintptr_t *args_vecs;; 130 sprintf(as_name, "%s/%s/%s", PROC_DIR, proc->d_name, "as");
131 int args_count; 131 try_again:
132 132 if ((ps_fd = open(ps_name, O_RDONLY)) == -1) {
133 if(proc->d_name[0] == '.') 133 continue;
134 continue; 134 }
135 135
136 sprintf(ps_name,"%s/%s/%s",PROC_DIR,proc->d_name,"psinfo"); 136 if ((as_fd = open(as_name, O_RDONLY)) == -1) {
137 sprintf(as_name,"%s/%s/%s",PROC_DIR,proc->d_name,"as"); 137 close(ps_fd);
138try_again: 138 continue;
139 if((ps_fd = open(ps_name, O_RDONLY)) == -1) 139 }
140 continue; 140
141 141 if (read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
142 if((as_fd = open(as_name, O_RDONLY)) == -1) { 142 int err = errno;
143 close(ps_fd); 143 close(ps_fd);
144 continue; 144 close(as_fd);
145 } 145 if (err == EAGAIN) {
146 146 goto try_again;
147 if(read(ps_fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { 147 }
148 int err = errno; 148 if (err != ENOENT) {
149 close(ps_fd); 149 fprintf(stderr, "%s: read() on %s: %s\n", szProg, ps_name, strerror(err));
150 close(as_fd); 150 }
151 if(err == EAGAIN) goto try_again; 151 continue;
152 if(err != ENOENT) 152 }
153 fprintf(stderr, "%s: read() on %s: %s\n", szProg, 153 close(ps_fd);
154 ps_name, strerror(err)); 154
155 continue; 155 /* system process, ignore since the previous version did */
156 } 156 if (psinfo.pr_nlwp == 0 || strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0) {
157 close(ps_fd); 157 continue;
158 158 }
159 /* system process, ignore since the previous version did */ 159
160 if( 160 /* get the procname to match previous versions */
161 psinfo.pr_nlwp == 0 || 161 procname = strdup(psinfo.pr_psargs);
162 strcmp(psinfo.pr_lwp.pr_clname, "SYS") == 0 162 if ((ptr = strchr(procname, ' ')) != NULL) {
163 ) { 163 *ptr = '\0';
164 continue; 164 }
165 } 165 if ((ptr = strrchr(procname, '/')) != NULL) {
166 166 ptr++;
167 /* get the procname to match previous versions */ 167 } else {
168 procname = strdup(psinfo.pr_psargs); 168 ptr = procname;
169 if((ptr = strchr(procname, ' ')) != NULL) 169 }
170 *ptr = '\0'; 170
171 if((ptr = strrchr(procname, '/')) != NULL) 171 /*
172 ptr++; 172 * print out what we currently know
173 else 173 */
174 ptr = procname; 174 printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", psinfo.pr_lwp.pr_sname, psinfo.pr_euid,
175 175 psinfo.pr_pid, psinfo.pr_ppid, psinfo.pr_size, psinfo.pr_rssize,
176 /* 176 ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), ptr);
177 * print out what we currently know 177 free(procname);
178 */ 178
179 printf("%c %5d %5d %5d %6lu %6lu %4.1f %s ", 179 /*
180 psinfo.pr_lwp.pr_sname, 180 * and now for the command line stuff
181 psinfo.pr_euid, 181 */
182 psinfo.pr_pid, 182
183 psinfo.pr_ppid, 183 args_addr = psinfo.pr_argv;
184 psinfo.pr_size, 184 args_count = psinfo.pr_argc;
185 psinfo.pr_rssize, 185 args_vecs = malloc(args_count * sizeof(uintptr_t));
186 ((float)(psinfo.pr_pctcpu) / 0x8000 * 100.0), 186
187 ptr 187 if (psinfo.pr_dmodel == PR_MODEL_NATIVE) {
188 ); 188 /* this process matches target process */
189 free(procname); 189 pread(as_fd, args_vecs, args_count * sizeof(uintptr_t), args_addr);
190 190 } else {
191 /* 191 /* this process is 64bit, target process is 32 bit*/
192 * and now for the command line stuff 192 caddr32_t *args_vecs32 = (caddr32_t *)args_vecs;
193 */ 193 pread(as_fd, args_vecs32, args_count * sizeof(caddr32_t), args_addr);
194 194 for (i = args_count - 1; i >= 0; --i) {
195 args_addr = psinfo.pr_argv; 195 args_vecs[i] = args_vecs32[i];
196 args_count = psinfo.pr_argc; 196 }
197 args_vecs = malloc(args_count * sizeof(uintptr_t)); 197 }
198 198
199 if(psinfo.pr_dmodel == PR_MODEL_NATIVE) { 199 /*
200 /* this process matches target process */ 200 * now read in the args - if what we read in fills buffer
201 pread(as_fd,args_vecs, args_count * sizeof(uintptr_t), 201 * resize buffer and reread that bit again
202 args_addr); 202 */
203 } else { 203 argslen = ARGS;
204 /* this process is 64bit, target process is 32 bit*/ 204 args = malloc(argslen + 1);
205 caddr32_t *args_vecs32 = (caddr32_t *)args_vecs; 205 for (i = 0; i < args_count; i++) {
206 pread(as_fd,args_vecs32,args_count * sizeof(caddr32_t), 206 memset(args, '\0', argslen + 1);
207 args_addr); 207 if (pread(as_fd, args, argslen, args_vecs[i]) <= 0) {
208 for (i=args_count-1;i>=0;--i) 208 break;
209 args_vecs[i]=args_vecs32[i]; 209 }
210 } 210 args[argslen] = '\0';
211 211 if (strlen(args) == argslen) {
212 /* 212 argslen += ARGS;
213 * now read in the args - if what we read in fills buffer 213 args = realloc(args, argslen + 1);
214 * resize buffer and reread that bit again 214 i--;
215 */ 215 continue;
216 argslen=ARGS; 216 }
217 args=malloc(argslen+1); 217 printf(" %s", args);
218 for(i=0;i<args_count;i++) { 218 }
219 memset(args,'\0',argslen+1); 219 free(args_vecs);
220 if(pread(as_fd, args, argslen, args_vecs[i]) <= 0) { 220 free(args);
221 break; 221 close(as_fd);
222 } 222 printf("\n");
223 args[argslen]='\0'; 223 }
224 if(strlen(args) == argslen){ 224
225 argslen += ARGS; 225 (void)closedir(procdir);
226 args = realloc(args, argslen + 1); 226
227 i--; 227 return (0);
228 continue;
229 }
230 printf(" %s", args);
231 }
232 free(args_vecs);
233 free(args);
234 close(as_fd);
235 printf("\n");
236 }
237
238 (void) closedir(procdir);
239
240 return (0);
241} 228}
242 229
243/*----------------------------------------------------------------------------*/ 230/*----------------------------------------------------------------------------*/
244 231
245void usage() { 232void usage() {
246 printf("%s: Help output\n\n", szProg); 233 printf("%s: Help output\n\n", szProg);
247 printf("If this program is given any arguments, this help is displayed.\n"); 234 printf("If this program is given any arguments, this help is displayed.\n");
248 printf("This command is used to print out the full command line for all\n"); 235 printf("This command is used to print out the full command line for all\n");
249 printf("running processes because /usr/bin/ps is limited to 80 chars and\n"); 236 printf("running processes because /usr/bin/ps is limited to 80 chars and\n");
250 printf("/usr/ucb/ps can merge columns together.\n\n"); 237 printf("/usr/ucb/ps can merge columns together.\n\n");
251 printf("Columns are:\n"); 238 printf("Columns are:\n");
252 printf("\tS - State of process - see 'ps' man page\n"); 239 printf("\tS - State of process - see 'ps' man page\n");
253 printf("\tUID - UID of the process owner\n"); 240 printf("\tUID - UID of the process owner\n");
254 printf("\tPID - PID of the process\n"); 241 printf("\tPID - PID of the process\n");
255 printf("\tPPID - PID of the parent process\n"); 242 printf("\tPPID - PID of the parent process\n");
256 printf("\tVSZ - Virtual memory usage (kilobytes)\n"); 243 printf("\tVSZ - Virtual memory usage (kilobytes)\n");
257 printf("\tRSS - Real memory usage (kilobytes)\n"); 244 printf("\tRSS - Real memory usage (kilobytes)\n");
258 printf("\t%%CPU - CPU usage\n"); 245 printf("\t%%CPU - CPU usage\n");
259 printf("\tCOMMAND - Command being run\n"); 246 printf("\tCOMMAND - Command being run\n");
260 printf("\tARGS - Full command line with arguments\n"); 247 printf("\tARGS - Full command line with arguments\n");
261 return; 248 return;
262} 249}
diff --git a/plugins-root/t/check_dhcp.t b/plugins-root/t/check_dhcp.t
index ce627736..70392154 100644
--- a/plugins-root/t/check_dhcp.t
+++ b/plugins-root/t/check_dhcp.t
@@ -12,14 +12,14 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 6; 15 plan tests => 7;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_dhcp"; 17 plan skip_all => "Need sudo to test check_dhcp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK: Received \d+ DHCPOFFER\(s\), \d+ of 1 requested servers responded, max lease time = \d+ sec\./'; 21my $successOutput = '/Received \d+ DHCPOFFER(s)*, max lease time = \d+ seconds/';
22my $failureOutput = '/CRITICAL: (No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/'; 22my $failureOutput = '/(No DHCPOFFERs were received|Received \d+ DHCPOFFER\(s\), 0 of 1 requested servers responded, max lease time = \d+ sec\.)/';
23my $invalidOutput = '/Invalid hostname/'; 23my $invalidOutput = '/Invalid hostname/';
24 24
25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE", 25my $host_responsive = getTestParameter( "NP_HOST_DHCP_RESPONSIVE",
@@ -34,6 +34,8 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
34 "An invalid (not known to DNS) hostname", 34 "An invalid (not known to DNS) hostname",
35 "nosuchhost" ); 35 "nosuchhost" );
36 36
37my $output_format = "--output-format mp-test-json";
38
37# try to determince interface 39# try to determince interface
38my $interface = ''; 40my $interface = '';
39 41
@@ -49,19 +51,21 @@ my $res;
49SKIP: { 51SKIP: {
50 skip('need responsive test host', 2) unless $host_responsive; 52 skip('need responsive test host', 2) unless $host_responsive;
51 $res = NPTest->testCmd( 53 $res = NPTest->testCmd(
52 "$sudo ./check_dhcp $interface -u -s $host_responsive" 54 "$sudo ./check_dhcp $interface -u -s $host_responsive $output_format"
53 ); 55 );
54 is( $res->return_code, 0, "Syntax ok" ); 56 is( $res->return_code, 0, "with JSON test format result should always be OK" );
55 like( $res->output, $successOutput, "Output OK" ); 57 like( $res->{'mp_test_result'}->{'state'}, "/OK/", "Output OK" );
58 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $successOutput, "Output OK" );
56}; 59};
57 60
58SKIP: { 61SKIP: {
59 skip('need nonresponsive test host', 2) unless $host_nonresponsive; 62 skip('need nonresponsive test host', 2) unless $host_nonresponsive;
60 $res = NPTest->testCmd( 63 $res = NPTest->testCmd(
61 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive" 64 "$sudo ./check_dhcp $interface -u -s $host_nonresponsive $output_format"
62 ); 65 );
63 is( $res->return_code, 2, "Exit code - host nonresponsive" ); 66 is( $res->return_code, 0, "with JSON test format result should always be OK" );
64 like( $res->output, $failureOutput, "Output OK" ); 67 like( $res->{'mp_test_result'}->{'state'}, "/CRITICAL/", "Exit code - host nonresponsive" );
68 like( $res->{'mp_test_result'}->{'checks'}->[0]->{'output'}, $failureOutput, "Output OK" );
65}; 69};
66 70
67SKIP: { 71SKIP: {
@@ -69,6 +73,6 @@ SKIP: {
69 $res = NPTest->testCmd( 73 $res = NPTest->testCmd(
70 "$sudo ./check_dhcp $interface -u -s $hostname_invalid" 74 "$sudo ./check_dhcp $interface -u -s $hostname_invalid"
71 ); 75 );
72 is( $res->return_code, 3, "Exit code - host invalid" ); 76 is( $res->return_code, 3, "invalid hostname/address should return UNKNOWN" );
73 like( $res->output, $invalidOutput, "Output OK" ); 77 like( $res->output, $invalidOutput, "Output OK" );
74}; 78};
diff --git a/plugins-root/t/check_icmp.t b/plugins-root/t/check_icmp.t
index de1d88d2..d414c3c7 100644
--- a/plugins-root/t/check_icmp.t
+++ b/plugins-root/t/check_icmp.t
@@ -12,15 +12,12 @@ my $allow_sudo = getTestParameter( "NP_ALLOW_SUDO",
12 "no" ); 12 "no" );
13 13
14if ($allow_sudo eq "yes" or $> == 0) { 14if ($allow_sudo eq "yes" or $> == 0) {
15 plan tests => 40; 15 plan tests => 17;
16} else { 16} else {
17 plan skip_all => "Need sudo to test check_icmp"; 17 plan skip_all => "Need sudo to test check_icmp";
18} 18}
19my $sudo = $> == 0 ? '' : 'sudo'; 19my $sudo = $> == 0 ? '' : 'sudo';
20 20
21my $successOutput = '/OK - .*? rta (?:[\d\.]+ms)|(?:nan), lost \d+%/';
22my $failureOutput = '/(WARNING|CRITICAL) - .*? rta (?:[\d\.]+ms > [\d\.]+ms|nan)/';
23
24my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE", 21my $host_responsive = getTestParameter( "NP_HOST_RESPONSIVE",
25 "The hostname of system responsive to network requests", 22 "The hostname of system responsive to network requests",
26 "localhost" ); 23 "localhost" );
@@ -36,108 +33,85 @@ my $hostname_invalid = getTestParameter( "NP_HOSTNAME_INVALID",
36my $res; 33my $res;
37 34
38$res = NPTest->testCmd( 35$res = NPTest->testCmd(
39 "$sudo ./check_icmp -H $host_responsive -w 10000ms,100% -c 10000ms,100%" 36 "$sudo ./check_icmp -H $host_responsive -w 100ms,100% -c 100ms,100%"
40 ); 37 );
41is( $res->return_code, 0, "Syntax ok" ); 38is( $res->return_code, 0, "Syntax ok" );
42like( $res->output, $successOutput, "Output OK" );
43 39
44$res = NPTest->testCmd( 40$res = NPTest->testCmd(
45 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 10000ms,100%" 41 "$sudo ./check_icmp -H $host_responsive -w 0ms,0% -c 100ms,100%"
46 ); 42 );
47is( $res->return_code, 1, "Syntax ok, with forced warning" ); 43is( $res->return_code, 1, "Syntax ok, with forced warning" );
48like( $res->output, $failureOutput, "Output OK" );
49 44
50$res = NPTest->testCmd( 45$res = NPTest->testCmd(
51 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%" 46 "$sudo ./check_icmp -H $host_responsive -w 0,0% -c 0,0%"
52 ); 47 );
53is( $res->return_code, 2, "Syntax ok, with forced critical" ); 48is( $res->return_code, 2, "Syntax ok, with forced critical" );
54like( $res->output, $failureOutput, "Output OK" );
55 49
56$res = NPTest->testCmd( 50$res = NPTest->testCmd(
57 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -t 2" 51 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100%"
58 ); 52 );
59is( $res->return_code, 2, "Timeout - host nonresponsive" ); 53is( $res->return_code, 2, "Timeout - host nonresponsive" );
60like( $res->output, '/pl=100%/', "Error contains 'pl=100%' string (for 100% packet loss)" );
61like( $res->output, '/rta=U/', "Error contains 'rta=U' string" );
62 54
63$res = NPTest->testCmd( 55$res = NPTest->testCmd(
64 "$sudo ./check_icmp -w 10000ms,100% -c 10000ms,100%" 56 "$sudo ./check_icmp -w 100ms,100% -c 100ms,100%"
65 ); 57 );
66is( $res->return_code, 3, "No hostname" ); 58is( $res->return_code, 3, "No hostname" );
67like( $res->output, '/No hosts to check/', "Output with appropriate error message");
68 59
69$res = NPTest->testCmd( 60$res = NPTest->testCmd(
70 "$sudo ./check_icmp -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 0 -t 2" 61 "$sudo ./check_icmp -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 0"
71 ); 62 );
72is( $res->return_code, 0, "One host nonresponsive - zero required" ); 63is( $res->return_code, 0, "One host nonresponsive - zero required" );
73like( $res->output, $successOutput, "Output OK" );
74 64
75$res = NPTest->testCmd( 65$res = NPTest->testCmd(
76 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 1 -t 2" 66 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 1"
77 ); 67 );
78is( $res->return_code, 0, "One of two host nonresponsive - one required" ); 68is( $res->return_code, 0, "One of two host nonresponsive - one required" );
79like( $res->output, $successOutput, "Output OK" );
80 69
81$res = NPTest->testCmd( 70$res = NPTest->testCmd(
82 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 71 "$sudo ./check_icmp -H $host_responsive -H $host_nonresponsive -w 100ms,100% -c 100ms,100% -n 1 -m 2"
83 ); 72 );
84is( $res->return_code, 2, "One of two host nonresponsive - two required" ); 73is( $res->return_code, 2, "One of two host nonresponsive - two required" );
85like( $res->output, $failureOutput, "Output OK" );
86 74
87$res = NPTest->testCmd( 75$res = NPTest->testCmd(
88 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 10000ms,100% -c 10000ms,100% -n 1 -m 2" 76 "$sudo ./check_icmp -H $host_responsive -s 127.0.15.15 -w 100ms,100% -c 100ms,100% -n 1"
89 ); 77 );
90is( $res->return_code, 0, "IPv4 source_ip accepted" ); 78is( $res->return_code, 0, "IPv4 source_ip accepted" );
91like( $res->output, $successOutput, "Output OK" );
92 79
93$res = NPTest->testCmd( 80$res = NPTest->testCmd(
94 "$sudo ./check_icmp -H $host_responsive -b 65507" 81 "$sudo ./check_icmp -H $host_responsive -b 65507"
95 ); 82 );
96is( $res->return_code, 0, "Try max packet size" ); 83is( $res->return_code, 0, "Try max packet size" );
97like( $res->output, $successOutput, "Output OK - Didn't overflow" );
98 84
99$res = NPTest->testCmd( 85$res = NPTest->testCmd(
100 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1 -t 2" 86 "$sudo ./check_icmp -H $host_responsive -R 100,100 -n 1"
101 ); 87 );
102is( $res->return_code, 0, "rta works" ); 88is( $res->return_code, 0, "rta works" );
103like( $res->output, $successOutput, "Output OK" );
104$res = NPTest->testCmd( 89$res = NPTest->testCmd(
105 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1 -t 2" 90 "$sudo ./check_icmp -H $host_responsive -P 80,90 -n 1"
106 ); 91 );
107is( $res->return_code, 0, "pl works" ); 92is( $res->return_code, 0, "pl works" );
108like( $res->output, '/lost 0%/', "Output OK" );
109 93
110$res = NPTest->testCmd( 94$res = NPTest->testCmd(
111 "$sudo ./check_icmp -H $host_responsive -J 80,90 -t 2" 95 "$sudo ./check_icmp -H $host_responsive -J 80,90"
112 ); 96 );
113is( $res->return_code, 0, "jitter works" ); 97is( $res->return_code, 0, "jitter works" );
114like( $res->output, '/jitter \d/', "Output OK" );
115 98
116$res = NPTest->testCmd( 99$res = NPTest->testCmd(
117 "$sudo ./check_icmp -H $host_responsive -M 4,3 -t 2" 100 "$sudo ./check_icmp -H $host_responsive -M 4,3"
118 ); 101 );
119is( $res->return_code, 0, "mos works" ); 102is( $res->return_code, 0, "mos works" );
120like( $res->output, '/MOS \d/', "Output OK" );
121 103
122$res = NPTest->testCmd( 104$res = NPTest->testCmd(
123 "$sudo ./check_icmp -H $host_responsive -S 80,70 -t 2" 105 "$sudo ./check_icmp -H $host_responsive -S 80,70"
124 ); 106 );
125is( $res->return_code, 0, "score works" ); 107is( $res->return_code, 0, "score works" );
126like( $res->output, '/Score \d/', "Output OK" );
127 108
128$res = NPTest->testCmd( 109$res = NPTest->testCmd(
129 "$sudo ./check_icmp -H $host_responsive -O -t 2" 110 "$sudo ./check_icmp -H $host_responsive -O"
130 ); 111 );
131is( $res->return_code, 0, "order works" ); 112is( $res->return_code, 0, "order works" );
132like( $res->output, '/Packets in order/', "Output OK" );
133 113
134$res = NPTest->testCmd( 114$res = NPTest->testCmd(
135 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100 -t 2" 115 "$sudo ./check_icmp -H $host_responsive -O -S 80,70 -M 4,3 -J 80,90 -P 80,90 -R 100,100"
136 ); 116 );
137is( $res->return_code, 0, "order works" ); 117is( $res->return_code, 0, "order works" );
138like( $res->output, '/Packets in order/', "Output OK" );
139like( $res->output, '/Score \d/', "Output OK" );
140like( $res->output, '/MOS \d/', "Output OK" );
141like( $res->output, '/jitter \d/', "Output OK" );
142like( $res->output, '/lost 0%/', "Output OK" );
143like( $res->output, $successOutput, "Output OK" );