1 /*
2 ex: set tabstop=4 shiftwidth=4 autoindent:
3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2002-2016 The Cacti Group |
5 | |
6 | This program is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2.1 of the License, or (at your option) any later version. |
10 | |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with this library; if not, write to the Free Software |
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | 02110-1301, USA |
20 | |
21 +-------------------------------------------------------------------------+
22 | spine: a backend data gatherer for cacti |
23 +-------------------------------------------------------------------------+
24 | This poller would not have been possible without: |
25 | - Larry Adams (current development and enhancements) |
26 | - Rivo Nurges (rrd support, mysql poller cache, misc functions) |
27 | - RTG (core poller code, pthreads, snmp, autoconf examples) |
28 | - Brady Alleman/Doug Warner (threading ideas, implimentation details) |
29 +-------------------------------------------------------------------------+
30 | - Cacti - http://www.cacti.net/ |
31 +-------------------------------------------------------------------------+
32 */
33
34 #include "common.h"
35 #include "spine.h"
36
37 /*! \fn int ping_host(host_t *host, ping_t *ping)
38 * \brief ping a host to determine if it is reachable for polling
39 * \param host a pointer to the current host structure
40 * \param ping a pointer to the current hosts ping structure
41 *
42 * This function pings a host using the method specified within the system
43 * configuration and then returns the host status to the calling function.
44 *
45 * \return HOST_UP if the host is reachable, HOST_DOWN otherwise.
46 */
ping_host(host_t * host,ping_t * ping)47 int ping_host(host_t *host, ping_t *ping) {
48 int ping_result;
49 int snmp_result;
50
51 /* snmp pinging has been selected at a minimum */
52 ping_result = 0;
53 snmp_result = 0;
54
55 /* icmp/tcp/udp ping test */
56 if ((host->availability_method == AVAIL_SNMP_AND_PING) ||
57 (host->availability_method == AVAIL_PING) ||
58 (host->availability_method == AVAIL_SNMP_OR_PING)) {
59
60 if (host->ping_method == PING_ICMP) {
61 if (set.icmp_avail == FALSE) {
62 SPINE_LOG_DEBUG(("Host[%i] DEBUG Falling back to UDP Ping Due to SetUID Issues", host->id));
63 host->ping_method = PING_UDP;
64 }
65 }
66
67 if (!strstr(host->hostname, "localhost")) {
68 if (host->ping_method == PING_ICMP) {
69 ping_result = ping_icmp(host, ping);
70 }else if (host->ping_method == PING_UDP) {
71 ping_result = ping_udp(host, ping);
72 }else if (host->ping_method == PING_TCP) {
73 ping_result = ping_tcp(host, ping);
74 }
75 }else{
76 snprintf(ping->ping_status, 50, "0.000");
77 snprintf(ping->ping_response, SMALL_BUFSIZE, "PING: Host does not require ping");
78 ping_result = HOST_UP;
79 }
80 }
81
82 /* snmp test */
83 if ((host->availability_method == AVAIL_SNMP) ||
84 (host->availability_method == AVAIL_SNMP_GET_SYSDESC) ||
85 (host->availability_method == AVAIL_SNMP_GET_NEXT) ||
86 (host->availability_method == AVAIL_SNMP_AND_PING) ||
87 ((host->availability_method == AVAIL_SNMP_OR_PING) && (ping_result != HOST_UP))) {
88 snmp_result = ping_snmp(host, ping);
89 }
90
91 switch (host->availability_method) {
92 case AVAIL_SNMP_AND_PING:
93 if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) {
94 if (ping_result == HOST_UP) {
95 return HOST_UP;
96 }else{
97 return HOST_DOWN;
98 }
99 }
100
101 if ((snmp_result == HOST_UP) && (ping_result == HOST_UP)) {
102 return HOST_UP;
103 }else{
104 return HOST_DOWN;
105 }
106 case AVAIL_SNMP_OR_PING:
107 if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) {
108 if (ping_result == HOST_UP) {
109 return HOST_UP;
110 }else{
111 return HOST_DOWN;
112 }
113 }
114
115 if (snmp_result == HOST_UP) {
116 return HOST_UP;
117 }
118
119 if (ping_result == HOST_UP) {
120 return HOST_UP;
121 }else{
122 return HOST_DOWN;
123 }
124 case AVAIL_SNMP:
125 case AVAIL_SNMP_GET_NEXT:
126 case AVAIL_SNMP_GET_SYSDESC:
127 if (snmp_result == HOST_UP) {
128 return HOST_UP;
129 }else{
130 return HOST_DOWN;
131 }
132 case AVAIL_PING:
133 if (ping_result == HOST_UP) {
134 return HOST_UP;
135 }else{
136 return HOST_DOWN;
137 }
138 case AVAIL_NONE:
139 return HOST_UP;
140 default:
141 return HOST_DOWN;
142 }
143 }
144
145 /*! \fn int ping_snmp(host_t *host, ping_t *ping)
146 * \brief ping a host using snmp sysUptime
147 * \param host a pointer to the current host structure
148 * \param ping a pointer to the current hosts ping structure
149 *
150 * This function pings a host using snmp. It polls sysUptime by default.
151 * It will modify the ping structure to include the specifics of the ping results.
152 *
153 * \return HOST_UP if the host is reachable, HOST_DOWN otherwise.
154 *
155 */
ping_snmp(host_t * host,ping_t * ping)156 int ping_snmp(host_t *host, ping_t *ping) {
157 char *poll_result;
158 char *oid;
159 double begin_time, end_time, total_time;
160 double one_thousand = 1000.00;
161
162 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Entering SNMP Ping", host->id));
163
164 if (host->snmp_session) {
165 if ((strlen(host->snmp_community) != 0) || (host->snmp_version == 3)) {
166 /* by default, we look at sysUptime */
167 if (host->availability_method == AVAIL_SNMP_GET_NEXT) {
168 oid = strdup(".1.3");
169 }else if (host->availability_method == AVAIL_SNMP_GET_SYSDESC) {
170 oid = strdup(".1.3.6.1.2.1.1.1.0");
171 }else {
172 oid = strdup(".1.3.6.1.2.1.1.3.0");
173 }
174
175 if (oid == NULL) die("ERROR: malloc(): strdup() oid ping.c failed");
176
177 /* record start time */
178 begin_time = get_time_as_double();
179
180 if (host->availability_method == AVAIL_SNMP_GET_NEXT) {
181 poll_result = snmp_getnext(host, oid);
182 } else {
183 poll_result = snmp_get(host, oid);
184 }
185
186 /* record end time */
187 end_time = get_time_as_double();
188
189 free(oid);
190
191 total_time = (end_time - begin_time) * one_thousand;
192
193 /* do positive test cases first */
194 if (host->snmp_status == SNMPERR_UNKNOWN_OBJID) {
195 snprintf(ping->snmp_response, SMALL_BUFSIZE, "Host responded to SNMP");
196 snprintf(ping->snmp_status, 50, "%.5f", total_time);
197 free(poll_result);
198 return HOST_UP;
199 }else if (host->snmp_status != SNMPERR_SUCCESS) {
200 SPINE_LOG_MEDIUM(("Host[%i] SNMP Ping Error: %s", host->id, snmp_api_errstring(host->snmp_status)));
201 snprintf(ping->snmp_response, SMALL_BUFSIZE, "Host did not respond to SNMP");
202 free(poll_result);
203 return HOST_DOWN;
204 }else{
205 snprintf(ping->snmp_response, SMALL_BUFSIZE, "Host responded to SNMP");
206 snprintf(ping->snmp_status, 50, "%.5f", total_time);
207 free(poll_result);
208 return HOST_UP;
209 }
210 }else{
211 snprintf(ping->snmp_status, 50, "0.00");
212 snprintf(ping->snmp_response, SMALL_BUFSIZE, "Host does not require SNMP");
213 return HOST_UP;
214 }
215 }else{
216 snprintf(ping->snmp_status, 50, "0.00");
217 snprintf(ping->snmp_response, SMALL_BUFSIZE, "Invalid SNMP Session");
218 return HOST_DOWN;
219 }
220 }
221
222 /*! \fn int ping_icmp(host_t *host, ping_t *ping)
223 * \brief ping a host using an ICMP packet
224 * \param host a pointer to the current host structure
225 * \param ping a pointer to the current hosts ping structure
226 *
227 * This function pings a host using ICMP. The ICMP packet contains a marker
228 * to the "Cacti" application so that firewall's can be configured to allow.
229 * It will modify the ping structure to include the specifics of the ping results.
230 *
231 * \return HOST_UP if the host is reachable, HOST_DOWN otherwise.
232 *
233 */
ping_icmp(host_t * host,ping_t * ping)234 int ping_icmp(host_t *host, ping_t *ping) {
235 int icmp_socket;
236
237 double begin_time, end_time, total_time;
238 double host_timeout;
239 double one_thousand = 1000.00;
240 struct timeval timeout;
241
242 struct sockaddr_in recvname;
243 struct sockaddr_in fromname;
244 char socket_reply[BUFSIZE];
245 int retry_count;
246 char *cacti_msg = "cacti-monitoring-system\0";
247 int packet_len;
248 socklen_t fromlen;
249 ssize_t return_code;
250 fd_set socket_fds;
251
252 static unsigned int seq = 0;
253 struct icmp *icmp;
254 struct ip *ip;
255 struct icmp *pkt;
256 unsigned char *packet;
257 char *new_hostname;
258
259 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Entering ICMP Ping", host->id));
260
261 /* remove "tcp:" from hostname */
262 new_hostname = remove_tcp_udp_from_hostname(host->hostname);
263
264 /* get ICMP socket */
265 retry_count = 0;
266 while ( TRUE ) {
267 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
268 if (hasCaps() != TRUE) {
269 thread_mutex_lock(LOCK_SETEUID);
270 seteuid(0);
271 }
272 #endif
273
274 if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) {
275 usleep(500000);
276 retry_count++;
277
278 if (retry_count > 4) {
279 snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Ping unable to create ICMP Socket");
280 snprintf(ping->ping_status, 50, "down");
281 free(new_hostname);
282 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
283 if (hasCaps() != TRUE) {
284 seteuid(getuid());
285 thread_mutex_unlock(LOCK_SETEUID);
286 }
287 #endif
288
289 return HOST_DOWN;
290
291 break;
292 }
293 }else{
294 break;
295 }
296 }
297 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
298 if (hasCaps() != TRUE) {
299 seteuid(getuid());
300 thread_mutex_unlock(LOCK_SETEUID);
301 }
302 #endif
303
304 /* convert the host timeout to a double precision number in seconds */
305 host_timeout = host->ping_timeout;
306
307 /* allocate the packet in memory */
308 packet_len = ICMP_HDR_SIZE + strlen(cacti_msg);
309
310 if (!(packet = malloc(packet_len))) {
311 die("ERROR: Fatal malloc error: ping.c ping_icmp!");
312 }
313 memset(packet, 0, packet_len);
314
315 /* set the memory of the ping address */
316 memset(&fromname, 0, sizeof(struct sockaddr_in));
317 memset(&recvname, 0, sizeof(struct sockaddr_in));
318
319 icmp = (struct icmp*) packet;
320
321 icmp->icmp_type = ICMP_ECHO;
322 icmp->icmp_code = 0;
323 icmp->icmp_id = getpid() & 0xFFFF;
324
325 /* lock set/get the sequence and unlock */
326 thread_mutex_lock(LOCK_GHBN);
327 icmp->icmp_seq = seq++;
328 thread_mutex_unlock(LOCK_GHBN);
329
330 icmp->icmp_cksum = 0;
331 memcpy(packet+ICMP_HDR_SIZE, cacti_msg, strlen(cacti_msg));
332 icmp->icmp_cksum = get_checksum(packet, packet_len);
333
334 /* hostname must be nonblank */
335 if ((strlen(host->hostname) != 0) && (icmp_socket != -1)) {
336 /* initialize variables */
337 snprintf(ping->ping_status, 50, "down");
338 snprintf(ping->ping_response, SMALL_BUFSIZE, "default");
339
340 /* get address of hostname */
341 if (init_sockaddr(&fromname, new_hostname, 7)) {
342 retry_count = 0;
343 total_time = 0;
344 begin_time = 0;
345
346 /* initialize file descriptor to review for input/output */
347 FD_ZERO(&socket_fds);
348 FD_SET(icmp_socket,&socket_fds);
349
350 while (1) {
351 if (retry_count > host->ping_retries) {
352 snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Ping timed out");
353 snprintf(ping->ping_status, 50, "down");
354 free(new_hostname);
355 free(packet);
356 close(icmp_socket);
357 return HOST_DOWN;
358 }
359
360 /* record start time */
361 if (total_time == 0) {
362 /* establish timeout value */
363 timeout.tv_sec = 0;
364 timeout.tv_usec = host->ping_timeout * 1000;
365
366 /* set the socket send and receive timeout */
367 setsockopt(icmp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
368 setsockopt(icmp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
369
370 begin_time = get_time_as_double();
371 }else{
372 /* decrement the timeout value by the total time */
373 timeout.tv_usec = (host->ping_timeout - total_time) * 1000;
374 }
375
376 /* send packet to destination */
377 return_code = sendto(icmp_socket, packet, packet_len, 0, (struct sockaddr *) &fromname, sizeof(fromname));
378
379 fromlen = sizeof(fromname);
380
381 /* wait for a response on the socket */
382 keep_listening:
383 return_code = select(FD_SETSIZE, &socket_fds, NULL, NULL, &timeout);
384
385 /* record end time */
386 end_time = get_time_as_double();
387
388 /* caculate total time */
389 total_time = (end_time - begin_time) * one_thousand;
390
391 /* check to see which socket talked */
392 if (total_time < host_timeout) {
393 #if !(defined(__CYGWIN__))
394 return_code = recvfrom(icmp_socket, socket_reply, BUFSIZE, MSG_WAITALL, (struct sockaddr *) &recvname, &fromlen);
395 #else
396 return_code = recvfrom(icmp_socket, socket_reply, BUFSIZE, MSG_PEEK, (struct sockaddr *) &recvname, &fromlen);
397 #endif
398
399 if (return_code < 0) {
400 if (errno == EINTR) {
401 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Received EINTR", host->id));
402 /* call was interrupted by some system event */
403 goto keep_listening;
404 }
405 }else{
406 ip = (struct ip *) socket_reply;
407 pkt = (struct icmp *) (socket_reply + (ip->ip_hl << 2));
408
409 if (fromname.sin_addr.s_addr == recvname.sin_addr.s_addr) {
410 if ((pkt->icmp_type == ICMP_ECHOREPLY)) {
411 SPINE_LOG_DEBUG(("Host[%i] DEBUG: ICMP Host Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time)));
412 snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Host is Alive");
413 snprintf(ping->ping_status, 50, "%.5f", total_time);
414 free(new_hostname);
415 free(packet);
416 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
417 if (hasCaps() != TRUE) {
418 thread_mutex_lock(LOCK_SETEUID);
419 seteuid(0);
420 }
421 #endif
422 close(icmp_socket);
423 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
424 if (hasCaps() != TRUE) {
425 seteuid(getuid());
426 thread_mutex_unlock(LOCK_SETEUID);
427 }
428 #endif
429
430 return HOST_UP;
431 }else{
432 /* received a response other than an echo reply */
433 if (total_time > host_timeout) {
434 retry_count++;
435 total_time = 0;
436 }
437
438 continue;
439 }
440 }else{
441 /* another host responded */
442 goto keep_listening;
443 }
444 }
445 }else{
446 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Exceeded Host Timeout, Retrying", host->id));
447 }
448
449 total_time = 0;
450 retry_count++;
451 #ifndef SOLAR_THREAD
452 usleep(1000);
453 #endif
454 }
455 }else{
456 snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Destination hostname invalid");
457 snprintf(ping->ping_status, 50, "down");
458 free(new_hostname);
459 free(packet);
460 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
461 if (hasCaps() != TRUE) {
462 thread_mutex_lock(LOCK_SETEUID);
463 seteuid(0);
464 }
465 #endif
466 close(icmp_socket);
467 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
468 if (hasCaps() != TRUE) {
469 seteuid(getuid());
470 thread_mutex_unlock(LOCK_SETEUID);
471 }
472 #endif
473 return HOST_DOWN;
474 }
475 }else{
476 snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Destination address not specified");
477 snprintf(ping->ping_status, 50, "down");
478 free(new_hostname);
479 free(packet);
480 if (icmp_socket != -1) {
481 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
482 if (hasCaps() != TRUE) {
483 thread_mutex_lock(LOCK_SETEUID);
484 seteuid(0);
485 }
486 #endif
487 close(icmp_socket);
488 #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV))
489 if (hasCaps() != TRUE) {
490 seteuid(getuid());
491 thread_mutex_unlock(LOCK_SETEUID);
492 }
493 #endif
494 }
495 return HOST_DOWN;
496 }
497 }
498
499 /*! \fn int ping_udp(host_t *host, ping_t *ping)
500 * \brief ping a host using an UDP datagram
501 * \param host a pointer to the current host structure
502 * \param ping a pointer to the current hosts ping structure
503 *
504 * This function pings a host using UDP. The UDP datagram contains a marker
505 * to the "Cacti" application so that firewall's can be configured to allow.
506 * It will modify the ping structure to include the specifics of the ping results.
507 *
508 * \return HOST_UP if the host is reachable, HOST_DOWN otherwise.
509 *
510 */
ping_udp(host_t * host,ping_t * ping)511 int ping_udp(host_t *host, ping_t *ping) {
512 double begin_time, end_time, total_time;
513 double host_timeout;
514 double one_thousand = 1000.00;
515 struct timeval timeout;
516 int udp_socket;
517 struct sockaddr_in servername;
518 char socket_reply[BUFSIZE];
519 int retry_count;
520 char request[BUFSIZE];
521 int request_len;
522 int return_code;
523 fd_set socket_fds;
524 char *new_hostname;
525
526 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Entering UDP Ping", host->id));
527
528 /* set total time */
529 total_time = 0;
530
531 /* remove "udp:" from hostname */
532 new_hostname = remove_tcp_udp_from_hostname(host->hostname);
533
534 /* convert the host timeout to a double precision number in seconds */
535 host_timeout = host->ping_timeout;
536
537 /* establish timeout value */
538 if (host->ping_timeout >= 1000) {
539 timeout.tv_sec = rint(floor(host_timeout / 1000));
540 timeout.tv_usec = (timeout.tv_sec * 1000000) - (host->ping_timeout * 1000);
541 }else{
542 timeout.tv_sec = 0;
543 timeout.tv_usec = (host->ping_timeout * 1000);
544 }
545
546 /* initilize the socket */
547 udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
548
549 /* hostname must be nonblank */
550 if ((strlen(host->hostname) != 0) && (udp_socket != -1)) {
551 /* initialize variables */
552 snprintf(ping->ping_status, 50, "down");
553 snprintf(ping->ping_response, SMALL_BUFSIZE, "default");
554
555 /* set the socket timeout */
556 setsockopt(udp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
557 setsockopt(udp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
558
559 /* get address of hostname */
560 if (init_sockaddr(&servername, new_hostname, host->ping_port)) {
561 if (connect(udp_socket, (struct sockaddr *) &servername, sizeof(servername)) < 0) {
562 snprintf(ping->ping_status, 50, "down");
563 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Cannot connect to host");
564 free(new_hostname);
565 close(udp_socket);
566 return HOST_DOWN;
567 }
568
569 /* format packet */
570 snprintf(request, BUFSIZE, "cacti-monitoring-system"); /* the actual test data */
571 request_len = strlen(request);
572
573 retry_count = 0;
574
575 /* initialize file descriptor to review for input/output */
576 FD_ZERO(&socket_fds);
577 FD_SET(udp_socket,&socket_fds);
578
579 while (1) {
580 if (retry_count > host->ping_retries) {
581 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Ping timed out");
582 snprintf(ping->ping_status, 50, "down");
583 free(new_hostname);
584 close(udp_socket);
585 return HOST_DOWN;
586 }
587
588 /* record start time */
589 begin_time = get_time_as_double();
590
591 /* establish timeout value */
592 if (host->ping_timeout >= 1000) {
593 timeout.tv_sec = rint(floor(host_timeout / 1000));
594 timeout.tv_usec = (timeout.tv_sec * 1000000) - (host->ping_timeout * 1000);
595 }else{
596 timeout.tv_sec = 0;
597 timeout.tv_usec = (host->ping_timeout * 1000);
598 }
599
600 /* send packet to destination */
601 send(udp_socket, request, request_len, 0);
602
603 /* wait for a response on the socket */
604 wait_more:
605 return_code = select(FD_SETSIZE, &socket_fds, NULL, NULL, &timeout);
606
607 /* record end time */
608 end_time = get_time_as_double();
609
610 /* caculate total time */
611 total_time = (end_time - begin_time) * one_thousand;
612
613 /* check to see which socket talked */
614 if (return_code > 0) {
615 if (FD_ISSET(udp_socket, &socket_fds)) {
616 return_code = read(udp_socket, socket_reply, BUFSIZE);
617
618 if ((return_code == -1) && ((errno == ECONNRESET) || (errno == ECONNREFUSED))) {
619 SPINE_LOG_DEBUG(("Host[%i] DEBUG: UDP Host Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time)));
620 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Host is Alive");
621 snprintf(ping->ping_status, 50, "%.5f", total_time);
622 free(new_hostname);
623 close(udp_socket);
624 return HOST_UP;
625 }
626 }
627 }else if (return_code == -1) {
628 if (errno == EINTR) {
629 /* interrupted, try again */
630 goto wait_more;
631 }else{
632 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Host is Down");
633 snprintf(ping->ping_status, 50, "%.5f", total_time);
634 free(new_hostname);
635 close(udp_socket);
636 return HOST_DOWN;
637 }
638 }else{
639 /* timeout */
640 }
641
642 SPINE_LOG_DEBUG(("Host[%i] DEBUG: UDP Timeout, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time)));
643
644 retry_count++;
645 #ifndef SOLAR_THREAD
646 usleep(1000);
647 #endif
648 }
649 }else{
650 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Destination hostname invalid");
651 snprintf(ping->ping_status, 50, "down");
652 free(new_hostname);
653 close(udp_socket);
654 return HOST_DOWN;
655 }
656 }else{
657 snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Destination address invalid or unable to create socket");
658 snprintf(ping->ping_status, 50, "down");
659 free(new_hostname);
660 if (udp_socket != -1) close(udp_socket);
661 return HOST_DOWN;
662 }
663 }
664
665
666 /*! \fn int ping_tcp(host_t *host, ping_t *ping)
667 * \brief ping a host using an TCP syn
668 * \param host a pointer to the current host structure
669 * \param ping a pointer to the current hosts ping structure
670 *
671 * This function pings a host using TCP. The TCP socket contains a marker
672 * to the "Cacti" application so that firewall's can be configured to allow.
673 * It will modify the ping structure to include the specifics of the ping results.
674 *
675 * \return HOST_UP if the host is reachable, HOST_DOWN otherwise.
676 *
677 */
ping_tcp(host_t * host,ping_t * ping)678 int ping_tcp(host_t *host, ping_t *ping) {
679 double begin_time, end_time, total_time;
680 double host_timeout;
681 double one_thousand = 1000.00;
682 struct timeval timeout;
683 int tcp_socket;
684 struct sockaddr_in servername;
685 int retry_count;
686 int return_code;
687 char *new_hostname;
688
689 SPINE_LOG_DEBUG(("Host[%i] DEBUG: Entering TCP Ping", host->id));
690
691 /* remove "tcp:" from hostname */
692 new_hostname = remove_tcp_udp_from_hostname(host->hostname);
693
694 /* convert the host timeout to a double precision number in seconds */
695 host_timeout = host->ping_timeout;
696
697 /* establish timeout value */
698 if (host->ping_timeout >= 1000) {
699 timeout.tv_sec = rint(floor(host_timeout / 1000));
700 timeout.tv_usec = (timeout.tv_sec * 1000000) - (host->ping_timeout * 1000);
701 }else{
702 timeout.tv_sec = 0;
703 timeout.tv_usec = (host->ping_timeout * 1000);
704 }
705
706 /* initilize the socket */
707 tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
708
709 /* hostname must be nonblank */
710 if ((strlen(host->hostname) != 0) && (tcp_socket != -1)) {
711 /* initialize variables */
712 snprintf(ping->ping_status, 50, "down");
713 snprintf(ping->ping_response, SMALL_BUFSIZE, "default");
714
715 /* set the socket timeout */
716 setsockopt(tcp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
717 setsockopt(tcp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
718
719 /* get address of hostname */
720 if (init_sockaddr(&servername, new_hostname, host->ping_port)) {
721 /* first attempt a connect */
722 retry_count = 0;
723
724 while (1) {
725 /* record start time */
726 begin_time = get_time_as_double();
727
728 /* make the connection */
729 return_code = connect(tcp_socket, (struct sockaddr *) &servername, sizeof(servername));
730
731 /* record end time */
732 end_time = get_time_as_double();
733
734 /* caculate total time */
735 total_time = (end_time - begin_time) * one_thousand;
736
737 if (((return_code == -1) && (errno == ECONNREFUSED)) ||
738 (return_code == 0)) {
739 SPINE_LOG_DEBUG(("Host[%i] DEBUG: TCP Host Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time)));
740 snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Host is Alive");
741 snprintf(ping->ping_status, 50, "%.5f", total_time);
742 free(new_hostname);
743 close(tcp_socket);
744 return HOST_UP;
745 }else{
746 #if defined(__CYGWIN__)
747 snprintf(ping->ping_status, 50, "down");
748 snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Cannot connect to host");
749 free(new_hostname);
750 close(tcp_socket);
751 return HOST_DOWN;
752 #else
753 if (retry_count > host->ping_retries) {
754 snprintf(ping->ping_status, 50, "down");
755 snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Cannot connect to host");
756 free(new_hostname);
757 close(tcp_socket);
758 return HOST_DOWN;
759 }else{
760 retry_count++;
761 }
762 #endif
763 }
764 }
765 }else{
766 snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Destination hostname invalid");
767 snprintf(ping->ping_status, 50, "down");
768 free(new_hostname);
769 close(tcp_socket);
770 return HOST_DOWN;
771 }
772 }else{
773 snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Destination address invalid or unable to create socket");
774 snprintf(ping->ping_status, 50, "down");
775 free(new_hostname);
776 if (tcp_socket != -1) close(tcp_socket);
777 return HOST_DOWN;
778 }
779 }
780
781 /*! \fn int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port)
782 * \brief converts a hostname to an internet address
783 *
784 * \return TRUE if successful, FALSE otherwise.
785 *
786 */
init_sockaddr(struct sockaddr_in * name,const char * hostname,unsigned short int port)787 int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port) {
788 struct hostent *hostinfo;
789 int retry_count;
790 #if !defined(H_ERRNO_DECLARED) && !defined(_AIX)
791 extern int h_errno;
792 #endif
793
794 name->sin_family = AF_INET;
795 name->sin_port = htons (port);
796
797 retry_count = 0;
798
799 #ifdef HAVE_THREADSAFE_GETHOSTBYNAME
800 retry:
801 hostinfo = gethostbyname(hostname);
802
803 if (!hostinfo) {
804 if (h_errno == TRY_AGAIN && retry_count < 3) {
805 retry_count++;
806 usleep(50000);
807 goto retry;
808 }else{
809 return NULL;
810 }
811 }else{
812 name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
813 }
814
815 #else
816 #ifdef HAVE_GETHOSTBYNAME_R_GLIBC
817 struct hostent result_buf;
818 size_t len = 1024;
819 char *buf;
820 int herr;
821 int rv;
822
823 buf = malloc(len*sizeof(char));
824 memset(buf, 0, sizeof(buf));
825
826 while (1) {
827 rv = gethostbyname_r(hostname, &result_buf, buf, len,
828 &hostinfo, &herr);
829
830 if (!hostinfo) {
831 if (rv == ERANGE) {
832 len *= 2;
833 buf = realloc(buf, len*sizeof(char));
834
835 continue;
836 }else if (herr == TRY_AGAIN && retry_count < 3) {
837 retry_count++;
838 usleep(50000);
839 continue;
840 }else{
841 free(buf);
842 return FALSE;
843 }
844 }else{
845 break;
846 }
847 }
848
849 name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
850
851 free(buf);
852 #else
853 #ifdef HAVE_GETHOSTBYNAME_R_SOLARIS
854 size_t len = 8192;
855 char *buf = NULL;
856 struct hostent result;
857
858 buf = malloc(len*sizeof(char));
859 memset(buf, 0, sizeof(buf));
860
861 while (1) {
862 hostinfo = gethostbyname_r(hostname, &result, buf, len, &h_errno);
863 if (!hostinfo) {
864 if (errno == ERANGE) {
865 len += 1024;
866 buf = realloc(buf, len*sizeof(char));
867 memset(buf, 0, sizeof(buf));
868
869 continue;
870 }else if (h_errno == TRY_AGAIN && retry_count < 3) {
871 retry_count++;
872 usleep(50000);
873 continue;
874 }else{
875 free(buf);
876 return NULL;
877 }
878 }else{
879 break;
880 }
881 }
882
883 name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
884
885 free(buf);
886 #else
887 #ifdef HAVE_GETHOSTBYNAME_R_HPUX
888 struct hostent hostent;
889 struct hostent_data buf;
890 int rv;
891
892 rv = gethostbyname_r(hostname, &hostent, &buf);
893 if (!rv) {
894 name->sin_addr = *(struct in_addr *) hostent->h_addr;
895 }
896
897 #else
898 retry:
899 thread_mutex_lock(LOCK_GHBN);
900 hostinfo = gethostbyname(hostname);
901 if (!hostinfo) {
902 thread_mutex_unlock(LOCK_GHBN);
903 if (h_errno == TRY_AGAIN && retry_count < 3) {
904 retry_count++;
905 usleep(50000);
906 goto retry;
907 }else{
908 hostinfo = NULL;
909 }
910 }else{
911 name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
912 thread_mutex_unlock(LOCK_GHBN);
913 }
914 #endif
915 #endif
916 #endif
917 #endif
918
919 if (hostinfo == NULL) {
920 SPINE_LOG(("WARNING: Unknown host %s", hostname));
921 return FALSE;
922 }else{
923 return TRUE;
924 }
925 }
926
927 /*! \fn char *remove_tcp_udp_from_hostname(char *hostname)
928 * \brief removes 'TCP:' or 'UDP:' from a hostname required to ping
929 *
930 * \return char hostname a trimmed hostname
931 *
932 */
remove_tcp_udp_from_hostname(char * hostname)933 char *remove_tcp_udp_from_hostname(char *hostname) {
934 char *cleaned_hostname;
935
936 if (!(cleaned_hostname = (char *) malloc(strlen(hostname)+1))) {
937 die("ERROR: Fatal malloc error: ping.c remove_tcp_udp_from_hostname");
938 }
939
940 if (!strncasecmp(hostname, "TCP:", 4) ||
941 !strncasecmp(hostname, "UDP:", 4)) {
942 memcpy(cleaned_hostname, hostname+4, strlen(hostname)-4);
943 cleaned_hostname[strlen(hostname)-4] = '\0';
944 }else{
945 strcpy(cleaned_hostname, hostname);
946 }
947
948 return(cleaned_hostname);
949 }
950
951 /*! \fn unsigned short int get_checksum(void* buf, int len)
952 * \brief calculates a 16bit checksum of a packet buffer
953 * \param buf the input buffer to calculate the checksum of
954 * \param len the size of the input buffer
955 *
956 * \return 16bit checksum of an input buffer of size len.
957 *
958 */
get_checksum(void * buf,int len)959 unsigned short int get_checksum(void* buf, int len) {
960 int nleft = len;
961 int32_t sum = 0;
962 unsigned short int answer;
963 unsigned short int* w = (unsigned short int*)buf;
964 unsigned short int odd_byte = 0;
965
966 while (nleft > 1) {
967 sum += *w++;
968 nleft -= 2;
969 }
970
971 if (nleft == 1) {
972 *(unsigned char*)(&odd_byte) = *(unsigned char*)w;
973 sum += odd_byte;
974 }
975
976 sum = (sum >> 16) + (sum & 0xffff);
977 sum += (sum >> 16);
978 answer = ~sum; /* truncate to 16 bits */
979
980 return answer;
981 }
982
983 /*! \fn void update_host_status(int status, host_t *host, ping_t *ping, int availability_method)
984 * \brief update the host table in Cacti with the result of the ping of the host.
985 * \param status the current poll status of the host, either HOST_UP, or HOST_DOWN
986 * \param host a pointer to the current host structure
987 * \param ping a pointer to the current hosts ping structure
988 * \param availability_method the method that was used to poll the host
989 *
990 * This function will determine if the host is UP, DOWN, or RECOVERING based upon
991 * the ping result and it's current status. It will update the Cacti database
992 * with the calculated status.
993 *
994 */
update_host_status(int status,host_t * host,ping_t * ping,int availability_method)995 void update_host_status(int status, host_t *host, ping_t *ping, int availability_method) {
996 int issue_log_message = FALSE;
997 double ping_time;
998 double hundred_percent = 100.00;
999 char current_date[40];
1000
1001 time_t nowbin;
1002 struct tm now_time;
1003 struct tm *now_ptr;
1004
1005 /* get time for poller_output table */
1006 if (time(&nowbin) == (time_t) - 1) {
1007 die("ERROR: Could not get time of day from time()");
1008 }
1009 localtime_r(&nowbin,&now_time);
1010 now_ptr = &now_time;
1011
1012 strftime(current_date, 40, "%Y-%m-%d %H:%M", now_ptr);
1013
1014 /* host is down */
1015 if (status == HOST_DOWN) {
1016 /* update total polls, failed polls and availability */
1017 host->failed_polls = host->failed_polls + 1;
1018 host->total_polls = host->total_polls + 1;
1019 host->availability = hundred_percent * (host->total_polls - host->failed_polls) / host->total_polls;
1020
1021 /*determine the error message to display */
1022 switch (availability_method) {
1023 case AVAIL_SNMP_OR_PING:
1024 case AVAIL_SNMP_AND_PING:
1025 if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) {
1026 snprintf(host->status_last_error, SMALL_BUFSIZE, "%s", ping->ping_response);
1027 }else {
1028 snprintf(host->status_last_error, SMALL_BUFSIZE,"%s, %s",ping->snmp_response,ping->ping_response);
1029 }
1030 break;
1031 case AVAIL_SNMP:
1032 if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) {
1033 snprintf(host->status_last_error, SMALL_BUFSIZE, "%s", "Device does not require SNMP");
1034 }else {
1035 snprintf(host->status_last_error, SMALL_BUFSIZE, "%s", ping->snmp_response);
1036 }
1037 break;
1038 default:
1039 snprintf(host->status_last_error, SMALL_BUFSIZE, "%s", ping->ping_response);
1040 }
1041
1042 /* determine if to send an alert and update remainder of statistics */
1043 if (host->status == HOST_UP) {
1044 /* increment the event failure count */
1045 host->status_event_count++;
1046
1047 /* if it's time to issue an error message, indicate so */
1048 if (host->status_event_count >= set.ping_failure_count) {
1049 /* host is now down, flag it that way */
1050 host->status = HOST_DOWN;
1051
1052 issue_log_message = TRUE;
1053
1054 /* update the failure date only if the failure count is 1 */
1055 if (set.ping_failure_count == 1) {
1056 snprintf(host->status_fail_date, 40, "%s", current_date);
1057 }
1058 /* host is down, but not ready to issue log message */
1059 }else{
1060 /* host down for the first time, set event date */
1061 if (host->status_event_count == 1) {
1062 snprintf(host->status_fail_date, 40, "%s", current_date);
1063 }
1064 }
1065 /* host is recovering, put back in failed state */
1066 }else if (host->status == HOST_RECOVERING) {
1067 host->status_event_count = 1;
1068 host->status = HOST_DOWN;
1069
1070 /* host was unknown and now is down */
1071 }else if (host->status == HOST_UNKNOWN) {
1072 host->status = HOST_DOWN;
1073 host->status_event_count = 0;
1074 }else{
1075 host->status_event_count++;
1076 }
1077 /* host is up!! */
1078 }else{
1079 /* update total polls and availability */
1080 host->total_polls = host->total_polls + 1;
1081 host->availability = hundred_percent * (host->total_polls - host->failed_polls) / host->total_polls;
1082
1083 /* determine the ping statistic to set and do so */
1084 if (availability_method == AVAIL_SNMP_AND_PING) {
1085 if (strlen(host->snmp_community) == 0) {
1086 ping_time = atof(ping->ping_status);
1087 }else {
1088 /* calculate the average of the two times */
1089 ping_time = (atof(ping->snmp_status) + atof(ping->ping_status)) / 2;
1090 }
1091 }else if (availability_method == AVAIL_SNMP) {
1092 if (strlen(host->snmp_community) == 0) {
1093 ping_time = 0.000;
1094 }else {
1095 ping_time = atof(ping->snmp_status);
1096 }
1097 }else if (availability_method == AVAIL_NONE) {
1098 ping_time = 0.000;
1099 }else {
1100 ping_time = atof(ping->ping_status);
1101 }
1102
1103 /* update times as required */
1104 host->cur_time = ping_time;
1105
1106 /* maximum time */
1107 if (ping_time > host->max_time)
1108 host->max_time = ping_time;
1109
1110 /* minimum time */
1111 if (ping_time < host->min_time)
1112 host->min_time = ping_time;
1113
1114 /* average time */
1115 host->avg_time = (((host->total_polls-1-host->failed_polls)
1116 * host->avg_time) + ping_time) / (host->total_polls-host->failed_polls);
1117
1118 /* the host was down, now it's recovering */
1119 if ((host->status == HOST_DOWN) || (host->status == HOST_RECOVERING )) {
1120 /* just up, change to recovering */
1121 if (host->status == HOST_DOWN) {
1122 host->status = HOST_RECOVERING;
1123 host->status_event_count = 1;
1124 }else{
1125 host->status_event_count++;
1126 }
1127
1128 /* if it's time to issue a recovery message, indicate so */
1129 if (host->status_event_count >= set.ping_recovery_count) {
1130 /* host is up, flag it that way */
1131 host->status = HOST_UP;
1132
1133 issue_log_message = TRUE;
1134
1135 /* update the recovery date only if the recovery count is 1 */
1136 if (set.ping_recovery_count == 1) {
1137 snprintf(host->status_rec_date, 40, "%s", current_date);
1138 }
1139
1140 /* reset the event counter */
1141 host->status_event_count = 0;
1142 /* host is recovering, but not ready to issue log message */
1143 }else{
1144 /* host recovering for the first time, set event date */
1145 if (host->status_event_count == 1) {
1146 snprintf(host->status_rec_date, 40, "%s", current_date);
1147 }
1148 }
1149 }else{
1150 /* host was unknown and now is up */
1151 host->status = HOST_UP;
1152 host->status_event_count = 0;
1153 }
1154 }
1155 /* if the user wants a flood of information then flood them */
1156 if (set.log_level >= POLLER_VERBOSITY_HIGH) {
1157 if ((host->status == HOST_UP) || (host->status == HOST_RECOVERING)) {
1158 /* log ping result if we are to use a ping for reachability testing */
1159 if (availability_method == AVAIL_SNMP_AND_PING) {
1160 SPINE_LOG_HIGH(("Host[%i] PING Result: %s", host->id, ping->ping_response));
1161 SPINE_LOG_HIGH(("Host[%i] SNMP Result: %s", host->id, ping->snmp_response));
1162 }else if (availability_method == AVAIL_SNMP_OR_PING) {
1163 SPINE_LOG_HIGH(("Host[%i] PING Result: %s", host->id, ping->ping_response));
1164 SPINE_LOG_HIGH(("Host[%i] SNMP Result: %s", host->id, ping->snmp_response));
1165 }else if (availability_method == AVAIL_SNMP) {
1166 if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) {
1167 SPINE_LOG_HIGH(("Host[%i] SNMP Result: Device does not require SNMP", host->id));
1168 }else{
1169 SPINE_LOG_HIGH(("Host[%i] SNMP Result: %s", host->id, ping->snmp_response));
1170 }
1171 }else if (availability_method == AVAIL_NONE) {
1172 SPINE_LOG_HIGH(("Host[%i] No Host Availability Method Selected", host->id));
1173 }else{
1174 SPINE_LOG_HIGH(("Host[%i] PING: Result %s", host->id, ping->ping_response));
1175 }
1176 }else{
1177 if (availability_method == AVAIL_SNMP_AND_PING) {
1178 SPINE_LOG_HIGH(("Host[%i] PING Result: %s", host->id, ping->ping_response));
1179 SPINE_LOG_HIGH(("Host[%i] SNMP Result: %s", host->id, ping->snmp_response));
1180 }else if (availability_method == AVAIL_SNMP) {
1181 SPINE_LOG_HIGH(("Host[%i] SNMP Result: %s", host->id, ping->snmp_response));
1182 }else if (availability_method == AVAIL_NONE) {
1183 SPINE_LOG_HIGH(("Host[%i] No Host Availability Method Selected", host->id));
1184 }else{
1185 SPINE_LOG_HIGH(("Host[%i] PING Result: %s", host->id, ping->ping_response));
1186 }
1187 }
1188 }
1189
1190 /* if there is supposed to be an event generated, do it */
1191 if (issue_log_message) {
1192 if (host->status == HOST_DOWN) {
1193 SPINE_LOG(("Host[%i] Hostname[%s] ERROR: HOST EVENT: Host is DOWN Message: %s", host->id, host->hostname, host->status_last_error));
1194 }else{
1195 SPINE_LOG(("Host[%i] Hostname[%s] NOTICE: HOST EVENT: Host Returned from DOWN State", host->id, host->hostname));
1196 }
1197 }
1198 }
1199