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

24 Commits
v1.5 ... v2.0

Author SHA1 Message Date
Denny Page
c276feb339 Confirm that the rrd file is writable before starting 2016-03-01 22:14:36 -08:00
Denny Page
ef21655e77 Change title 2016-03-01 22:14:13 -08:00
Denny Page
2d2d21892a Change the default time period and send interval from 30s/250m to 60s/500m
Check time period vs send interval and loss interval to ensure there is always one resolved slot
2016-03-01 22:11:03 -08:00
Denny Page
a24c0cd0d0 Add option to set receive thread scheduling class
Don't call fatal with a NULL format
2016-03-01 21:58:10 -08:00
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
10 changed files with 492 additions and 93 deletions

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>

193
dpinger.c
View File

@@ -74,16 +74,17 @@ static const char * pidfile_name = NULL;
// Flags
static unsigned int flag_rewind = 0;
static unsigned int flag_syslog = 0;
static unsigned int flag_priority = 0;
// String representation of target
#define ADDR_STR_MAX (INET6_ADDRSTRLEN + IF_NAMESIZE + 1)
static char dest_str[ADDR_STR_MAX];
// Time period over which we are averaging results in ms
static unsigned long time_period_msec = 30000;
static unsigned long time_period_msec = 60000;
// Interval between sends in ms
static unsigned long send_interval_msec = 250;
static unsigned long send_interval_msec = 500;
// Interval before a sequence is initially treated as lost
// Input from command line in ms and used in us
@@ -150,8 +151,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
@@ -180,7 +181,7 @@ typedef struct
#define IPV6_ICMP_DATA_MAX (IP_MAXPACKET - sizeof(icmphdr_t))
#define PACKET_BUFLEN (IP_MAXPACKET + 256)
unsigned long echo_data_len = 0;
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;
@@ -195,6 +196,7 @@ static uint16_t sequence_limit;
//
// Termination handler
//
__attribute__ ((noreturn))
static void
term_handler(void)
{
@@ -214,10 +216,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,
@@ -262,7 +261,7 @@ cksum(
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return ~sum;
return (uint16_t) ~sum;
}
@@ -288,7 +287,7 @@ llsqrt(
}
}
return s;
return (unsigned long) s;
}
@@ -300,7 +299,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)
@@ -312,18 +311,21 @@ 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
@@ -351,34 +353,33 @@ send_thread(
array[next_slot].status = PACKET_STATUS_EMPTY;
sched_yield();
clock_gettime(CLOCK_MONOTONIC, &array[next_slot].time_sent);
r = sendto(send_sock, echo_request, echo_request_len, 0, (struct sockaddr *) &dest_addr, dest_addr_len);
if (r == -1)
clock_gettime(CLOCK_MONOTONIC, &array[next_slot].time_sent);
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);
}
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)
{
struct sockaddr_storage src_addr;
socklen_t src_addr_len;
unsigned int len;
ssize_t len;
icmphdr_t * icmp;
struct timespec now;
unsigned int array_slot;
@@ -387,7 +388,7 @@ recv_thread(
{
src_addr_len = sizeof(src_addr);
len = recvfrom(recv_sock, echo_reply, echo_reply_len, 0, (struct sockaddr *) &src_addr, &src_addr_len);
if (len == (unsigned int) -1)
if (len == -1)
{
logger("%s%s: recvfrom error: %d\n", identifier, dest_str, errno);
continue;
@@ -400,15 +401,15 @@ recv_thread(
size_t ip_len;
// With IPv4, we get the entire IP packet
if (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 = echo_reply;
ip_len = ip->ip_hl << 2;
ip_len = (size_t) ip->ip_hl << 2;
icmp = (void *) ip + ip_len;
icmp = (void *) ((char *) ip + ip_len);
len -= ip_len;
}
else
@@ -418,7 +419,7 @@ recv_thread(
}
// This should never happen
if (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;
@@ -440,9 +441,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;
}
@@ -474,7 +472,7 @@ report(
packets_received++;
latency_usec = array[slot].latency_usec;
total_latency_usec += latency_usec;
total_latency_usec2 += 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)
@@ -492,7 +490,7 @@ report(
// stddev = sqrt((sum(rtt^2) / packets) - (sum(rtt) / packets)^2)
*average_latency_usec = avg;
*latency_deviation = llsqrt(avg2 - (avg * avg));
*latency_deviation = llsqrt(avg2 - ((unsigned long long) avg * avg));
}
else
{
@@ -514,8 +512,10 @@ report(
//
// Report thread
//
__attribute__ ((noreturn))
static void *
report_thread(
__attribute__ ((unused))
void * arg)
{
char buf[OUTPUT_MAX];
@@ -523,7 +523,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
@@ -546,33 +547,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;
@@ -666,16 +666,15 @@ alert_thread(
}
}
}
// notreached
return arg;
}
//
// Unix socket thread
//
__attribute__ ((noreturn))
static void *
usocket_thread(
__attribute__ ((unused))
void * arg)
{
char buf[OUTPUT_MAX];
@@ -683,7 +682,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)
@@ -700,14 +700,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);
@@ -716,9 +716,6 @@ usocket_thread(
logger("close error in usocket thread: %d\n", errno);
}
}
// notreached
return arg;
}
@@ -828,15 +825,16 @@ 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] [-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, " %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);
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, " -P priority scheduling for receive thread (requires root)\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 5x send interval)\n");
fprintf(stderr, " -t time period over which results are averaged (default 30s)\n");
fprintf(stderr, " -s time interval between echo requests (default 500ms)\n");
fprintf(stderr, " -l time interval before packets are treated as lost (default 4x send interval)\n");
fprintf(stderr, " -t time period over which results are averaged (default 60s)\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");
@@ -864,19 +862,17 @@ usage(void)
//
// Fatal error
//
__attribute__ ((noreturn, format (printf, 1, 2)))
static void
fatal(
const char * format,
...)
{
if (format)
{
va_list args;
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(EXIT_FAILURE);
}
@@ -900,7 +896,7 @@ parse_args(
progname = argv[0];
while((opt = getopt(argc, argv, "fRSB:s:l:t:r:d:o:A:D:L:C:i:u:p:")) != -1)
while((opt = getopt(argc, argv, "fRSPB:s:l:t:r:d:o:A:D:L:C:i:u:p:")) != -1)
{
switch (opt)
{
@@ -916,6 +912,10 @@ parse_args(
flag_syslog = 1;
break;
case 'P':
flag_priority = 1;
break;
case 'B':
bind_arg = optarg;
break;
@@ -1003,7 +1003,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);
@@ -1021,7 +1021,7 @@ parse_args(
default:
usage();
fatal(NULL);
exit(EXIT_FAILURE);
}
}
@@ -1029,7 +1029,7 @@ parse_args(
if (argc != optind + 1)
{
usage();
fatal(NULL);
exit(EXIT_FAILURE);
}
dest_arg = argv[optind];
@@ -1039,17 +1039,17 @@ parse_args(
fatal("no activity enabled\n");
}
// Ensure we have something to average over
if (time_period_msec < send_interval_msec)
// Ensure there is a minimum of one resolved slot at all times
if (time_period_msec <= send_interval_msec * 2 + loss_interval_msec)
{
fatal("time period cannot be less than send interval\n");
fatal("the time period must be greater than twice the send interval plus the loss interval\n");
}
// Ensure we don't have sequence space issues. This really should only be hit by
// complete accident. Even a ratio of 16384:1 would be excessive.
if (time_period_msec / send_interval_msec > 65536)
{
fatal("ratio of time period to send interval cannot exceed 65536:1\n");
fatal("the ratio of time period to send interval cannot exceed 65536:1\n");
}
// Check destination address
@@ -1105,14 +1105,14 @@ parse_args(
{
if (echo_data_len > IPV4_ICMP_DATA_MAX)
{
fatal("data length too large for IPv4 - maximum is %u bytes\n", 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", IPV6_ICMP_DATA_MAX);
fatal("data length too large for IPv6 - maximum is %u bytes\n", (unsigned) IPV6_ICMP_DATA_MAX);
}
}
@@ -1134,6 +1134,7 @@ main(
pthread_t thread;
struct sigaction act;
int buflen = PACKET_BUFLEN;
ssize_t rs;
int r;
// Handle command line args
@@ -1176,8 +1177,8 @@ main(
}
// Drop privileges
r = setgid(getgid());
r = setuid(getuid());
(void) setgid(getgid());
(void) setuid(getuid());
// Create report file
if (report_name)
@@ -1211,7 +1212,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));
@@ -1274,14 +1274,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))
@@ -1289,8 +1289,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");
@@ -1305,7 +1305,7 @@ 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)
{
@@ -1323,7 +1323,7 @@ main(
// Set the default loss interval
if (loss_interval_msec == 0)
{
loss_interval_msec = send_interval_msec * 5;
loss_interval_msec = send_interval_msec * 4;
}
loss_interval_usec = loss_interval_msec * 1000;
@@ -1349,13 +1349,13 @@ main(
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
@@ -1366,6 +1366,27 @@ main(
fatal("cannot create recv thread\n");
}
// Set priority on recv thread if requested
if (flag_priority)
{
struct sched_param thread_sched_param;
r = sched_get_priority_min(SCHED_RR);
if (r == -1)
{
perror("sched_get_priority_min");
fatal("cannot determin minimum shceduling priority for SCHED_RR\n");
}
thread_sched_param.sched_priority = r;
r = pthread_setschedparam(thread, SCHED_RR, &thread_sched_param);
if (r != 0)
{
perror("pthread_setschedparam");
fatal("cannot set receive thread priority\n");
}
}
// Create send thread
r = pthread_create(&thread, NULL, &send_thread, NULL);
if (r != 0)

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"

27
rrd/dpinger_rrd_update Executable file
View File

@@ -0,0 +1,27 @@
#!/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"
if [ \! -w ${rrdfile} ]
then
echo "$0: file \"${rrdfile}\" does not exist or is not writable"
exit 1
fi
${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>>Latency Statistics for WAN</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>