commit
bba1f90ff5
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
|
@ -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);
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
[Match]
|
||||
Name=dummy98
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
Address=10.1.2.3/16
|
||||
|
||||
#[TrafficControlQueueingDiscipline]
|
||||
#Parent=root
|
||||
|
||||
[TrafficControlQueueingDiscipline]
|
||||
Parent=clsact
|
|
@ -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
|
|
@ -1,12 +0,0 @@
|
|||
[Match]
|
||||
Name=test1
|
||||
|
||||
[Network]
|
||||
IPv6AcceptRA=no
|
||||
Address=10.1.2.4/16
|
||||
|
||||
#[QDisc]
|
||||
#Parent=root
|
||||
|
||||
[QDisc]
|
||||
Parent=ingress
|
|
@ -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
|
|
@ -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
|
|
@ -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 = [
|
||||
|
|
Loading…
Reference in New Issue