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