huge commit of work..
This commit is contained in:
parent
ecd056a3a0
commit
9d61959f82
11 changed files with 538 additions and 177 deletions
|
@ -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
|
||||
|
|
29
project/config.h
Normal file
29
project/config.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/** Global configuration.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @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
|
77
project/emulate.c
Normal file
77
project/emulate.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/** Setup and update netem qdisc.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
|
||||
#include <error.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,4 +1,11 @@
|
|||
#define _POSIX_C_SOURCE 199309L
|
||||
/** Main routine.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -7,15 +14,25 @@
|
|||
#include <signal.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
182
project/probe.c
182
project/probe.c
|
@ -1,3 +1,10 @@
|
|||
/** Probing for RTT, Loss, Duplication, Corruption.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
#define _BSD_SOURCE
|
||||
|
||||
|
@ -18,59 +25,148 @@
|
|||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <netlink/addr.h>
|
||||
|
||||
#include <linux/ip.h>
|
||||
|
||||
#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;
|
||||
}
|
167
project/tc.c
167
project/tc.c
|
@ -1,6 +1,17 @@
|
|||
#include <libnl3/netlink/route/qdisc/netem.h>
|
||||
#include <libnl3/netlink/route/qdisc/prio.h>
|
||||
#include <libnl3/netlink/route/cls/fw.h>
|
||||
/** Trafic Controller (TC) related functions
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
|
||||
#include <netlink/route/qdisc/netem.h>
|
||||
#include <netlink/route/qdisc/prio.h>
|
||||
#include <netlink/route/cls/fw.h>
|
||||
#include <netlink/fib_lookup/request.h>
|
||||
#include <netlink/fib_lookup/lookup.h>
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
49
project/tc.h
49
project/tc.h
|
@ -1,14 +1,25 @@
|
|||
#include <libnl3/netlink/route/tc.h>
|
||||
#include <libnl3/netlink/route/qdisc.h>
|
||||
#include <libnl3/netlink/route/classifier.h>
|
||||
/** Trafic Controller (TC) related functions
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
|
||||
#ifndef _TC_H_
|
||||
#define _TC_H_
|
||||
|
||||
#include <netlink/route/tc.h>
|
||||
#include <netlink/route/qdisc.h>
|
||||
#include <netlink/route/classifier.h>
|
||||
|
||||
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
|
18
project/tcp.c
Normal file
18
project/tcp.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
/** Userspace TCP functions.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @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);
|
||||
}
|
14
project/tcp.h
Normal file
14
project/tcp.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/** Userspace TCP functions.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @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
|
66
project/ts.c
66
project/ts.c
|
@ -1,11 +1,22 @@
|
|||
/** Hardware / Kernelspace Timestamping of network packets.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @copyright 2014-2015, Steffen Vogel
|
||||
* @license GPLv3
|
||||
*********************************************************************************/
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/poll.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
|
@ -14,15 +25,37 @@
|
|||
#include <linux/errqueue.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
|
||||
#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;
|
||||
|
|
15
project/ts.h
15
project/ts.h
|
@ -1,10 +1,19 @@
|
|||
/** Hardware / Kernelspace Timestamping of network packets.
|
||||
*
|
||||
* @author Steffen Vogel <post@steffenvogel.de>
|
||||
* @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
|
Loading…
Add table
Reference in a new issue