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

25 Commits
v1.3 ... v1.10

Author SHA1 Message Date
Denny Page
fdbd4a1d96 Add a safety cast for 32 bit systems 2016-02-27 21:29:33 -08:00
Denny Page
6796fa0752 Fix integer overflow on 32 bit 2016-02-27 19:34:47 -08:00
Denny Page
24022ac098 Fix race condition with ultra low latency links 2016-02-25 20:09:30 -08:00
Denny Page
42c84e965e Time duration doesn't work in older versions of rrdtool 2016-02-21 14:38:53 -08:00
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
11 changed files with 552 additions and 91 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>

253
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
@@ -150,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
@@ -175,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;
@@ -187,6 +195,7 @@ static uint16_t sequence_limit;
//
// Termination handler
//
__attribute__ ((noreturn))
static void
term_handler(void)
{
@@ -206,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,
@@ -254,7 +260,7 @@ cksum(
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return ~sum;
return (uint16_t) ~sum;
}
@@ -280,7 +286,7 @@ llsqrt(
}
}
return s;
return (unsigned long) s;
}
@@ -292,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)
@@ -304,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;
@@ -336,17 +346,17 @@ 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)
array[next_slot].status = PACKET_STATUS_SENT;
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);
}
@@ -354,23 +364,21 @@ send_thread(
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;
@@ -378,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;
@@ -392,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;
@@ -432,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;
}
@@ -450,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;
@@ -463,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 += (unsigned long long) latency_usec * latency_usec;
}
else if (array[slot].status == PACKET_STATUS_SENT &&
ts_elapsed_usec(&array[slot].time_sent, &now) > loss_interval_usec)
@@ -477,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 - ((unsigned long long) avg * avg));
}
else
{
@@ -502,8 +511,10 @@ report(
//
// Report thread
//
__attribute__ ((noreturn))
static void *
report_thread(
__attribute__ ((unused))
void * arg)
{
char buf[OUTPUT_MAX];
@@ -511,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
@@ -534,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;
@@ -654,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];
@@ -671,7 +681,8 @@ 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)
@@ -688,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);
@@ -704,9 +715,6 @@ usocket_thread(
logger("close error in usocket thread: %d\n", errno);
}
}
// notreached
return arg;
}
@@ -731,7 +739,7 @@ get_time_arg_msec(
else if (*suffix == 's')
{
// Seconds
*value *= 1000;
t *= 1000;
suffix++;
}
@@ -774,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
//
@@ -781,7 +824,7 @@ 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");
@@ -791,6 +834,7 @@ usage(void)
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");
@@ -816,6 +860,7 @@ usage(void)
//
// Fatal error
//
__attribute__ ((noreturn, format (printf, 1, 2)))
static void
fatal(
const char * format,
@@ -852,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)
{
@@ -904,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;
@@ -935,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");
@@ -947,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);
@@ -1041,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;
}
}
@@ -1056,6 +1130,8 @@ main(
int pidfile_fd = -1;
pthread_t thread;
struct sigaction act;
int buflen = PACKET_BUFLEN;
ssize_t rs;
int r;
// Handle command line args
@@ -1069,6 +1145,7 @@ main(
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)
@@ -1077,6 +1154,7 @@ main(
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)
@@ -1096,8 +1174,8 @@ main(
}
// Drop privileges
r = setgid(getgid());
r = setuid(getuid());
(void) setgid(getgid());
(void) setuid(getuid());
// Create report file
if (report_name)
@@ -1131,7 +1209,6 @@ main(
fatal("cannot create unix domain socket\n");
}
(void) fcntl(usocket_fd, F_SETFL, FD_CLOEXEC);
(void) unlink(usocket_name);
memset(&uaddr, 0, sizeof(uaddr));
@@ -1194,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_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))
@@ -1209,8 +1286,8 @@ 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");
@@ -1225,13 +1302,21 @@ 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)
{
@@ -1255,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.

30
rrd/dpinger_rrd_create Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
if [ $# -ne 1 ]
then
echo "usage: $0 name"
exit 1
fi
name="$1"
rrdfile="${name}.rrd"
echo "Creating rrd file ${rrdfile}"
# Time duration method doesn't work in all versions of rrdtool
#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
# This method works in all versions
rrdtool create "${rrdfile}" --step 60 \
DS:latency:GAUGE:300:0:U \
DS:stddev:GAUGE:300:0:U \
DS:loss:GAUGE:300:0:100 \
RRA:AVERAGE:0.5:1:21600 \
RRA:AVERAGE:0.5:5:25920 \
RRA:AVERAGE:0.5:60:26352

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>