added new option to adjust ICMP payload size

This commit is contained in:
Steffen Vogel 2018-01-26 16:31:29 +01:00
parent 64d11f10f7
commit 7e500eec1d
5 changed files with 102 additions and 90 deletions

View file

@ -11,18 +11,31 @@
#define VERSION "0.1"
struct config {
int mark;
int mask;
int interval;
int limit;
int warmup;
double rate;
double scaling;
char *dev;
enum {
FORMAT_TC,
FORMAT_VILLAS
} format;
struct {
enum {
PROBE_ICMP,
PROBE_TCP
} mode;
int payload;
int limit;
double rate;
int warmup;
} probe;
struct {
enum {
FORMAT_TC,
FORMAT_VILLAS
} format;
double scaling;
} dist;
struct {
int mark;
int mask;
char *dev;
int interval;
} emulate;
};
/* Declared in main.c */

View file

@ -68,7 +68,7 @@ static short * dist_make(FILE *fp, double *mu, double *sigma, double *rho, int *
error(-1, 0, "Nothing much read!");
for (int i = 0; i < *cnt; i++)
measurements[i] *= cfg.scaling;
measurements[i] *= cfg.dist.scaling;
arraystats(measurements, *cnt, mu, sigma, rho);
@ -112,7 +112,7 @@ static int dist_generate(int argc, char *argv[])
printf("# Generated %s, by %s on %s\n", date, user, host);
printf("#\n");
switch (cfg.format) {
switch (cfg.dist.format) {
case FORMAT_TC:
printtable(inverse, TABLESIZE);
break;
@ -169,9 +169,9 @@ static int dist_load(int argc, char *argv[])
nl_connect(sock, NETLINK_ROUTE);
/* Get interface */
link = tc_get_link(sock, cfg.dev);
link = tc_get_link(sock, cfg.emulate.dev);
if (!link)
error(-1, 0, "Interface does not exist: %s", cfg.dev);
error(-1, 0, "Interface does not exist: %s", cfg.emulate.dev);
/* Reset TC subsystem */
ret = tc_reset(sock, link);
@ -182,7 +182,7 @@ static int dist_load(int argc, char *argv[])
if ((ret = tc_prio(sock, link, &qdisc_prio)))
error(-1, 0, "Failed to setup TC: prio qdisc: %s", nl_geterror(ret));
if ((ret = tc_classifier(sock, link, &cls_fw, cfg.mark, cfg.mask)))
if ((ret = tc_classifier(sock, link, &cls_fw, cfg.emulate.mark, cfg.emulate.mask)))
error(-1, 0, "Failed to setup TC: fw filter: %s", nl_geterror(ret));
if ((ret = tc_netem(sock, link, &qdisc_netem)))

View file

@ -38,18 +38,18 @@ enum input_fields {
};
static int emulate_parse_line(char *line, struct rtnl_tc *tc)
{
{
double val;
char *cur, *end = line;
int i = 0;
struct rtnl_qdisc *ne = (struct rtnl_qdisc *) tc;
do {
cur = end;
val = strtod(cur, &end);
switch (i) {
switch (i) {
case CURRENT_RTT:
rtnl_netem_set_delay(ne, val * 1e6 / 2);
break; /* we approximate: delay = RTT / 2 */
@ -87,8 +87,7 @@ static int emulate_parse_line(char *line, struct rtnl_tc *tc)
break;
}
} while (cur != end && ++i < MAXFIELDS);
return (i >= 3) ? 0 : -1; /* we need at least 3 fields: rtt + jitter */
}
@ -97,7 +96,7 @@ int emulate(int argc, char *argv[])
int ret, tfd, run = 0;
struct nl_sock *sock;
struct rtnl_link *link;
struct rtnl_tc *qdisc_prio = NULL;
struct rtnl_tc *qdisc_netem = NULL;
@ -108,51 +107,51 @@ int emulate(int argc, char *argv[])
/* Create connection to netlink */
sock = nl_socket_alloc();
nl_connect(sock, NETLINK_ROUTE);
/* Get interface */
link = tc_get_link(sock, cfg.dev);
link = tc_get_link(sock, cfg.emulate.dev);
if (!link)
error(-1, 0, "Interface does not exist: %s", cfg.dev);
error(-1, 0, "Interface does not exist: %s", cfg.emulate.dev);
/* Reset TC subsystem */
ret = tc_reset(sock, link);
if (ret && ret != -NLE_OBJ_NOTFOUND)
error(-1, 0, "Failed to reset TC: %s", nl_geterror(ret));
/* Setup TC subsystem */
if ((ret = tc_prio(sock, link, &qdisc_prio)))
error(-1, 0, "Failed to setup TC: prio qdisc: %s", nl_geterror(ret));
if ((ret = tc_classifier(sock, link, &cls_fw, cfg.mark, cfg.mask)))
if ((ret = tc_classifier(sock, link, &cls_fw, cfg.emulate.mark, cfg.emulate.mask)))
error(-1, 0, "Failed to setup TC: fw filter: %s", nl_geterror(ret));
if ((ret = tc_netem(sock, link, &qdisc_netem)))
error(-1, 0, "Failed to setup TC: netem qdisc: %s", nl_geterror(ret));
/* We will use the default normal distribution for now */
if (rtnl_netem_set_delay_distribution((struct rtnl_qdisc *) qdisc_netem, "normal"))
error(-1, 0, "Failed to set netem delay distrubtion: %s", nl_geterror(ret));
rtnl_netem_set_limit((struct rtnl_qdisc *) qdisc_netem, 0);
/* Start timer */
if ((tfd = timerfd_init(cfg.rate)) < 0)
if ((tfd = timerfd_init(cfg.probe.rate)) < 0)
error(-1, errno, "Failed to initilize timer");
char *line = NULL;
size_t linelen = 0;
ssize_t len;
do {
#if 0
struct nl_dump_params dp_param = {
.dp_type = NL_DUMP_DETAILS,
.dp_fd = stdout
};
nl_object_dump((struct nl_object *) qdisc_netem, &dp_param);
nl_object_dump((struct nl_object *) qdisc_prio, &dp_param);
nl_object_dump((struct nl_object *) cls_fw, &dp_param);
nl_object_dump((struct nl_object *) qdisc_prio, &dp_param);
nl_object_dump((struct nl_object *) cls_fw, &dp_param);
#endif
tc_print_netem(qdisc_netem);
@ -163,20 +162,20 @@ next_line: len = getline(&line, &linelen, stdin);
break; /* EOF => quit */
else if (len < 0)
error(-1, errno, "Failed to read data from stdin");
if (line[0] == '#' || line[0] == '\r' || line[0] == '\n')
goto next_line;
if (emulate_parse_line(line, qdisc_netem))
error(-1, 0, "Failed to parse stdin");
ret = tc_netem(sock, link, &qdisc_netem);
if (ret)
error(-1, 0, "Failed to update TC: netem qdisc: %s", nl_geterror(ret));
run = timerfd_wait(tfd);
} while (!cfg.limit || run < cfg.limit);
} while (!cfg.probe.limit || run < cfg.probe.limit);
/* Shutdown */
free(line);

View file

@ -25,14 +25,21 @@ int running = 1;
/* Default settings */
struct config cfg = {
.limit = 100,
.rate = 1,
.scaling = 1,
.mark = 0xCD,
.mask = 0xFFFFFFFF,
.warmup = 200,
.dev = "eth0",
.format = FORMAT_TC
.probe = {
.payload = 0,
.rate = 1,
.warmup = 200,
.limit = 100
},
.dist = {
.format = FORMAT_TC,
.scaling = 1
},
.emulate = {
.mark = 0xCD,
.mask = 0xFFFFFFFF,
.dev = "eth0"
}
};
int probe(int argc, char *argv[]);
@ -70,6 +77,7 @@ int main(int argc, char *argv[])
" -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"
" -p SZ payload size for ICMP messages\n"
"\n"
"netem util %s (built on %s %s)\n"
" Copyright 2017, Steffen Vogel <post@steffenvogel.de>\n", argv[0], VERSION, __DATE__, __TIME__);
@ -92,37 +100,40 @@ int main(int argc, char *argv[])
/* Parse Arguments */
char c, *endptr;
while ((c = getopt(argc, argv, "h:m:M:i:l:d:r:s:f:w:")) != -1) {
while ((c = getopt(argc, argv, "h:m:M:i:l:d:r:s:f:w:p:")) != -1) {
switch (c) {
case 'm':
cfg.mark = strtoul(optarg, &endptr, 0);
cfg.emulate.mark = strtoul(optarg, &endptr, 0);
goto check;
case 'M':
cfg.mask = strtoul(optarg, &endptr, 0);
cfg.emulate.mask = strtoul(optarg, &endptr, 0);
goto check;
case 'w':
cfg.warmup = strtoul(optarg, &endptr, 0);
cfg.probe.warmup = strtoul(optarg, &endptr, 0);
goto check;
case 'i':
cfg.interval = strtoul(optarg, &endptr, 10);
cfg.emulate.interval = strtoul(optarg, &endptr, 10);
goto check;
case 'r':
cfg.rate = strtof(optarg, &endptr);
cfg.probe.rate = strtof(optarg, &endptr);
goto check;
case 'l':
cfg.limit = strtoul(optarg, &endptr, 10);
cfg.probe.limit = strtoul(optarg, &endptr, 10);
goto check;
case 'd':
cfg.dev = strdup(optarg);
cfg.emulate.dev = strdup(optarg);
break;
case 's':
cfg.scaling = strtof(optarg, &endptr);
cfg.dist.scaling = strtof(optarg, &endptr);
goto check;
case 'p':
cfg.probe.payload = strtoul(optarg, &endptr, 10);
goto check;
case 'f':
if (strcmp(optarg, "villas") == 0)
cfg.format = FORMAT_VILLAS;
cfg.dist.format = FORMAT_VILLAS;
else if (strcmp(optarg, "tc") == 0)
cfg.format = FORMAT_TC;
cfg.dist.format = FORMAT_TC;
else {
error(-1, 0, "Unknown format: %s.", optarg);
exit(EXIT_FAILURE);

View file

@ -58,7 +58,7 @@ int probe_icmp_tx(int sd, struct sockaddr_in *dst)
struct timespec ts_req;
ssize_t bytes, wbytes;
bytes = sizeof(struct icmphdr) + sizeof(struct icmppl);
bytes = sizeof(struct icmphdr) + sizeof(struct icmppl) + cfg.probe.payload;
struct msghdr msgh;
struct iovec iov;
@ -129,7 +129,7 @@ int probe_icmp_rx(int sd)
rbytes = ts_recvmsg(sd, &msgh, MSG_DONTWAIT, &ts_rep);
if (rbytes < 0) {
if (errno == EAGAIN)
return 0;
return 1;
else
error(-1, errno, "Failed to receive ICMP echo reply");
}
@ -228,18 +228,13 @@ retry: rbytes = ts_recvmsg(sd, &msgh, 0, &ts_ack);
int probe(int argc, char *argv[])
{
enum probe_mode {
PROBE_ICMP,
PROBE_TCP
} mode = PROBE_ICMP;
int run = 0, tfd, sd, ret, prot;
socklen_t sinlen;
if (mode == PROBE_TCP) {
if (cfg.probe.mode == PROBE_TCP) {
prot = IPPROTO_TCP;
}
else if (mode == PROBE_ICMP) {
else if (cfg.probe.mode == PROBE_ICMP) {
prot = IPPROTO_ICMP;
}
else {
@ -294,24 +289,22 @@ int probe(int argc, char *argv[])
/* Prepare statistics */
/* Start timer */
if ((tfd = timerfd_init(cfg.rate)) < 0)
if ((tfd = timerfd_init(cfg.probe.rate)) < 0)
error(-1, errno, "Failed to initilize timer");
if (mode == PROBE_ICMP) {
if (cfg.probe.mode == PROBE_ICMP) {
do {
if (!cfg.limit || run < cfg.limit)
if (!cfg.probe.limit || run < cfg.probe.limit)
probe_icmp_tx(sd, &dst);
probe_icmp_rx(sd);
do {
ret = probe_icmp_rx(sd);
} while (!ret);
int steps = timerfd_wait(tfd);
if (steps > 1)
fprintf(stderr, "Missed steps: %d\n", steps - 1);
run += steps;
} while (cfg.limit && run < 2*cfg.limit);
run += timerfd_wait(tfd);
} while (cfg.probe.limit && run < 2*cfg.probe.limit);
}
else if (mode == PROBE_TCP) {
else if (cfg.probe.mode == PROBE_TCP) {
do {
probe_tcp(sd, &src, &dst, &ts);
@ -319,12 +312,8 @@ int probe(int argc, char *argv[])
printf("%d,%.10f\n", run, rtt);
int steps = timerfd_wait(tfd);
if (steps > 1)
fprintf(stderr, "Missed steps: %d\n", steps - 1);
run += steps;
} while (cfg.limit && run < cfg.limit);
run += timerfd_wait(tfd);
} while (cfg.probe.limit && run < cfg.probe.limit);
}
return 0;