Merge branch 'master' of github.com:stv0g/netem
This commit is contained in:
commit
e7ca4adf42
6 changed files with 143 additions and 54 deletions
|
@ -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")
|
||||
|
|
5
config.h
5
config.h
|
@ -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 */
|
||||
|
|
|
@ -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
64
dist.c
|
@ -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
44
main.c
|
@ -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
77
probe.c
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue