Merge branch 'master' of github.com:stv0g/netem

This commit is contained in:
Steffen Vogel 2017-12-20 10:31:06 +01:00
commit e7ca4adf42
6 changed files with 143 additions and 54 deletions

View file

@ -21,6 +21,11 @@ target_include_directories(netem PUBLIC "/usr/include/libnl3")
add_library(mark mark.c)
install(TARGETS netem mark
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
set(CPACK_PACKAGE_NAME "netem")
set(CPACK_PACKAGE_CONTACT "Steffen Vogel <post@steffenvogel.de>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "netem measures and emulates network delay distributions using the NetEm queuing discipline")

View file

@ -17,7 +17,12 @@ struct config {
int limit;
int warmup;
double rate;
double scaling;
char *dev;
enum {
FORMAT_TC,
FORMAT_VILLAS
} format;
};
/* Declared in main.c */

View file

@ -176,8 +176,6 @@ void printtable(const short *table, int limit)
{
int i;
printf("# This is the distribution table for the experimental distribution.\n");
for (i=0 ; i < limit; ++i) {
printf("%d%c", table[i],
(i % 8) == 7 ? '\n' : ' ');

64
dist.c
View file

@ -18,6 +18,8 @@
#include <error.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <netlink/route/qdisc.h>
#include <netlink/route/tc.h>
@ -54,30 +56,31 @@ int rtnl_netem_set_delay_distribution_data(struct rtnl_qdisc *qdisc, short *data
return 0;
}
static short * dist_make(FILE *fp, double *mu, double *sigma, double *rho)
static short * dist_make(FILE *fp, double *mu, double *sigma, double *rho, int *cnt)
{
int limit;
double *x;
double *measurements;
int *table;
short *inverse;
int total;
x = readdoubles(fp, &limit);
if (limit <= 0)
measurements = readdoubles(fp, cnt);
if (*cnt <= 0)
error(-1, 0, "Nothing much read!");
arraystats(x, limit, mu, sigma, rho);
for (int i = 0; i < *cnt; i++)
measurements[i] *= cfg.scaling;
fprintf(stderr, "Read %d values, mu %10.4f, sigma %10.4f, rho %10.4f\n",
limit, *mu, *sigma, *rho);
table = makedist(x, limit, *mu, *sigma);
free((void *) x);
arraystats(measurements, *cnt, mu, sigma, rho);
table = makedist(measurements, *cnt, *mu, *sigma);
cumulativedist(table, DISTTABLESIZE, &total);
inverse = inverttable(table, TABLESIZE, DISTTABLESIZE, total);
interpolatetable(inverse, TABLESIZE);
free((void *) measurements);
free((void *) table);
return inverse;
}
@ -85,6 +88,7 @@ static int dist_generate(int argc, char *argv[])
{
FILE *fp;
double mu, sigma, rho;
int cnt;
if (argc == 1) {
if (!(fp = fopen(argv[0], "r")))
@ -93,11 +97,42 @@ static int dist_generate(int argc, char *argv[])
else
fp = stdin;
short *inverse = dist_make(fp, &mu, &sigma, &rho);
short *inverse = dist_make(fp, &mu, &sigma, &rho, &cnt);
if (!inverse)
error(-1, 0, "Failed to generate distribution");
printtable(inverse, TABLESIZE);
char date[100], user[100], host[100];
time_t now = time (0);
strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&now));
gethostname(host, sizeof(host));
getlogin_r(user, sizeof(user));
printf("# This is the distribution table for the experimental distribution.\n");
printf("# Read %d values, mu %.6f, sigma %.6f, rho %.6f\n", cnt, mu, sigma, rho);
printf("# Generated %s, by %s on %s\n", date, user, host);
printf("#\n");
switch (cfg.format) {
case FORMAT_TC:
printtable(inverse, TABLESIZE);
break;
case FORMAT_VILLAS:
printf("netem = {\n");
printf(" delay = %f\n", mu * 1e6);
printf(" jitter = %f\n", sigma * 1e6);
printf(" distribution = [ %d", inverse[0]);
for (int i = 1; i < TABLESIZE; i++)
printf(", %d", inverse[i]);
printf(" ]\n");
printf(" loss = 0,\n");
printf(" duplicate = 0,\n");
printf(" corrupt = 0\n");
printf(" }\n");
break;
}
return 0;
}
@ -106,6 +141,7 @@ static int dist_load(int argc, char *argv[])
{
FILE *fp;
double mu, sigma, rho;
int cnt;
if (argc == 1) {
if (!(fp = fopen(argv[0], "r")))
@ -114,7 +150,7 @@ static int dist_load(int argc, char *argv[])
else
fp = stdin;
short *inverse = dist_make(fp, &mu, &sigma, &rho);
short *inverse = dist_make(fp, &mu, &sigma, &rho, &cnt);
if (!inverse)
error(-1, 0, "Failed to generate distribution");

44
main.c
View file

@ -25,12 +25,14 @@ int running = 1;
/* Default settings */
struct config cfg = {
.limit = 5000,
.rate = 1000,
.limit = 100,
.rate = 1,
.scaling = 1,
.mark = 0xCD,
.mask = 0xFFFFFFFF,
.warmup = 200,
.dev = "eth0"
.dev = "eth0",
.format = FORMAT_TC
};
int probe(int argc, char *argv[]);
@ -59,13 +61,15 @@ int main(int argc, char *argv[])
" recorded measurements. This iCDF can either be used by tc(8) or 'netem table'\n"
"\n"
" OPTIONS:\n\n"
" -m N apply emulation only to packet buffers with mark N\n"
" -M N an optional mask for the fw mark\n"
" -i N update the emulation parameters every N seconds\n"
" -r R rate limit used for measurements and updates of network emulation\n"
" -l L how many probes should we sent\n"
" -w W number of probe samples to be collected for estimating histogram boundaries\n"
" -d IF network interface\n"
" -m N apply emulation only to packet buffers with mark N\n"
" -M N an optional mask for the fw mark\n"
" -i N update the emulation parameters every N seconds\n"
" -r RATE rate limit used for measurements and updates of network emulation\n"
" -l CNT how many probes should we sent\n"
" -w SAMPLES number of probe samples to be collected for estimating histogram boundaries\n"
" -d IF network interface\n"
" -s FACTOR a scaling factor for the dist subcommands\n"
" -f FMT the output format of the distribution tables\n"
"\n"
"netem util %s (built on %s %s)\n"
" Copyright 2017, Steffen Vogel <post@steffenvogel.de>\n", argv[0], VERSION, __DATE__, __TIME__);
@ -88,7 +92,7 @@ int main(int argc, char *argv[])
/* Parse Arguments */
char c, *endptr;
while ((c = getopt (argc-1, argv+1, "h:m:M:i:l:d:r:w:")) != -1) {
while ((c = getopt(argc, argv, "h:m:M:i:l:d:r:s:f:w:")) != -1) {
switch (c) {
case 'm':
cfg.mark = strtoul(optarg, &endptr, 0);
@ -108,11 +112,22 @@ int main(int argc, char *argv[])
case 'l':
cfg.limit = strtoul(optarg, &endptr, 10);
goto check;
case 'd':
cfg.dev = strdup(optarg);
break;
case 's':
cfg.scaling = strtof(optarg, &endptr);
goto check;
case 'f':
if (strcmp(optarg, "villas") == 0)
cfg.format = FORMAT_VILLAS;
else if (strcmp(optarg, "tc") == 0)
cfg.format = FORMAT_TC;
else {
error(-1, 0, "Unknown format: %s.", optarg);
exit(EXIT_FAILURE);
}
break;
case '?':
if (optopt == 'c')
error(-1, 0, "Option -%c requires an argument.", optopt);
@ -120,6 +135,7 @@ int main(int argc, char *argv[])
error(-1, 0, "Unknown option '-%c'.", optopt);
else
error(-1, 0, "Unknown option character '\\x%x'.", optopt);
exit(EXIT_FAILURE);
default:
abort();
@ -131,7 +147,7 @@ check:
error(-1, 0, "Failed to parse parse option argument '-%c %s'", c, optarg);
}
char *cmd = argv[1];
char *cmd = argv[optind];
if (!strcmp(cmd, "probe"))
return probe(argc-optind-1, argv+optind+1);

77
probe.c
View file

@ -36,47 +36,72 @@
#include "utils.h"
#include "hist.h"
int probe_tcp(int sd, unsigned short dport, struct timespec *ts)
struct phdr {
struct in_addr source;
struct in_addr destination;
uint8_t reserved;
uint8_t protocol;
uint16_t length;
} __attribute__((packed));
int probe_tcp(int sd, unsigned short dport, struct in_addr src, struct in_addr dst, struct timespec *ts)
{
struct timespec ts_syn, ts_ack;
struct iphdr ihdr;
struct tcphdr thdr;
struct phdr *phdr;
struct iphdr *ihdr;
struct tcphdr *thdr;
char buf[sizeof(struct phdr) + sizeof(struct iphdr) + sizeof(struct tcphdr)];
struct msghdr msgh;
ssize_t len;
struct iovec iov[2] = {
{ .iov_base = &ihdr, .iov_len = sizeof(ihdr) }, // IP header
{ .iov_base = &thdr, .iov_len = sizeof(thdr) } // TCP header
};
/* Sending SYN */
memset(&ihdr, 0, sizeof(ihdr));
memset(&thdr, 0, sizeof(thdr));
memset(buf, 0, sizeof(buf));
memset(&msgh, 0, sizeof(msgh));
/* Randomize sequence number and source port */
unsigned int seq = (unsigned) rand();
unsigned short sport = (rand() + 1024) & 0xFFFF;
thdr.syn = 1;
thdr.seq = htonl(seq);
thdr.source = htons(sport);
thdr.dest = htons(dport);
thdr.doff = 5;
thdr.check = chksum_rfc1071((unsigned short *) &thdr, sizeof(thdr));
phdr = (struct phdr *) buf;
thdr = (struct tcphdr *) (phdr + 1);
msgh.msg_iov = &iov[1]; // only send TCP header
phdr->source = src;
phdr->destination = dst;
phdr->protocol = IPPROTO_TCP;
phdr->length = sizeof(struct tcphdr);
thdr->syn = 1;
thdr->seq = htonl(seq);
thdr->source = htons(sport);
thdr->dest = htons(dport);
thdr->doff = 5;
thdr->check = chksum_rfc1071((unsigned short *) &phdr, sizeof(struct phdr) + sizeof(struct tcphdr));
struct iovec iov1[] = {
{ .iov_base = thdr, .iov_len = sizeof(struct tcphdr) } // TCP header
};
msgh.msg_iov = iov1; // only send TCP header
msgh.msg_iovlen = 1;
if (ts_sendmsg(sd, &msgh, 0, &ts_syn) < 0)
error(-1, errno, "Failed to send SYN packet");
/* Receiving ACK */
memset(&ihdr, 0, sizeof(ihdr));
memset(&thdr, 0, sizeof(thdr));
memset(buf, 0, sizeof(buf));
msgh.msg_iov = &iov[0]; // receive IP + TCP header
ihdr = (struct iphdr *) buf;
thdr = (struct tcphdr *) (ihdr + 1);
struct iovec iov2[] = {
{ .iov_base = ihdr, .iov_len = sizeof(struct iphdr) }, // IP header
{ .iov_base = thdr, .iov_len = sizeof(struct tcphdr) } // TCP header
};
msgh.msg_iov = iov2; // receive IP + TCP header
msgh.msg_iovlen = 2;
retry: len = ts_recvmsg(sd, &msgh, 0, &ts_ack);
@ -87,15 +112,15 @@ retry: len = ts_recvmsg(sd, &msgh, 0, &ts_ack);
// len, thdr.syn, thdr.ack, thdr.rst, ntohl(thdr.seq), ntohl(thdr.ack_seq), ntohs(thdr.source), ntohs(thdr.dest));
/* Check response */
if (thdr.source != htons(dport) || thdr.dest != htons(sport)) {
if (thdr->source != htons(dport) || thdr->dest != htons(sport)) {
printf("Skipping invalid ports\n");
goto retry;
}
else if (!thdr.rst && !(thdr.ack && thdr.syn)) {
else if (!thdr->rst && !(thdr->ack && thdr->syn)) {
printf("Skipping invalid flags\n");
goto retry;
}
else if (ntohl(thdr.ack_seq) != seq + 1) {
else if (ntohl(thdr->ack_seq) != seq + 1) {
printf("Skipping invalid seq\n");
goto retry;
}
@ -112,6 +137,7 @@ int probe(int argc, char *argv[])
/* Parse address */
struct nl_addr *addr;
struct sockaddr_in sin;
struct in_addr src, dst;
/* Parse args */
if (argc != 2)
@ -128,6 +154,9 @@ int probe(int argc, char *argv[])
if (nl_addr_fill_sockaddr(addr, (struct sockaddr *) &sin, &sinlen))
error(-1, 0, "Failed to fill sockaddr");
dst = sin.sin_addr;
inet_aton("134.130.169.31", &src);
/* Create RAW socket */
int sd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sd < 0)
@ -155,7 +184,7 @@ int probe(int argc, char *argv[])
error(-1, errno, "Failed to initilize timer");
do {
probe_tcp(sd, dport, &ts);
probe_tcp(sd, dport, src, dst, &ts);
double rtt = time_to_double(&ts);
hist_put(&hist, rtt);