diff --git a/CMakeLists.txt b/CMakeLists.txt index eb18c97..2aef5a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "netem measures and emulates network delay distributions using the NetEm queuing discipline") diff --git a/config.h b/config.h index 919623d..19dd4a6 100644 --- a/config.h +++ b/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 */ diff --git a/dist-maketable.c b/dist-maketable.c index 78a4d89..c9fb034 100644 --- a/dist-maketable.c +++ b/dist-maketable.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' : ' '); diff --git a/dist.c b/dist.c index ac11ef9..90928de 100644 --- a/dist.c +++ b/dist.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -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"); diff --git a/main.c b/main.c index 10fb01b..a52161c 100644 --- a/main.c +++ b/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 \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); diff --git a/probe.c b/probe.c index 798c25a..6752c40 100644 --- a/probe.c +++ b/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);