Merge pull request #13747 from ssahani/tc-qdisc

network: introduce Traffic Control
This commit is contained in:
Yu Watanabe
2019-10-30 14:35:51 +09:00
committed by GitHub
21 changed files with 1942 additions and 4 deletions
File diff suppressed because it is too large Load Diff
+17 -1
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 = {
+4
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);
+43
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;
}
+6
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')
+30
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) {
@@ -1094,6 +1095,9 @@ void link_check_ready(Link *link) {
if (!link->routing_policy_rules_configured)
return;
if (!link->qdiscs_configured)
return;
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
@@ -2580,6 +2584,28 @@ static int link_drop_config(Link *link) {
return 0;
}
static int link_configure_qdiscs(Link *link) {
QDiscs *qdisc;
Iterator i;
int r;
link->qdiscs_configured = false;
link->qdisc_messages = 0;
ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
r = qdisc_configure(link, qdisc);
if (r < 0)
return r;
}
if (link->qdisc_messages == 0)
link->qdiscs_configured = true;
else
log_link_debug(link, "Configuring QDiscs");
return 0;
}
static int link_configure(Link *link) {
int r;
@@ -2587,6 +2613,10 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
r = link_configure_qdiscs(link);
if (r < 0)
return r;
if (link->iftype == ARPHRD_CAN)
return link_configure_can(link);
+2
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;
@@ -113,6 +114,7 @@ typedef struct Link {
bool static_routes_ready:1;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1;
bool qdiscs_configured:1;
bool setting_mtu:1;
LIST_HEAD(Address, pool_addresses);
+7
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)
+2
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)
+2
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
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
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);
+203
View File
@@ -0,0 +1,203 @@
/* 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");
link_enter_failed(link);
return 1;
}
if (link->route_messages == 0) {
log_link_debug(link, "QDiscs configured");
link->qdiscs_configured = true;
link_check_ready(link);
}
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
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
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
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);
+4
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);