diff --git a/project/Makefile b/project/Makefile index 0783dad..0afbae7 100644 --- a/project/Makefile +++ b/project/Makefile @@ -1,17 +1,30 @@ -TARGET = netem +TARGETS = netem mark -OBJS = main.o probe.o ts.o tc.o +OBJS = main.o probe.o emulate.o timing.o hist.o utils.o ts.o tc.o tcp.o -CC = gcc -CFLAGS = -g -lrt -std=c99 -Wall +CC = gcc -all: $(TARGET) Makefile +CFLAGS = -g -lrt -std=c99 -Wall +CFLAGS += -I/usr/local/include/libnl3 +CFLAGS += -I/usr/include/libnl3 + +LDLIBS = -lnl-3 -lnl-route-3 -lm + +all: $(TARGETS) Makefile -$(TARGET): $(OBJS) - $(CC) -o $@ $^ +netem: $(OBJS) + $(CC) $(LDLIBS) -o $@ $^ + +mark: mark.c + $(CC) -g -fPIC -c -o mark.o mark.c + $(CC) -shared -o mark.so mark.o -ldl clean: rm -f $(TARGET) rm -f *.o *~ + make -C libnl clean + +libnl: + make -C libnl -.PHONY: all clean +.PHONY: all clean libnl diff --git a/project/config.h b/project/config.h new file mode 100644 index 0000000..8dfe53a --- /dev/null +++ b/project/config.h @@ -0,0 +1,29 @@ +/** Global configuration. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define VERSION "0.1" + +#define SRC_PORT 23532 +#define DEST_PORT 22 + +struct config { + int mark; + int mask; + int interval; + int rate; + int limit; + + char *dev; +}; + +/* Declared in main.c */ +extern struct config cfg; + +#endif \ No newline at end of file diff --git a/project/emulate.c b/project/emulate.c new file mode 100644 index 0000000..90ff21f --- /dev/null +++ b/project/emulate.c @@ -0,0 +1,77 @@ +/** Setup and update netem qdisc. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#include + +#include "tc.h" +#include "config.h" + +int emulate(int argc, char *argv[]) +{ + int ret; + + struct nl_sock *sock; + struct nl_dump_params dp_param = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout + }; + struct rtnl_link *link; + struct rtnl_tc *qdisc_prio = NULL; + struct rtnl_tc *qdisc_netem = NULL; + struct rtnl_tc *cls_fw = NULL; + + struct tc_netem netem = { + .delay = 1000000, + .limit = cfg.limit + }; + + /* Create connection to netlink */ + sock = nl_socket_alloc(); + nl_connect(sock, NETLINK_ROUTE); + + /* Get interface */ + link = tc_get_link(sock, cfg.dev); + if (!link) + error(-1, 0, "Interface does not exist: %s", cfg.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 */ + ret = tc_prio(sock, link, &qdisc_prio); + if (ret) + error(-1, 0, "Failed to setup TC: prio qdisc: %s", nl_geterror(ret)); + + ret = tc_netem(sock, link, &qdisc_netem, &netem); + if (ret) + error(-1, 0, "Failed to setup TC: netem qdisc: %s", nl_geterror(ret)); + + ret = tc_classifier(sock, link, &cls_fw, cfg.mark, cfg.mask); + if (ret) + error(-1, 0, "Failed to setup TC: fw filter: %s", nl_geterror(ret)); + + int run = 0; + while (cfg.limit && run < cfg.limit) { + rtnl_tc_dump_stats(qdisc_netem, &dp_param); + sleep(1); + + netem.delay += 10000; + ret = tc_netem(sock, link, &qdisc_netem, &netem); + if (ret) + error(-1, 0, "Failed to update TC: netem qdisc: %s", nl_geterror(ret)); + + run++; + } + + /* Shutdown */ + nl_close(sock); + nl_socket_free(sock); + + return 0; +} \ No newline at end of file diff --git a/project/main.c b/project/main.c index b412057..55e7a6f 100644 --- a/project/main.c +++ b/project/main.c @@ -1,4 +1,11 @@ -#define _POSIX_C_SOURCE 199309L +/** Main routine. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#define _POSIX_C_SOURCE 200809L #include #include @@ -7,15 +14,25 @@ #include #include #include +#include #include #include -#define VERSION "0.1" +#include "config.h" int running = 1; -int probe(); +/* Default settings */ +struct config cfg = { + .limit = 100, + .mark = 0xCD, + .mask = 0xFFFFFFFF, + .dev = "eth0" +}; + +int probe(int argc, char *argv[]); +int emulate(int argc, char *argv[]); void quit(int sig, siginfo_t *si, void *ptr) { @@ -24,24 +41,21 @@ void quit(int sig, siginfo_t *si, void *ptr) } int main(int argc, char *argv[]) -{ - /* Options */ - int mark; - int interval; - int rate; - +{ if (argc < 2) { printf("usage: %s CMD [OPTIONS]\n", argv[0]); printf(" CMD can be one of:\n"); printf(" live \n"); printf(" probe \n"); printf(" emulate \n"); - - /* - -m --mark N apply emulation only to packet buffers with mark N - -i --interval N update the emulation parameters every N seconds - -r --rate the packet rate used for measurements - */ + printf("\n"); + printf(" OPTIONS:\n"); + printf(" -m --mark N apply emulation only to packet buffers with mark N\n"); + printf(" -M --mask N an optional mask for the fw mark\n"); + printf(" -i --interval N update the emulation parameters every N seconds\n"); + printf(" -r --rate the packet rate used for measurements\n"); + printf(" -l --limit how many probes should we sent\n"); + printf(" -d --dev network interface\n"); printf("\n"); printf("netem util %s (built on %s %s)\n", @@ -63,19 +77,32 @@ int main(int argc, char *argv[]) sigaction(SIGTERM, &sa_quit, NULL); sigaction(SIGINT, &sa_quit, NULL); + /* Initialize PRNG for TCP sequence nos */ + srand(time(NULL)); + /* Parse Arguments */ char c, *endptr; - while ((c = getopt (argc-1, argv+1, "h:m:i:")) != -1) { + while ((c = getopt (argc-1, argv+1, "h:m:M:i:l:d:")) != -1) { switch (c) { case 'm': - mark = strtoul(optarg, &endptr, 10); + cfg.mark = strtoul(optarg, &endptr, 0); + goto check; + case 'M': + cfg.mask = strtoul(optarg, &endptr, 0); goto check; case 'i': - interval = strtoul(optarg, &endptr, 10); + cfg.interval = strtoul(optarg, &endptr, 10); goto check; case 'r': - rate = strtoul(optarg, &endptr, 10); + cfg.rate = strtoul(optarg, &endptr, 10); goto check; + case 'l': + cfg.limit = strtoul(optarg, &endptr, 10); + goto check; + + case 'd': + cfg.dev = strdup(optarg); + break; case '?': if (optopt == 'c') @@ -96,7 +123,9 @@ check: } if (!strcmp(cmd, "probe")) - return probe(); + return probe(argc-optind-1, argv+optind+1); + else if (!strcmp(cmd, "emulate")) + return emulate(argc-optind-1, argv+optind+1); else error(-1, 0, "Unknown command: %s", cmd); diff --git a/project/probe.c b/project/probe.c index a3e5ebf..84e165d 100644 --- a/project/probe.c +++ b/project/probe.c @@ -1,3 +1,10 @@ +/** Probing for RTT, Loss, Duplication, Corruption. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + #define _POSIX_C_SOURCE 199309L #define _BSD_SOURCE @@ -18,59 +25,148 @@ #include #include +#include + +#include + #include "ts.h" +#include "timing.h" +#include "config.h" +#include "tcp.h" +#include "utils.h" +#include "hist.h" -int probe() +int probe_tcp(int sd, struct timespec *ts) { - char *dev = "lo"; - - printf("Start probing\n"); + struct timespec ts_syn, ts_ack; - /* Create RAW socket */ - int sd = socket(AF_INET, SOCK_RAW, 88); - if (sd < 0) - error(-1, errno, "Failed to create socket"); + struct iphdr ihdr; + struct tcphdr thdr; + struct msghdr msgh; + ssize_t len; - if (ts_enable_if(dev)) - error(-1, errno, "Failed to enable timestamping for interface: %s", dev); - if (ts_enable_sd(sd)) - error(-1, errno, "Failed to set SO_TIMESTAMPING"); - - struct timespec ts[3]; - struct sockaddr_in sa = { - .sin_family = AF_INET, - .sin_port = htons(7), + struct iovec iov[2] = { + { .iov_base = &ihdr, .iov_len = sizeof(ihdr) }, // IP header + { .iov_base = &thdr, .iov_len = sizeof(thdr) } // TCP header }; - struct msghdr msgh = { - .msg_name = (struct sockaddr *) &sa, - .msg_namelen = sizeof(sa) - }; - - inet_aton("8.8.8.8", &sa.sin_addr); - - if (sendto(sd, "test", sizeof("test"), 0, (struct sockaddr*) &sa, sizeof(sa)) == -1) - error(-1, errno, "Failed to send"); - enum direction { TX, RX } dir = RX; + /* Sending SYN */ + memset(&ihdr, 0, sizeof(ihdr)); + memset(&thdr, 0, sizeof(thdr)); + memset(&msgh, 0, sizeof(msgh)); + + unsigned seq = (unsigned) rand(); + + thdr.syn = 1; + thdr.seq = htonl(seq); + thdr.source = htons(SRC_PORT); + thdr.dest = htons(DEST_PORT); + thdr.doff = 5; + thdr.check = tcp_csum((unsigned short *) &thdr, sizeof(thdr)); + + msgh.msg_iov = &iov[1]; // 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)); - /* Polling for new timestamps */ - struct pollfd pfd = { - .fd = sd, - .events = POLLIN - }; -retry: if (poll(&pfd, 1, 10000) < 0) - error(-1, errno, "Failed poll"); - if ((dir == TX) && !(pfd.revents & POLLERR)) + msgh.msg_iov = &iov[0]; // receive IP + TCP header + msgh.msg_iovlen = 2; + +retry: len = ts_recvmsg(sd, &msgh, 0, &ts_ack); + if (len < 0) + error(-1, 0, "Failed to receive ACK / RST packet"); + + //printf("TCP: len=%u, syn=%u, ack=%u, rst=%u, seq=%u, ack_seq=%u, src=%u, dst=%u\n", + // 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(DEST_PORT) || thdr.dest != htons(SRC_PORT)) { + printf("Skipping invalid ports\n"); goto retry; - - int id, key; - if (ts_recvmsg(sd, &msgh, (dir == TX) ? MSG_ERRQUEUE : 0, ts, &key, &id) < 0) - error(-1, errno, "Failed to receive TS"); + } + else if (!thdr.rst && !(thdr.ack && thdr.syn)) { + printf("Skipping invalid flags\n"); + goto retry; + } + else if (ntohl(thdr.ack_seq) != seq + 1) { + printf("Skipping invalid seq\n"); + goto retry; + } - printf("Received key=%d, id=%d\n", key, id); + *ts = time_diff(&ts_syn, &ts_ack); - for (int i = 0; i < 3; i++) - ts_print(&ts[i]); - return 0; } + +int probe(int argc, char *argv[]) +{ + /* Parse address */ + struct nl_addr *addr; + struct sockaddr_in sin; + char buf[128]; + + if (argc != 1) + error(-1, 0, "usage: netem probe IP-ADDRESS"); + + if (nl_addr_parse(argv[0], AF_UNSPEC, &addr)) + error(-1, 0, "Failed to parse address: %s", argv[0]); + + printf("Start probing: %s\n", nl_addr2str(addr, buf, sizeof(buf))); + + socklen_t sinlen = sizeof(sin); + if (nl_addr_fill_sockaddr(addr, (struct sockaddr *) &sin, &sinlen)) + error(-1, 0, "Failed to fill sockaddr"); + + /* Create RAW socket */ + int sd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); + if (sd < 0) + error(-1, errno, "Failed to create socket"); + + /* Bind socket to destination */ + if (connect(sd, (struct sockaddr *) &sin, sizeof(sin))) + error(-1, 0, "Failed to connect socket"); + + /* Enable Kernel TS support */ + if (ts_enable_if("lo")) + perror("Failed to enable timestamping"); + if (ts_enable_sd(sd)) + perror("Failed to set SO_TIMESTAMPING"); + + /* Prepare payload */ + struct timespec ts; + + /* Prepare statistics */ + struct hist hist; + hist_create(&hist, 0, 0, 1); + + while (hist.total < cfg.limit) { + probe_tcp(sd, &ts); + + double rtt = time_to_double(&ts); + hist_put(&hist, rtt); + + printf("n=%u, rtt=%f, min=%f, max=%f, avg=%f, stddev=%f\n", + hist.total, rtt, hist.lowest, hist.highest, hist_mean(&hist), hist_stddev(&hist)); + + /* Warmup: adjust histogram after rough estimation of RTT */ + if (hist.total == 20 && hist.high == 0) { + double span = hist.highest - hist.lowest; + hist_destroy(&hist); + hist_create(&hist, MAX(0, hist.lowest - span * 0.1), hist.highest + span * 0.2, 1e-3); + printf("Created new histogram: high=%f, low=%f, buckets=%u\n", + hist.high, hist.low, hist.length); + } + + //usleep(250 * 1e3); // 250 ms + } + + hist_print(&hist); + hist_destroy(&hist); + + return 0; +} \ No newline at end of file diff --git a/project/tc.c b/project/tc.c index 86af039..464a79a 100644 --- a/project/tc.c +++ b/project/tc.c @@ -1,6 +1,17 @@ -#include -#include -#include +/** Trafic Controller (TC) related functions + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#include +#include +#include +#include +#include + +#include #include "tc.h" @@ -16,103 +27,115 @@ struct rtnl_link * tc_get_link(struct nl_sock *sock, const char *dev) return link; } -int tc_init_prio(struct nl_sock *sock) +int tc_prio(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc) { - q = rtnl_qdisc_alloc(); + /* This is the default priomap +1 for every entry. + * The first band (value == 0) with the highest priority is reserved for the netem traffic */ + uint8_t map[] = { 2, 3, 3, 3, 2, 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2 }; - rtnl_tc_set_ifindex(TC_CAST(q), if_index); + struct rtnl_qdisc *q = rtnl_qdisc_alloc(); + + rtnl_tc_set_link(TC_CAST(q), link); rtnl_tc_set_parent(TC_CAST(q), TC_H_ROOT); rtnl_tc_set_handle(TC_CAST(q), TC_HANDLE(1, 0)); rtnl_tc_set_kind(TC_CAST(q), "prio"); - rtnl_qdisc_prio_set_bands (struct rtnl_qdisc *qdisc, int bands); - rtnl_qdisc_prio_set_priomap (struct rtnl_qdisc *qdisc, uint8_t priomap[], int len); + rtnl_qdisc_prio_set_bands(q, 3+1); + rtnl_qdisc_prio_set_priomap(q, map, sizeof(map) / sizeof(map[0])); - rtnl_qdisc_add(sock, q, NLM_F_CREATE); - rtnl_qdisc_put(q); + int ret = rtnl_qdisc_add(sock, q, NLM_F_CREATE); + + *tc = TC_CAST(q); + + return ret; } -int tc_init_netem(struct nl_sock *sock, struct tc_netem *ne) +int tc_netem(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc, struct tc_netem *ne) { - q = rtnl_qdisc_alloc(); + struct rtnl_qdisc *q; + + if (*tc == NULL) { + q = rtnl_qdisc_alloc(); - rtnl_tc_set_ifindex(TC_CAST(q), if_index); - rtnl_tc_set_parent(TC_CAST(q), TC_H_ROOT); - rtnl_tc_set_handle(TC_CAST(q), TC_HANDLE(1, 0)); - rtnl_tc_set_kind(TC_CAST(q), "netem"); + rtnl_tc_set_link(TC_CAST(q), link); + rtnl_tc_set_parent(TC_CAST(q), TC_HANDLE(1, 1)); + rtnl_tc_set_handle(TC_CAST(q), TC_HANDLE(2, 0)); + rtnl_tc_set_kind(TC_CAST(q), "netem"); + } + else + q = (struct rtnl_qdisc *) (*tc); - rtnl_netem_set_gap(q, int); - rtnl_netem_set_reorder_probability(q, int); - rtnl_netem_set_reorder_correlation(q, int); - rtnl_netem_set_corruption_probability(q, int); - rtnl_netem_set_corruption_correlation(q, int); - rtnl_netem_set_loss(q, int); - rtnl_netem_set_loss_correlation(q, int); - rtnl_netem_set_duplicate(q, int); - rtnl_netem_set_duplicate_correlation(q, int); - rtnl_netem_set_delay(q, int); - rtnl_netem_set_jitter(q, int); - rtnl_netem_set_delay_correlation(q, int); - rtnl_netem_set_delay_distribution(q, const char *); + rtnl_netem_set_limit(q, ne->limit); + rtnl_netem_set_gap(q, ne->gap); + rtnl_netem_set_reorder_probability(q, ne->reorder_prob); + rtnl_netem_set_reorder_correlation(q, ne->reorder_corr); + rtnl_netem_set_corruption_probability(q, ne->corruption_prob); + rtnl_netem_set_corruption_correlation(q, ne->corruption_corr); + rtnl_netem_set_loss(q, ne->loss_prob); + rtnl_netem_set_loss_correlation(q, ne->loss_corr); + rtnl_netem_set_duplicate(q, ne->duplication_prob); + rtnl_netem_set_duplicate_correlation(q, ne->duplication_corr); + rtnl_netem_set_delay(q, ne->delay); + rtnl_netem_set_jitter(q, ne->jitter); + rtnl_netem_set_delay_correlation(q, ne->delay_corr); + //rtnl_netem_set_delay_distribution(q, ne->delay_distr); - rtnl_qdisc_add(sock, q, NLM_F_CREATE); - rtnl_qdisc_put(q); + int ret = rtnl_qdisc_add(sock, q, NLM_F_CREATE); + + *tc = TC_CAST(q); + + return ret; } -int tc_init_classifier(struct nl_sock *sock, int mark) +int tc_classifier(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc, int mark, int mask) { struct rtnl_cls *c = rtnl_cls_alloc(); - rtnl_tc_set_link(TC_CAST(c), if_index); - rtnl_tc_set_parent(TC_CAST(c), TC_H_ROOT); - rtnl_tc_set_handle(TC_CAST(c), TC_HANDLE(1, 0)); - rtnl_tc_set_kind(TC_CAST(c), "netem"); + rtnl_tc_set_link(TC_CAST(c), link); + rtnl_tc_set_handle(TC_CAST(c), mark); + rtnl_tc_set_kind(TC_CAST(c), "fw"); - rtnl_fw_set_classid(c, uint32_t classid); - rtnl_fw_set_mask(c, uint32_t mask); - - rtnl_cls_add(sock, cls, int flags); - rtnl_cls_put(cls); -} - -struct nl_sock * tc_init(int mark) -{ - struct nl_sock *sock; - struct rtnl_link *link; - struct rtnl_qdisc *q; - - /* Create connection to netlink */ - sock = nl_socket_alloc(); - nl_connect(sock, NETLINK_ROUTE); - - if (tc_init_prio(sock)) - error(0, -1, "Failed to setup TC: prio qdisc"); - if (tc_init_classifier(sock, mark)) - error(0, -1, "Failed to setup TC: fw filter"); + rtnl_cls_set_protocol(c, ETH_P_ALL); - /* Get interface index */ - int ifindex = tc_get_ifindex; + rtnl_fw_set_classid(c, TC_HANDLE(1, 1)); + rtnl_fw_set_mask(c, mask); - /* Setup classifier */ - rtnl_fw_set_classid (struct rtnl_cls *cls, uint32_t classid); - rtnl_fw_set_mask (struct rtnl_cls *cls, uint32_t mask); - rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags); + int ret = rtnl_cls_add(sock, c, NLM_F_CREATE); - return sock; + *tc = TC_CAST(c); + + return ret; } -int tc_reset(struct nl_sock *) +int tc_reset(struct nl_sock *sock, struct rtnl_link *link) { - struct rtnl_qdisc *q; + struct rtnl_qdisc *q = rtnl_qdisc_alloc(); /* Restore default qdisc by deleting the root qdisc (see tc-pfifo_fast(8)) */ - q = rtnl_qdisc_alloc(); rtnl_tc_set_link(TC_CAST(q), link); - rtnl_qdisc_delete(sock, q); + rtnl_tc_set_parent(TC_CAST(q), TC_H_ROOT); + + int ret = rtnl_qdisc_delete(sock, q); rtnl_qdisc_put(q); - - /* Shutdown */ - nl_close(sock); - nl_socket_free(sock); + + return ret; } +int tc_get_stats(struct nl_sock *sock, struct rtnl_tc *tc, struct tc_stats *stats) +{ + uint64_t *counters = (uint64_t *) stats; + + struct nl_cache *cache; + + rtnl_qdisc_alloc_cache(sock, &cache); + + for (int i = 0; i <= RTNL_TC_STATS_MAX; i++) + counters[i] = rtnl_tc_get_stat(tc, i); + + nl_cache_free(cache); +} + +int tc_print_stats(struct tc_stats *stats) +{ + +} diff --git a/project/tc.h b/project/tc.h index 0a1a791..15caf3e 100644 --- a/project/tc.h +++ b/project/tc.h @@ -1,14 +1,25 @@ -#include -#include -#include +/** Trafic Controller (TC) related functions + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#ifndef _TC_H_ +#define _TC_H_ + +#include +#include +#include struct tc_netem { + int limit; int gap; - int reorder_prop; + int reorder_prob; int reorder_corr; - int corruption_prop; + int corruption_prob; int corruption_corr; - int loss_prop; + int loss_prob; int loss_corr; int duplication_prob; int duplication_corr; @@ -18,14 +29,30 @@ struct tc_netem { char *delay_distr; }; +/*struct tc_stats { + uint64_t packets; // Number of packets seen. + uint64_t bytes; // Total bytes seen. + uint64_t rate_bps; // Current bits/s (rate estimator) + uint64_t rate_pps; // Current packet/s (rate estimator) + uint64_t qlen; // Current queue length. + uint64_t backlog; // Current backlog length. + uint64_t drops; // Total number of packets dropped. + uint64_t requeues; // Total number of requeues. + uint64_t overlimits; // Total number of overlimits. +};*/ + struct rtnl_link * tc_get_link(struct nl_sock *sock, const char *dev); -int tc_init_prio(struct nl_sock *sock); +int tc_prio(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc); -int tc_init_netem(struct nl_sock *sock, struct tc_netem *ne); +int tc_netem(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc, struct tc_netem *ne); -int tc_init_classifier(struct nl_sock *sock, int mark); +int tc_classifier(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc, int mark, int mask); -struct nl_sock * tc_init(int mark); +int tc_reset(struct nl_sock *sock, struct rtnl_link *link); -int tc_reset(struct nl_sock *); +int tc_get_stats(struct nl_sock *sock, struct rtnl_tc *tc, struct tc_stats *stats); + +int tc_print_stats(struct tc_stats *stats); + +#endif \ No newline at end of file diff --git a/project/tcp.c b/project/tcp.c new file mode 100644 index 0000000..f364b47 --- /dev/null +++ b/project/tcp.c @@ -0,0 +1,18 @@ +/** Userspace TCP functions. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +unsigned short tcp_csum(unsigned short *buf, int len) +{ + unsigned long sum; + + for(sum=0; len>0; len--) + sum += *buf++; + sum = (sum >> 16) + (sum &0xffff); + sum += (sum >> 16); + + return (unsigned short)(~sum); +} \ No newline at end of file diff --git a/project/tcp.h b/project/tcp.h new file mode 100644 index 0000000..4411fe3 --- /dev/null +++ b/project/tcp.h @@ -0,0 +1,14 @@ +/** Userspace TCP functions. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#ifndef _TCP_H_ +#define _TCP_H_ + +/** Simple checksum function, may use others such as Cyclic Redundancy Check, CRC */ +unsigned short tcp_csum(unsigned short *buf, int len); + +#endif \ No newline at end of file diff --git a/project/ts.c b/project/ts.c index 0396e90..50b9ac0 100644 --- a/project/ts.c +++ b/project/ts.c @@ -1,11 +1,22 @@ +/** Hardware / Kernelspace Timestamping of network packets. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ +#define _POSIX_C_SOURCE 199309L + #include #include #include +#include +#include +#include -#include #include #include #include +#include #include #include @@ -14,15 +25,37 @@ #include #include -#define SIOCSHWTSTAMP 0x89b0 -#define SIOCGHWTSTAMP 0x89b1 +#include "ts.h" + +#define SIOCSHWTSTAMP 0x89b0 +#define SIOCGHWTSTAMP 0x89b1 ssize_t ts_sendmsg(int sd, const struct msghdr *msg, int flags, struct timespec *ts) { - return sendmsg(sd, msg, flags); + /* Waiting for ACK or RST */ + struct pollfd pfd = { + .fd = sd, + .events = POLLIN + }; + + clock_gettime(CLOCK_REALTIME, ts); // Fallback if now kernelspace TS are available + ssize_t ret = sendmsg(sd, msg, flags); + return ret; // TODO + + /* Wait for TS */ +retry: if (poll(&pfd, 1, 10000) < 0) + error(-1, errno, "Failed poll"); + if (!(pfd.revents & POLLERR)) + goto retry; + + struct msghdr msg2 = { 0 }; + if (ts_recvmsg(sd, &msg2, MSG_ERRQUEUE, ts) < 0) + error(-1, errno, "Failed to receive TS"); + + return ret; } -ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts, int *key, int *id) +ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts) { char buf[1024]; msgh->msg_control = &buf; @@ -32,8 +65,11 @@ ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts, struct sock_extended_err *serr = NULL; struct timespec *tss = NULL; - int ret = recvmsg(sd, msgh, flags); + ssize_t ret = recvmsg(sd, msgh, flags); if (ret >= 0) { + clock_gettime(CLOCK_REALTIME, ts); // Fallback if now kernelspace TS are available + return ret; // TODO + for (cmsg = CMSG_FIRSTHDR(msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(msgh, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPING) tss = (struct timespec *) CMSG_DATA(cmsg); @@ -42,26 +78,16 @@ ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts, serr = (struct sock_extended_err *) CMSG_DATA(cmsg); } - if (!tss) - return -2; + if (tss) + *ts = tss[0]; - for (int i = 0; i < 3; i++) - ts[i] = tss[i]; - - if (serr) { - *key = serr->ee_info; - *id = serr->ee_data; - } + if (serr) + printf("Debug: key = %u, id = %u\n", serr->ee_info, serr->ee_data); } return ret; } -void ts_print(struct timespec *ts) -{ - printf("%lld.%.9ld\n", (long long) ts->tv_sec, ts->tv_nsec); -} - int ts_enable_sd(int sd) { int val; diff --git a/project/ts.h b/project/ts.h index cb945ff..4741af1 100644 --- a/project/ts.h +++ b/project/ts.h @@ -1,10 +1,19 @@ +/** Hardware / Kernelspace Timestamping of network packets. + * + * @author Steffen Vogel + * @copyright 2014-2015, Steffen Vogel + * @license GPLv3 + *********************************************************************************/ + +#ifndef _TS_H_ +#define _TS_H_ + ssize_t ts_sendmsg(int sd, const struct msghdr *msgh, int flags, struct timespec *ts); -ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts, int *key, int *id); - -void ts_print(struct timespec *ts); +ssize_t ts_recvmsg(int sd, struct msghdr *msgh, int flags, struct timespec *ts); int ts_enable_if(const char *dev); int ts_enable_sd(int sd); +#endif \ No newline at end of file