From 91012d9af758a48c4c57fc940dfcc8a581fa46ac Mon Sep 17 00:00:00 2001
From: Dimitri Staessens <dimitri.staessens@ugent.be>
Date: Wed, 14 Feb 2018 13:55:00 +0100
Subject: build: Allow out-of-tree build of tools

This removes the dependencies for the tools on some ouroboros internal
headers (endian.h and time_utils.h) so they can be built out-of-tree.
The echo-app tool has been renamed oecho and the cbr tool has been
renamed ocbr.

Signed-off-by: Dimitri Staessens <dimitri.staessens@ugent.be>
Signed-off-by: Sander Vrijders <sander.vrijders@ugent.be>
---
 src/tools/CMakeLists.txt           |   4 +-
 src/tools/cbr/CMakeLists.txt       |  16 ---
 src/tools/cbr/cbr.c                | 159 -----------------------
 src/tools/cbr/cbr_client.c         | 164 -----------------------
 src/tools/cbr/cbr_server.c         | 260 -------------------------------------
 src/tools/echo/CMakeLists.txt      |  16 ---
 src/tools/echo/echo.c              | 152 ----------------------
 src/tools/irm/irm_ipcp_bootstrap.c |   1 -
 src/tools/ocbr/CMakeLists.txt      |  16 +++
 src/tools/ocbr/ocbr.c              | 159 +++++++++++++++++++++++
 src/tools/ocbr/ocbr_client.c       | 164 +++++++++++++++++++++++
 src/tools/ocbr/ocbr_server.c       | 260 +++++++++++++++++++++++++++++++++++++
 src/tools/oecho/CMakeLists.txt     |  16 +++
 src/tools/oecho/oecho.c            | 152 ++++++++++++++++++++++
 src/tools/operf/CMakeLists.txt     |   5 +
 src/tools/operf/operf.c            |  12 +-
 src/tools/operf/operf_client.c     |  12 --
 src/tools/operf/operf_server.c     |   4 -
 src/tools/oping/CMakeLists.txt     |   5 +
 src/tools/oping/oping.c            |  15 ++-
 src/tools/oping/oping_client.c     |  28 ++--
 src/tools/oping/oping_server.c     |   4 -
 src/tools/time_utils.h             |  97 ++++++++++++++
 23 files changed, 907 insertions(+), 814 deletions(-)
 delete mode 100644 src/tools/cbr/CMakeLists.txt
 delete mode 100644 src/tools/cbr/cbr.c
 delete mode 100644 src/tools/cbr/cbr_client.c
 delete mode 100644 src/tools/cbr/cbr_server.c
 delete mode 100644 src/tools/echo/CMakeLists.txt
 delete mode 100644 src/tools/echo/echo.c
 create mode 100644 src/tools/ocbr/CMakeLists.txt
 create mode 100644 src/tools/ocbr/ocbr.c
 create mode 100644 src/tools/ocbr/ocbr_client.c
 create mode 100644 src/tools/ocbr/ocbr_server.c
 create mode 100644 src/tools/oecho/CMakeLists.txt
 create mode 100644 src/tools/oecho/oecho.c
 create mode 100644 src/tools/time_utils.h

(limited to 'src/tools')

diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index e8181d5f..b81e5439 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -1,5 +1,5 @@
 add_subdirectory(irm)
-add_subdirectory(echo)
-add_subdirectory(cbr)
+add_subdirectory(ocbr)
+add_subdirectory(oecho)
 add_subdirectory(oping)
 add_subdirectory(operf)
diff --git a/src/tools/cbr/CMakeLists.txt b/src/tools/cbr/CMakeLists.txt
deleted file mode 100644
index 158b5c87..00000000
--- a/src/tools/cbr/CMakeLists.txt
+++ /dev/null
@@ -1,16 +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)
-
-set(SOURCE_FILES
-  # Add source files here
-  cbr.c
-  )
-
-add_executable(cbr ${SOURCE_FILES})
-
-target_link_libraries(cbr LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS cbr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/cbr/cbr.c b/src/tools/cbr/cbr.c
deleted file mode 100644
index abba8ebe..00000000
--- a/src/tools/cbr/cbr.c
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2018
- *
- * CBR traffic generator
- *
- *    Dimitri Staessens <dimitri.staessens@ugent.be>
- *    Sander Vrijders   <sander.vrijders@ugent.be>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define _POSIX_C_SOURCE 199506L
-#define __XSI_VISIBLE 500
-
-#include <stdio.h>
-#include <string.h>
-#include <sys/time.h>
-#include <limits.h>
-#include <time.h>
-#include <stdbool.h>
-
-#define BUF_SIZE 1500
-
-#include "cbr_client.c"
-
-struct s {
-        long interval;
-        long timeout;
-} server_settings;
-
-#include "cbr_server.c"
-
-static void usage(void)
-{
-        printf("Usage: cbr [OPTION]...\n"
-               "Sends SDUs from client to server at a constant bit rate.\n\n"
-               "  -l, --listen              Run in server mode\n"
-               "\n"
-               "Server options:\n"
-               "  -i, --interval            Server report interval (s)\n"
-               "  -t, --timeout             Server timeout interval (s)\n"
-               "\n"
-               "Client options:\n"
-               "  -n, --server_apn          Specify the name of the server.\n"
-               "  -d, --duration            Duration for sending (s)\n"
-               "  -f, --flood               Send SDUs as fast as possible\n"
-               "  -s, --size                SDU size (B)\n"
-               "  -r, --rate                Rate (b/s)\n"
-               "      --sleep               Sleep in between sending SDUs\n"
-               "\n\n"
-               "      --help                Display this help text and exit\n");
-}
-
-int main(int argc, char ** argv)
-{
-        int    duration = 60;  /* One minute test */
-        int    size = 1000;    /* 1000 byte SDUs */
-        long   rate = 1000000; /* 1 Mb/s */
-        bool   flood = false;
-        bool   sleep = false;
-        int    ret = 0;
-        char * rem = NULL;
-        char * s_apn = NULL;
-
-        bool server = false;
-
-        server_settings.interval = 1; /* One second reporting interval */
-        server_settings.timeout  = 1;
-
-        argc--;
-        argv++;
-        while (argc > 0) {
-                if (strcmp(*argv, "-i") == 0 ||
-                    strcmp(*argv, "--interval") == 0) {
-                        server_settings.interval = strtol(*(++argv), &rem, 10);
-                        --argc;
-                } else if (strcmp(*argv, "-t") == 0 ||
-                           strcmp(*argv, "--timeout") == 0) {
-                        server_settings.timeout = strtol(*(++argv), &rem, 10);
-                        --argc;
-                } else if (strcmp(*argv, "-n") == 0 ||
-                           strcmp(*argv, "--server_apn") == 0) {
-                        s_apn = *(++argv);
-                        --argc;
-                } else if (strcmp(*argv, "-d") == 0 ||
-                           strcmp(*argv, "--duration") == 0) {
-                        duration = strtol(*(++argv), &rem, 10);
-                        --argc;
-                } else if (strcmp(*argv, "-s") == 0 ||
-                           strcmp(*argv, "--size") == 0) {
-                        size = strtol(*(++argv), &rem, 10);
-                        --argc;
-                } else if (strcmp(*argv, "-r") == 0 ||
-                           strcmp(*argv, "--rate") == 0) {
-                        rate = strtol(*(++argv), &rem, 10);
-                        if (*rem == 'k')
-                                rate *= 1000;
-                        if (*rem == 'M')
-                                rate *= MILLION;
-                        if (*rem == 'G')
-                                rate *= BILLION;
-                        --argc;
-                } else if (strcmp(*argv, "-l") == 0 ||
-                           strcmp(*argv, "--listen") == 0) {
-                        server = true;
-                } else if (strcmp(*argv, "-f") == 0 ||
-                           strcmp(*argv, "--flood") == 0) {
-                        flood = true;
-                } else if (strcmp(*argv, "--sleep") == 0) {
-                        sleep = true;
-                } else {
-                        usage();
-                        return 0;
-                }
-                argc--;
-                argv++;
-        }
-
-        if (server) {
-                ret = server_main();
-        } else {
-                if (s_apn == NULL) {
-                        printf("No server specified.\n");
-                        usage();
-                        return 0;
-                }
-
-                ret = client_main(s_apn, duration, size, rate, flood, sleep);
-        }
-
-        return ret;
-}
diff --git a/src/tools/cbr/cbr_client.c b/src/tools/cbr/cbr_client.c
deleted file mode 100644
index bf527317..00000000
--- a/src/tools/cbr/cbr_client.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2018
- *
- * A simple CBR generator
- *
- *    Dimitri Staessens <dimitri.staessens@ugent.be>
- *    Sander Vrijders   <sander.vrijders@ugent.be>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <ouroboros/dev.h>
-#include <ouroboros/time_utils.h>
-
-#include <signal.h>
-
-volatile bool stop;
-
-static void shutdown_client(int signo, siginfo_t * info, void * c)
-{
-        (void) info;
-        (void) c;
-
-        switch(signo) {
-        case SIGINT:
-        case SIGTERM:
-        case SIGHUP:
-                stop = true;
-        default:
-                return;
-        }
-}
-
-static void busy_wait_until(const struct timespec * deadline)
-{
-        struct timespec now;
-        clock_gettime(CLOCK_REALTIME, &now);
-        while (now.tv_sec < deadline->tv_sec)
-                clock_gettime(CLOCK_REALTIME, &now);
-        while (now.tv_sec == deadline->tv_sec
-               && now.tv_nsec < deadline->tv_nsec)
-                clock_gettime(CLOCK_REALTIME, &now);
-}
-
-int client_main(char * server,
-                int duration,
-                int size,
-                long rate,
-                bool flood,
-                bool sleep)
-{
-        struct sigaction sig_act;
-
-        int fd = 0;
-        char buf[size];
-        long seqnr = 0;
-        long gap = size * 8.0 * (BILLION / (double) rate);
-
-        struct timespec start;
-        struct timespec end;
-        struct timespec intv = {(gap / BILLION), gap % BILLION};
-        int ms;
-
-        stop = false;
-
-        memset(&sig_act, 0, sizeof sig_act);
-        sig_act.sa_sigaction = &shutdown_client;
-        sig_act.sa_flags = 0;
-
-        if (sigaction(SIGINT,  &sig_act, NULL) ||
-            sigaction(SIGTERM, &sig_act, NULL) ||
-            sigaction(SIGHUP,  &sig_act, NULL) ||
-            sigaction(SIGPIPE, &sig_act, NULL)) {
-                printf("Failed to install sighandler.\n");
-                return -1;
-        }
-
-        printf("Client started, duration %d, rate %lu b/s, size %d B.\n",
-               duration, rate, size);
-
-        fd = flow_alloc(server, NULL, NULL);
-        if (fd < 0) {
-                printf("Failed to allocate flow.\n");
-                return -1;
-        }
-
-        clock_gettime(CLOCK_REALTIME, &start);
-        if (!flood) {
-                while (!stop) {
-                        clock_gettime(CLOCK_REALTIME, &end);
-                        ts_add(&end, &intv, &end);
-                        memcpy(buf, &seqnr, sizeof(seqnr));
-
-                        if (flow_write(fd, buf, size) < 0) {
-                                stop = true;
-                                continue;
-                        }
-
-                        if (sleep)
-                                nanosleep(&intv, NULL);
-                        else
-                                busy_wait_until(&end);
-
-                        ++seqnr;
-
-                        if (ts_diff_us(&start, &end) / MILLION >= duration)
-                                stop = true;
-                }
-        } else { /* flood */
-                while (!stop) {
-                        clock_gettime(CLOCK_REALTIME, &end);
-                        if (flow_write(fd, buf, (size_t) size) < 0) {
-                                stop = true;
-                                continue;
-                        }
-
-                        ++seqnr;
-
-                        if (ts_diff_us(&start, &end) / MILLION
-                            >= (long) duration)
-                                stop = true;
-                }
-
-        }
-
-        clock_gettime(CLOCK_REALTIME, &end);
-
-        ms = ts_diff_ms(&start, &end);
-
-        printf("sent statistics: "
-               "%9ld SDUs, %12ld bytes in %9d ms, %4.4f Mb/s\n",
-               seqnr, seqnr * size, ms, (seqnr / (ms * 1000.0)) * size * 8.0);
-
-        flow_dealloc(fd);
-
-        return 0;
-}
diff --git a/src/tools/cbr/cbr_server.c b/src/tools/cbr/cbr_server.c
deleted file mode 100644
index 874155ed..00000000
--- a/src/tools/cbr/cbr_server.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2018
- *
- * A simple CBR generator
- *
- *    Dimitri Staessens <dimitri.staessens@ugent.be>
- *    Sander Vrijders   <sander.vrijders@ugent.be>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <ouroboros/dev.h>
-#include <ouroboros/time_utils.h>
-#include <ouroboros/fccntl.h>
-
-#include <stdbool.h>
-
-#ifdef __FreeBSD__
-#define __XSI_VISIBLE 500
-#endif
-
-#include <signal.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <pthread.h>
-
-#define THREADS_SIZE 10
-
-pthread_t       listen_thread;
-pthread_t       threads[THREADS_SIZE];
-int             fds[THREADS_SIZE];
-int             fds_count = 0;
-int             fds_index = 0;
-pthread_mutex_t fds_lock;
-pthread_cond_t  fds_signal;
-
-static void shutdown_server(int signo, siginfo_t * info, void * c)
-{
-        (void) info;
-        (void) c;
-
-        switch(signo) {
-        case SIGINT:
-        case SIGTERM:
-        case SIGHUP:
-                pthread_cancel(listen_thread);
-        default:
-                return;
-        }
-}
-
-static void handle_flow(int fd)
-{
-        int count = 0;
-        char buf[BUF_SIZE];
-
-        struct timespec now;
-        struct timespec alive;
-        struct timespec intv = {server_settings.interval, 0};
-
-        struct timespec iv_start;
-        struct timespec iv_end;
-
-        bool stop = false;
-
-        long sdus            = 0;
-        long sdus_intv       = 0;
-        long bytes_read      = 0;
-        long bytes_read_intv = 0;
-
-
-        clock_gettime(CLOCK_REALTIME, &iv_start);
-        alive = iv_start;
-        ts_add(&iv_start, &intv, &iv_end);
-
-        fccntl(fd, FLOWSFLAGS, FLOWFNONBLOCK);
-
-        while (!stop) {
-                clock_gettime(CLOCK_REALTIME, &now);
-
-                count = flow_read(fd, buf, BUF_SIZE);
-
-                if (count > 0) {
-                        clock_gettime(CLOCK_REALTIME, &alive);
-                        sdus++;
-                        bytes_read += count;
-                }
-
-                if (ts_diff_us(&alive, &now)
-                    > server_settings.timeout * MILLION) {
-                        printf("Test on flow %d timed out\n", fd);
-                        stop = true;
-                }
-
-                if (stop || ts_diff_ms(&now, &iv_end) < 0) {
-                        long us = ts_diff_us(&iv_start, &now);
-                        printf("Flow %4d: %9ld SDUs (%12ld bytes) in %9ld ms"
-                               " => %9.4f p/s, %9.4f Mb/s\n",
-                               fd,
-                               sdus - sdus_intv,
-                               bytes_read - bytes_read_intv,
-                               us / 1000,
-                               ((sdus - sdus_intv) / (double) us) * MILLION,
-                               8 * ((bytes_read - bytes_read_intv)
-                                    / (double)(us)));
-                        iv_start = iv_end;
-                        sdus_intv = sdus;
-                        bytes_read_intv = bytes_read;
-                        ts_add(&iv_start, &intv, &iv_end);
-                }
-        }
-
-        flow_dealloc(fd);
-}
-
-static void * worker(void * o)
-{
-        int cli_fd;
-
-        (void) o;
-
-        while (true) {
-                pthread_mutex_lock(&fds_lock);
-                pthread_cleanup_push((void(*)(void *)) pthread_mutex_unlock,
-                                     (void *) &fds_lock);
-                while (fds[fds_index] == -1)
-                        pthread_cond_wait(&fds_signal, &fds_lock);
-
-                cli_fd = fds[fds_index];
-                fds[fds_index] = -1;
-
-                pthread_cleanup_pop(true);
-
-                handle_flow(cli_fd);
-
-                pthread_mutex_lock(&fds_lock);
-                fds_count--;
-
-                pthread_cond_signal(&fds_signal);
-                pthread_mutex_unlock(&fds_lock);
-        }
-
-        return 0;
-}
-
-static void * listener(void * o)
-{
-        int fd = 0;
-        qosspec_t qs;
-
-        (void) o;
-
-        printf("Server started, interval is %ld s, timeout is %ld s.\n",
-               server_settings.interval, server_settings.timeout);
-
-        while (true) {
-                pthread_mutex_lock(&fds_lock);
-                pthread_cleanup_push((void(*)(void *)) pthread_mutex_unlock,
-                                     (void *) &fds_lock);
-
-                while (fds_count == THREADS_SIZE) {
-                        printf("Can't accept any more flows, waiting.\n");
-                        pthread_cond_wait(&fds_signal, &fds_lock);
-                }
-
-                pthread_cleanup_pop(true);
-
-                fd = flow_accept(&qs, NULL);
-                if (fd < 0) {
-                        printf("Failed to accept flow.\n");
-                        break;
-                }
-
-                printf("New flow.\n");
-
-                pthread_mutex_lock(&fds_lock);
-
-                fds_count++;
-                fds_index = (fds_index + 1) % THREADS_SIZE;
-                fds[fds_index] = fd;
-
-                pthread_cond_signal(&fds_signal);
-                pthread_mutex_unlock(&fds_lock);
-        }
-
-        return 0;
-}
-
-int server_main(void)
-{
-        struct sigaction sig_act;
-        int i;
-
-        memset(&sig_act, 0, sizeof sig_act);
-        sig_act.sa_sigaction = &shutdown_server;
-        sig_act.sa_flags = 0;
-
-        for (i = 0; i < THREADS_SIZE; i++)
-                fds[i] = -1;
-
-        if (sigaction(SIGINT,  &sig_act, NULL) ||
-            sigaction(SIGTERM, &sig_act, NULL) ||
-            sigaction(SIGHUP,  &sig_act, NULL) ||
-            sigaction(SIGPIPE, &sig_act, NULL)) {
-                printf("Failed to install sighandler.\n");
-                return -1;
-        }
-
-        if (pthread_mutex_init(&fds_lock, NULL)) {
-                printf("Failed to init mutex.\n");
-                exit(EXIT_FAILURE);
-        }
-
-        if (pthread_cond_init(&fds_signal, NULL)) {
-                printf("Failed to init cond.\n");
-                return -1;
-        }
-
-        for (i = 0; i < THREADS_SIZE; i++)
-                pthread_create(&threads[i], NULL, worker, NULL);
-
-        pthread_create(&listen_thread, NULL, listener, NULL);
-
-        pthread_join(listen_thread, NULL);
-
-        for (i = 0; i < THREADS_SIZE; i++)
-                pthread_cancel(threads[i]);
-
-        for (i = 0; i < THREADS_SIZE; i++)
-                pthread_join(threads[i], NULL);
-
-        return 0;
-}
diff --git a/src/tools/echo/CMakeLists.txt b/src/tools/echo/CMakeLists.txt
deleted file mode 100644
index 4766ab84..00000000
--- a/src/tools/echo/CMakeLists.txt
+++ /dev/null
@@ -1,16 +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)
-
-set(SOURCE_FILES
-  # Add source files here
-  echo.c
-  )
-
-add_executable(echo-app ${SOURCE_FILES})
-
-target_link_libraries(echo-app LINK_PUBLIC ouroboros-dev)
-
-install(TARGETS echo-app RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/echo/echo.c b/src/tools/echo/echo.c
deleted file mode 100644
index 3896a85a..00000000
--- a/src/tools/echo/echo.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Ouroboros - Copyright (C) 2016 - 2018
- *
- * A simple echo application
- *
- *    Dimitri Staessens <dimitri.staessens@ugent.be>
- *    Sander Vrijders   <sander.vrijders@ugent.be>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * 3. Neither the name of the copyright holder nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#define _POSIX_C_SOURCE 199309L
-
-#include <ouroboros/dev.h>
-
-#include <stdio.h>
-#include <string.h>
-
-#define BUF_SIZE 256
-
-static void usage(void)
-{
-        printf("Usage: echo-app [OPTION]...\n"
-               "Sends an echo between a server and a client\n\n"
-               "  -l, --listen              Run in server mode\n"
-               "      --help                Display this help text and exit\n");
-}
-
-int server_main(void)
-{
-        int     fd = 0;
-        char    buf[BUF_SIZE];
-        ssize_t count = 0;
-
-        printf("Starting the server.\n");
-
-        while (true) {
-                fd = flow_accept(NULL, NULL);
-                if (fd < 0) {
-                        printf("Failed to accept flow.\n");
-                        break;
-                }
-
-                printf("New flow.\n");
-
-                count = flow_read(fd, &buf, BUF_SIZE);
-                if (count < 0) {
-                        printf("Failed to read SDU.\n");
-                        flow_dealloc(fd);
-                        continue;
-                }
-
-                printf("Message from client is %.*s.\n", (int) count, buf);
-
-                if (flow_write(fd, buf, count) == -1) {
-                        printf("Failed to write SDU.\n");
-                        flow_dealloc(fd);
-                        continue;
-                }
-
-                flow_dealloc(fd);
-        }
-
-        return 0;
-}
-
-int client_main(void)
-{
-        int     fd      = 0;
-        char    buf[BUF_SIZE];
-        char *  message = "Client says hi!";
-        ssize_t count   = 0;
-
-        fd = flow_alloc("echo", NULL, NULL);
-        if (fd < 0) {
-                printf("Failed to allocate flow.\n");
-                return -1;
-        }
-
-        if (flow_write(fd, message, strlen(message) + 1) < 0) {
-                printf("Failed to write SDU.\n");
-                flow_dealloc(fd);
-                return -1;
-        }
-
-        count = flow_read(fd, buf, BUF_SIZE);
-        if (count < 0) {
-                printf("Failed to read SDU.\n");
-                flow_dealloc(fd);
-                return -1;
-        }
-
-        printf("Server replied with %.*s\n", (int) count, buf);
-
-        flow_dealloc(fd);
-
-        return 0;
-}
-
-int main(int argc, char ** argv)
-{
-        int ret = -1;
-        bool server = false;
-
-        argc--;
-        argv++;
-        while (argc > 0) {
-                if (strcmp(*argv, "-l") == 0 ||
-                    strcmp(*argv, "--listen") == 0) {
-                        server = true;
-                } else {
-                        usage();
-                        return 0;
-                }
-                argc--;
-                argv++;
-        }
-
-        if (server)
-                ret = server_main();
-        else
-                ret = client_main();
-
-        return ret;
-}
diff --git a/src/tools/irm/irm_ipcp_bootstrap.c b/src/tools/irm/irm_ipcp_bootstrap.c
index 3cad072a..986c45e1 100644
--- a/src/tools/irm/irm_ipcp_bootstrap.c
+++ b/src/tools/irm/irm_ipcp_bootstrap.c
@@ -44,7 +44,6 @@
 #include <sys/socket.h>
 #endif
 #include <ouroboros/irm.h>
-#include <ouroboros/ipcp.h>
 
 #include "irm_ops.h"
 #include "irm_utils.h"
diff --git a/src/tools/ocbr/CMakeLists.txt b/src/tools/ocbr/CMakeLists.txt
new file mode 100644
index 00000000..5dac3e63
--- /dev/null
+++ b/src/tools/ocbr/CMakeLists.txt
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_BINARY_DIR}/include)
+
+set(SOURCE_FILES
+  # Add source files here
+  ocbr.c
+  )
+
+add_executable(ocbr ${SOURCE_FILES})
+
+target_link_libraries(ocbr LINK_PUBLIC ouroboros-dev)
+
+install(TARGETS ocbr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/ocbr/ocbr.c b/src/tools/ocbr/ocbr.c
new file mode 100644
index 00000000..2c22cc3c
--- /dev/null
+++ b/src/tools/ocbr/ocbr.c
@@ -0,0 +1,159 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * CBR traffic generator
+ *
+ *    Dimitri Staessens <dimitri.staessens@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _POSIX_C_SOURCE 199506L
+#define __XSI_VISIBLE 500
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <time.h>
+#include <stdbool.h>
+
+#define BUF_SIZE 1500
+
+#include "ocbr_client.c"
+
+struct s {
+        long interval;
+        long timeout;
+} server_settings;
+
+#include "ocbr_server.c"
+
+static void usage(void)
+{
+        printf("Usage: cbr [OPTION]...\n"
+               "Sends SDUs from client to server at a constant bit rate.\n\n"
+               "  -l, --listen              Run in server mode\n"
+               "\n"
+               "Server options:\n"
+               "  -i, --interval            Server report interval (s)\n"
+               "  -t, --timeout             Server timeout interval (s)\n"
+               "\n"
+               "Client options:\n"
+               "  -n, --server_apn          Specify the name of the server.\n"
+               "  -d, --duration            Duration for sending (s)\n"
+               "  -f, --flood               Send SDUs as fast as possible\n"
+               "  -s, --size                SDU size (B)\n"
+               "  -r, --rate                Rate (b/s)\n"
+               "      --sleep               Sleep in between sending SDUs\n"
+               "\n\n"
+               "      --help                Display this help text and exit\n");
+}
+
+int main(int argc, char ** argv)
+{
+        int    duration = 60;  /* One minute test */
+        int    size = 1000;    /* 1000 byte SDUs */
+        long   rate = 1000000; /* 1 Mb/s */
+        bool   flood = false;
+        bool   sleep = false;
+        int    ret = 0;
+        char * rem = NULL;
+        char * s_apn = NULL;
+
+        bool server = false;
+
+        server_settings.interval = 1; /* One second reporting interval */
+        server_settings.timeout  = 1;
+
+        argc--;
+        argv++;
+        while (argc > 0) {
+                if (strcmp(*argv, "-i") == 0 ||
+                    strcmp(*argv, "--interval") == 0) {
+                        server_settings.interval = strtol(*(++argv), &rem, 10);
+                        --argc;
+                } else if (strcmp(*argv, "-t") == 0 ||
+                           strcmp(*argv, "--timeout") == 0) {
+                        server_settings.timeout = strtol(*(++argv), &rem, 10);
+                        --argc;
+                } else if (strcmp(*argv, "-n") == 0 ||
+                           strcmp(*argv, "--server_apn") == 0) {
+                        s_apn = *(++argv);
+                        --argc;
+                } else if (strcmp(*argv, "-d") == 0 ||
+                           strcmp(*argv, "--duration") == 0) {
+                        duration = strtol(*(++argv), &rem, 10);
+                        --argc;
+                } else if (strcmp(*argv, "-s") == 0 ||
+                           strcmp(*argv, "--size") == 0) {
+                        size = strtol(*(++argv), &rem, 10);
+                        --argc;
+                } else if (strcmp(*argv, "-r") == 0 ||
+                           strcmp(*argv, "--rate") == 0) {
+                        rate = strtol(*(++argv), &rem, 10);
+                        if (*rem == 'k')
+                                rate *= 1000;
+                        if (*rem == 'M')
+                                rate *= MILLION;
+                        if (*rem == 'G')
+                                rate *= BILLION;
+                        --argc;
+                } else if (strcmp(*argv, "-l") == 0 ||
+                           strcmp(*argv, "--listen") == 0) {
+                        server = true;
+                } else if (strcmp(*argv, "-f") == 0 ||
+                           strcmp(*argv, "--flood") == 0) {
+                        flood = true;
+                } else if (strcmp(*argv, "--sleep") == 0) {
+                        sleep = true;
+                } else {
+                        usage();
+                        return 0;
+                }
+                argc--;
+                argv++;
+        }
+
+        if (server) {
+                ret = server_main();
+        } else {
+                if (s_apn == NULL) {
+                        printf("No server specified.\n");
+                        usage();
+                        return 0;
+                }
+
+                ret = client_main(s_apn, duration, size, rate, flood, sleep);
+        }
+
+        return ret;
+}
diff --git a/src/tools/ocbr/ocbr_client.c b/src/tools/ocbr/ocbr_client.c
new file mode 100644
index 00000000..bf527317
--- /dev/null
+++ b/src/tools/ocbr/ocbr_client.c
@@ -0,0 +1,164 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * A simple CBR generator
+ *
+ *    Dimitri Staessens <dimitri.staessens@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ouroboros/dev.h>
+#include <ouroboros/time_utils.h>
+
+#include <signal.h>
+
+volatile bool stop;
+
+static void shutdown_client(int signo, siginfo_t * info, void * c)
+{
+        (void) info;
+        (void) c;
+
+        switch(signo) {
+        case SIGINT:
+        case SIGTERM:
+        case SIGHUP:
+                stop = true;
+        default:
+                return;
+        }
+}
+
+static void busy_wait_until(const struct timespec * deadline)
+{
+        struct timespec now;
+        clock_gettime(CLOCK_REALTIME, &now);
+        while (now.tv_sec < deadline->tv_sec)
+                clock_gettime(CLOCK_REALTIME, &now);
+        while (now.tv_sec == deadline->tv_sec
+               && now.tv_nsec < deadline->tv_nsec)
+                clock_gettime(CLOCK_REALTIME, &now);
+}
+
+int client_main(char * server,
+                int duration,
+                int size,
+                long rate,
+                bool flood,
+                bool sleep)
+{
+        struct sigaction sig_act;
+
+        int fd = 0;
+        char buf[size];
+        long seqnr = 0;
+        long gap = size * 8.0 * (BILLION / (double) rate);
+
+        struct timespec start;
+        struct timespec end;
+        struct timespec intv = {(gap / BILLION), gap % BILLION};
+        int ms;
+
+        stop = false;
+
+        memset(&sig_act, 0, sizeof sig_act);
+        sig_act.sa_sigaction = &shutdown_client;
+        sig_act.sa_flags = 0;
+
+        if (sigaction(SIGINT,  &sig_act, NULL) ||
+            sigaction(SIGTERM, &sig_act, NULL) ||
+            sigaction(SIGHUP,  &sig_act, NULL) ||
+            sigaction(SIGPIPE, &sig_act, NULL)) {
+                printf("Failed to install sighandler.\n");
+                return -1;
+        }
+
+        printf("Client started, duration %d, rate %lu b/s, size %d B.\n",
+               duration, rate, size);
+
+        fd = flow_alloc(server, NULL, NULL);
+        if (fd < 0) {
+                printf("Failed to allocate flow.\n");
+                return -1;
+        }
+
+        clock_gettime(CLOCK_REALTIME, &start);
+        if (!flood) {
+                while (!stop) {
+                        clock_gettime(CLOCK_REALTIME, &end);
+                        ts_add(&end, &intv, &end);
+                        memcpy(buf, &seqnr, sizeof(seqnr));
+
+                        if (flow_write(fd, buf, size) < 0) {
+                                stop = true;
+                                continue;
+                        }
+
+                        if (sleep)
+                                nanosleep(&intv, NULL);
+                        else
+                                busy_wait_until(&end);
+
+                        ++seqnr;
+
+                        if (ts_diff_us(&start, &end) / MILLION >= duration)
+                                stop = true;
+                }
+        } else { /* flood */
+                while (!stop) {
+                        clock_gettime(CLOCK_REALTIME, &end);
+                        if (flow_write(fd, buf, (size_t) size) < 0) {
+                                stop = true;
+                                continue;
+                        }
+
+                        ++seqnr;
+
+                        if (ts_diff_us(&start, &end) / MILLION
+                            >= (long) duration)
+                                stop = true;
+                }
+
+        }
+
+        clock_gettime(CLOCK_REALTIME, &end);
+
+        ms = ts_diff_ms(&start, &end);
+
+        printf("sent statistics: "
+               "%9ld SDUs, %12ld bytes in %9d ms, %4.4f Mb/s\n",
+               seqnr, seqnr * size, ms, (seqnr / (ms * 1000.0)) * size * 8.0);
+
+        flow_dealloc(fd);
+
+        return 0;
+}
diff --git a/src/tools/ocbr/ocbr_server.c b/src/tools/ocbr/ocbr_server.c
new file mode 100644
index 00000000..874155ed
--- /dev/null
+++ b/src/tools/ocbr/ocbr_server.c
@@ -0,0 +1,260 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * A simple CBR generator
+ *
+ *    Dimitri Staessens <dimitri.staessens@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ouroboros/dev.h>
+#include <ouroboros/time_utils.h>
+#include <ouroboros/fccntl.h>
+
+#include <stdbool.h>
+
+#ifdef __FreeBSD__
+#define __XSI_VISIBLE 500
+#endif
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+
+#define THREADS_SIZE 10
+
+pthread_t       listen_thread;
+pthread_t       threads[THREADS_SIZE];
+int             fds[THREADS_SIZE];
+int             fds_count = 0;
+int             fds_index = 0;
+pthread_mutex_t fds_lock;
+pthread_cond_t  fds_signal;
+
+static void shutdown_server(int signo, siginfo_t * info, void * c)
+{
+        (void) info;
+        (void) c;
+
+        switch(signo) {
+        case SIGINT:
+        case SIGTERM:
+        case SIGHUP:
+                pthread_cancel(listen_thread);
+        default:
+                return;
+        }
+}
+
+static void handle_flow(int fd)
+{
+        int count = 0;
+        char buf[BUF_SIZE];
+
+        struct timespec now;
+        struct timespec alive;
+        struct timespec intv = {server_settings.interval, 0};
+
+        struct timespec iv_start;
+        struct timespec iv_end;
+
+        bool stop = false;
+
+        long sdus            = 0;
+        long sdus_intv       = 0;
+        long bytes_read      = 0;
+        long bytes_read_intv = 0;
+
+
+        clock_gettime(CLOCK_REALTIME, &iv_start);
+        alive = iv_start;
+        ts_add(&iv_start, &intv, &iv_end);
+
+        fccntl(fd, FLOWSFLAGS, FLOWFNONBLOCK);
+
+        while (!stop) {
+                clock_gettime(CLOCK_REALTIME, &now);
+
+                count = flow_read(fd, buf, BUF_SIZE);
+
+                if (count > 0) {
+                        clock_gettime(CLOCK_REALTIME, &alive);
+                        sdus++;
+                        bytes_read += count;
+                }
+
+                if (ts_diff_us(&alive, &now)
+                    > server_settings.timeout * MILLION) {
+                        printf("Test on flow %d timed out\n", fd);
+                        stop = true;
+                }
+
+                if (stop || ts_diff_ms(&now, &iv_end) < 0) {
+                        long us = ts_diff_us(&iv_start, &now);
+                        printf("Flow %4d: %9ld SDUs (%12ld bytes) in %9ld ms"
+                               " => %9.4f p/s, %9.4f Mb/s\n",
+                               fd,
+                               sdus - sdus_intv,
+                               bytes_read - bytes_read_intv,
+                               us / 1000,
+                               ((sdus - sdus_intv) / (double) us) * MILLION,
+                               8 * ((bytes_read - bytes_read_intv)
+                                    / (double)(us)));
+                        iv_start = iv_end;
+                        sdus_intv = sdus;
+                        bytes_read_intv = bytes_read;
+                        ts_add(&iv_start, &intv, &iv_end);
+                }
+        }
+
+        flow_dealloc(fd);
+}
+
+static void * worker(void * o)
+{
+        int cli_fd;
+
+        (void) o;
+
+        while (true) {
+                pthread_mutex_lock(&fds_lock);
+                pthread_cleanup_push((void(*)(void *)) pthread_mutex_unlock,
+                                     (void *) &fds_lock);
+                while (fds[fds_index] == -1)
+                        pthread_cond_wait(&fds_signal, &fds_lock);
+
+                cli_fd = fds[fds_index];
+                fds[fds_index] = -1;
+
+                pthread_cleanup_pop(true);
+
+                handle_flow(cli_fd);
+
+                pthread_mutex_lock(&fds_lock);
+                fds_count--;
+
+                pthread_cond_signal(&fds_signal);
+                pthread_mutex_unlock(&fds_lock);
+        }
+
+        return 0;
+}
+
+static void * listener(void * o)
+{
+        int fd = 0;
+        qosspec_t qs;
+
+        (void) o;
+
+        printf("Server started, interval is %ld s, timeout is %ld s.\n",
+               server_settings.interval, server_settings.timeout);
+
+        while (true) {
+                pthread_mutex_lock(&fds_lock);
+                pthread_cleanup_push((void(*)(void *)) pthread_mutex_unlock,
+                                     (void *) &fds_lock);
+
+                while (fds_count == THREADS_SIZE) {
+                        printf("Can't accept any more flows, waiting.\n");
+                        pthread_cond_wait(&fds_signal, &fds_lock);
+                }
+
+                pthread_cleanup_pop(true);
+
+                fd = flow_accept(&qs, NULL);
+                if (fd < 0) {
+                        printf("Failed to accept flow.\n");
+                        break;
+                }
+
+                printf("New flow.\n");
+
+                pthread_mutex_lock(&fds_lock);
+
+                fds_count++;
+                fds_index = (fds_index + 1) % THREADS_SIZE;
+                fds[fds_index] = fd;
+
+                pthread_cond_signal(&fds_signal);
+                pthread_mutex_unlock(&fds_lock);
+        }
+
+        return 0;
+}
+
+int server_main(void)
+{
+        struct sigaction sig_act;
+        int i;
+
+        memset(&sig_act, 0, sizeof sig_act);
+        sig_act.sa_sigaction = &shutdown_server;
+        sig_act.sa_flags = 0;
+
+        for (i = 0; i < THREADS_SIZE; i++)
+                fds[i] = -1;
+
+        if (sigaction(SIGINT,  &sig_act, NULL) ||
+            sigaction(SIGTERM, &sig_act, NULL) ||
+            sigaction(SIGHUP,  &sig_act, NULL) ||
+            sigaction(SIGPIPE, &sig_act, NULL)) {
+                printf("Failed to install sighandler.\n");
+                return -1;
+        }
+
+        if (pthread_mutex_init(&fds_lock, NULL)) {
+                printf("Failed to init mutex.\n");
+                exit(EXIT_FAILURE);
+        }
+
+        if (pthread_cond_init(&fds_signal, NULL)) {
+                printf("Failed to init cond.\n");
+                return -1;
+        }
+
+        for (i = 0; i < THREADS_SIZE; i++)
+                pthread_create(&threads[i], NULL, worker, NULL);
+
+        pthread_create(&listen_thread, NULL, listener, NULL);
+
+        pthread_join(listen_thread, NULL);
+
+        for (i = 0; i < THREADS_SIZE; i++)
+                pthread_cancel(threads[i]);
+
+        for (i = 0; i < THREADS_SIZE; i++)
+                pthread_join(threads[i], NULL);
+
+        return 0;
+}
diff --git a/src/tools/oecho/CMakeLists.txt b/src/tools/oecho/CMakeLists.txt
new file mode 100644
index 00000000..50a66138
--- /dev/null
+++ b/src/tools/oecho/CMakeLists.txt
@@ -0,0 +1,16 @@
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+include_directories(${CMAKE_SOURCE_DIR}/include)
+include_directories(${CMAKE_BINARY_DIR}/include)
+
+set(SOURCE_FILES
+  # Add source files here
+  oecho.c
+  )
+
+add_executable(oecho ${SOURCE_FILES})
+
+target_link_libraries(oecho LINK_PUBLIC ouroboros-dev)
+
+install(TARGETS oecho RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/tools/oecho/oecho.c b/src/tools/oecho/oecho.c
new file mode 100644
index 00000000..3896a85a
--- /dev/null
+++ b/src/tools/oecho/oecho.c
@@ -0,0 +1,152 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * A simple echo application
+ *
+ *    Dimitri Staessens <dimitri.staessens@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _POSIX_C_SOURCE 199309L
+
+#include <ouroboros/dev.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#define BUF_SIZE 256
+
+static void usage(void)
+{
+        printf("Usage: echo-app [OPTION]...\n"
+               "Sends an echo between a server and a client\n\n"
+               "  -l, --listen              Run in server mode\n"
+               "      --help                Display this help text and exit\n");
+}
+
+int server_main(void)
+{
+        int     fd = 0;
+        char    buf[BUF_SIZE];
+        ssize_t count = 0;
+
+        printf("Starting the server.\n");
+
+        while (true) {
+                fd = flow_accept(NULL, NULL);
+                if (fd < 0) {
+                        printf("Failed to accept flow.\n");
+                        break;
+                }
+
+                printf("New flow.\n");
+
+                count = flow_read(fd, &buf, BUF_SIZE);
+                if (count < 0) {
+                        printf("Failed to read SDU.\n");
+                        flow_dealloc(fd);
+                        continue;
+                }
+
+                printf("Message from client is %.*s.\n", (int) count, buf);
+
+                if (flow_write(fd, buf, count) == -1) {
+                        printf("Failed to write SDU.\n");
+                        flow_dealloc(fd);
+                        continue;
+                }
+
+                flow_dealloc(fd);
+        }
+
+        return 0;
+}
+
+int client_main(void)
+{
+        int     fd      = 0;
+        char    buf[BUF_SIZE];
+        char *  message = "Client says hi!";
+        ssize_t count   = 0;
+
+        fd = flow_alloc("echo", NULL, NULL);
+        if (fd < 0) {
+                printf("Failed to allocate flow.\n");
+                return -1;
+        }
+
+        if (flow_write(fd, message, strlen(message) + 1) < 0) {
+                printf("Failed to write SDU.\n");
+                flow_dealloc(fd);
+                return -1;
+        }
+
+        count = flow_read(fd, buf, BUF_SIZE);
+        if (count < 0) {
+                printf("Failed to read SDU.\n");
+                flow_dealloc(fd);
+                return -1;
+        }
+
+        printf("Server replied with %.*s\n", (int) count, buf);
+
+        flow_dealloc(fd);
+
+        return 0;
+}
+
+int main(int argc, char ** argv)
+{
+        int ret = -1;
+        bool server = false;
+
+        argc--;
+        argv++;
+        while (argc > 0) {
+                if (strcmp(*argv, "-l") == 0 ||
+                    strcmp(*argv, "--listen") == 0) {
+                        server = true;
+                } else {
+                        usage();
+                        return 0;
+                }
+                argc--;
+                argv++;
+        }
+
+        if (server)
+                ret = server_main();
+        else
+                ret = client_main();
+
+        return ret;
+}
diff --git a/src/tools/operf/CMakeLists.txt b/src/tools/operf/CMakeLists.txt
index 895d706c..b6faf04e 100644
--- a/src/tools/operf/CMakeLists.txt
+++ b/src/tools/operf/CMakeLists.txt
@@ -4,6 +4,11 @@ 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")
diff --git a/src/tools/operf/operf.c b/src/tools/operf/operf.c
index 69fc5cb9..fe387724 100644
--- a/src/tools/operf/operf.c
+++ b/src/tools/operf/operf.c
@@ -39,14 +39,24 @@
 #define _POSIX_C_SOURCE 199506L
 #define __XSI_VISIBLE   500
 
-#include <ouroboros/fqueue.h>
 #include <ouroboros/dev.h>
+#include <ouroboros/fccntl.h>
+#include <ouroboros/fqueue.h>
+
+#include "time_utils.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <math.h>
+#include <errno.h>
+#include <float.h>
 
 #define OPERF_BUF_SIZE (1024 * 1024)
 
diff --git a/src/tools/operf/operf_client.c b/src/tools/operf/operf_client.c
index c6f32440..1518bdf5 100644
--- a/src/tools/operf/operf_client.c
+++ b/src/tools/operf/operf_client.c
@@ -36,18 +36,6 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ouroboros/dev.h>
-#include <ouroboros/fccntl.h>
-#include <ouroboros/time_utils.h>
-
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <math.h>
-#include <errno.h>
-#include <float.h>
-
 static void busy_wait_until(const struct timespec * deadline)
 {
         struct timespec now;
diff --git a/src/tools/operf/operf_server.c b/src/tools/operf/operf_server.c
index ac6306af..11eb92fc 100644
--- a/src/tools/operf/operf_server.c
+++ b/src/tools/operf/operf_server.c
@@ -36,10 +36,6 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdlib.h>
-#include <signal.h>
-#include <arpa/inet.h>
-
 void shutdown_server(int signo, siginfo_t * info, void * c)
 {
         (void) info;
diff --git a/src/tools/oping/CMakeLists.txt b/src/tools/oping/CMakeLists.txt
index ebf96bdb..31a4f961 100644
--- a/src/tools/oping/CMakeLists.txt
+++ b/src/tools/oping/CMakeLists.txt
@@ -4,6 +4,11 @@ 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")
diff --git a/src/tools/oping/oping.c b/src/tools/oping/oping.c
index 5e01e026..3c1d4fe9 100644
--- a/src/tools/oping/oping.c
+++ b/src/tools/oping/oping.c
@@ -37,17 +37,26 @@
  */
 
 #define _POSIX_C_SOURCE 199506L
-#define __XSI_VISIBLE   500
+#define __XSI_VISIBLE 500
 
-#include <ouroboros/endian.h>
-#include <ouroboros/fqueue.h>
 #include <ouroboros/dev.h>
+#include <ouroboros/fccntl.h>
+#include <ouroboros/fqueue.h>
+
+#include "time_utils.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdbool.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <math.h>
+#include <errno.h>
+#include <float.h>
 
 #define OPING_BUF_SIZE 1500
 
diff --git a/src/tools/oping/oping_client.c b/src/tools/oping/oping_client.c
index 8952f5ed..07fbde74 100644
--- a/src/tools/oping/oping_client.c
+++ b/src/tools/oping/oping_client.c
@@ -36,18 +36,6 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ouroboros/dev.h>
-#include <ouroboros/fccntl.h>
-#include <ouroboros/time_utils.h>
-
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <math.h>
-#include <errno.h>
-#include <float.h>
-
 volatile bool stop;
 
 void shutdown_client(int signo, siginfo_t * info, void * c)
@@ -88,12 +76,12 @@ void * reader(void * o)
                 if (msg_len < 0)
                         continue;
 
-                if (ntoh32(msg->type) != ECHO_REPLY) {
+                if (ntohl(msg->type) != ECHO_REPLY) {
                         printf("Invalid message on fd %d.\n", fd);
                         continue;
                 }
 
-                if (ntoh32(msg->id) >= client.count) {
+                if ((uint32_t) ntohl(msg->id) >= client.count) {
                         printf("Invalid id.\n");
                         continue;
                 }
@@ -102,8 +90,8 @@ void * reader(void * o)
 
                 clock_gettime(CLOCK_MONOTONIC, &now);
 
-                sent.tv_sec = ntoh64(msg->tv_sec);
-                sent.tv_nsec = ntoh64(msg->tv_nsec);
+                sent.tv_sec = msg->tv_sec;
+                sent.tv_nsec = msg->tv_nsec;
 
                 ms = ts_diff_us(&sent, &now) / 1000.0;
 
@@ -155,10 +143,10 @@ void * writer(void * o)
 
                 clock_gettime(CLOCK_MONOTONIC, &now);
 
-                msg->type = hton32(ECHO_REQUEST);
-                msg->id = hton32(client.sent++);
-                msg->tv_sec = hton64(now.tv_sec);
-                msg->tv_nsec = hton64(now.tv_nsec);
+                msg->type = htonl(ECHO_REQUEST);
+                msg->id = htonl(client.sent++);
+                msg->tv_sec = now.tv_sec;
+                msg->tv_nsec = now.tv_nsec;
 
                 if (flow_write(*fdp, buf, client.size) == -1) {
                         printf("Failed to send SDU.\n");
diff --git a/src/tools/oping/oping_server.c b/src/tools/oping/oping_server.c
index 49b14f81..e91b6f10 100644
--- a/src/tools/oping/oping_server.c
+++ b/src/tools/oping/oping_server.c
@@ -36,10 +36,6 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <stdlib.h>
-#include <signal.h>
-#include <arpa/inet.h>
-
 void shutdown_server(int signo, siginfo_t * info, void * c)
 {
         (void) info;
diff --git a/src/tools/time_utils.h b/src/tools/time_utils.h
new file mode 100644
index 00000000..c9760a8b
--- /dev/null
+++ b/src/tools/time_utils.h
@@ -0,0 +1,97 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2018
+ *
+ * Time utilities
+ *
+ *    Dimitri Staessens <dimitri.staessens@ugent.be>
+ *    Sander Vrijders   <sander.vrijders@ugent.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OUROBOROS_TOOLS_TIME_UTILS_H
+#define OUROBOROS_TOOLS_TIME_UTILS_H
+
+#ifdef MILLION
+#undef MILLION
+#endif
+
+#ifdef BILLION
+#undef BILLION
+#endif
+
+#define MILLION  1000000L
+#define BILLION  1000000000L
+
+#include <time.h>
+#include <sys/time.h>
+#include <limits.h> /* LONG_MAX */
+
+/* functions for timespecs */
+#define ts_diff_ns(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * BILLION     \
+                            + ((tx)->tv_nsec - (t0)->tv_nsec))
+#define ts_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION     \
+                            + ((tx)->tv_nsec - (t0)->tv_nsec) / 1000L)
+#define ts_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L       \
+                            + ((tx)->tv_nsec - (t0)->tv_nsec) / MILLION)
+
+/* functions for timevals are the same */
+#define tv_diff_us(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * MILLION     \
+                            + ((tx)->tv_usec - (t0)->tv_usec) / 1000L)
+#define tv_diff_ms(t0, tx) (((tx)->tv_sec - (t0)->tv_sec) * 1000L       \
+                            + ((tx)->tv_usec - (t0)->tv_usec) / MILLION)
+
+/* functions for timespecs */
+int ts_add(const struct timespec * t,
+           const struct timespec * intv,
+           struct timespec *       res);
+
+int ts_diff(const struct timespec * t,
+            const struct timespec * intv,
+            struct timespec *       res);
+
+/* functions for timevals */
+int tv_add(const struct timeval * t,
+           const struct timeval * intv,
+           struct timeval *       res);
+
+int tv_diff(const struct timeval * t,
+            const struct timeval * intv,
+            struct timeval *       res);
+
+/* copying a timeval into a timespec */
+int tv_to_ts(const struct timeval * src,
+             struct timespec *      dst);
+
+/* copying a timespec into a timeval (loss of resolution) */
+int ts_to_tv(const struct timespec * src,
+             struct timeval *        dst);
+
+#endif /* OUROBOROS_TOOLS_TIME_UTILS_H */
-- 
cgit v1.2.3