#include "options.h"
#include "optparse.h"
+#include <inttypes.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdint.h>
#include <time.h>
#include "seqmap.h"
#include <stddef.h>
#include <string.h>
+#include <time.h>
+
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#define EMAIL "david@schweikert.ch"
+#if HAVE_SO_TIMESTAMPNS
+#define CLOCKID CLOCK_REALTIME
+#endif
+
+#if !defined(CLOCKID)
+#if defined(CLOCK_MONOTONIC)
+#define CLOCKID CLOCK_MONOTONIC
+#else
+#define CLOCKID CLOCK_REALTIME
+#endif
+#endif
+
/*** Ping packet defines ***/
#define MAX_IP_PACKET 65536 /* (theoretical) max IP packet size */
* to be sent, or the timeout, if the last ping was sent */
struct host_entry* ev_prev; /* double linked list for the event-queue */
struct host_entry* ev_next; /* double linked list for the event-queue */
- struct timeval ev_time; /* time, after which this event should happen */
+ struct timespec ev_time; /* time, after which this event should happen */
int ev_type; /* event type */
int i; /* index into array */
int timeout; /* time to wait for response */
unsigned char running; /* unset when through sending */
unsigned char waiting; /* waiting for response */
- struct timeval last_send_time; /* time of last packet sent */
+ struct timespec last_send_time; /* time of last packet sent */
int num_sent; /* number of ping packets sent */
int num_recv; /* number of pings received (duplicates ignored) */
int num_recv_total; /* number of pings received, including duplicates */
int min_reply_i; /* shortest response time */
int total_time_i; /* sum of response times */
int discard_next_recv_i; /* don't count next received reply for split reporting */
- int* resp_times; /* individual response times */
+ int64_t* resp_times; /* individual response times */
#if defined(DEBUG) || defined(_DEBUG)
- int* sent_times; /* per-sent-ping timestamp */
+ int64_t* sent_times; /* per-sent-ping timestamp */
#endif /* DEBUG || _DEBUG */
} HOST_ENTRY;
num_pingreceived = 0, /* total pings received */
num_othericmprcvd = 0; /* total non-echo-reply ICMP received */
-struct timeval current_time; /* current time (pseudo) */
-struct timeval start_time;
-struct timeval end_time;
-struct timeval last_send_time; /* time last ping was sent */
-struct timeval next_report_time; /* time next -Q report is expected */
+struct timespec current_time; /* current time (pseudo) */
+struct timespec start_time;
+struct timespec end_time;
+struct timespec last_send_time; /* time last ping was sent */
+struct timespec next_report_time; /* time next -Q report is expected */
/* switches */
int generate_flag = 0; /* flag for IP list generation */
char* get_host_by_address(struct in_addr in);
void remove_job(HOST_ENTRY* h);
int send_ping(HOST_ENTRY* h);
-long timeval_diff(struct timeval* a, struct timeval* b);
-void timeval_add(struct timeval* a, long t_10u);
+void timespec_from_ns(struct timespec* a, uint64_t ns);
+int64_t timespec_ns(struct timespec* a);
+int64_t timespec_diff(struct timespec* a, struct timespec* b);
+long timespec_diff_10us(struct timespec* a, struct timespec* b);
+void timespec_add_10us(struct timespec* a, long t_10u);
void usage(int);
int wait_for_reply(long);
void print_per_system_stats(void);
void print_global_stats(void);
void main_loop();
void finish();
-char* sprint_tm(int t);
+const char* sprint_tm(int64_t t);
void ev_enqueue(HOST_ENTRY* h);
HOST_ENTRY* ev_dequeue();
void ev_remove(HOST_ENTRY* h);
/*** function definitions ***/
+static inline void copy_timespec(struct timespec *dst, const struct timespec *src)
+{
+ dst->tv_sec = src->tv_sec;
+ dst->tv_nsec = src->tv_nsec;
+}
+
+static inline long ns_to_tick(int64_t ns)
+{
+ ns /= 1000; // ns -> us
+ ns /= 10; // us -> 10us ticks
+ return (long)ns;
+}
+
/************************************************************
Function: main
#endif
}
-#if HAVE_SO_TIMESTAMP
+#if HAVE_SO_TIMESTAMPNS
{
int opt = 1;
if (socket4 >= 0) {
- if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
- perror("setting SO_TIMESTAMP option");
+ if (setsockopt(socket4, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS option");
}
}
#ifdef IPV6
if (socket6 >= 0) {
- if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) {
- perror("setting SO_TIMESTAMP option (IPv6)");
+ if (setsockopt(socket6, SOL_SOCKET, SO_TIMESTAMPNS, &opt, sizeof(opt))) {
+ perror("setting SO_TIMESTAMPNS option (IPv6)");
}
}
#endif
signal(SIGINT, finish);
- gettimeofday(&start_time, NULL);
+ clock_gettime(CLOCKID, &start_time);
current_time = start_time;
if (report_interval) {
next_report_time = start_time;
- timeval_add(&next_report_time, report_interval);
+ timespec_add_10us(&next_report_time, report_interval);
}
last_send_time.tv_sec = current_time.tv_sec - 10000;
#if defined(DEBUG) || defined(_DEBUG)
if (randomly_lose_flag)
- srandom(start_time.tv_usec);
+ srandom(start_time.tv_nsec);
#endif /* DEBUG || _DEBUG */
seqmap_init();
while (ev_first) {
/* Any event that can be processed now ? */
- if (ev_first->ev_time.tv_sec < current_time.tv_sec || (ev_first->ev_time.tv_sec == current_time.tv_sec && ev_first->ev_time.tv_usec < current_time.tv_usec)) {
+ if (timespec_diff_10us(&ev_first->ev_time, ¤t_time) < 0) {
/* Event type: ping */
if (ev_first->ev_type == EV_TYPE_PING) {
/* Make sure that we don't ping more than once every "interval" */
- lt = timeval_diff(¤t_time, &last_send_time);
+ lt = timespec_diff_10us(¤t_time, &last_send_time);
if (lt < interval)
goto wait_for_reply;
/* Normal mode: schedule retry */
if (h->waiting < retry + 1) {
h->ev_type = EV_TYPE_PING;
- h->ev_time.tv_sec = last_send_time.tv_sec;
- h->ev_time.tv_usec = last_send_time.tv_usec;
- timeval_add(&h->ev_time, h->timeout);
+ copy_timespec(&h->ev_time, &last_send_time);
+ timespec_add_10us(&h->ev_time, h->timeout);
ev_enqueue(h);
if (backoff_flag) {
/* Normal mode: schedule timeout for last retry */
else {
h->ev_type = EV_TYPE_TIMEOUT;
- h->ev_time.tv_sec = last_send_time.tv_sec;
- h->ev_time.tv_usec = last_send_time.tv_usec;
- timeval_add(&h->ev_time, h->timeout);
+ copy_timespec(&h->ev_time, &last_send_time);
+ timespec_add_10us(&h->ev_time, h->timeout);
ev_enqueue(h);
}
}
/* Loop and count mode: schedule next ping */
else if (loop_flag || (count_flag && h->num_sent < count)) {
h->ev_type = EV_TYPE_PING;
- h->ev_time.tv_sec = last_send_time.tv_sec;
- h->ev_time.tv_usec = last_send_time.tv_usec;
- timeval_add(&h->ev_time, perhost_interval);
+ copy_timespec(&h->ev_time, &last_send_time);
+ timespec_add_10us(&h->ev_time, perhost_interval);
ev_enqueue(h);
}
/* Count mode: schedule timeout after last ping */
else if (count_flag && h->num_sent >= count) {
h->ev_type = EV_TYPE_TIMEOUT;
- h->ev_time.tv_sec = last_send_time.tv_sec;
- h->ev_time.tv_usec = last_send_time.tv_usec;
- timeval_add(&h->ev_time, h->timeout);
+ copy_timespec(&h->ev_time, &last_send_time);
+ timespec_add_10us(&h->ev_time, h->timeout);
ev_enqueue(h);
}
}
wait_time = 0;
}
else {
- wait_time = timeval_diff(&ev_first->ev_time, ¤t_time);
+ wait_time = timespec_diff_10us(&ev_first->ev_time, ¤t_time);
if (wait_time < 0)
wait_time = 0;
}
/* make sure that we wait enough, so that the inter-ping delay is
* bigger than 'interval' */
if (wait_time < interval) {
- lt = timeval_diff(¤t_time, &last_send_time);
+ lt = timespec_diff_10us(¤t_time, &last_send_time);
if (lt < interval) {
wait_time = interval - lt;
}
#if defined(DEBUG) || defined(_DEBUG)
if (trace_flag) {
- fprintf(stderr, "next event in %d ms (%s)\n", wait_time / 100, ev_first->host);
+ fprintf(stderr, "next event in %ld ms (%s)\n", wait_time / 100, ev_first->host);
}
#endif
}
/* Make sure we don't wait too long, in case a report is expected */
if (report_interval && (loop_flag || count_flag)) {
- wait_time_next_report = timeval_diff(&next_report_time, ¤t_time);
+ wait_time_next_report = timespec_diff_10us(&next_report_time, ¤t_time);
if (wait_time_next_report < wait_time) {
wait_time = wait_time_next_report;
if (wait_time < 0) {
; /* process other replies in the queue */
}
- gettimeofday(¤t_time, NULL);
+ clock_gettime(CLOCKID, ¤t_time);
/* Print report */
- if (report_interval && (loop_flag || count_flag) && (timeval_diff(¤t_time, &next_report_time) >= 0)) {
+ if (report_interval && (loop_flag || count_flag) && (timespec_diff_10us(¤t_time, &next_report_time) >= 0)) {
if (netdata_flag)
print_netdata();
else
print_per_system_splits();
- while (timeval_diff(¤t_time, &next_report_time) >= 0)
- timeval_add(&next_report_time, report_interval);
+ while (timespec_diff_10us(¤t_time, &next_report_time) >= 0)
+ timespec_add_10us(&next_report_time, report_interval);
}
}
}
int i;
HOST_ENTRY* h;
- gettimeofday(&end_time, NULL);
+ clock_gettime(CLOCKID, &end_time);
/* tot up unreachables */
for (i = 0; i < num_hosts; i++) {
{
int i, j, avg, outage_ms;
HOST_ENTRY* h;
- int resp;
+ int64_t resp;
fflush(stdout);
if (report_all_rtts_flag) {
for (j = 0; j < h->num_sent; j++) {
if ((resp = h->resp_times[j]) >= 0)
- fprintf(stderr, " %d.%02d", resp / 100, resp % 100);
+ fprintf(stderr, " %s", sprint_tm(resp));
else
fprintf(stderr, " -");
}
/* if we just sent the probe and didn't receive a reply, we shouldn't count it */
h->discard_next_recv_i = 0;
- if (h->waiting && timeval_diff(¤t_time, &h->last_send_time) < h->timeout) {
+ if (h->waiting && timespec_diff_10us(¤t_time, &h->last_send_time) < h->timeout) {
if (h->num_sent_i) {
h->num_sent_i--;
h->discard_next_recv_i = 1;
if (!sent_charts) {
printf("CHART fping.%s_latency '' 'FPing Latency for host %s' ms '%s' fping.latency area 110000 %d\n", h->name, h->host, h->name, report_interval / 100000);
- printf("DIMENSION min minimum absolute 10 1000\n");
- printf("DIMENSION max maximum absolute 10 1000\n");
- printf("DIMENSION avg average absolute 10 1000\n");
+ printf("DIMENSION min minimum absolute 1 1000000\n");
+ printf("DIMENSION max maximum absolute 1 1000000\n");
+ printf("DIMENSION avg average absolute 1 1000000\n");
}
printf("BEGIN fping.%s_latency\n", h->name);
if (verbose_flag || per_recv_flag)
fprintf(stderr, "\n");
- gettimeofday(¤t_time, NULL);
+ clock_gettime(CLOCKID, ¤t_time);
curr_tm = localtime((time_t*)¤t_time.tv_sec);
fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour,
curr_tm->tm_min, curr_tm->tm_sec);
/* if we just sent the probe and didn't receive a reply, we shouldn't count it */
h->discard_next_recv_i = 0;
- if (h->waiting && timeval_diff(¤t_time, &h->last_send_time) < h->timeout) {
+ if (h->waiting && timespec_diff_10us(¤t_time, &h->last_send_time) < h->timeout) {
if (h->num_sent_i) {
h->num_sent_i--;
h->discard_next_recv_i = 1;
sprint_tm((int)(sum_replies / total_replies)));
fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply));
fprintf(stderr, " %12.3f sec (elapsed real time)\n",
- timeval_diff(&end_time, &start_time) / 100000.0);
+ timespec_diff(&end_time, &start_time) / 1e9);
fprintf(stderr, "\n");
}
int myseq;
int ret = 1;
- gettimeofday(&h->last_send_time, NULL);
+ clock_gettime(CLOCKID, &h->last_send_time);
myseq = seqmap_add(h->i, h->num_sent, &h->last_send_time);
#if defined(DEBUG) || defined(_DEBUG)
#if defined(DEBUG) || defined(_DEBUG)
if (sent_times_flag)
- h->sent_times[h->num_sent] = timeval_diff(&h->last_send_time, &start_time);
+ h->sent_times[h->num_sent] = timespec_diff(&h->last_send_time, &start_time);
#endif
}
int socket_can_read(struct timeval* timeout)
{
int nfound;
- fd_set readset, writeset;
+ fd_set readset;
int socketmax;
#ifndef IPV6
select_again:
FD_ZERO(&readset);
- FD_ZERO(&writeset);
if(socket4 >= 0) FD_SET(socket4, &readset);
#ifdef IPV6
if(socket6 >= 0) FD_SET(socket6, &readset);
#endif
- nfound = select(socketmax + 1, &readset, &writeset, NULL, timeout);
+ nfound = select(socketmax + 1, &readset, NULL, NULL, timeout);
if (nfound < 0) {
if (errno == EINTR) {
/* interrupted system call: redo the select */
}
int receive_packet(int socket,
- struct timeval* reply_timestamp,
+ struct timespec* reply_timestamp,
struct sockaddr* reply_src_addr,
size_t reply_src_addr_len,
char* reply_buf,
0
};
int timestamp_set = 0;
+#if HAVE_SO_TIMESTAMPNS
struct cmsghdr* cmsg;
+#endif
recv_len = recvmsg(socket, &recv_msghdr, 0);
if (recv_len <= 0) {
return 0;
}
-#if HAVE_SO_TIMESTAMP
+#if HAVE_SO_TIMESTAMPNS
/* ancilliary data */
for (cmsg = CMSG_FIRSTHDR(&recv_msghdr);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&recv_msghdr, cmsg)) {
- if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
memcpy(reply_timestamp, CMSG_DATA(cmsg), sizeof(*reply_timestamp));
timestamp_set = 1;
}
#endif
if (!timestamp_set) {
- gettimeofday(reply_timestamp, NULL);
+ clock_gettime(CLOCKID, reply_timestamp);
}
#if defined(DEBUG) || defined(_DEBUG)
HOST_ENTRY* h;
long this_reply;
int this_count;
- struct timeval* sent_time;
- struct timeval recv_time;
+ struct timespec* sent_time;
+ struct timespec recv_time = {0};
SEQMAP_VALUE* seqmap_value;
unsigned short id;
unsigned short seq;
return 1;
}
- gettimeofday(¤t_time, NULL);
+ clock_gettime(CLOCKID, ¤t_time);
/* Process ICMP packet and retrieve id/seq */
if (response_addr.ss_family == AF_INET) {
h = table[n];
sent_time = &seqmap_value->ping_ts;
this_count = seqmap_value->ping_count;
- this_reply = timeval_diff(&recv_time, sent_time);
+ this_reply = timespec_diff(&recv_time, sent_time);
/* discard reply if delay is larger than timeout
* (see also: github #32) */
- if (this_reply > h->timeout) {
+ if (ns_to_tick(this_reply) > h->timeout) {
return 1;
}
if (per_recv_flag) {
if (timestamp_flag) {
- printf("[%lu.%06lu] ",
+ printf("[%lu.%09lu] ",
(unsigned long)recv_time.tv_sec,
- (unsigned long)recv_time.tv_usec);
+ (unsigned long)recv_time.tv_nsec);
}
avg = h->total_time / h->num_recv;
printf("%s%s : [%d], %d bytes, %s ms",
void add_addr(char* name, char* host, struct sockaddr* ipaddr, socklen_t ipaddr_len)
{
HOST_ENTRY* p;
- int n, *i;
+ int n;
+ int64_t *i;
p = (HOST_ENTRY*)malloc(sizeof(HOST_ENTRY));
if (!p)
/* array for response time results */
if (!loop_flag) {
- i = (int*)malloc(trials * sizeof(int));
+ i = (int64_t*)malloc(trials * sizeof(int64_t));
if (!i)
crash_and_burn("can't allocate resp_times array");
#if defined(DEBUG) || defined(_DEBUG)
/* likewise for sent times */
if (sent_times_flag) {
- i = (int*)malloc(trials * sizeof(int));
+ i = (int64_t*)malloc(trials * sizeof(int64_t));
if (!i)
crash_and_burn("can't allocate sent_times array");
/* schedule first ping */
p->ev_type = EV_TYPE_PING;
p->ev_time.tv_sec = 0;
- p->ev_time.tv_usec = 0;
+ p->ev_time.tv_nsec = 0;
ev_enqueue(p);
num_hosts++;
/************************************************************
- Function: timeval_diff
+ Function: timespec_diff
*************************************************************
- Inputs: struct timeval *a, struct timeval *b
+ Inputs: struct timespec *a, struct timespec *b
- Returns: long
+ Returns: int64_t
Description:
- timeval_diff now returns result in hundredths of milliseconds
- ie, tens of microseconds
+ timespec_diff now returns result in nanoseconds
************************************************************/
-long timeval_diff(struct timeval* a, struct timeval* b)
+int64_t timespec_ns(struct timespec* a)
{
- long sec_diff = a->tv_sec - b->tv_sec;
- if (sec_diff == 0) {
- return (a->tv_usec - b->tv_usec) / 10;
- }
- else if (sec_diff < 100) {
- return (sec_diff * 1000000 + a->tv_usec - b->tv_usec) / 10;
- }
- else {
- /* For such large differences, we don't really care about the microseconds... */
- return sec_diff * 100000;
- }
+ return (a->tv_sec * 1000000000LL) + a->tv_nsec;
+}
+int64_t timespec_10us(struct timespec* a)
+{
+ return (a->tv_sec * 100000) + a->tv_nsec / 10000;
+}
+void timespec_from_ns(struct timespec* a, uint64_t ns)
+{
+ a->tv_sec = ns / 1000000000ULL;
+ a->tv_nsec = ns % 1000000000ULL;
+}
+
+int64_t timespec_diff(struct timespec* a, struct timespec* b)
+{
+ return timespec_ns(a) - timespec_ns(b);
+}
+long timespec_diff_10us(struct timespec* a, struct timespec* b)
+{
+ return (long)(timespec_10us(a) - timespec_10us(b));
}
/************************************************************
- Function: timeval_add
+ Function: timespec_add
*************************************************************/
-void timeval_add(struct timeval* a, long t_10u)
+void timespec_add_10us(struct timespec* a, long t_10u)
{
- t_10u *= 10;
- a->tv_sec += (t_10u + a->tv_usec) / 1000000;
- a->tv_usec = (t_10u + a->tv_usec) % 1000000;
+ uint64_t ns = t_10u;
+ ns *= 10; // tick -> us
+ ns *= 1000; // us -> ns
+ a->tv_sec += (ns + a->tv_nsec) / 1000000000ULL;
+ a->tv_nsec = (ns + a->tv_nsec) % 1000000000ULL;
}
/************************************************************
************************************************************/
-char* sprint_tm(int t)
+const char* sprint_tm(int64_t ns)
{
- static char buf[12];
+ static char buf[10];
+ double t = (double)ns / 1e6;
- if (t < 0) {
+ if (t < 0.0) {
/* negative (unexpected) */
- sprintf(buf, "%.2g", (double)t / 100);
+ sprintf(buf, "%.2g", (double)t / 1e9);
}
- else if (t < 100) {
+ else if (t < 1.0) {
/* <= 0.99 ms */
- sprintf(buf, "0.%02d", t);
+ sprintf(buf, "%.6f", t);
}
- else if (t < 1000) {
+ else if (t < 10.0) {
/* 1.00 - 9.99 ms */
- sprintf(buf, "%d.%02d", t / 100, t % 100);
+ sprintf(buf, "%.2f", t);
}
- else if (t < 10000) {
+ else if (t < 100.0) {
/* 10.0 - 99.9 ms */
- sprintf(buf, "%d.%d", t / 100, (t % 100) / 10);
+ sprintf(buf, "%.1f", t);
}
- else if (t < 100000000) {
+ else if (t < 1000000.0) {
/* 100 - 1'000'000 ms */
- sprintf(buf, "%d", t / 100);
+ sprintf(buf, "%d", (int)t);
}
else {
- sprintf(buf, "%.2e", (double)(t / 100));
+ sprintf(buf, "%.2e", t);
}
return (buf);
#if defined(DEBUG) || defined(_DEBUG)
if (trace_flag) {
- long st = timeval_diff(&h->ev_time, ¤t_time);
- fprintf(stderr, "Enqueue: host=%s, when=%d ms (%d, %d)\n", h->host, st / 100, h->ev_time.tv_sec, h->ev_time.tv_usec);
+ long st = timespec_diff_10us(&h->ev_time, ¤t_time);
+ fprintf(stderr, "Enqueue: host=%s, when=%ld ms (%ld, %ld)\n", h->host, st / 100, h->ev_time.tv_sec, h->ev_time.tv_nsec);
}
#endif
}
/* Insert on tail? */
- if (h->ev_time.tv_sec > ev_last->ev_time.tv_sec || (h->ev_time.tv_sec == ev_last->ev_time.tv_sec && h->ev_time.tv_usec >= ev_last->ev_time.tv_usec)) {
+ if (timespec_diff(&h->ev_time, &ev_last->ev_time) >= 0) {
h->ev_next = NULL;
h->ev_prev = ev_last;
ev_last->ev_next = h;
i = ev_last;
while (1) {
i_prev = i->ev_prev;
- if (i_prev == NULL || h->ev_time.tv_sec > i_prev->ev_time.tv_sec || (h->ev_time.tv_sec == i_prev->ev_time.tv_sec && h->ev_time.tv_usec >= i_prev->ev_time.tv_usec)) {
+ if (i_prev == NULL || timespec_diff(&h->ev_time, &i_prev->ev_time) >= 0) {
h->ev_prev = i_prev;
h->ev_next = i;
i->ev_prev = h;