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