1
0
mirror of https://github.com/dennypage/dpinger.git synced 2024-05-19 06:50:01 +00:00

27 Commits
v1.2 ... v1.7

Author SHA1 Message Date
Denny Page
f94f6dcd47 Separate rrd and png names 2016-02-17 20:15:33 -08:00
dennypage
99208a60fe Update README.md 2016-02-13 21:06:56 -08:00
Denny Page
eb67d76de0 Merge branch 'master' of https://github.com/dennypage/dpinger 2016-02-13 21:03:46 -08:00
Denny Page
0b68c438f2 Output to name.cgi 2016-02-13 21:03:13 -08:00
dennypage
daac746074 Update README.md 2016-02-13 20:46:43 -08:00
dennypage
5348af36c6 Create README.md 2016-02-13 20:44:42 -08:00
Denny Page
b0fc95f618 Add RRD sample scripts 2016-02-13 19:42:41 -08:00
Denny Page
e9ffd0b43e Add void casts for discarded return values 2016-02-03 10:26:31 -08:00
Denny Page
9e8968adce Add -Wno-unused-result to fix spurious gcc warning
Default to optimized build
2016-02-03 10:11:48 -08:00
Denny Page
a8b44bedac Fix warnings from clang -Weverything 2016-02-02 22:28:21 -08:00
Denny Page
1d34caccbd Add explicit gcc/clang warning examples 2016-02-02 22:13:39 -08:00
Denny Page
9c5bac8658 Add casts to eliminate some warnings from clang -Weverything 2016-02-01 13:21:46 -08:00
Denny Page
3a19391cee Update Copyright 2016-02-01 13:21:11 -08:00
Denny Page
570ade420a Add simple Makefile 2016-02-01 13:16:02 -08:00
Denny Page
3825066db9 Rename Makefile to Makefile.freebsd 2016-02-01 10:57:32 -08:00
Denny Page
f06b3c8f36 Fix compile warnings for FreeBSD ports 2016-02-01 09:27:55 -08:00
Denny Page
4a57f2584c Add -d option to allow setting data payload length 2016-01-30 21:18:44 -08:00
Denny Page
afbde05bcb Make the standard deviation calculation a little easier on the eyes 2016-01-30 20:44:10 -08:00
Denny Page
85d345f47f Fix defective parsing of time values with 's' suffix 2016-01-14 20:56:46 -08:00
dennypage
e0a0ae14f9 Merge pull request #19 from pfsense/ignore_files
Ignore dpinger.full, dpinger.debug and vim swap files
2016-01-04 10:09:13 -08:00
Renato Botelho
87cd4b6e3b Ignore dpinger.full, dpinger.debug and vim swap files 2016-01-04 10:16:50 -02:00
Denny Page
919fad77a2 Change default time period to 30s and relax default loss interval to 5x send interval to address nagging low level loss reports
Add usage note about loss percentage reporting and calculation
2015-12-30 12:06:54 -08:00
dennypage
2e0430edea Merge pull request #18 from pfsense/fix_build_9
Initialize pidfile_fd to silence FreeBSD 9 warning
2015-12-30 10:39:29 -08:00
Renato Botelho
31432284dc Initialize pidfile_fd to silence FreeBSD 9 warning 2015-12-30 09:15:39 -02:00
Denny Page
e1d00b4210 Set close on exec for all file descriptors 2015-12-29 14:20:06 -08:00
Denny Page
1ec615486b Temporary to allow building on old systems (FreeBSD 9.3, Linux 2.6.26) 2015-12-29 11:28:14 -08:00
Denny Page
6789e90a38 Set close on exec flag if report file is explicit 2015-12-29 10:57:28 -08:00
11 changed files with 571 additions and 102 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
/dpinger
/dpinger.debug
/dpinger.full
/dpinger.o
/.*.swp

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015, Denny Page
Copyright (c) 2015-2016, Denny Page
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@@ -1,9 +1,9 @@
PROG= dpinger
MAN=
#CC=gcc
#WARNINGS=-Wall -Wextra -Wformat=2 -Wno-unused-result
BINDIR= ${PREFIX}/bin
WARNS= 6
CC=clang
WARNINGS=-Weverything -Wno-padded -Wno-disabled-macro-expansion
LDADD= -lpthread
CFLAGS=${WARNINGS} -pthread -g -O2
.include <bsd.prog.mk>
all: dpinger

9
Makefile.freebsd Normal file
View File

@@ -0,0 +1,9 @@
PROG= dpinger
MAN=
BINDIR= ${PREFIX}/bin
WARNS= 6
LDADD= -lpthread
.include <bsd.prog.mk>

294
dpinger.c
View File

@@ -1,6 +1,6 @@
//
// Copyright (c) 2015, Denny Page
// Copyright (c) 2015-2016, Denny Page
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@@ -52,6 +52,18 @@
#include <pthread.h>
#include <syslog.h>
// TODO:
//
// After December 31st, 2016, review use of fcntl() for setting non blocking
// and close on exec. It would be preferable to use accept4(), SOCK_CLOEXEC
// and SOCK_NONBLOCK. These are currently avoided to allow use on older
// systems such as FreeBSD 9.3, Linux 2.6.26.
// For Linux accept4() currently requires defining _GNU_SOURCE which we would
// like to avoid.
// For FreeBSD, these definitions were introduced with FreeBSD 10.0 and are
// not present in 9.3 which is supported through 2016.
// Who we are
static const char * progname;
@@ -68,7 +80,7 @@ static unsigned int flag_syslog = 0;
static char dest_str[ADDR_STR_MAX];
// Time period over which we are averaging results in ms
static unsigned long time_period_msec = 25000;
static unsigned long time_period_msec = 30000;
// Interval between sends in ms
static unsigned long send_interval_msec = 250;
@@ -138,8 +150,8 @@ static int recv_sock;
// IPv4 / IPv6 parameters
static uint16_t af_family = AF_INET; // IPv6: AF_INET6
static uint16_t echo_request_type = ICMP_ECHO; // IPv6: ICMP6_ECHO_REQUEST
static uint16_t echo_reply_type = ICMP_ECHOREPLY; // IPv6: ICMP6_ECHO_REPLY
static uint8_t echo_request_type = ICMP_ECHO; // IPv6: ICMP6_ECHO_REQUEST
static uint8_t echo_reply_type = ICMP_ECHOREPLY; // IPv6: ICMP6_ECHO_REPLY
static int ip_proto = IPPROTO_ICMP; // IPv6: IPPROTO_ICMPV6
// Destination address
@@ -163,8 +175,16 @@ typedef struct
uint16_t sequence;
} icmphdr_t;
// Echo request header for sendto
static icmphdr_t echo_request;
// Echo request/reply packet buffers
#define IPV4_ICMP_DATA_MAX (IP_MAXPACKET - sizeof(struct ip) - sizeof(icmphdr_t))
#define IPV6_ICMP_DATA_MAX (IP_MAXPACKET - sizeof(icmphdr_t))
#define PACKET_BUFLEN (IP_MAXPACKET + 256)
static unsigned long echo_data_len = 0;
static unsigned int echo_request_len = sizeof(icmphdr_t);
static unsigned int echo_reply_len = IP_MAXPACKET;
static icmphdr_t * echo_request;
static void * echo_reply;
// Echo id and Sequence information
static uint16_t echo_id;
@@ -175,6 +195,7 @@ static uint16_t sequence_limit;
//
// Termination handler
//
__attribute__ ((noreturn))
static void
term_handler(void)
{
@@ -194,10 +215,7 @@ term_handler(void)
//
// Log for abnormal events
//
#ifdef __GNUC__
static void logger(const char * format, ...) __attribute__ ((format (printf, 1, 2)));
#endif
__attribute__ ((format (printf, 1, 2)))
static void
logger(
const char * format,
@@ -242,7 +260,7 @@ cksum(
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return ~sum;
return (uint16_t) ~sum;
}
@@ -280,7 +298,7 @@ ts_elapsed_usec(
const struct timespec * old,
const struct timespec * new)
{
unsigned long r_usec;
long r_usec;
// Note that we are using monotonic clock and time cannot run backwards
if (new->tv_nsec >= old->tv_nsec)
@@ -292,24 +310,28 @@ ts_elapsed_usec(
r_usec = (new->tv_sec - old->tv_sec - 1) * 1000000 + (1000000000 + new->tv_nsec - old->tv_nsec) / 1000;
}
return r_usec;
return (unsigned long) r_usec;
}
//
// Send thread
//
__attribute__ ((noreturn))
static void *
send_thread(
__attribute__ ((unused))
void * arg)
{
struct timespec sleeptime;
ssize_t len;
int r;
// Set up our echo request packet
echo_request.type = echo_request_type;
echo_request.code = 0;
echo_request.id = echo_id;
memset(echo_request, 0, echo_request_len);
echo_request->type = echo_request_type;
echo_request->code = 0;
echo_request->id = echo_id;
// Set up the timespec for nanosleep
sleeptime.tv_sec = send_interval_msec / 1000;
@@ -324,41 +346,39 @@ send_thread(
}
// Set sequence number and checksum
echo_request.sequence = htons(next_sequence);
echo_request.cksum = 0;
echo_request.cksum = cksum((uint16_t *) &echo_request, sizeof(icmphdr_t));
echo_request->sequence = htons(next_sequence);
echo_request->cksum = 0;
echo_request->cksum = cksum((uint16_t *) echo_request, sizeof(icmphdr_t));
array[next_slot].status = PACKET_STATUS_EMPTY;
sched_yield();
clock_gettime(CLOCK_MONOTONIC, &array[next_slot].time_sent);
array[next_slot].status = PACKET_STATUS_SENT;
r = sendto(send_sock, &echo_request, sizeof(icmphdr_t), 0, (struct sockaddr *) &dest_addr, dest_addr_len);
if (r == -1)
len = sendto(send_sock, echo_request, echo_request_len, 0, (struct sockaddr *) &dest_addr, dest_addr_len);
if (len == -1)
{
logger("%s%s: sendto error: %d\n", identifier, dest_str, errno);
}
array[next_slot].status = PACKET_STATUS_SENT;
next_slot = (next_slot + 1) % array_size;
next_sequence = (next_sequence + 1) % sequence_limit;
}
// notreached
return arg;
}
//
// Receive thread
//
__attribute__ ((noreturn))
static void *
recv_thread(
__attribute__ ((unused))
void * arg)
{
char packet[1024];
unsigned int packet_len;
struct sockaddr_storage src_addr;
socklen_t src_addr_len;
ssize_t len;
icmphdr_t * icmp;
struct timespec now;
unsigned int array_slot;
@@ -366,8 +386,8 @@ recv_thread(
while (1)
{
src_addr_len = sizeof(src_addr);
packet_len = recvfrom(recv_sock, &packet, sizeof(packet), 0, (struct sockaddr *) &src_addr, &src_addr_len);
if (packet_len == (unsigned int) -1)
len = recvfrom(recv_sock, echo_reply, echo_reply_len, 0, (struct sockaddr *) &src_addr, &src_addr_len);
if (len == -1)
{
logger("%s%s: recvfrom error: %d\n", identifier, dest_str, errno);
continue;
@@ -380,25 +400,25 @@ recv_thread(
size_t ip_len;
// With IPv4, we get the entire IP packet
if (packet_len < sizeof(struct ip))
if (len < (ssize_t) sizeof(struct ip))
{
logger("%s%s: received packet too small for IP header\n", identifier, dest_str);
continue;
}
ip = (void *) packet;
ip_len = ip->ip_hl << 2;
ip = echo_reply;
ip_len = (size_t) ip->ip_hl << 2;
icmp = (void *) (packet + ip_len);
packet_len -= ip_len;
icmp = (void *) ((char *) ip + ip_len);
len -= ip_len;
}
else
{
// With IPv6, we just get the ICMP payload
icmp = (void *) (packet);
icmp = echo_reply;
}
// This should never happen
if (packet_len < sizeof(icmphdr_t))
if (len < (ssize_t) sizeof(icmphdr_t))
{
logger("%s%s: received packet too small for ICMP header\n", identifier, dest_str);
continue;
@@ -420,9 +440,6 @@ recv_thread(
array[array_slot].latency_usec = ts_elapsed_usec(&array[array_slot].time_sent, &now);
array[array_slot].status = PACKET_STATUS_RECEIVED;
}
// notreached
return arg;
}
@@ -438,6 +455,7 @@ report(
struct timespec now;
unsigned long packets_received = 0;
unsigned long packets_lost = 0;
unsigned long latency_usec = 0;
unsigned long total_latency_usec = 0;
unsigned long long total_latency_usec2 = 0;
unsigned int slot;
@@ -451,8 +469,9 @@ report(
if (array[slot].status == PACKET_STATUS_RECEIVED)
{
packets_received++;
total_latency_usec += array[slot].latency_usec;
total_latency_usec2 += array[slot].latency_usec * array[slot].latency_usec;
latency_usec = array[slot].latency_usec;
total_latency_usec += latency_usec;
total_latency_usec2 += latency_usec * latency_usec;
}
else if (array[slot].status == PACKET_STATUS_SENT &&
ts_elapsed_usec(&array[slot].time_sent, &now) > loss_interval_usec)
@@ -465,10 +484,12 @@ report(
if (packets_received)
{
*average_latency_usec = total_latency_usec / packets_received;
unsigned long avg = total_latency_usec / packets_received;
unsigned long long avg2 = total_latency_usec2 / packets_received;
// sqrt( (sum(rtt^2) / packets) - (sum(rtt) / packets)^2)
*latency_deviation = llsqrt((total_latency_usec2 / packets_received) - (total_latency_usec / packets_received) * (total_latency_usec / packets_received));
// stddev = sqrt((sum(rtt^2) / packets) - (sum(rtt) / packets)^2)
*average_latency_usec = avg;
*latency_deviation = llsqrt(avg2 - (avg * avg));
}
else
{
@@ -490,8 +511,10 @@ report(
//
// Report thread
//
__attribute__ ((noreturn))
static void *
report_thread(
__attribute__ ((unused))
void * arg)
{
char buf[OUTPUT_MAX];
@@ -499,7 +522,8 @@ report_thread(
unsigned long average_latency_usec;
unsigned long latency_deviation;
unsigned long average_loss_percent;
int len;
ssize_t len;
ssize_t rs;
int r;
// Set up the timespec for nanosleep
@@ -522,33 +546,32 @@ report_thread(
logger("error formatting output in report thread\n");
}
r = write(report_fd, buf, len);
if (r == -1)
rs = write(report_fd, buf, (size_t) len);
if (rs == -1)
{
logger("write error in report thread: %d\n", errno);
}
else if (r != len)
else if (rs != len)
{
logger("short write in report thread: %d/%d\n", r, len);
logger("short write in report thread: %zd/%zd\n", rs, len);
}
if (flag_rewind)
{
ftruncate(report_fd, len);
lseek(report_fd, SEEK_SET, 0);
(void) ftruncate(report_fd, len);
(void) lseek(report_fd, SEEK_SET, 0);
}
}
// notreached
return arg;
}
//
// Alert thread
//
__attribute__ ((noreturn))
static void *
alert_thread(
__attribute__ ((unused))
void * arg)
{
struct timespec sleeptime;
@@ -642,16 +665,15 @@ alert_thread(
}
}
}
// notreached
return arg;
}
//
// Unix socket thread
//
__attribute__ ((noreturn))
static void *
usocket_thread(
__attribute__ ((unused))
void * arg)
{
char buf[OUTPUT_MAX];
@@ -659,12 +681,14 @@ usocket_thread(
unsigned long latency_deviation;
unsigned long average_loss_percent;
int sock_fd;
int len;
ssize_t len;
ssize_t rs;
int r;
while (1)
{
sock_fd = accept(usocket_fd, NULL, NULL);
(void) fcntl(sock_fd, F_SETFL, FD_CLOEXEC);
(void) fcntl(sock_fd, F_SETFL, fcntl(sock_fd, F_GETFL, 0) | O_NONBLOCK);
report(&average_latency_usec, &latency_deviation, &average_loss_percent);
@@ -675,14 +699,14 @@ usocket_thread(
logger("error formatting output in usocket thread\n");
}
r = write(sock_fd, buf, len);
if (r == -1)
rs = write(sock_fd, buf, (size_t) len);
if (rs == -1)
{
logger("write error in usocket thread: %d\n", errno);
}
else if (r != len)
else if (rs != len)
{
logger("short write in usocket thread: %d/%d\n", r, len);
logger("short write in usocket thread: %zd/%zd\n", rs, len);
}
r = close(sock_fd);
@@ -691,9 +715,6 @@ usocket_thread(
logger("close error in usocket thread: %d\n", errno);
}
}
// notreached
return arg;
}
@@ -718,7 +739,7 @@ get_time_arg_msec(
else if (*suffix == 's')
{
// Seconds
*value *= 1000;
t *= 1000;
suffix++;
}
@@ -761,6 +782,41 @@ get_percent_arg(
}
//
// Decode a byte length argument
//
static int
get_length_arg(
const char * arg,
unsigned long * value)
{
unsigned long t;
char * suffix;
t = strtoul(arg, &suffix, 10);
if (*suffix == 'b')
{
// Bytes
suffix++;
}
else if (*suffix == 'k')
{
// Kilobytes
t *= 1024;
suffix++;
}
// Garbage in the number?
if (*suffix != 0)
{
return 1;
}
*value = t;
return 0;
}
//
// Output usage
//
@@ -768,16 +824,17 @@ static void
usage(void)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s [-f] [-R] [-S] [-B bind_addr] [-s send_interval] [-l loss_interval] [-t time_period] [-r report_interval] [-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);
fprintf(stderr, " %s [-f] [-R] [-S] [-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);
fprintf(stderr, " options:\n");
fprintf(stderr, " -f run in foreground\n");
fprintf(stderr, " -R rewind output file between reports\n");
fprintf(stderr, " -S log warnings via syslog\n");
fprintf(stderr, " -B bind (source) address\n");
fprintf(stderr, " -s time interval between echo requests (default 250ms)\n");
fprintf(stderr, " -l time interval before packets are treated as lost (default 2x send interval)\n");
fprintf(stderr, " -t time period over which results are averaged (default 25s)\n");
fprintf(stderr, " -l time interval before packets are treated as lost (default 5x send interval)\n");
fprintf(stderr, " -t time period over which results are averaged (default 30s)\n");
fprintf(stderr, " -r time interval between reports (default 1s)\n");
fprintf(stderr, " -d data length (default 0)\n");
fprintf(stderr, " -o output file for reports (default stdout)\n");
fprintf(stderr, " -A time interval between alerts (default 1s)\n");
fprintf(stderr, " -D time threshold for latency alarm (default none)\n");
@@ -787,11 +844,13 @@ usage(void)
fprintf(stderr, " -u unix socket name for polling\n");
fprintf(stderr, " -p process id file name\n\n");
fprintf(stderr, " notes:\n");
fprintf(stderr, " IP addresses can be in either IPv4 or IPv6 format\n\n");
fprintf(stderr, " time values can be expressed with a suffix of 'm' (milliseconds) or 's' (seconds)\n");
fprintf(stderr, " if no suffix is specified, milliseconds is the default\n\n");
fprintf(stderr, " IP addresses can be in either IPv4 or IPv6 format\n\n");
fprintf(stderr, " the output format is \"latency_avg latency_stddev loss_pct\"\n");
fprintf(stderr, " latency values are output in microseconds\n\n");
fprintf(stderr, " latency values are output in microseconds\n");
fprintf(stderr, " loss percentage is reported in whole numbers of 0-100\n");
fprintf(stderr, " resolution of loss calculation is: 100 * send_interval / (time_period - loss_interval)\n\n");
fprintf(stderr, " the alert_cmd is invoked as \"alert_cmd dest_addr alarm_flag latency_avg loss_avg\"\n");
fprintf(stderr, " alarm_flag is set to 1 if either latency or loss is in alarm state\n");
fprintf(stderr, " alarm_flag will return to 0 when both have have cleared alarm state\n\n");
@@ -801,6 +860,7 @@ usage(void)
//
// Fatal error
//
__attribute__ ((noreturn, format (printf, 1, 2)))
static void
fatal(
const char * format,
@@ -837,7 +897,7 @@ parse_args(
progname = argv[0];
while((opt = getopt(argc, argv, "fRSB:s:l:t:r:o:A:D:L:C:i:u:p:")) != -1)
while((opt = getopt(argc, argv, "fRSB:s:l:t:r:d:o:A:D:L:C:i:u:p:")) != -1)
{
switch (opt)
{
@@ -889,6 +949,14 @@ parse_args(
}
break;
case 'd':
r = get_length_arg(optarg, &echo_data_len);
if (r)
{
fatal("invalid data length %s\n", optarg);
}
break;
case 'o':
report_name = optarg;
break;
@@ -920,7 +988,7 @@ parse_args(
case 'C':
alert_cmd_offset = strlen(optarg);
alert_cmd = malloc (alert_cmd_offset + OUTPUT_MAX);
alert_cmd = malloc(alert_cmd_offset + OUTPUT_MAX);
if (alert_cmd == NULL)
{
fatal("malloc of alert command buffer failed\n");
@@ -932,7 +1000,7 @@ parse_args(
len = strlen(optarg);
if (len >= sizeof(identifier) - 1)
{
fatal("identifier argument too large (max %u bytes)\n", sizeof(identifier) - 1);
fatal("identifier argument too large (max %u bytes)\n", (unsigned) sizeof(identifier) - 1);
}
// optarg with a space appended
memcpy(identifier, optarg, len);
@@ -1026,6 +1094,27 @@ parse_args(
memcpy(&bind_addr, addr_info->ai_addr, bind_addr_len);
freeaddrinfo(addr_info);
}
// Check requested data length
if (echo_data_len)
{
if (af_family == AF_INET)
{
if (echo_data_len > IPV4_ICMP_DATA_MAX)
{
fatal("data length too large for IPv4 - maximum is %u bytes\n", (unsigned) IPV4_ICMP_DATA_MAX);
}
}
else
{
if (echo_data_len > IPV6_ICMP_DATA_MAX)
{
fatal("data length too large for IPv6 - maximum is %u bytes\n", (unsigned) IPV6_ICMP_DATA_MAX);
}
}
echo_request_len += echo_data_len;
}
}
@@ -1038,9 +1127,11 @@ main(
char *argv[])
{
char bind_str[ADDR_STR_MAX] = "(none)";
int pidfile_fd;
int pidfile_fd = -1;
pthread_t thread;
struct sigaction act;
int buflen = PACKET_BUFLEN;
ssize_t rs;
int r;
// Handle command line args
@@ -1053,12 +1144,17 @@ main(
perror("socket");
fatal("cannot create send socket\n");
}
(void) fcntl(send_sock, F_SETFL, FD_CLOEXEC);
(void) setsockopt(send_sock, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof(buflen));
recv_sock = socket(af_family, SOCK_RAW, ip_proto);
if (recv_sock == -1)
{
perror("socket");
fatal("cannot create recv socket\n");
}
(void) fcntl(recv_sock, F_SETFL, FD_CLOEXEC);
(void) setsockopt(recv_sock, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(buflen));
// Bind our sockets to an address if requested
if (bind_addr_len)
@@ -1078,13 +1174,13 @@ main(
}
// Drop privileges
r = setgid(getgid());
r = setuid(getuid());
(void) setgid(getgid());
(void) setuid(getuid());
// Create report file
if (report_name)
{
report_fd = open(report_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
report_fd = open(report_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (report_fd == -1)
{
perror("open");
@@ -1106,13 +1202,13 @@ main(
fatal("socket name too large\n");
}
usocket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
usocket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (usocket_fd == -1)
{
perror("socket");
fatal("cannot create unix domain socket\n");
}
(void) fcntl(usocket_fd, F_SETFL, FD_CLOEXEC);
(void) unlink(usocket_name);
memset(&uaddr, 0, sizeof(uaddr));
@@ -1143,7 +1239,7 @@ main(
// Create pid file
if (pidfile_name)
{
pidfile_fd = open(pidfile_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
pidfile_fd = open(pidfile_name, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644);
if (pidfile_fd == -1)
{
perror("open");
@@ -1175,14 +1271,14 @@ main(
// Termination handler
memset(&act, 0, sizeof(act));
act.sa_handler = (void (*)(int)) term_handler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGTERM, &act, NULL);
(void) sigaction(SIGINT, &act, NULL);
// Write pid file
if (pidfile_name)
if (pidfile_fd != -1)
{
char buf[64];
int len;
ssize_t len;
len = snprintf(buf, sizeof(buf), "%u\n", (unsigned) getpid());
if (len < 0 || (size_t) len > sizeof(buf))
@@ -1190,14 +1286,14 @@ main(
fatal("error formatting pidfile\n");
}
r = write(pidfile_fd, buf, len);
if (r == -1)
rs = write(pidfile_fd, buf, (size_t) len);
if (rs == -1)
{
perror("write");
fatal("error writing pidfile\n");
}
r= close(pidfile_fd);
r = close(pidfile_fd);
if (r == -1)
{
perror("close");
@@ -1206,17 +1302,25 @@ main(
}
// Create the array
array_size = time_period_msec / send_interval_msec;
array_size = (unsigned int) (time_period_msec / send_interval_msec);
array = calloc(array_size, sizeof(*array));
if (array == NULL)
{
fatal("calloc of packet array failed\n");
}
// Allocate the echo request/reply packet buffers
echo_request = (icmphdr_t *) malloc(echo_request_len);
echo_reply = malloc(echo_reply_len);
if (echo_request == NULL || echo_reply == NULL)
{
fatal("malloc of packet buffers failed\n");
}
// Set the default loss interval
if (loss_interval_msec == 0)
{
loss_interval_msec = send_interval_msec * 4;
loss_interval_msec = send_interval_msec * 5;
}
loss_interval_usec = loss_interval_msec * 1000;
@@ -1236,19 +1340,19 @@ main(
}
}
logger("send_interval %lums loss_interval %lums time_period %lums report_interval %lums alert_interval %lums latency_alarm %lums loss_alarm %lu%% dest_addr %s bind_addr %s identifier \"%s\"\n",
send_interval_msec, loss_interval_msec, time_period_msec, report_interval_msec,
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",
send_interval_msec, loss_interval_msec, time_period_msec, report_interval_msec, echo_data_len,
alert_interval_msec, latency_alarm_threshold_msec, loss_alarm_threshold_percent,
dest_str, bind_str, identifier);
// Set my echo id
echo_id = htons(getpid());
echo_id = htons((uint16_t) getpid());
// Set the limit for sequence number to ensure a multiple of array size
sequence_limit = array_size;
sequence_limit = (uint16_t) array_size;
while ((sequence_limit & 0x8000) == 0)
{
sequence_limit = sequence_limit << 1;
sequence_limit <<= 1;
}
// Create recv thread

25
rrd/README.md Normal file
View File

@@ -0,0 +1,25 @@
Example scripts for creating RRD graphs with dpinger
<br>
Files and Usage:
dpinger_rrd_create <name>
Create the rrd initial file.
dpinger_rrd_update <name> <target> <additional dpinger options>
Daemon updater script. Runs dpinger and feeds the rrd file.
dpinger_rrd_gencgi <name>
Generate a cgi script that displays graphs.
dpinger_rrd_graph <name>
Generate png files for use with static html
sample.html
Sample static html to display graphs.

19
rrd/dpinger_rrd_create Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
if [ $# -ne 1 ]
then
echo "usage: $0 name"
exit 1
fi
name="$1"
rrdfile="${name}.rrd"
echo "Creating rrd file ${rrdfile}"
rrdtool create "${rrdfile}" --step 1m \
DS:latency:GAUGE:5m:0:U \
DS:stddev:GAUGE:5m:0:U \
DS:loss:GAUGE:5m:0:100 \
RRA:AVERAGE:0.5:1m:15d \
RRA:AVERAGE:0.5:5m:90d \
RRA:AVERAGE:0.5:1h:3y

140
rrd/dpinger_rrd_gencgi Executable file
View File

@@ -0,0 +1,140 @@
#!/bin/sh
if [ $# -ne 2 ]
then
echo "usage: $0 rrdname pngname"
exit 1
fi
rrdname="${1}"
pngname="${2}"
# Prefixes for rrd and png files. Note that if the prefix is a directory, it must incldue the trailing slash
# If no value is set, the files are located in the current directory when the cgi script runs
rrdprefix=
pngprefix=/tmp/
# Graph dimensions
#graph_height=240
#graph_width=720
graph_height=280
graph_width=840
# Preferred font
font="DejaVuSansMono"
# Latency breakpoints in milliseconds
latency_s0=20
latency_s1=40
latency_s2=80
latency_s3=160
latency_s4=320
# Latency colors
latency_c0="dddddd"
latency_c1="ddbbbb"
latency_c2="d4aaaa"
latency_c3="cc9999"
latency_c4="c38888"
latency_c5="bb7777"
# Standard deviation color & opacity
stddev_c="55333355"
# Loss color
loss_c="ee0000"
gen_graph()
{
png=$1
rrd=$2
start=$3
end=$4
step=$5
description=$6
echo "<RRD::GRAPH \"${png}\""
echo "--lazy"
echo "--start \"${start}\" --end \"${end}\" --step \"${step}\""
echo "--height ${graph_height} --width ${graph_width}"
echo "--title \"Average Latency and Packet Loss - ${description}\""
echo "--disable-rrdtool-tag"
echo "--color BACK#ffffff"
echo "--font DEFAULT:9:\"${font}\""
echo "--font AXIS:8:\"${font}\""
echo "DEF:latency_us=\"${rrd}\":latency:AVERAGE:step=\"${step}\""
echo "CDEF:latency=latency_us,1000,/"
echo "CDEF:latency_s0=latency,${latency_s0},MIN"
echo "CDEF:latency_s1=latency,${latency_s1},MIN"
echo "CDEF:latency_s2=latency,${latency_s2},MIN"
echo "CDEF:latency_s3=latency,${latency_s3},MIN"
echo "CDEF:latency_s4=latency,${latency_s4},MIN"
echo "VDEF:latency_min=latency,MINIMUM"
echo "VDEF:latency_max=latency,MAXIMUM"
echo "VDEF:latency_avg=latency,AVERAGE"
echo "VDEF:latency_last=latency,LAST"
echo "DEF:stddev_us=\"${rrd}\":stddev:AVERAGE:step=\"${step}\""
echo "CDEF:stddev=stddev_us,1000,/"
echo "VDEF:stddev_min=stddev,MINIMUM"
echo "VDEF:stddev_max=stddev,MAXIMUM"
echo "VDEF:stddev_avg=stddev,AVERAGE"
echo "VDEF:stddev_last=stddev,LAST"
echo "DEF:loss=\"${rrd}\":loss:AVERAGE:step=\"${step}\""
echo "CDEF:loss_neg=loss,-1,*"
echo "VDEF:loss_min=loss,MINIMUM"
echo "VDEF:loss_max=loss,MAXIMUM"
echo "VDEF:loss_avg=loss,AVERAGE"
echo "VDEF:loss_last=loss,LAST"
echo "COMMENT:\" Min Max Avg Last\n\""
echo "COMMENT:\" \""
echo "AREA:latency#${latency_c5}"
echo "AREA:latency_s4#${latency_c4}"
echo "AREA:latency_s3#${latency_c3}"
echo "AREA:latency_s2#${latency_c2}"
echo "AREA:latency_s1#${latency_c1}"
echo "AREA:latency_s0#${latency_c0}"
echo "LINE1:latency#000000:\"Latency \""
echo "GPRINT:\"latency_min:%8.3lf ms\t\""
echo "GPRINT:\"latency_max:%8.3lf ms\t\""
echo "GPRINT:\"latency_avg:%8.3lf ms\t\""
echo "GPRINT:\"latency_last:%8.3lf ms\n\""
echo "COMMENT:\" \""
echo "LINE1:stddev#${stddev_c}:\"Stddev \""
echo "GPRINT:\"stddev_min:%8.3lf ms\t\""
echo "GPRINT:\"stddev_max:%8.3lf ms\t\""
echo "GPRINT:\"stddev_avg:%8.3lf ms\t\""
echo "GPRINT:\"stddev_last:%8.3lf ms\n\""
echo "COMMENT:\" \""
echo "AREA:loss_neg#${loss_c}:\"Loss \""
echo "GPRINT:\"loss_min:%4.1lf %%\t\t\""
echo "GPRINT:\"loss_max:%4.1lf %%\t\t\""
echo "GPRINT:\"loss_avg:%4.1lf %%\t\t\""
echo "GPRINT:\"loss_last:%4.1lf %%\n\""
echo "COMMENT:\" \n\""
echo "GPRINT:\"latency_last:Ending at %H\\:%M on %B %d, %Y\\r:strftime\""
echo ">"
echo "<p>"
}
(
echo "#!/usr/bin/rrdcgi"
echo "<html> <head> <title>Latency Statistics for ${rrdname}</title> </head> <body>"
gen_graph "${pngprefix}${pngname}-1.png" "${rrdprefix}${rrdname}.rrd" "now-8h" "now" "60" "Last 8 hours - 1 minute intervals"
gen_graph "${pngprefix}${pngname}-2.png" "${rrdprefix}${rrdname}.rrd" "now-36h" "now" "300" "Last 36 hours - 5 minute intervals"
gen_graph "${pngprefix}${pngname}-3.png" "${rrdprefix}${rrdname}.rrd" "now-8d" "now" "1800" "Last 8 days - 30 minute intervals"
gen_graph "${pngprefix}${pngname}-4.png" "${rrdprefix}${rrdname}.rrd" "now-60d" "now" "14400" "Last 60 days - 4 hour intervals"
gen_graph "${pngprefix}${pngname}-5.png" "${rrdprefix}${rrdname}.rrd" "now-1y" "now" "86400" "Last 1 year - 1 day intervals"
gen_graph "${pngprefix}${pngname}-6.png" "${rrdprefix}${rrdname}.rrd" "now-4y" "now" "86400" "Last 4 years - 1 day intervals"
echo "</body> </html>"
) > "${pngname}.cgi"

131
rrd/dpinger_rrd_graph Executable file
View File

@@ -0,0 +1,131 @@
#!/bin/sh
if [ $# -ne 2 ]
then
echo "usage: $0 rrdname pngname"
exit 1
fi
rrdname="${1}"
pngname="${2}"
# Prefixes for rrd and png files. Note that if the prefix is a directory, it must incldue the trailing slash
# If no value is set, the files are located in the current directory
rrdprefix=
pngprefix=/tmp/
# Graph dimensions
graph_height=240
graph_width=720
#graph_height=280
#graph_width=840
# Preferred font
font="DejaVuSansMono"
# Latency breakpoints in milliseconds
latency_s0=20
latency_s1=40
latency_s2=80
latency_s3=160
latency_s4=320
# Latency colors
latency_c0="dddddd"
latency_c1="ddbbbb"
latency_c2="d4aaaa"
latency_c3="cc9999"
latency_c4="c38888"
latency_c5="bb7777"
# Standard deviation color & opacity
stddev_c="55333355"
# Loss color
loss_c="ee0000"
gen_graph()
{
png=$1
rrd=$2
start=$3
end=$4
step=$5
description=$6
rrdtool graph "${png}" \
--lazy \
--start "${start}" --end "${end}" --step "${step}" \
--height "${graph_height}" --width "${graph_width}" \
--title "Average Latency and Packet Loss - ${description}" \
--disable-rrdtool-tag \
--color BACK#ffffff \
--font DEFAULT:9:"${font}" \
--font AXIS:8:"${font}" \
\
DEF:latency_us="${rrd}":latency:AVERAGE:step="${step}" \
CDEF:latency=latency_us,1000,/ \
CDEF:latency_s0=latency,${latency_s0},MIN \
CDEF:latency_s1=latency,${latency_s1},MIN \
CDEF:latency_s2=latency,${latency_s2},MIN \
CDEF:latency_s3=latency,${latency_s3},MIN \
CDEF:latency_s4=latency,${latency_s4},MIN \
VDEF:latency_min=latency,MINIMUM \
VDEF:latency_max=latency,MAXIMUM \
VDEF:latency_avg=latency,AVERAGE \
VDEF:latency_last=latency,LAST \
\
DEF:stddev_us="${rrd}":stddev:AVERAGE:step="${step}" \
CDEF:stddev=stddev_us,1000,/ \
VDEF:stddev_min=stddev,MINIMUM \
VDEF:stddev_max=stddev,MAXIMUM \
VDEF:stddev_avg=stddev,AVERAGE \
VDEF:stddev_last=stddev,LAST \
\
DEF:loss="${rrd}":loss:AVERAGE:step="${step}" \
CDEF:loss_neg=loss,-1,* \
VDEF:loss_min=loss,MINIMUM \
VDEF:loss_max=loss,MAXIMUM \
VDEF:loss_avg=loss,AVERAGE \
VDEF:loss_last=loss,LAST \
\
COMMENT:" Min Max Avg Last\n" \
\
COMMENT:" " \
AREA:latency#${latency_c5} \
AREA:latency_s4#${latency_c4} \
AREA:latency_s3#${latency_c3} \
AREA:latency_s2#${latency_c2} \
AREA:latency_s1#${latency_c1} \
AREA:latency_s0#${latency_c0} \
LINE1:latency#000000:"Latency " \
GPRINT:"latency_min:%8.3lf ms\t" \
GPRINT:"latency_max:%8.3lf ms\t" \
GPRINT:"latency_avg:%8.3lf ms\t" \
GPRINT:"latency_last:%8.3lf ms\n" \
\
COMMENT:" " \
LINE1:stddev#${stddev_c}:"Stddev " \
GPRINT:"stddev_min:%8.3lf ms\t" \
GPRINT:"stddev_max:%8.3lf ms\t" \
GPRINT:"stddev_avg:%8.3lf ms\t" \
GPRINT:"stddev_last:%8.3lf ms\n" \
\
COMMENT:" " \
AREA:loss_neg#${loss_c}:"Loss " \
GPRINT:"loss_min:%4.1lf %%\t\t" \
GPRINT:"loss_max:%4.1lf %%\t\t" \
GPRINT:"loss_avg:%4.1lf %%\t\t" \
GPRINT:"loss_last:%4.1lf %%\n" \
\
COMMENT:" \n" \
GPRINT:"latency_last:Ending at %H\:%M on %B %d, %Y\r:strftime"
}
gen_graph "${pngprefix}${pngname}-1.png" "${rrdprefix}${rrdname}.rrd" "now-8h" "now" "60" "Last 8 hours - 1 minute intervals"
gen_graph "${pngprefix}${pngname}-2.png" "${rrdprefix}${rrdname}.rrd" "now-36h" "now" "300" "Last 36 hours - 5 minute intervals"
gen_graph "${pngprefix}${pngname}-3.png" "${rrdprefix}${rrdname}.rrd" "now-8d" "now" "1800" "Last 8 days - 30 minute intervals"
gen_graph "${pngprefix}${pngname}-4.png" "${rrdprefix}${rrdname}.rrd" "now-60d" "now" "14400" "Last 60 days - 4 hour intervals"
gen_graph "${pngprefix}${pngname}-5.png" "${rrdprefix}${rrdname}.rrd" "now-1y" "now" "86400" "Last 1 year - 1 day intervals"
gen_graph "${pngprefix}${pngname}-6.png" "${rrdprefix}${rrdname}.rrd" "now-4y" "now" "86400" "Last 4 years - 1 day intervals"

22
rrd/dpinger_rrd_update Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
if [ $# -lt 2 ]
then
echo "usage: $0 rrdname targetip [dpinger options]"
exit 1
fi
name="$1"
targetip="$2"
shift 2
options=$*
# Where the dpinger executable is located
dpinger=/usr/local/bin/dpinger
rrdfile="${name}.rrd"
${dpinger} -f ${options} -s 500m -t 60s -r 60s ${targetip} |
while read -r latency stddev loss; do
rrdtool update "${rrdfile}" -t latency:stddev:loss "N:$latency:$stddev:$loss"
done

16
rrd/sample.html Normal file
View File

@@ -0,0 +1,16 @@
<html>
<head><title>WAN Statistics</title></head>
<body>
<img src="/tmp/wan-1.png" alt="wan-1">
<p>
<img src="/tmp/wan-2.png" alt="wan-2">
<p>
<img src="/tmp/wan-3.png" alt="wan-3">
<p>
<img src="/tmp/wan-4.png" alt="wan-4">
<p>
<img src="/tmp/wan-5.png" alt="wan-5">
<p>
<img src="/tmp/wan-6.png" alt="wan-6">
</body>
</html>