| | 68 | static int ping_icmp_is_complete(noit_module_t *self, noit_check_t check) { |
|---|
| | 69 | int i; |
|---|
| | 70 | struct check_info *data; |
|---|
| | 71 | data = (struct check_info *)check->closure; |
|---|
| | 72 | for(i=0; i<data->expected_count; i++) |
|---|
| | 73 | if(data->turnaround[i] == 0.0) { |
|---|
| | 74 | noit_log(nldeb, NULL, "ping_icmp: %s %d is still outstanding.\n", |
|---|
| | 75 | check->target, i); |
|---|
| | 76 | return 0; |
|---|
| | 77 | } |
|---|
| | 78 | return 1; |
|---|
| | 79 | } |
|---|
| | 80 | static void ping_icmp_log_results(noit_module_t *self, noit_check_t check) { |
|---|
| | 81 | struct check_info *data; |
|---|
| | 82 | float avail, min = MAXFLOAT, max = 0.0, avg = 0.0, cnt; |
|---|
| | 83 | int i, points = 0; |
|---|
| | 84 | |
|---|
| | 85 | data = (struct check_info *)check->closure; |
|---|
| | 86 | for(i=0; i<data->expected_count; i++) { |
|---|
| | 87 | if(data->turnaround[i] != 0) { |
|---|
| | 88 | points++; |
|---|
| | 89 | avg += data->turnaround[i]; |
|---|
| | 90 | if(data->turnaround[i] > max) max = data->turnaround[i]; |
|---|
| | 91 | if(data->turnaround[i] < min) min = data->turnaround[i]; |
|---|
| | 92 | } |
|---|
| | 93 | } |
|---|
| | 94 | if(points == 0) { |
|---|
| | 95 | min = 0.0 / 0.0; |
|---|
| | 96 | max = 0.0 / 0.0; |
|---|
| | 97 | } |
|---|
| | 98 | cnt = data->expected_count; |
|---|
| | 99 | avail = (float)points /cnt; |
|---|
| | 100 | avg /= (float)points; |
|---|
| | 101 | noit_log(nldeb, NULL, "ping_icmp(%s) [cnt=%d,avail=%0.0f,min=%0.4f,max=%0.4f,avg=%0.4f]\n", check->target, (int)cnt, 100.0*avail, min, max, avg); |
|---|
| | 102 | } |
|---|
| | 103 | static int ping_icmp_timeout(eventer_t e, int mask, |
|---|
| | 104 | void *closure, struct timeval *now) { |
|---|
| | 105 | struct ping_closure *pcl = (struct ping_closure *)closure; |
|---|
| | 106 | struct check_info *data; |
|---|
| | 107 | ping_icmp_log_results(pcl->self, pcl->check); |
|---|
| | 108 | data = (struct check_info *)pcl->check->closure; |
|---|
| | 109 | data->timeout_event = NULL; |
|---|
| | 110 | free(pcl); |
|---|
| | 111 | return 0; |
|---|
| | 112 | } |
|---|
| | 113 | static int ping_icmp_handler(eventer_t e, int mask, |
|---|
| | 114 | void *closure, struct timeval *now) { |
|---|
| | 115 | noit_module_t *self = (noit_module_t *)closure; |
|---|
| | 116 | struct check_info *data; |
|---|
| | 117 | char packet[1500]; |
|---|
| | 118 | int packet_len = sizeof(packet); |
|---|
| | 119 | union { |
|---|
| | 120 | struct sockaddr_in in4; |
|---|
| | 121 | struct sockaddr_in6 in6; |
|---|
| | 122 | } from; |
|---|
| | 123 | unsigned int from_len; |
|---|
| | 124 | struct ip *ip = (struct ip *)packet;; |
|---|
| | 125 | struct icmp *icp; |
|---|
| | 126 | struct ping_payload *payload; |
|---|
| | 127 | |
|---|
| | 128 | while(1) { |
|---|
| | 129 | float t1, t2; |
|---|
| | 130 | int inlen, iphlen; |
|---|
| | 131 | noit_check_t check; |
|---|
| | 132 | struct timeval tt; |
|---|
| | 133 | |
|---|
| | 134 | from_len = sizeof(from); |
|---|
| | 135 | |
|---|
| | 136 | inlen = recvfrom(e->fd, packet, packet_len, 0, |
|---|
| | 137 | (struct sockaddr *)&from, &from_len); |
|---|
| | 138 | gettimeofday(now, NULL); /* set it, as we care about accuracy */ |
|---|
| | 139 | |
|---|
| | 140 | if(inlen < 0) { |
|---|
| | 141 | if(errno == EAGAIN || errno == EINTR) break; |
|---|
| | 142 | noit_log(nlerr, now, "ping_icmp recvfrom: %s\n", strerror(errno)); |
|---|
| | 143 | break; |
|---|
| | 144 | } |
|---|
| | 145 | iphlen = ip->ip_hl << 2; |
|---|
| | 146 | if((inlen-iphlen) != (sizeof(struct icmp)+sizeof(struct ping_payload))) { |
|---|
| | 147 | noit_log(nlerr, now, |
|---|
| | 148 | "ping_icmp bad size: %d+%d\n", iphlen, inlen-iphlen); |
|---|
| | 149 | continue; |
|---|
| | 150 | } |
|---|
| | 151 | icp = (struct icmp *)(packet + iphlen); |
|---|
| | 152 | payload = (struct ping_payload *)(icp + 1); |
|---|
| | 153 | if(icp->icmp_type != ICMP_ECHOREPLY) { |
|---|
| | 154 | continue; |
|---|
| | 155 | } |
|---|
| | 156 | if(icp->icmp_id != (unsigned short)self) { |
|---|
| | 157 | noit_log(nlerr, now, |
|---|
| | 158 | "ping_icmp not sent from this instance (%d:%d) vs. %d\n", |
|---|
| | 159 | icp->icmp_id, ntohs(icp->icmp_seq), (unsigned short)self); |
|---|
| | 160 | continue; |
|---|
| | 161 | } |
|---|
| | 162 | check = noit_poller_lookup(payload->checkid); |
|---|
| | 163 | if(!check) { |
|---|
| | 164 | char uuid_str[37]; |
|---|
| | 165 | uuid_unparse_lower(payload->checkid, uuid_str); |
|---|
| | 166 | noit_log(nlerr, now, |
|---|
| | 167 | "ping_icmp response for unknown check '%s'\n", uuid_str); |
|---|
| | 168 | continue; |
|---|
| | 169 | } |
|---|
| | 170 | data = (struct check_info *)check->closure; |
|---|
| | 171 | |
|---|
| | 172 | /* If there is no timeout_event, the check must have completed. |
|---|
| | 173 | * We have nothing to do. */ |
|---|
| | 174 | if(!data->timeout_event) continue; |
|---|
| | 175 | |
|---|
| | 176 | /* Sanity check the payload */ |
|---|
| | 177 | if(payload->check_no != data->check_no) continue; |
|---|
| | 178 | if(payload->check_pack_cnt != data->expected_count) continue; |
|---|
| | 179 | if(payload->check_pack_no < 0 || |
|---|
| | 180 | payload->check_pack_no >= data->expected_count) continue; |
|---|
| | 181 | |
|---|
| | 182 | sub_timeval(*now, payload->whence, &tt); |
|---|
| | 183 | t1 = (float)tt.tv_sec + (float)tt.tv_usec / 1000000.0; |
|---|
| | 184 | data->turnaround[payload->check_pack_no] = t1; |
|---|
| | 185 | if(ping_icmp_is_complete(self, check)) { |
|---|
| | 186 | ping_icmp_log_results(self, check); |
|---|
| | 187 | eventer_remove(data->timeout_event); |
|---|
| | 188 | free(data->timeout_event->closure); |
|---|
| | 189 | eventer_free(data->timeout_event); |
|---|
| | 190 | data->timeout_event = NULL; |
|---|
| | 191 | } |
|---|
| | 192 | } |
|---|
| | 193 | return EVENTER_READ; |
|---|
| | 194 | } |
|---|
| | 195 | |
|---|
| | 197 | socklen_t on; |
|---|
| | 198 | struct protoent *proto; |
|---|
| | 199 | ping_icmp_data_t *data; |
|---|
| | 200 | |
|---|
| | 201 | data = malloc(sizeof(*data)); |
|---|
| | 202 | data->ipv4_fd = data->ipv6_fd = -1; |
|---|
| | 203 | |
|---|
| | 204 | if ((proto = getprotobyname("icmp")) == NULL) { |
|---|
| | 205 | noit_log(nlerr, NULL, "Couldn't find 'icmp' protocol\n"); |
|---|
| | 206 | return -1; |
|---|
| | 207 | } |
|---|
| | 208 | |
|---|
| | 209 | data->ipv4_fd = socket(AF_INET, SOCK_RAW, proto->p_proto); |
|---|
| | 210 | if(data->ipv4_fd < 0) { |
|---|
| | 211 | noit_log(nlerr, NULL, "ping_icmp: socket failed: %s\n", |
|---|
| | 212 | strerror(errno)); |
|---|
| | 213 | } |
|---|
| | 214 | else { |
|---|
| | 215 | on = 1; |
|---|
| | 216 | if(ioctl(data->ipv4_fd, FIONBIO, &on)) { |
|---|
| | 217 | close(data->ipv4_fd); |
|---|
| | 218 | data->ipv4_fd = -1; |
|---|
| | 219 | noit_log(nlerr, NULL, |
|---|
| | 220 | "ping_icmp: could not set socket non-blocking: %s\n", |
|---|
| | 221 | strerror(errno)); |
|---|
| | 222 | } |
|---|
| | 223 | } |
|---|
| | 224 | if(data->ipv4_fd >= 0) { |
|---|
| | 225 | eventer_t newe; |
|---|
| | 226 | newe = eventer_alloc(); |
|---|
| | 227 | newe->fd = data->ipv4_fd; |
|---|
| | 228 | newe->mask = EVENTER_READ; |
|---|
| | 229 | newe->callback = ping_icmp_handler; |
|---|
| | 230 | newe->closure = self; |
|---|
| | 231 | eventer_add(newe); |
|---|
| | 232 | } |
|---|
| | 233 | |
|---|
| | 234 | data->ipv6_fd = socket(AF_INET6, SOCK_RAW, proto->p_proto); |
|---|
| | 235 | if(data->ipv6_fd < 0) { |
|---|
| | 236 | noit_log(nlerr, NULL, "ping_icmp: socket failed: %s\n", |
|---|
| | 237 | strerror(errno)); |
|---|
| | 238 | } |
|---|
| | 239 | else { |
|---|
| | 240 | on = 1; |
|---|
| | 241 | if(ioctl(data->ipv6_fd, FIONBIO, &on)) { |
|---|
| | 242 | close(data->ipv6_fd); |
|---|
| | 243 | data->ipv6_fd = -1; |
|---|
| | 244 | noit_log(nlerr, NULL, |
|---|
| | 245 | "ping_icmp: could not set socket non-blocking: %s\n", |
|---|
| | 246 | strerror(errno)); |
|---|
| | 247 | } |
|---|
| | 248 | } |
|---|
| | 249 | if(data->ipv6_fd >= 0) { |
|---|
| | 250 | eventer_t newe; |
|---|
| | 251 | newe = eventer_alloc(); |
|---|
| | 252 | newe->fd = data->ipv6_fd; |
|---|
| | 253 | newe->mask = EVENTER_READ; |
|---|
| | 254 | newe->callback = ping_icmp_handler; |
|---|
| | 255 | newe->closure = self; |
|---|
| | 256 | eventer_add(newe); |
|---|
| | 257 | } |
|---|
| | 258 | |
|---|
| | 259 | noit_module_set_userdata(self, data); |
|---|
| | 260 | return 0; |
|---|
| | 261 | } |
|---|
| | 262 | |
|---|
| | 263 | static int ping_icmp_real_send(eventer_t e, int mask, |
|---|
| | 264 | void *closure, struct timeval *now) { |
|---|
| | 265 | struct ping_closure *pcl = (struct ping_closure *)closure; |
|---|
| | 266 | struct icmp *icp; |
|---|
| | 267 | struct ping_payload *payload; |
|---|
| | 268 | ping_icmp_data_t *data; |
|---|
| | 269 | int i; |
|---|
| | 270 | |
|---|
| | 271 | noit_log(nldeb, NULL, "ping_icmp_real_send(%s)\n", pcl->check->target); |
|---|
| | 272 | data = noit_module_get_userdata(pcl->self); |
|---|
| | 273 | icp = (struct icmp *)pcl->payload; |
|---|
| | 274 | payload = (struct ping_payload *)(icp + 1); |
|---|
| | 275 | gettimeofday(&payload->whence, NULL); /* now isn't accurate enough */ |
|---|
| | 276 | icp->icmp_cksum = in_cksum(pcl->payload, pcl->payload_len); |
|---|
| | 277 | if(pcl->check->target_family == AF_INET) { |
|---|
| | 278 | struct sockaddr_in sin; |
|---|
| | 279 | memset(&sin, 0, sizeof(sin)); |
|---|
| | 280 | sin.sin_family = AF_INET; |
|---|
| | 281 | memcpy(&sin.sin_addr, |
|---|
| | 282 | &pcl->check->target_addr.addr, sizeof(sin.sin_addr)); |
|---|
| | 283 | i = sendto(data->ipv4_fd, |
|---|
| | 284 | pcl->payload, pcl->payload_len, 0, |
|---|
| | 285 | (struct sockaddr *)&sin, sizeof(sin)); |
|---|
| | 286 | } |
|---|
| | 287 | else { |
|---|
| | 288 | struct sockaddr_in6 sin; |
|---|
| | 289 | memset(&sin, 0, sizeof(sin)); |
|---|
| | 290 | sin.sin6_family = AF_INET6; |
|---|
| | 291 | memcpy(&sin.sin6_addr, |
|---|
| | 292 | &pcl->check->target_addr.addr6, sizeof(sin.sin6_addr)); |
|---|
| | 293 | i = sendto(data->ipv6_fd, |
|---|
| | 294 | pcl->payload, pcl->payload_len, 0, |
|---|
| | 295 | (struct sockaddr *)&sin, sizeof(sin)); |
|---|
| | 296 | } |
|---|
| | 297 | if(i != pcl->payload_len) { |
|---|
| | 298 | noit_log(nlerr, now, "Error sending ICMP packet to %s: %s\n", |
|---|
| | 299 | pcl->check->target, strerror(errno)); |
|---|
| | 300 | } |
|---|
| | 301 | free(pcl->payload); |
|---|
| | 302 | free(pcl); |
|---|
| | 303 | return 0; |
|---|
| | 304 | } |
|---|
| | 305 | static int ping_icmp_send(noit_module_t *self, noit_check_t check, |
|---|
| | 306 | int interval, int count) { |
|---|
| | 307 | struct timeval when, p_int; |
|---|
| | 308 | struct icmp *icp; |
|---|
| | 309 | struct ping_payload *payload; |
|---|
| | 310 | struct ping_closure *pcl; |
|---|
| | 311 | struct check_info *ci = (struct check_info *)check->closure; |
|---|
| | 312 | int packet_len, i; |
|---|
| | 313 | eventer_t newe; |
|---|
| | 314 | |
|---|
| | 315 | noit_log(nldeb, NULL, "ping_icmp_send(%p,%s,%d,%d)\n", |
|---|
| | 316 | self, check->target, interval, count); |
|---|
| | 317 | |
|---|
| | 318 | /* remove a timeout if we still have one -- we should unless someone |
|---|
| | 319 | * has set a lower timeout than the period. |
|---|
| | 320 | */ |
|---|
| | 321 | if(ci->timeout_event) { |
|---|
| | 322 | eventer_remove(ci->timeout_event); |
|---|
| | 323 | free(ci->timeout_event->closure); |
|---|
| | 324 | eventer_free(ci->timeout_event); |
|---|
| | 325 | ci->timeout_event = NULL; |
|---|
| | 326 | } |
|---|
| | 327 | |
|---|
| | 328 | gettimeofday(&when, NULL); |
|---|
| | 329 | memcpy(&check->last_fire_time, &when, sizeof(when)); |
|---|
| | 330 | |
|---|
| | 331 | /* Setup some stuff used in the loop */ |
|---|
| | 332 | p_int.tv_sec = interval / 1000; |
|---|
| | 333 | p_int.tv_usec = (interval % 1000) * 1000; |
|---|
| | 334 | packet_len = sizeof(*icp) + sizeof(*payload); |
|---|
| | 335 | |
|---|
| | 336 | /* Prep holding spots for return info */ |
|---|
| | 337 | ci->expected_count = count; |
|---|
| | 338 | if(ci->turnaround) free(ci->turnaround); |
|---|
| | 339 | ci->turnaround = calloc(count, sizeof(*ci->turnaround)); |
|---|
| | 340 | |
|---|
| | 341 | ++ci->check_no; |
|---|
| | 342 | for(i=0; i<count; i++) { |
|---|
| | 343 | newe = eventer_alloc(); |
|---|
| | 344 | newe->callback = ping_icmp_real_send; |
|---|
| | 345 | newe->mask = EVENTER_TIMER; |
|---|
| | 346 | memcpy(&newe->whence, &when, sizeof(when)); |
|---|
| | 347 | add_timeval(when, p_int, &when); /* Next one is a bit later */ |
|---|
| | 348 | |
|---|
| | 349 | icp = malloc(packet_len); |
|---|
| | 350 | payload = (struct ping_payload *)(icp + 1); |
|---|
| | 351 | |
|---|
| | 352 | icp->icmp_type = ICMP_ECHO; |
|---|
| | 353 | icp->icmp_code = 0; |
|---|
| | 354 | icp->icmp_cksum = 0; |
|---|
| | 355 | icp->icmp_seq = htons(ci->seq++); |
|---|
| | 356 | icp->icmp_id = (unsigned short)self; |
|---|
| | 357 | |
|---|
| | 358 | uuid_copy(payload->checkid, check->checkid); |
|---|
| | 359 | payload->check_no = ci->check_no; |
|---|
| | 360 | payload->check_pack_no = i; |
|---|
| | 361 | payload->check_pack_cnt = count; |
|---|
| | 362 | |
|---|
| | 363 | pcl = calloc(1, sizeof(*pcl)); |
|---|
| | 364 | pcl->self = self; |
|---|
| | 365 | pcl->check = check; |
|---|
| | 366 | pcl->payload = icp; |
|---|
| | 367 | pcl->payload_len = packet_len; |
|---|
| | 368 | |
|---|
| | 369 | newe->closure = pcl; |
|---|
| | 370 | eventer_add(newe); |
|---|
| | 371 | } |
|---|
| | 372 | newe = eventer_alloc(); |
|---|
| | 373 | newe->mask = EVENTER_TIMER; |
|---|
| | 374 | gettimeofday(&when, NULL); |
|---|
| | 375 | p_int.tv_sec = check->timeout / 1000; |
|---|
| | 376 | p_int.tv_usec = (check->timeout % 1000) * 1000; |
|---|
| | 377 | add_timeval(when, p_int, &newe->whence); |
|---|
| | 378 | pcl = calloc(1, sizeof(*pcl)); |
|---|
| | 379 | pcl->self = self; |
|---|
| | 380 | pcl->check = check; |
|---|
| | 381 | newe->closure = pcl; |
|---|
| | 382 | newe->callback = ping_icmp_timeout; |
|---|
| | 383 | eventer_add(newe); |
|---|
| | 384 | ci->timeout_event = newe; |
|---|
| | 385 | |
|---|
| | 386 | return 0; |
|---|
| | 387 | } |
|---|
| | 388 | static int ping_icmp_schedule_next(noit_module_t *self, |
|---|
| | 389 | eventer_t e, noit_check_t check, |
|---|
| | 390 | struct timeval *now) { |
|---|
| | 391 | eventer_t newe; |
|---|
| | 392 | struct timeval last_check = { 0L, 0L }; |
|---|
| | 393 | struct timeval period, earliest; |
|---|
| | 394 | struct ping_closure *pcl; |
|---|
| | 395 | |
|---|
| | 396 | /* If we have an event, we know when we intended it to fire. This means |
|---|
| | 397 | * we should schedule that point + period. |
|---|
| | 398 | */ |
|---|
| | 399 | if(now) |
|---|
| | 400 | memcpy(&earliest, now, sizeof(earliest)); |
|---|
| | 401 | else |
|---|
| | 402 | gettimeofday(&earliest, NULL); |
|---|
| | 403 | if(e) memcpy(&last_check, &e->whence, sizeof(last_check)); |
|---|
| | 404 | period.tv_sec = check->period / 1000; |
|---|
| | 405 | period.tv_usec = (check->period % 1000) * 1000; |
|---|
| | 406 | |
|---|
| | 407 | newe = eventer_alloc(); |
|---|
| | 408 | memcpy(&newe->whence, &last_check, sizeof(last_check)); |
|---|
| | 409 | add_timeval(newe->whence, period, &newe->whence); |
|---|
| | 410 | if(compare_timeval(newe->whence, earliest) < 0) |
|---|
| | 411 | memcpy(&newe->whence, &earliest, sizeof(earliest)); |
|---|
| | 412 | newe->mask = EVENTER_TIMER; |
|---|
| | 413 | newe->callback = ping_icmp_recur_handler; |
|---|
| | 414 | pcl = calloc(1, sizeof(*pcl)); |
|---|
| | 415 | pcl->self = self; |
|---|
| | 416 | pcl->check = check; |
|---|
| | 417 | newe->closure = pcl; |
|---|
| | 418 | |
|---|
| | 419 | eventer_add(newe); |
|---|
| | 420 | check->fire_event = newe; |
|---|
| | 421 | return 0; |
|---|
| | 422 | } |
|---|
| | 423 | static int ping_icmp_recur_handler(eventer_t e, int mask, void *closure, |
|---|
| | 424 | struct timeval *now) { |
|---|
| | 425 | struct ping_closure *cl = (struct ping_closure *)closure; |
|---|
| | 426 | ping_icmp_schedule_next(cl->self, e, cl->check, now); |
|---|
| | 427 | ping_icmp_send(cl->self, cl->check, PING_INTERVAL, PING_COUNT); |
|---|
| | 428 | free(cl); |
|---|