Compare commits

...

8 Commits

Author SHA1 Message Date
Yu Watanabe a346aa7c38
Merge pull request #13747 from ssahani/tc-qdisc
network: introduce Traffic Control
2019-10-30 14:35:51 +09:00
Yu Watanabe afa1a54eb5
Merge pull request #13867 from keszybz/man-condition
Refactor description of conditons
2019-10-30 09:40:00 +09:00
Yu Watanabe ef3c8a92b7 test-network: add tests for qdisc 2019-10-30 09:33:51 +09:00
Yu Watanabe 4ecdcb07c9 network: wait for QDiscs to be configured 2019-10-30 09:33:51 +09:00
Susant Sahani 0f5bd7fe24 network: introduce TrafficControl
Add network delay to a interface
2019-10-30 09:33:51 +09:00
Zbigniew Jędrzejewski-Szmek 54166ceece man: reword description of triggering conditions
Fixes #13758.
2019-10-29 14:54:36 +01:00
Zbigniew Jędrzejewski-Szmek 337b733449 man: split out description of Conditions and Assert to new section
We slowly added many many conditions over the years, and the text became
very hard to read, because all the terms were squished in one <termitem>.
This rearragnes the text into a new subsection, with minimal grammar changes
and removal of repetitions.
2019-10-29 14:52:27 +01:00
Zbigniew Jędrzejewski-Szmek c82a2b5b84 shared: small typo 2019-10-28 21:39:55 +01:00
23 changed files with 2360 additions and 362 deletions

View File

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

View File

@ -516,7 +516,6 @@
type of unit:</para> type of unit:</para>
<variablelist class='unit-directives'> <variablelist class='unit-directives'>
<varlistentry> <varlistentry>
<term><varname>Description=</varname></term> <term><varname>Description=</varname></term>
<listitem><para>A human readable name for the unit. This is used by <listitem><para>A human readable name for the unit. This is used by
@ -990,7 +989,6 @@
the start will not be permitted. Defaults to <option>none</option>.</para></listitem> the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>RebootArgument=</varname></term> <term><varname>RebootArgument=</varname></term>
<listitem><para>Configure the optional argument for the <listitem><para>Configure the optional argument for the
@ -1000,61 +998,63 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>ConditionArchitecture=</varname></term> <term><varname>SourcePath=</varname></term>
<term><varname>ConditionVirtualization=</varname></term> <listitem><para>A path to a configuration file this unit has
<term><varname>ConditionHost=</varname></term> been generated from. This is primarily useful for
<term><varname>ConditionKernelCommandLine=</varname></term> implementation of generator tools that convert configuration
<term><varname>ConditionKernelVersion=</varname></term> from an external configuration file format into native unit
<term><varname>ConditionSecurity=</varname></term> files. This functionality should not be used in normal
<term><varname>ConditionCapability=</varname></term> units.</para></listitem>
<term><varname>ConditionACPower=</varname></term> </varlistentry>
<term><varname>ConditionNeedsUpdate=</varname></term> </variablelist>
<term><varname>ConditionFirstBoot=</varname></term>
<term><varname>ConditionPathExists=</varname></term>
<term><varname>ConditionPathExistsGlob=</varname></term>
<term><varname>ConditionPathIsDirectory=</varname></term>
<term><varname>ConditionPathIsSymbolicLink=</varname></term>
<term><varname>ConditionPathIsMountPoint=</varname></term>
<term><varname>ConditionPathIsReadWrite=</varname></term>
<term><varname>ConditionDirectoryNotEmpty=</varname></term>
<term><varname>ConditionFileNotEmpty=</varname></term>
<term><varname>ConditionFileIsExecutable=</varname></term>
<term><varname>ConditionUser=</varname></term>
<term><varname>ConditionGroup=</varname></term>
<term><varname>ConditionControlGroupController=</varname></term>
<term><varname>ConditionMemory=</varname></term>
<term><varname>ConditionCPUs=</varname></term>
<refsect2>
<title>Conditions and Asserts</title>
<para>Unit files may also include a number of <varname noindex="true">Condition…=</varname> and
<varname noindex="true">Assert…=</varname> settings. Before the unit is started, systemd will verify
that the specified conditions are true. If not, the starting of the unit will be (mostly silently)
skipped. Failing conditions will not result in the unit being moved into the <literal>failed</literal>
state. The conditions are checked at the time the queued start job is to be executed. The ordering
dependencies are still respected, so other units are still pulled in and ordered as if this unit was
successfully activated. Use condition expressions in order to skip units that do not apply to the local
system, for example because the kernel or runtime environment doesn't require their functionality.
</para>
<para>If multiple conditions are specified, the unit will be executed if all of them apply (i.e. a
logical AND is applied). Condition checks can use a pipe symbol (<literal>|</literal>) after the equals
sign (<literal>Condition…=|…</literal>), which causes the condition becomes a triggering condition. If
at least one triggering condition is defined for a unit, then the unit will be executed if at least one
of the triggering conditions apply and all of the non-triggering conditions. If you prefix an argument
with the pipe symbol and an exclamation mark, the pipe symbol must be passed first, the exclamation
second. If any of these options is assigned the empty string, the list of conditions is reset
completely, all previous condition settings (of any kind) will have no effect.</para>
<para>The <varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options
provide a similar mechanism that causes the job to fail (instead of being skipped). The failed check is
logged. Units with failed conditions are considered to be in a clean state and will be garbage
collected if they are not referenced. This means that when queried, the condition failure may or may
not show up in the state of the unit.</para>
<para>Note that neither assertion nor condition expressions result in unit state changes. Also note
that both are checked at the time the job is to be executed, i.e. long after depending jobs and it
itself were queued. Thus, neither condition nor assertion expressions are suitable for conditionalizing
unit dependencies.</para>
<para>The <command>condition</command> verb of
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry> can
be used to test condition and assert expressions.</para>
<para>Except for <varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks.</para>
<variablelist class='unit-directives'>
<!-- We do not document ConditionNull= here, as it is not particularly useful and probably just <!-- We do not document ConditionNull= here, as it is not particularly useful and probably just
confusing. --> confusing. -->
<listitem><para>Before starting a unit, verify that the specified condition is true. If it is not true, the <varlistentry>
starting of the unit will be (mostly silently) skipped, however all ordering dependencies of it are still <term><varname>ConditionArchitecture=</varname></term>
respected. A failing condition will not result in the unit being moved into the <literal>failed</literal>
state. The condition is checked at the time the queued start job is to be executed. Use condition expressions
in order to silently skip units that do not apply to the local running system, for example because the kernel
or runtime environment doesn't require their functionality. Use the various
<varname>AssertArchitecture=</varname>, <varname>AssertVirtualization=</varname>, … options for a similar
mechanism that causes the job to fail (instead of being skipped) and results in logging about the failed check
(instead of being silently processed). For details about assertion conditions see below. Units with failed
conditions are considered to be in a clean state and will be garbage collected if they are not referenced.
This means, that when queried, the condition failure may or may not show up in the state of the unit.</para>
<para>If multiple conditions are specified, the unit will be executed if all of them apply (i.e. a <listitem><para>Check whether the system is running on a specific architecture. Takes one of
logical AND is applied). Condition checks can be prefixed with a pipe symbol (<literal>|</literal>)
in which case a condition becomes a triggering condition. If at least one triggering condition is
defined for a unit, then the unit will be executed if at least one of the triggering conditions apply
and all of the non-triggering conditions. If you prefix an argument with the pipe symbol and an
exclamation mark, the pipe symbol must be passed first, the exclamation second. Except for
<varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks. If any of these
options is assigned the empty string, the list of conditions is reset completely, all previous
condition settings (of any kind) will have no effect. The <command>condition</command> verb of
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
can be used to test condition and assert expressions.</para>
<para><varname>ConditionArchitecture=</varname> may be used to
check whether the system is running on a specific
architecture. Takes one of
<literal>x86</literal>, <literal>x86</literal>,
<literal>x86-64</literal>, <literal>x86-64</literal>,
<literal>ppc</literal>, <literal>ppc</literal>,
@ -1083,26 +1083,28 @@
<literal>tilegx</literal>, <literal>tilegx</literal>,
<literal>cris</literal>, <literal>cris</literal>,
<literal>arc</literal>, <literal>arc</literal>,
<literal>arc-be</literal> to test <literal>arc-be</literal>, or
against a specific architecture. The architecture is <literal>native</literal>.</para>
determined from the information returned by
<para>The architecture is determined from the information returned by
<citerefentry project='man-pages'><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
and is thus subject to and is thus subject to
<citerefentry><refentrytitle>personality</refentrytitle><manvolnum>2</manvolnum></citerefentry>. <citerefentry><refentrytitle>personality</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
Note that a <varname>Personality=</varname> setting in the Note that a <varname>Personality=</varname> setting in the same unit file has no effect on this
same unit file has no effect on this condition. A special condition. A special architecture name <literal>native</literal> is mapped to the architecture the
architecture name <literal>native</literal> is mapped to the system manager itself is compiled for. The test may be negated by prepending an exclamation
architecture the system manager itself is compiled for. The mark.</para>
test may be negated by prepending an exclamation mark.</para> </listitem>
</varlistentry>
<para><varname>ConditionVirtualization=</varname> may be used <varlistentry>
to check whether the system is executed in a virtualized <term><varname>ConditionArchitecture=</varname></term>
environment and optionally test whether it is a specific
implementation. Takes either boolean value to check if being <listitem><para>Check whether the system is executed in a virtualized environment and optionally
executed in any virtualized environment, or one of test whether it is a specific implementation. Takes either boolean value to check if being executed
in any virtualized environment, or one of
<literal>vm</literal> and <literal>vm</literal> and
<literal>container</literal> to test against a generic type of <literal>container</literal> to test against a generic type of virtualization solution, or one of
virtualization solution, or one of
<literal>qemu</literal>, <literal>qemu</literal>,
<literal>kvm</literal>, <literal>kvm</literal>,
<literal>zvm</literal>, <literal>zvm</literal>,
@ -1126,189 +1128,263 @@
against a specific implementation, or against a specific implementation, or
<literal>private-users</literal> to check whether we are running in a user namespace. See <literal>private-users</literal> to check whether we are running in a user namespace. See
<citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry> <citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for a full list of known virtualization technologies and their for a full list of known virtualization technologies and their identifiers. If multiple
identifiers. If multiple virtualization technologies are virtualization technologies are nested, only the innermost is considered. The test may be negated
nested, only the innermost is considered. The test may be by prepending an exclamation mark.</para>
negated by prepending an exclamation mark.</para> </listitem>
</varlistentry>
<para><varname>ConditionHost=</varname> may be used to match <varlistentry>
against the hostname or machine ID of the host. This either <term><varname>ConditionHost=</varname></term>
takes a hostname string (optionally with shell style globs)
which is tested against the locally set hostname as returned <listitem><para><varname>ConditionHost=</varname> may be used to match against the hostname or
by machine ID of the host. This either takes a hostname string (optionally with shell style globs)
<citerefentry><refentrytitle>gethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry>, which is tested against the locally set hostname as returned by
or a machine ID formatted as string (see <citerefentry><refentrytitle>gethostname</refentrytitle><manvolnum>2</manvolnum></citerefentry>, or
a machine ID formatted as string (see
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>). <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
The test may be negated by prepending an exclamation The test may be negated by prepending an exclamation mark.</para>
mark.</para> </listitem>
</varlistentry>
<para><varname>ConditionKernelCommandLine=</varname> may be <varlistentry>
used to check whether a specific kernel command line option is <term><varname>ConditionKernelCommandLine=</varname></term>
set (or if prefixed with the exclamation mark unset). The
argument must either be a single word, or an assignment (i.e.
two words, separated <literal>=</literal>). In the former case
the kernel command line is searched for the word appearing as
is, or as left hand side of an assignment. In the latter case,
the exact assignment is looked for with right and left hand
side matching.</para>
<para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel version (as <listitem><para><varname>ConditionKernelCommandLine=</varname> may be used to check whether a
reported by <command>uname -r</command>) matches a certain expression (or if prefixed with the specific kernel command line option is set (or if prefixed with the exclamation mark — unset). The
exclamation mark does not match it). The argument must be a list of (potentially quoted) expressions. argument must either be a single word, or an assignment (i.e. two words, separated by
For each of the expressions, if it starts with one of <literal>&lt;</literal>, <literal>=</literal>). In the former case the kernel command line is searched for the word
appearing as is, or as left hand side of an assignment. In the latter case, the exact assignment is
looked for with right and left hand side matching.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionKernelVersion=</varname></term>
<listitem><para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel
version (as reported by <command>uname -r</command>) matches a certain expression (or if prefixed
with the exclamation mark does not match it). The argument must be a list of (potentially quoted)
expressions. For each of the expressions, if it starts with one of <literal>&lt;</literal>,
<literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>&gt;=</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>&gt;=</literal>,
<literal>&gt;</literal> a relative version comparison is done, otherwise the specified string is <literal>&gt;</literal> a relative version comparison is done, otherwise the specified string is
matched with shell-style globs.</para> matched with shell-style globs.</para>
<para>Note that using the kernel version string is an unreliable way to determine which features are supported <para>Note that using the kernel version string is an unreliable way to determine which features
by a kernel, because of the widespread practice of backporting drivers, features, and fixes from newer upstream are supported by a kernel, because of the widespread practice of backporting drivers, features, and
kernels into older versions provided by distributions. Hence, this check is inherently unportable and should fixes from newer upstream kernels into older versions provided by distributions. Hence, this check
not be used for units which may be used on different distributions.</para> is inherently unportable and should not be used for units which may be used on different
distributions.</para>
</listitem>
</varlistentry>
<para><varname>ConditionSecurity=</varname> may be used to check <varlistentry>
whether the given security technology is enabled on the <term><varname>ConditionSecurity=</varname></term>
system. Currently, the recognized values are
<literal>selinux</literal>, <literal>apparmor</literal>,
<literal>tomoyo</literal>, <literal>ima</literal>,
<literal>smack</literal>, <literal>audit</literal> and
<literal>uefi-secureboot</literal>. The test may be negated by
prepending an exclamation mark.</para>
<para><varname>ConditionCapability=</varname> may be used to <listitem><para><varname>ConditionSecurity=</varname> may be used to check whether the given
check whether the given capability exists in the capability security technology is enabled on the system. Currently, the recognized values are
bounding set of the service manager (i.e. this does not check <literal>selinux</literal>, <literal>apparmor</literal>, <literal>tomoyo</literal>,
whether capability is actually available in the permitted or <literal>ima</literal>, <literal>smack</literal>, <literal>audit</literal> and
effective sets, see <literal>uefi-secureboot</literal>. The test may be negated by prepending an exclamation
mark.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionCapability=</varname></term>
<listitem><para>Check whether the given capability exists in the capability bounding set of the
service manager (i.e. this does not check whether capability is actually available in the permitted
or effective sets, see
<citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry> <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details). Pass a capability name such as for details). Pass a capability name such as <literal>CAP_MKNOD</literal>, possibly prefixed with
<literal>CAP_MKNOD</literal>, possibly prefixed with an an exclamation mark to negate the check.</para>
exclamation mark to negate the check.</para> </listitem>
</varlistentry>
<para><varname>ConditionACPower=</varname> may be used to <varlistentry>
check whether the system has AC power, or is exclusively <term><varname>ConditionACPower=</varname></term>
battery powered at the time of activation of the unit. This
takes a boolean argument. If set to <literal>true</literal>,
the condition will hold only if at least one AC connector of
the system is connected to a power source, or if no AC
connectors are known. Conversely, if set to
<literal>false</literal>, the condition will hold only if
there is at least one AC connector known and all AC connectors
are disconnected from a power source.</para>
<para><varname>ConditionNeedsUpdate=</varname> takes one of <listitem><para>Check whether the system has AC power, or is exclusively battery powered at the
<filename>/var</filename> or <filename>/etc</filename> as time of activation of the unit. This takes a boolean argument. If set to <literal>true</literal>,
argument, possibly prefixed with a <literal>!</literal> (for the condition will hold only if at least one AC connector of the system is connected to a power
inverting the condition). This condition may be used to source, or if no AC connectors are known. Conversely, if set to <literal>false</literal>, the
conditionalize units on whether the specified directory condition will hold only if there is at least one AC connector known and all AC connectors are
requires an update because <filename>/usr</filename>'s disconnected from a power source.</para>
modification time is newer than the stamp file </listitem>
<filename>.updated</filename> in the specified directory. This </varlistentry>
is useful to implement offline updates of the vendor operating
system resources in <filename>/usr</filename> that require <varlistentry>
updating of <filename>/etc</filename> or <term><varname>ConditionNeedsUpdate=</varname></term>
<filename>/var</filename> on the next following boot. Units
making use of this condition should order themselves before <listitem><para>Takes one of <filename>/var</filename> or <filename>/etc</filename> as argument,
possibly prefixed with a <literal>!</literal> (to inverting the condition). This condition may be
used to conditionalize units on whether the specified directory requires an update because
<filename>/usr</filename>'s modification time is newer than the stamp file
<filename>.updated</filename> in the specified directory. This is useful to implement offline
updates of the vendor operating system resources in <filename>/usr</filename> that require updating
of <filename>/etc</filename> or <filename>/var</filename> on the next following boot. Units making
use of this condition should order themselves before
<citerefentry><refentrytitle>systemd-update-done.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, <citerefentry><refentrytitle>systemd-update-done.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
to make sure they run before the stamp file's modification to make sure they run before the stamp file's modification time gets reset indicating a completed
time gets reset indicating a completed update.</para> update.</para>
</listitem>
</varlistentry>
<para><varname>ConditionFirstBoot=</varname> takes a boolean argument. This condition may be used to <varlistentry>
conditionalize units on whether the system is booting up with an unpopulated <filename>/etc</filename> <term><varname>ConditionFirstBoot=</varname></term>
directory (specifically: an <filename>/etc</filename> with no <filename>/etc/machine-id</filename>). This may
be used to populate <filename>/etc</filename> on the first boot after factory reset, or when a new system
instance boots up for the first time.</para>
<para>With <varname>ConditionPathExists=</varname> a file <listitem><para>Takes a boolean argument. This condition may be used to conditionalize units on
existence condition is checked before a unit is started. If whether the system is booting up with an unpopulated <filename>/etc</filename> directory
the specified absolute path name does not exist, the condition (specifically: an <filename>/etc</filename> with no <filename>/etc/machine-id</filename>). This may
will fail. If the absolute path name passed to be used to populate <filename>/etc</filename> on the first boot after factory reset, or when a new
<varname>ConditionPathExists=</varname> is prefixed with an system instance boots up for the first time.</para>
exclamation mark (<literal>!</literal>), the test is negated, </listitem>
and the unit is only started if the path does not </varlistentry>
<varlistentry>
<term><varname>ConditionPathExists=</varname></term>
<listitem><para>Check for the exists of a file. If the specified absolute path name does not exist,
the condition will fail. If the absolute path name passed to
<varname>ConditionPathExists=</varname> is prefixed with an exclamation mark
(<literal>!</literal>), the test is negated, and the unit is only started if the path does not
exist.</para> exist.</para>
</listitem>
</varlistentry>
<para><varname>ConditionPathExistsGlob=</varname> is similar <varlistentry>
to <varname>ConditionPathExists=</varname>, but checks for the <term><varname>ConditionPathExistsGlob=</varname></term>
existence of at least one file or directory matching the
specified globbing pattern.</para>
<para><varname>ConditionPathIsDirectory=</varname> is similar <listitem><para><varname>ConditionPathExistsGlob=</varname> is similar to
to <varname>ConditionPathExists=</varname> but verifies <varname>ConditionPathExists=</varname>, but checks for the existence of at least one file or
whether a certain path exists and is a directory.</para> directory matching the specified globbing pattern.</para>
</listitem>
</varlistentry>
<para><varname>ConditionPathIsSymbolicLink=</varname> is <varlistentry>
similar to <varname>ConditionPathExists=</varname> but <term><varname>ConditionPathIsDirectory=</varname></term>
verifies whether a certain path exists and is a symbolic
link.</para>
<para><varname>ConditionPathIsMountPoint=</varname> is similar <listitem><para><varname>ConditionPathIsDirectory=</varname> is similar to
to <varname>ConditionPathExists=</varname> but verifies <varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a
whether a certain path exists and is a mount point.</para>
<para><varname>ConditionPathIsReadWrite=</varname> is similar
to <varname>ConditionPathExists=</varname> but verifies
whether the underlying file system is readable and writable
(i.e. not mounted read-only).</para>
<para><varname>ConditionDirectoryNotEmpty=</varname> is
similar to <varname>ConditionPathExists=</varname> but
verifies whether a certain path exists and is a non-empty
directory.</para> directory.</para>
</listitem>
</varlistentry>
<para><varname>ConditionFileNotEmpty=</varname> is similar to <varlistentry>
<varname>ConditionPathExists=</varname> but verifies whether a <term><varname>ConditionPathIsSymbolicLink=</varname></term>
certain path exists and refers to a regular file with a
non-zero size.</para>
<para><varname>ConditionFileIsExecutable=</varname> is similar <listitem><para><varname>ConditionPathIsSymbolicLink=</varname> is similar to
to <varname>ConditionPathExists=</varname> but verifies <varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a symbolic
whether a certain path exists, is a regular file and marked link.</para>
executable.</para> </listitem>
</varlistentry>
<para><varname>ConditionUser=</varname> takes a numeric <varlistentry>
<literal>UID</literal>, a UNIX user name, or the special value <term><varname>ConditionPathIsMountPoint=</varname></term>
<literal>@system</literal>. This condition may be used to check
whether the service manager is running as the given user. The
special value <literal>@system</literal> can be used to check
if the user id is within the system user range. This option is not
useful for system services, as the system manager exclusively
runs as the root user, and thus the test result is constant.</para>
<para><varname>ConditionGroup=</varname> is similar <listitem><para><varname>ConditionPathIsMountPoint=</varname> is similar to
to <varname>ConditionUser=</varname> but verifies that the <varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a mount
service manager's real or effective group, or any of its point.</para>
auxiliary groups match the specified group or GID. This setting </listitem>
does not have a special value <literal>@system</literal>.</para> </varlistentry>
<para><varname>ConditionControlGroupController=</varname> takes a <varlistentry>
cgroup controller name (eg. <literal>cpu</literal>), verifying that it is <term><varname>ConditionPathIsReadWrite=</varname></term>
available for use on the system. For example, a particular controller
may not be available if it was disabled on the kernel command line with
<varname>cgroup_disable=controller</varname>. Multiple controllers may
be passed with a space separating them; in this case the condition will
only pass if all listed controllers are available for use. Controllers
unknown to systemd are ignored. Valid controllers are
<literal>cpu</literal>, <literal>cpuacct</literal>, <literal>io</literal>,
<literal>blkio</literal>, <literal>memory</literal>,
<literal>devices</literal>, and <literal>pids</literal>.</para>
<para><varname>ConditionMemory=</varname> verifies if the specified amount of system memory is <listitem><para><varname>ConditionPathIsReadWrite=</varname> is similar to
available to the current system. Takes a memory size in bytes as argument, optionally prefixed with a <varname>ConditionPathExists=</varname> but verifies that the underlying file system is readable
comparison operator <literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, and writable (i.e. not mounted read-only).</para>
<literal>!=</literal>, <literal>&gt;=</literal>, <literal>&gt;</literal>. On bare-metal systems </listitem>
compares the amount of physical memory in the system with the specified size, adhering to the </varlistentry>
specified comparison operator. In containers compares the amount of memory assigned to the container
instead.</para>
<para><varname>ConditionCPUs=</varname> verifies if the specified number of CPUs is available to the <varlistentry>
current system. Takes a number of CPUs as argument, optionally prefixed with a comparison operator <term><varname>ConditionDirectoryNotEmpty=</varname></term>
<listitem><para><varname>ConditionDirectoryNotEmpty=</varname> is similar to
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a non-empty
directory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionFileNotEmpty=</varname></term>
<listitem><para><varname>ConditionFileNotEmpty=</varname> is similar to
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and refers to a
regular file with a non-zero size.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionFileIsExecutable=</varname></term>
<listitem><para><varname>ConditionFileIsExecutable=</varname> is similar to
<varname>ConditionPathExists=</varname> but verifies that a certain path exists, is a regular file,
and marked executable.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionUser=</varname></term>
<listitem><para><varname>ConditionUser=</varname> takes a numeric <literal>UID</literal>, a UNIX
user name, or the special value <literal>@system</literal>. This condition may be used to check
whether the service manager is running as the given user. The special value
<literal>@system</literal> can be used to check if the user id is within the system user
range. This option is not useful for system services, as the system manager exclusively runs as the
root user, and thus the test result is constant.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionGroup=</varname></term>
<listitem><para><varname>ConditionGroup=</varname> is similar to <varname>ConditionUser=</varname>
but verifies that the service manager's real or effective group, or any of its auxiliary groups,
match the specified group or GID. This setting does not support the special value
<literal>@system</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionControlGroupController=</varname></term>
<listitem><para>Verify that the given cgroup controller (eg. <literal>cpu</literal>) is available
for use on the system. For example, a particular controller may not be available if it was disabled
on the kernel command line with <varname>cgroup_disable=controller</varname>. Multiple controllers
may be passed with a space separating them; in this case the condition will only pass if all listed
controllers are available for use. Controllers unknown to systemd are ignored. Valid controllers
are <literal>cpu</literal>, <literal>cpuacct</literal>, <literal>io</literal>,
<literal>blkio</literal>, <literal>memory</literal>, <literal>devices</literal>, and
<literal>pids</literal>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionMemory=</varname></term>
<listitem><para>Verify that the specified amount of system memory is available to the current
system. Takes a memory size in bytes as argument, optionally prefixed with a comparison operator
<literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</literal>. Compares the number of CPUs in the CPU affinity mask <literal>&gt;=</literal>, <literal>&gt;</literal>. On bare-metal systems compares the amount of
configured of the service manager itself with the specified number, adhering to the specified physical memory in the system with the specified size, adhering to the specified comparison
operator. In containers compares the amount of memory assigned to the container instead.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>ConditionCPUs=</varname></term>
<listitem><para>Verify that the specified number of CPUs is available to the current system. Takes
a number of CPUs as argument, optionally prefixed with a comparison operator
<literal>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>,
<literal>&gt;=</literal>, <literal>&gt;</literal>. Compares the number of CPUs in the CPU affinity
mask configured of the service manager itself with the specified number, adhering to the specified
comparison operator. On physical systems the number of CPUs in the affinity mask of the service comparison operator. On physical systems the number of CPUs in the affinity mask of the service
manager usually matches the number of physical CPUs, but in special and virtual environments might manager usually matches the number of physical CPUs, but in special and virtual environments might
differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned to differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned
the container and not the physically available ones.</para></listitem> to the container and not the physically available ones.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -1336,34 +1412,18 @@
<term><varname>AssertControlGroupController=</varname></term> <term><varname>AssertControlGroupController=</varname></term>
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>, <listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings add <varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings
assertion checks to the start-up of the unit. However, unlike the conditions settings, any assertion setting add assertion checks to the start-up of the unit. However, unlike the conditions settings, any
that is not met results in failure of the start job (which means this is logged loudly). Note that hitting a assertion setting that is not met results in failure of the start job (which means this is logged
configured assertion does not cause the unit to enter the <literal>failed</literal> state (or in fact result in loudly). Note that hitting a configured assertion does not cause the unit to enter the
any state change of the unit), it affects only the job queued for it. Use assertion expressions for units that <literal>failed</literal> state (or in fact result in any state change of the unit), it affects
cannot operate when specific requirements are not met, and when this is something the administrator or user only the job queued for it. Use assertion expressions for units that cannot operate when specific
should look into.</para> requirements are not met, and when this is something the administrator or user should look
into.</para>
<para>Note that neither assertion nor condition expressions result in unit state changes. Also note that both </listitem>
are checked at the time the job is to be executed, i.e. long after depending jobs and it itself were
queued. Thus, neither condition nor assertion expressions are suitable for conditionalizing unit
dependencies.</para>
<para>The <command>condition</command> verb of
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
can be used to test condition and assert expressions.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SourcePath=</varname></term>
<listitem><para>A path to a configuration file this unit has
been generated from. This is primarily useful for
implementation of generator tools that convert configuration
from an external configuration file format into native unit
files. This functionality should not be used in normal
units.</para></listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
</refsect2>
</refsect1> </refsect1>
<refsect1> <refsect1>

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

@ -43,6 +43,7 @@
#include "tmpfile-util.h" #include "tmpfile-util.h"
#include "udev-util.h" #include "udev-util.h"
#include "util.h" #include "util.h"
#include "tc/qdisc.h"
#include "virt.h" #include "virt.h"
uint32_t link_get_vrf_table(Link *link) { uint32_t link_get_vrf_table(Link *link) {
@ -1094,6 +1095,9 @@ void link_check_ready(Link *link) {
if (!link->routing_policy_rules_configured) if (!link->routing_policy_rules_configured)
return; return;
if (!link->qdiscs_configured)
return;
if (link_has_carrier(link) || !link->network->configure_without_carrier) { if (link_has_carrier(link) || !link->network->configure_without_carrier) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address) if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
@ -2580,6 +2584,28 @@ static int link_drop_config(Link *link) {
return 0; return 0;
} }
static int link_configure_qdiscs(Link *link) {
QDiscs *qdisc;
Iterator i;
int r;
link->qdiscs_configured = false;
link->qdisc_messages = 0;
ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
r = qdisc_configure(link, qdisc);
if (r < 0)
return r;
}
if (link->qdisc_messages == 0)
link->qdiscs_configured = true;
else
log_link_debug(link, "Configuring QDiscs");
return 0;
}
static int link_configure(Link *link) { static int link_configure(Link *link) {
int r; int r;
@ -2587,6 +2613,10 @@ static int link_configure(Link *link) {
assert(link->network); assert(link->network);
assert(link->state == LINK_STATE_INITIALIZED); assert(link->state == LINK_STATE_INITIALIZED);
r = link_configure_qdiscs(link);
if (r < 0)
return r;
if (link->iftype == ARPHRD_CAN) if (link->iftype == ARPHRD_CAN)
return link_configure_can(link); return link_configure_can(link);

View File

@ -78,6 +78,7 @@ typedef struct Link {
unsigned nexthop_messages; unsigned nexthop_messages;
unsigned routing_policy_rule_messages; unsigned routing_policy_rule_messages;
unsigned routing_policy_rule_remove_messages; unsigned routing_policy_rule_remove_messages;
unsigned qdisc_messages;
unsigned enslaving; unsigned enslaving;
Set *addresses; Set *addresses;
@ -113,6 +114,7 @@ typedef struct Link {
bool static_routes_ready:1; bool static_routes_ready:1;
bool static_nexthops_configured:1; bool static_nexthops_configured:1;
bool routing_policy_rules_configured:1; bool routing_policy_rules_configured:1;
bool qdiscs_configured:1;
bool setting_mtu:1; bool setting_mtu:1;
LIST_HEAD(Address, pool_addresses); LIST_HEAD(Address, pool_addresses);

View File

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

View File

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

View File

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

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

View File

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

View File

@ -0,0 +1,20 @@
[Match]
Name=dummy98
[Network]
IPv6AcceptRA=no
Address=10.1.2.3/16
[TrafficControlQueueingDiscipline]
Parent=root
NetworkEmulatorDelaySec=50ms
NetworkEmulatorDelayJitterSec=10ms
NetworkEmulatorLossRate=20%
NetworkEmulatorPacketLimit=100
[TrafficControlQueueingDiscipline]
Parent=clsact
NetworkEmulatorDelaySec=100ms
NetworkEmulatorDelayJitterSec=13ms
NetworkEmulatorLossRate=20.5%
NetworkEmulatorPacketLimit=200

View File

@ -1485,15 +1485,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
'25-gre-tunnel-remote-any.netdev', '25-gre-tunnel-remote-any.netdev',
'25-ip6gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel-remote-any.netdev',
'25-ipv6-address-label-section.network', '25-ipv6-address-label-section.network',
'25-link-local-addressing-no.network',
'25-link-local-addressing-yes.network',
'25-link-section-unmanaged.network',
'25-neighbor-section.network', '25-neighbor-section.network',
'25-neighbor-next.network', '25-neighbor-next.network',
'25-neighbor-ipv6.network', '25-neighbor-ipv6.network',
'25-neighbor-ip-dummy.network', '25-neighbor-ip-dummy.network',
'25-neighbor-ip.network', '25-neighbor-ip.network',
'25-nexthop.network', '25-nexthop.network',
'25-link-local-addressing-no.network', '25-qdisc.network',
'25-link-local-addressing-yes.network',
'25-link-section-unmanaged.network',
'25-route-ipv6-src.network', '25-route-ipv6-src.network',
'25-route-static.network', '25-route-static.network',
'25-gateway-static.network', '25-gateway-static.network',
@ -2051,6 +2052,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertRegex(output, '192.168.5.1') self.assertRegex(output, '192.168.5.1')
def test_qdisc(self):
copy_unit_to_networkd_unit_path('25-qdisc.network', '12-dummy.netdev')
start_networkd()
self.wait_online(['dummy98:routable'])
output = check_output('tc qdisc show dev dummy98')
print(output)
self.assertRegex(output, 'limit 100 delay 50.0ms 10.0ms loss 20%')
self.assertRegex(output, 'limit 200 delay 100.0ms 13.0ms loss 20.5%')
class NetworkdStateFileTests(unittest.TestCase, Utilities): class NetworkdStateFileTests(unittest.TestCase, Utilities):
links = [ links = [
'dummy98', 'dummy98',