added new option to adjust ICMP payload size
This commit is contained in:
parent
64d11f10f7
commit
7e500eec1d
5 changed files with 102 additions and 90 deletions
37
src/config.h
37
src/config.h
|
@ -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 */
|
||||
|
|
10
src/dist.c
10
src/dist.c
|
@ -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)))
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
49
src/main.c
49
src/main.c
|
@ -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);
|
||||
|
|
41
src/probe.c
41
src/probe.c
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue