Merge pull request #14890 from yuwata/network-tc-next

network: tc-next
This commit is contained in:
Yu Watanabe 2020-03-07 01:44:15 +09:00 committed by GitHub
commit bba1f90ff5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 2230 additions and 220 deletions

View File

@ -2348,7 +2348,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2412,7 +2414,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2489,6 +2493,38 @@
</variablelist>
</refsect1>
<refsect1>
<title>[StochasticFairBlue] Section Options</title>
<para>The <literal>[StochasticFairBlue]</literal> section manages the queueing discipline
(qdisc) of stochastic fair blue (sfb).</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>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[StochasticFairnessQueueing] Section Options</title>
<para>The <literal>[StochasticFairnessQueueing]</literal> section manages the queueing discipline
@ -2499,7 +2535,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2520,6 +2558,82 @@
</variablelist>
</refsect1>
<refsect1>
<title>[PFIFO] Section Options</title>
<para>The <literal>[PFIFO]</literal> section manages the queueing discipline (qdisc) of
Packet First In First Out (pfifo).</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>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>PacketLimit=</varname></term>
<listitem>
<para>Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer size) to prevent it
from overflowing in case it is unable to dequeue packets as quickly as it receives them. When this limit is reached,
incoming packets are dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[CAKE] Section Options</title>
<para>The <literal>[CAKE]</literal> section manages the queueing discipline (qdisc) of
Common Applications Kept Enhanced (CAKE).</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>,
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Overhead=</varname></term>
<listitem>
<para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative.
Takes an integer ranges -64 to 256. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Bandwidth=</varname></term>
<listitem>
<para>Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is
parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to
unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[ControlledDelay] Section Options</title>
<para>The <literal>[ControlledDelay]</literal> section manages the queueing discipline (qdisc) of
@ -2530,7 +2644,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2584,6 +2700,53 @@
</variablelist>
</refsect1>
<refsect1>
<title>[GenericRandomEarlyDetection] Section Options</title>
<para>The <literal>[GenericRandomEarlyDetection]</literal> section manages the queueing discipline
(qdisc) of Generic Random Early Detection (GRED).</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>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>VirtualQueues=</varname></term>
<listitem>
<para>Specifies the number of virtual queues. Takes a integer in the range 1-16. Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DefaultVirtualQueue=</varname></term>
<listitem>
<para>Specifies the number of default virtual queue. This must be less than <varname>VirtualQueue=</varname>.
Defaults to unset and kernel's default is used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>GenericRIO=</varname></term>
<listitem>
<para>Takes a boolean. It turns on the RIO-like buffering scheme. Defaults to
unset and kernel's default is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[FairQueueingControlledDelay] Section Options</title>
<para>The <literal>[FairQueueingControlledDelay]</literal> section manages the queueing discipline
@ -2594,7 +2757,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2684,7 +2849,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2786,7 +2953,9 @@
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
@ -2810,6 +2979,94 @@
</variablelist>
</refsect1>
<refsect1>
<title>[HierarchyTokenBucket] Section Options</title>
<para>The <literal>[HierarchyTokenBucket]</literal> section manages the queueing discipline (qdisc) of
hierarchy token bucket (htb).</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>,
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DefaultClass=</varname></term>
<listitem>
<para>Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent
to the class. Defaults to unset.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[HierarchyTokenBucketClass] Section Options</title>
<para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
hierarchy token bucket (htb).</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 a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ClassId=</varname></term>
<listitem>
<para>Specifies the major and minur number of unique identifier of the class, known as the
class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Priority=</varname></term>
<listitem>
<para>Specifies the priority of the class. In the round-robin process, classes with the lowest
priority field are tried for packets first. This setting is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Rate=</varname></term>
<listitem>
<para>Specifies the maximum rate this class and all its children are guaranteed. When suffixed
with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively,
to the base of 1000. This setting is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>CeilRate=</varname></term>
<listitem>
<para>Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare.
When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits,
respectively, to the base of 1000. When unset, the value specified with <varname>Rate=</varname>
is used.</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

View File

@ -45,9 +45,13 @@ static inline int safe_atoux16(const char *s, uint16_t *ret) {
int safe_atoi16(const char *s, int16_t *ret);
static inline int safe_atou32(const char *s, uint32_t *ret_u) {
static inline int safe_atou32_full(const char *s, unsigned base, uint32_t *ret_u) {
assert_cc(sizeof(uint32_t) == sizeof(unsigned));
return safe_atou(s, (unsigned*) ret_u);
return safe_atou_full(s, base, (unsigned*) ret_u);
}
static inline int safe_atou32(const char *s, uint32_t *ret_u) {
return safe_atou32_full(s, 0, (unsigned*) ret_u);
}
static inline int safe_atoi32(const char *s, int32_t *ret_i) {

View File

@ -343,6 +343,74 @@ int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, ui
return 0;
}
int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
if (r < 0)
return r;
r = add_rtattr(m, type, &data, sizeof(int8_t));
if (r < 0)
return r;
return 0;
}
int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
if (r < 0)
return r;
r = add_rtattr(m, type, &data, sizeof(int16_t));
if (r < 0)
return r;
return 0;
}
int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
if (r < 0)
return r;
r = add_rtattr(m, type, &data, sizeof(int32_t));
if (r < 0)
return r;
return 0;
}
int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
int r;
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
if (r < 0)
return r;
r = add_rtattr(m, type, &data, sizeof(int64_t));
if (r < 0)
return r;
return 0;
}
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
int r;

View File

@ -745,6 +745,12 @@ static const NLTypeSystem rtnl_nexthop_type_system = {
.types = rtnl_nexthop_types,
};
static const NLType rtnl_tca_option_data_cake_types[] = {
[TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
[TCA_CAKE_OVERHEAD] = { .type = NETLINK_TYPE_S32 },
[TCA_CAKE_MPU] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_codel_types[] = {
[TCA_CODEL_TARGET] = { .type = NETLINK_TYPE_U32 },
[TCA_CODEL_LIMIT] = { .type = NETLINK_TYPE_U32 },
@ -780,6 +786,23 @@ static const NLType rtnl_tca_option_data_fq_codel_types[] = {
[TCA_FQ_CODEL_MEMORY_LIMIT] = { .type = NETLINK_TYPE_U32 },
};
static const NLType rtnl_tca_option_data_gred_types[] = {
[TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
};
static const NLType rtnl_tca_option_data_htb_types[] = {
[TCA_HTB_PARMS] = { .size = sizeof(struct tc_htb_opt) },
[TCA_HTB_INIT] = { .size = sizeof(struct tc_htb_glob) },
[TCA_HTB_CTAB] = { .size = TC_RTAB_SIZE },
[TCA_HTB_RTAB] = { .size = TC_RTAB_SIZE },
[TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
[TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
};
static const NLType rtnl_tca_option_data_sfb_types[] = {
[TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
};
static const NLType rtnl_tca_option_data_tbf_types[] = {
[TCA_TBF_PARMS] = { .size = sizeof(struct tc_tbf_qopt) },
[TCA_TBF_RTAB] = { .size = TC_RTAB_SIZE },
@ -791,21 +814,33 @@ static const NLType rtnl_tca_option_data_tbf_types[] = {
};
static const char* const nl_union_tca_option_data_table[] = {
[NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
[NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
[NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
[NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
[NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
[NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
[NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData);
static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
[NL_UNION_TCA_OPTION_DATA_CAKE] = { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types),
.types = rtnl_tca_option_data_cake_types },
[NL_UNION_TCA_OPTION_DATA_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types),
.types = rtnl_tca_option_data_codel_types },
[NL_UNION_TCA_OPTION_DATA_FQ] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
.types = rtnl_tca_option_data_fq_types },
[NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),
.types = rtnl_tca_option_data_fq_codel_types },
[NL_UNION_TCA_OPTION_DATA_GRED] = { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types),
.types = rtnl_tca_option_data_gred_types },
[NL_UNION_TCA_OPTION_DATA_HTB] = { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
.types = rtnl_tca_option_data_htb_types },
[NL_UNION_TCA_OPTION_DATA_SFB] = { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
.types = rtnl_tca_option_data_sfb_types },
[NL_UNION_TCA_OPTION_DATA_TBF] = { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),
.types = rtnl_tca_option_data_tbf_types },
};
@ -818,16 +853,16 @@ static const NLTypeSystemUnion rtnl_tca_option_data_type_system_union = {
.match = TCA_KIND,
};
static const NLType rtnl_qdisc_types[] = {
static const NLType rtnl_tca_types[] = {
[TCA_KIND] = { .type = NETLINK_TYPE_STRING },
[TCA_OPTIONS] = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
[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 NLTypeSystem rtnl_tca_type_system = {
.count = ELEMENTSOF(rtnl_tca_types),
.types = rtnl_tca_types,
};
static const NLType error_types[] = {
@ -868,9 +903,12 @@ 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) },
[RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
[RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
[RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
[RTM_NEWTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
[RTM_DELTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
[RTM_GETTCLASS] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
};
const NLTypeSystem rtnl_type_system_root = {

View File

@ -9,6 +9,10 @@ enum {
NETLINK_TYPE_U16, /* NLA_U16 */
NETLINK_TYPE_U32, /* NLA_U32 */
NETLINK_TYPE_U64, /* NLA_U64 */
NETLINK_TYPE_S8, /* NLA_S8 */
NETLINK_TYPE_S16, /* NLA_S16 */
NETLINK_TYPE_S32, /* NLA_S32 */
NETLINK_TYPE_S64, /* NLA_S64 */
NETLINK_TYPE_STRING, /* NLA_STRING */
NETLINK_TYPE_FLAG, /* NLA_FLAG */
NETLINK_TYPE_IN_ADDR,
@ -92,9 +96,13 @@ const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_;
NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
typedef enum NLUnionTCAOptionData {
NL_UNION_TCA_OPTION_DATA_CAKE,
NL_UNION_TCA_OPTION_DATA_CODEL,
NL_UNION_TCA_OPTION_DATA_FQ,
NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
NL_UNION_TCA_OPTION_DATA_GRED,
NL_UNION_TCA_OPTION_DATA_HTB,
NL_UNION_TCA_OPTION_DATA_SFB,
NL_UNION_TCA_OPTION_DATA_TBF,
_NL_UNION_TCA_OPTION_DATA_MAX,
_NL_UNION_TCA_OPTION_DATA_INVALID = -1,

View File

@ -47,6 +47,10 @@ static inline bool rtnl_message_type_is_qdisc(uint16_t type) {
return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
}
static inline bool rtnl_message_type_is_tclass(uint16_t type) {
return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
}
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);
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);

View File

@ -1077,3 +1077,46 @@ int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) {
return 0;
}
int sd_rtnl_message_new_tclass(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_tclass(nlmsg_type), -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
if (r < 0)
return r;
if (nlmsg_type == RTM_NEWTCLASS)
(*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_tclass_parent(sd_netlink_message *m, uint32_t parent) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_parent = parent;
return 0;
}
int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_handle = handle;
return 0;
}

View File

@ -107,22 +107,36 @@ sources = files('''
networkd-util.h
networkd-wifi.c
networkd-wifi.h
tc/cake.c
tc/cake.h
tc/codel.c
tc/codel.h
tc/fifo.c
tc/fifo.h
tc/fq.c
tc/fq.h
tc/fq-codel.c
tc/fq-codel.h
tc/gred.c
tc/gred.h
tc/htb.c
tc/htb.h
tc/netem.c
tc/netem.h
tc/qdisc.c
tc/qdisc.h
tc/sfb.c
tc/sfb.h
tc/sfq.c
tc/sfq.h
tc/tbf.c
tc/tbf.h
tc/tc-util.c
tc/tc-util.h
tc/tc.c
tc/tc.h
tc/tclass.c
tc/tclass.h
tc/teql.c
tc/teql.h
'''.split())

View File

@ -34,7 +34,6 @@
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-wifi.h"
#include "qdisc.h"
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
@ -42,6 +41,7 @@
#include "string-table.h"
#include "strv.h"
#include "sysctl-util.h"
#include "tc.h"
#include "tmpfile-util.h"
#include "udev-util.h"
#include "util.h"
@ -1116,7 +1116,7 @@ void link_check_ready(Link *link) {
if (!link->routing_policy_rules_configured)
return;
if (!link->qdiscs_configured)
if (!link->tc_configured)
return;
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
@ -2743,24 +2743,24 @@ static int link_configure_ipv4_dad(Link *link) {
return 0;
}
static int link_configure_qdiscs(Link *link) {
QDisc *qdisc;
static int link_configure_traffic_control(Link *link) {
TrafficControl *tc;
Iterator i;
int r;
link->qdiscs_configured = false;
link->qdisc_messages = 0;
link->tc_configured = false;
link->tc_messages = 0;
ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
r = qdisc_configure(link, qdisc);
ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section, i) {
r = traffic_control_configure(link, tc);
if (r < 0)
return r;
}
if (link->qdisc_messages == 0)
link->qdiscs_configured = true;
if (link->tc_messages == 0)
link->tc_configured = true;
else
log_link_debug(link, "Configuring queuing discipline (qdisc)");
log_link_debug(link, "Configuring traffic control");
return 0;
}
@ -2772,7 +2772,7 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
r = link_configure_qdiscs(link);
r = link_configure_traffic_control(link);
if (r < 0)
return r;

View File

@ -80,7 +80,7 @@ typedef struct Link {
unsigned nexthop_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned qdisc_messages;
unsigned tc_messages;
unsigned enslaving;
Set *addresses;
@ -116,7 +116,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 tc_configured:1;
bool setting_mtu:1;
bool setting_genmode:1;
bool ipv6_mtu_set:1;

View File

@ -14,6 +14,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "qdisc.h"
#include "tclass.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@ -256,6 +257,10 @@ CAN.RestartSec, config_parse_sec,
CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
QDisc.Parent, config_parse_qdisc_parent, _QDISC_KIND_INVALID, 0
QDisc.Handle, config_parse_qdisc_handle, _QDISC_KIND_INVALID, 0
CAKE.Parent, config_parse_qdisc_parent, QDISC_KIND_CAKE, 0
CAKE.Handle, config_parse_qdisc_handle, QDISC_KIND_CAKE, 0
CAKE.Bandwidth, config_parse_cake_bandwidth, QDISC_KIND_CAKE, 0
CAKE.Overhead, config_parse_cake_overhead, QDISC_KIND_CAKE, 0
ControlledDelay.Parent, config_parse_qdisc_parent, QDISC_KIND_CODEL, 0
ControlledDelay.Handle, config_parse_qdisc_handle, QDISC_KIND_CODEL, 0
ControlledDelay.PacketLimit, config_parse_controlled_delay_u32, QDISC_KIND_CODEL, 0
@ -263,6 +268,9 @@ ControlledDelay.TargetSec, config_parse_controlled_delay_usec,
ControlledDelay.IntervalSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0
ControlledDelay.CEThresholdSec, config_parse_controlled_delay_usec, QDISC_KIND_CODEL, 0
ControlledDelay.ECN, config_parse_controlled_delay_bool, QDISC_KIND_CODEL, 0
PFIFO.Parent, config_parse_qdisc_parent, QDISC_KIND_PFIFO, 0
PFIFO.Handle, config_parse_qdisc_handle, QDISC_KIND_PFIFO, 0
PFIFO.PacketLimit, config_parse_fifo_size, QDISC_KIND_PFIFO, 0
FairQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_FQ, 0
FairQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_FQ, 0
FairQueueing.PacketLimit, config_parse_fair_queueing_u32, QDISC_KIND_FQ, 0
@ -284,6 +292,19 @@ FairQueueingControlledDelay.TargetSec, config_parse_fair_queueing_controll
FairQueueingControlledDelay.IntervalSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0
FairQueueingControlledDelay.CEThresholdSec, config_parse_fair_queueing_controlled_delay_usec, QDISC_KIND_FQ_CODEL, 0
FairQueueingControlledDelay.ECN, config_parse_fair_queueing_controlled_delay_bool, QDISC_KIND_FQ_CODEL, 0
GenericRandomEarlyDetection.Parent, config_parse_qdisc_parent, QDISC_KIND_GRED, 0
GenericRandomEarlyDetection.Handle, config_parse_qdisc_handle, QDISC_KIND_GRED, 0
GenericRandomEarlyDetection.VirtualQueues, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0
GenericRandomEarlyDetection.DefaultVirtualQueue, config_parse_generic_random_early_detection_u32, QDISC_KIND_GRED, 0
GenericRandomEarlyDetection.GenericRIO, config_parse_generic_random_early_detection_bool, QDISC_KIND_GRED, 0
HierarchyTokenBucket.Parent, config_parse_qdisc_parent, QDISC_KIND_HTB, 0
HierarchyTokenBucket.Handle, config_parse_qdisc_handle, QDISC_KIND_HTB, 0
HierarchyTokenBucket.DefaultClass, config_parse_hierarchy_token_bucket_default_class, QDISC_KIND_HTB, 0
HierarchyTokenBucketClass.Parent, config_parse_tclass_parent, TCLASS_KIND_HTB, 0
HierarchyTokenBucketClass.ClassId, config_parse_tclass_classid, TCLASS_KIND_HTB, 0
HierarchyTokenBucketClass.Priority, config_parse_hierarchy_token_bucket_u32, TCLASS_KIND_HTB, 0
HierarchyTokenBucketClass.Rate, config_parse_hierarchy_token_bucket_rate, TCLASS_KIND_HTB, 0
HierarchyTokenBucketClass.CeilRate, config_parse_hierarchy_token_bucket_rate, TCLASS_KIND_HTB, 0
NetworkEmulator.Parent, config_parse_qdisc_parent, QDISC_KIND_NETEM, 0
NetworkEmulator.Handle, config_parse_qdisc_handle, QDISC_KIND_NETEM, 0
NetworkEmulator.DelaySec, config_parse_network_emulator_delay, QDISC_KIND_NETEM, 0
@ -291,6 +312,9 @@ NetworkEmulator.DelayJitterSec, config_parse_network_emulator_delay
NetworkEmulator.LossRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
NetworkEmulator.DuplicateRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
NetworkEmulator.PacketLimit, config_parse_network_emulator_packet_limit, QDISC_KIND_NETEM, 0
StochasticFairBlue.Parent, config_parse_qdisc_parent, QDISC_KIND_SFB, 0
StochasticFairBlue.Handle, config_parse_qdisc_handle, QDISC_KIND_SFB, 0
StochasticFairBlue.PacketLimit, config_parse_stochastic_fair_blue_u32, QDISC_KIND_SFB, 0
StochasticFairnessQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_SFQ, 0
StochasticFairnessQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_SFQ, 0
StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ, 0

View File

@ -22,6 +22,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "tc.h"
#include "util.h"
/* Let's assume that anything above this number is a user misconfiguration. */
@ -154,7 +155,7 @@ int network_verify(Network *network) {
Prefix *prefix, *prefix_next;
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
QDisc *qdisc;
TrafficControl *tc;
Iterator i;
assert(network);
@ -316,9 +317,9 @@ int network_verify(Network *network) {
routing_policy_rule_free(rule);
bool has_root = false, has_clsact = false;
ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
qdisc_free(qdisc);
ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i)
if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
traffic_control_free(tc);
return 0;
}
@ -484,10 +485,16 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
"TrafficControlQueueingDiscipline\0"
"CAN\0"
"QDisc\0"
"CAKE\0"
"ControlledDelay\0"
"PFIFO\0"
"FairQueueing\0"
"FairQueueingControlledDelay\0"
"GenericRandomEarlyDetection\0"
"HierarchyTokenBucket\0"
"HierarchyTokenBucketClass\0"
"NetworkEmulator\0"
"StochasticFairBlue\0"
"StochasticFairnessQueueing\0"
"TokenBucketFilter\0"
"TrivialLinkEqualizer\0",
@ -691,7 +698,7 @@ static Network *network_free(Network *network) {
hashmap_free(network->prefixes_by_section);
hashmap_free(network->route_prefixes_by_section);
hashmap_free(network->rules_by_section);
ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
if (network->manager &&
network->manager->duids_requesting_uuid)

View File

@ -30,7 +30,6 @@
#include "networkd-routing-policy-rule.h"
#include "networkd-util.h"
#include "ordered-set.h"
#include "qdisc.h"
#include "resolve-util.h"
typedef enum IPv6PrivacyExtensions {
@ -276,7 +275,7 @@ struct Network {
Hashmap *prefixes_by_section;
Hashmap *route_prefixes_by_section;
Hashmap *rules_by_section;
OrderedHashmap *qdiscs_by_section;
OrderedHashmap *tc_by_section;
/* All kinds of DNS configuration */
struct in_addr_data *dns;

159
src/network/tc/cake.c Normal file
View File

@ -0,0 +1,159 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "cake.h"
#include "conf-parser.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
#include "string-util.h"
static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
CommonApplicationsKeptEnhanced *c;
int r;
assert(link);
assert(qdisc);
assert(req);
c = CAKE(qdisc);
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
if (c->bandwidth > 0) {
r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_CAKE_BASE_RATE64 attribute: %m");
}
r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m");
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
int config_parse_cake_bandwidth(
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) QDisc *qdisc = NULL;
CommonApplicationsKeptEnhanced *c;
Network *network = data;
uint64_t k;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
c = CAKE(qdisc);
if (isempty(rvalue)) {
c->bandwidth = 0;
qdisc = NULL;
return 0;
}
r = parse_size(rvalue, 1000, &k);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
c->bandwidth = k/8;
qdisc = NULL;
return 0;
}
int config_parse_cake_overhead(
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) QDisc *qdisc = NULL;
CommonApplicationsKeptEnhanced *c;
Network *network = data;
int32_t v;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
c = CAKE(qdisc);
if (isempty(rvalue)) {
c->overhead = 0;
qdisc = NULL;
return 0;
}
r = safe_atoi32(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'Overhead=', ignoring assignment: %s",
rvalue);
return 0;
}
if (v < -64 || v > 256) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid 'Overhead=', ignoring assignment: %s",
rvalue);
return 0;
}
c->overhead = v;
qdisc = NULL;
return 0;
}
const QDiscVTable cake_vtable = {
.object_size = sizeof(CommonApplicationsKeptEnhanced),
.tca_kind = "cake",
.fill_message = cake_fill_message,
};

20
src/network/tc/cake.h Normal file
View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
typedef struct CommonApplicationsKeptEnhanced {
QDisc meta;
int overhead;
uint64_t bandwidth;
} CommonApplicationsKeptEnhanced;
DEFINE_QDISC_CAST(CAKE, CommonApplicationsKeptEnhanced);
extern const QDiscVTable cake_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_cake_bandwidth);
CONFIG_PARSER_PROTOTYPE(config_parse_cake_overhead);

87
src/network/tc/fifo.c Normal file
View File

@ -0,0 +1,87 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "fifo.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "string-util.h"
static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
struct tc_fifo_qopt opt = {};
FirstInFirstOut *fifo;
int r;
assert(link);
assert(qdisc);
assert(req);
fifo = PFIFO(qdisc);
opt.limit = fifo->limit;
r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_fifo_qopt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m");
return 0;
}
int config_parse_fifo_size(
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) QDisc *qdisc = NULL;
Network *network = data;
FirstInFirstOut *fifo;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_PFIFO, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
fifo = PFIFO(qdisc);
if (isempty(rvalue)) {
fifo->limit = 0;
qdisc = NULL;
return 0;
}
r = safe_atou32(rvalue, &fifo->limit);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qdisc = NULL;
return 0;
}
const QDiscVTable pfifo_vtable = {
.object_size = sizeof(FirstInFirstOut),
.tca_kind = "pfifo",
.fill_message = fifo_fill_message,
};

17
src/network/tc/fifo.h Normal file
View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
typedef struct FirstInFirstOut {
QDisc meta;
uint32_t limit;
} FirstInFirstOut;
DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut);
extern const QDiscVTable pfifo_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_fifo_size);

193
src/network/tc/gred.c Normal file
View File

@ -0,0 +1,193 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
#include "string-util.h"
static int generic_random_early_detection_init(QDisc *qdisc) {
GenericRandomEarlyDetection *gred;
assert(qdisc);
gred = GRED(qdisc);
gred->grio = -1;
return 0;
}
static int generic_random_early_detection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
GenericRandomEarlyDetection *gred;
struct tc_gred_sopt opt = {};
int r;
assert(link);
assert(qdisc);
assert(req);
gred = GRED(qdisc);
opt.DPs = gred->virtual_queues;
opt.def_DP = gred->default_virtual_queue;
if (gred->grio >= 0)
opt.grio = gred->grio;
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "gred");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_GRED_DPS attribute: %m");
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
static int generic_random_early_detection_verify(QDisc *qdisc) {
GenericRandomEarlyDetection *gred = GRED(qdisc);
if (gred->default_virtual_queue >= gred->virtual_queues)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: DefaultVirtualQueue= must be less than VirtualQueues=. "
"Ignoring [GenericRandomEarlyDetection] section from line %u.",
qdisc->section->filename, qdisc->section->line);
return 0;
}
int config_parse_generic_random_early_detection_u32(
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) QDisc *qdisc = NULL;
GenericRandomEarlyDetection *gred;
Network *network = data;
uint32_t *p;
uint32_t v;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
gred = GRED(qdisc);
if (streq(lvalue, "VirtualQueues"))
p = &gred->virtual_queues;
else if (streq(lvalue, "DefaultVirtualQueue"))
p = &gred->default_virtual_queue;
else
assert_not_reached("Invalid lvalue.");
if (isempty(rvalue)) {
*p = 0;
qdisc = NULL;
return 0;
}
r = safe_atou32(rvalue, &v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
if (v > MAX_DPs) {
log_syntax(unit, LOG_ERR, filename, line, 0,
"Invalid '%s=', ignoring assignment: %s",
lvalue, rvalue);
}
*p = v;
qdisc = NULL;
return 0;
}
int config_parse_generic_random_early_detection_bool(
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) QDisc *qdisc = NULL;
GenericRandomEarlyDetection *gred;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
gred = GRED(qdisc);
if (isempty(rvalue)) {
gred->grio = -1;
qdisc = NULL;
return 0;
}
r = parse_boolean(rvalue);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
gred->grio = r;
qdisc = NULL;
return 0;
}
const QDiscVTable gred_vtable = {
.object_size = sizeof(GenericRandomEarlyDetection),
.tca_kind = "gred",
.init = generic_random_early_detection_init,
.fill_message = generic_random_early_detection_fill_message,
.verify = generic_random_early_detection_verify,
};

20
src/network/tc/gred.h Normal file
View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
typedef struct GenericRandomEarlyDetection {
QDisc meta;
uint32_t virtual_queues;
uint32_t default_virtual_queue;
int grio;
} GenericRandomEarlyDetection;
DEFINE_QDISC_CAST(GRED, GenericRandomEarlyDetection);
extern const QDiscVTable gred_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_u32);
CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_bool);

279
src/network/tc/htb.c Normal file
View File

@ -0,0 +1,279 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
#include "htb.h"
#include "string-util.h"
#include "tc-util.h"
static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
HierarchyTokenBucket *htb;
struct tc_htb_glob opt = {
.rate2quantum = 10,
.version = 3,
};
int r;
assert(link);
assert(qdisc);
assert(req);
htb = HTB(qdisc);
opt.defcls = htb->default_class;
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_data(req, TCA_HTB_INIT, &opt, sizeof(opt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_INIT attribute: %m");
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
int config_parse_hierarchy_token_bucket_default_class(
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) QDisc *qdisc = NULL;
HierarchyTokenBucket *htb;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
htb = HTB(qdisc);
if (isempty(rvalue)) {
htb->default_class = 0;
qdisc = NULL;
return 0;
}
r = safe_atou32_full(rvalue, 16, &htb->default_class);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qdisc = NULL;
return 0;
}
const QDiscVTable htb_vtable = {
.object_size = sizeof(HierarchyTokenBucket),
.tca_kind = "htb",
.fill_message = hierarchy_token_bucket_fill_message,
};
static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
HierarchyTokenBucketClass *htb;
struct tc_htb_opt opt = {};
uint32_t rtab[256], ctab[256], mtu = 1600; /* Ethernet packet length */
int r;
assert(link);
assert(tclass);
assert(req);
htb = TCLASS_TO_HTB(tclass);
if (htb->ceil_rate == 0)
htb->ceil_rate = htb->rate;
opt.prio = htb->priority;
opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate;
opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate;
r = tc_transmit_time(htb->rate, mtu, &opt.buffer);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate buffer size: %m");
r = tc_transmit_time(htb->ceil_rate, mtu, &opt.cbuffer);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m");
r = tc_fill_ratespec_and_table(&opt.rate, rtab, mtu);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate rate table: %m");
r = tc_fill_ratespec_and_table(&opt.ceil, ctab, mtu);
if (r < 0)
return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m");
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_data(req, TCA_HTB_PARMS, &opt, sizeof(opt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m");
if (htb->rate >= (1ULL << 32)) {
r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m");
}
if (htb->ceil_rate >= (1ULL << 32)) {
r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m");
}
r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m");
r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB attribute: %m");
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
int config_parse_hierarchy_token_bucket_u32(
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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
HierarchyTokenBucketClass *htb;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to create traffic control class, ignoring assignment: %m");
htb = TCLASS_TO_HTB(tclass);
if (isempty(rvalue)) {
htb->priority = 0;
tclass = NULL;
return 0;
}
r = safe_atou32(rvalue, &htb->priority);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
tclass = NULL;
return 0;
}
int config_parse_hierarchy_token_bucket_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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
HierarchyTokenBucketClass *htb;
Network *network = data;
uint64_t *v;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to create traffic control class, ignoring assignment: %m");
htb = TCLASS_TO_HTB(tclass);
if (streq(lvalue, "Rate"))
v = &htb->rate;
else if (streq(lvalue, "CeilRate"))
v = &htb->ceil_rate;
else
assert_not_reached("Invalid lvalue");
if (isempty(rvalue)) {
*v = 0;
tclass = NULL;
return 0;
}
r = parse_size(rvalue, 1000, v);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
*v /= 8;
tclass = NULL;
return 0;
}
const TClassVTable htb_tclass_vtable = {
.object_size = sizeof(HierarchyTokenBucketClass),
.tca_kind = "htb",
.fill_message = hierarchy_token_bucket_class_fill_message,
};

31
src/network/tc/htb.h Normal file
View File

@ -0,0 +1,31 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
#include "tclass.h"
typedef struct HierarchyTokenBucket {
QDisc meta;
uint32_t default_class;
} HierarchyTokenBucket;
DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
extern const QDiscVTable htb_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
typedef struct HierarchyTokenBucketClass {
TClass meta;
uint32_t priority;
uint64_t rate;
uint64_t ceil_rate;
} HierarchyTokenBucketClass;
DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass);
extern const TClassVTable htb_tclass_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_rate);

View File

@ -12,12 +12,19 @@
#include "qdisc.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
#include "tc-util.h"
const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
[QDISC_KIND_CAKE] = &cake_vtable,
[QDISC_KIND_CODEL] = &codel_vtable,
[QDISC_KIND_FQ] = &fq_vtable,
[QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
[QDISC_KIND_GRED] = &gred_vtable,
[QDISC_KIND_HTB] = &htb_vtable,
[QDISC_KIND_NETEM] = &netem_vtable,
[QDISC_KIND_PFIFO] = &pfifo_vtable,
[QDISC_KIND_SFB] = &sfb_vtable,
[QDISC_KIND_SFQ] = &sfq_vtable,
[QDISC_KIND_TBF] = &tbf_vtable,
[QDISC_KIND_TEQL] = &teql_vtable,
@ -33,6 +40,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
return -ENOMEM;
*qdisc = (QDisc) {
.meta.kind = TC_KIND_QDISC,
.family = AF_UNSPEC,
.parent = TC_H_ROOT,
.kind = kind,
@ -42,6 +50,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
if (!qdisc)
return -ENOMEM;
qdisc->meta.kind = TC_KIND_QDISC,
qdisc->family = AF_UNSPEC;
qdisc->parent = TC_H_ROOT;
qdisc->kind = kind;
@ -61,7 +70,8 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(qdisc_freep) QDisc *qdisc = NULL;
QDisc *existing;
TrafficControl *existing;
QDisc *q = NULL;
int r;
assert(network);
@ -73,15 +83,20 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
if (r < 0)
return r;
existing = ordered_hashmap_get(network->qdiscs_by_section, n);
existing = ordered_hashmap_get(network->tc_by_section, n);
if (existing) {
if (existing->kind != _QDISC_KIND_INVALID &&
kind != _QDISC_KIND_INVALID &&
existing->kind != kind)
if (existing->kind != TC_KIND_QDISC)
return -EINVAL;
if (existing->kind == kind || kind == _QDISC_KIND_INVALID) {
*ret = existing;
q = TC_TO_QDISC(existing);
if (q->kind != _QDISC_KIND_INVALID &&
kind != _QDISC_KIND_INVALID &&
q->kind != kind)
return -EINVAL;
if (q->kind == kind || kind == _QDISC_KIND_INVALID) {
*ret = q;
return 0;
}
}
@ -90,23 +105,23 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
if (r < 0)
return r;
if (existing) {
qdisc->family = existing->family;
qdisc->handle = existing->handle;
qdisc->parent = existing->parent;
qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
if (q) {
qdisc->family = q->family;
qdisc->handle = q->handle;
qdisc->parent = q->parent;
qdisc->tca_kind = TAKE_PTR(q->tca_kind);
qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n));
qdisc_free(q);
}
qdisc->network = network;
qdisc->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
r = ordered_hashmap_put(network->tc_by_section, qdisc->section, TC(qdisc));
if (r < 0)
return r;
@ -119,7 +134,7 @@ void qdisc_free(QDisc *qdisc) {
return;
if (qdisc->network && qdisc->section)
ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section);
network_config_section_free(qdisc->section);
@ -131,8 +146,8 @@ 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--;
assert(link->tc_messages > 0);
link->tc_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
@ -144,9 +159,9 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return 1;
}
if (link->qdisc_messages == 0) {
log_link_debug(link, "QDisc configured");
link->qdiscs_configured = true;
if (link->tc_messages == 0) {
log_link_debug(link, "Traffic control configured");
link->tc_configured = true;
link_check_ready(link);
}
@ -203,7 +218,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
link->qdisc_messages++;
link->tc_messages++;
return 0;
}
@ -279,19 +294,21 @@ int config_parse_qdisc_parent(
qdisc->parent = TC_H_INGRESS;
qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0);
} else {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'Parent=', ignoring assignment: %s",
rvalue);
return 0;
r = parse_handle(rvalue, &qdisc->parent);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'Parent=', ignoring assignment: %s",
rvalue);
return 0;
}
}
if (streq(rvalue, "root"))
qdisc->tca_kind = mfree(qdisc->tca_kind);
else {
if (STR_IN_SET(rvalue, "clsact", "ingress")) {
r = free_and_strdup(&qdisc->tca_kind, rvalue);
if (r < 0)
return log_oom();
}
} else
qdisc->tca_kind = mfree(qdisc->tca_kind);
qdisc = NULL;

View File

@ -6,12 +6,18 @@
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
#include "tc.h"
typedef enum QDiscKind {
QDISC_KIND_CAKE,
QDISC_KIND_CODEL,
QDISC_KIND_FQ,
QDISC_KIND_FQ_CODEL,
QDISC_KIND_GRED,
QDISC_KIND_HTB,
QDISC_KIND_NETEM,
QDISC_KIND_PFIFO,
QDISC_KIND_SFB,
QDISC_KIND_SFQ,
QDISC_KIND_TBF,
QDISC_KIND_TEQL,
@ -20,6 +26,8 @@ typedef enum QDiscKind {
} QDiscKind;
typedef struct QDisc {
TrafficControl meta;
NetworkConfigSection *section;
Network *network;
@ -65,13 +73,20 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
DEFINE_TC_CAST(QDISC, QDisc);
CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent);
CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
#include "cake.h"
#include "codel.h"
#include "fifo.h"
#include "fq-codel.h"
#include "fq.h"
#include "gred.h"
#include "htb.h"
#include "netem.h"
#include "sfb.h"
#include "sfq.h"
#include "tbf.h"
#include "teql.h"

106
src/network/tc/sfb.c Normal file
View File

@ -0,0 +1,106 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "netlink-util.h"
#include "parse-util.h"
#include "qdisc.h"
#include "sfb.h"
#include "string-util.h"
static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
StochasticFairBlue *sfb;
struct tc_sfb_qopt opt = {
.rehash_interval = 600*1000,
.warmup_time = 60*1000,
.penalty_rate = 10,
.penalty_burst = 20,
.increment = (SFB_MAX_PROB + 1000) / 2000,
.decrement = (SFB_MAX_PROB + 10000) / 20000,
.max = 25,
.bin_size = 20,
};
int r;
assert(link);
assert(qdisc);
assert(req);
sfb = SFB(qdisc);
opt.limit = sfb->packet_limit;
r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "sfb");
if (r < 0)
return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(struct tc_sfb_qopt));
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_SFB_PARMS attribute: %m");
r = sd_netlink_message_close_container(req);
if (r < 0)
return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
return 0;
}
int config_parse_stochastic_fair_blue_u32(
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) QDisc *qdisc = NULL;
StochasticFairBlue *sfb;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = qdisc_new_static(QDISC_KIND_SFB, network, filename, section_line, &qdisc);
if (r == -ENOMEM)
return log_oom();
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"More than one kind of queueing discipline, ignoring assignment: %m");
sfb = SFB(qdisc);
if (isempty(rvalue)) {
sfb->packet_limit = 0;
qdisc = NULL;
return 0;
}
r = safe_atou32(rvalue, &sfb->packet_limit);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse '%s=', ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
qdisc = NULL;
return 0;
}
const QDiscVTable sfb_vtable = {
.object_size = sizeof(StochasticFairBlue),
.tca_kind = "sfb",
.fill_message = stochastic_fair_blue_fill_message,
};

17
src/network/tc/sfb.h Normal file
View File

@ -0,0 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2020 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "qdisc.h"
typedef struct StochasticFairBlue {
QDisc meta;
uint32_t packet_limit;
} StochasticFairBlue;
DEFINE_QDISC_CAST(SFB, StochasticFairBlue);
extern const QDiscVTable sfb_vtable;
CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fair_blue_u32);

View File

@ -2,6 +2,7 @@
* Copyright © 2019 VMware, Inc. */
#include "alloc-util.h"
#include "extract-word.h"
#include "fileio.h"
#include "parse-util.h"
#include "tc-util.h"
@ -92,3 +93,32 @@ int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_
rate->linklayer = TC_LINKLAYER_ETHERNET;
return 0;
}
int parse_handle(const char *t, uint32_t *ret) {
_cleanup_free_ char *word = NULL;
uint16_t major, minor;
int r;
assert(t);
assert(ret);
/* Extract the major number. */
r = extract_first_word(&t, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
if (!t)
return -EINVAL;
r = safe_atou16_full(word, 16, &major);
if (r < 0)
return r;
r = safe_atou16_full(t, 16, &minor);
if (r < 0)
return r;
*ret = ((uint32_t) major << 16) | minor;
return 0;
}

View File

@ -10,3 +10,4 @@ int tc_time_to_tick(usec_t t, uint32_t *ret);
int parse_tc_percent(const char *s, uint32_t *percent);
int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret);
int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu);
int parse_handle(const char *t, uint32_t *ret);

49
src/network/tc/tc.c Normal file
View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "macro.h"
#include "qdisc.h"
#include "tc.h"
#include "tclass.h"
void traffic_control_free(TrafficControl *tc) {
if (!tc)
return;
switch (tc->kind) {
case TC_KIND_QDISC:
qdisc_free(TC_TO_QDISC(tc));
break;
case TC_KIND_TCLASS:
tclass_free(TC_TO_TCLASS(tc));
break;
default:
assert_not_reached("Invalid traffic control type");
}
}
int traffic_control_configure(Link *link, TrafficControl *tc) {
assert(link);
assert(tc);
switch(tc->kind) {
case TC_KIND_QDISC:
return qdisc_configure(link, TC_TO_QDISC(tc));
case TC_KIND_TCLASS:
return tclass_configure(link, TC_TO_TCLASS(tc));
default:
assert_not_reached("Invalid traffic control type");
}
}
int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) {
assert(tc);
switch(tc->kind) {
case TC_KIND_QDISC:
return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact);
case TC_KIND_TCLASS:
return tclass_section_verify(TC_TO_TCLASS(tc));
default:
assert_not_reached("Invalid traffic control type");
}
}

32
src/network/tc/tc.h Normal file
View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
#include "networkd-link.h"
typedef enum TrafficControlKind {
TC_KIND_QDISC,
TC_KIND_TCLASS,
TC_KIND_FILTER,
_TC_KIND_MAX,
_TC_KIND_INVALID = -1,
} TrafficControlKind;
typedef struct TrafficControl {
TrafficControlKind kind;
} TrafficControl;
/* For casting a tc into the various tc kinds */
#define DEFINE_TC_CAST(UPPERCASE, MixedCase) \
static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) { \
if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \
return NULL; \
\
return (MixedCase*) tc; \
}
/* For casting the various tc kinds into a tc */
#define TC(tc) (&(tc)->meta)
void traffic_control_free(TrafficControl *tc);
int traffic_control_configure(Link *link, TrafficControl *tc);
int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact);

277
src/network/tc/tclass.c Normal file
View File

@ -0,0 +1,277 @@
/* 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 "set.h"
#include "string-util.h"
#include "strv.h"
#include "tc-util.h"
#include "tclass.h"
const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
[TCLASS_KIND_HTB] = &htb_tclass_vtable,
};
static int tclass_new(TClassKind kind, TClass **ret) {
TClass *tclass;
int r;
tclass = malloc0(tclass_vtable[kind]->object_size);
if (!tclass)
return -ENOMEM;
tclass->meta.kind = TC_KIND_TCLASS,
tclass->parent = TC_H_ROOT;
tclass->kind = kind;
if (TCLASS_VTABLE(tclass)->init) {
r = TCLASS_VTABLE(tclass)->init(tclass);
if (r < 0)
return r;
}
*ret = TAKE_PTR(tclass);
return 0;
}
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(tclass_freep) TClass *tclass = NULL;
TrafficControl *existing;
int r;
assert(network);
assert(ret);
assert(filename);
assert(section_line > 0);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
existing = ordered_hashmap_get(network->tc_by_section, n);
if (existing) {
TClass *t;
if (existing->kind != TC_KIND_TCLASS)
return -EINVAL;
t = TC_TO_TCLASS(existing);
if (t->kind != kind)
return -EINVAL;
*ret = t;
return 0;
}
r = tclass_new(kind, &tclass);
if (r < 0)
return r;
tclass->network = network;
tclass->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
if (r < 0)
return r;
*ret = TAKE_PTR(tclass);
return 0;
}
void tclass_free(TClass *tclass) {
if (!tclass)
return;
if (tclass->network && tclass->section)
ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
network_config_section_free(tclass->section);
free(tclass);
}
static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
assert(link->tc_messages > 0);
link->tc_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_message_error_errno(link, m, r, "Could not set TClass");
link_enter_failed(link);
return 1;
}
if (link->tc_messages == 0) {
log_link_debug(link, "Traffic control configured");
link->tc_configured = true;
link_check_ready(link);
}
return 1;
}
int tclass_configure(Link *link, TClass *tclass) {
_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_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
if (r < 0)
return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
if (tclass->classid != TC_H_UNSPEC) {
r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
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, TCLASS_VTABLE(tclass)->tca_kind);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
if (TCLASS_VTABLE(tclass)->fill_message) {
r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
if (r < 0)
return r;
}
r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_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->tc_messages++;
return 0;
}
int tclass_section_verify(TClass *tclass) {
int r;
assert(tclass);
if (section_is_invalid(tclass->section))
return -EINVAL;
if (TCLASS_VTABLE(tclass)->verify) {
r = TCLASS_VTABLE(tclass)->verify(tclass);
if (r < 0)
return r;
}
return 0;
}
int config_parse_tclass_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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
if (r < 0)
return r;
if (streq(rvalue, "root"))
tclass->parent = TC_H_ROOT;
else {
r = parse_handle(rvalue, &tclass->parent);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'Parent=', ignoring assignment: %s",
rvalue);
return 0;
}
}
tclass = NULL;
return 0;
}
int config_parse_tclass_classid(
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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
if (r < 0)
return r;
if (isempty(rvalue)) {
tclass->classid = TC_H_UNSPEC;
tclass = NULL;
return 0;
}
r = parse_handle(rvalue, &tclass->classid);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'ClassId=', ignoring assignment: %s",
rvalue);
return 0;
}
tclass = NULL;
return 0;
}

67
src/network/tc/tclass.h Normal file
View File

@ -0,0 +1,67 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#pragma once
#include "conf-parser.h"
#include "networkd-link.h"
#include "networkd-network.h"
#include "networkd-util.h"
#include "tc.h"
typedef enum TClassKind {
TCLASS_KIND_HTB,
_TCLASS_KIND_MAX,
_TCLASS_KIND_INVALID = -1,
} TClassKind;
typedef struct TClass {
TrafficControl meta;
NetworkConfigSection *section;
Network *network;
uint32_t classid;
uint32_t parent;
TClassKind kind;
} TClass;
typedef struct TClassVTable {
size_t object_size;
const char *tca_kind;
/* called in tclass_new() */
int (*init)(TClass *tclass);
int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m);
int (*verify)(TClass *tclass);
} TClassVTable;
extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL)
/* For casting a tclass into the various tclass kinds */
#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase) \
static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) { \
if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \
return NULL; \
\
return (MixedCase*) t; \
}
/* For casting the various tclass kinds into a tclass */
#define TCLASS(t) (&(t)->meta)
void tclass_free(TClass *tclass);
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
int tclass_configure(Link *link, TClass *tclass);
int tclass_section_verify(TClass *tclass);
DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
DEFINE_TC_CAST(TCLASS, TClass);
CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
#include "htb.h"

View File

@ -86,6 +86,10 @@ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uin
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
@ -208,6 +212,10 @@ int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16
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);
int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent);
int sd_rtnl_message_set_tclass_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

@ -329,6 +329,11 @@ TargetSec=
IntervalSec=
CEThresholdSec=
ECN=
[CAKE]
Parent=
Handle=
Bandwidth=
Overhead=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
@ -340,3 +345,27 @@ NetworkEmulatorPacketLimit=
Parent=
Handle=
Id=
[HierarchyTokenBucket]
Parent=
Handle=
DefaultClass=
[HierarchyTokenBucketClass]
Parent=
ClassId=
Priority=
Rate=
CeilRate=
[PFIFO]
Parent=
Handle=
PacketLimit=
[GenericRandomEarlyDetection]
Parent=
Handle=
VirtualQueues=
DefaultVirtualQueue=
GenericRIO=
[StochasticFairBlue]
Parent=
Handle=
PacketLimit=

View File

@ -5,7 +5,8 @@ Name=dummy98
IPv6AcceptRA=no
Address=10.1.2.3/16
[TrivialLinkEqualizer]
[CAKE]
Parent=root
Handle=0002
Id=1
Handle=3a
Overhead=128
Bandwidth=500M

View File

@ -0,0 +1,162 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
[QDisc]
Parent=clsact
[HierarchyTokenBucket]
Parent=root
Handle=0002
DefaultClass=30
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0030
Priority=1
Rate=1M
CeilRate=0.5M
[NetworkEmulator]
Parent=2:30
Handle=0030
DelaySec=50ms
DelayJitterSec=10ms
LossRate=20%
PacketLimit=100
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0031
Priority=1
Rate=1M
CeilRate=0.5M
[TrivialLinkEqualizer]
Parent=2:31
Handle=0031
Id=1
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0032
Priority=1
Rate=1M
CeilRate=0.5M
[FairQueueing]
Parent=2:32
Handle=0032
PacketLimit=1000
FlowLimit=200
Quantum=1500
InitialQuantum=13000
MaximumRate=1M
Buckets=512
OrphanMask=511
Pacing=yes
CEThresholdSec=100ms
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0033
Priority=1
Rate=1M
CeilRate=0.5M
[ControlledDelay]
Parent=2:33
Handle=0033
PacketLimit=2000
TargetSec=10ms
IntervalSec=50ms
ECN=yes
CEThresholdSec=100ms
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0034
Priority=1
Rate=1M
CeilRate=0.5M
[FairQueueingControlledDelay]
Parent=2:34
Handle=0034
PacketLimit=20480
MemoryLimit=64M
Flows=2048
TargetSec=10ms
IntervalSec=200ms
Quantum=1400
ECN=yes
CEThresholdSec=100ms
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0035
Priority=1
Rate=1M
CeilRate=0.5M
[TokenBucketFilter]
Parent=2:35
Handle=0035
Rate=1G
Burst=5K
LatencySec=70msec
PeakRate=100G
MTUBytes=1M
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0036
Priority=1
Rate=1M
CeilRate=0.5M
[StochasticFairnessQueueing]
Parent=2:36
Handle=0036
PerturbPeriodSec=5sec
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0037
Priority=1
Rate=1M
CeilRate=0.5M
[PFIFO]
Parent=2:37
Handle=0037
PacketLimit=100000
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0038
Priority=1
Rate=1M
CeilRate=0.5M
[GenericRandomEarlyDetection]
Parent=2:38
Handle=0038
VirtualQueues=12
DefaultVirtualQueue=10
GenericRIO=yes
[HierarchyTokenBucketClass]
Parent=root
ClassId=0002:0039
Priority=1
Rate=1M
CeilRate=0.5M
[StochasticFairBlue]
Parent=2:39
Handle=0039
PacketLimit=200000

View File

@ -1,12 +0,0 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
#[TrafficControlQueueingDiscipline]
#Parent=root
[TrafficControlQueueingDiscipline]
Parent=clsact

View File

@ -1,27 +0,0 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
[FairQueueing]
Parent=root
Handle=0003
PacketLimit=1000
FlowLimit=200
Quantum=1500
InitialQuantum=13000
MaximumRate=1M
Buckets=512
OrphanMask=511
Pacing=yes
CEThresholdSec=100ms
[ControlledDelay]
Parent=clsact
PacketLimit=2000
TargetSec=10ms
IntervalSec=50ms
ECN=yes
CEThresholdSec=100ms

View File

@ -1,12 +0,0 @@
[Match]
Name=test1
[Network]
IPv6AcceptRA=no
Address=10.1.2.4/16
#[QDisc]
#Parent=root
[QDisc]
Parent=ingress

View File

@ -1,25 +0,0 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
[NetworkEmulator]
Parent=root
Handle=001f
DelaySec=50ms
DelayJitterSec=10ms
LossRate=20%
PacketLimit=100
[FairQueueingControlledDelay]
Parent=ingress
PacketLimit=20480
MemoryLimit=64M
Flows=2048
TargetSec=10ms
IntervalSec=200ms
Quantum=1400
ECN=yes
CEThresholdSec=100ms

View File

@ -1,19 +0,0 @@
[Match]
Name=test1
[Network]
IPv6AcceptRA=no
Address=10.1.2.4/16
[TokenBucketFilter]
Parent=root
Handle=003f
Rate=1G
Burst=5K
LatencySec=70msec
PeakRate=100G
MTUBytes=1M
[StochasticFairnessQueueing]
Parent=clsact
PerturbPeriodSec=5sec

View File

@ -157,6 +157,18 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
return f
def expectedFailureIfCAKEIsNotAvailable():
def f(func):
call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
rc = call('tc qdisc add dev dummy98 parent root cake', stderr=subprocess.DEVNULL)
call('ip link del dummy98', stderr=subprocess.DEVNULL)
if rc == 0:
return func
else:
return unittest.expectedFailure(func)
return f
def setUpModule():
global running_units
@ -1621,13 +1633,9 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-neighbor-ip-dummy.network',
'25-neighbor-ip.network',
'25-nexthop.network',
'25-qdisc-clsact-root-compat.network',
'25-qdisc-fq-codel.network',
'25-qdisc-cake.network',
'25-qdisc-clsact-and-htb.network',
'25-qdisc-ingress-netem-compat.network',
'25-qdisc-ingress-root.network',
'25-qdisc-netem-and-fqcodel.network',
'25-qdisc-tbf-and-sfq.network',
'25-qdisc-teql.network',
'25-route-ipv6-src.network',
'25-route-static.network',
'25-route-vrf.network',
@ -2254,71 +2262,85 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.assertRegex(output, '192.168.5.1')
def test_qdisc(self):
copy_unit_to_networkd_unit_path('25-qdisc-netem-and-fqcodel.network', '12-dummy.netdev',
'25-qdisc-tbf-and-sfq.network', '11-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc netem 1f:')
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
self.assertRegex(output, 'qdisc fq_codel')
self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
output = check_output('tc qdisc show dev test1')
print(output)
self.assertRegex(output, 'qdisc tbf 3f:')
self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms')
self.assertRegex(output, 'qdisc sfq')
self.assertRegex(output, 'perturb 5sec')
def test_qdisc2(self):
copy_unit_to_networkd_unit_path('25-qdisc-fq-codel.network', '12-dummy.netdev',
'25-qdisc-ingress-root.network', '11-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc fq 3:')
self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
self.assertRegex(output, 'quantum 1500')
self.assertRegex(output, 'initial_quantum 13000')
self.assertRegex(output, 'maxrate 1Mbit')
self.assertRegex(output, 'qdisc codel')
self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn')
output = check_output('tc qdisc show dev test1')
print(output)
self.assertRegex(output, 'qdisc ingress')
def test_qdisc3(self):
copy_unit_to_networkd_unit_path('25-qdisc-clsact-root-compat.network', '12-dummy.netdev',
copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
'25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
check_output('modprobe sch_teql max_equalizers=2')
start_networkd()
self.wait_online(['dummy98:routable', 'test1:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc clsact')
output = check_output('tc qdisc show dev test1')
print(output)
self.assertRegex(output, 'qdisc netem')
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
self.assertRegex(output, 'qdisc ingress')
def test_qdisc4(self):
copy_unit_to_networkd_unit_path('25-qdisc-teql.network', '12-dummy.netdev')
check_output('modprobe sch_teql max_equalizers=2')
start_networkd()
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc clsact')
self.assertRegex(output, 'qdisc htb 2: root')
self.assertRegex(output, r'default (0x30|30)')
self.assertRegex(output, 'qdisc netem 30: parent 2:30')
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
self.assertRegex(output, 'qdisc fq_codel')
self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
self.assertRegex(output, 'qdisc fq 32: parent 2:32')
self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
self.assertRegex(output, 'quantum 1500')
self.assertRegex(output, 'initial_quantum 13000')
self.assertRegex(output, 'maxrate 1Mbit')
self.assertRegex(output, 'qdisc codel 33: parent 2:33')
self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn')
self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms')
self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
self.assertRegex(output, 'perturb 5sec')
self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
self.assertRegex(output, 'limit 100000p')
self.assertRegex(output, 'qdisc gred 38: parent 2:38')
self.assertRegex(output, 'vqs 12 default 10 grio')
self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
self.assertRegex(output, 'limit 200000')
output = check_output('tc class show dev dummy98')
print(output)
self.assertRegex(output, 'class htb 2:30 root leaf 30:')
self.assertRegex(output, 'class htb 2:31 root leaf 31:')
self.assertRegex(output, 'class htb 2:32 root leaf 32:')
self.assertRegex(output, 'class htb 2:33 root leaf 33:')
self.assertRegex(output, 'class htb 2:34 root leaf 34:')
self.assertRegex(output, 'class htb 2:35 root leaf 35:')
self.assertRegex(output, 'class htb 2:36 root leaf 36:')
self.assertRegex(output, 'class htb 2:37 root leaf 37:')
self.assertRegex(output, 'class htb 2:38 root leaf 38:')
self.assertRegex(output, 'class htb 2:39 root leaf 39:')
self.assertRegex(output, 'prio 1 rate 1Mbit ceil 500Kbit')
@expectedFailureIfCAKEIsNotAvailable()
def test_qdisc_cake(self):
copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'qdisc teql1 2:')
self.assertRegex(output, 'qdisc cake 3a: root')
self.assertRegex(output, 'bandwidth 500Mbit')
self.assertRegex(output, 'overhead 128')
class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [