diff --git a/src/config.h b/src/config.h index 19dd4a6..c4abea0 100644 --- a/src/config.h +++ b/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 */ diff --git a/src/dist.c b/src/dist.c index a46a1a8..cf52243 100644 --- a/src/dist.c +++ b/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))) diff --git a/src/emulate.c b/src/emulate.c index 2c6ca4a..e98750e 100644 --- a/src/emulate.c +++ b/src/emulate.c @@ -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); diff --git a/src/main.c b/src/main.c index a52161c..c9d8c5c 100644 --- a/src/main.c +++ b/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 \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); diff --git a/src/probe.c b/src/probe.c index dce3541..bb4abec 100644 --- a/src/probe.c +++ b/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;