Compare commits

...

43 Commits

Author SHA1 Message Date
Yu Watanabe bba1f90ff5
Merge pull request #14890 from yuwata/network-tc-next
network: tc-next
2020-03-07 01:44:15 +09:00
Yu Watanabe a34811e4ef udev: support to update flow control parameter
Closes #14770.
2020-03-07 01:43:26 +09:00
Susant Sahani 5d3b801764 network: Add support to ignore foreign routes 2020-03-07 01:42:52 +09:00
Yu Watanabe d739fddeb5 network: add setting to support RA without DHCPv6 client
Closes #13991.
2020-03-07 01:39:26 +09:00
Susant Sahani cd305af1fe network: Allow DHCPv6 client to start without router's managed flag. 2020-03-07 01:38:26 +09:00
Jörg Thalheim eec394f10b systemd-resolved: use hostname for certificate validation in DoT
Widely accepted certificates for IP addresses are expensive and only
affordable for larger organizations. Therefore if the user provides
the hostname in the DNS= option, we should use it instead of the IP
address.
2020-03-07 01:37:30 +09:00
Chris Down 0d14eefb0d
Merge pull request #14893 from benzea/benzea/generator-path-environment-variables
Generator path environment variables
2020-03-06 16:15:08 +00:00
Chris Down a1e13c30de
Merge pull request #14904 from ssahani/dhcp-server-force-renew
network: Introduce force renew for DHCP server
2020-03-06 16:14:30 +00:00
Chris Down 5bb67b107f
Merge pull request #14956 from ssahani/delegated-prefix-14474
nettwork: introduce delegated prefix assign
2020-03-06 16:13:13 +00:00
Chris Down dfb3303b6c
Merge pull request #15010 from cgzones/selinux_reload_cache_enforce
SELinux: add trigger for policy reload and cache enforced status
2020-03-06 16:12:48 +00:00
Benjamin Berg b7cf4b4ef5 core: Fix resolution of nested DM devices for cgroups
When using the cgroups IO controller, the device that is controlled
should always be the toplevel block device. This did not get resolved
correctly for an LVM volume inside a LUKS device, because the code would
only resolve one level of indirection.

Fix this by recursively looking up the originating block device for DM
devices.

Resolves: #15008
2020-03-06 16:11:44 +00:00
Benjamin Berg 5b058473fe man: Add documentation for generator path environment variables
This adds documentation for the SYSTEMD_GENERATOR_PATH and
SYSTEMD_ENVIRONMENT_GENERATOR_PATH variables to the systemd man page
grouped with the existing SYSTEMD_UNIT_PATH.

Also added is a description about how these variables work, i.e. that a
trailing : can be used to prepend paths to the usual set.
2020-03-04 11:32:19 +01:00
Benjamin Berg 96b10a13f2 test: Add test for setting generator paths via environment 2020-03-04 11:25:14 +01:00
Benjamin Berg 39f7d10c24 path-lookup: Allow setting generator paths via environment
This adds SYSTEMD_GENERATOR_PATH and SYSTEMD_ENVIRONMENT_GENERATOR_PATH
environment variables that will be read in the same manner as
SYSTEMD_UNIT_PATH is. i.e. if set, these paths will be used and a
trailing empty entry means that the usual paths will be appended, while
no trailing entry means that solely the given paths are used.
2020-03-04 11:25:12 +01:00
Benjamin Berg 2909f4dd28 path-lookup: Split out helper to get paths from environment
This is so that we can use the same pattern for other sets of paths.
2020-03-04 11:24:37 +01:00
Benjamin Berg cccf570355 core: Move environment generator path lookup into path-lookup.c 2020-03-04 11:24:33 +01:00
Christian Göttsche 257188f80c selinux: cache enforced status and treat retrieve failure as enforced mode 2020-03-03 20:30:59 +01:00
Christian Göttsche 61f3e897f1 selinux: add trigger for policy reload to refresh internal selabel cache
Reload the internal selabel cache automatically on SELinux policy reloads so non pid-1 daemons are participating.

Run the reload function `mac_selinux_reload()` not manually on daemon-reload, but rather pass it as callback to libselinux.
Trigger the callback prior usage of the systemd internal selabel cache by depleting the selinux netlink socket via `avc_netlink_check_nb()`.

Improves: a9dfac21ec ("core: reload SELinux label cache on daemon-reload")
Improves: #13363
2020-03-03 20:25:49 +01:00
Yu Watanabe f6c6af3811 test-network: add a test case for CAKE 2020-03-02 15:59:37 +09:00
Susant Sahani ad8352f4ff network: tc: introduce cake
CAKE (Common Applications Kept Enhanced)

Please see http://man7.org/linux/man-pages/man8/tc-cake.8.html
2020-03-02 15:59:37 +09:00
Susant Sahani aa550d2a51 sd-netlink: introduce sd_netlink_message_append_s8() and friends 2020-03-02 15:59:32 +09:00
Yu Watanabe f2c5c1296a test-network: add a test case for sfb 2020-03-02 15:48:24 +09:00
Susant Sahani 982998b087 network: TC introduce sfb - Stochastic Fair Blue
Please see https://www.systutorials.com/docs/linux/man/8-tc-sfb/
2020-03-02 15:48:24 +09:00
Yu Watanabe 95edcf3fac test-network: add a test case for GRED 2020-03-02 15:48:24 +09:00
Susant Sahani 609e8340bb network: TC introduce GRED, Generic Random Early Detection
http://tldp.org/en/Traffic-Control-HOWTO/ar01s06.html
2020-03-02 15:48:24 +09:00
Yu Watanabe bc0769c9f7 test-network: add a test case for PFIFO 2020-03-02 15:48:11 +09:00
Susant Sahani a74760653c network: TC introduce PFIFO 2020-03-02 15:48:00 +09:00
Yu Watanabe f1de1eb3e3 test-network: add test case for HTB class 2020-03-02 15:46:28 +09:00
Yu Watanabe 19f86a6351 network: tc: support HTB class 2020-03-02 15:46:28 +09:00
Yu Watanabe 4666f63bb8 network: introduce struct TClass to prepare for supporting tc class 2020-03-02 15:46:28 +09:00
Yu Watanabe 0ebb76de8b sd-netlink: introduce sd_rtnl_message_new_tclass() and friends 2020-03-02 15:46:28 +09:00
Yu Watanabe 34658df256 network: introduce TrafficControlKind to prepare for supporting tc class 2020-03-02 15:46:28 +09:00
Yu Watanabe 931c8c824a test-network: add a test case for HTB 2020-03-02 15:46:28 +09:00
Yu Watanabe b934ac3d6e network: tc: support Hierarchy Token Bucket (HTB) 2020-03-02 15:46:28 +09:00
Yu Watanabe 2a09633117 network: tc: make Parent= take class id 2020-03-02 15:46:28 +09:00
Yu Watanabe 3c874fd79d test-network: add a test case for [IPv6Prefix] Assign=yes 2020-03-01 00:49:24 +09:00
Susant Sahani a781ddefe5 network: make use of generate_ipv6_eui_64_address 2020-03-01 00:49:24 +09:00
Susant Sahani bd6379ec57 network: introduce IPv6 prefix assign
Expose a boolean flag to automatically add an address from the delegated prefix to the interface
2020-03-01 00:49:19 +09:00
Susant Sahani 34332af2bc network: radv introduce sd_radv_prefix_get_prefix 2020-03-01 00:16:51 +09:00
Susant Sahani 5ead535224 network: Introduce method to generate EUI-64 addresses 2020-03-01 00:16:51 +09:00
Susant Sahani 3efdd6af2c network: Add support in networkctl for DHCP server forcerenew 2020-02-28 03:29:28 +01:00
Susant Sahani a32c7a4615 network: Add policy in polkit DHCP server forcenenew 2020-02-19 07:41:36 +01:00
Susant Sahani 90867f6a3a network: introduce bus method forcerenew for DHCP server 2020-02-19 07:40:42 +01:00
80 changed files with 2874 additions and 342 deletions

View File

@ -267,6 +267,14 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
Takes interface name or index number.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>forcerenew</command>
</term>
<listitem><para>Send a FORCERENEW message to all connected clients, triggering DHCP reconfiguration.
Takes interface name or index number.</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>reconfigure</command>

View File

@ -61,6 +61,15 @@
<listitem><para>Specifies the time interval to calculate the traffic speed of each interface.
If <varname>SpeedMeter=no</varname>, the value is ignored. Defaults to 10sec.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ManageForeignRoutes=</varname></term>
<listitem><para>A boolean. When true, <command>systemd-networkd</command> will store any routes
configured by other tools in its memory. When false, <command>systemd-networkd</command> will
not manage the foreign routes, thus they are kept even if <varname>KeepConfiguration=</varname>
is false. Defaults to yes.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -193,11 +193,17 @@
<varlistentry>
<term><varname>DNSOverTLS=</varname></term>
<listitem>
<para>Takes a boolean argument or <literal>opportunistic</literal>.
If true all connections to the server will be encrypted. Note that
this mode requires a DNS server that supports DNS-over-TLS and has
a valid certificate for it's IP. If the DNS server does not support
DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
<para>Takes a boolean argument or <literal>opportunistic</literal>. If
true all connections to the server will be encrypted. Note that this
mode requires a DNS server that supports DNS-over-TLS and has a valid
certificate. If the hostname was specified in <varname>DNS=</varname>
by using the format format <literal>address#server_name</literal> it
is used to validate its certificate and also to enable Server Name
Indication (SNI) when opening a TLS connection. Otherwise
the certificate is checked against the server's IP.
If the DNS server does not support DNS-over-TLS all DNS requests will fail.</para>
<para>When set to <literal>opportunistic</literal>
DNS request are attempted to send encrypted with DNS-over-TLS.
If the DNS server does not support TLS, DNS-over-TLS is disabled.
Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
@ -214,9 +220,6 @@
resolver is not capable of authenticating the server, so it is
vulnerable to "man-in-the-middle" attacks.</para>
<para>Server Name Indication (SNI) can be used when opening a TLS connection.
Entries in <varname>DNS=</varname> should be in format <literal>address#server_name</literal>.</para>
<para>In addition to this global DNSOverTLS setting
<citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
also maintains per-link DNSOverTLS settings. For system DNS

View File

@ -700,6 +700,30 @@
<para>Takes a integer. Specifies the NIC transmit ring buffer size. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>RxFlowControl=</varname></term>
<listitem>
<para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>TxFlowControl=</varname></term>
<listitem>
<para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
default will be used.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>AutoNegotiationFlowControl=</varname></term>
<listitem>
<para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
advertisements with the connected peer so that the two devices can agree on the ethernet
PAUSE configuration. When unset, the kernel's default will be used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -1694,6 +1694,14 @@
a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>WithoutRA=</varname></term>
<listitem>
<para>When true, DHCPv6 client starts without router advertisements's managed or other address configuration flag.
Defaults to false.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -1768,6 +1776,14 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DHCPv6Client=</varname></term>
<listitem>
<para>Takes a boolean. When true (the default), the DHCPv6 client will be started when the
RA has the managed or other information flag.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -2038,6 +2054,11 @@
to 2592000 seconds (30 days).</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Assign=</varname></term>
<listitem><para>Takes a boolean. When true, adds an address from the prefix. Default to false.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@ -2327,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>
@ -2391,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>
@ -2468,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
@ -2478,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>
@ -2499,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
@ -2509,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>
@ -2563,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
@ -2573,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>
@ -2663,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>
@ -2765,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>
@ -2789,6 +2979,94 @@
</variablelist>
</refsect1>
<refsect1>
<title>[HierarchyTokenBucket] Section Options</title>
<para>The <literal>[HierarchyTokenBucket]</literal> section manages the queueing discipline (qdisc) of
hierarchy token bucket (htb).</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
<literal>clsact</literal>, <literal>ingress</literal> or a class id. The class id takes the
major and minor number in hexadecimal ranges 1 to ffff separated with a colon
(<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Handle=</varname></term>
<listitem>
<para>Specifies the major number of unique identifier of the qdisc, known as the handle.
Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DefaultClass=</varname></term>
<listitem>
<para>Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent
to the class. Defaults to unset.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[HierarchyTokenBucketClass] Section Options</title>
<para>The <literal>[HierarchyTokenBucketClass]</literal> section manages the traffic control class of
hierarchy token bucket (htb).</para>
<variablelist class='network-directives'>
<varlistentry>
<term><varname>Parent=</varname></term>
<listitem>
<para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
or a qdisc id. The qdisc id takes the major and minor number in hexadecimal ranges 1 to ffff
separated with a colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ClassId=</varname></term>
<listitem>
<para>Specifies the major and minur number of unique identifier of the class, known as the
class ID. Each number is in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Priority=</varname></term>
<listitem>
<para>Specifies the priority of the class. In the round-robin process, classes with the lowest
priority field are tried for packets first. This setting is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Rate=</varname></term>
<listitem>
<para>Specifies the maximum rate this class and all its children are guaranteed. When suffixed
with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively,
to the base of 1000. This setting is mandatory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>CeilRate=</varname></term>
<listitem>
<para>Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare.
When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits,
respectively, to the base of 1000. When unset, the value specified with <varname>Rate=</varname>
is used.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>[BridgeVLAN] Section Options</title>
<para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts

View File

@ -649,9 +649,17 @@
<varlistentry>
<term><varname>$SYSTEMD_UNIT_PATH</varname></term>
<term><varname>$SYSTEMD_GENERATOR_PATH</varname></term>
<term><varname>$SYSTEMD_ENVIRONMENT_GENERATOR_PATH</varname></term>
<listitem><para>Controls where systemd looks for unit
files.</para></listitem>
<listitem><para>Controls where systemd looks for unit files and
generators.</para>
<para>These variables may contain a list of paths, separated by colons
(<literal>:</literal>). When set, if the list ends with an empty
component (<literal>...:</literal>), this list is prepended to the
usual set of of paths. Otherwise, the specified list replaces the usual
set of paths.
</para></listitem>
</varlistentry>
<varlistentry>

View File

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

View File

@ -12,6 +12,7 @@
#include <syslog.h>
#if HAVE_SELINUX
#include <selinux/avc.h>
#include <selinux/context.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
@ -31,11 +32,14 @@
DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
#define _cleanup_context_free_ _cleanup_(context_freep)
static int mac_selinux_reload(int seqno);
static int cached_use = -1;
static int cached_enforcing = -1;
static struct selabel_handle *label_hnd = NULL;
#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
#define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__)
#define log_enforcing(...) log_full(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
#define log_enforcing_errno(r, ...) log_full_errno(mac_selinux_enforcing() ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__)
#endif
bool mac_selinux_use(void) {
@ -49,12 +53,37 @@ bool mac_selinux_use(void) {
#endif
}
bool mac_selinux_enforcing(void) {
#if HAVE_SELINUX
if (cached_enforcing < 0) {
cached_enforcing = security_getenforce();
if (cached_enforcing == -1) {
log_error_errno(errno, "Failed to get SELinux enforced status: %m");
}
}
/* treat failure as enforced mode */
return (cached_enforcing != 0);
#else
return false;
#endif
}
void mac_selinux_retest(void) {
#if HAVE_SELINUX
cached_use = -1;
cached_enforcing = -1;
#endif
}
#if HAVE_SELINUX
static int setenforce_callback(int enforcing) {
cached_enforcing = enforcing;
return 0;
}
#endif
int mac_selinux_init(void) {
int r = 0;
@ -62,6 +91,9 @@ int mac_selinux_init(void) {
usec_t before_timestamp, after_timestamp;
struct mallinfo before_mallinfo, after_mallinfo;
selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
selinux_set_callback(SELINUX_CB_SETENFORCE, (union selinux_callback) setenforce_callback);
if (label_hnd)
return 0;
@ -74,7 +106,7 @@ int mac_selinux_init(void) {
label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
if (!label_hnd) {
log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
r = security_getenforce() == 1 ? -errno : 0;
r = mac_selinux_enforcing() ? -errno : 0;
} else {
char timespan[FORMAT_TIMESPAN_MAX];
int l;
@ -104,13 +136,12 @@ void mac_selinux_finish(void) {
#endif
}
void mac_selinux_reload(void) {
#if HAVE_SELINUX
static int mac_selinux_reload(int seqno) {
struct selabel_handle *backup_label_hnd;
if (!label_hnd)
return;
return 0;
backup_label_hnd = TAKE_PTR(label_hnd);
@ -121,8 +152,10 @@ void mac_selinux_reload(void) {
selabel_close(backup_label_hnd);
else
label_hnd = backup_label_hnd;
#endif
return 0;
}
#endif
int mac_selinux_fix(const char *path, LabelFixFlags flags) {
@ -151,6 +184,9 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
if (fstat(fd, &st) < 0)
return -errno;
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
(void) avc_netlink_check_nb();
if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
r = -errno;
@ -186,7 +222,7 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
fail:
log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path);
if (security_getenforce() == 1)
if (mac_selinux_enforcing())
return r;
#endif
@ -204,7 +240,7 @@ int mac_selinux_apply(const char *path, const char *label) {
if (setfilecon(path, label) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
if (security_getenforce() > 0)
if (mac_selinux_enforcing())
return -errno;
}
#endif
@ -349,6 +385,9 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
assert(abspath);
assert(path_is_absolute(abspath));
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
(void) avc_netlink_check_nb();
r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
if (r < 0) {
/* No context specified by the policy? Proceed without setting it. */
@ -363,7 +402,7 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
}
if (security_getenforce() > 0)
if (mac_selinux_enforcing())
return -errno;
return 0;
@ -444,7 +483,7 @@ int mac_selinux_create_socket_prepare(const char *label) {
if (setsockcreatecon(label) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
if (security_getenforce() == 1)
if (mac_selinux_enforcing())
return -errno;
}
#endif
@ -497,6 +536,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
/* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
(void) avc_netlink_check_nb();
if (path_is_absolute(path))
r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
else {
@ -515,13 +557,13 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
goto skipped;
log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
if (security_getenforce() > 0)
if (mac_selinux_enforcing())
return -errno;
} else {
if (setfscreatecon_raw(fcon) < 0) {
log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
if (security_getenforce() > 0)
if (mac_selinux_enforcing())
return -errno;
} else
context_changed = true;

View File

@ -16,11 +16,11 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon);
#endif
bool mac_selinux_use(void);
bool mac_selinux_enforcing(void);
void mac_selinux_retest(void);
int mac_selinux_init(void);
void mac_selinux_finish(void);
void mac_selinux_reload(void);
int mac_selinux_fix(const char *path, LabelFixFlags flags);
int mac_selinux_apply(const char *path, const char *label);

View File

@ -678,8 +678,8 @@ static int lookup_block_device(const char *p, dev_t *ret) {
return log_warning_errno(r, "Failed to determine block device backing btrfs file system '%s': %m", p);
}
/* If this is a LUKS device, try to get the originating block device */
(void) block_get_originating(*ret, ret);
/* If this is a LUKS/DM device, recursively try to get the originating block device */
while (block_get_originating(*ret, ret) > 0);
/* If this is a partition, try to get the originating block device */
(void) block_get_whole_disk(*ret, ret);

View File

@ -1747,8 +1747,6 @@ static int invoke_main_loop(
saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
mac_selinux_reload();
(void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
set_manager_defaults(m);

View File

@ -3826,25 +3826,9 @@ static bool generator_path_any(const char* const* paths) {
return found;
}
static const char *const system_env_generator_binary_paths[] = {
"/run/systemd/system-environment-generators",
"/etc/systemd/system-environment-generators",
"/usr/local/lib/systemd/system-environment-generators",
SYSTEM_ENV_GENERATOR_PATH,
NULL
};
static const char *const user_env_generator_binary_paths[] = {
"/run/systemd/user-environment-generators",
"/etc/systemd/user-environment-generators",
"/usr/local/lib/systemd/user-environment-generators",
USER_ENV_GENERATOR_PATH,
NULL
};
static int manager_run_environment_generators(Manager *m) {
char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
const char *const *paths;
_cleanup_strv_free_ char **paths = NULL;
void* args[] = {
[STDOUT_GENERATE] = &tmp,
[STDOUT_COLLECT] = &tmp,
@ -3855,13 +3839,15 @@ static int manager_run_environment_generators(Manager *m) {
if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
return 0;
paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
paths = env_generator_binary_paths(MANAGER_IS_SYSTEM(m));
if (!paths)
return log_oom();
if (!generator_path_any(paths))
if (!generator_path_any((const char* const*) paths))
return 0;
RUN_WITH_UMASK(0022)
r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment,
r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
args, NULL, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
return r;
}

View File

@ -143,16 +143,16 @@ static int access_init(sd_bus_error *error) {
return 1;
if (avc_open(NULL, 0) != 0) {
int enforce, saved_errno = errno;
int saved_errno = errno;
const bool enforce = mac_selinux_enforcing();
enforce = security_getenforce();
log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
log_full_errno(enforce ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
/* If enforcement isn't on, then let's suppress this
* error, and just don't do any AVC checks. The
* warning we printed is hence all the admin will
* see. */
if (enforce == 0)
if (!enforce)
return 0;
/* Return an access denied error, if we couldn't load
@ -185,7 +185,7 @@ int mac_selinux_generic_access_check(
_cleanup_free_ char *cl = NULL;
_cleanup_freecon_ char *fcon = NULL;
char **cmdline = NULL;
bool enforce = false; /* Will be set to the real value later if needed */
const bool enforce = mac_selinux_enforcing();
int r = 0;
assert(message);
@ -223,7 +223,6 @@ int mac_selinux_generic_access_check(
if (getfilecon_raw(path, &fcon) < 0) {
r = -errno;
enforce = security_getenforce() > 0;
log_warning_errno(r, "SELinux getfilecon_raw on '%s' failed%s (perm=%s): %m",
path,
@ -240,7 +239,6 @@ int mac_selinux_generic_access_check(
} else {
if (getcon_raw(&fcon) < 0) {
r = -errno;
enforce = security_getenforce() > 0;
log_warning_errno(r, "SELinux getcon_raw failed%s (perm=%s): %m",
enforce ? "" : ", ignoring",
@ -266,7 +264,6 @@ int mac_selinux_generic_access_check(
r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
if (r < 0) {
r = errno_or_else(EPERM);
enforce = security_getenforce() > 0;
if (enforce)
sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");

View File

@ -829,6 +829,18 @@ _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr
return 0;
}
_public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
unsigned char *ret_prefixlen) {
assert_return(p, -EINVAL);
assert_return(ret_in6_addr, -EINVAL);
assert_return(ret_prefixlen, -EINVAL);
*ret_in6_addr = p->opt.in6_addr;
*ret_prefixlen = p->opt.prefixlen;
return 0;
}
_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
assert_return(p, -EINVAL);

View File

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

View File

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

View File

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

View File

@ -47,6 +47,10 @@ static inline bool rtnl_message_type_is_qdisc(uint16_t type) {
return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
}
static inline bool rtnl_message_type_is_tclass(uint16_t type) {
return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
}
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);

View File

@ -1077,3 +1077,46 @@ int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) {
return 0;
}
int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) {
struct tcmsg *tcm;
int r;
assert_return(rtnl_message_type_is_tclass(nlmsg_type), -EINVAL);
assert_return(ret, -EINVAL);
r = message_new(rtnl, ret, nlmsg_type);
if (r < 0)
return r;
if (nlmsg_type == RTM_NEWTCLASS)
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
tcm = NLMSG_DATA((*ret)->hdr);
tcm->tcm_family = tcm_family;
tcm->tcm_ifindex = tcm_ifindex;
return 0;
}
int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_parent = parent;
return 0;
}
int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) {
struct tcmsg *tcm;
assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
tcm = NLMSG_DATA(m->hdr);
tcm->tcm_handle = handle;
return 0;
}

View File

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

View File

@ -1943,6 +1943,48 @@ static int link_renew(int argc, char *argv[], void *userdata) {
return k;
}
static int link_force_renew_one(sd_bus *bus, int index, const char *name) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
r = sd_bus_call_method(
bus,
"org.freedesktop.network1",
"/org/freedesktop/network1",
"org.freedesktop.network1.Manager",
"ForceRenewLink",
&error,
NULL,
"i", index);
if (r < 0)
return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s",
name, bus_error_message(&error, r));
return 0;
}
static int link_force_renew(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
int index, i, k = 0, r;
r = sd_bus_open_system(&bus);
if (r < 0)
return log_error_errno(r, "Failed to connect system bus: %m");
for (i = 1; i < argc; i++) {
index = resolve_interface_or_warn(&rtnl, argv[i]);
if (index < 0)
return index;
r = link_force_renew_one(bus, index, argv[i]);
if (r < 0 && k >= 0)
k = r;
}
return k;
}
static int verb_reload(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
@ -2029,6 +2071,7 @@ static int help(void) {
" label Show current address label entries in the kernel\n"
" delete DEVICES... Delete virtual netdevs\n"
" renew DEVICES... Renew dynamic configurations\n"
" forcerenew DEVICES... Trigger DHCP reconfiguration of all connected clients\n"
" reconfigure DEVICES... Reconfigure interfaces\n"
" reload Reload .network and .netdev files\n"
"\nOptions:\n"
@ -2130,6 +2173,7 @@ static int networkctl_main(int argc, char *argv[]) {
{ "label", VERB_ANY, VERB_ANY, 0, list_address_labels },
{ "delete", 2, VERB_ANY, 0, link_delete },
{ "renew", 2, VERB_ANY, 0, link_renew },
{ "forcerenew", 2, VERB_ANY, 0, link_force_renew },
{ "reconfigure", 2, VERB_ANY, 0, verb_reconfigure },
{ "reload", 1, 1, 0, verb_reload },
{}

View File

@ -20,6 +20,24 @@
#define ADDRESSES_PER_LINK_MAX 2048U
#define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) {
assert(link);
assert(ret);
/* see RFC4291 section 2.5.1 */
ret->s6_addr[8] = link->mac.ether_addr_octet[0];
ret->s6_addr[8] ^= 1 << 1;
ret->s6_addr[9] = link->mac.ether_addr_octet[1];
ret->s6_addr[10] = link->mac.ether_addr_octet[2];
ret->s6_addr[11] = 0xff;
ret->s6_addr[12] = 0xfe;
ret->s6_addr[13] = link->mac.ether_addr_octet[3];
ret->s6_addr[14] = link->mac.ether_addr_octet[4];
ret->s6_addr[15] = link->mac.ether_addr_octet[5];
return 0;
}
int address_new(Address **ret) {
_cleanup_(address_freep) Address *address = NULL;

View File

@ -66,6 +66,8 @@ bool address_is_ready(const Address *a);
int address_section_verify(Address *a);
int configure_ipv4_duplicate_address_detection(Link *link, Address *address);
int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret);
DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
extern const struct hash_ops address_hash_ops;

View File

@ -20,5 +20,6 @@ struct ConfigPerfItem;
%%
Network.SpeedMeter, config_parse_bool, 0, offsetof(Manager, use_speed_meter)
Network.SpeedMeterIntervalSec, config_parse_sec, 0, offsetof(Manager, speed_meter_interval_usec)
Network.ManageForeignRoutes, config_parse_bool, 0, offsetof(Manager, manage_foreign_routes)
DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid)
DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid)

View File

@ -572,6 +572,35 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e
return sd_bus_reply_method_return(message, NULL);
}
int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
int r;
assert(l);
if (!l->network)
return sd_bus_error_setf(error, BUS_ERROR_UNMANAGED_INTERFACE,
"Interface %s is not managed by systemd-networkd",
l->ifname);
r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
"org.freedesktop.network1.forcerenew",
NULL, true, UID_INVALID,
&l->manager->polkit_registry, error);
if (r < 0)
return r;
if (r == 0)
return 1; /* Polkit will call us back */
if (l->dhcp_server) {
r = sd_dhcp_server_forcerenew(l->dhcp_server);
if (r < 0)
return r;
}
return sd_bus_reply_method_return(message, NULL);
}
int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Link *l = userdata;
int r;
@ -651,6 +680,7 @@ const sd_bus_vtable link_vtable[] = {
SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ForceRenew", NULL, NULL, bus_link_method_force_renew, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END

View File

@ -31,4 +31,5 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error);

View File

@ -34,7 +34,6 @@
#include "networkd-radv.h"
#include "networkd-routing-policy-rule.h"
#include "networkd-wifi.h"
#include "qdisc.h"
#include "set.h"
#include "socket-util.h"
#include "stat-util.h"
@ -42,6 +41,7 @@
#include "string-table.h"
#include "strv.h"
#include "sysctl-util.h"
#include "tc.h"
#include "tmpfile-util.h"
#include "udev-util.h"
#include "util.h"
@ -1116,7 +1116,7 @@ void link_check_ready(Link *link) {
if (!link->routing_policy_rules_configured)
return;
if (!link->qdiscs_configured)
if (!link->tc_configured)
return;
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
@ -1222,6 +1222,7 @@ static int link_set_bridge_fdb(Link *link) {
static int link_request_set_addresses(Link *link) {
AddressLabel *label;
Address *ad;
Prefix *p;
int r;
assert(link);
@ -1257,6 +1258,35 @@ static int link_request_set_addresses(Link *link) {
link->address_messages++;
}
if (IN_SET(link->network->router_prefix_delegation,
RADV_PREFIX_DELEGATION_STATIC,
RADV_PREFIX_DELEGATION_BOTH))
LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
_cleanup_(address_freep) Address *address = NULL;
if (!p->assign)
continue;
r = address_new(&address);
if (r < 0)
return log_link_error_errno(link, r, "Could not allocate address: %m");
r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
if (r < 0)
return r;
r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
if (r < 0)
return r;
address->family = AF_INET6;
r = address_configure(address, link, address_handler, true);
if (r < 0)
return log_link_warning_errno(link, r, "Could not set addresses: %m");
if (r > 0)
link->address_messages++;
}
LIST_FOREACH(labels, label, link->network->address_labels) {
r = address_label_configure(label, link, NULL, false);
if (r < 0)
@ -1523,6 +1553,17 @@ static int link_acquire_ipv6_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
if (link_dhcp6_enabled(link) && link->network->dhcp6_without_ra) {
assert(link->dhcp6_client);
assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
r = dhcp6_request_address(link, true);
if (r < 0 && r != -EBUSY)
return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
else
log_link_debug(link, "Acquiring DHCPv6 lease");
}
(void) dhcp6_request_prefix_delegation(link);
return 0;
@ -2702,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;
}
@ -2731,7 +2772,7 @@ static int link_configure(Link *link) {
assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED);
r = link_configure_qdiscs(link);
r = link_configure_traffic_control(link);
if (r < 0)
return r;

View File

@ -80,7 +80,7 @@ typedef struct Link {
unsigned nexthop_messages;
unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages;
unsigned qdisc_messages;
unsigned tc_messages;
unsigned enslaving;
Set *addresses;
@ -116,7 +116,7 @@ typedef struct Link {
bool static_routes_ready:1;
bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1;
bool qdiscs_configured:1;
bool tc_configured:1;
bool setting_mtu:1;
bool setting_genmode:1;
bool ipv6_mtu_set:1;

View File

@ -191,6 +191,10 @@ static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus
return call_link_method(userdata, message, bus_link_method_renew, error);
}
static int bus_method_force_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_force_renew, error);
}
static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
return call_link_method(userdata, message, bus_link_method_reconfigure, error);
}
@ -249,6 +253,7 @@ const sd_bus_vtable manager_vtable[] = {
SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ForceRenewLink", "i", NULL, bus_method_force_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED),

View File

@ -494,7 +494,8 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
log_link_debug(link,
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering",
(!route && !link->manager->manage_foreign_routes) || type == RTM_DELROUTE ? "Forgetting" :
route ? "Received remembered" : "Remembering",
strna(buf_dst), strempty(buf_dst_prefixlen),
strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
@ -505,7 +506,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
switch (type) {
case RTM_NEWROUTE:
if (!route) {
if (!route && link->manager->manage_foreign_routes) {
/* A route appeared that we did not request */
r = route_add_foreign(link, tmp, &route);
if (r < 0) {
@ -1747,6 +1748,7 @@ int manager_new(Manager **ret) {
*m = (Manager) {
.speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
.manage_foreign_routes = true,
};
m->state_file = strdup("/run/systemd/netif/state");

View File

@ -30,6 +30,7 @@ struct Manager {
bool enumerating:1;
bool dirty:1;
bool restarting:1;
bool manage_foreign_routes;
Set *dirty_links;

View File

@ -324,16 +324,10 @@ static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint3
*new_address = *address;
/* see RFC4291 section 2.5.1 */
new_address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0];
new_address->in_addr.in6.s6_addr[8] ^= 1 << 1;
new_address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1];
new_address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
new_address->in_addr.in6.s6_addr[11] = 0xff;
new_address->in_addr.in6.s6_addr[12] = 0xfe;
new_address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
new_address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
new_address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
r = generate_ipv6_eui_64_address(link, &new_address->in_addr.in6);
if (r < 0)
return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m");
new_address->prefixlen = prefixlen;
new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
new_address->cinfo.ifa_prefered = lifetime_preferred;
@ -800,7 +794,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) &&
link->network->ipv6_accept_ra_start_dhcp6_client) {
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
if (r < 0 && r != -EBUSY)

View File

@ -14,6 +14,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "qdisc.h"
#include "tclass.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
@ -188,10 +189,12 @@ DHCPv6.UseNTP, config_parse_bool,
DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
DHCPv6.WithoutRA, config_parse_bool, 0, offsetof(Network, dhcp6_without_ra)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.DHCPv6Client, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0
IPv6AcceptRA.BlackList, config_parse_ndisc_black_listed_prefix, 0, 0
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
@ -245,6 +248,7 @@ IPv6Prefix.OnLink, config_parse_prefix_flags,
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0
IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0
IPv6Prefix.Assign, config_parse_prefix_assign, 0, 0
IPv6RoutePrefix.Route, config_parse_route_prefix, 0, 0
IPv6RoutePrefix.LifetimeSec, config_parse_route_prefix_lifetime, 0, 0
CAN.BitRate, config_parse_si_uint64, 0, offsetof(Network, can_bitrate)
@ -253,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
@ -260,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
@ -281,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
@ -288,6 +312,9 @@ NetworkEmulator.DelayJitterSec, config_parse_network_emulator_delay
NetworkEmulator.LossRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
NetworkEmulator.DuplicateRate, config_parse_network_emulator_rate, QDISC_KIND_NETEM, 0
NetworkEmulator.PacketLimit, config_parse_network_emulator_packet_limit, QDISC_KIND_NETEM, 0
StochasticFairBlue.Parent, config_parse_qdisc_parent, QDISC_KIND_SFB, 0
StochasticFairBlue.Handle, config_parse_qdisc_handle, QDISC_KIND_SFB, 0
StochasticFairBlue.PacketLimit, config_parse_stochastic_fair_blue_u32, QDISC_KIND_SFB, 0
StochasticFairnessQueueing.Parent, config_parse_qdisc_parent, QDISC_KIND_SFQ, 0
StochasticFairnessQueueing.Handle, config_parse_qdisc_handle, QDISC_KIND_SFQ, 0
StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ, 0

View File

@ -22,6 +22,7 @@
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "tc.h"
#include "util.h"
/* Let's assume that anything above this number is a user misconfiguration. */
@ -154,7 +155,7 @@ int network_verify(Network *network) {
Prefix *prefix, *prefix_next;
Route *route, *route_next;
FdbEntry *fdb, *fdb_next;
QDisc *qdisc;
TrafficControl *tc;
Iterator i;
assert(network);
@ -316,9 +317,9 @@ int network_verify(Network *network) {
routing_policy_rule_free(rule);
bool has_root = false, has_clsact = false;
ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
qdisc_free(qdisc);
ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i)
if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
traffic_control_free(tc);
return 0;
}
@ -451,6 +452,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.ipv6_accept_ra_use_onlink_prefix = true,
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
.ipv6_accept_ra_route_table_set = false,
.ipv6_accept_ra_start_dhcp6_client = true,
.keep_configuration = _KEEP_CONFIGURATION_INVALID,
@ -483,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",
@ -690,7 +698,7 @@ static Network *network_free(Network *network) {
hashmap_free(network->prefixes_by_section);
hashmap_free(network->route_prefixes_by_section);
hashmap_free(network->rules_by_section);
ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
if (network->manager &&
network->manager->duids_requesting_uuid)

View File

@ -30,7 +30,6 @@
#include "networkd-routing-policy-rule.h"
#include "networkd-util.h"
#include "ordered-set.h"
#include "qdisc.h"
#include "resolve-util.h"
typedef enum IPv6PrivacyExtensions {
@ -126,6 +125,7 @@ struct Network {
/* DHCPv6 Client support*/
bool dhcp6_use_dns;
bool dhcp6_use_ntp;
bool dhcp6_without_ra;
uint8_t dhcp6_pd_length;
struct in6_addr dhcp6_pd_address;
@ -213,6 +213,7 @@ struct Network {
bool ipv6_accept_ra_use_dns;
bool ipv6_accept_ra_use_autonomous_prefix;
bool ipv6_accept_ra_use_onlink_prefix;
bool ipv6_accept_ra_start_dhcp6_client;
bool active_slave;
bool primary_slave;
DHCPUseDomains ipv6_accept_ra_use_domains;
@ -274,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;

View File

@ -319,6 +319,46 @@ int config_parse_prefix_lifetime(const char *unit,
return 0;
}
int config_parse_prefix_assign(
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) {
Network *network = userdata;
_cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
int r;
assert(filename);
assert(section);
assert(lvalue);
assert(rvalue);
assert(data);
r = prefix_new_static(network, filename, section_line, &p);
if (r < 0)
return r;
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;
}
p->assign = r;
p = NULL;
return 0;
}
int config_parse_route_prefix(const char *unit,
const char *filename,
unsigned line,

View File

@ -28,6 +28,8 @@ struct Prefix {
sd_radv_prefix *radv_prefix;
bool assign;
LIST_FIELDS(Prefix, prefixes);
};
@ -59,6 +61,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);

View File

@ -14,6 +14,7 @@
[Network]
#SpeedMeter=no
#SpeedMeterIntervalSec=10sec
#ManageForeignRoutes=yes
[DHCP]
#DUIDType=vendor

View File

@ -139,6 +139,17 @@
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
</action>
<action id="org.freedesktop.network1.forcerenew">
<description gettext-domain="systemd">DHCP server sends force renew message</description>
<message gettext-domain="systemd">Authentication is required to send force renew message.</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>auth_admin_keep</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
</action>
<action id="org.freedesktop.network1.renew">
<description gettext-domain="systemd">Renew dynamic addresses</description>
<message gettext-domain="systemd">Authentication is required to renew dynamic addresses.</message>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

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

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

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

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

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

@ -0,0 +1,277 @@
/* SPDX-License-Identifier: LGPL-2.1+
* Copyright © 2019 VMware, Inc. */
#include <linux/pkt_sched.h>
#include "alloc-util.h"
#include "conf-parser.h"
#include "in-addr-util.h"
#include "netlink-util.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "set.h"
#include "string-util.h"
#include "strv.h"
#include "tc-util.h"
#include "tclass.h"
const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
[TCLASS_KIND_HTB] = &htb_tclass_vtable,
};
static int tclass_new(TClassKind kind, TClass **ret) {
TClass *tclass;
int r;
tclass = malloc0(tclass_vtable[kind]->object_size);
if (!tclass)
return -ENOMEM;
tclass->meta.kind = TC_KIND_TCLASS,
tclass->parent = TC_H_ROOT;
tclass->kind = kind;
if (TCLASS_VTABLE(tclass)->init) {
r = TCLASS_VTABLE(tclass)->init(tclass);
if (r < 0)
return r;
}
*ret = TAKE_PTR(tclass);
return 0;
}
int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
_cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
_cleanup_(tclass_freep) TClass *tclass = NULL;
TrafficControl *existing;
int r;
assert(network);
assert(ret);
assert(filename);
assert(section_line > 0);
r = network_config_section_new(filename, section_line, &n);
if (r < 0)
return r;
existing = ordered_hashmap_get(network->tc_by_section, n);
if (existing) {
TClass *t;
if (existing->kind != TC_KIND_TCLASS)
return -EINVAL;
t = TC_TO_TCLASS(existing);
if (t->kind != kind)
return -EINVAL;
*ret = t;
return 0;
}
r = tclass_new(kind, &tclass);
if (r < 0)
return r;
tclass->network = network;
tclass->section = TAKE_PTR(n);
r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
if (r < 0)
return r;
*ret = TAKE_PTR(tclass);
return 0;
}
void tclass_free(TClass *tclass) {
if (!tclass)
return;
if (tclass->network && tclass->section)
ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
network_config_section_free(tclass->section);
free(tclass);
}
static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
assert(link);
assert(link->tc_messages > 0);
link->tc_messages--;
if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
return 1;
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_message_error_errno(link, m, r, "Could not set TClass");
link_enter_failed(link);
return 1;
}
if (link->tc_messages == 0) {
log_link_debug(link, "Traffic control configured");
link->tc_configured = true;
link_check_ready(link);
}
return 1;
}
int tclass_configure(Link *link, TClass *tclass) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
int r;
assert(link);
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
if (r < 0)
return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
if (r < 0)
return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
if (tclass->classid != TC_H_UNSPEC) {
r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
if (r < 0)
return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
}
r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
if (r < 0)
return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
if (TCLASS_VTABLE(tclass)->fill_message) {
r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
if (r < 0)
return r;
}
r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
link_ref(link);
link->tc_messages++;
return 0;
}
int tclass_section_verify(TClass *tclass) {
int r;
assert(tclass);
if (section_is_invalid(tclass->section))
return -EINVAL;
if (TCLASS_VTABLE(tclass)->verify) {
r = TCLASS_VTABLE(tclass)->verify(tclass);
if (r < 0)
return r;
}
return 0;
}
int config_parse_tclass_parent(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
if (r < 0)
return r;
if (streq(rvalue, "root"))
tclass->parent = TC_H_ROOT;
else {
r = parse_handle(rvalue, &tclass->parent);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'Parent=', ignoring assignment: %s",
rvalue);
return 0;
}
}
tclass = NULL;
return 0;
}
int config_parse_tclass_classid(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
_cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
Network *network = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
r = tclass_new_static(ltype, network, filename, section_line, &tclass);
if (r < 0)
return r;
if (isempty(rvalue)) {
tclass->classid = TC_H_UNSPEC;
tclass = NULL;
return 0;
}
r = parse_handle(rvalue, &tclass->classid);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse 'ClassId=', ignoring assignment: %s",
rvalue);
return 0;
}
tclass = NULL;
return 0;
}

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

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

View File

@ -56,15 +56,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
}
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
if (server->family == AF_INET) {
stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
stream->dnstls_data.validation.size = 4;
} else {
stream->dnstls_data.validation.data = server->address.in6.s6_addr;
stream->dnstls_data.validation.size = 16;
if (server->server_name)
gnutls_session_set_verify_cert(gs, server->server_name, 0);
else {
stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
if (server->family == AF_INET) {
stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
stream->dnstls_data.validation.size = 4;
} else {
stream->dnstls_data.validation.data = server->address.in6.s6_addr;
stream->dnstls_data.validation.size = 16;
}
gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
}
if (server->server_name) {

View File

@ -6,6 +6,7 @@
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
#include "io-util.h"
#include "resolved-dns-stream.h"
@ -80,13 +81,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
X509_VERIFY_PARAM *v;
const unsigned char *ip;
SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
v = SSL_get0_param(s);
ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
return -ECONNREFUSED;
if (server->server_name) {
X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
return -ECONNREFUSED;
} else {
const unsigned char *ip;
ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
return -ECONNREFUSED;
}
}
if (server->server_name) {

View File

@ -851,6 +851,55 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
return 0;
}
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
struct ethtool_pauseparam ecmd = {
.cmd = ETHTOOL_GPAUSEPARAM
};
struct ifreq ifr = {
.ifr_data = (void*) &ecmd
};
bool need_update = false;
int r;
if (*fd < 0) {
r = ethtool_connect_or_warn(fd, true);
if (r < 0)
return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
r = ioctl(*fd, SIOCETHTOOL, &ifr);
if (r < 0)
return -errno;
if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
ecmd.rx_pause = rx;
need_update = true;
}
if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
ecmd.tx_pause = tx;
need_update = true;
}
if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
ecmd.autoneg = autoneg;
need_update = true;
}
if (need_update) {
ecmd.cmd = ETHTOOL_SPAUSEPARAM;
r = ioctl(*fd, SIOCETHTOOL, &ifr);
if (r < 0)
return -errno;
}
return 0;
}
int config_parse_channel(const char *unit,
const char *filename,
unsigned line,

View File

@ -103,6 +103,7 @@ int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
int autonegotiation, uint32_t advertise[static N_ADVERTISE],
uint64_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels);
int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
const char *duplex_to_string(Duplex d) _const_;
Duplex duplex_from_string(const char *d) _pure_;

View File

@ -479,6 +479,36 @@ static int patch_root_prefix_strv(char **l, const char *root_dir) {
return 0;
}
static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
const char *e;
int r;
assert(var);
assert(paths);
assert(append);
*append = false;
e = getenv(var);
if (e) {
const char *k;
k = endswith(e, ":");
if (k) {
e = strndupa(e, k - e);
*append = true;
}
/* FIXME: empty components in other places should be rejected. */
r = path_split_and_make_absolute(e, paths);
if (r < 0)
return r;
}
return 0;
}
int lookup_paths_init(
LookupPaths *p,
UnitFileScope scope,
@ -496,7 +526,6 @@ int lookup_paths_init(
*persistent_attached = NULL, *runtime_attached = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
const char *e;
int r;
assert(p);
@ -562,22 +591,9 @@ int lookup_paths_init(
return r;
/* First priority is whatever has been passed to us via env vars */
e = getenv("SYSTEMD_UNIT_PATH");
if (e) {
const char *k;
k = endswith(e, ":");
if (k) {
e = strndupa(e, k - e);
append = true;
}
/* FIXME: empty components in other places should be rejected. */
r = path_split_and_make_absolute(e, &paths);
if (r < 0)
return r;
}
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
if (r < 0)
return r;
if (!paths || append) {
/* Let's figure something out. */
@ -817,23 +833,90 @@ void lookup_paths_flush_generator(LookupPaths *p) {
}
char **generator_binary_paths(UnitFileScope scope) {
bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
int r;
switch (scope) {
/* First priority is whatever has been passed to us via env vars */
r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
if (r < 0)
return NULL;
case UNIT_FILE_SYSTEM:
return strv_new("/run/systemd/system-generators",
"/etc/systemd/system-generators",
"/usr/local/lib/systemd/system-generators",
SYSTEM_GENERATOR_PATH);
if (!paths || append) {
_cleanup_strv_free_ char **add = NULL;
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER:
return strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
USER_GENERATOR_PATH);
switch (scope) {
default:
assert_not_reached("Hmm, unexpected scope.");
case UNIT_FILE_SYSTEM:
add = strv_new("/run/systemd/system-generators",
"/etc/systemd/system-generators",
"/usr/local/lib/systemd/system-generators",
SYSTEM_GENERATOR_PATH);
break;
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER:
add = strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
USER_GENERATOR_PATH);
break;
default:
assert_not_reached("Hmm, unexpected scope.");
}
if (!add)
return NULL;
if (paths) {
r = strv_extend_strv(&paths, add, true);
if (r < 0)
return NULL;
} else
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
* and don't have to copy anything */
paths = TAKE_PTR(add);
}
return TAKE_PTR(paths);
}
char **env_generator_binary_paths(bool is_system) {
bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
_cleanup_strv_free_ char **add = NULL;
int r;
/* First priority is whatever has been passed to us via env vars */
r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
if (r < 0)
return NULL;
if (!paths || append) {
if (is_system)
add = strv_new("/run/systemd/system-environment-generators",
"/etc/systemd/system-environment-generators",
"/usr/local/lib/systemd/system-environment-generators",
SYSTEM_ENV_GENERATOR_PATH);
else
add = strv_new("/run/systemd/user-environment-generators",
"/etc/systemd/user-environment-generators",
"/usr/local/lib/systemd/user-environment-generators",
USER_ENV_GENERATOR_PATH);
if (!add)
return NULL;
}
if (paths) {
r = strv_extend_strv(&paths, add, true);
if (r < 0)
return NULL;
} else
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
* and don't have to copy anything */
paths = TAKE_PTR(add);
return TAKE_PTR(paths);
}

View File

@ -72,3 +72,4 @@ void lookup_paths_flush_generator(LookupPaths *p);
void lookup_paths_free(LookupPaths *p);
char **generator_binary_paths(UnitFileScope scope);
char **env_generator_binary_paths(bool is_system);

View File

@ -86,6 +86,10 @@ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uin
int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
@ -208,6 +212,10 @@ int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle);
int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent);
int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle);
/* genl */
int sd_genl_socket_open(sd_netlink **nl);
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);

View File

@ -74,6 +74,8 @@ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
unsigned char prefixlen);
int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
unsigned char *ret_prefixlen);
int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
int address_autoconfiguration);

View File

@ -67,15 +67,54 @@ static void test_user_and_global_paths(void) {
log_info("+ %s", *p);
}
static void print_generator_binary_paths(UnitFileScope scope) {
_cleanup_strv_free_ char **paths;
static void test_generator_binary_paths(UnitFileScope scope) {
char template[] = "/tmp/test-path-lookup.XXXXXXX";
_cleanup_strv_free_ char **gp_without_env = NULL;
_cleanup_strv_free_ char **env_gp_without_env = NULL;
_cleanup_strv_free_ char **gp_with_env = NULL;
_cleanup_strv_free_ char **env_gp_with_env = NULL;
char *systemd_generator_path = NULL;
char *systemd_env_generator_path = NULL;
char **dir;
log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
assert_se(mkdtemp(template));
paths = generator_binary_paths(scope);
STRV_FOREACH(dir, paths)
assert_se(unsetenv("SYSTEMD_GENERATOR_PATH") == 0);
assert_se(unsetenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH") == 0);
gp_without_env = generator_binary_paths(scope);
env_gp_without_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
STRV_FOREACH(dir, gp_without_env)
log_info(" %s", *dir);
log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
STRV_FOREACH(dir, env_gp_without_env)
log_info(" %s", *dir);
assert_se(!strv_isempty(gp_without_env));
assert_se(!strv_isempty(env_gp_without_env));
systemd_generator_path = strjoina(template, "/systemd-generator-path");
systemd_env_generator_path = strjoina(template, "/systemd-environment-generator-path");
assert_se(setenv("SYSTEMD_GENERATOR_PATH", systemd_generator_path, 1) == 0);
assert_se(setenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", systemd_env_generator_path, 1) == 0);
gp_with_env = generator_binary_paths(scope);
env_gp_with_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
STRV_FOREACH(dir, gp_with_env)
log_info(" %s", *dir);
log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
STRV_FOREACH(dir, env_gp_with_env)
log_info(" %s", *dir);
assert_se(strv_equal(gp_with_env, STRV_MAKE(systemd_generator_path)));
assert_se(strv_equal(env_gp_with_env, STRV_MAKE(systemd_env_generator_path)));
}
int main(int argc, char **argv) {
@ -87,8 +126,8 @@ int main(int argc, char **argv) {
test_user_and_global_paths();
print_generator_binary_paths(UNIT_FILE_SYSTEM);
print_generator_binary_paths(UNIT_FILE_USER);
test_generator_binary_paths(UNIT_FILE_SYSTEM);
test_generator_binary_paths(UNIT_FILE_USER);
return EXIT_SUCCESS;
}

View File

@ -60,3 +60,6 @@ Link.CombinedChannels, config_parse_channel, 0,
Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise)
Link.RxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring)
Link.TxBufferSize, config_parse_nic_buffer_size, 0, offsetof(link_config, ring)
Link.RxFlowControl, config_parse_tristate, 0, offsetof(link_config, rx_flow_control)
Link.TxFlowControl, config_parse_tristate, 0, offsetof(link_config, tx_flow_control)
Link.AutoNegotiationFlowControl, config_parse_tristate, 0, offsetof(link_config, autoneg_flow_control)

View File

@ -148,6 +148,9 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
.duplex = _DUP_INVALID,
.port = _NET_DEV_PORT_INVALID,
.autonegotiation = -1,
.rx_flow_control = -1,
.tx_flow_control = -1,
.autoneg_flow_control = -1,
};
for (i = 0; i < ELEMENTSOF(link->features); i++)
@ -409,6 +412,10 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
}
r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
if (r < 0)
log_warning_errno(r, "Could not set flow control of %s: %m", old_name);
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return log_device_warning_errno(device, r, "Could not find ifindex: %m");

View File

@ -62,6 +62,9 @@ struct link_config {
int features[_NET_DEV_FEAT_MAX];
netdev_channels channels;
netdev_ring_param ring;
int rx_flow_control;
int tx_flow_control;
int autoneg_flow_control;
LIST_FIELDS(link_config, links);
};

View File

@ -41,3 +41,6 @@ CombinedChannels=
Advertise=
RxBufferSize=
TxBufferSize=
RxFlowControl=
TxFlowControl=
AutoNegotiationFlowControl=

View File

@ -107,6 +107,7 @@ UseDNS=
RapidCommit=
ForceDHCPv6PDOtherInformation=
PrefixDelegationHint=
WithoutRA=
[Route]
Destination=
Protocol=
@ -186,6 +187,7 @@ OnLink=
PreferredLifetimeSec=
AddressAutoconfiguration=
ValidLifetimeSec=
Assign=
[IPv6RoutePrefix]
Route=
LifetimeSec=
@ -249,6 +251,7 @@ Prefix=
UseDomains=
RouteTable=
UseDNS=
DHCPv6Client=
UseAutonomousPrefix=
UseOnLinkPrefix=
BlackList=
@ -326,6 +329,11 @@ TargetSec=
IntervalSec=
CEThresholdSec=
ECN=
[CAKE]
Parent=
Handle=
Bandwidth=
Overhead=
[TrafficControlQueueingDiscipline]
Parent=
NetworkEmulatorDelaySec=
@ -337,3 +345,27 @@ NetworkEmulatorPacketLimit=
Parent=
Handle=
Id=
[HierarchyTokenBucket]
Parent=
Handle=
DefaultClass=
[HierarchyTokenBucketClass]
Parent=
ClassId=
Priority=
Rate=
CeilRate=
[PFIFO]
Parent=
Handle=
PacketLimit=
[GenericRandomEarlyDetection]
Parent=
Handle=
VirtualQueues=
DefaultVirtualQueue=
GenericRIO=
[StochasticFairBlue]
Parent=
Handle=
PacketLimit=

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,10 +4,13 @@ Name=veth99
[Network]
DHCP=no
IPv6PrefixDelegation=yes
Address=2001:db8:0:1::1/64
[IPv6Prefix]
Prefix=2001:db8:0:1::4/64
Prefix=2001:db8:0:1::/64
[IPv6Prefix]
Prefix=2001:db8:0:2::/64
Assign=yes
[IPv6RoutePrefix]
Route=2001:db0:fff::/64

View File

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