184 lines
4.5 KiB
C
184 lines
4.5 KiB
C
/** Trafic Controller (TC) related functions
|
|
*
|
|
* @author Steffen Vogel <post@steffenvogel.de>
|
|
* @copyright 2014-2017, Steffen Vogel
|
|
* @license GPLv3
|
|
*********************************************************************************/
|
|
|
|
#define _POSIX_C_SOURCE 200112L
|
|
#include <netdb.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#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"
|
|
|
|
struct rtnl_link * tc_get_link(struct nl_sock *sock, const char *dev)
|
|
{
|
|
struct nl_cache *cache;
|
|
struct rtnl_link *link;
|
|
|
|
rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache);
|
|
link = rtnl_link_get_by_name(cache, "eth0");
|
|
nl_cache_put(cache);
|
|
|
|
return link;
|
|
}
|
|
|
|
int tc_prio(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc)
|
|
{
|
|
/* 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, 0 };
|
|
|
|
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(q, 3+1);
|
|
rtnl_qdisc_prio_set_priomap(q, map, 7);
|
|
|
|
int ret = rtnl_qdisc_add(sock, q, NLM_F_CREATE);
|
|
|
|
*tc = TC_CAST(q);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int tc_netem(struct nl_sock *sock, struct rtnl_link *link, struct rtnl_tc **tc)
|
|
{
|
|
struct rtnl_qdisc *q;
|
|
|
|
if (*tc == NULL) {
|
|
q = rtnl_qdisc_alloc();
|
|
|
|
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);
|
|
|
|
int ret = rtnl_qdisc_add(sock, q, NLM_F_CREATE);
|
|
|
|
*tc = TC_CAST(q);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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), link);
|
|
rtnl_tc_set_handle(TC_CAST(c), mark);
|
|
rtnl_tc_set_kind(TC_CAST(c), "fw");
|
|
|
|
rtnl_cls_set_protocol(c, ETH_P_ALL);
|
|
|
|
rtnl_fw_set_classid(c, TC_HANDLE(1, 1));
|
|
rtnl_fw_set_mask(c, mask);
|
|
|
|
int ret = rtnl_cls_add(sock, c, NLM_F_CREATE);
|
|
|
|
*tc = TC_CAST(c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int tc_reset(struct nl_sock *sock, struct rtnl_link *link)
|
|
{
|
|
struct rtnl_qdisc *q = rtnl_qdisc_alloc();
|
|
|
|
/* Restore default qdisc by deleting the root qdisc (see tc-pfifo_fast(8)) */
|
|
rtnl_tc_set_link(TC_CAST(q), link);
|
|
rtnl_tc_set_parent(TC_CAST(q), TC_H_ROOT);
|
|
|
|
int ret = rtnl_qdisc_delete(sock, q);
|
|
rtnl_qdisc_put(q);
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tc_print_stats(struct tc_statistics *stats)
|
|
{
|
|
printf("packets %lu bytes %lu\n", stats->packets, stats->bytes);
|
|
}
|
|
|
|
int tc_print_netem(struct rtnl_tc *tc)
|
|
{
|
|
struct rtnl_qdisc *ne = (struct rtnl_qdisc *) tc;
|
|
|
|
if (rtnl_netem_get_limit(ne) > 0)
|
|
printf("limit %upkts", rtnl_netem_get_limit(ne));
|
|
|
|
if (rtnl_netem_get_delay(ne) > 0) {
|
|
printf("delay %fms ", rtnl_netem_get_delay(ne) / 1000.0);
|
|
|
|
if (rtnl_netem_get_jitter(ne) > 0) {
|
|
printf("jitter %fms ", rtnl_netem_get_jitter(ne) / 1000.0);
|
|
|
|
if (rtnl_netem_get_delay_correlation(ne) > 0)
|
|
printf("%u%% ", rtnl_netem_get_delay_correlation(ne));
|
|
}
|
|
}
|
|
|
|
if (rtnl_netem_get_loss(ne) > 0) {
|
|
printf("loss %u%% ", rtnl_netem_get_loss(ne));
|
|
|
|
if (rtnl_netem_get_loss_correlation(ne) > 0)
|
|
printf("%u%% ", rtnl_netem_get_loss_correlation(ne));
|
|
}
|
|
|
|
if (rtnl_netem_get_reorder_probability(ne) > 0) {
|
|
printf(" reorder%u%% ", rtnl_netem_get_reorder_probability(ne));
|
|
|
|
if (rtnl_netem_get_reorder_correlation(ne) > 0)
|
|
printf("%u%% ", rtnl_netem_get_reorder_correlation(ne));
|
|
}
|
|
|
|
if (rtnl_netem_get_corruption_probability(ne) > 0) {
|
|
printf("corruption %u%% ", rtnl_netem_get_corruption_probability(ne));
|
|
|
|
if (rtnl_netem_get_corruption_correlation(ne) > 0)
|
|
printf("%u%% ", rtnl_netem_get_corruption_correlation(ne));
|
|
}
|
|
|
|
if (rtnl_netem_get_duplicate(ne) > 0) {
|
|
printf("duplication %u%% ", rtnl_netem_get_duplicate(ne));
|
|
|
|
if (rtnl_netem_get_duplicate_correlation(ne) > 0)
|
|
printf("%u%% ", rtnl_netem_get_duplicate_correlation(ne));
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
return 0;
|
|
}
|