summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--lib/output.c83
-rw-r--r--lib/output.h31
-rw-r--r--lib/perfdata.c8
-rw-r--r--lib/perfdata.h4
-rw-r--r--plugins-root/Makefile.am6
-rw-r--r--plugins-root/check_icmp.c2828
-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.h111
-rw-r--r--plugins-root/t/check_icmp.t58
-rw-r--r--plugins/utils.h2
12 files changed, 1990 insertions, 1345 deletions
diff --git a/.clang-format b/.clang-format
index ca411edd..0ff68114 100644
--- a/.clang-format
+++ b/.clang-format
@@ -4,7 +4,7 @@ TabWidth: 4
4AllowShortIfStatementsOnASingleLine: false 4AllowShortIfStatementsOnASingleLine: false
5BreakBeforeBraces: Attach 5BreakBeforeBraces: Attach
6AlignConsecutiveMacros: true 6AlignConsecutiveMacros: true
7ColumnLimit: 140 7ColumnLimit: 100
8IndentPPDirectives: AfterHash 8IndentPPDirectives: AfterHash
9SortIncludes: Never 9SortIncludes: Never
10AllowShortEnumsOnASingleLine: false 10AllowShortEnumsOnASingleLine: false
diff --git a/lib/output.c b/lib/output.c
index c408a2f5..b398c2ad 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -16,7 +16,8 @@ static mp_output_format output_format = MP_FORMAT_DEFAULT;
16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL; 16static mp_output_detail_level level_of_detail = MP_DETAIL_ALL;
17 17
18// == Prototypes == 18// == Prototypes ==
19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation); 19static char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
20 unsigned int indentation);
20static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck); 21static inline cJSON *json_serialize_subcheck(mp_subcheck subcheck);
21 22
22// == Implementation == 23// == Implementation ==
@@ -58,7 +59,9 @@ static inline char *fmt_subcheck_perfdata(mp_subcheck check) {
58 * It sets useful defaults 59 * It sets useful defaults
59 */ 60 */
60mp_check mp_check_init(void) { 61mp_check mp_check_init(void) {
61 mp_check check = {0}; 62 mp_check check = {
63 .evaluation_function = &mp_eval_check_default,
64 };
62 return check; 65 return check;
63} 66}
64 67
@@ -121,7 +124,8 @@ void mp_add_perfdata_to_subcheck(mp_subcheck check[static 1], const mp_perfdata
121 */ 124 */
122int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) { 125int mp_add_subcheck_to_subcheck(mp_subcheck check[static 1], mp_subcheck subcheck) {
123 if (subcheck.output == NULL) { 126 if (subcheck.output == NULL) {
124 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__, "Sub check output is NULL"); 127 die(STATE_UNKNOWN, "%s - %s #%d: %s", __FILE__, __func__, __LINE__,
128 "Sub check output is NULL");
125 } 129 }
126 130
127 mp_subcheck_list *tmp = NULL; 131 mp_subcheck_list *tmp = NULL;
@@ -194,18 +198,30 @@ char *get_subcheck_summary(mp_check check) {
194 return result; 198 return result;
195} 199}
196 200
201mp_state_enum mp_compute_subcheck_state(const mp_subcheck subcheck) {
202 if (subcheck.evaluation_function == NULL) {
203 return mp_eval_subcheck_default(subcheck);
204 }
205 return subcheck.evaluation_function(subcheck);
206}
207
197/* 208/*
198 * Generate the result state of a mp_subcheck object based on it's own state and it's subchecks states 209 * Generate the result state of a mp_subcheck object based on its own state and its subchecks
210 * states
199 */ 211 */
200mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) { 212mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck) {
201 if (check.state_set_explicitly) { 213 if (subcheck.evaluation_function != NULL) {
202 return check.state; 214 return subcheck.evaluation_function(subcheck);
203 } 215 }
204 216
205 mp_subcheck_list *scl = check.subchecks; 217 if (subcheck.state_set_explicitly) {
218 return subcheck.state;
219 }
220
221 mp_subcheck_list *scl = subcheck.subchecks;
206 222
207 if (scl == NULL) { 223 if (scl == NULL) {
208 return check.default_state; 224 return subcheck.default_state;
209 } 225 }
210 226
211 mp_state_enum result = STATE_OK; 227 mp_state_enum result = STATE_OK;
@@ -218,10 +234,18 @@ mp_state_enum mp_compute_subcheck_state(const mp_subcheck check) {
218 return result; 234 return result;
219} 235}
220 236
237mp_state_enum mp_compute_check_state(const mp_check check) {
238 // just a safety check
239 if (check.evaluation_function == NULL) {
240 return mp_eval_check_default(check);
241 }
242 return check.evaluation_function(check);
243}
244
221/* 245/*
222 * Generate the result state of a mp_check object based on it's own state and it's subchecks states 246 * Generate the result state of a mp_check object based on it's own state and it's subchecks states
223 */ 247 */
224mp_state_enum mp_compute_check_state(const mp_check check) { 248mp_state_enum mp_eval_check_default(const mp_check check) {
225 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here 249 assert(check.subchecks != NULL); // a mp_check without subchecks is invalid, die here
226 250
227 mp_subcheck_list *scl = check.subchecks; 251 mp_subcheck_list *scl = check.subchecks;
@@ -253,8 +277,10 @@ char *mp_fmt_output(mp_check check) {
253 mp_subcheck_list *subchecks = check.subchecks; 277 mp_subcheck_list *subchecks = check.subchecks;
254 278
255 while (subchecks != NULL) { 279 while (subchecks != NULL) {
256 if (level_of_detail == MP_DETAIL_ALL || mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) { 280 if (level_of_detail == MP_DETAIL_ALL ||
257 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1)); 281 mp_compute_subcheck_state(subchecks->subcheck) != STATE_OK) {
282 asprintf(&result, "%s\n%s", result,
283 fmt_subcheck_output(MP_FORMAT_MULTI_LINE, subchecks->subcheck, 1));
258 } 284 }
259 subchecks = subchecks->next; 285 subchecks = subchecks->next;
260 } 286 }
@@ -266,7 +292,8 @@ char *mp_fmt_output(mp_check check) {
266 if (pd_string == NULL) { 292 if (pd_string == NULL) {
267 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck)); 293 asprintf(&pd_string, "%s", fmt_subcheck_perfdata(subchecks->subcheck));
268 } else { 294 } else {
269 asprintf(&pd_string, "%s %s", pd_string, fmt_subcheck_perfdata(subchecks->subcheck)); 295 asprintf(&pd_string, "%s %s", pd_string,
296 fmt_subcheck_perfdata(subchecks->subcheck));
270 } 297 }
271 298
272 subchecks = subchecks->next; 299 subchecks = subchecks->next;
@@ -335,19 +362,21 @@ static char *generate_indentation_string(unsigned int indentation) {
335/* 362/*
336 * Helper function to generate the output string of mp_subcheck 363 * Helper function to generate the output string of mp_subcheck
337 */ 364 */
338static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check, unsigned int indentation) { 365static inline char *fmt_subcheck_output(mp_output_format output_format, mp_subcheck check,
366 unsigned int indentation) {
339 char *result = NULL; 367 char *result = NULL;
340 mp_subcheck_list *subchecks = NULL; 368 mp_subcheck_list *subchecks = NULL;
341 369
342 switch (output_format) { 370 switch (output_format) {
343 case MP_FORMAT_MULTI_LINE: 371 case MP_FORMAT_MULTI_LINE:
344 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation), state_text(mp_compute_subcheck_state(check)), 372 asprintf(&result, "%s\\_[%s] - %s", generate_indentation_string(indentation),
345 check.output); 373 state_text(mp_compute_subcheck_state(check)), check.output);
346 374
347 subchecks = check.subchecks; 375 subchecks = check.subchecks;
348 376
349 while (subchecks != NULL) { 377 while (subchecks != NULL) {
350 asprintf(&result, "%s\n%s", result, fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1)); 378 asprintf(&result, "%s\n%s", result,
379 fmt_subcheck_output(output_format, subchecks->subcheck, indentation + 1));
351 subchecks = subchecks->next; 380 subchecks = subchecks->next;
352 } 381 }
353 return result; 382 return result;
@@ -551,3 +580,23 @@ mp_output_format mp_get_format(void) { return output_format; }
551void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; } 580void mp_set_level_of_detail(mp_output_detail_level level) { level_of_detail = level; }
552 581
553mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; } 582mp_output_detail_level mp_get_level_of_detail(void) { return level_of_detail; }
583
584mp_state_enum mp_eval_ok(mp_check overall) {
585 (void)overall;
586 return STATE_OK;
587}
588
589mp_state_enum mp_eval_warning(mp_check overall) {
590 (void)overall;
591 return STATE_WARNING;
592}
593
594mp_state_enum mp_eval_critical(mp_check overall) {
595 (void)overall;
596 return STATE_CRITICAL;
597}
598
599mp_state_enum mp_eval_unknown(mp_check overall) {
600 (void)overall;
601 return STATE_UNKNOWN;
602}
diff --git a/lib/output.h b/lib/output.h
index 3bd91f90..c63c8e3f 100644
--- a/lib/output.h
+++ b/lib/output.h
@@ -7,15 +7,21 @@
7/* 7/*
8 * A partial check result 8 * A partial check result
9 */ 9 */
10typedef struct { 10typedef struct mp_subcheck mp_subcheck;
11struct mp_subcheck {
11 mp_state_enum state; // OK, Warning, Critical ... set explicitly 12 mp_state_enum state; // OK, Warning, Critical ... set explicitly
12 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly 13 mp_state_enum default_state; // OK, Warning, Critical .. if not set explicitly
13 bool state_set_explicitly; // was the state set explicitly (or should it be derived from subchecks) 14 bool state_set_explicitly; // was the state set explicitly (or should it be derived from
15 // subchecks)
14 16
15 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP connection to..") 17 char *output; // Text output for humans ("Filesystem xyz is fine", "Could not create TCP
16 pd_list *perfdata; // Performance data for this check 18 // connection to..")
19 pd_list *perfdata; // Performance data for this check
17 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy 20 struct subcheck_list *subchecks; // subchecks deeper in the hierarchy
18} mp_subcheck; 21
22 // the evaluation_functions computes the state of subcheck
23 mp_state_enum (*evaluation_function)(mp_subcheck);
24};
19 25
20/* 26/*
21 * A list of subchecks, used in subchecks and the main check 27 * A list of subchecks, used in subchecks and the main check
@@ -57,10 +63,14 @@ mp_output_detail_level mp_get_level_of_detail(void);
57 * The final result is always derived from the children and the "worst" state 63 * The final result is always derived from the children and the "worst" state
58 * in the first layer of subchecks 64 * in the first layer of subchecks
59 */ 65 */
60typedef struct { 66typedef struct mp_check mp_check;
67struct mp_check {
61 char *summary; // Overall summary, if not set a summary will be automatically generated 68 char *summary; // Overall summary, if not set a summary will be automatically generated
62 mp_subcheck_list *subchecks; 69 mp_subcheck_list *subchecks;
63} mp_check; 70
71 // the evaluation_functions computes the state of check
72 mp_state_enum (*evaluation_function)(mp_check);
73};
64 74
65mp_check mp_check_init(void); 75mp_check mp_check_init(void);
66mp_subcheck mp_subcheck_init(void); 76mp_subcheck mp_subcheck_init(void);
@@ -78,6 +88,13 @@ void mp_add_summary(mp_check check[static 1], char *summary);
78mp_state_enum mp_compute_check_state(mp_check); 88mp_state_enum mp_compute_check_state(mp_check);
79mp_state_enum mp_compute_subcheck_state(mp_subcheck); 89mp_state_enum mp_compute_subcheck_state(mp_subcheck);
80 90
91mp_state_enum mp_eval_ok(mp_check overall);
92mp_state_enum mp_eval_warning(mp_check overall);
93mp_state_enum mp_eval_critical(mp_check overall);
94mp_state_enum mp_eval_unknown(mp_check overall);
95mp_state_enum mp_eval_check_default(mp_check check);
96mp_state_enum mp_eval_subcheck_default(mp_subcheck subcheck);
97
81typedef struct { 98typedef struct {
82 bool parsing_success; 99 bool parsing_success;
83 mp_output_format output_format; 100 mp_output_format output_format;
diff --git a/lib/perfdata.c b/lib/perfdata.c
index f425ffcf..b87de7e0 100644
--- a/lib/perfdata.c
+++ b/lib/perfdata.c
@@ -257,6 +257,10 @@ mp_perfdata mp_set_pd_value_double(mp_perfdata pd, double value) {
257 return pd; 257 return pd;
258} 258}
259 259
260mp_perfdata mp_set_pd_value_char(mp_perfdata pd, char value) { return mp_set_pd_value_long_long(pd, (long long)value); }
261
262mp_perfdata mp_set_pd_value_u_char(mp_perfdata pd, unsigned char value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
263
260mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); } 264mp_perfdata mp_set_pd_value_int(mp_perfdata pd, int value) { return mp_set_pd_value_long_long(pd, (long long)value); }
261 265
262mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); } 266mp_perfdata mp_set_pd_value_u_int(mp_perfdata pd, unsigned int value) { return mp_set_pd_value_u_long_long(pd, (unsigned long long)value); }
@@ -288,6 +292,10 @@ mp_perfdata_value mp_create_pd_value_double(double value) {
288 292
289mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); } 293mp_perfdata_value mp_create_pd_value_float(float value) { return mp_create_pd_value_double((double)value); }
290 294
295mp_perfdata_value mp_create_pd_value_char(char value) { return mp_create_pd_value_long_long((long long)value); }
296
297mp_perfdata_value mp_create_pd_value_u_char(unsigned char value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
298
291mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); } 299mp_perfdata_value mp_create_pd_value_int(int value) { return mp_create_pd_value_long_long((long long)value); }
292 300
293mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); } 301mp_perfdata_value mp_create_pd_value_u_int(unsigned int value) { return mp_create_pd_value_u_long_long((unsigned long long)value); }
diff --git a/lib/perfdata.h b/lib/perfdata.h
index cb552678..7fd908a9 100644
--- a/lib/perfdata.h
+++ b/lib/perfdata.h
@@ -155,6 +155,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
155 _Generic((V), \ 155 _Generic((V), \
156 float: mp_create_pd_value_float, \ 156 float: mp_create_pd_value_float, \
157 double: mp_create_pd_value_double, \ 157 double: mp_create_pd_value_double, \
158 char: mp_create_pd_value_char, \
159 unsigned char: mp_create_pd_value_u_char, \
158 int: mp_create_pd_value_int, \ 160 int: mp_create_pd_value_int, \
159 unsigned int: mp_create_pd_value_u_int, \ 161 unsigned int: mp_create_pd_value_u_int, \
160 long: mp_create_pd_value_long, \ 162 long: mp_create_pd_value_long, \
@@ -164,6 +166,8 @@ mp_perfdata mp_set_pd_value_u_long_long(mp_perfdata, unsigned long long);
164 166
165mp_perfdata_value mp_create_pd_value_float(float); 167mp_perfdata_value mp_create_pd_value_float(float);
166mp_perfdata_value mp_create_pd_value_double(double); 168mp_perfdata_value mp_create_pd_value_double(double);
169mp_perfdata_value mp_create_pd_value_char(char);
170mp_perfdata_value mp_create_pd_value_u_char(unsigned char);
167mp_perfdata_value mp_create_pd_value_int(int); 171mp_perfdata_value mp_create_pd_value_int(int);
168mp_perfdata_value mp_create_pd_value_u_int(unsigned int); 172mp_perfdata_value mp_create_pd_value_u_int(unsigned int);
169mp_perfdata_value mp_create_pd_value_long(long); 173mp_perfdata_value mp_create_pd_value_long(long);
diff --git a/plugins-root/Makefile.am b/plugins-root/Makefile.am
index a80229e2..fffc0575 100644
--- a/plugins-root/Makefile.am
+++ b/plugins-root/Makefile.am
@@ -24,7 +24,8 @@ 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
28 29
29BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a 30BASEOBJS = ../plugins/utils.o ../lib/libmonitoringplug.a ../gl/libgnu.a
30NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS) 31NETOBJS = ../plugins/netutils.o $(BASEOBJS) $(EXTRA_NETOBJS)
@@ -82,6 +83,7 @@ install-exec-local: $(noinst_PROGRAMS)
82# the actual targets 83# the actual targets
83check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO) 84check_dhcp_LDADD = @LTLIBINTL@ $(NETLIBS) $(LIB_CRYPTO)
84check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO) 85check_icmp_LDADD = @LTLIBINTL@ $(NETLIBS) $(SOCKETLIBS) $(LIB_CRYPTO)
86check_icmp_SOURCES = check_icmp.c check_icmp.d/check_icmp_helpers.c
85 87
86# -m64 needed at compiler and linker phase 88# -m64 needed at compiler and linker phase
87pst3_CFLAGS = @PST3CFLAGS@ 89pst3_CFLAGS = @PST3CFLAGS@
@@ -89,7 +91,7 @@ pst3_LDFLAGS = @PST3CFLAGS@
89# pst3 must not use monitoringplug/gnulib includes! 91# pst3 must not use monitoringplug/gnulib includes!
90pst3_CPPFLAGS = 92pst3_CPPFLAGS =
91 93
92check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS) 94check_dhcp_DEPENDENCIES = check_dhcp.c $(NETOBJS) $(DEPLIBS)
93check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS) 95check_icmp_DEPENDENCIES = check_icmp.c $(NETOBJS)
94 96
95clean-local: 97clean-local:
diff --git a/plugins-root/check_icmp.c b/plugins-root/check_icmp.c
index dcaceddb..55405b8a 100644
--- a/plugins-root/check_icmp.c
+++ b/plugins-root/check_icmp.c
@@ -46,6 +46,8 @@ 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>
@@ -65,6 +67,17 @@ const char *email = "devel@monitoring-plugins.org";
65#include <netinet/icmp6.h> 67#include <netinet/icmp6.h>
66#include <arpa/inet.h> 68#include <arpa/inet.h>
67#include <math.h> 69#include <math.h>
70#include <netdb.h>
71#include <sys/types.h>
72#include <unistd.h>
73#include <stdint.h>
74#include <sys/socket.h>
75#include <assert.h>
76#include <sys/select.h>
77
78#include "../lib/states.h"
79#include "./check_icmp.d/config.h"
80#include "./check_icmp.d/check_icmp_helpers.h"
68 81
69/** sometimes undefined system macros (quite a few, actually) **/ 82/** sometimes undefined system macros (quite a few, actually) **/
70#ifndef MAXTTL 83#ifndef MAXTTL
@@ -96,56 +109,8 @@ const char *email = "devel@monitoring-plugins.org";
96# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 109# define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
97#endif 110#endif
98 111
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. */ 112#define FLAG_LOST_CAUSE 0x01 /* decidedly dead target. */
133 113
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 { 114typedef union ip_hdr {
150 struct ip ip; 115 struct ip ip;
151 struct ip6_hdr ip6; 116 struct ip6_hdr ip6;
@@ -158,24 +123,6 @@ typedef union icmp_packet {
158 u_short *cksum_in; 123 u_short *cksum_in;
159} icmp_packet; 124} icmp_packet;
160 125
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 { 126enum enum_threshold_mode {
180 const_rta_mode, 127 const_rta_mode,
181 const_packet_loss_mode, 128 const_packet_loss_mode,
@@ -186,89 +133,453 @@ enum enum_threshold_mode {
186 133
187typedef enum enum_threshold_mode threshold_mode; 134typedef enum enum_threshold_mode threshold_mode;
188 135
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 **/ 136/** prototypes **/
209void print_help(void); 137void print_help();
210void print_usage(void); 138void print_usage(void);
211static u_int get_timevar(const char *); 139
212static u_int get_timevaldiff(struct timeval *, struct timeval *); 140/* Time related */
213static in_addr_t get_ip_address(const char *); 141typedef struct {
214static int wait_for_reply(int, u_int); 142 int error_code;
215static int recvfrom_wto(int, void *, unsigned int, struct sockaddr *, u_int *, struct timeval *); 143 time_t time_range;
216static int send_icmp_ping(int, struct rta_host *); 144} get_timevar_wrapper;
217static int get_threshold(char *str, threshold *th); 145static get_timevar_wrapper get_timevar(const char *str);
218static bool get_threshold2(char *str, size_t length, threshold *, threshold *, threshold_mode mode); 146static 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); 147static time_t get_timevaldiff_to_now(struct timeval earlier);
220static void run_checks(void); 148
221static void set_source_ip(char *); 149static in_addr_t get_ip_address(const char *ifname);
222static int add_target(char *); 150static void set_source_ip(char *arg, int icmp_sock, sa_family_t addr_family);
223static int add_target_ip(char *, struct sockaddr_storage *); 151
224static int handle_random_icmp(unsigned char *, struct sockaddr_storage *); 152/* Receiving data */
225static void parse_address(struct sockaddr_storage *, char *, int); 153static int wait_for_reply(check_icmp_socket_set sockset, time_t time_interval,
226static unsigned short icmp_checksum(uint16_t *, size_t); 154 unsigned short icmp_pkt_size, time_t *pkt_interval,
227static void finish(int); 155 time_t *target_interval, uint16_t sender_id, ping_target **table,
228static void crash(const char *, ...); 156 unsigned short packets, unsigned short number_of_targets,
229 157 check_icmp_state *program_state);
230/** external **/ 158
231extern int optind; 159typedef struct {
232extern char *optarg; 160 sa_family_t recv_proto;
233extern char **environ; 161 ssize_t received;
162} recvfrom_wto_wrapper;
163static recvfrom_wto_wrapper recvfrom_wto(check_icmp_socket_set sockset, void *buf, unsigned int len,
164 struct sockaddr *saddr, time_t *timeout,
165 struct timeval *received_timestamp);
166static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
167 time_t *pkt_interval, time_t *target_interval, uint16_t sender_id,
168 ping_target **table, unsigned short packets,
169 unsigned short number_of_targets, check_icmp_state *program_state);
170
171/* Sending data */
172static int send_icmp_ping(check_icmp_socket_set sockset, ping_target *host,
173 unsigned short icmp_pkt_size, uint16_t sender_id,
174 check_icmp_state *program_state);
175
176/* Threshold related */
177typedef struct {
178 int errorcode;
179 check_icmp_threshold threshold;
180} get_threshold_wrapper;
181static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold);
182
183typedef struct {
184 int errorcode;
185 check_icmp_threshold warn;
186 check_icmp_threshold crit;
187} get_threshold2_wrapper;
188static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
189 check_icmp_threshold crit, threshold_mode mode);
190
191typedef struct {
192 int errorcode;
193 check_icmp_threshold result;
194} parse_threshold2_helper_wrapper;
195static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
196 size_t length,
197 check_icmp_threshold thr,
198 threshold_mode mode);
199
200/* main test function */
201static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval,
202 uint16_t sender_id, check_icmp_execution_mode mode,
203 time_t max_completion_time, struct timeval prog_start, ping_target **table,
204 unsigned short packets, check_icmp_socket_set sockset,
205 unsigned short number_of_targets, check_icmp_state *program_state);
206mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
207 check_icmp_threshold warn, check_icmp_threshold crit);
208
209typedef struct {
210 int targets_ok;
211 int targets_warn;
212 mp_subcheck sc_host;
213} evaluate_host_wrapper;
214evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
215 check_icmp_mode_switches modes, check_icmp_threshold warn,
216 check_icmp_threshold crit);
217
218/* Target acquisition */
219typedef struct {
220 int error_code;
221 check_icmp_target_container host;
222 bool has_v4;
223 bool has_v6;
224} add_host_wrapper;
225static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
226 sa_family_t enforced_proto);
227
228typedef struct {
229 int error_code;
230 ping_target *targets;
231 unsigned int number_of_targets;
232 bool has_v4;
233 bool has_v6;
234} add_target_wrapper;
235static add_target_wrapper add_target(char *arg, check_icmp_execution_mode mode,
236 sa_family_t enforced_proto);
237
238typedef struct {
239 int error_code;
240 ping_target *target;
241} add_target_ip_wrapper;
242static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address);
243
244static void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size);
245
246static unsigned short icmp_checksum(uint16_t *packet, size_t packet_size);
247
248/* End of run function */
249static void finish(int sign, check_icmp_mode_switches modes, int min_hosts_alive,
250 check_icmp_threshold warn, check_icmp_threshold crit,
251 unsigned short number_of_targets, check_icmp_state *program_state,
252 check_icmp_target_container host_list[], unsigned short number_of_hosts,
253 mp_check overall[static 1]);
254
255/* Error exit */
256static void crash(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
234 257
235/** global variables **/ 258/** global variables **/
236static struct rta_host **table, *cursor, *list; 259static int debug = 0;
237 260
238static threshold crit = {.pl = 80, .rta = 500000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 261extern unsigned int timeout;
239static threshold warn = {.pl = 40, .rta = 200000, .jitter = 0.0, .mos = 0.0, .score = 0.0}; 262
240 263/** the working code **/
241static int mode, protocols, sockets, debug = 0, timeout = 10; 264static inline unsigned short targets_alive(unsigned short targets, unsigned short targets_down) {
242static unsigned short icmp_data_size = DEFAULT_PING_DATA_SIZE; 265 return targets - targets_down;
243static unsigned short icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN; 266}
244 267static 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; 268 unsigned int icmp_lost) {
246#define icmp_pkts_en_route (icmp_sent - (icmp_recv + icmp_lost)) 269 return icmp_sent - (icmp_recv + icmp_lost);
247static unsigned short targets_down = 0, targets = 0, packets = 0; 270}
248#define targets_alive (targets - targets_down) 271
249static unsigned int retry_interval, pkt_interval, target_interval; 272// Create configuration from cli parameters
250static int icmp_sock, tcp_sock, udp_sock, status = STATE_OK; 273typedef struct {
251static pid_t pid; 274 int errorcode;
252static struct timezone tz; 275 check_icmp_config config;
253static struct timeval prog_start; 276} check_icmp_config_wrapper;
254static unsigned long long max_completion_time = 0; 277check_icmp_config_wrapper process_arguments(int argc, char **argv) {
255static unsigned int warn_down = 1, crit_down = 1; /* host down threshold values */ 278 /* get calling name the old-fashioned way for portability instead
256static int min_hosts_alive = -1; 279 * of relying on the glibc-ism __progname */
257static float pkt_backoff_factor = 1.5; 280 char *ptr = strrchr(argv[0], '/');
258static float target_backoff_factor = 1.5; 281 if (ptr) {
259static bool rta_mode = false; 282 progname = &ptr[1];
260static bool pl_mode = false; 283 } else {
261static bool jitter_mode = false; 284 progname = argv[0];
262static bool score_mode = false; 285 }
263static bool mos_mode = false; 286
264static bool order_mode = false; 287 check_icmp_config_wrapper result = {
288 .errorcode = OK,
289 .config = check_icmp_config_init(),
290 };
291
292 /* use the pid to mark packets as ours */
293 /* Some systems have 32-bit pid_t so mask off only 16 bits */
294 result.config.sender_id = getpid() & 0xffff;
295
296 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) {
297 result.config.mode = MODE_ICMP;
298 } else if (!strcmp(progname, "check_host")) {
299 result.config.mode = MODE_HOSTCHECK;
300 result.config.pkt_interval = 1000000;
301 result.config.number_of_packets = 5;
302 result.config.crit.rta = result.config.warn.rta = 1000000;
303 result.config.crit.pl = result.config.warn.pl = 100;
304 } else if (!strcmp(progname, "check_rta_multi")) {
305 result.config.mode = MODE_ALL;
306 result.config.target_interval = 0;
307 result.config.pkt_interval = 50000;
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 // Parse protocol arguments first
323 // and count hosts here
324 char *opts_str = "vhVw:c:n:p:t:H:s:i:b:I:l:m:P:R:J:S:M:O64";
325 for (int i = 1; i < argc; i++) {
326 long int arg;
327 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
328 switch (arg) {
329 case '4':
330 if (enforced_ai_family != AF_UNSPEC) {
331 crash("Multiple protocol versions not supported");
332 }
333 enforced_ai_family = AF_INET;
334 break;
335 case '6':
336 if (enforced_ai_family != AF_UNSPEC) {
337 crash("Multiple protocol versions not supported");
338 }
339 enforced_ai_family = AF_INET6;
340 break;
341 case 'H': {
342 result.config.number_of_hosts++;
343 break;
344 }
345 case 'v':
346 debug++;
347 break;
348 }
349 }
350 }
351
352 char **tmp = &argv[optind];
353 while (*tmp) {
354 result.config.number_of_hosts++;
355 tmp++;
356 }
357
358 // Sanity check: if hostmode is selected,only a single host is allowed
359 if (result.config.mode == MODE_HOSTCHECK && result.config.number_of_hosts > 1) {
360 usage("check_host only allows a single host");
361 }
362
363 // Allocate hosts
364 result.config.hosts =
365 calloc(result.config.number_of_hosts, sizeof(check_icmp_target_container));
366 if (result.config.hosts == NULL) {
367 crash("failed to allocate memory");
368 }
369
370 /* Reset argument scanning */
371 optind = 1;
372
373 int host_counter = 0;
374 /* parse the arguments */
375 for (int i = 1; i < argc; i++) {
376 long int arg;
377 while ((arg = getopt(argc, argv, opts_str)) != EOF) {
378 switch (arg) {
379 case 'b': {
380 long size = strtol(optarg, NULL, 0);
381 if ((unsigned long)size >= (sizeof(struct icmp) + sizeof(struct icmp_ping_data)) &&
382 size < MAX_PING_DATA) {
383 result.config.icmp_data_size = (unsigned short)size;
384 result.config.icmp_pkt_size = (unsigned short)(size + ICMP_MINLEN);
385 } else {
386 usage_va("ICMP data length must be between: %lu and %lu",
387 sizeof(struct icmp) + sizeof(struct icmp_ping_data),
388 MAX_PING_DATA - 1);
389 }
390 } break;
391 case 'i': {
392 get_timevar_wrapper parsed_time = get_timevar(optarg);
393
394 if (parsed_time.error_code == OK) {
395 result.config.pkt_interval = parsed_time.time_range;
396 } else {
397 crash("failed to parse packet interval");
398 }
399 } break;
400 case 'I': {
401 get_timevar_wrapper parsed_time = get_timevar(optarg);
402
403 if (parsed_time.error_code == OK) {
404 result.config.target_interval = parsed_time.time_range;
405 } else {
406 crash("failed to parse target interval");
407 }
408 } break;
409 case 'w': {
410 get_threshold_wrapper warn = get_threshold(optarg, result.config.warn);
411 if (warn.errorcode == OK) {
412 result.config.warn = warn.threshold;
413 } else {
414 crash("failed to parse warning threshold");
415 }
416 } break;
417 case 'c': {
418 get_threshold_wrapper crit = get_threshold(optarg, result.config.crit);
419 if (crit.errorcode == OK) {
420 result.config.crit = crit.threshold;
421 } else {
422 crash("failed to parse critical threshold");
423 }
424 } break;
425 case 'n':
426 case 'p':
427 result.config.number_of_packets = (unsigned short)strtoul(optarg, NULL, 0);
428 if (result.config.number_of_packets > 20) {
429 errno = 0;
430 crash("packets is > 20 (%d)", result.config.number_of_packets);
431 }
432 break;
433 case 't':
434 // WARNING Deprecated since execution time is determined by the other factors
435 break;
436 case 'H': {
437 add_host_wrapper host_add_result =
438 add_host(optarg, result.config.mode, enforced_ai_family);
439 if (host_add_result.error_code == OK) {
440 result.config.hosts[host_counter] = host_add_result.host;
441 host_counter++;
442
443 if (result.config.targets != NULL) {
444 result.config.number_of_targets += ping_target_list_append(
445 result.config.targets, host_add_result.host.target_list);
446 } else {
447 result.config.targets = host_add_result.host.target_list;
448 result.config.number_of_targets += host_add_result.host.number_of_targets;
449 }
450
451 if (host_add_result.has_v4) {
452 result.config.need_v4 = true;
453 }
454 if (host_add_result.has_v6) {
455 result.config.need_v6 = true;
456 }
457 } else {
458 // TODO adding host failed, crash here
459 }
460 } break;
461 case 'l':
462 result.config.ttl = strtoul(optarg, NULL, 0);
463 break;
464 case 'm':
465 result.config.min_hosts_alive = (int)strtoul(optarg, NULL, 0);
466 break;
467 case 's': /* specify source IP address */
468 result.config.source_ip = optarg;
469 break;
470 case 'V': /* version */
471 print_revision(progname, NP_VERSION);
472 exit(STATE_UNKNOWN);
473 case 'h': /* help */
474 print_help();
475 exit(STATE_UNKNOWN);
476 break;
477 case 'R': /* RTA mode */ {
478 get_threshold2_wrapper rta_th = get_threshold2(
479 optarg, strlen(optarg), result.config.warn, result.config.crit, const_rta_mode);
480
481 if (rta_th.errorcode != OK) {
482 crash("Failed to parse RTA threshold");
483 }
484
485 result.config.warn = rta_th.warn;
486 result.config.crit = rta_th.crit;
487 result.config.modes.rta_mode = true;
488 } break;
489 case 'P': /* packet loss mode */ {
490 get_threshold2_wrapper pl_th =
491 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
492 const_packet_loss_mode);
493 if (pl_th.errorcode != OK) {
494 crash("Failed to parse packet loss threshold");
495 }
496
497 result.config.warn = pl_th.warn;
498 result.config.crit = pl_th.crit;
499 result.config.modes.pl_mode = true;
500 } break;
501 case 'J': /* jitter mode */ {
502 get_threshold2_wrapper jitter_th =
503 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
504 const_jitter_mode);
505 if (jitter_th.errorcode != OK) {
506 crash("Failed to parse jitter threshold");
507 }
508
509 result.config.warn = jitter_th.warn;
510 result.config.crit = jitter_th.crit;
511 result.config.modes.jitter_mode = true;
512 } break;
513 case 'M': /* MOS mode */ {
514 get_threshold2_wrapper mos_th = get_threshold2(
515 optarg, strlen(optarg), result.config.warn, result.config.crit, const_mos_mode);
516 if (mos_th.errorcode != OK) {
517 crash("Failed to parse MOS threshold");
518 }
519
520 result.config.warn = mos_th.warn;
521 result.config.crit = mos_th.crit;
522 result.config.modes.mos_mode = true;
523 } break;
524 case 'S': /* score mode */ {
525 get_threshold2_wrapper score_th =
526 get_threshold2(optarg, strlen(optarg), result.config.warn, result.config.crit,
527 const_score_mode);
528 if (score_th.errorcode != OK) {
529 crash("Failed to parse score threshold");
530 }
531
532 result.config.warn = score_th.warn;
533 result.config.crit = score_th.crit;
534 result.config.modes.score_mode = true;
535 } break;
536 case 'O': /* out of order mode */
537 result.config.modes.order_mode = true;
538 break;
539 }
540 }
541 }
542
543 argv = &argv[optind];
544 while (*argv) {
545 add_target(*argv, result.config.mode, enforced_ai_family);
546 argv++;
547 }
548
549 if (!result.config.number_of_targets) {
550 errno = 0;
551 crash("No hosts to check");
552 }
553
554 /* stupid users should be able to give whatever thresholds they want
555 * (nothing will break if they do), but some anal plugin maintainer
556 * will probably add some printf() thing here later, so it might be
557 * best to at least show them where to do it. ;) */
558 if (result.config.warn.pl > result.config.crit.pl) {
559 result.config.warn.pl = result.config.crit.pl;
560 }
561 if (result.config.warn.rta > result.config.crit.rta) {
562 result.config.warn.rta = result.config.crit.rta;
563 }
564 if (result.config.warn.jitter > result.config.crit.jitter) {
565 result.config.crit.jitter = result.config.warn.jitter;
566 }
567 if (result.config.warn.mos < result.config.crit.mos) {
568 result.config.warn.mos = result.config.crit.mos;
569 }
570 if (result.config.warn.score < result.config.crit.score) {
571 result.config.warn.score = result.config.crit.score;
572 }
573
574 return result;
575}
265 576
266/** code start **/ 577/** code start **/
267static void crash(const char *fmt, ...) { 578static void crash(const char *fmt, ...) {
268 va_list ap;
269 579
270 printf("%s: ", progname); 580 printf("%s: ", progname);
271 581
582 va_list ap;
272 va_start(ap, fmt); 583 va_start(ap, fmt);
273 vprintf(fmt, ap); 584 vprintf(fmt, ap);
274 va_end(ap); 585 va_end(ap);
@@ -385,18 +696,20 @@ static const char *get_icmp_error_msg(unsigned char icmp_type, unsigned char icm
385 return msg; 696 return msg;
386} 697}
387 698
388static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr) { 699static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *addr,
389 struct icmp p, sent_icmp; 700 time_t *pkt_interval, time_t *target_interval,
390 struct rta_host *host = NULL; 701 const uint16_t sender_id, ping_target **table, unsigned short packets,
391 702 const unsigned short number_of_targets,
392 memcpy(&p, packet, sizeof(p)); 703 check_icmp_state *program_state) {
393 if (p.icmp_type == ICMP_ECHO && ntohs(p.icmp_id) == pid) { 704 struct icmp icmp_packet;
705 memcpy(&icmp_packet, packet, sizeof(icmp_packet));
706 if (icmp_packet.icmp_type == ICMP_ECHO && ntohs(icmp_packet.icmp_id) == sender_id) {
394 /* echo request from us to us (pinging localhost) */ 707 /* echo request from us to us (pinging localhost) */
395 return 0; 708 return 0;
396 } 709 }
397 710
398 if (debug) { 711 if (debug) {
399 printf("handle_random_icmp(%p, %p)\n", (void *)&p, (void *)addr); 712 printf("handle_random_icmp(%p, %p)\n", (void *)&icmp_packet, (void *)addr);
400 } 713 }
401 714
402 /* only handle a few types, since others can't possibly be replies to 715 /* only handle a few types, since others can't possibly be replies to
@@ -409,14 +722,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 722 * 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 723 * 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. */ 724 * 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) { 725 if (icmp_packet.icmp_type != ICMP_UNREACH && icmp_packet.icmp_type != ICMP_TIMXCEED &&
726 icmp_packet.icmp_type != ICMP_SOURCEQUENCH && icmp_packet.icmp_type != ICMP_PARAMPROB) {
413 return 0; 727 return 0;
414 } 728 }
415 729
416 /* might be for us. At least it holds the original package (according 730 /* might be for us. At least it holds the original package (according
417 * to RFC 792). If it isn't, just ignore it */ 731 * to RFC 792). If it isn't, just ignore it */
732 struct icmp sent_icmp;
418 memcpy(&sent_icmp, packet + 28, sizeof(sent_icmp)); 733 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) { 734 if (sent_icmp.icmp_type != ICMP_ECHO || ntohs(sent_icmp.icmp_id) != sender_id ||
735 ntohs(sent_icmp.icmp_seq) >= number_of_targets * packets) {
420 if (debug) { 736 if (debug) {
421 printf("Packet is no response to a packet we sent\n"); 737 printf("Packet is no response to a packet we sent\n");
422 } 738 }
@@ -424,14 +740,15 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
424 } 740 }
425 741
426 /* it is indeed a response for us */ 742 /* it is indeed a response for us */
427 host = table[ntohs(sent_icmp.icmp_seq) / packets]; 743 ping_target *host = table[ntohs(sent_icmp.icmp_seq) / packets];
428 if (debug) { 744 if (debug) {
429 char address[INET6_ADDRSTRLEN]; 745 char address[INET6_ADDRSTRLEN];
430 parse_address(addr, address, sizeof(address)); 746 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); 747 printf("Received \"%s\" from %s for ICMP ECHO sent.\n",
748 get_icmp_error_msg(icmp_packet.icmp_type, icmp_packet.icmp_code), address);
432 } 749 }
433 750
434 icmp_lost++; 751 program_state->icmp_lost++;
435 host->icmp_lost++; 752 host->icmp_lost++;
436 /* don't spend time on lost hosts any more */ 753 /* don't spend time on lost hosts any more */
437 if (host->flags & FLAG_LOST_CAUSE) { 754 if (host->flags & FLAG_LOST_CAUSE) {
@@ -440,305 +757,114 @@ static int handle_random_icmp(unsigned char *packet, struct sockaddr_storage *ad
440 757
441 /* source quench means we're sending too fast, so increase the 758 /* source quench means we're sending too fast, so increase the
442 * interval and mark this packet lost */ 759 * interval and mark this packet lost */
443 if (p.icmp_type == ICMP_SOURCEQUENCH) { 760 if (icmp_packet.icmp_type == ICMP_SOURCEQUENCH) {
444 pkt_interval *= pkt_backoff_factor; 761 *pkt_interval = (unsigned int)((double)*pkt_interval * PACKET_BACKOFF_FACTOR);
445 target_interval *= target_backoff_factor; 762 *target_interval = (unsigned int)((double)*target_interval * TARGET_BACKOFF_FACTOR);
446 } else { 763 } else {
447 targets_down++; 764 program_state->targets_down++;
448 host->flags |= FLAG_LOST_CAUSE; 765 host->flags |= FLAG_LOST_CAUSE;
449 } 766 }
450 host->icmp_type = p.icmp_type; 767 host->icmp_type = icmp_packet.icmp_type;
451 host->icmp_code = p.icmp_code; 768 host->icmp_code = icmp_packet.icmp_code;
452 host->error_addr = *addr; 769 host->error_addr = *addr;
453 770
454 return 0; 771 return 0;
455} 772}
456 773
457void parse_address(struct sockaddr_storage *addr, char *address, int size) { 774void parse_address(const struct sockaddr_storage *addr, char *dst, socklen_t size) {
458 switch (address_family) { 775 switch (addr->ss_family) {
459 case AF_INET: 776 case AF_INET:
460 inet_ntop(address_family, &((struct sockaddr_in *)addr)->sin_addr, address, size); 777 inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, dst, size);
461 break; 778 break;
462 case AF_INET6: 779 case AF_INET6:
463 inet_ntop(address_family, &((struct sockaddr_in6 *)addr)->sin6_addr, address, size); 780 inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, dst, size);
464 break; 781 break;
782 default:
783 assert(false);
465 } 784 }
466} 785}
467 786
468int main(int argc, char **argv) { 787int 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, ""); 788 setlocale(LC_ALL, "");
484 bindtextdomain(PACKAGE, LOCALEDIR); 789 bindtextdomain(PACKAGE, LOCALEDIR);
485 textdomain(PACKAGE); 790 textdomain(PACKAGE);
486 791
487 /* we only need to be setsuid when we get the sockets, so do 792 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */
488 * that before pointer magic (esp. on network data) */ 793 environ = NULL;
489 icmp_sockerrno = udp_sockerrno = tcp_sockerrno = sockets = 0;
490
491 address_family = -1;
492 int icmp_proto = IPPROTO_ICMP;
493 794
494 /* get calling name the old-fashioned way for portability instead 795 /* Parse extra opts if any */
495 * of relying on the glibc-ism __progname */ 796 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 797
503 /* now set defaults. Use progname to set them initially (allows for 798 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 799
526 if (!strcmp(progname, "check_icmp") || !strcmp(progname, "check_ping")) { 800 if (tmp_config.errorcode != OK) {
527 mode = MODE_ICMP; 801 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 } 802 }
541 803
542 /* support "--help" and "--version" */ 804 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 805
552 /* Parse protocol arguments first */ 806 // int icmp_proto = IPPROTO_ICMP;
553 for (i = 1; i < argc; i++) { 807 // add_target might change address_family
554 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 808 // switch (address_family) {
555 switch (arg) { 809 // case AF_INET:
556 case '4': 810 // icmp_proto = IPPROTO_ICMP;
557 if (address_family != -1) { 811 // break;
558 crash("Multiple protocol versions not supported"); 812 // case AF_INET6:
559 } 813 // icmp_proto = IPPROTO_ICMPV6;
560 address_family = AF_INET; 814 // break;
561 break; 815 // default:
562 case '6': 816 // crash("Address family not supported");
563#ifdef USE_IPV6 817 // }
564 if (address_family != -1) { 818
565 crash("Multiple protocol versions not supported"); 819 check_icmp_socket_set sockset = {
566 } 820 .socket4 = -1,
567 address_family = AF_INET6; 821 .socket6 = -1,
568#else 822 };
569 usage(_("IPv6 support not available\n")); 823
570#endif 824 if (config.need_v4) {
571 break; 825 sockset.socket4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
572 } 826 if (sockset.socket4 == -1) {
827 crash("Failed to obtain ICMP v4 socket");
573 } 828 }
574 }
575 829
576 /* Reset argument scanning */ 830 if (config.source_ip) {
577 optind = 1;
578 831
579 unsigned long size; 832 struct in_addr tmp = {};
580 bool err; 833 int error_code = inet_pton(AF_INET, config.source_ip, &tmp);
581 /* parse the arguments */ 834 if (error_code == 1) {
582 for (i = 1; i < argc; i++) { 835 set_source_ip(config.source_ip, sockset.socket4, AF_INET);
583 while ((arg = getopt(argc, argv, opts_str)) != EOF) { 836 } else {
584 switch (arg) { 837 // just try this mindlessly if it's not a v4 address
585 case 'v': 838 set_source_ip(config.source_ip, sockset.socket6, AF_INET6);
586 debug++; 839 }
587 break; 840 }
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
651 rta_mode = true;
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
659 pl_mode = true;
660 break;
661 case 'J': /* jitter mode */
662 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_jitter_mode);
663 if (!err) {
664 crash("Failed to parse jitter threshold");
665 }
666 841
667 jitter_mode = true; 842#ifdef SO_TIMESTAMP
668 break; 843 if (sockset.socket4 != -1) {
669 case 'M': /* MOS mode */ 844 int on = 1;
670 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_mos_mode); 845 if (setsockopt(sockset.socket4, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
671 if (!err) { 846 if (debug) {
672 crash("Failed to parse MOS threshold"); 847 printf("Warning: no SO_TIMESTAMP support\n");
673 } 848 }
674 849 }
675 mos_mode = true; 850 }
676 break; 851 if (sockset.socket6 != -1) {
677 case 'S': /* score mode */ 852 int on = 1;
678 err = get_threshold2(optarg, strlen(optarg), &warn, &crit, const_score_mode); 853 if (setsockopt(sockset.socket6, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) {
679 if (!err) { 854 if (debug) {
680 crash("Failed to parse score threshold"); 855 printf("Warning: no SO_TIMESTAMP support\n");
681 } 856 }
682
683 score_mode = true;
684 break;
685 case 'O': /* out of order mode */
686 order_mode = true;
687 break;
688 } 857 }
689 } 858 }
859#endif // SO_TIMESTAMP
690 } 860 }
691 861
692 /* POSIXLY_CORRECT might break things, so unset it (the portable way) */ 862 if (config.need_v6) {
693 environ = NULL; 863 sockset.socket6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
694 864 if (sockset.socket6 == -1) {
695 /* use the pid to mark packets as ours */ 865 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 } 866 }
740 } 867 }
741#endif // SO_TIMESTAMP
742 868
743 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */ 869 /* now drop privileges (no effect if not setsuid or geteuid() == 0) */
744 if (setuid(getuid()) == -1) { 870 if (setuid(getuid()) == -1) {
@@ -746,186 +872,184 @@ int main(int argc, char **argv) {
746 return 1; 872 return 1;
747 } 873 }
748 874
749 if (!sockets) { 875 if (sockset.socket4) {
750 if (icmp_sock == -1) { 876 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) { 877 if (debug) {
773 if (result == -1) { 878 if (result == -1) {
774 printf("setsockopt failed\n"); 879 printf("setsockopt failed\n");
775 } else { 880 } else {
776 printf("ttl set to %u\n", ttl); 881 printf("ttl set to %lu\n", config.ttl);
777 } 882 }
778 } 883 }
779 } 884 }
780 885
781 /* stupid users should be able to give whatever thresholds they want 886 if (sockset.socket6) {
782 * (nothing will break if they do), but some anal plugin maintainer 887 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 888 if (debug) {
784 * best to at least show them where to do it. ;) */ 889 if (result == -1) {
785 if (warn.pl > crit.pl) { 890 printf("setsockopt failed\n");
786 warn.pl = crit.pl; 891 } else {
787 } 892 printf("ttl set to %lu\n", config.ttl);
788 if (warn.rta > crit.rta) { 893 }
789 warn.rta = crit.rta; 894 }
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 } 895 }
822 alarm(timeout);
823 896
824 /* make sure we don't wait any longer than necessary */ 897 /* make sure we don't wait any longer than necessary */
825 gettimeofday(&prog_start, &tz); 898 struct timeval prog_start;
826 max_completion_time = ((targets * packets * pkt_interval) + (targets * target_interval)) + (targets * packets * crit.rta) + crit.rta; 899 gettimeofday(&prog_start, NULL);
900
901 time_t max_completion_time =
902 ((config.pkt_interval * config.number_of_targets * config.number_of_packets) +
903 (config.target_interval * config.number_of_targets)) +
904 (config.crit.rta * config.number_of_targets * config.number_of_packets) + config.crit.rta;
827 905
828 if (debug) { 906 if (debug) {
829 printf("packets: %u, targets: %u\n" 907 printf("packets: %u, targets: %u\n"
830 "target_interval: %0.3f, pkt_interval %0.3f\n" 908 "target_interval: %0.3f, pkt_interval %0.3f\n"
831 "crit.rta: %0.3f\n" 909 "crit.rta: %0.3f\n"
832 "max_completion_time: %0.3f\n", 910 "max_completion_time: %0.3f\n",
833 packets, targets, (float)target_interval / 1000, (float)pkt_interval / 1000, (float)crit.rta / 1000, 911 config.number_of_packets, config.number_of_targets,
834 (float)max_completion_time / 1000); 912 (float)config.target_interval / 1000, (float)config.pkt_interval / 1000,
913 (float)config.crit.rta / 1000, (float)max_completion_time / 1000);
835 } 914 }
836 915
837 if (debug) { 916 if (debug) {
838 if (max_completion_time > (u_int)timeout * 1000000) { 917 if (max_completion_time > (timeout * 1000000)) {
839 printf("max_completion_time: %llu timeout: %u\n", max_completion_time, timeout); 918 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); 919 printf("Timeout must be at least %ld\n", (max_completion_time / 1000000) + 1);
841 } 920 }
842 } 921 }
843 922
844 if (debug) { 923 if (debug) {
845 printf("crit = {%u, %u%%}, warn = {%u, %u%%}\n", crit.rta, crit.pl, warn.rta, warn.pl); 924 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); 925 config.warn.rta, config.warn.pl);
847 printf("icmp_pkt_size: %u timeout: %u\n", icmp_pkt_size, timeout); 926 printf("pkt_interval: %ld target_interval: %ld\n", config.pkt_interval,
927 config.target_interval);
928 printf("icmp_pkt_size: %u timeout: %u\n", config.icmp_pkt_size, timeout);
848 } 929 }
849 930
850 if (packets > 20) { 931 if (config.min_hosts_alive < -1) {
851 errno = 0; 932 errno = 0;
852 crash("packets is > 20 (%d)", packets); 933 crash("minimum alive hosts is negative (%i)", config.min_hosts_alive);
853 } 934 }
854 935
855 if (min_hosts_alive < -1) { 936 // Build an index table of all targets
856 errno = 0; 937 ping_target *host = config.targets;
857 crash("minimum alive hosts is negative (%i)", min_hosts_alive); 938 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) { 939 if (!table) {
863 crash("main(): malloc failed for host table"); 940 crash("main(): malloc failed for host table");
864 } 941 }
865 942
866 i = 0; 943 unsigned short target_index = 0;
867 while (host) { 944 while (host) {
868 host->id = i * packets; 945 host->id = target_index * config.number_of_packets;
869 table[i] = host; 946 table[target_index] = host;
870 host = host->next; 947 host = host->next;
871 i++; 948 target_index++;
872 } 949 }
873 950
874 run_checks(); 951 time_t pkt_interval = config.pkt_interval;
952 time_t target_interval = config.target_interval;
953
954 check_icmp_state program_state = check_icmp_state_init();
955
956 run_checks(config.icmp_data_size, &pkt_interval, &target_interval, config.sender_id,
957 config.mode, max_completion_time, prog_start, table, config.number_of_packets,
958 sockset, config.number_of_targets, &program_state);
875 959
876 errno = 0; 960 errno = 0;
877 finish(0);
878 961
879 return (0); 962 mp_check overall = mp_check_init();
880} 963 finish(0, config.modes, config.min_hosts_alive, config.warn, config.crit,
964 config.number_of_targets, &program_state, config.hosts, config.number_of_hosts,
965 &overall);
881 966
882static void run_checks(void) { 967 if (sockset.socket4) {
883 u_int i, t; 968 close(sockset.socket4);
884 u_int final_wait, time_passed; 969 }
970 if (sockset.socket6) {
971 close(sockset.socket6);
972 }
973
974 mp_exit(overall);
975}
885 976
977static void run_checks(unsigned short icmp_pkt_size, time_t *pkt_interval, time_t *target_interval,
978 const uint16_t sender_id, const check_icmp_execution_mode mode,
979 const time_t max_completion_time, const struct timeval prog_start,
980 ping_target **table, const unsigned short packets,
981 const check_icmp_socket_set sockset, const unsigned short number_of_targets,
982 check_icmp_state *program_state) {
886 /* this loop might actually violate the pkt_interval or target_interval 983 /* 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 984 * settings, but only if there aren't any packets on the wire which
888 * indicates that the target can handle an increased packet rate */ 985 * indicates that the target can handle an increased packet rate */
889 for (i = 0; i < packets; i++) { 986 for (unsigned int packet_index = 0; packet_index < packets; packet_index++) {
890 for (t = 0; t < targets; t++) { 987 for (unsigned int target_index = 0; target_index < number_of_targets; target_index++) {
891 /* don't send useless packets */ 988 /* don't send useless packets */
892 if (!targets_alive) { 989 if (!targets_alive(number_of_targets, program_state->targets_down)) {
893 finish(0); 990 return;
894 } 991 }
895 if (table[t]->flags & FLAG_LOST_CAUSE) { 992 if (table[target_index]->flags & FLAG_LOST_CAUSE) {
896 if (debug) { 993 if (debug) {
897 printf("%s is a lost cause. not sending any more\n", table[t]->name); 994
995 char address[INET6_ADDRSTRLEN];
996 parse_address(&table[target_index]->address, address, sizeof(address));
997 printf("%s is a lost cause. not sending any more\n", address);
898 } 998 }
899 continue; 999 continue;
900 } 1000 }
901 1001
902 /* we're still in the game, so send next packet */ 1002 /* we're still in the game, so send next packet */
903 (void)send_icmp_ping(icmp_sock, table[t]); 1003 (void)send_icmp_ping(sockset, table[target_index], icmp_pkt_size, sender_id,
904 wait_for_reply(icmp_sock, target_interval); 1004 program_state);
1005
1006 /* wrap up if all targets are declared dead */
1007 if (targets_alive(number_of_targets, program_state->targets_down) ||
1008 get_timevaldiff(prog_start, prog_start) < max_completion_time ||
1009 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1010 wait_for_reply(sockset, *target_interval, icmp_pkt_size, pkt_interval,
1011 target_interval, sender_id, table, packets, number_of_targets,
1012 program_state);
1013 }
1014 }
1015 if (targets_alive(number_of_targets, program_state->targets_down) ||
1016 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1017 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1018 wait_for_reply(sockset, *pkt_interval * number_of_targets, icmp_pkt_size, pkt_interval,
1019 target_interval, sender_id, table, packets, number_of_targets,
1020 program_state);
905 } 1021 }
906 wait_for_reply(icmp_sock, pkt_interval * targets);
907 } 1022 }
908 1023
909 if (icmp_pkts_en_route && targets_alive) { 1024 if (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
910 time_passed = get_timevaldiff(NULL, NULL); 1025 program_state->icmp_lost) &&
911 final_wait = max_completion_time - time_passed; 1026 targets_alive(number_of_targets, program_state->targets_down)) {
1027 time_t time_passed = get_timevaldiff_to_now(prog_start);
1028 time_t final_wait = max_completion_time - time_passed;
912 1029
913 if (debug) { 1030 if (debug) {
914 printf("time_passed: %u final_wait: %u max_completion_time: %llu\n", time_passed, final_wait, max_completion_time); 1031 printf("time_passed: %ld final_wait: %ld max_completion_time: %ld\n", time_passed,
1032 final_wait, max_completion_time);
915 } 1033 }
916 if (time_passed > max_completion_time) { 1034 if (time_passed > max_completion_time) {
917 if (debug) { 1035 if (debug) {
918 printf("Time passed. Finishing up\n"); 1036 printf("Time passed. Finishing up\n");
919 } 1037 }
920 finish(0); 1038 return;
921 } 1039 }
922 1040
923 /* catch the packets that might come in within the timeframe, but 1041 /* catch the packets that might come in within the timeframe, but
924 * haven't yet */ 1042 * haven't yet */
925 if (debug) { 1043 if (debug) {
926 printf("Waiting for %u micro-seconds (%0.3f msecs)\n", final_wait, (float)final_wait / 1000); 1044 printf("Waiting for %ld micro-seconds (%0.3f msecs)\n", final_wait,
1045 (float)final_wait / 1000);
1046 }
1047 if (targets_alive(number_of_targets, program_state->targets_down) ||
1048 get_timevaldiff_to_now(prog_start) < max_completion_time ||
1049 !(mode == MODE_HOSTCHECK && program_state->targets_down)) {
1050 wait_for_reply(sockset, final_wait, icmp_pkt_size, pkt_interval, target_interval,
1051 sender_id, table, packets, number_of_targets, program_state);
927 } 1052 }
928 wait_for_reply(icmp_sock, final_wait);
929 } 1053 }
930} 1054}
931 1055
@@ -939,18 +1063,12 @@ static void run_checks(void) {
939 * both: 1063 * both:
940 * icmp echo reply : the rest 1064 * icmp echo reply : the rest
941 */ 1065 */
942static int wait_for_reply(int sock, u_int t) { 1066static int wait_for_reply(check_icmp_socket_set sockset, const time_t time_interval,
943 int n, hlen; 1067 unsigned short icmp_pkt_size, time_t *pkt_interval,
944 static unsigned char buf[65536]; 1068 time_t *target_interval, uint16_t sender_id, ping_target **table,
945 struct sockaddr_storage resp_addr; 1069 const unsigned short packets, const unsigned short number_of_targets,
946 union ip_hdr *ip; 1070 check_icmp_state *program_state) {
947 union icmp_packet packet; 1071 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))) { 1072 if (!(packet.buf = malloc(icmp_pkt_size))) {
955 crash("send_icmp_ping(): failed to malloc %d bytes for send buffer", icmp_pkt_size); 1073 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 */ 1074 return -1; /* might be reached if we're in debug mode */
@@ -959,177 +1077,174 @@ static int wait_for_reply(int sock, u_int t) {
959 memset(packet.buf, 0, icmp_pkt_size); 1077 memset(packet.buf, 0, icmp_pkt_size);
960 1078
961 /* if we can't listen or don't have anything to listen to, just return */ 1079 /* if we can't listen or don't have anything to listen to, just return */
962 if (!t || !icmp_pkts_en_route) { 1080 if (!time_interval || !icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
1081 program_state->icmp_lost)) {
963 free(packet.buf); 1082 free(packet.buf);
964 return 0; 1083 return 0;
965 } 1084 }
966 1085
967 gettimeofday(&wait_start, &tz); 1086 // Get current time stamp
1087 struct timeval wait_start;
1088 gettimeofday(&wait_start, NULL);
968 1089
969 i = t; 1090 struct sockaddr_storage resp_addr;
970 per_pkt_wait = t / icmp_pkts_en_route; 1091 time_t per_pkt_wait =
971 while (icmp_pkts_en_route && get_timevaldiff(&wait_start, NULL) < i) { 1092 time_interval / icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
972 t = per_pkt_wait; 1093 program_state->icmp_lost);
973 1094 static unsigned char buf[65536];
974 /* wrap up if all targets are declared dead */ 1095 union ip_hdr *ip_header;
975 if (!targets_alive || get_timevaldiff(&prog_start, NULL) >= max_completion_time || (mode == MODE_HOSTCHECK && targets_down)) { 1096 struct timeval packet_received_timestamp;
976 finish(0); 1097 while (icmp_pkts_en_route(program_state->icmp_sent, program_state->icmp_recv,
977 } 1098 program_state->icmp_lost) &&
1099 get_timevaldiff_to_now(wait_start) < time_interval) {
1100 time_t loop_time_interval = per_pkt_wait;
978 1101
979 /* reap responses until we hit a timeout */ 1102 /* reap responses until we hit a timeout */
980 n = recvfrom_wto(sock, buf, sizeof(buf), (struct sockaddr *)&resp_addr, &t, &now); 1103 recvfrom_wto_wrapper recv_foo =
981 if (!n) { 1104 recvfrom_wto(sockset, buf, sizeof(buf), (struct sockaddr *)&resp_addr,
1105 &loop_time_interval, &packet_received_timestamp);
1106 if (!recv_foo.received) {
982 if (debug > 1) { 1107 if (debug > 1) {
983 printf("recvfrom_wto() timed out during a %u usecs wait\n", per_pkt_wait); 1108 printf("recvfrom_wto() timed out during a %ld usecs wait\n", per_pkt_wait);
984 } 1109 }
985 continue; /* timeout for this one, so keep trying */ 1110 continue; /* timeout for this one, so keep trying */
986 } 1111 }
987 if (n < 0) { 1112
1113 if (recv_foo.received < 0) {
988 if (debug) { 1114 if (debug) {
989 printf("recvfrom_wto() returned errors\n"); 1115 printf("recvfrom_wto() returned errors\n");
990 } 1116 }
991 free(packet.buf); 1117 free(packet.buf);
992 return n; 1118 return (int)recv_foo.received;
993 } 1119 }
994 1120
995 // FIXME: with ipv6 we don't have an ip header here 1121 if (recv_foo.recv_proto != AF_INET6) {
996 if (address_family != AF_INET6) { 1122 ip_header = (union ip_hdr *)buf;
997 ip = (union ip_hdr *)buf;
998 1123
999 if (debug > 1) { 1124 if (debug > 1) {
1000 char address[INET6_ADDRSTRLEN]; 1125 char address[INET6_ADDRSTRLEN];
1001 parse_address(&resp_addr, address, sizeof(address)); 1126 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); 1127 printf("received %u bytes from %s\n",
1128 address_family == AF_INET6 ? ntohs(ip_header->ip6.ip6_plen)
1129 : ntohs(ip_header->ip.ip_len),
1130 address);
1003 } 1131 }
1004 } 1132 }
1005 1133
1006 /* obsolete. alpha on tru64 provides the necessary defines, but isn't broken */ 1134 int hlen = (recv_foo.recv_proto == AF_INET6) ? 0 : ip_header->ip.ip_hl << 2;
1007 /* #if defined( __alpha__ ) && __STDC__ && !defined( __GLIBC__ ) */ 1135
1008 /* alpha headers are decidedly broken. Using an ansi compiler, 1136 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]; 1137 char address[INET6_ADDRSTRLEN];
1018 parse_address(&resp_addr, address, sizeof(address)); 1138 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); 1139 crash("received packet too short for ICMP (%ld bytes, expected %d) from %s\n",
1140 recv_foo.received, hlen + icmp_pkt_size, address);
1020 } 1141 }
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 */ 1142 /* check the response */
1028
1029 memcpy(packet.buf, buf + hlen, icmp_pkt_size); 1143 memcpy(packet.buf, buf + hlen, icmp_pkt_size);
1030 /* address_family == AF_INET6 ? sizeof(struct icmp6_hdr)
1031 : sizeof(struct icmp));*/
1032 1144
1033 if ((address_family == PF_INET && (ntohs(packet.icp->icmp_id) != pid || packet.icp->icmp_type != ICMP_ECHOREPLY || 1145 if ((recv_foo.recv_proto == AF_INET &&
1034 ntohs(packet.icp->icmp_seq) >= targets * packets)) || 1146 (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 || 1147 ntohs(packet.icp->icmp_seq) >= number_of_targets * packets)) ||
1036 ntohs(packet.icp6->icmp6_seq) >= targets * packets))) { 1148 (recv_foo.recv_proto == AF_INET6 &&
1149 (ntohs(packet.icp6->icmp6_id) != sender_id ||
1150 packet.icp6->icmp6_type != ICMP6_ECHO_REPLY ||
1151 ntohs(packet.icp6->icmp6_seq) >= number_of_targets * packets))) {
1037 if (debug > 2) { 1152 if (debug > 2) {
1038 printf("not a proper ICMP_ECHOREPLY\n"); 1153 printf("not a proper ICMP_ECHOREPLY\n");
1039 } 1154 }
1040 handle_random_icmp(buf + hlen, &resp_addr); 1155
1156 handle_random_icmp(buf + hlen, &resp_addr, pkt_interval, target_interval, sender_id,
1157 table, packets, number_of_targets, program_state);
1158
1041 continue; 1159 continue;
1042 } 1160 }
1043 1161
1044 /* this is indeed a valid response */ 1162 /* this is indeed a valid response */
1045 if (address_family == PF_INET) { 1163 ping_target *target;
1164 struct icmp_ping_data data;
1165 if (address_family == AF_INET) {
1046 memcpy(&data, packet.icp->icmp_data, sizeof(data)); 1166 memcpy(&data, packet.icp->icmp_data, sizeof(data));
1047 if (debug > 2) { 1167 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), 1168 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); 1169 ntohs(packet.icp->icmp_id), ntohs(packet.icp->icmp_seq),
1170 packet.icp->icmp_cksum);
1050 } 1171 }
1051 host = table[ntohs(packet.icp->icmp_seq) / packets]; 1172 target = table[ntohs(packet.icp->icmp_seq) / packets];
1052 } else { 1173 } else {
1053 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data)); 1174 memcpy(&data, &packet.icp6->icmp6_dataun.icmp6_un_data8[4], sizeof(data));
1054 if (debug > 2) { 1175 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), 1176 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); 1177 ntohs(packet.icp6->icmp6_id), ntohs(packet.icp6->icmp6_seq),
1178 packet.icp6->icmp6_cksum);
1057 } 1179 }
1058 host = table[ntohs(packet.icp6->icmp6_seq) / packets]; 1180 target = table[ntohs(packet.icp6->icmp6_seq) / packets];
1059 } 1181 }
1060 1182
1061 tdiff = get_timevaldiff(&data.stime, &now); 1183 time_t tdiff = get_timevaldiff(data.stime, packet_received_timestamp);
1062 1184
1063 if (host->last_tdiff > 0) { 1185 if (target->last_tdiff > 0) {
1064 /* Calculate jitter */ 1186 /* Calculate jitter */
1065 if (host->last_tdiff > tdiff) { 1187 double jitter_tmp;
1066 jitter_tmp = host->last_tdiff - tdiff; 1188 if (target->last_tdiff > tdiff) {
1189 jitter_tmp = (double)(target->last_tdiff - tdiff);
1067 } else { 1190 } else {
1068 jitter_tmp = tdiff - host->last_tdiff; 1191 jitter_tmp = (double)(tdiff - target->last_tdiff);
1069 } 1192 }
1070 1193
1071 if (host->jitter == 0) { 1194 if (target->jitter == 0) {
1072 host->jitter = jitter_tmp; 1195 target->jitter = jitter_tmp;
1073 host->jitter_max = jitter_tmp; 1196 target->jitter_max = jitter_tmp;
1074 host->jitter_min = jitter_tmp; 1197 target->jitter_min = jitter_tmp;
1075 } else { 1198 } else {
1076 host->jitter += jitter_tmp; 1199 target->jitter += jitter_tmp;
1077 1200
1078 if (jitter_tmp < host->jitter_min) { 1201 if (jitter_tmp < target->jitter_min) {
1079 host->jitter_min = jitter_tmp; 1202 target->jitter_min = jitter_tmp;
1080 } 1203 }
1081 1204
1082 if (jitter_tmp > host->jitter_max) { 1205 if (jitter_tmp > target->jitter_max) {
1083 host->jitter_max = jitter_tmp; 1206 target->jitter_max = jitter_tmp;
1084 } 1207 }
1085 } 1208 }
1086 1209
1087 /* Check if packets in order */ 1210 /* Check if packets in order */
1088 if (host->last_icmp_seq >= packet.icp->icmp_seq) { 1211 if (target->last_icmp_seq >= packet.icp->icmp_seq) {
1089 host->order_status = STATE_CRITICAL; 1212 target->found_out_of_order_packets = true;
1090 } 1213 }
1091 } 1214 }
1092 host->last_tdiff = tdiff; 1215 target->last_tdiff = tdiff;
1093 1216
1094 host->last_icmp_seq = packet.icp->icmp_seq; 1217 target->last_icmp_seq = packet.icp->icmp_seq;
1095 1218
1096 host->time_waited += tdiff; 1219 target->time_waited += tdiff;
1097 host->icmp_recv++; 1220 target->icmp_recv++;
1098 icmp_recv++; 1221 program_state->icmp_recv++;
1099 1222
1100 if (tdiff > (unsigned int)host->rtmax) { 1223 if (tdiff > (unsigned int)target->rtmax) {
1101 host->rtmax = tdiff; 1224 target->rtmax = (double)tdiff;
1102 } 1225 }
1103 1226
1104 if ((host->rtmin == INFINITY) || (tdiff < (unsigned int)host->rtmin)) { 1227 if ((target->rtmin == INFINITY) || (tdiff < (unsigned int)target->rtmin)) {
1105 host->rtmin = tdiff; 1228 target->rtmin = (double)tdiff;
1106 } 1229 }
1107 1230
1108 if (debug) { 1231 if (debug) {
1109 char address[INET6_ADDRSTRLEN]; 1232 char address[INET6_ADDRSTRLEN];
1110 parse_address(&resp_addr, address, sizeof(address)); 1233 parse_address(&resp_addr, address, sizeof(address));
1111 1234
1112 switch (address_family) { 1235 switch (recv_foo.recv_proto) {
1113 case AF_INET: { 1236 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, 1237 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); 1238 (float)tdiff / 1000, address, ip_header->ip.ip_ttl,
1239 (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1116 break; 1240 break;
1117 }; 1241 };
1118 case AF_INET6: { 1242 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, 1243 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); 1244 address, (float)target->rtmax / 1000, (float)target->rtmin / 1000);
1121 }; 1245 };
1122 } 1246 }
1123 } 1247 }
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 } 1248 }
1134 1249
1135 free(packet.buf); 1250 free(packet.buf);
@@ -1137,38 +1252,28 @@ static int wait_for_reply(int sock, u_int t) {
1137} 1252}
1138 1253
1139/* the ping functions */ 1254/* the ping functions */
1140static int send_icmp_ping(int sock, struct rta_host *host) { 1255static int send_icmp_ping(const check_icmp_socket_set sockset, ping_target *host,
1141 long int len; 1256 const unsigned short icmp_pkt_size, const uint16_t sender_id,
1142 size_t addrlen; 1257 check_icmp_state *program_state) {
1143 struct icmp_ping_data data; 1258 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) { 1259 if (!buf) {
1156 if (!(buf = malloc(icmp_pkt_size))) { 1260 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); 1261 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 } 1262 }
1161 memset(buf, 0, icmp_pkt_size);
1162 1263
1163 if ((gettimeofday(&tv, &tz)) == -1) { 1264 struct timeval current_time;
1265 if ((gettimeofday(&current_time, NULL)) == -1) {
1164 free(buf); 1266 free(buf);
1165 return -1; 1267 return -1;
1166 } 1268 }
1167 1269
1270 struct icmp_ping_data data;
1168 data.ping_id = 10; /* host->icmp.icmp_sent; */ 1271 data.ping_id = 10; /* host->icmp.icmp_sent; */
1169 memcpy(&data.stime, &tv, sizeof(tv)); 1272 memcpy(&data.stime, &current_time, sizeof(current_time));
1273
1274 socklen_t addrlen = 0;
1170 1275
1171 if (address_family == AF_INET) { 1276 if (host->address.ss_family == AF_INET) {
1172 struct icmp *icp = (struct icmp *)buf; 1277 struct icmp *icp = (struct icmp *)buf;
1173 addrlen = sizeof(struct sockaddr_in); 1278 addrlen = sizeof(struct sockaddr_in);
1174 1279
@@ -1177,15 +1282,19 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1177 icp->icmp_type = ICMP_ECHO; 1282 icp->icmp_type = ICMP_ECHO;
1178 icp->icmp_code = 0; 1283 icp->icmp_code = 0;
1179 icp->icmp_cksum = 0; 1284 icp->icmp_cksum = 0;
1180 icp->icmp_id = htons(pid); 1285 icp->icmp_id = htons((uint16_t)sender_id);
1181 icp->icmp_seq = htons(host->id++); 1286 icp->icmp_seq = htons(host->id++);
1182 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size); 1287 icp->icmp_cksum = icmp_checksum((uint16_t *)buf, (size_t)icmp_pkt_size);
1183 1288
1184 if (debug > 2) { 1289 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), 1290 char address[INET6_ADDRSTRLEN];
1186 ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum, host->name); 1291 parse_address((&host->address), address, sizeof(address));
1292
1293 printf("Sending ICMP echo-request of len %lu, id %u, seq %u, cksum 0x%X to host %s\n",
1294 sizeof(data), ntohs(icp->icmp_id), ntohs(icp->icmp_seq), icp->icmp_cksum,
1295 address);
1187 } 1296 }
1188 } else { 1297 } else if (host->address.ss_family == AF_INET6) {
1189 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf; 1298 struct icmp6_hdr *icp6 = (struct icmp6_hdr *)buf;
1190 addrlen = sizeof(struct sockaddr_in6); 1299 addrlen = sizeof(struct sockaddr_in6);
1191 1300
@@ -1194,659 +1303,439 @@ static int send_icmp_ping(int sock, struct rta_host *host) {
1194 icp6->icmp6_type = ICMP6_ECHO_REQUEST; 1303 icp6->icmp6_type = ICMP6_ECHO_REQUEST;
1195 icp6->icmp6_code = 0; 1304 icp6->icmp6_code = 0;
1196 icp6->icmp6_cksum = 0; 1305 icp6->icmp6_cksum = 0;
1197 icp6->icmp6_id = htons(pid); 1306 icp6->icmp6_id = htons((uint16_t)sender_id);
1198 icp6->icmp6_seq = htons(host->id++); 1307 icp6->icmp6_seq = htons(host->id++);
1199 // let checksum be calculated automatically 1308 // let checksum be calculated automatically
1200 1309
1201 if (debug > 2) { 1310 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), 1311 char address[INET6_ADDRSTRLEN];
1203 ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_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 target %s\n",
1315 sizeof(data), ntohs(icp6->icmp6_id), ntohs(icp6->icmp6_seq), icp6->icmp6_cksum,
1316 address);
1204 } 1317 }
1318 } else {
1319 // unknown address family
1320 crash("unknown address family in %s", __func__);
1205 } 1321 }
1206 1322
1323 struct iovec iov;
1207 memset(&iov, 0, sizeof(iov)); 1324 memset(&iov, 0, sizeof(iov));
1208 iov.iov_base = buf; 1325 iov.iov_base = buf;
1209 iov.iov_len = icmp_pkt_size; 1326 iov.iov_len = icmp_pkt_size;
1210 1327
1328 struct msghdr hdr;
1211 memset(&hdr, 0, sizeof(hdr)); 1329 memset(&hdr, 0, sizeof(hdr));
1212 hdr.msg_name = (struct sockaddr *)&host->saddr_in; 1330 hdr.msg_name = (struct sockaddr *)&host->address;
1213 hdr.msg_namelen = addrlen; 1331 hdr.msg_namelen = addrlen;
1214 hdr.msg_iov = &iov; 1332 hdr.msg_iov = &iov;
1215 hdr.msg_iovlen = 1; 1333 hdr.msg_iovlen = 1;
1216 1334
1217 errno = 0; 1335 errno = 0;
1218 1336
1219/* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */ 1337 long int len;
1338 /* MSG_CONFIRM is a linux thing and only available on linux kernels >= 2.3.15, see send(2) */
1339 if (host->address.ss_family == AF_INET) {
1340#ifdef MSG_CONFIRM
1341 len = sendmsg(sockset.socket4, &hdr, MSG_CONFIRM);
1342#else
1343 len = sendmsg(sockset.socket4, &hdr, 0);
1344#endif
1345 } else if (host->address.ss_family == AF_INET6) {
1220#ifdef MSG_CONFIRM 1346#ifdef MSG_CONFIRM
1221 len = sendmsg(sock, &hdr, MSG_CONFIRM); 1347 len = sendmsg(sockset.socket6, &hdr, MSG_CONFIRM);
1222#else 1348#else
1223 len = sendmsg(sock, &hdr, 0); 1349 len = sendmsg(sockset.socket6, &hdr, 0);
1224#endif 1350#endif
1351 } else {
1352 assert(false);
1353 }
1225 1354
1226 free(buf); 1355 free(buf);
1227 1356
1228 if (len < 0 || (unsigned int)len != icmp_pkt_size) { 1357 if (len < 0 || (unsigned int)len != icmp_pkt_size) {
1229 if (debug) { 1358 if (debug) {
1230 char address[INET6_ADDRSTRLEN]; 1359 char address[INET6_ADDRSTRLEN];
1231 parse_address((struct sockaddr_storage *)&host->saddr_in, address, sizeof(address)); 1360 parse_address((&host->address), address, sizeof(address));
1232 printf("Failed to send ping to %s: %s\n", address, strerror(errno)); 1361 printf("Failed to send ping to %s: %s\n", address, strerror(errno));
1233 } 1362 }
1234 errno = 0; 1363 errno = 0;
1235 return -1; 1364 return -1;
1236 } 1365 }
1237 1366
1238 icmp_sent++; 1367 program_state->icmp_sent++;
1239 host->icmp_sent++; 1368 host->icmp_sent++;
1240 1369
1241 return 0; 1370 return 0;
1242} 1371}
1243 1372
1244static int recvfrom_wto(int sock, void *buf, unsigned int len, struct sockaddr *saddr, u_int *timo, struct timeval *tv) { 1373static recvfrom_wto_wrapper recvfrom_wto(const check_icmp_socket_set sockset, void *buf,
1245 u_int slen; 1374 const unsigned int len, struct sockaddr *saddr,
1246 int n, ret; 1375 time_t *timeout, struct timeval *received_timestamp) {
1247 struct timeval to, then, now;
1248 fd_set rd, wr;
1249#ifdef HAVE_MSGHDR_MSG_CONTROL 1376#ifdef HAVE_MSGHDR_MSG_CONTROL
1250 char ans_data[4096]; 1377 char ans_data[4096];
1251#endif // HAVE_MSGHDR_MSG_CONTROL 1378#endif // HAVE_MSGHDR_MSG_CONTROL
1252 struct msghdr hdr;
1253 struct iovec iov;
1254#ifdef SO_TIMESTAMP 1379#ifdef SO_TIMESTAMP
1255 struct cmsghdr *chdr; 1380 struct cmsghdr *chdr;
1256#endif 1381#endif
1257 1382
1258 if (!*timo) { 1383 recvfrom_wto_wrapper result = {
1384 .received = 0,
1385 .recv_proto = AF_UNSPEC,
1386 };
1387
1388 if (!*timeout) {
1259 if (debug) { 1389 if (debug) {
1260 printf("*timo is not\n"); 1390 printf("*timeout is not\n");
1261 } 1391 }
1262 return 0; 1392 return result;
1393 }
1394
1395 struct timeval real_timeout;
1396 real_timeout.tv_sec = *timeout / 1000000;
1397 real_timeout.tv_usec = (*timeout - (real_timeout.tv_sec * 1000000));
1398
1399 // Dummy fds for select
1400 fd_set dummy_write_fds;
1401 FD_ZERO(&dummy_write_fds);
1402
1403 // Read fds for select with the socket
1404 fd_set read_fds;
1405 FD_ZERO(&read_fds);
1406
1407 if (sockset.socket4 != -1) {
1408 FD_SET(sockset.socket4, &read_fds);
1409 }
1410 if (sockset.socket6 != -1) {
1411 FD_SET(sockset.socket6, &read_fds);
1263 } 1412 }
1264 1413
1265 to.tv_sec = *timo / 1000000; 1414 int nfds = (sockset.socket4 > sockset.socket6 ? sockset.socket4 : sockset.socket6) + 1;
1266 to.tv_usec = (*timo - (to.tv_sec * 1000000)); 1415
1416 struct timeval then;
1417 gettimeofday(&then, NULL);
1267 1418
1268 FD_ZERO(&rd);
1269 FD_ZERO(&wr);
1270 FD_SET(sock, &rd);
1271 errno = 0; 1419 errno = 0;
1272 gettimeofday(&then, &tz); 1420 int select_return = select(nfds, &read_fds, &dummy_write_fds, NULL, &real_timeout);
1273 n = select(sock + 1, &rd, &wr, NULL, &to); 1421 if (select_return < 0) {
1274 if (n < 0) {
1275 crash("select() in recvfrom_wto"); 1422 crash("select() in recvfrom_wto");
1276 } 1423 }
1277 gettimeofday(&now, &tz);
1278 *timo = get_timevaldiff(&then, &now);
1279 1424
1280 if (!n) { 1425 struct timeval now;
1281 return 0; /* timeout */ 1426 gettimeofday(&now, NULL);
1427 *timeout = get_timevaldiff(then, now);
1428
1429 if (!select_return) {
1430 return result; /* timeout */
1282 } 1431 }
1283 1432
1284 slen = sizeof(struct sockaddr_storage); 1433 unsigned int slen = sizeof(struct sockaddr_storage);
1285 1434
1286 memset(&iov, 0, sizeof(iov)); 1435 struct iovec iov = {
1287 iov.iov_base = buf; 1436 .iov_base = buf,
1288 iov.iov_len = len; 1437 .iov_len = len,
1438 };
1289 1439
1290 memset(&hdr, 0, sizeof(hdr)); 1440 struct msghdr hdr = {
1291 hdr.msg_name = saddr; 1441 .msg_name = saddr,
1292 hdr.msg_namelen = slen; 1442 .msg_namelen = slen,
1293 hdr.msg_iov = &iov; 1443 .msg_iov = &iov,
1294 hdr.msg_iovlen = 1; 1444 .msg_iovlen = 1,
1295#ifdef HAVE_MSGHDR_MSG_CONTROL 1445#ifdef HAVE_MSGHDR_MSG_CONTROL
1296 hdr.msg_control = ans_data; 1446 .msg_control = ans_data,
1297 hdr.msg_controllen = sizeof(ans_data); 1447 .msg_controllen = sizeof(ans_data),
1298#endif 1448#endif
1449 };
1450
1451 ssize_t ret;
1452 if (FD_ISSET(sockset.socket4, &read_fds)) {
1453 ret = recvmsg(sockset.socket4, &hdr, 0);
1454 result.recv_proto = AF_INET;
1455 } else if (FD_ISSET(sockset.socket6, &read_fds)) {
1456 ret = recvmsg(sockset.socket6, &hdr, 0);
1457 result.recv_proto = AF_INET6;
1458 } else {
1459 assert(false);
1460 }
1461
1462 result.received = ret;
1299 1463
1300 ret = recvmsg(sock, &hdr, 0);
1301#ifdef SO_TIMESTAMP 1464#ifdef SO_TIMESTAMP
1302 for (chdr = CMSG_FIRSTHDR(&hdr); chdr; chdr = CMSG_NXTHDR(&hdr, chdr)) { 1465 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))) { 1466 if (chdr->cmsg_level == SOL_SOCKET && chdr->cmsg_type == SO_TIMESTAMP &&
1304 memcpy(tv, CMSG_DATA(chdr), sizeof(*tv)); 1467 chdr->cmsg_len >= CMSG_LEN(sizeof(struct timeval))) {
1468 memcpy(received_timestamp, CMSG_DATA(chdr), sizeof(*received_timestamp));
1305 break; 1469 break;
1306 } 1470 }
1307 } 1471 }
1308 1472
1309 if (!chdr) 1473 if (!chdr) {
1474 gettimeofday(received_timestamp, NULL);
1475 }
1476#else
1477 gettimeofday(tv, NULL);
1310#endif // SO_TIMESTAMP 1478#endif // SO_TIMESTAMP
1311 gettimeofday(tv, &tz);
1312 return (ret);
1313}
1314 1479
1315static void finish(int sig) { 1480 return (result);
1316 u_int i = 0; 1481}
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 1482
1483static void finish(int sig, check_icmp_mode_switches modes, int min_hosts_alive,
1484 check_icmp_threshold warn, check_icmp_threshold crit,
1485 const unsigned short number_of_targets, check_icmp_state *program_state,
1486 check_icmp_target_container host_list[], unsigned short number_of_hosts,
1487 mp_check overall[static 1]) {
1488 // Deactivate alarm
1326 alarm(0); 1489 alarm(0);
1490
1327 if (debug > 1) { 1491 if (debug > 1) {
1328 printf("finish(%d) called\n", sig); 1492 printf("finish(%d) called\n", sig);
1329 } 1493 }
1330 1494
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) { 1495 if (debug) {
1342 printf("icmp_sent: %u icmp_recv: %u icmp_lost: %u\n", icmp_sent, icmp_recv, icmp_lost); 1496 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); 1497 program_state->icmp_recv, program_state->icmp_lost);
1498 printf("targets: %u targets_alive: %u\n", number_of_targets,
1499 targets_alive(number_of_targets, program_state->targets_down));
1344 } 1500 }
1345 1501
1346 /* iterate thrice to calculate values, give output, and print perfparse */ 1502 // loop over targets to evaluate each one
1347 status = STATE_OK; 1503 int targets_ok = 0;
1348 host = list; 1504 int targets_warn = 0;
1349 1505 for (unsigned short i = 0; i < number_of_hosts; i++) {
1350 while (host) { 1506 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 1507
1398 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a 1508 targets_ok += host_check.targets_ok;
1399 // loss of 5% will be entered as 5). 1509 targets_warn += host_check.targets_warn;
1400 R = R - (pl * 2.5);
1401 1510
1402 if (R < 0) { 1511 mp_add_subcheck_to_check(overall, host_check.sc_host);
1403 R = 0;
1404 }
1405
1406 host->score = R;
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 } 1512 }
1493 1513
1494 /* this is inevitable */ 1514 /* this is inevitable */
1495 if (!targets_alive) { 1515 // if (targets_alive(number_of_targets, program_state->targets_down) == 0) {
1496 status = STATE_CRITICAL; 1516 // mp_subcheck sc_no_target_alive = mp_subcheck_init();
1497 } 1517 // sc_no_target_alive = mp_set_subcheck_state(sc_no_target_alive, STATE_CRITICAL);
1498 if (min_hosts_alive > -1) { 1518 // sc_no_target_alive.output = strdup("No target is alive!");
1499 if (hosts_ok >= min_hosts_alive) { 1519 // mp_add_subcheck_to_check(overall, sc_no_target_alive);
1500 status = STATE_OK; 1520 // }
1501 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) {
1502 status = STATE_WARNING;
1503 }
1504 }
1505 printf("%s - ", status_string[status]);
1506
1507 host = list;
1508 while (host) {
1509 if (debug) {
1510 puts("");
1511 }
1512
1513 if (i) {
1514 if (i < targets) {
1515 printf(" :: ");
1516 } else {
1517 printf("\n");
1518 }
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 }
1658
1659 host = host->next;
1660 }
1661 1521
1662 if (min_hosts_alive > -1) { 1522 if (min_hosts_alive > -1) {
1663 if (hosts_ok >= min_hosts_alive) { 1523 mp_subcheck sc_min_targets_alive = mp_subcheck_init();
1664 status = STATE_OK; 1524 sc_min_targets_alive = mp_set_subcheck_default_state(sc_min_targets_alive, STATE_OK);
1665 } else if ((hosts_ok + hosts_warn) >= min_hosts_alive) { 1525
1666 status = STATE_WARNING; 1526 if (targets_ok >= min_hosts_alive) {
1527 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_OK);
1528 xasprintf(&sc_min_targets_alive.output, "%u targets OK of a minimum of %u", targets_ok,
1529 min_hosts_alive);
1530
1531 // Overwrite main state here
1532 overall->evaluation_function = &mp_eval_ok;
1533 } else if ((targets_ok + targets_warn) >= min_hosts_alive) {
1534 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_WARNING);
1535 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1536 targets_ok + targets_warn, min_hosts_alive);
1537 overall->evaluation_function = &mp_eval_warning;
1538 } else {
1539 sc_min_targets_alive = mp_set_subcheck_state(sc_min_targets_alive, STATE_CRITICAL);
1540 xasprintf(&sc_min_targets_alive.output, "%u targets OK or Warning of a minimum of %u",
1541 targets_ok + targets_warn, min_hosts_alive);
1542 overall->evaluation_function = &mp_eval_critical;
1667 } 1543 }
1544
1545 mp_add_subcheck_to_check(overall, sc_min_targets_alive);
1668 } 1546 }
1669 1547
1670 /* finish with an empty line */ 1548 /* finish with an empty line */
1671 puts("");
1672 if (debug) { 1549 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, 1550 printf(
1674 hosts_warn, min_hosts_alive); 1551 "targets: %u, targets_alive: %u, hosts_ok: %u, hosts_warn: %u, min_hosts_alive: %i\n",
1552 number_of_targets, targets_alive(number_of_targets, program_state->targets_down),
1553 targets_ok, targets_warn, min_hosts_alive);
1675 } 1554 }
1676
1677 exit(status);
1678} 1555}
1679 1556
1680static u_int get_timevaldiff(struct timeval *early, struct timeval *later) { 1557static 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 */ 1558 /* 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)) { 1559 if (earlier.tv_sec > later.tv_sec ||
1560 (earlier.tv_sec == later.tv_sec && earlier.tv_usec > later.tv_usec)) {
1694 return 0; 1561 return 0;
1695 } 1562 }
1696 ret = (later->tv_sec - early->tv_sec) * 1000000; 1563
1697 ret += later->tv_usec - early->tv_usec; 1564 time_t ret = (later.tv_sec - earlier.tv_sec) * 1000000;
1565 ret += later.tv_usec - earlier.tv_usec;
1698 1566
1699 return ret; 1567 return ret;
1700} 1568}
1701 1569
1702static int add_target_ip(char *arg, struct sockaddr_storage *in) { 1570static time_t get_timevaldiff_to_now(struct timeval earlier) {
1703 struct rta_host *host; 1571 struct timeval now;
1704 struct sockaddr_in *sin, *host_sin; 1572 gettimeofday(&now, NULL);
1705 struct sockaddr_in6 *sin6, *host_sin6;
1706 1573
1707 if (address_family == AF_INET) { 1574 return get_timevaldiff(earlier, now);
1708 sin = (struct sockaddr_in *)in; 1575}
1576
1577static add_target_ip_wrapper add_target_ip(struct sockaddr_storage address) {
1578 assert((address.ss_family == AF_INET) || (address.ss_family == AF_INET6));
1579
1580 if (debug) {
1581 char straddr[INET6_ADDRSTRLEN];
1582 parse_address((&address), straddr, sizeof(straddr));
1583 printf("add_target_ip called with: %s\n", straddr);
1584 }
1585 struct sockaddr_in *sin;
1586 struct sockaddr_in6 *sin6;
1587 if (address.ss_family == AF_INET) {
1588 sin = (struct sockaddr_in *)&address;
1589 } else if (address.ss_family == AF_INET6) {
1590 sin6 = (struct sockaddr_in6 *)&address;
1709 } else { 1591 } else {
1710 sin6 = (struct sockaddr_in6 *)in; 1592 assert(false);
1711 } 1593 }
1712 1594
1595 add_target_ip_wrapper result = {
1596 .error_code = OK,
1597 .target = NULL,
1598 };
1599
1713 /* disregard obviously stupid addresses 1600 /* disregard obviously stupid addresses
1714 * (I didn't find an ipv6 equivalent to INADDR_NONE) */ 1601 * (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))) || 1602 if (((address.ss_family == AF_INET &&
1716 (address_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) { 1603 (sin->sin_addr.s_addr == INADDR_NONE || sin->sin_addr.s_addr == INADDR_ANY))) ||
1717 return -1; 1604 (address.ss_family == AF_INET6 && (sin6->sin6_addr.s6_addr == in6addr_any.s6_addr))) {
1605 result.error_code = ERROR;
1606 return result;
1718 } 1607 }
1719 1608
1720 /* no point in adding two identical IP's, so don't. ;) */ 1609 // get string representation of address
1721 host = list; 1610 char straddr[INET6_ADDRSTRLEN];
1722 while (host) { 1611 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 1612
1736 /* add the fresh ip */ 1613 /* add the fresh ip */
1737 host = (struct rta_host *)malloc(sizeof(struct rta_host)); 1614 ping_target *target = (ping_target *)calloc(1, sizeof(ping_target));
1738 if (!host) { 1615 if (!target) {
1739 char straddr[INET6_ADDRSTRLEN]; 1616 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 } 1617 }
1743 memset(host, 0, sizeof(struct rta_host));
1744 1618
1745 /* set the values. use calling name for output */ 1619 ping_target_create_wrapper target_wrapper = ping_target_create(address);
1746 host->name = strdup(arg);
1747 1620
1748 /* fill out the sockaddr_storage struct */ 1621 if (target_wrapper.errorcode == OK) {
1749 if (address_family == AF_INET) { 1622 *target = target_wrapper.host;
1750 host_sin = (struct sockaddr_in *)&host->saddr_in; 1623 result.target = target;
1751 host_sin->sin_family = AF_INET;
1752 host_sin->sin_addr.s_addr = sin->sin_addr.s_addr;
1753 } else {
1754 host_sin6 = (struct sockaddr_in6 *)&host->saddr_in;
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 { 1624 } else {
1778 cursor->next = host; 1625 result.error_code = target_wrapper.errorcode;
1779 } 1626 }
1780 1627
1781 cursor = host; 1628 return result;
1782 targets++;
1783
1784 return 0;
1785} 1629}
1786 1630
1787/* wrapper for add_target_ip */ 1631/* wrapper for add_target_ip */
1788static int add_target(char *arg) { 1632static add_target_wrapper add_target(char *arg, const check_icmp_execution_mode mode,
1789 int error, result = -1; 1633 sa_family_t enforced_proto) {
1790 struct sockaddr_storage ip; 1634 if (debug > 0) {
1791 struct addrinfo hints, *res, *p; 1635 printf("add_target called with argument %s\n", arg);
1792 struct sockaddr_in *sin; 1636 }
1793 struct sockaddr_in6 *sin6; 1637
1794 1638 struct sockaddr_storage address_storage = {};
1795 switch (address_family) { 1639 struct sockaddr_in *sin = NULL;
1796 case -1: 1640 struct sockaddr_in6 *sin6 = NULL;
1797 /* -4 and -6 are not specified on cmdline */ 1641 int error_code = -1;
1798 address_family = AF_INET; 1642
1799 sin = (struct sockaddr_in *)&ip; 1643 switch (enforced_proto) {
1800 result = inet_pton(address_family, arg, &sin->sin_addr); 1644 case AF_UNSPEC:
1801#ifdef USE_IPV6 1645 /*
1802 if (result != 1) { 1646 * no enforced protocol family
1803 address_family = AF_INET6; 1647 * try to parse the address with each one
1804 sin6 = (struct sockaddr_in6 *)&ip; 1648 */
1805 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1649 sin = (struct sockaddr_in *)&address_storage;
1806 } 1650 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1807#endif 1651 address_storage.ss_family = AF_INET;
1808 /* If we don't find any valid addresses, we still don't know the address_family */ 1652
1809 if (result != 1) { 1653 if (error_code != 1) {
1810 address_family = -1; 1654 sin6 = (struct sockaddr_in6 *)&address_storage;
1655 error_code = inet_pton(AF_INET6, arg, &sin6->sin6_addr);
1656 address_storage.ss_family = AF_INET6;
1811 } 1657 }
1812 break; 1658 break;
1813 case AF_INET: 1659 case AF_INET:
1814 sin = (struct sockaddr_in *)&ip; 1660 sin = (struct sockaddr_in *)&address_storage;
1815 result = inet_pton(address_family, arg, &sin->sin_addr); 1661 error_code = inet_pton(AF_INET, arg, &sin->sin_addr);
1662 address_storage.ss_family = AF_INET;
1816 break; 1663 break;
1817 case AF_INET6: 1664 case AF_INET6:
1818 sin6 = (struct sockaddr_in6 *)&ip; 1665 sin6 = (struct sockaddr_in6 *)&address_storage;
1819 result = inet_pton(address_family, arg, &sin6->sin6_addr); 1666 error_code = inet_pton(AF_INET, arg, &sin6->sin6_addr);
1667 address_storage.ss_family = AF_INET6;
1820 break; 1668 break;
1821 default: 1669 default:
1822 crash("Address family not supported"); 1670 crash("Address family not supported");
1823 } 1671 }
1824 1672
1825 /* don't resolve if we don't have to */ 1673 add_target_wrapper result = {
1826 if (result == 1) { 1674 .error_code = OK,
1675 .targets = NULL,
1676 .has_v4 = false,
1677 .has_v6 = false,
1678 };
1679
1680 // if error_code == 1 the address was a valid address parsed above
1681 if (error_code == 1) {
1827 /* don't add all ip's if we were given a specific one */ 1682 /* don't add all ip's if we were given a specific one */
1828 return add_target_ip(arg, &ip); 1683 add_target_ip_wrapper targeted = add_target_ip(address_storage);
1829 } else { 1684
1830 errno = 0; 1685 if (targeted.error_code != OK) {
1831 memset(&hints, 0, sizeof(hints)); 1686 result.error_code = ERROR;
1832 if (address_family == -1) { 1687 return result;
1833 hints.ai_family = AF_UNSPEC;
1834 } else {
1835 hints.ai_family = address_family == AF_INET ? PF_INET : PF_INET6;
1836 } 1688 }
1837 hints.ai_socktype = SOCK_RAW; 1689
1838 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) { 1690 if (targeted.target->address.ss_family == AF_INET) {
1839 errno = 0; 1691 result.has_v4 = true;
1840 crash("Failed to resolve %s: %s", arg, gai_strerror(error)); 1692 } else if (targeted.target->address.ss_family == AF_INET6) {
1841 return -1; 1693 result.has_v6 = true;
1694 } else {
1695 assert(false);
1842 } 1696 }
1843 address_family = res->ai_family; 1697 result.targets = targeted.target;
1698 result.number_of_targets = 1;
1699 return result;
1700 }
1701
1702 struct addrinfo hints = {};
1703 errno = 0;
1704 hints.ai_family = enforced_proto;
1705 hints.ai_socktype = SOCK_RAW;
1706
1707 int error;
1708 struct addrinfo *res;
1709 if ((error = getaddrinfo(arg, NULL, &hints, &res)) != 0) {
1710 errno = 0;
1711 crash("Failed to resolve %s: %s", arg, gai_strerror(error));
1712 result.error_code = ERROR;
1713 return result;
1844 } 1714 }
1845 1715
1846 /* possibly add all the IP's as targets */ 1716 /* possibly add all the IP's as targets */
1847 for (p = res; p != NULL; p = p->ai_next) { 1717 for (struct addrinfo *address = res; address != NULL; address = address->ai_next) {
1848 memcpy(&ip, p->ai_addr, p->ai_addrlen); 1718 struct sockaddr_storage temporary_ip_address;
1849 add_target_ip(arg, &ip); 1719 memcpy(&temporary_ip_address, address->ai_addr, address->ai_addrlen);
1720
1721 add_target_ip_wrapper tmp = add_target_ip(temporary_ip_address);
1722
1723 if (tmp.error_code != OK) {
1724 // No proper error handling
1725 // What to do?
1726 } else {
1727 if (result.targets == NULL) {
1728 result.targets = tmp.target;
1729 result.number_of_targets = 1;
1730 } else {
1731 result.number_of_targets += ping_target_list_append(result.targets, tmp.target);
1732 }
1733 if (address->ai_family == AF_INET) {
1734 result.has_v4 = true;
1735 } else if (address->ai_family == AF_INET6) {
1736 result.has_v6 = true;
1737 }
1738 }
1850 1739
1851 /* this is silly, but it works */ 1740 /* this is silly, but it works */
1852 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) { 1741 if (mode == MODE_HOSTCHECK || mode == MODE_ALL) {
@@ -1855,18 +1744,20 @@ static int add_target(char *arg) {
1855 } 1744 }
1856 continue; 1745 continue;
1857 } 1746 }
1747
1748 // Abort after first hit if not in of the modes above
1858 break; 1749 break;
1859 } 1750 }
1860 freeaddrinfo(res); 1751 freeaddrinfo(res);
1861 1752
1862 return 0; 1753 return result;
1863} 1754}
1864 1755
1865static void set_source_ip(char *arg) { 1756static void set_source_ip(char *arg, const int icmp_sock, sa_family_t addr_family) {
1866 struct sockaddr_in src; 1757 struct sockaddr_in src;
1867 1758
1868 memset(&src, 0, sizeof(src)); 1759 memset(&src, 0, sizeof(src));
1869 src.sin_family = address_family; 1760 src.sin_family = addr_family;
1870 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) { 1761 if ((src.sin_addr.s_addr = inet_addr(arg)) == INADDR_NONE) {
1871 src.sin_addr.s_addr = get_ip_address(arg); 1762 src.sin_addr.s_addr = get_ip_address(arg);
1872 } 1763 }
@@ -1878,8 +1769,8 @@ static void set_source_ip(char *arg) {
1878/* TODO: Move this to netutils.c and also change check_dhcp to use that. */ 1769/* TODO: Move this to netutils.c and also change check_dhcp to use that. */
1879static in_addr_t get_ip_address(const char *ifname) { 1770static in_addr_t get_ip_address(const char *ifname) {
1880 // TODO: Rewrite this so the function return an error and we exit somewhere else 1771 // TODO: Rewrite this so the function return an error and we exit somewhere else
1881 struct sockaddr_in ip; 1772 struct sockaddr_in ip_address;
1882 ip.sin_addr.s_addr = 0; // Fake initialization to make compiler happy 1773 ip_address.sin_addr.s_addr = 0; // Fake initialization to make compiler happy
1883#if defined(SIOCGIFADDR) 1774#if defined(SIOCGIFADDR)
1884 struct ifreq ifr; 1775 struct ifreq ifr;
1885 1776
@@ -1897,7 +1788,7 @@ static in_addr_t get_ip_address(const char *ifname) {
1897 errno = 0; 1788 errno = 0;
1898 crash("Cannot get interface IP address on this platform."); 1789 crash("Cannot get interface IP address on this platform.");
1899#endif 1790#endif
1900 return ip.sin_addr.s_addr; 1791 return ip_address.sin_addr.s_addr;
1901} 1792}
1902 1793
1903/* 1794/*
@@ -1906,103 +1797,127 @@ static in_addr_t get_ip_address(const char *ifname) {
1906 * s = seconds 1797 * s = seconds
1907 * return value is in microseconds 1798 * return value is in microseconds
1908 */ 1799 */
1909static u_int get_timevar(const char *str) { 1800static get_timevar_wrapper get_timevar(const char *str) {
1910 char p, u, *ptr; 1801 get_timevar_wrapper result = {
1911 size_t len; 1802 .error_code = OK,
1912 u_int i, d; /* integer and decimal, respectively */ 1803 .time_range = 0,
1913 u_int factor = 1000; /* default to milliseconds */ 1804 };
1914 1805
1915 if (!str) { 1806 if (!str) {
1916 return 0; 1807 result.error_code = ERROR;
1808 return result;
1917 } 1809 }
1918 len = strlen(str); 1810
1811 size_t len = strlen(str);
1919 if (!len) { 1812 if (!len) {
1920 return 0; 1813 result.error_code = ERROR;
1814 return result;
1921 } 1815 }
1922 1816
1923 /* unit might be given as ms|m (millisec), 1817 /* unit might be given as ms|m (millisec),
1924 * us|u (microsec) or just plain s, for seconds */ 1818 * us|u (microsec) or just plain s, for seconds */
1925 p = '\0'; 1819 char tmp = '\0';
1926 u = str[len - 1]; 1820 char unit = str[len - 1];
1927 if (len >= 2 && !isdigit((int)str[len - 2])) { 1821 if (len >= 2 && !isdigit((int)str[len - 2])) {
1928 p = str[len - 2]; 1822 tmp = str[len - 2];
1929 } 1823 }
1930 if (p && u == 's') { 1824
1931 u = p; 1825 if (tmp && unit == 's') {
1932 } else if (!p) { 1826 unit = tmp;
1933 p = u; 1827 } else if (!tmp) {
1828 tmp = unit;
1934 } 1829 }
1830
1935 if (debug > 2) { 1831 if (debug > 2) {
1936 printf("evaluating %s, u: %c, p: %c\n", str, u, p); 1832 printf("evaluating %s, u: %c, p: %c\n", str, unit, tmp);
1937 } 1833 }
1938 1834
1939 if (u == 'u') { 1835 unsigned int factor = 1000; /* default to milliseconds */
1836 if (unit == 'u') {
1940 factor = 1; /* microseconds */ 1837 factor = 1; /* microseconds */
1941 } else if (u == 'm') { 1838 } else if (unit == 'm') {
1942 factor = 1000; /* milliseconds */ 1839 factor = 1000; /* milliseconds */
1943 } else if (u == 's') { 1840 } else if (unit == 's') {
1944 factor = 1000000; /* seconds */ 1841 factor = 1000000; /* seconds */
1945 } 1842 }
1843
1946 if (debug > 2) { 1844 if (debug > 2) {
1947 printf("factor is %u\n", factor); 1845 printf("factor is %u\n", factor);
1948 } 1846 }
1949 1847
1950 i = strtoul(str, &ptr, 0); 1848 char *ptr;
1849 unsigned long pre_radix;
1850 pre_radix = strtoul(str, &ptr, 0);
1951 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) { 1851 if (!ptr || *ptr != '.' || strlen(ptr) < 2 || factor == 1) {
1952 return i * factor; 1852 result.time_range = (unsigned int)(pre_radix * factor);
1853 return result;
1953 } 1854 }
1954 1855
1955 /* time specified in usecs can't have decimal points, so ignore them */ 1856 /* time specified in usecs can't have decimal points, so ignore them */
1956 if (factor == 1) { 1857 if (factor == 1) {
1957 return i; 1858 result.time_range = (unsigned int)pre_radix;
1859 return result;
1958 } 1860 }
1959 1861
1960 d = strtoul(ptr + 1, NULL, 0); 1862 /* integer and decimal, respectively */
1863 unsigned int post_radix = (unsigned int)strtoul(ptr + 1, NULL, 0);
1961 1864
1962 /* d is decimal, so get rid of excess digits */ 1865 /* d is decimal, so get rid of excess digits */
1963 while (d >= factor) { 1866 while (post_radix >= factor) {
1964 d /= 10; 1867 post_radix /= 10;
1965 } 1868 }
1966 1869
1967 /* the last parenthesis avoids floating point exceptions. */ 1870 /* the last parenthesis avoids floating point exceptions. */
1968 return ((i * factor) + (d * (factor / 10))); 1871 result.time_range = (unsigned int)((pre_radix * factor) + (post_radix * (factor / 10)));
1872 return result;
1969} 1873}
1970 1874
1971/* not too good at checking errors, but it'll do (main() should barfe on -1) */ 1875static get_threshold_wrapper get_threshold(char *str, check_icmp_threshold threshold) {
1972static int get_threshold(char *str, threshold *th) { 1876 get_threshold_wrapper result = {
1973 char *p = NULL, i = 0; 1877 .errorcode = OK,
1878 .threshold = threshold,
1879 };
1974 1880
1975 if (!str || !strlen(str) || !th) { 1881 if (!str || !strlen(str)) {
1976 return -1; 1882 result.errorcode = ERROR;
1883 return result;
1977 } 1884 }
1978 1885
1979 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */ 1886 /* pointer magic slims code by 10 lines. i is bof-stop on stupid libc's */
1980 p = &str[strlen(str) - 1]; 1887 bool is_at_last_char = false;
1981 while (p != &str[1]) { 1888 char *tmp = &str[strlen(str) - 1];
1982 if (*p == '%') { 1889 while (tmp != &str[1]) {
1983 *p = '\0'; 1890 if (*tmp == '%') {
1984 } else if (*p == ',' && i) { 1891 *tmp = '\0';
1985 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1892 } else if (*tmp == ',' && is_at_last_char) {
1986 th->pl = (unsigned char)strtoul(p + 1, NULL, 0); 1893 *tmp = '\0'; /* reset it so get_timevar(str) works nicely later */
1894 result.threshold.pl = (unsigned char)strtoul(tmp + 1, NULL, 0);
1987 break; 1895 break;
1988 } 1896 }
1989 i = 1; 1897 is_at_last_char = true;
1990 p--; 1898 tmp--;
1991 } 1899 }
1992 th->rta = get_timevar(str);
1993 1900
1994 if (!th->rta) { 1901 get_timevar_wrapper parsed_time = get_timevar(str);
1995 return -1; 1902
1903 if (parsed_time.error_code == OK) {
1904 result.threshold.rta = parsed_time.time_range;
1905 } else {
1906 if (debug > 1) {
1907 printf("%s: failed to parse rta threshold\n", __FUNCTION__);
1908 }
1909 result.errorcode = ERROR;
1910 return result;
1996 } 1911 }
1997 1912
1998 if (th->rta > MAXTTL * 1000000) { 1913 if (result.threshold.rta > MAXTTL * 1000000) {
1999 th->rta = MAXTTL * 1000000; 1914 result.threshold.rta = MAXTTL * 1000000;
2000 } 1915 }
2001 if (th->pl > 100) { 1916 if (result.threshold.pl > 100) {
2002 th->pl = 100; 1917 result.threshold.pl = 100;
2003 } 1918 }
2004 1919
2005 return 0; 1920 return result;
2006} 1921}
2007 1922
2008/* 1923/*
@@ -2013,91 +1928,118 @@ static int get_threshold(char *str, threshold *th) {
2013 * @param[in] length strlen(str) 1928 * @param[in] length strlen(str)
2014 * @param[out] warn Pointer to the warn threshold struct to which the values should be assigned 1929 * @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 1930 * @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) 1931 * @param[in] mode Determines whether this a threshold for rta, packet_loss, jitter, mos or score
1932 * (exclusively)
2017 */ 1933 */
2018static bool get_threshold2(char *str, size_t length, threshold *warn, threshold *crit, threshold_mode mode) { 1934static get_threshold2_wrapper get_threshold2(char *str, size_t length, check_icmp_threshold warn,
2019 if (!str || !length || !warn || !crit) { 1935 check_icmp_threshold crit, threshold_mode mode) {
2020 return false; 1936 get_threshold2_wrapper result = {
1937 .errorcode = OK,
1938 .warn = warn,
1939 .crit = crit,
1940 };
1941
1942 if (!str || !length) {
1943 result.errorcode = ERROR;
1944 return result;
2021 } 1945 }
2022 1946
2023 // p points to the last char in str 1947 // p points to the last char in str
2024 char *p = &str[length - 1]; 1948 char *work_pointer = &str[length - 1];
2025 1949
2026 // first_iteration is bof-stop on stupid libc's 1950 // first_iteration is bof-stop on stupid libc's
2027 bool first_iteration = true; 1951 bool first_iteration = true;
2028 1952
2029 while (p != &str[0]) { 1953 while (work_pointer != &str[0]) {
2030 if ((*p == 'm') || (*p == '%')) { 1954 if ((*work_pointer == 'm') || (*work_pointer == '%')) {
2031 *p = '\0'; 1955 *work_pointer = '\0';
2032 } else if (*p == ',' && !first_iteration) { 1956 } else if (*work_pointer == ',' && !first_iteration) {
2033 *p = '\0'; /* reset it so get_timevar(str) works nicely later */ 1957 *work_pointer = '\0'; /* reset it so get_timevar(str) works nicely later */
2034 1958
2035 char *start_of_value = p + 1; 1959 char *start_of_value = work_pointer + 1;
2036 1960
2037 if (!parse_threshold2_helper(start_of_value, strlen(start_of_value), crit, mode)) { 1961 parse_threshold2_helper_wrapper tmp =
2038 return false; 1962 parse_threshold2_helper(start_of_value, strlen(start_of_value), result.crit, mode);
1963 if (tmp.errorcode != OK) {
1964 result.errorcode = ERROR;
1965 return result;
2039 } 1966 }
1967 result.crit = tmp.result;
2040 } 1968 }
2041 first_iteration = false; 1969 first_iteration = false;
2042 p--; 1970 work_pointer--;
2043 } 1971 }
2044 1972
2045 return parse_threshold2_helper(p, strlen(p), warn, mode); 1973 parse_threshold2_helper_wrapper tmp =
1974 parse_threshold2_helper(work_pointer, strlen(work_pointer), result.warn, mode);
1975 if (tmp.errorcode != OK) {
1976 result.errorcode = ERROR;
1977 } else {
1978 result.warn = tmp.result;
1979 }
1980 return result;
2046} 1981}
2047 1982
2048static bool parse_threshold2_helper(char *s, size_t length, threshold *thr, threshold_mode mode) { 1983static parse_threshold2_helper_wrapper parse_threshold2_helper(char *threshold_string,
1984 size_t length,
1985 check_icmp_threshold thr,
1986 threshold_mode mode) {
2049 char *resultChecker = {0}; 1987 char *resultChecker = {0};
1988 parse_threshold2_helper_wrapper result = {
1989 .result = thr,
1990 .errorcode = OK,
1991 };
2050 1992
2051 switch (mode) { 1993 switch (mode) {
2052 case const_rta_mode: 1994 case const_rta_mode:
2053 thr->rta = strtod(s, &resultChecker) * 1000; 1995 result.result.rta = (unsigned int)(strtod(threshold_string, &resultChecker) * 1000);
2054 break; 1996 break;
2055 case const_packet_loss_mode: 1997 case const_packet_loss_mode:
2056 thr->pl = (unsigned char)strtoul(s, &resultChecker, 0); 1998 result.result.pl = (unsigned char)strtoul(threshold_string, &resultChecker, 0);
2057 break; 1999 break;
2058 case const_jitter_mode: 2000 case const_jitter_mode:
2059 thr->jitter = strtod(s, &resultChecker); 2001 result.result.jitter = strtod(threshold_string, &resultChecker);
2060
2061 break; 2002 break;
2062 case const_mos_mode: 2003 case const_mos_mode:
2063 thr->mos = strtod(s, &resultChecker); 2004 result.result.mos = strtod(threshold_string, &resultChecker);
2064 break; 2005 break;
2065 case const_score_mode: 2006 case const_score_mode:
2066 thr->score = strtod(s, &resultChecker); 2007 result.result.score = strtod(threshold_string, &resultChecker);
2067 break; 2008 break;
2068 } 2009 }
2069 2010
2070 if (resultChecker == s) { 2011 if (resultChecker == threshold_string) {
2071 // Failed to parse 2012 // Failed to parse
2072 return false; 2013 result.errorcode = ERROR;
2014 return result;
2073 } 2015 }
2074 2016
2075 if (resultChecker != (s + length)) { 2017 if (resultChecker != (threshold_string + length)) {
2076 // Trailing symbols 2018 // Trailing symbols
2077 return false; 2019 result.errorcode = ERROR;
2078 } 2020 }
2079 2021
2080 return true; 2022 return result;
2081} 2023}
2082 2024
2083unsigned short icmp_checksum(uint16_t *p, size_t n) { 2025unsigned short icmp_checksum(uint16_t *packet, size_t packet_size) {
2084 unsigned short cksum;
2085 long sum = 0; 2026 long sum = 0;
2086 2027
2087 /* sizeof(uint16_t) == 2 */ 2028 /* sizeof(uint16_t) == 2 */
2088 while (n >= 2) { 2029 while (packet_size >= 2) {
2089 sum += *(p++); 2030 sum += *(packet++);
2090 n -= 2; 2031 packet_size -= 2;
2091 } 2032 }
2092 2033
2093 /* mop up the occasional odd byte */ 2034 /* mop up the occasional odd byte */
2094 if (n == 1) { 2035 if (packet_size == 1) {
2095 sum += *((uint8_t *)p - 1); 2036 sum += *((uint8_t *)packet - 1);
2096 } 2037 }
2097 2038
2098 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 2039 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
2099 sum += (sum >> 16); /* add carry */ 2040 sum += (sum >> 16); /* add carry */
2100 cksum = ~sum; /* ones-complement, trunc to 16 bits */ 2041 unsigned short cksum;
2042 cksum = (unsigned short)~sum; /* ones-complement, trunc to 16 bits */
2101 2043
2102 return cksum; 2044 return cksum;
2103} 2045}
@@ -2121,13 +2063,14 @@ void print_help(void) {
2121 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets")); 2063 printf(" %s\n", _("Use IPv4 (default) or IPv6 to communicate with the targets"));
2122 printf(" %s\n", "-w"); 2064 printf(" %s\n", "-w");
2123 printf(" %s", _("warning threshold (currently ")); 2065 printf(" %s", _("warning threshold (currently "));
2124 printf("%0.3fms,%u%%)\n", (float)warn.rta / 1000, warn.pl); 2066 printf("%0.3fms,%u%%)\n", (float)DEFAULT_WARN_RTA / 1000, DEFAULT_WARN_PL);
2125 printf(" %s\n", "-c"); 2067 printf(" %s\n", "-c");
2126 printf(" %s", _("critical threshold (currently ")); 2068 printf(" %s", _("critical threshold (currently "));
2127 printf("%0.3fms,%u%%)\n", (float)crit.rta / 1000, crit.pl); 2069 printf("%0.3fms,%u%%)\n", (float)DEFAULT_CRIT_RTA / 1000, DEFAULT_CRIT_PL);
2128 2070
2129 printf(" %s\n", "-R"); 2071 printf(" %s\n", "-R");
2130 printf(" %s\n", _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms")); 2072 printf(" %s\n",
2073 _("RTA, round trip average, mode warning,critical, ex. 100ms,200ms unit in ms"));
2131 printf(" %s\n", "-P"); 2074 printf(" %s\n", "-P");
2132 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %")); 2075 printf(" %s\n", _("packet loss mode, ex. 40%,50% , unit in %"));
2133 printf(" %s\n", "-J"); 2076 printf(" %s\n", "-J");
@@ -2144,28 +2087,29 @@ void print_help(void) {
2144 printf(" %s\n", _("specify a source IP address or device name")); 2087 printf(" %s\n", _("specify a source IP address or device name"));
2145 printf(" %s\n", "-n"); 2088 printf(" %s\n", "-n");
2146 printf(" %s", _("number of packets to send (currently ")); 2089 printf(" %s", _("number of packets to send (currently "));
2147 printf("%u)\n", packets); 2090 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2148 printf(" %s\n", "-p"); 2091 printf(" %s\n", "-p");
2149 printf(" %s", _("number of packets to send (currently ")); 2092 printf(" %s", _("number of packets to send (currently "));
2150 printf("%u)\n", packets); 2093 printf("%u)\n", DEFAULT_NUMBER_OF_PACKETS);
2151 printf(" %s\n", "-i"); 2094 printf(" %s\n", "-i");
2152 printf(" %s", _("max packet interval (currently ")); 2095 printf(" %s", _("max packet interval (currently "));
2153 printf("%0.3fms)\n", (float)pkt_interval / 1000); 2096 printf("%0.3fms)\n", (float)DEFAULT_PKT_INTERVAL / 1000);
2154 printf(" %s\n", "-I"); 2097 printf(" %s\n", "-I");
2155 printf(" %s", _("max target interval (currently ")); 2098 printf(" %s", _("max target interval (currently "));
2156 printf("%0.3fms)\n", (float)target_interval / 1000); 2099 printf("%0.3fms)\n", (float)DEFAULT_TARGET_INTERVAL / 1000);
2157 printf(" %s\n", "-m"); 2100 printf(" %s\n", "-m");
2158 printf(" %s", _("number of alive hosts required for success")); 2101 printf(" %s", _("number of alive hosts required for success"));
2159 printf("\n"); 2102 printf("\n");
2160 printf(" %s\n", "-l"); 2103 printf(" %s\n", "-l");
2161 printf(" %s", _("TTL on outgoing packets (currently ")); 2104 printf(" %s", _("TTL on outgoing packets (currently "));
2162 printf("%u)\n", ttl); 2105 printf("%u)\n", DEFAULT_TTL);
2163 printf(" %s\n", "-t"); 2106 printf(" %s\n", "-t");
2164 printf(" %s", _("timeout value (seconds, currently ")); 2107 printf(" %s", _("timeout value (seconds, currently "));
2165 printf("%u)\n", timeout); 2108 printf("%u)\n", DEFAULT_TIMEOUT);
2166 printf(" %s\n", "-b"); 2109 printf(" %s\n", "-b");
2167 printf(" %s\n", _("Number of icmp data bytes to send")); 2110 printf(" %s\n", _("Number of icmp data bytes to send"));
2168 printf(" %s %u + %d)\n", _("Packet size will be data bytes + icmp header (currently"), icmp_data_size, ICMP_MINLEN); 2111 printf(" %s %lu + %d)\n", _("Packet size will be data bytes + icmp header (currently"),
2112 DEFAULT_PING_DATA_SIZE, ICMP_MINLEN);
2169 printf(" %s\n", "-v"); 2113 printf(" %s\n", "-v");
2170 printf(" %s\n", _("verbose")); 2114 printf(" %s\n", _("verbose"));
2171 printf("\n"); 2115 printf("\n");
@@ -2175,12 +2119,15 @@ void print_help(void) {
2175 printf("\n"); 2119 printf("\n");
2176 printf(" %s\n", _("Threshold format for -w and -c is 200.25,60% for 200.25 msec RTA and 60%")); 2120 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.")); 2121 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")); 2122 printf(" %s\n",
2179 printf(" %s\n", _("us (microseconds), ms (milliseconds, default) or just plain s for seconds.")); 2123 _("You can specify different RTA factors using the standardized abbreviations"));
2124 printf(" %s\n",
2125 _("us (microseconds), ms (milliseconds, default) or just plain s for seconds."));
2180 /* -d not yet implemented */ 2126 /* -d not yet implemented */
2181 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12 hops")); 2127 /* printf ("%s\n", _("Threshold format for -d is warn,crit. 12,14 means WARNING if >= 12
2182 printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); 2128 hops")); printf ("%s\n", _("are spent and CRITICAL if >= 14 hops are spent.")); printf
2183 printf ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do not."));*/ 2129 ("%s\n\n", _("NOTE: Some systems decrease TTL when forming ICMP_ECHOREPLY, others do
2130 not."));*/
2184 printf("\n"); 2131 printf("\n");
2185 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity.")); 2132 printf(" %s\n", _("The -v switch can be specified several times for increased verbosity."));
2186 /* printf ("%s\n", _("Long options are currently unsupported.")); 2133 /* printf ("%s\n", _("Long options are currently unsupported."));
@@ -2194,3 +2141,334 @@ void print_usage(void) {
2194 printf("%s\n", _("Usage:")); 2141 printf("%s\n", _("Usage:"));
2195 printf(" %s [options] [-H] host1 host2 hostN\n", progname); 2142 printf(" %s [options] [-H] host1 host2 hostN\n", progname);
2196} 2143}
2144
2145static add_host_wrapper add_host(char *arg, check_icmp_execution_mode mode,
2146 sa_family_t enforced_proto) {
2147 if (debug) {
2148 printf("add_host called with argument %s\n", arg);
2149 }
2150
2151 add_host_wrapper result = {
2152 .error_code = OK,
2153 .host = check_icmp_target_container_init(),
2154 .has_v4 = false,
2155 .has_v6 = false,
2156 };
2157
2158 add_target_wrapper targets = add_target(arg, mode, enforced_proto);
2159
2160 if (targets.error_code != OK) {
2161 result.error_code = targets.error_code;
2162 return result;
2163 }
2164
2165 result.has_v4 = targets.has_v4;
2166 result.has_v6 = targets.has_v6;
2167
2168 result.host = check_icmp_target_container_init();
2169
2170 result.host.name = strdup(arg);
2171 result.host.target_list = targets.targets;
2172 result.host.number_of_targets = targets.number_of_targets;
2173
2174 return result;
2175}
2176
2177mp_subcheck evaluate_target(ping_target target, check_icmp_mode_switches modes,
2178 check_icmp_threshold warn, check_icmp_threshold crit) {
2179 /* if no new mode selected, use old schema */
2180 if (!modes.rta_mode && !modes.pl_mode && !modes.jitter_mode && !modes.score_mode &&
2181 !modes.mos_mode && !modes.order_mode) {
2182 modes.rta_mode = true;
2183 modes.pl_mode = true;
2184 }
2185
2186 mp_subcheck result = mp_subcheck_init();
2187 result = mp_set_subcheck_default_state(result, STATE_OK);
2188
2189 char address[INET6_ADDRSTRLEN];
2190 memset(address, 0, INET6_ADDRSTRLEN);
2191 parse_address(&target.address, address, sizeof(address));
2192
2193 xasprintf(&result.output, "%s", address);
2194
2195 double packet_loss;
2196 time_t rta;
2197 if (!target.icmp_recv) {
2198 /* rta 0 is of course not entirely correct, but will still show up
2199 * conspicuously as missing entries in perfparse and cacti */
2200 packet_loss = 100;
2201 rta = 0;
2202 result = mp_set_subcheck_state(result, STATE_CRITICAL);
2203 /* up the down counter if not already counted */
2204
2205 if (target.flags & FLAG_LOST_CAUSE) {
2206 xasprintf(&result.output, "%s: %s @ %s", result.output,
2207 get_icmp_error_msg(target.icmp_type, target.icmp_code), address);
2208 } else { /* not marked as lost cause, so we have no flags for it */
2209 xasprintf(&result.output, "%s", result.output);
2210 }
2211 } else {
2212 packet_loss =
2213 (unsigned char)((target.icmp_sent - target.icmp_recv) * 100) / target.icmp_sent;
2214 rta = target.time_waited / target.icmp_recv;
2215 }
2216
2217 double EffectiveLatency;
2218 double mos; /* Mean opinion score */
2219 double score; /* score */
2220
2221 if (target.icmp_recv > 1) {
2222 /*
2223 * This algorithm is probably pretty much blindly copied from
2224 * locations like this one:
2225 * https://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#mos It calculates a MOS
2226 * value (range of 1 to 5, where 1 is bad and 5 really good). According to some quick
2227 * research MOS originates from the Audio/Video transport network area. Whether it can
2228 * and should be computed from ICMP data, I can not say.
2229 *
2230 * Anyway the basic idea is to map a value "R" with a range of 0-100 to the MOS value
2231 *
2232 * MOS stands likely for Mean Opinion Score (
2233 * https://en.wikipedia.org/wiki/Mean_Opinion_Score )
2234 *
2235 * More links:
2236 * - https://confluence.slac.stanford.edu/display/IEPM/MOS
2237 */
2238 target.jitter = (target.jitter / (target.icmp_recv - 1) / 1000);
2239
2240 /*
2241 * Take the average round trip latency (in milliseconds), add
2242 * round trip jitter, but double the impact to latency
2243 * then add 10 for protocol latencies (in milliseconds).
2244 */
2245 EffectiveLatency = ((double)rta / 1000) + target.jitter * 2 + 10;
2246
2247 double R;
2248 if (EffectiveLatency < 160) {
2249 R = 93.2 - (EffectiveLatency / 40);
2250 } else {
2251 R = 93.2 - ((EffectiveLatency - 120) / 10);
2252 }
2253
2254 // Now, let us deduct 2.5 R values per percentage of packet loss (i.e. a
2255 // loss of 5% will be entered as 5).
2256 R = R - (packet_loss * 2.5);
2257
2258 if (R < 0) {
2259 R = 0;
2260 }
2261
2262 score = R;
2263 mos = 1 + ((0.035) * R) + ((.000007) * R * (R - 60) * (100 - R));
2264 } else {
2265 target.jitter = 0;
2266 target.jitter_min = 0;
2267 target.jitter_max = 0;
2268 mos = 0;
2269 }
2270
2271 /* Check which mode is on and do the warn / Crit stuff */
2272 if (modes.rta_mode) {
2273 mp_subcheck sc_rta = mp_subcheck_init();
2274 sc_rta = mp_set_subcheck_default_state(sc_rta, STATE_OK);
2275 xasprintf(&sc_rta.output, "rta %0.3fms", (double)rta / 1000);
2276
2277 if (rta >= crit.rta) {
2278 sc_rta = mp_set_subcheck_state(sc_rta, STATE_CRITICAL);
2279 xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)crit.rta / 1000);
2280 } else if (rta >= warn.rta) {
2281 sc_rta = mp_set_subcheck_state(sc_rta, STATE_WARNING);
2282 xasprintf(&sc_rta.output, "%s > %0.3fms", sc_rta.output, (double)warn.rta / 1000);
2283 }
2284
2285 if (packet_loss < 100) {
2286 mp_perfdata pd_rta = perfdata_init();
2287 xasprintf(&pd_rta.label, "%srta", address);
2288 pd_rta.uom = strdup("ms");
2289 pd_rta.value = mp_create_pd_value(rta / 1000);
2290 pd_rta.min = mp_create_pd_value(0);
2291
2292 pd_rta.warn = mp_range_set_end(pd_rta.warn, mp_create_pd_value(warn.rta));
2293 pd_rta.crit = mp_range_set_end(pd_rta.crit, mp_create_pd_value(crit.rta));
2294 mp_add_perfdata_to_subcheck(&sc_rta, pd_rta);
2295
2296 mp_perfdata pd_rt_min = perfdata_init();
2297 xasprintf(&pd_rt_min.label, "%srtmin", address);
2298 pd_rt_min.value = mp_create_pd_value(target.rtmin / 1000);
2299 pd_rt_min.uom = strdup("ms");
2300 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_min);
2301
2302 mp_perfdata pd_rt_max = perfdata_init();
2303 xasprintf(&pd_rt_max.label, "%srtmax", address);
2304 pd_rt_max.value = mp_create_pd_value(target.rtmax / 1000);
2305 pd_rt_max.uom = strdup("ms");
2306 mp_add_perfdata_to_subcheck(&sc_rta, pd_rt_max);
2307 }
2308
2309 mp_add_subcheck_to_subcheck(&result, sc_rta);
2310 }
2311
2312 if (modes.pl_mode) {
2313 mp_subcheck sc_pl = mp_subcheck_init();
2314 sc_pl = mp_set_subcheck_default_state(sc_pl, STATE_OK);
2315 xasprintf(&sc_pl.output, "packet loss %.1f%%", packet_loss);
2316
2317 if (packet_loss >= crit.pl) {
2318 sc_pl = mp_set_subcheck_state(sc_pl, STATE_CRITICAL);
2319 xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl);
2320 } else if (packet_loss >= warn.pl) {
2321 sc_pl = mp_set_subcheck_state(sc_pl, STATE_WARNING);
2322 xasprintf(&sc_pl.output, "%s > %u%%", sc_pl.output, crit.pl);
2323 }
2324
2325 mp_perfdata pd_pl = perfdata_init();
2326 xasprintf(&pd_pl.label, "%spl", address);
2327 pd_pl.uom = strdup("%");
2328
2329 pd_pl.warn = mp_range_set_end(pd_pl.warn, mp_create_pd_value(warn.pl));
2330 pd_pl.crit = mp_range_set_end(pd_pl.crit, mp_create_pd_value(crit.pl));
2331 pd_pl.value = mp_create_pd_value(packet_loss);
2332
2333 mp_add_perfdata_to_subcheck(&sc_pl, pd_pl);
2334
2335 mp_add_subcheck_to_subcheck(&result, sc_pl);
2336 }
2337
2338 if (modes.jitter_mode) {
2339 mp_subcheck sc_jitter = mp_subcheck_init();
2340 sc_jitter = mp_set_subcheck_default_state(sc_jitter, STATE_OK);
2341 xasprintf(&sc_jitter.output, "jitter %0.3fms", target.jitter);
2342
2343 if (target.jitter >= crit.jitter) {
2344 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_CRITICAL);
2345 xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, crit.jitter);
2346 } else if (target.jitter >= warn.jitter) {
2347 sc_jitter = mp_set_subcheck_state(sc_jitter, STATE_WARNING);
2348 xasprintf(&sc_jitter.output, "%s > %0.3fms", sc_jitter.output, warn.jitter);
2349 }
2350
2351 if (packet_loss < 100) {
2352 mp_perfdata pd_jitter = perfdata_init();
2353 pd_jitter.uom = strdup("ms");
2354 xasprintf(&pd_jitter.label, "%sjitter_avg", address);
2355 pd_jitter.value = mp_create_pd_value(target.jitter);
2356 pd_jitter.warn = mp_range_set_end(pd_jitter.warn, mp_create_pd_value(warn.jitter));
2357 pd_jitter.crit = mp_range_set_end(pd_jitter.crit, mp_create_pd_value(crit.jitter));
2358 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter);
2359
2360 mp_perfdata pd_jitter_min = perfdata_init();
2361 pd_jitter_min.uom = strdup("ms");
2362 xasprintf(&pd_jitter_min.label, "%sjitter_min", address);
2363 pd_jitter_min.value = mp_create_pd_value(target.jitter_min);
2364 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_min);
2365
2366 mp_perfdata pd_jitter_max = perfdata_init();
2367 pd_jitter_max.uom = strdup("ms");
2368 xasprintf(&pd_jitter_max.label, "%sjitter_max", address);
2369 pd_jitter_max.value = mp_create_pd_value(target.jitter_max);
2370 mp_add_perfdata_to_subcheck(&sc_jitter, pd_jitter_max);
2371 }
2372 mp_add_subcheck_to_subcheck(&result, sc_jitter);
2373 }
2374
2375 if (modes.mos_mode) {
2376 mp_subcheck sc_mos = mp_subcheck_init();
2377 sc_mos = mp_set_subcheck_default_state(sc_mos, STATE_OK);
2378 xasprintf(&sc_mos.output, "MOS %0.1f", mos);
2379
2380 if (mos <= crit.mos) {
2381 sc_mos = mp_set_subcheck_state(sc_mos, STATE_CRITICAL);
2382 xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, crit.mos);
2383 } else if (mos <= warn.mos) {
2384 sc_mos = mp_set_subcheck_state(sc_mos, STATE_WARNING);
2385 xasprintf(&sc_mos.output, "%s < %0.1f", sc_mos.output, warn.mos);
2386 }
2387
2388 if (packet_loss < 100) {
2389 mp_perfdata pd_mos = perfdata_init();
2390 xasprintf(&pd_mos.label, "%smos", address);
2391 pd_mos.value = mp_create_pd_value(mos);
2392 pd_mos.warn = mp_range_set_end(pd_mos.warn, mp_create_pd_value(warn.mos));
2393 pd_mos.crit = mp_range_set_end(pd_mos.crit, mp_create_pd_value(crit.mos));
2394 pd_mos.min = mp_create_pd_value(0);
2395 pd_mos.max = mp_create_pd_value(5);
2396 mp_add_perfdata_to_subcheck(&sc_mos, pd_mos);
2397 }
2398 mp_add_subcheck_to_subcheck(&result, sc_mos);
2399 }
2400
2401 if (modes.score_mode) {
2402 mp_subcheck sc_score = mp_subcheck_init();
2403 sc_score = mp_set_subcheck_default_state(sc_score, STATE_OK);
2404 xasprintf(&sc_score.output, "Score %f", score);
2405
2406 if (score <= crit.score) {
2407 sc_score = mp_set_subcheck_state(sc_score, STATE_CRITICAL);
2408 xasprintf(&sc_score.output, "%s < %f", sc_score.output, crit.score);
2409 } else if (score <= warn.score) {
2410 sc_score = mp_set_subcheck_state(sc_score, STATE_WARNING);
2411 xasprintf(&sc_score.output, "%s < %f", sc_score.output, warn.score);
2412 }
2413
2414 if (packet_loss < 100) {
2415 mp_perfdata pd_score = perfdata_init();
2416 xasprintf(&pd_score.label, "%sscore", address);
2417 pd_score.value = mp_create_pd_value(score);
2418 pd_score.warn = mp_range_set_end(pd_score.warn, mp_create_pd_value(warn.score));
2419 pd_score.crit = mp_range_set_end(pd_score.crit, mp_create_pd_value(crit.score));
2420 pd_score.min = mp_create_pd_value(0);
2421 pd_score.max = mp_create_pd_value(100);
2422 mp_add_perfdata_to_subcheck(&sc_score, pd_score);
2423 }
2424
2425 mp_add_subcheck_to_subcheck(&result, sc_score);
2426 }
2427
2428 if (modes.order_mode) {
2429 mp_subcheck sc_order = mp_subcheck_init();
2430 sc_order = mp_set_subcheck_default_state(sc_order, STATE_OK);
2431
2432 if (target.found_out_of_order_packets) {
2433 mp_set_subcheck_state(sc_order, STATE_CRITICAL);
2434 xasprintf(&sc_order.output, "Packets out of order");
2435 } else {
2436 xasprintf(&sc_order.output, "Packets in order");
2437 }
2438
2439 mp_add_subcheck_to_subcheck(&result, sc_order);
2440 }
2441
2442 return result;
2443}
2444
2445evaluate_host_wrapper evaluate_host(check_icmp_target_container host,
2446 check_icmp_mode_switches modes, check_icmp_threshold warn,
2447 check_icmp_threshold crit) {
2448 evaluate_host_wrapper result = {
2449 .targets_warn = 0,
2450 .targets_ok = 0,
2451 .sc_host = mp_subcheck_init(),
2452 };
2453 result.sc_host = mp_set_subcheck_default_state(result.sc_host, STATE_OK);
2454
2455 result.sc_host.output = strdup(host.name);
2456
2457 ping_target *target = host.target_list;
2458 for (unsigned int i = 0; i < host.number_of_targets; i++) {
2459 mp_subcheck sc_target = evaluate_target(*target, modes, warn, crit);
2460
2461 mp_state_enum target_state = mp_compute_subcheck_state(sc_target);
2462
2463 if (target_state == STATE_WARNING) {
2464 result.targets_warn++;
2465 } else if (target_state == STATE_OK) {
2466 result.targets_ok++;
2467 }
2468 mp_add_subcheck_to_subcheck(&result.sc_host, sc_target);
2469
2470 target = target->next;
2471 }
2472
2473 return result;
2474}
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..ec786305
--- /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 .icmp_pkt_size = DEFAULT_PING_DATA_SIZE + ICMP_MINLEN,
38 .pkt_interval = DEFAULT_PKT_INTERVAL,
39 .target_interval = 0,
40 .number_of_packets = DEFAULT_NUMBER_OF_PACKETS,
41
42 .source_ip = NULL,
43 .need_v4 = false,
44 .need_v6 = false,
45
46 .sender_id = 0,
47
48 .mode = MODE_RTA,
49
50 .number_of_targets = 0,
51 .targets = NULL,
52
53 .number_of_hosts = 0,
54 .hosts = NULL,
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 = OK,
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 = ERROR;
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..fc9dd5a6
--- /dev/null
+++ b/plugins-root/check_icmp.d/config.h
@@ -0,0 +1,111 @@
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
16/* threshold structure. all values are maximum allowed, exclusive */
17typedef struct {
18 unsigned char pl; /* max allowed packet loss in percent */
19 time_t rta; /* roundtrip time average, microseconds */
20 double jitter; /* jitter time average, microseconds */
21 double mos; /* MOS */
22 double score; /* Score */
23} check_icmp_threshold;
24
25/* the different modes of this program are as follows:
26 * MODE_RTA: send all packets no matter what (mimic check_icmp and check_ping)
27 * MODE_HOSTCHECK: Return immediately upon any sign of life
28 * In addition, sends packets to ALL addresses assigned
29 * to this host (as returned by gethostbyname() or
30 * gethostbyaddr() and expects one host only to be checked at
31 * a time. Therefore, any packet response what so ever will
32 * count as a sign of life, even when received outside
33 * crit.rta limit. Do not misspell any additional IP's.
34 * MODE_ALL: Requires packets from ALL requested IP to return OK (default).
35 * MODE_ICMP: Default Mode
36 */
37typedef enum {
38 MODE_RTA,
39 MODE_HOSTCHECK,
40 MODE_ALL,
41 MODE_ICMP,
42} check_icmp_execution_mode;
43
44typedef struct {
45 bool order_mode;
46 bool mos_mode;
47 bool rta_mode;
48 bool pl_mode;
49 bool jitter_mode;
50 bool score_mode;
51} check_icmp_mode_switches;
52
53typedef struct {
54 check_icmp_mode_switches modes;
55
56 int min_hosts_alive;
57 check_icmp_threshold crit;
58 check_icmp_threshold warn;
59
60 unsigned long ttl;
61 unsigned short icmp_data_size;
62 unsigned short icmp_pkt_size;
63 time_t pkt_interval;
64 time_t target_interval;
65 unsigned short number_of_packets;
66
67 char *source_ip;
68 bool need_v4;
69 bool need_v6;
70
71 uint16_t sender_id; // PID of the main process, which is used as an ID in packets
72
73 check_icmp_execution_mode mode;
74
75 unsigned short number_of_targets;
76 ping_target *targets;
77
78 unsigned short number_of_hosts;
79 check_icmp_target_container *hosts;
80} check_icmp_config;
81
82check_icmp_config check_icmp_config_init();
83
84/* the data structure */
85typedef struct icmp_ping_data {
86 struct timeval stime; /* timestamp (saved in protocol struct as well) */
87 unsigned short ping_id;
88} icmp_ping_data;
89
90#define MAX_IP_PKT_SIZE 65536 /* (theoretical) max IP packet size */
91#define IP_HDR_SIZE 20
92#define MAX_PING_DATA (MAX_IP_PKT_SIZE - IP_HDR_SIZE - ICMP_MINLEN)
93#define MIN_PING_DATA_SIZE sizeof(struct icmp_ping_data)
94#define DEFAULT_PING_DATA_SIZE (MIN_PING_DATA_SIZE + 44)
95
96/* 80 msec packet interval by default */
97#define DEFAULT_PKT_INTERVAL 80000
98#define DEFAULT_TARGET_INTERVAL 0
99
100#define DEFAULT_WARN_RTA 200000
101#define DEFAULT_CRIT_RTA 500000
102#define DEFAULT_WARN_PL 40
103#define DEFAULT_CRIT_PL 80
104
105#define DEFAULT_TIMEOUT 10
106#define DEFAULT_TTL 64
107
108#define DEFAULT_NUMBER_OF_PACKETS 5
109
110#define PACKET_BACKOFF_FACTOR 1.5
111#define TARGET_BACKOFF_FACTOR 1.5
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" );
diff --git a/plugins/utils.h b/plugins/utils.h
index 92a6c115..1d3c153c 100644
--- a/plugins/utils.h
+++ b/plugins/utils.h
@@ -76,7 +76,7 @@ char *strnl(char *);
76char *strpcpy(char *, const char *, const char *); 76char *strpcpy(char *, const char *, const char *);
77char *strpcat(char *, const char *, const char *); 77char *strpcat(char *, const char *, const char *);
78int xvasprintf(char **strp, const char *fmt, va_list ap); 78int xvasprintf(char **strp, const char *fmt, va_list ap);
79int xasprintf(char **strp, const char *fmt, ...); 79int xasprintf(char **strp, const char *fmt, ...)__attribute__ ((format (printf, 2, 3)));
80 80
81void usage(const char *) __attribute__((noreturn)); 81void usage(const char *) __attribute__((noreturn));
82void usage2(const char *, const char *) __attribute__((noreturn)); 82void usage2(const char *, const char *) __attribute__((noreturn));