1
2 //
3 // Copyright (c) 2015-2017, Denny Page
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions
8 // are met:
9 //
10 // 1. Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 //
13 // 2. Redistributions in binary form must reproduce the above copyright
14 // notice, this list of conditions and the following disclaimer in the
15 // documentation and/or other materials provided with the distribution.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
23 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30
31 // Silly that this is required for accept4 on Linux
32 #define _GNU_SOURCE
33
34
35 #include <stdio.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <stdint.h>
41 #include <unistd.h>
42 #include <time.h>
43 #include <fcntl.h>
44 #include <signal.h>
45
46 #include <netdb.h>
47 #include <sys/socket.h>
48 #include <net/if.h>
49 #include <sys/un.h>
50 #include <sys/stat.h>
51 #include <sys/file.h>
52 #include <netinet/in.h>
53 #include <netinet/ip.h>
54 #include <netinet/ip_icmp.h>
55 #include <netinet/icmp6.h>
56 #include <arpa/inet.h>
57
58 #include <pthread.h>
59 #include <syslog.h>
60
61
62 // Who we are
63 static const char * progname;
64
65 // Process ID file
66 static unsigned int foreground = 0;
67 static const char * pidfile_name = NULL;
68
69 // Flags
70 static unsigned int flag_rewind = 0;
71 static unsigned int flag_syslog = 0;
72 static unsigned int flag_priority = 0;
73
74 // String representation of target
75 #define ADDR_STR_MAX (INET6_ADDRSTRLEN + IF_NAMESIZE + 1)
76 static char dest_str[ADDR_STR_MAX];
77
78 // Time period over which we are averaging results in ms
79 static unsigned long time_period_msec = 60000;
80
81 // Interval between sends in ms
82 static unsigned long send_interval_msec = 500;
83
84 // Interval before a sequence is initially treated as lost
85 // Input from command line in ms and used in us
86 static unsigned long loss_interval_msec = 0;
87 static unsigned long loss_interval_usec = 0;
88
89 // Interval between reports in ms
90 static unsigned long report_interval_msec = 1000;
91
92 // Interval between alert checks in ms
93 static unsigned long alert_interval_msec = 1000;
94
95 // Threshold for triggering alarms based on latency
96 // Input from command line in ms and used in us
97 static unsigned long latency_alarm_threshold_msec = 0;
98 static unsigned long latency_alarm_threshold_usec = 0;
99
100 // Threshold for triggering alarms based on loss percentage
101 static unsigned long loss_alarm_threshold_percent = 0;
102
103 // Command to invoke for alerts
104 static char * alert_cmd = NULL;
105 static size_t alert_cmd_offset;
106
107 // Number of periods to wait to declare an alarm as cleared
108 #define ALARM_DECAY_PERIODS 10
109
110 // Report file
111 static const char * report_name = NULL;
112 static int report_fd;
113
114 // Unix socket
115 static const char * usocket_name = NULL;
116 static int usocket_fd;
117
118 static char identifier[64] = "\0";
119
120 // Length of maximum output (dest_str alarm_flag average_latency_usec latency_deviation average_loss_percent)
121 #define OUTPUT_MAX (sizeof(identifier) + sizeof(dest_str) + sizeof(" 1 999999999999 999999999999 100\0"))
122
123
124 // Main ping status array
125 typedef struct
126 {
127 enum
128 {
129 PACKET_STATUS_EMPTY = 0,
130 PACKET_STATUS_SENT = 1,
131 PACKET_STATUS_RECEIVED = 2
132 } status;
133
134 struct timespec time_sent;
135 unsigned long latency_usec;
136 } ping_entry_t;
137
138 static ping_entry_t * array;
139 static unsigned int array_size;
140 static unsigned int next_slot = 0;
141
142
143 // Sockets used to send and receive
144 static int send_sock;
145 static int recv_sock;
146
147 // IPv4 / IPv6 parameters
148 static uint16_t af_family = AF_INET; // IPv6: AF_INET6
149 static uint8_t echo_request_type = ICMP_ECHO; // IPv6: ICMP6_ECHO_REQUEST
150 static uint8_t echo_reply_type = ICMP_ECHOREPLY; // IPv6: ICMP6_ECHO_REPLY
151 static int ip_proto = IPPROTO_ICMP; // IPv6: IPPROTO_ICMPV6
152
153 // Destination address
154 static struct sockaddr_storage dest_addr;
155 static socklen_t dest_addr_len;
156
157 // Source (bind) address
158 static struct sockaddr_storage bind_addr;
159 static socklen_t bind_addr_len = 0;
160
161 // ICMP echo request/reply header
162 //
163 // The physical layout of the ICMP is the same between IPv4 and IPv6 so we define our
164 // own type for convenience
165 typedef struct
166 {
167 uint8_t type;
168 uint8_t code;
169 uint16_t cksum;
170 uint16_t id;
171 uint16_t sequence;
172 } icmphdr_t;
173
174 // Echo request/reply packet buffers
175 #define IPV4_ICMP_DATA_MAX (IP_MAXPACKET - sizeof(struct ip) - sizeof(icmphdr_t))
176 #define IPV6_ICMP_DATA_MAX (IP_MAXPACKET - sizeof(icmphdr_t))
177 #define PACKET_BUFLEN (IP_MAXPACKET + 256)
178
179 static unsigned long echo_data_len = 0;
180 static unsigned int echo_request_len = sizeof(icmphdr_t);
181 static unsigned int echo_reply_len = IP_MAXPACKET;
182 static icmphdr_t * echo_request;
183 static void * echo_reply;
184
185 // Echo id and Sequence information
186 static uint16_t echo_id;
187 static uint16_t next_sequence = 0;
188 static uint16_t sequence_limit;
189
190 // Receive thread ready
191 static unsigned int recv_ready = 0;
192
193 //
194 // Termination handler
195 //
196 __attribute__ ((noreturn))
197 static void
term_handler(void)198 term_handler(void)
199 {
200 // NB: This function may be simultaneously invoked by multiple threads
201 if (usocket_name)
202 {
203 (void) unlink(usocket_name);
204 }
205 if (pidfile_name)
206 {
207 (void) unlink(pidfile_name);
208 }
209 exit(0);
210 }
211
212
213 //
214 // Log for abnormal events
215 //
216 __attribute__ ((format (printf, 1, 2)))
217 static void
logger(const char * format,...)218 logger(
219 const char * format,
220 ...)
221 {
222 va_list args;
223
224 va_start(args, format);
225 if (flag_syslog)
226 {
227 vsyslog(LOG_WARNING, format, args);
228 }
229 else
230 {
231 vfprintf(stderr, format, args);
232 }
233 va_end(args);
234 }
235
236
237 //
238 // Compute checksum for ICMP
239 //
240 static uint16_t
cksum(const uint16_t * p,int len)241 cksum(
242 const uint16_t * p,
243 int len)
244 {
245 uint32_t sum = 0;
246
247 while (len > 1)
248 {
249 sum += *p++;
250 len -= sizeof(*p);
251 }
252
253 if (len == 1)
254 {
255 sum += (uint16_t) *((const uint8_t *) p);
256 }
257
258 sum = (sum >> 16) + (sum & 0xFFFF);
259 sum += (sum >> 16);
260
261 return (uint16_t) ~sum;
262 }
263
264
265 //
266 // sqrt function for standard deviation
267 //
268 static unsigned long
llsqrt(unsigned long long x)269 llsqrt(
270 unsigned long long x)
271 {
272 unsigned long long prev;
273 unsigned long long s;
274
275 s = x;
276 if (s)
277 {
278 prev = ~((unsigned long long) 1 << 63);
279
280 while (s < prev)
281 {
282 prev = s;
283 s = (s + (x / s)) / 2;
284 }
285 }
286
287 return (unsigned long) s;
288 }
289
290
291 //
292 // Compute delta between old time and new time in microseconds
293 //
294 static unsigned long
ts_elapsed_usec(const struct timespec * old,const struct timespec * new)295 ts_elapsed_usec(
296 const struct timespec * old,
297 const struct timespec * new)
298 {
299 long r_usec;
300
301 // Note that we are using monotonic clock and time cannot run backwards
302 if (new->tv_nsec >= old->tv_nsec)
303 {
304 r_usec = (new->tv_sec - old->tv_sec) * 1000000 + (new->tv_nsec - old->tv_nsec) / 1000;
305 }
306 else
307 {
308 r_usec = (new->tv_sec - old->tv_sec - 1) * 1000000 + (1000000000 + new->tv_nsec - old->tv_nsec) / 1000;
309 }
310
311 return (unsigned long) r_usec;
312 }
313
314
315 //
316 // Send thread
317 //
318 __attribute__ ((noreturn))
319 static void *
send_thread(void * arg)320 send_thread(
321 __attribute__ ((unused))
322 void * arg)
323 {
324 struct timespec sleeptime;
325 ssize_t len;
326 int r;
327
328 // Set up our echo request packet
329 memset(echo_request, 0, echo_request_len);
330 echo_request->type = echo_request_type;
331 echo_request->code = 0;
332 echo_request->id = echo_id;
333
334 // Give the recv thread a moment to initialize
335 sleeptime.tv_sec = 0;
336 sleeptime.tv_nsec = 10000; // 10us
337 do {
338 r = nanosleep(&sleeptime, NULL);
339 if (r == -1)
340 {
341 logger("nanosleep error in send thread waiting for recv thread: %d\n", errno);
342 }
343 } while (recv_ready == 0);
344
345 // Set up the timespec for nanosleep
346 sleeptime.tv_sec = send_interval_msec / 1000;
347 sleeptime.tv_nsec = (send_interval_msec % 1000) * 1000000;
348
349 while (1)
350 {
351 // Set sequence number and checksum
352 echo_request->sequence = htons(next_sequence);
353 echo_request->cksum = 0;
354 echo_request->cksum = cksum((uint16_t *) echo_request, sizeof(icmphdr_t));
355
356 array[next_slot].status = PACKET_STATUS_EMPTY;
357 sched_yield();
358
359 clock_gettime(CLOCK_MONOTONIC, &array[next_slot].time_sent);
360 array[next_slot].status = PACKET_STATUS_SENT;
361 len = sendto(send_sock, echo_request, echo_request_len, 0, (struct sockaddr *) &dest_addr, dest_addr_len);
362 if (len == -1)
363 {
364 logger("%s%s: sendto error: %d\n", identifier, dest_str, errno);
365 }
366
367 next_slot = (next_slot + 1) % array_size;
368 next_sequence = (next_sequence + 1) % sequence_limit;
369
370 r = nanosleep(&sleeptime, NULL);
371 if (r == -1)
372 {
373 logger("nanosleep error in send thread: %d\n", errno);
374 }
375 }
376 }
377
378
379 //
380 // Receive thread
381 //
382 __attribute__ ((noreturn))
383 static void *
recv_thread(void * arg)384 recv_thread(
385 __attribute__ ((unused))
386 void * arg)
387 {
388 struct sockaddr_storage src_addr;
389 socklen_t src_addr_len;
390 ssize_t len;
391 icmphdr_t * icmp;
392 struct timespec now;
393 unsigned int array_slot;
394
395 // Thread startup complete
396 recv_ready = 1;
397
398 while (1)
399 {
400 src_addr_len = sizeof(src_addr);
401 len = recvfrom(recv_sock, echo_reply, echo_reply_len, 0, (struct sockaddr *) &src_addr, &src_addr_len);
402 if (len == -1)
403 {
404 logger("%s%s: recvfrom error: %d\n", identifier, dest_str, errno);
405 continue;
406 }
407 clock_gettime(CLOCK_MONOTONIC, &now);
408
409 if (af_family == AF_INET)
410 {
411 struct ip * ip;
412 size_t ip_len;
413
414 // With IPv4, we get the entire IP packet
415 if (len < (ssize_t) sizeof(struct ip))
416 {
417 logger("%s%s: received packet too small for IP header\n", identifier, dest_str);
418 continue;
419 }
420 ip = echo_reply;
421 ip_len = (size_t) ip->ip_hl << 2;
422
423 icmp = (void *) ((char *) ip + ip_len);
424 len -= ip_len;
425 }
426 else
427 {
428 // With IPv6, we just get the ICMP payload
429 icmp = echo_reply;
430 }
431
432 // This should never happen
433 if (len < (ssize_t) sizeof(icmphdr_t))
434 {
435 logger("%s%s: received packet too small for ICMP header\n", identifier, dest_str);
436 continue;
437 }
438
439 // If it's not an echo reply for us, skip the packet
440 if (icmp->type != echo_reply_type || icmp->id != echo_id)
441 {
442 continue;
443 }
444
445 array_slot = ntohs(icmp->sequence) % array_size;
446 if (array[array_slot].status == PACKET_STATUS_RECEIVED)
447 {
448 logger("%s%s: duplicate echo reply received\n", identifier, dest_str);
449 continue;
450 }
451
452 array[array_slot].latency_usec = ts_elapsed_usec(&array[array_slot].time_sent, &now);
453 array[array_slot].status = PACKET_STATUS_RECEIVED;
454 }
455 }
456
457
458 //
459 // Generate a report
460 //
461 static void
report(unsigned long * average_latency_usec,unsigned long * latency_deviation,unsigned long * average_loss_percent)462 report(
463 unsigned long *average_latency_usec,
464 unsigned long *latency_deviation,
465 unsigned long *average_loss_percent)
466 {
467 struct timespec now;
468 unsigned long packets_received = 0;
469 unsigned long packets_lost = 0;
470 unsigned long latency_usec = 0;
471 unsigned long total_latency_usec = 0;
472 unsigned long long total_latency_usec2 = 0;
473 unsigned int slot;
474 unsigned int i;
475
476 clock_gettime(CLOCK_MONOTONIC, &now);
477
478 slot = next_slot;
479 for (i = 0; i < array_size; i++)
480 {
481 if (array[slot].status == PACKET_STATUS_RECEIVED)
482 {
483 packets_received++;
484 latency_usec = array[slot].latency_usec;
485 total_latency_usec += latency_usec;
486 total_latency_usec2 += (unsigned long long) latency_usec * latency_usec;
487 }
488 else if (array[slot].status == PACKET_STATUS_SENT &&
489 ts_elapsed_usec(&array[slot].time_sent, &now) > loss_interval_usec)
490 {
491 packets_lost++;
492 }
493
494 slot = (slot + 1) % array_size;
495 }
496
497 if (packets_received)
498 {
499 unsigned long avg = total_latency_usec / packets_received;
500 unsigned long long avg2 = total_latency_usec2 / packets_received;
501
502 // stddev = sqrt((sum(rtt^2) / packets) - (sum(rtt) / packets)^2)
503 *average_latency_usec = avg;
504 *latency_deviation = llsqrt(avg2 - ((unsigned long long) avg * avg));
505 }
506 else
507 {
508 *average_latency_usec = 0;
509 *latency_deviation = 0;
510 }
511
512 if (packets_lost)
513 {
514 *average_loss_percent = packets_lost * 100 / (packets_received + packets_lost);
515 }
516 else
517 {
518 *average_loss_percent = 0;
519 }
520 }
521
522
523 //
524 // Report thread
525 //
526 __attribute__ ((noreturn))
527 static void *
report_thread(void * arg)528 report_thread(
529 __attribute__ ((unused))
530 void * arg)
531 {
532 char buf[OUTPUT_MAX];
533 struct timespec sleeptime;
534 unsigned long average_latency_usec;
535 unsigned long latency_deviation;
536 unsigned long average_loss_percent;
537 ssize_t len;
538 ssize_t rs;
539 int r;
540
541 // Set up the timespec for nanosleep
542 sleeptime.tv_sec = report_interval_msec / 1000;
543 sleeptime.tv_nsec = (report_interval_msec % 1000) * 1000000;
544
545 while (1)
546 {
547 r = nanosleep(&sleeptime, NULL);
548 if (r == -1)
549 {
550 logger("nanosleep error in report thread: %d\n", errno);
551 }
552
553 report(&average_latency_usec, &latency_deviation, &average_loss_percent);
554
555 len = snprintf(buf, sizeof(buf), "%s%lu %lu %lu\n", identifier, average_latency_usec, latency_deviation, average_loss_percent);
556 if (len < 0 || (size_t) len > sizeof(buf))
557 {
558 logger("error formatting output in report thread\n");
559 }
560
561 rs = write(report_fd, buf, (size_t) len);
562 if (rs == -1)
563 {
564 logger("write error in report thread: %d\n", errno);
565 }
566 else if (rs != len)
567 {
568 logger("short write in report thread: %zd/%zd\n", rs, len);
569 }
570
571 if (flag_rewind)
572 {
573 (void) ftruncate(report_fd, len);
574 (void) lseek(report_fd, SEEK_SET, 0);
575 }
576 }
577 }
578
579
580 //
581 // Alert thread
582 //
583 __attribute__ ((noreturn))
584 static void *
alert_thread(void * arg)585 alert_thread(
586 __attribute__ ((unused))
587 void * arg)
588 {
589 struct timespec sleeptime;
590 unsigned long average_latency_usec;
591 unsigned long latency_deviation;
592 unsigned long average_loss_percent;
593 unsigned int latency_alarm_decay = 0;
594 unsigned int loss_alarm_decay = 0;
595 unsigned int alert = 0;
596 unsigned int alarm_on;
597 int r;
598
599 // Set up the timespec for nanosleep
600 sleeptime.tv_sec = alert_interval_msec / 1000;
601 sleeptime.tv_nsec = (alert_interval_msec % 1000) * 1000000;
602
603 while (1)
604 {
605 r = nanosleep(&sleeptime, NULL);
606 if (r == -1)
607 {
608 logger("nanosleep error in alert thread: %d\n", errno);
609 }
610
611 report(&average_latency_usec, &latency_deviation, &average_loss_percent);
612
613 if (latency_alarm_threshold_usec)
614 {
615 if (average_latency_usec > latency_alarm_threshold_usec)
616 {
617 if (latency_alarm_decay == 0)
618 {
619 alert = 1;
620 }
621
622 latency_alarm_decay = ALARM_DECAY_PERIODS;
623 }
624 else if (latency_alarm_decay)
625 {
626 latency_alarm_decay--;
627 if (latency_alarm_decay == 0)
628 {
629 alert = 1;
630 }
631 }
632 }
633
634 if (loss_alarm_threshold_percent)
635 {
636 if (average_loss_percent > loss_alarm_threshold_percent)
637 {
638 if (loss_alarm_decay == 0)
639 {
640 alert = 1;
641 }
642
643 loss_alarm_decay = ALARM_DECAY_PERIODS;
644 }
645 else if (loss_alarm_decay)
646 {
647 loss_alarm_decay--;
648 if (loss_alarm_decay == 0)
649 {
650 alert = 1;
651 }
652 }
653 }
654
655 if (alert)
656 {
657 alert = 0;
658
659 alarm_on = latency_alarm_decay || loss_alarm_decay;
660 logger("%s%s: %s latency %luus stddev %luus loss %lu%%\n", identifier, dest_str, alarm_on ? "Alarm" : "Clear", average_latency_usec, latency_deviation, average_loss_percent);
661
662 if (alert_cmd)
663 {
664 r = snprintf(alert_cmd + alert_cmd_offset, OUTPUT_MAX, " %s%s %u %lu %lu %lu", identifier, dest_str, alarm_on, average_latency_usec, latency_deviation, average_loss_percent);
665 if (r < 0 || (size_t) r >= OUTPUT_MAX)
666 {
667 logger("error formatting command in alert thread\n");
668 continue;
669 }
670
671 // Note that system waits for the alert command to finish before returning
672 r = system(alert_cmd);
673 if (r == -1)
674 {
675 logger("error executing command in alert thread\n");
676 }
677 }
678 }
679 }
680 }
681
682 //
683 // Unix socket thread
684 //
685 __attribute__ ((noreturn))
686 static void *
usocket_thread(void * arg)687 usocket_thread(
688 __attribute__ ((unused))
689 void * arg)
690 {
691 char buf[OUTPUT_MAX];
692 unsigned long average_latency_usec;
693 unsigned long latency_deviation;
694 unsigned long average_loss_percent;
695 int sock_fd;
696 ssize_t len;
697 ssize_t rs;
698 int r;
699
700 while (1)
701 {
702 #if defined(DISABLE_ACCEPT4)
703 // Legacy
704 sock_fd = accept(usocket_fd, NULL, NULL);
705 (void) fcntl(sock_fd, F_SETFL, FD_CLOEXEC);
706 (void) fcntl(sock_fd, F_SETFL, fcntl(sock_fd, F_GETFL, 0) | O_NONBLOCK);
707 #else
708 sock_fd = accept4(usocket_fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
709 #endif
710
711 report(&average_latency_usec, &latency_deviation, &average_loss_percent);
712
713 len = snprintf(buf, sizeof(buf), "%s%lu %lu %lu\n", identifier, average_latency_usec, latency_deviation, average_loss_percent);
714 if (len < 0 || (size_t) len > sizeof(buf))
715 {
716 logger("error formatting output in usocket thread\n");
717 }
718
719 rs = write(sock_fd, buf, (size_t) len);
720 if (rs == -1)
721 {
722 logger("write error in usocket thread: %d\n", errno);
723 }
724 else if (rs != len)
725 {
726 logger("short write in usocket thread: %zd/%zd\n", rs, len);
727 }
728
729 r = close(sock_fd);
730 if (r == -1)
731 {
732 logger("close error in usocket thread: %d\n", errno);
733 }
734 }
735 }
736
737
738
739 //
740 // Decode a time argument
741 //
742 static int
get_time_arg_msec(const char * arg,unsigned long * value)743 get_time_arg_msec(
744 const char * arg,
745 unsigned long * value)
746 {
747 long t;
748 char * suffix;
749
750 t = strtol(arg, &suffix, 10);
751 if (*suffix == 'm')
752 {
753 // Milliseconds
754 suffix++;
755 }
756 else if (*suffix == 's')
757 {
758 // Seconds
759 t *= 1000;
760 suffix++;
761 }
762
763 // Invalid specification?
764 if (t < 0 || *suffix != 0)
765 {
766 return 1;
767 }
768
769 *value = (unsigned long) t;
770 return 0;
771 }
772
773
774 //
775 // Decode a percent argument
776 //
777 static int
get_percent_arg(const char * arg,unsigned long * value)778 get_percent_arg(
779 const char * arg,
780 unsigned long * value)
781 {
782 long t;
783 char * suffix;
784
785 t = strtol(arg, &suffix, 10);
786 if (*suffix == '%')
787 {
788 suffix++;
789 }
790
791 // Invalid specification?
792 if (t < 0 || t > 100 || *suffix != 0)
793 {
794 return 1;
795 }
796
797 *value = (unsigned long) t;
798 return 0;
799 }
800
801
802 //
803 // Decode a byte length argument
804 //
805 static int
get_length_arg(const char * arg,unsigned long * value)806 get_length_arg(
807 const char * arg,
808 unsigned long * value)
809 {
810 long t;
811 char * suffix;
812
813 t = strtol(arg, &suffix, 10);
814 if (*suffix == 'b')
815 {
816 // Bytes
817 suffix++;
818 }
819 else if (*suffix == 'k')
820 {
821 // Kilobytes
822 t *= 1024;
823 suffix++;
824 }
825
826 // Invalid specification?
827 if (t < 0 || *suffix != 0)
828 {
829 return 1;
830 }
831
832 *value = (unsigned long) t;
833 return 0;
834 }
835
836
837 //
838 // Output usage
839 //
840 static void
usage(void)841 usage(void)
842 {
843 fprintf(stderr, "Usage:\n");
844 fprintf(stderr, " %s [-f] [-R] [-S] [-P] [-B bind_addr] [-s send_interval] [-l loss_interval] [-t time_period] [-r report_interval] [-d data_length] [-o output_file] [-A alert_interval] [-D latency_alarm] [-L loss_alarm] [-C alert_cmd] [-i identifier] [-u usocket] [-p pidfile] dest_addr\n\n", progname);
845 fprintf(stderr, " options:\n");
846 fprintf(stderr, " -f run in foreground\n");
847 fprintf(stderr, " -R rewind output file between reports\n");
848 fprintf(stderr, " -S log warnings via syslog\n");
849 fprintf(stderr, " -P priority scheduling for receive thread (requires root)\n");
850 fprintf(stderr, " -B bind (source) address\n");
851 fprintf(stderr, " -s time interval between echo requests (default 500ms)\n");
852 fprintf(stderr, " -l time interval before packets are treated as lost (default 4x send interval)\n");
853 fprintf(stderr, " -t time period over which results are averaged (default 60s)\n");
854 fprintf(stderr, " -r time interval between reports (default 1s)\n");
855 fprintf(stderr, " -d data length (default 0)\n");
856 fprintf(stderr, " -o output file for reports (default stdout)\n");
857 fprintf(stderr, " -A time interval between alerts (default 1s)\n");
858 fprintf(stderr, " -D time threshold for latency alarm (default none)\n");
859 fprintf(stderr, " -L percent threshold for loss alarm (default none)\n");
860 fprintf(stderr, " -C optional command to be invoked via system() for alerts\n");
861 fprintf(stderr, " -i identifier text to include in output\n");
862 fprintf(stderr, " -u unix socket name for polling\n");
863 fprintf(stderr, " -p process id file name\n\n");
864 fprintf(stderr, " notes:\n");
865 fprintf(stderr, " IP addresses can be in either IPv4 or IPv6 format\n\n");
866 fprintf(stderr, " time values can be expressed with a suffix of 'm' (milliseconds) or 's' (seconds)\n");
867 fprintf(stderr, " if no suffix is specified, milliseconds is the default\n\n");
868 fprintf(stderr, " the output format is \"latency_avg latency_stddev loss_pct\"\n");
869 fprintf(stderr, " latency values are output in microseconds\n");
870 fprintf(stderr, " loss percentage is reported in whole numbers of 0-100\n");
871 fprintf(stderr, " resolution of loss calculation is: 100 * send_interval / (time_period - loss_interval)\n\n");
872 fprintf(stderr, " the alert_cmd is invoked as \"alert_cmd dest_addr alarm_flag latency_avg loss_avg\"\n");
873 fprintf(stderr, " alarm_flag is set to 1 if either latency or loss is in alarm state\n");
874 fprintf(stderr, " alarm_flag will return to 0 when both have have cleared alarm state\n\n");
875 }
876
877
878 //
879 // Fatal error
880 //
881 __attribute__ ((noreturn, format (printf, 1, 2)))
882 static void
fatal(const char * format,...)883 fatal(
884 const char * format,
885 ...)
886 {
887 va_list args;
888
889 va_start(args, format);
890 vfprintf(stderr, format, args);
891 va_end(args);
892
893 exit(EXIT_FAILURE);
894 }
895
896
897 //
898 // Parse command line arguments
899 //
900 static void
parse_args(int argc,char * const argv[])901 parse_args(
902 int argc,
903 char * const argv[])
904 {
905 struct addrinfo hint;
906 struct addrinfo * addr_info;
907 const char * dest_arg;
908 const char * bind_arg = NULL;
909 size_t len;
910 int opt;
911 int r;
912
913 progname = argv[0];
914
915 while((opt = getopt(argc, argv, "fRSPB:s:l:t:r:d:o:A:D:L:C:i:u:p:")) != -1)
916 {
917 switch (opt)
918 {
919 case 'f':
920 foreground = 1;
921 break;
922
923 case 'R':
924 flag_rewind = 1;
925 break;
926
927 case 'S':
928 flag_syslog = 1;
929 break;
930
931 case 'P':
932 flag_priority = 1;
933 break;
934
935 case 'B':
936 bind_arg = optarg;
937 break;
938
939 case 's':
940 r = get_time_arg_msec(optarg, &send_interval_msec);
941 if (r || send_interval_msec == 0)
942 {
943 fatal("invalid send interval %s\n", optarg);
944 }
945 break;
946
947 case 'l':
948 r = get_time_arg_msec(optarg, &loss_interval_msec);
949 if (r || loss_interval_msec == 0)
950 {
951 fatal("invalid loss interval %s\n", optarg);
952 }
953 break;
954
955 case 't':
956 r = get_time_arg_msec(optarg, &time_period_msec);
957 if (r || time_period_msec == 0)
958 {
959 fatal("invalid averaging time period %s\n", optarg);
960 }
961 break;
962
963 case 'r':
964 r = get_time_arg_msec(optarg, &report_interval_msec);
965 if (r)
966 {
967 fatal("invalid report interval %s\n", optarg);
968 }
969 break;
970
971 case 'd':
972 r = get_length_arg(optarg, &echo_data_len);
973 if (r)
974 {
975 fatal("invalid data length %s\n", optarg);
976 }
977 break;
978
979 case 'o':
980 report_name = optarg;
981 break;
982
983 case 'A':
984 r = get_time_arg_msec(optarg, &alert_interval_msec);
985 if (r || alert_interval_msec == 0)
986 {
987 fatal("invalid alert interval %s\n", optarg);
988 }
989 break;
990
991 case 'D':
992 r = get_time_arg_msec(optarg, &latency_alarm_threshold_msec);
993 if (r)
994 {
995 fatal("invalid latency alarm threshold %s\n", optarg);
996 }
997 latency_alarm_threshold_usec = latency_alarm_threshold_msec * 1000;
998 break;
999
1000 case 'L':
1001 r = get_percent_arg(optarg, &loss_alarm_threshold_percent);
1002 if (r)
1003 {
1004 fatal("invalid loss alarm threshold %s\n", optarg);
1005 }
1006 break;
1007
1008 case 'C':
1009 alert_cmd_offset = strlen(optarg);
1010 alert_cmd = malloc(alert_cmd_offset + OUTPUT_MAX);
1011 if (alert_cmd == NULL)
1012 {
1013 fatal("malloc of alert command buffer failed\n");
1014 }
1015 memcpy(alert_cmd, optarg, alert_cmd_offset);
1016 break;
1017
1018 case 'i':
1019 len = strlen(optarg);
1020 if (len >= sizeof(identifier) - 1)
1021 {
1022 fatal("identifier argument too large (max %u bytes)\n", (unsigned) sizeof(identifier) - 1);
1023 }
1024 // optarg with a space appended
1025 memcpy(identifier, optarg, len);
1026 identifier[len] = ' ';
1027 identifier[len + 1] = '\0';
1028 break;
1029
1030 case 'u':
1031 usocket_name = optarg;
1032 break;
1033
1034 case 'p':
1035 pidfile_name = optarg;
1036 break;
1037
1038 default:
1039 usage();
1040 exit(EXIT_FAILURE);
1041 }
1042 }
1043
1044 // Ensure we have the correct number of parameters
1045 if (argc != optind + 1)
1046 {
1047 usage();
1048 exit(EXIT_FAILURE);
1049 }
1050 dest_arg = argv[optind];
1051
1052 // Ensure we have something to do: at least one of alarm, report, socket
1053 if (report_interval_msec == 0 && latency_alarm_threshold_msec == 0 && loss_alarm_threshold_percent == 0 && usocket_name == NULL)
1054 {
1055 fatal("no activity enabled\n");
1056 }
1057
1058 // Ensure there is a minimum of one resolved slot at all times
1059 if (time_period_msec <= send_interval_msec * 2 + loss_interval_msec)
1060 {
1061 fatal("the time period must be greater than twice the send interval plus the loss interval\n");
1062 }
1063
1064 // Ensure we don't have sequence space issues. This really should only be hit by
1065 // complete accident. Even a ratio of 16384:1 would be excessive.
1066 if (time_period_msec / send_interval_msec > 65536)
1067 {
1068 fatal("the ratio of time period to send interval cannot exceed 65536:1\n");
1069 }
1070
1071 // Check destination address
1072 memset(&hint, 0, sizeof(struct addrinfo));
1073 hint.ai_flags = AI_NUMERICHOST;
1074 hint.ai_family = AF_UNSPEC;
1075 hint.ai_socktype = SOCK_RAW;
1076
1077 r = getaddrinfo(dest_arg, NULL, &hint, &addr_info);
1078 if (r != 0)
1079 {
1080 fatal("invalid destination IP address %s\n", dest_arg);
1081 }
1082
1083 if (addr_info->ai_family == AF_INET6)
1084 {
1085 af_family = AF_INET6;
1086 ip_proto = IPPROTO_ICMPV6;
1087 echo_request_type = ICMP6_ECHO_REQUEST;
1088 echo_reply_type = ICMP6_ECHO_REPLY;
1089 }
1090 else if (addr_info->ai_family != AF_INET)
1091 {
1092 fatal("invalid destination IP address %s\n", dest_arg);
1093 }
1094
1095
1096 dest_addr_len = addr_info->ai_addrlen;
1097 memcpy(&dest_addr, addr_info->ai_addr, dest_addr_len);
1098 freeaddrinfo(addr_info);
1099
1100 // Check bind address
1101 if (bind_arg)
1102 {
1103 // Address family must match
1104 hint.ai_family = af_family;
1105
1106 r = getaddrinfo(bind_arg, NULL, &hint, &addr_info);
1107 if (r != 0)
1108 {
1109 fatal("invalid bind IP address %s\n", bind_arg);
1110 }
1111
1112 bind_addr_len = addr_info->ai_addrlen;
1113 memcpy(&bind_addr, addr_info->ai_addr, bind_addr_len);
1114 freeaddrinfo(addr_info);
1115 }
1116
1117 // Check requested data length
1118 if (echo_data_len)
1119 {
1120 if (af_family == AF_INET)
1121 {
1122 if (echo_data_len > IPV4_ICMP_DATA_MAX)
1123 {
1124 fatal("data length too large for IPv4 - maximum is %u bytes\n", (unsigned) IPV4_ICMP_DATA_MAX);
1125 }
1126 }
1127 else
1128 {
1129 if (echo_data_len > IPV6_ICMP_DATA_MAX)
1130 {
1131 fatal("data length too large for IPv6 - maximum is %u bytes\n", (unsigned) IPV6_ICMP_DATA_MAX);
1132 }
1133 }
1134
1135 echo_request_len += echo_data_len;
1136 }
1137 }
1138
1139
1140 //
1141 // Main
1142 //
1143 int
main(int argc,char * argv[])1144 main(
1145 int argc,
1146 char *argv[])
1147 {
1148 char bind_str[ADDR_STR_MAX] = "(none)";
1149 char pidbuf[64];
1150 int pidfile_fd = -1;
1151 pid_t pid;
1152 pthread_t thread;
1153 struct sigaction act;
1154 int buflen = PACKET_BUFLEN;
1155 ssize_t len;
1156 ssize_t rs;
1157 int r;
1158
1159 // Handle command line args
1160 parse_args(argc, argv);
1161
1162 // Set up our sockets
1163 send_sock = socket(af_family, SOCK_RAW, ip_proto);
1164 if (send_sock == -1)
1165 {
1166 perror("socket");
1167 fatal("cannot create send socket\n");
1168 }
1169 (void) fcntl(send_sock, F_SETFL, FD_CLOEXEC);
1170 (void) setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof(buflen));
1171
1172 recv_sock = socket(af_family, SOCK_RAW, ip_proto);
1173 if (recv_sock == -1)
1174 {
1175 perror("socket");
1176 fatal("cannot create recv socket\n");
1177 }
1178 (void) fcntl(recv_sock, F_SETFL, FD_CLOEXEC);
1179 (void) setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(buflen));
1180
1181 // Bind our sockets to an address if requested
1182 if (bind_addr_len)
1183 {
1184 r = bind(send_sock, (struct sockaddr *) &bind_addr, bind_addr_len);
1185 if (r == -1)
1186 {
1187 perror("bind");
1188 fatal("cannot bind send socket\n");
1189 }
1190 r = bind(recv_sock, (struct sockaddr *) &bind_addr, bind_addr_len);
1191 if (r == -1)
1192 {
1193 perror("bind");
1194 fatal("cannot bind recv socket\n");
1195 }
1196 }
1197
1198 // Drop privileges
1199 (void) setgid(getgid());
1200 (void) setuid(getuid());
1201
1202 // Create pid file
1203 if (pidfile_name)
1204 {
1205 pidfile_fd = open(pidfile_name, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
1206 if (pidfile_fd != -1)
1207 {
1208 // Lock the pid file
1209 r = flock(pidfile_fd, LOCK_EX | LOCK_NB);
1210 if (r == -1)
1211 {
1212 perror("flock");
1213 fatal("error locking pid file\n");
1214 }
1215 }
1216 else
1217 {
1218 // Pid file already exists?
1219 pidfile_fd = open(pidfile_name, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
1220 if (pidfile_fd == -1)
1221 {
1222 perror("open");
1223 fatal("cannot create/open pid file %s\n", pidfile_name);
1224 }
1225
1226 // Lock the pid file
1227 r = flock(pidfile_fd, LOCK_EX | LOCK_NB);
1228 if (r == -1)
1229 {
1230 fatal("pid file %s is in use by another process\n", pidfile_name);
1231 }
1232
1233 // Check for existing pid
1234 rs = read(pidfile_fd, pidbuf, sizeof(pidbuf) - 1);
1235 if (rs > 0)
1236 {
1237 pidbuf[rs] = 0;
1238
1239 pid = (pid_t) strtol(pidbuf, NULL, 10);
1240 if (pid > 0)
1241 {
1242 // Is the pid still alive?
1243 r = kill(pid, 0);
1244 if (r == 0)
1245 {
1246 fatal("pid file %s is in use by process %u\n", pidfile_name, (unsigned int) pid);
1247 }
1248 }
1249 }
1250
1251 // Reset the pid file
1252 (void) lseek(pidfile_fd, 0, 0);
1253 r = ftruncate(pidfile_fd, 0);
1254 if (r == -1)
1255 {
1256 perror("ftruncate");
1257 fatal("cannot write pid file %s\n", pidfile_name);
1258 }
1259 }
1260 }
1261
1262 // Create report file
1263 if (report_name)
1264 {
1265 report_fd = open(report_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
1266 if (report_fd == -1)
1267 {
1268 perror("open");
1269 fatal("cannot open/create report file %s\n", report_name);
1270 }
1271 }
1272 else
1273 {
1274 report_fd = fileno(stdout);
1275 }
1276
1277 // Create unix socket
1278 if (usocket_name)
1279 {
1280 struct sockaddr_un uaddr;
1281
1282 if (strlen(usocket_name) >= sizeof(uaddr.sun_path))
1283 {
1284 fatal("socket name too large\n");
1285 }
1286
1287 usocket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
1288 if (usocket_fd == -1)
1289 {
1290 perror("socket");
1291 fatal("cannot create unix domain socket\n");
1292 }
1293 (void) fcntl(usocket_fd, F_SETFL, FD_CLOEXEC);
1294 (void) unlink(usocket_name);
1295
1296 memset(&uaddr, 0, sizeof(uaddr));
1297 uaddr.sun_family = AF_UNIX;
1298 strcpy(uaddr.sun_path, usocket_name);
1299 r = bind(usocket_fd, (struct sockaddr *) &uaddr, sizeof(uaddr));
1300 if (r == -1)
1301 {
1302 perror("bind");
1303 fatal("cannot bind unix domain socket\n");
1304 }
1305
1306 r = chmod(usocket_name, 0666);
1307 if (r == -1)
1308 {
1309 perror("fchmod");
1310 fatal("cannot fchmod unix domain socket\n");
1311 }
1312
1313 r = listen(usocket_fd, 5);
1314 if (r == -1)
1315 {
1316 perror("listen");
1317 fatal("cannot listen on unix domain socket\n");
1318 }
1319 }
1320
1321 // End of general errors from command line options
1322
1323 // Self background
1324 if (foreground == 0)
1325 {
1326 pid = fork();
1327
1328 if (pid == -1)
1329 {
1330 perror("fork");
1331 fatal("cannot background\n");
1332 }
1333
1334 if (pid)
1335 {
1336 _exit(EXIT_SUCCESS);
1337 }
1338
1339 (void) setsid();
1340 }
1341
1342 // Termination handler
1343 memset(&act, 0, sizeof(act));
1344 act.sa_handler = (void (*)(int)) term_handler;
1345 (void) sigaction(SIGTERM, &act, NULL);
1346 (void) sigaction(SIGINT, &act, NULL);
1347
1348 // Write pid file
1349 if (pidfile_fd != -1)
1350 {
1351 len = snprintf(pidbuf, sizeof(pidbuf), "%u\n", (unsigned) getpid());
1352 if (len < 0 || (size_t) len > sizeof(pidbuf))
1353 {
1354 fatal("error formatting pidfile\n");
1355 }
1356
1357 rs = write(pidfile_fd, pidbuf, (size_t) len);
1358 if (rs == -1)
1359 {
1360 perror("write");
1361 fatal("error writing pidfile\n");
1362 }
1363
1364 r = close(pidfile_fd);
1365 if (r == -1)
1366 {
1367 perror("close");
1368 fatal("error writing pidfile\n");
1369 }
1370 }
1371
1372 // Create the array
1373 array_size = (unsigned int) (time_period_msec / send_interval_msec);
1374 array = calloc(array_size, sizeof(*array));
1375 if (array == NULL)
1376 {
1377 fatal("calloc of packet array failed\n");
1378 }
1379
1380 // Allocate the echo request/reply packet buffers
1381 echo_request = (icmphdr_t *) malloc(echo_request_len);
1382 echo_reply = malloc(echo_reply_len);
1383 if (echo_request == NULL || echo_reply == NULL)
1384 {
1385 fatal("malloc of packet buffers failed\n");
1386 }
1387
1388 // Set the default loss interval
1389 if (loss_interval_msec == 0)
1390 {
1391 loss_interval_msec = send_interval_msec * 4;
1392 }
1393 loss_interval_usec = loss_interval_msec * 1000;
1394
1395 // Log our general parameters
1396 r = getnameinfo((struct sockaddr *) &dest_addr, dest_addr_len, dest_str, sizeof(dest_str), NULL, 0, NI_NUMERICHOST);
1397 if (r != 0)
1398 {
1399 fatal("getnameinfo of destination address failed\n");
1400 }
1401
1402 if (bind_addr_len)
1403 {
1404 r = getnameinfo((struct sockaddr *) &bind_addr, bind_addr_len, bind_str, sizeof(bind_str), NULL, 0, NI_NUMERICHOST);
1405 if (r != 0)
1406 {
1407 fatal("getnameinfo of bind address failed\n");
1408 }
1409 }
1410
1411 logger("send_interval %lums loss_interval %lums time_period %lums report_interval %lums data_len %lu alert_interval %lums latency_alarm %lums loss_alarm %lu%% dest_addr %s bind_addr %s identifier \"%s\"\n",
1412 send_interval_msec, loss_interval_msec, time_period_msec, report_interval_msec, echo_data_len,
1413 alert_interval_msec, latency_alarm_threshold_msec, loss_alarm_threshold_percent,
1414 dest_str, bind_str, identifier);
1415
1416 // Set my echo id
1417 echo_id = htons((uint16_t) getpid());
1418
1419 // Set the limit for sequence number to ensure a multiple of array size
1420 sequence_limit = (uint16_t) array_size;
1421 while ((sequence_limit & 0x8000) == 0)
1422 {
1423 sequence_limit <<= 1;
1424 }
1425
1426 // Create recv thread
1427 r = pthread_create(&thread, NULL, &recv_thread, NULL);
1428 if (r != 0)
1429 {
1430 perror("pthread_create");
1431 fatal("cannot create recv thread\n");
1432 }
1433
1434 // Set priority on recv thread if requested
1435 if (flag_priority)
1436 {
1437 struct sched_param thread_sched_param;
1438
1439 r = sched_get_priority_min(SCHED_RR);
1440 if (r == -1)
1441 {
1442 perror("sched_get_priority_min");
1443 fatal("cannot determin minimum shceduling priority for SCHED_RR\n");
1444 }
1445 thread_sched_param.sched_priority = r;
1446
1447 r = pthread_setschedparam(thread, SCHED_RR, &thread_sched_param);
1448 if (r != 0)
1449 {
1450 perror("pthread_setschedparam");
1451 fatal("cannot set receive thread priority\n");
1452 }
1453 }
1454
1455 // Create send thread
1456 r = pthread_create(&thread, NULL, &send_thread, NULL);
1457 if (r != 0)
1458 {
1459 perror("pthread_create");
1460 fatal("cannot create send thread\n");
1461 }
1462
1463 // Report thread
1464 if (report_interval_msec)
1465 {
1466 r = pthread_create(&thread, NULL, &report_thread, NULL);
1467 if (r != 0)
1468 {
1469 perror("pthread_create");
1470 fatal("cannot create report thread\n");
1471 }
1472 }
1473
1474 // Create alert thread
1475 if (latency_alarm_threshold_msec || loss_alarm_threshold_percent)
1476 {
1477 r = pthread_create(&thread, NULL, &alert_thread, NULL);
1478 if (r != 0)
1479 {
1480 perror("pthread_create");
1481 fatal("cannot create alert thread\n");
1482 }
1483 }
1484
1485 // Create usocket thread
1486 if (usocket_name)
1487 {
1488 r = pthread_create(&thread, NULL, &usocket_thread, NULL);
1489 if (r != 0)
1490 {
1491 perror("pthread_create");
1492 fatal("cannot create usocket thread\n");
1493 }
1494 }
1495
1496 // Wait (forever) for last thread started
1497 pthread_join(thread, NULL);
1498
1499 // notreached
1500 return 0;
1501 }
1502