network: introduce TrafficControl

Add network delay to a interface
This commit is contained in:
Susant Sahani 2019-10-07 16:19:00 +02:00 committed by Yu Watanabe
parent 644ee25461
commit 0f5bd7fe24
19 changed files with 1875 additions and 1 deletions

View File

@ -2287,6 +2287,56 @@
</variablelist>
</refsect1>
<refsect1>
<title>[TrafficControlQueueingDiscipline] Section Options</title>
<para>The <literal>[TrafficControlQueueingDiscipline]</literal> section manages the Traffic control. It can be used
to configure the kernel packet scheduler and simulate packet delay and loss for UDP or TCP applications,
or limit the bandwidth usage of a particular service to simulate internet connections.</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>
or <literal>clsact</literal>. Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetworkEmulatorDelaySec=</varname></term>
<listitem>
<para>Specifies the fixed amount of delay to be added to all packets going out of the
interface. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetworkEmulatorDelayJitterSec=</varname></term>
<listitem>
<para>Specifies the chosen delay to be added to the packets outgoing to the network
interface. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetworkEmulatorPacketLimit=</varname></term>
<listitem>
<para>Specifies the maximum number of packets the qdisc may hold queued at a time.
An unsigned integer ranges 0 to 4294967294. Defaults to 1000.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>NetworkEmulatorLossRate=</varname></term>
<listitem>
<para>Specifies an independent loss probability to be added to the packets outgoing from the
network interface. Takes a percentage value, suffixed with "%". Defaults to unset.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[BridgeVLAN] Section Options</title>
<para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts

1184
src/basic/linux/pkt_sched.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,9 +18,10 @@
#include <linux/if_link.h>
#include <linux/if_macsec.h>
#include <linux/if_tunnel.h>
#include <linux/nexthop.h>
#include <linux/l2tp.h>
#include <linux/nexthop.h>
#include <linux/nl80211.h>
#include <linux/pkt_sched.h>
#include <linux/veth.h>
#include <linux/wireguard.h>
@ -733,6 +734,18 @@ static const NLTypeSystem rtnl_nexthop_type_system = {
.types = rtnl_nexthop_types,
};
static const NLType rtnl_qdisc_types[] = {
[TCA_KIND] = { .type = NETLINK_TYPE_STRING },
[TCA_OPTIONS] = { .size = sizeof(struct tc_netem_qopt) },
[TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
[TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
};
static const NLTypeSystem rtnl_qdisc_type_system = {
.count = ELEMENTSOF(rtnl_qdisc_types),
.types = rtnl_qdisc_types,
};
static const NLType rtnl_types[] = {
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
@ -758,6 +771,9 @@ static const NLType rtnl_types[] = {
[RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
[RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
[RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
[RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
};
const NLTypeSystem rtnl_type_system_root = {

View File

@ -41,6 +41,10 @@ static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
}
static inline bool rtnl_message_type_is_qdisc(uint16_t type) {
return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
}
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);

View File

@ -1033,3 +1033,46 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(const sd_netlink_m
return 0;
}
int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) {
struct tcmsg *tcm;
int r;
assert_return(rtnl_message_type_is_qdisc(nlmsg_type), -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
if (r < 0)
return r;
if (nlmsg_type == RTM_NEWQDISC)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
tcm = NLMSG_DATA((*ret)->hdr);
tcm->tcm_family = tcm_family;
tcm->tcm_ifindex = tcm_ifindex;
return 0;
}
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_parent = parent;
return 0;
}
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_handle = handle;
return 0;
}

View File

@ -105,6 +105,12 @@ sources = files('''
networkd-util.h
networkd-wifi.c
networkd-wifi.h
tc/netem.c
tc/netem.h
tc/qdisc.c
tc/qdisc.h
tc/tc-util.c
tc/tc-util.h
'''.split())
systemd_networkd_sources = files('networkd.c')

View File

@ -43,6 +43,7 @@
#include "tmpfile-util.h"
#include "udev-util.h"
#include "util.h"
#include "tc/qdisc.h"
#include "virt.h"
uint32_t link_get_vrf_table(Link *link) {
@ -2581,6 +2582,8 @@ static int link_drop_config(Link *link) {
}
static int link_configure(Link *link) {
QDiscs *qdisc;
Iterator i;
int r;
assert(link);
@ -2590,6 +2593,9 @@ static int link_configure(Link *link) {
if (link->iftype == ARPHRD_CAN)
return link_configure_can(link);
ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i)
(void) qdisc_configure(link, qdisc);
/* Drop foreign config, but ignore loopback or critical devices.
* We do not want to remove loopback address or addresses used for root NFS. */
if (!(link->flags & IFF_LOOPBACK) &&

View File

@ -78,6 +78,7 @@ typedef struct Link {
unsigned nexthop_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned qdisc_messages;
unsigned enslaving;
Set *addresses;

View File

@ -13,6 +13,8 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "vlan-util.h"
#include "tc/qdisc.h"
#include "tc/netem.h"
%}
struct ConfigPerfItem;
%null_strings
@ -241,6 +243,11 @@ CAN.BitRate, config_parse_si_size,
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
TrafficControlQueueingDiscipline.Parent, config_parse_tc_qdiscs_parent, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_tc_network_emulator_delay, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_tc_network_emulator_delay, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_loss_rate, 0, 0
TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)

View File

@ -471,6 +471,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"IPv6PrefixDelegation\0"
"IPv6Prefix\0"
"IPv6RoutePrefix\0"
"TrafficControlQueueingDiscipline\0"
"CAN\0",
config_item_perf_lookup, network_network_gperf_lookup,
CONFIG_PARSE_WARN, network);
@ -663,6 +664,7 @@ static Network *network_free(Network *network) {
hashmap_free(network->address_labels_by_section);
hashmap_free(network->prefixes_by_section);
hashmap_free(network->rules_by_section);
ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
if (network->manager &&
network->manager->duids_requesting_uuid)

View File

@ -28,6 +28,7 @@
#include "networkd-util.h"
#include "ordered-set.h"
#include "resolve-util.h"
#include "tc/qdisc.h"
typedef enum IPv6PrivacyExtensions {
/* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
@ -265,6 +266,7 @@ struct Network {
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
OrderedHashmap *qdiscs_by_section;
/* All kinds of DNS configuration */
struct in_addr_data *dns;

213
src/network/tc/netem.c Normal file
View File

@ -0,0 +1,213 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#include <linux/pkt_sched.h>
#include <math.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "hashmap.h"
#include "in-addr-util.h"
#include "netem.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "qdisc.h"
#include "string-util.h"
#include "tc-util.h"
#include "util.h"
int network_emulator_new(NetworkEmulator **ret) {
NetworkEmulator *ne = NULL;
ne = new(NetworkEmulator, 1);
if (!ne)
return -ENOMEM;
*ne = (NetworkEmulator) {
.delay = USEC_INFINITY,
.jitter = USEC_INFINITY,
};
*ret = TAKE_PTR(ne);
return 0;
}
int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req) {
struct tc_netem_qopt opt = {
.limit = 1000,
};
int r;
assert(link);
assert(qdisc);
assert(req);
if (qdisc->ne.limit > 0)
opt.limit = qdisc->ne.limit;
if (qdisc->ne.loss > 0)
opt.loss = qdisc->ne.loss;
if (qdisc->ne.delay != USEC_INFINITY) {
r = tc_time_to_tick(qdisc->ne.delay, &opt.latency);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate latency in TCA_OPTION: %m");
}
if (qdisc->ne.jitter != USEC_INFINITY) {
r = tc_time_to_tick(qdisc->ne.jitter, &opt.jitter);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m");
}
r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_netem_qopt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_OPTION attribute: %m");
return 0;
}
int config_parse_tc_network_emulator_delay(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
Network *network = data;
usec_t u;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (isempty(rvalue)) {
if (streq(lvalue, "NetworkEmulatorDelaySec"))
qdisc->ne.delay = USEC_INFINITY;
else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
qdisc->ne.jitter = USEC_INFINITY;
qdisc = NULL;
return 0;
}
r = parse_sec(rvalue, &u);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (streq(lvalue, "NetworkEmulatorDelaySec"))
qdisc->ne.delay = u;
else if (streq(lvalue, "NetworkEmulatorDelayJitterSec"))
qdisc->ne.jitter = u;
qdisc->has_network_emulator = true;
qdisc = NULL;
return 0;
}
int config_parse_tc_network_emulator_loss_rate(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (isempty(rvalue)) {
qdisc->ne.loss = 0;
qdisc = NULL;
return 0;
}
r = parse_tc_percent(rvalue, &qdisc->ne.loss);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'NetworkEmularorLossRate=', ignoring assignment: %s",
rvalue);
return 0;
}
qdisc = NULL;
return 0;
}
int config_parse_tc_network_emulator_packet_limit(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (isempty(rvalue)) {
qdisc->ne.limit = 0;
qdisc = NULL;
return 0;
}
r = safe_atou(rvalue, &qdisc->ne.limit);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'NetworkEmulatorPacketLimit=', ignoring assignment: %s",
rvalue);
return 0;
}
qdisc = NULL;
return 0;
}

28
src/network/tc/netem.h Normal file
View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#pragma once
#include "sd-netlink.h"
#include "conf-parser.h"
#include "macro.h"
#include "../networkd-link.h"
#include "time-util.h"
typedef struct NetworkEmulator NetworkEmulator;
typedef struct QDiscs QDiscs;
struct NetworkEmulator {
usec_t delay;
usec_t jitter;
uint32_t limit;
uint32_t loss;
};
int network_emulator_new(NetworkEmulator **ret);
int network_emulator_fill_message(Link *link, QDiscs *qdisc, sd_netlink_message *req);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_delay);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_loss_rate);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_network_emulator_packet_limit);

196
src/network/tc/qdisc.c Normal file
View File

@ -0,0 +1,196 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "in-addr-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "qdisc.h"
#include "set.h"
#include "string-util.h"
#include "util.h"
static int qdisc_new(QDiscs **ret) {
QDiscs *qdisc;
qdisc = new(QDiscs, 1);
if (!qdisc)
return -ENOMEM;
*qdisc = (QDiscs) {
.family = AF_UNSPEC,
.parent = TC_H_ROOT,
};
*ret = TAKE_PTR(qdisc);
return 0;
}
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(qdisc_freep) QDiscs *qdisc = NULL;
int r;
assert(network);
assert(ret);
assert(!!filename == (section_line > 0));
if (filename) {
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
qdisc = ordered_hashmap_get(network->qdiscs_by_section, n);
if (qdisc) {
*ret = TAKE_PTR(qdisc);
return 0;
}
}
r = qdisc_new(&qdisc);
if (r < 0)
return r;
qdisc->network = network;
if (filename) {
qdisc->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
if (r < 0)
return r;
}
*ret = TAKE_PTR(qdisc);
return 0;
}
void qdisc_free(QDiscs *qdisc) {
if (!qdisc)
return;
if (qdisc->network && qdisc->section)
ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
network_config_section_free(qdisc->section);
free(qdisc);
}
static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
assert(link->qdisc_messages > 0);
link->qdisc_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set QDisc: %m");
return 1;
}
return 1;
}
int qdisc_configure(Link *link, QDiscs *qdisc) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, qdisc->family, link->ifindex);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_NEWQDISC message: %m");
r = sd_rtnl_message_set_qdisc_parent(req, qdisc->parent);
if (r < 0)
return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
if (qdisc->parent == TC_H_CLSACT) {
r = sd_rtnl_message_set_qdisc_handle(req, TC_H_MAKE(TC_H_CLSACT, 0));
if (r < 0)
return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
r = sd_netlink_message_append_string(req, TCA_KIND, "clsact");
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
}
if (qdisc->has_network_emulator) {
r = sd_netlink_message_append_string(req, TCA_KIND, "netem");
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
r = network_emulator_fill_message(link, qdisc, req);
if (r < 0)
return r;
}
r = netlink_call_async(link->manager->rtnl, NULL, req, qdisc_handler, link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
link->qdisc_messages++;
return 0;
}
int config_parse_tc_qdiscs_parent(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(network, filename, section_line, &qdisc);
if (r < 0)
return r;
if (streq(rvalue, "root"))
qdisc->parent = TC_H_ROOT;
else if (streq(rvalue, "clsact"))
qdisc->parent = TC_H_CLSACT;
else {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse [QueueDiscs] 'Parent=', ignoring assignment: %s",
rvalue);
return 0;
}
qdisc = NULL;
return 0;
}

35
src/network/tc/qdisc.h Normal file
View File

@ -0,0 +1,35 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "macro.h"
#include "netem.h"
#include "../networkd-util.h"
typedef struct QDiscs QDiscs;
struct QDiscs {
NetworkConfigSection *section;
Network *network;
Link *link;
int family;
uint32_t handle;
uint32_t parent;
bool has_network_emulator:1;
NetworkEmulator ne;
};
void qdisc_free(QDiscs *qdisc);
int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret);
int qdisc_configure(Link *link, QDiscs *qdisc);
DEFINE_NETWORK_SECTION_FUNCTIONS(QDiscs, qdisc_free);
CONFIG_PARSER_PROTOTYPE(config_parse_tc_qdiscs_parent);

63
src/network/tc/tc-util.c Normal file
View File

@ -0,0 +1,63 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#include "alloc-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "tc-util.h"
#include "time-util.h"
static int tc_init(double *ticks_in_usec) {
uint32_t clock_resolution, ticks_to_usec, usec_to_ticks;
_cleanup_free_ char *line = NULL;
double clock_factor;
int r;
r = read_one_line_file("/proc/net/psched", &line);
if (r < 0)
return r;
r = sscanf(line, "%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution);
if (r < 3)
return -EIO;
clock_factor = (double) clock_resolution / USEC_PER_SEC;
*ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor;
return 0;
}
int tc_time_to_tick(usec_t t, uint32_t *ret) {
static double ticks_in_usec = -1;
usec_t a;
int r;
assert(ret);
if (ticks_in_usec < 0) {
r = tc_init(&ticks_in_usec);
if (r < 0)
return r;
}
a = t * ticks_in_usec;
if (a > UINT32_MAX)
return -ERANGE;
*ret = a;
return 0;
}
int parse_tc_percent(const char *s, uint32_t *percent) {
int r;
assert(s);
assert(percent);
r = parse_permille(s);
if (r < 0)
return r;
*percent = (double) r / 1000 * UINT32_MAX;
return 0;
}

8
src/network/tc/tc-util.h Normal file
View File

@ -0,0 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#pragma once
#include "time-util.h"
int tc_time_to_tick(usec_t t, uint32_t *ret);
int parse_tc_percent(const char *s, uint32_t *percent);

View File

@ -202,6 +202,10 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_type(const sd_netlink_message *m
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags);
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, unsigned *flags);
int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle);
/* genl */
int sd_genl_socket_open(sd_netlink **nl);
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);

View File

@ -262,3 +262,9 @@ DNS=
[NextHop]
Id=
Gateway=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
NetworkEmulatorDelayJitterSec=
NetworkEmulatorLossRate=
NetworkEmulatorPacketLimit=