summaryrefslogtreecommitdiff
path: root/src/tools/oping
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/oping')
-rw-r--r--src/tools/oping/CMakeLists.txt28
-rw-r--r--src/tools/oping/oping.c26
-rw-r--r--src/tools/oping/oping_client.c253
-rw-r--r--src/tools/oping/oping_server.c75
4 files changed, 300 insertions, 82 deletions
diff --git a/src/tools/oping/CMakeLists.txt b/src/tools/oping/CMakeLists.txt
deleted file mode 100644
index 31a4f961..00000000
--- a/src/tools/oping/CMakeLists.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-include_directories(${CMAKE_SOURCE_DIR}/include)
-include_directories(${CMAKE_BINARY_DIR}/include)
-
-get_filename_component(CURRENT_SOURCE_PARENT_DIR
- ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
-
-include_directories(${CURRENT_SOURCE_PARENT_DIR})
-
-find_library(LIBM_LIBRARIES m)
-if(NOT LIBM_LIBRARIES)
- message(FATAL_ERROR "libm not found")
-endif()
-
-mark_as_advanced(LIBM_LIBRARIES)
-
-set(SOURCE_FILES
- # Add source files here
- oping.c
- )
-
-add_executable(oping ${SOURCE_FILES})
-
-target_link_libraries(oping LINK_PUBLIC ${LIBM_LIBRARIES} ouroboros-dev)
-
-install(TARGETS oping RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/oping/oping.c b/src/tools/oping/oping.c
index 87c1ee18..763c0d62 100644
--- a/src/tools/oping/oping.c
+++ b/src/tools/oping/oping.c
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2024
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
@@ -72,12 +72,16 @@
"and reports the Round Trip Time (RTT)\n" \
"\n" \
" -l, --listen Run in server mode\n" \
+" --poll Server uses polling (lower latency)\n" \
+" --busy Server uses busy-poll (single flow)\n" \
"\n" \
" -c, --count Number of packets\n" \
" -d, --duration Duration of the test (default 1s)\n" \
+" -f, --flood Send back-to-back without waiting\n" \
+" -F, --flood-busy Flood with busy-polling (lower latency)\n" \
" -i, --interval Interval (default 1000ms)\n" \
" -n, --server-name Name of the oping server\n" \
-" -q, --qos QoS (raw, best, video, voice, data)\n" \
+" -q, --qos QoS (raw, best, video, voice, data)\n" \
" -s, --size Payload size (B, default 64)\n" \
" -Q, --quiet Only print final statistics\n" \
" -D, --timeofday Print time of day before each line\n" \
@@ -90,6 +94,8 @@ struct {
uint32_t count;
int size;
bool timestamp;
+ bool flood;
+ bool flood_busy;
qosspec_t qs;
/* stats */
@@ -114,6 +120,8 @@ struct {
pthread_mutex_t lock;
bool quiet;
+ bool poll;
+ bool busy;
pthread_t cleaner_pt;
pthread_t accept_pt;
@@ -172,9 +180,13 @@ int main(int argc,
client.size = 64;
client.count = INT_MAX;
client.timestamp = false;
+ client.flood = false;
+ client.flood_busy = false;
client.qs = qos_raw;
client.quiet = false;
server.quiet = false;
+ server.poll = false;
+ server.busy = false;
while (argc > 0) {
if ((strcmp(*argv, "-i") == 0 ||
@@ -212,6 +224,12 @@ int main(int argc,
} else if (strcmp(*argv, "-l") == 0 ||
strcmp(*argv, "--listen") == 0) {
serv = true;
+ } else if (strcmp(*argv, "-f") == 0 ||
+ strcmp(*argv, "--flood") == 0) {
+ client.flood = true;
+ } else if (strcmp(*argv, "-F") == 0 ||
+ strcmp(*argv, "--flood-busy") == 0) {
+ client.flood_busy = true;
} else if (strcmp(*argv, "-D") == 0 ||
strcmp(*argv, "--timeofday") == 0) {
client.timestamp = true;
@@ -219,6 +237,10 @@ int main(int argc,
strcmp(*argv, "--quiet") == 0) {
client.quiet = true;
server.quiet = true;
+ } else if (strcmp(*argv, "--poll") == 0) {
+ server.poll = true;
+ } else if (strcmp(*argv, "--busy") == 0) {
+ server.busy = true;
} else {
goto fail;
}
diff --git a/src/tools/oping/oping_client.c b/src/tools/oping/oping_client.c
index 5a9e03dc..23807f65 100644
--- a/src/tools/oping/oping_client.c
+++ b/src/tools/oping/oping_client.c
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2024
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
@@ -53,6 +53,40 @@ void shutdown_client(int signo, siginfo_t * info, void * c)
}
}
+static void update_rtt_stats(double ms)
+{
+ double d;
+
+ if (ms < client.rtt_min)
+ client.rtt_min = ms;
+ if (ms > client.rtt_max)
+ client.rtt_max = ms;
+
+ d = (ms - client.rtt_avg);
+ client.rtt_avg += d / client.rcvd;
+ client.rtt_m2 += d * (ms - client.rtt_avg);
+}
+
+static double rtt_val(double ms)
+{
+ return ms < 0.1 ? ms * 1000 : ms;
+}
+
+static const char * rtt_unit(double ms)
+{
+ return ms < 0.1 ? "µs" : "ms";
+}
+
+static void print_rtt(int len, int seq,
+ double ms, const char * suf)
+{
+ printf("%d bytes from %s: seq=%d "
+ "time=%.3f %s%s\n",
+ len, client.s_apn, seq,
+ rtt_val(ms), rtt_unit(ms),
+ suf != NULL ? suf : "");
+}
+
void * reader(void * o)
{
struct timespec timeout = {client.interval / 1000 + 2, 0};
@@ -64,7 +98,6 @@ void * reader(void * o)
int fd = *((int *) o);
int msg_len = 0;
double ms = 0;
- double d = 0;
uint32_t exp_id = 0;
fccntl(fd, FLOWSRCVTIMEO, &timeout);
@@ -114,22 +147,12 @@ void * reader(void * o)
(size_t) rtc.tv_nsec / 1000);
}
- printf("%d bytes from %s: seq=%d time=%.3f ms%s\n",
- msg_len,
- client.s_apn,
- ntohl(msg->id),
- ms,
- id < exp_id ? " [out-of-order]" : "");
+ print_rtt(msg_len, ntohl(msg->id), ms,
+ id < exp_id ?
+ " [out-of-order]" : NULL);
}
- if (ms < client.rtt_min)
- client.rtt_min = ms;
- if (ms > client.rtt_max)
- client.rtt_max = ms;
-
- d = (ms - client.rtt_avg);
- client.rtt_avg += d / client.rcvd;
- client.rtt_m2 += d * (ms - client.rtt_avg);
+ update_rtt_stats(ms);
if (id >= exp_id)
exp_id = id + 1;
@@ -204,13 +227,174 @@ static void client_fini(void)
return;
}
+static void print_stats(struct timespec * tic,
+ struct timespec * toc)
+{
+ printf("\n");
+ printf("--- %s ping statistics ---\n", client.s_apn);
+ printf("%d packets transmitted, ", client.sent);
+ printf("%d received, ", client.rcvd);
+ printf("%zd out-of-order, ", client.ooo);
+ printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
+ ceil(100 - (100 * (client.rcvd / (float) client.sent))));
+ printf("time: %.3f ms\n", ts_diff_us(toc, tic) / 1000.0);
+
+ if (client.rcvd > 0) {
+ double a = client.rtt_avg;
+ double f = a < 0.1 ? 1000 : 1;
+ printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
+ client.rtt_min * f, client.rtt_avg * f,
+ client.rtt_max * f);
+ if (client.rcvd > 1)
+ printf("%.3f %s\n",
+ sqrt(client.rtt_m2 /
+ (client.rcvd - 1)) * f,
+ rtt_unit(a));
+ else
+ printf("NaN %s\n", rtt_unit(a));
+ }
+}
+
+static int flood_busy_ping(int fd)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ struct timespec sent;
+ struct timespec rcvd;
+ double ms;
+ int n;
+
+ memset(buf, 0, client.size);
+
+ fccntl(fd, FLOWSFLAGS,
+ FLOWFRDWR | FLOWFRNOPART | FLOWFRNOBLOCK);
+
+ if (!client.quiet)
+ printf("Pinging %s with %d bytes"
+ " of data (%u packets,"
+ " busy-poll):\n\n",
+ client.s_apn, client.size,
+ client.count);
+
+ while (!stop && client.sent < client.count) {
+ clock_gettime(CLOCK_MONOTONIC, &sent);
+
+ msg->type = htonl(ECHO_REQUEST);
+ msg->id = htonl(client.sent);
+ msg->tv_sec = sent.tv_sec;
+ msg->tv_nsec = sent.tv_nsec;
+
+ if (flow_write(fd, buf,
+ client.size) < 0) {
+ printf("Failed to send "
+ "packet.\n");
+ break;
+ }
+
+ ++client.sent;
+
+ do {
+ n = flow_read(fd, buf,
+ OPING_BUF_SIZE);
+ } while (n == -EAGAIN && !stop);
+
+ if (n < 0)
+ break;
+
+ clock_gettime(CLOCK_MONOTONIC, &rcvd);
+
+ if (ntohl(msg->type) != ECHO_REPLY)
+ continue;
+
+ ++client.rcvd;
+
+ sent.tv_sec = msg->tv_sec;
+ sent.tv_nsec = msg->tv_nsec;
+ ms = ts_diff_us(&rcvd, &sent) / 1000.0;
+
+ update_rtt_stats(ms);
+
+ if (!client.quiet)
+ print_rtt(client.size,
+ ntohl(msg->id), ms,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int flood_ping(int fd)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ struct timespec sent;
+ struct timespec rcvd;
+ double ms;
+
+ memset(buf, 0, client.size);
+
+ if (!client.quiet)
+ printf("Pinging %s with %d bytes of data (%u packets):\n\n",
+ client.s_apn, client.size, client.count);
+
+ while (!stop && client.sent < client.count) {
+ clock_gettime(CLOCK_MONOTONIC, &sent);
+
+ msg->type = htonl(ECHO_REQUEST);
+ msg->id = htonl(client.sent);
+ msg->tv_sec = sent.tv_sec;
+ msg->tv_nsec = sent.tv_nsec;
+
+ if (flow_write(fd, buf, client.size) < 0) {
+ printf("Failed to send packet.\n");
+ break;
+ }
+
+ ++client.sent;
+
+ if (flow_read(fd, buf, OPING_BUF_SIZE) < 0) {
+ printf("Failed to read packet.\n");
+ break;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &rcvd);
+
+ if (ntohl(msg->type) != ECHO_REPLY)
+ continue;
+
+ ++client.rcvd;
+
+ sent.tv_sec = msg->tv_sec;
+ sent.tv_nsec = msg->tv_nsec;
+ ms = ts_diff_us(&rcvd, &sent) / 1000.0;
+
+ update_rtt_stats(ms);
+
+ if (!client.quiet)
+ print_rtt(client.size,
+ ntohl(msg->id), ms,
+ NULL);
+ }
+
+ return 0;
+}
+
+static int threaded_ping(int fd)
+{
+ pthread_create(&client.reader_pt, NULL, reader, &fd);
+ pthread_create(&client.writer_pt, NULL, writer, &fd);
+
+ pthread_join(client.writer_pt, NULL);
+ pthread_join(client.reader_pt, NULL);
+
+ return 0;
+}
+
static int client_main(void)
{
struct sigaction sig_act;
-
struct timespec tic;
struct timespec toc;
-
int fd;
memset(&sig_act, 0, sizeof sig_act);
@@ -241,37 +425,18 @@ static int client_main(void)
clock_gettime(CLOCK_REALTIME, &tic);
- pthread_create(&client.reader_pt, NULL, reader, &fd);
- pthread_create(&client.writer_pt, NULL, writer, &fd);
-
- pthread_join(client.writer_pt, NULL);
- pthread_join(client.reader_pt, NULL);
+ if (client.flood_busy)
+ flood_busy_ping(fd);
+ else if (client.flood)
+ flood_ping(fd);
+ else
+ threaded_ping(fd);
clock_gettime(CLOCK_REALTIME, &toc);
- printf("\n");
- printf("--- %s ping statistics ---\n", client.s_apn);
- printf("%d packets transmitted, ", client.sent);
- printf("%d received, ", client.rcvd);
- printf("%zd out-of-order, ", client.ooo);
- printf("%.0lf%% packet loss, ", client.sent == 0 ? 0 :
- ceil(100 - (100 * (client.rcvd / (float) client.sent))));
- printf("time: %.3f ms\n", ts_diff_us(&toc, &tic) / 1000.0);
-
- if (client.rcvd > 0) {
- printf("rtt min/avg/max/mdev = %.3f/%.3f/%.3f/",
- client.rtt_min,
- client.rtt_avg,
- client.rtt_max);
- if (client.rcvd > 1)
- printf("%.3f ms\n",
- sqrt(client.rtt_m2 / (client.rcvd - 1)));
- else
- printf("NaN ms\n");
- }
+ print_stats(&tic, &toc);
flow_dealloc(fd);
-
client_fini();
return 0;
diff --git a/src/tools/oping/oping_server.c b/src/tools/oping/oping_server.c
index c1d5e6e5..33af28c4 100644
--- a/src/tools/oping/oping_server.c
+++ b/src/tools/oping/oping_server.c
@@ -1,5 +1,5 @@
/*
- * Ouroboros - Copyright (C) 2016 - 2024
+ * Ouroboros - Copyright (C) 2016 - 2026
*
* Ouroboros ping application
*
@@ -89,12 +89,15 @@ void * server_thread(void *o)
struct oping_msg * msg = (struct oping_msg *) buf;
struct timespec now = {0, 0};
struct timespec timeout = {0, 100 * MILLION};
+ struct timespec poll_timeout = {0, 0};
int fd;
(void) o;
while (true) {
- if (fevent(server.flows, server.fq, &timeout) == -ETIMEDOUT)
+ if (fevent(server.flows, server.fq,
+ server.poll ? &poll_timeout : &timeout)
+ == -ETIMEDOUT)
continue;
while ((fd = fqueue_next(server.fq)) >= 0) {
@@ -135,7 +138,10 @@ void * accept_thread(void * o)
(void) o;
- printf("Ouroboros ping server started.\n");
+ printf("Ouroboros ping server started.");
+ if (server.busy)
+ printf(" [busy-poll]");
+ printf("\n");
while (true) {
fd = flow_accept(&qs, NULL);
@@ -155,12 +161,56 @@ void * accept_thread(void * o)
pthread_mutex_unlock(&server.lock);
fccntl(fd, FLOWSFLAGS,
- FLOWFRNOBLOCK | FLOWFRDWR | FLOWFRNOPART);
+ FLOWFRNOBLOCK | FLOWFRDWR
+ | FLOWFRNOPART);
}
return (void *) 0;
}
+void * busy_thread(void * o)
+{
+ char buf[OPING_BUF_SIZE];
+ struct oping_msg * msg = (struct oping_msg *) buf;
+ int fd;
+ int msg_len;
+
+ (void) o;
+
+ /* Accept a single flow. */
+ fd = flow_accept(NULL, NULL);
+ if (fd < 0) {
+ printf("Failed to accept flow.\n");
+ return (void *) -1;
+ }
+
+ printf("New flow %d (busy-poll).\n", fd);
+
+ fccntl(fd, FLOWSFLAGS,
+ FLOWFRNOBLOCK | FLOWFRDWR
+ | FLOWFRNOPART);
+
+ while (true) {
+ msg_len = flow_read(fd, buf,
+ OPING_BUF_SIZE);
+ if (msg_len == -EAGAIN)
+ continue;
+ if (msg_len < 0)
+ break;
+
+ if (ntohl(msg->type) != ECHO_REQUEST)
+ continue;
+
+ msg->type = htonl(ECHO_REPLY);
+
+ flow_write(fd, buf, msg_len);
+ }
+
+ flow_dealloc(fd);
+
+ return (void *) 0;
+}
+
int server_main(void)
{
struct sigaction sig_act;
@@ -188,12 +238,21 @@ int server_main(void)
}
pthread_create(&server.cleaner_pt, NULL, cleaner_thread, NULL);
- pthread_create(&server.accept_pt, NULL, accept_thread, NULL);
- pthread_create(&server.server_pt, NULL, server_thread, NULL);
- pthread_join(server.accept_pt, NULL);
+ if (server.busy) {
+ pthread_create(&server.server_pt, NULL,
+ busy_thread, NULL);
+ pthread_join(server.server_pt, NULL);
+ pthread_cancel(server.cleaner_pt);
+ } else {
+ pthread_create(&server.accept_pt, NULL,
+ accept_thread, NULL);
+ pthread_create(&server.server_pt, NULL,
+ server_thread, NULL);
+ pthread_join(server.accept_pt, NULL);
+ pthread_cancel(server.server_pt);
+ }
- pthread_cancel(server.server_pt);
pthread_cancel(server.cleaner_pt);
fset_destroy(server.flows);