diff --git a/emulate.c b/emulate.c index 90ff21f..cbc6a29 100644 --- a/emulate.c +++ b/emulate.c @@ -5,10 +5,19 @@ * @license GPLv3 *********************************************************************************/ +#define _POSIX_C_SOURCE 200809L + +#include +#include #include +#include + +#include +#include #include "tc.h" #include "config.h" +#include "timing.h" int emulate(int argc, char *argv[]) { @@ -25,8 +34,8 @@ int emulate(int argc, char *argv[]) struct rtnl_tc *cls_fw = NULL; struct tc_netem netem = { - .delay = 1000000, - .limit = cfg.limit + .limit = cfg.limit, + .delay = 100000 }; /* Create connection to netlink */ @@ -48,24 +57,60 @@ int emulate(int argc, char *argv[]) if (ret) error(-1, 0, "Failed to setup TC: prio qdisc: %s", nl_geterror(ret)); - ret = tc_netem(sock, link, &qdisc_netem, &netem); - if (ret) - error(-1, 0, "Failed to setup TC: netem qdisc: %s", nl_geterror(ret)); - ret = tc_classifier(sock, link, &cls_fw, cfg.mark, cfg.mask); if (ret) error(-1, 0, "Failed to setup TC: fw filter: %s", nl_geterror(ret)); - int run = 0; - while (cfg.limit && run < cfg.limit) { - rtnl_tc_dump_stats(qdisc_netem, &dp_param); - sleep(1); + ret = tc_netem(sock, link, &qdisc_netem, &netem); + if (ret) + error(-1, 0, "Failed to setup TC: netem qdisc: %s", nl_geterror(ret)); + + /* We will use the default normal distribution for now */ + rtnl_netem_set_delay_distribution(qdisc_netem, "normal"); + + /* Start timer */ + struct itimerspec its = { + .it_interval = time_from_double(1 / cfg.rate), + .it_value = { 1, 0 } + }; + + int tfd = timerfd_create(CLOCK_REALTIME, 0); + if (tfd < 0) + error(-1, errno, "Failed to create timer"); + + if (timerfd_settime(tfd, 0, &its, NULL)) + error(-1, errno, "Failed to start timer"); + + char *line = NULL; + size_t linelen = 0; + + unsigned run = 0; + while (!cfg.limit || run < cfg.limit) { + float rtt, mu, sigma; - netem.delay += 10000; + /* Show queuing discipline statistics */ + rtnl_tc_dump_stats(qdisc_netem, &dp_param); + + /* Parse new data */ + if (feof(stdin) || getline(&line, &linelen, stdin) == -1) + error(-1, errno, "Failed to read data from stdin"); + + if (line[0] == '#') + continue; + + if (sscanf(line, "%f %f %f ", &rtt, &mu, &sigma) != 3) + error(-1, 0, "Invalid data format"); + + /* Update the netem config according to the measurements */ + /* TODO: Add more characteristics */ + netem.delay = (mu / 2) * 1e6; + netem.jitter = sigma * 1e6; + ret = tc_netem(sock, link, &qdisc_netem, &netem); if (ret) error(-1, 0, "Failed to update TC: netem qdisc: %s", nl_geterror(ret)); + timerfd_wait(tfd); run++; } diff --git a/probe.c b/probe.c index 5e88821..6cc0e97 100644 --- a/probe.c +++ b/probe.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -148,25 +149,54 @@ int probe(int argc, char *argv[]) struct hist hist; hist_create(&hist, 0, 0, 1); - while (hist.total < cfg.limit) { - probe_tcp(sd, &ts); + /* Start timer */ + struct itimerspec its = { + .it_interval = time_from_double(1 / cfg.rate), + .it_value = { 1, 0 } + }; + + int tfd = timerfd_create(CLOCK_REALTIME, 0); + if (tfd < 0) + error(-1, errno, "Failed to create timer"); + + if (timerfd_settime(tfd, 0, &its, NULL)) + error(-1, errno, "Failed to start timer"); + + unsigned run = 0; + while (cfg.limit && run < cfg.limit) { + probe_tcp(sd, dport, &ts); double rtt = time_to_double(&ts); hist_put(&hist, rtt); - printf("n=%u, rtt=%f, min=%f, max=%f, avg=%f, stddev=%f\n", - hist.total, rtt, hist.lowest, hist.highest, hist_mean(&hist), hist_stddev(&hist)); + //printf("n=%u, rtt=%f, min=%f, max=%f, avg=%f, stddev=%f\n", + // hist.total, rtt, hist.lowest, hist.highest, hist_mean(&hist), hist_stddev(&hist)); /* Warmup: adjust histogram after rough estimation of RTT */ - if (hist.total == 20 && hist.high == 0) { + if (run == 20) { double span = hist.highest - hist.lowest; hist_destroy(&hist); - hist_create(&hist, MAX(0, hist.lowest - span * 0.1), hist.highest + span * 0.2, 1e-3); + hist_create(&hist, MAX(0, hist.lowest - span * 0.1), hist.highest + span * 0.2, span / 20); printf("Created new histogram: high=%f, low=%f, buckets=%u\n", hist.high, hist.low, hist.length); + + /* Print header for output */ + time_t t = time(NULL); + struct tm *tm = localtime(&t); + char addrs[32], date[32]; + + nl_addr2str(addr, addrs, sizeof(addrs)); + strftime(date, sizeof(date), "%a, %d %b %Y %T %z", tm); + + printf("# Probing: %s on port %u\n", addrs, dport); + printf("# Started: %s\n", date); + printf("# RTT mu sigma (units in S)\n"); } + //else if (run > 20) + printf("%f %f %f\n", rtt, hist_mean(&hist), hist_stddev(&hist)); - //usleep(250 * 1e3); // 250 ms + timerfd_wait(tfd); + run++; } hist_print(&hist);