Compare commits
8 Commits
644ee25461
...
a346aa7c38
Author | SHA1 | Date |
---|---|---|
Yu Watanabe | a346aa7c38 | |
Yu Watanabe | afa1a54eb5 | |
Yu Watanabe | ef3c8a92b7 | |
Yu Watanabe | 4ecdcb07c9 | |
Susant Sahani | 0f5bd7fe24 | |
Zbigniew Jędrzejewski-Szmek | 54166ceece | |
Zbigniew Jędrzejewski-Szmek | 337b733449 | |
Zbigniew Jędrzejewski-Szmek | c82a2b5b84 |
|
@ -2287,6 +2287,56 @@
|
|||
</variablelist>
|
||||
</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>
|
||||
<title>[BridgeVLAN] Section Options</title>
|
||||
<para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts
|
||||
|
|
|
@ -516,7 +516,6 @@
|
|||
type of unit:</para>
|
||||
|
||||
<variablelist class='unit-directives'>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Description=</varname></term>
|
||||
<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>
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RebootArgument=</varname></term>
|
||||
<listitem><para>Configure the optional argument for the
|
||||
|
@ -999,361 +997,6 @@
|
|||
works just like the optional argument to <command>systemctl reboot</command> command.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionArchitecture=</varname></term>
|
||||
<term><varname>ConditionVirtualization=</varname></term>
|
||||
<term><varname>ConditionHost=</varname></term>
|
||||
<term><varname>ConditionKernelCommandLine=</varname></term>
|
||||
<term><varname>ConditionKernelVersion=</varname></term>
|
||||
<term><varname>ConditionSecurity=</varname></term>
|
||||
<term><varname>ConditionCapability=</varname></term>
|
||||
<term><varname>ConditionACPower=</varname></term>
|
||||
<term><varname>ConditionNeedsUpdate=</varname></term>
|
||||
<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>
|
||||
|
||||
<!-- We do not document ConditionNull= here, as it is not particularly useful and probably just
|
||||
confusing. -->
|
||||
|
||||
<listitem><para>Before starting a unit, verify that the specified condition is true. If it is not true, the
|
||||
starting of the unit will be (mostly silently) skipped, however all ordering dependencies of it are still
|
||||
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
|
||||
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-64</literal>,
|
||||
<literal>ppc</literal>,
|
||||
<literal>ppc-le</literal>,
|
||||
<literal>ppc64</literal>,
|
||||
<literal>ppc64-le</literal>,
|
||||
<literal>ia64</literal>,
|
||||
<literal>parisc</literal>,
|
||||
<literal>parisc64</literal>,
|
||||
<literal>s390</literal>,
|
||||
<literal>s390x</literal>,
|
||||
<literal>sparc</literal>,
|
||||
<literal>sparc64</literal>,
|
||||
<literal>mips</literal>,
|
||||
<literal>mips-le</literal>,
|
||||
<literal>mips64</literal>,
|
||||
<literal>mips64-le</literal>,
|
||||
<literal>alpha</literal>,
|
||||
<literal>arm</literal>,
|
||||
<literal>arm-be</literal>,
|
||||
<literal>arm64</literal>,
|
||||
<literal>arm64-be</literal>,
|
||||
<literal>sh</literal>,
|
||||
<literal>sh64</literal>,
|
||||
<literal>m68k</literal>,
|
||||
<literal>tilegx</literal>,
|
||||
<literal>cris</literal>,
|
||||
<literal>arc</literal>,
|
||||
<literal>arc-be</literal> to test
|
||||
against a specific architecture. The architecture is
|
||||
determined from the information returned by
|
||||
<citerefentry project='man-pages'><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
and is thus subject to
|
||||
<citerefentry><refentrytitle>personality</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
|
||||
Note that a <varname>Personality=</varname> setting in the
|
||||
same unit file has no effect on this condition. A special
|
||||
architecture name <literal>native</literal> is mapped to the
|
||||
architecture the system manager itself is compiled for. The
|
||||
test may be negated by prepending an exclamation mark.</para>
|
||||
|
||||
<para><varname>ConditionVirtualization=</varname> may be used
|
||||
to check whether the system is executed in a virtualized
|
||||
environment and optionally 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>container</literal> to test against a generic type of
|
||||
virtualization solution, or one of
|
||||
<literal>qemu</literal>,
|
||||
<literal>kvm</literal>,
|
||||
<literal>zvm</literal>,
|
||||
<literal>vmware</literal>,
|
||||
<literal>microsoft</literal>,
|
||||
<literal>oracle</literal>,
|
||||
<literal>xen</literal>,
|
||||
<literal>bochs</literal>,
|
||||
<literal>uml</literal>,
|
||||
<literal>bhyve</literal>,
|
||||
<literal>qnx</literal>,
|
||||
<literal>openvz</literal>,
|
||||
<literal>lxc</literal>,
|
||||
<literal>lxc-libvirt</literal>,
|
||||
<literal>systemd-nspawn</literal>,
|
||||
<literal>docker</literal>,
|
||||
<literal>podman</literal>,
|
||||
<literal>rkt</literal>,
|
||||
<literal>wsl</literal>,
|
||||
<literal>acrn</literal> to test
|
||||
against a specific implementation, or
|
||||
<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>
|
||||
for a full list of known virtualization technologies and their
|
||||
identifiers. If multiple virtualization technologies are
|
||||
nested, only the innermost is considered. The test may be
|
||||
negated by prepending an exclamation mark.</para>
|
||||
|
||||
<para><varname>ConditionHost=</varname> may be used to match
|
||||
against the hostname or machine ID of the host. This either
|
||||
takes a hostname string (optionally with shell style globs)
|
||||
which is tested against the locally set hostname as returned
|
||||
by
|
||||
<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>).
|
||||
The test may be negated by prepending an exclamation
|
||||
mark.</para>
|
||||
|
||||
<para><varname>ConditionKernelCommandLine=</varname> may be
|
||||
used to check whether a specific kernel command line option is
|
||||
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
|
||||
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><</literal>,
|
||||
<literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
|
||||
<literal>></literal> a relative version comparison is done, otherwise the specified string is
|
||||
matched with shell-style globs.</para>
|
||||
|
||||
<para>Note that using the kernel version string is an unreliable way to determine which features are supported
|
||||
by a kernel, because of the widespread practice of backporting drivers, features, and fixes from newer upstream
|
||||
kernels into older versions provided by distributions. Hence, this check is inherently unportable and should
|
||||
not be used for units which may be used on different distributions.</para>
|
||||
|
||||
<para><varname>ConditionSecurity=</varname> may be used to check
|
||||
whether the given security technology is enabled on the
|
||||
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
|
||||
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>
|
||||
for details). Pass a capability name such as
|
||||
<literal>CAP_MKNOD</literal>, possibly prefixed with an
|
||||
exclamation mark to negate the check.</para>
|
||||
|
||||
<para><varname>ConditionACPower=</varname> may be used to
|
||||
check whether the system has AC power, or is exclusively
|
||||
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
|
||||
<filename>/var</filename> or <filename>/etc</filename> as
|
||||
argument, possibly prefixed with a <literal>!</literal> (for
|
||||
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>,
|
||||
to make sure they run before the stamp file's modification
|
||||
time gets reset indicating a completed update.</para>
|
||||
|
||||
<para><varname>ConditionFirstBoot=</varname> takes a boolean argument. This condition may be used to
|
||||
conditionalize units on whether the system is booting up with an unpopulated <filename>/etc</filename>
|
||||
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
|
||||
existence condition is checked before a unit is started. 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>
|
||||
|
||||
<para><varname>ConditionPathExistsGlob=</varname> is similar
|
||||
to <varname>ConditionPathExists=</varname>, but checks for the
|
||||
existence of at least one file or directory matching the
|
||||
specified globbing pattern.</para>
|
||||
|
||||
<para><varname>ConditionPathIsDirectory=</varname> is similar
|
||||
to <varname>ConditionPathExists=</varname> but verifies
|
||||
whether a certain path exists and is a directory.</para>
|
||||
|
||||
<para><varname>ConditionPathIsSymbolicLink=</varname> is
|
||||
similar to <varname>ConditionPathExists=</varname> but
|
||||
verifies whether a certain path exists and is a symbolic
|
||||
link.</para>
|
||||
|
||||
<para><varname>ConditionPathIsMountPoint=</varname> is similar
|
||||
to <varname>ConditionPathExists=</varname> but verifies
|
||||
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>
|
||||
|
||||
<para><varname>ConditionFileNotEmpty=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname> but verifies whether a
|
||||
certain path exists and refers to a regular file with a
|
||||
non-zero size.</para>
|
||||
|
||||
<para><varname>ConditionFileIsExecutable=</varname> is similar
|
||||
to <varname>ConditionPathExists=</varname> but verifies
|
||||
whether a certain path exists, is a regular file and marked
|
||||
executable.</para>
|
||||
|
||||
<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>
|
||||
|
||||
<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 have a special value <literal>@system</literal>.</para>
|
||||
|
||||
<para><varname>ConditionControlGroupController=</varname> takes a
|
||||
cgroup controller name (eg. <literal>cpu</literal>), verifying that it 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>
|
||||
|
||||
<para><varname>ConditionMemory=</varname> verifies if 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><</literal>, <literal><=</literal>, <literal>=</literal>,
|
||||
<literal>!=</literal>, <literal>>=</literal>, <literal>></literal>. On bare-metal systems
|
||||
compares the amount of 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>
|
||||
|
||||
<para><varname>ConditionCPUs=</varname> verifies if 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><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></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
|
||||
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
|
||||
the container and not the physically available ones.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>AssertArchitecture=</varname></term>
|
||||
<term><varname>AssertVirtualization=</varname></term>
|
||||
<term><varname>AssertHost=</varname></term>
|
||||
<term><varname>AssertKernelCommandLine=</varname></term>
|
||||
<term><varname>AssertKernelVersion=</varname></term>
|
||||
<term><varname>AssertSecurity=</varname></term>
|
||||
<term><varname>AssertCapability=</varname></term>
|
||||
<term><varname>AssertACPower=</varname></term>
|
||||
<term><varname>AssertNeedsUpdate=</varname></term>
|
||||
<term><varname>AssertFirstBoot=</varname></term>
|
||||
<term><varname>AssertPathExists=</varname></term>
|
||||
<term><varname>AssertPathExistsGlob=</varname></term>
|
||||
<term><varname>AssertPathIsDirectory=</varname></term>
|
||||
<term><varname>AssertPathIsSymbolicLink=</varname></term>
|
||||
<term><varname>AssertPathIsMountPoint=</varname></term>
|
||||
<term><varname>AssertPathIsReadWrite=</varname></term>
|
||||
<term><varname>AssertDirectoryNotEmpty=</varname></term>
|
||||
<term><varname>AssertFileNotEmpty=</varname></term>
|
||||
<term><varname>AssertFileIsExecutable=</varname></term>
|
||||
<term><varname>AssertUser=</varname></term>
|
||||
<term><varname>AssertGroup=</varname></term>
|
||||
<term><varname>AssertControlGroupController=</varname></term>
|
||||
|
||||
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
|
||||
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings add
|
||||
assertion checks to the start-up of the unit. However, unlike the conditions settings, any assertion setting
|
||||
that is not met results in failure of the start job (which means this is logged loudly). Note that hitting a
|
||||
configured assertion does not cause the unit to enter the <literal>failed</literal> state (or in fact result in
|
||||
any state change of the unit), it affects only the job queued for it. Use assertion expressions for units that
|
||||
cannot operate when specific 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
|
||||
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
|
||||
|
@ -1364,6 +1007,423 @@
|
|||
units.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<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
|
||||
confusing. -->
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionArchitecture=</varname></term>
|
||||
|
||||
<listitem><para>Check whether the system is running on a specific architecture. Takes one of
|
||||
<literal>x86</literal>,
|
||||
<literal>x86-64</literal>,
|
||||
<literal>ppc</literal>,
|
||||
<literal>ppc-le</literal>,
|
||||
<literal>ppc64</literal>,
|
||||
<literal>ppc64-le</literal>,
|
||||
<literal>ia64</literal>,
|
||||
<literal>parisc</literal>,
|
||||
<literal>parisc64</literal>,
|
||||
<literal>s390</literal>,
|
||||
<literal>s390x</literal>,
|
||||
<literal>sparc</literal>,
|
||||
<literal>sparc64</literal>,
|
||||
<literal>mips</literal>,
|
||||
<literal>mips-le</literal>,
|
||||
<literal>mips64</literal>,
|
||||
<literal>mips64-le</literal>,
|
||||
<literal>alpha</literal>,
|
||||
<literal>arm</literal>,
|
||||
<literal>arm-be</literal>,
|
||||
<literal>arm64</literal>,
|
||||
<literal>arm64-be</literal>,
|
||||
<literal>sh</literal>,
|
||||
<literal>sh64</literal>,
|
||||
<literal>m68k</literal>,
|
||||
<literal>tilegx</literal>,
|
||||
<literal>cris</literal>,
|
||||
<literal>arc</literal>,
|
||||
<literal>arc-be</literal>, or
|
||||
<literal>native</literal>.</para>
|
||||
|
||||
<para>The architecture is determined from the information returned by
|
||||
<citerefentry project='man-pages'><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>
|
||||
and is thus subject to
|
||||
<citerefentry><refentrytitle>personality</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
|
||||
Note that a <varname>Personality=</varname> setting in the same unit file has no effect on this
|
||||
condition. A special architecture name <literal>native</literal> is mapped to the architecture the
|
||||
system manager itself is compiled for. The test may be negated by prepending an exclamation
|
||||
mark.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionArchitecture=</varname></term>
|
||||
|
||||
<listitem><para>Check whether the system is executed in a virtualized environment and optionally
|
||||
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>container</literal> to test against a generic type of virtualization solution, or one of
|
||||
<literal>qemu</literal>,
|
||||
<literal>kvm</literal>,
|
||||
<literal>zvm</literal>,
|
||||
<literal>vmware</literal>,
|
||||
<literal>microsoft</literal>,
|
||||
<literal>oracle</literal>,
|
||||
<literal>xen</literal>,
|
||||
<literal>bochs</literal>,
|
||||
<literal>uml</literal>,
|
||||
<literal>bhyve</literal>,
|
||||
<literal>qnx</literal>,
|
||||
<literal>openvz</literal>,
|
||||
<literal>lxc</literal>,
|
||||
<literal>lxc-libvirt</literal>,
|
||||
<literal>systemd-nspawn</literal>,
|
||||
<literal>docker</literal>,
|
||||
<literal>podman</literal>,
|
||||
<literal>rkt</literal>,
|
||||
<literal>wsl</literal>,
|
||||
<literal>acrn</literal> to test
|
||||
against a specific implementation, or
|
||||
<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>
|
||||
for a full list of known virtualization technologies and their identifiers. If multiple
|
||||
virtualization technologies are nested, only the innermost is considered. The test may be negated
|
||||
by prepending an exclamation mark.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionHost=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionHost=</varname> may be used to match against the hostname or
|
||||
machine ID of the host. This either takes a hostname string (optionally with shell style globs)
|
||||
which is tested against the locally set hostname as returned by
|
||||
<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>).
|
||||
The test may be negated by prepending an exclamation mark.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionKernelCommandLine=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionKernelCommandLine=</varname> may be used to check whether a
|
||||
specific kernel command line option is 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 by
|
||||
<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><</literal>,
|
||||
<literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
|
||||
<literal>></literal> a relative version comparison is done, otherwise the specified string is
|
||||
matched with shell-style globs.</para>
|
||||
|
||||
<para>Note that using the kernel version string is an unreliable way to determine which features
|
||||
are supported by a kernel, because of the widespread practice of backporting drivers, features, and
|
||||
fixes from newer upstream kernels into older versions provided by distributions. Hence, this check
|
||||
is inherently unportable and should not be used for units which may be used on different
|
||||
distributions.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionSecurity=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionSecurity=</varname> may be used to check whether the given
|
||||
security technology is enabled on the 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>
|
||||
</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>
|
||||
for details). Pass a capability name such as <literal>CAP_MKNOD</literal>, possibly prefixed with
|
||||
an exclamation mark to negate the check.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionACPower=</varname></term>
|
||||
|
||||
<listitem><para>Check whether the system has AC power, or is exclusively 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>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionNeedsUpdate=</varname></term>
|
||||
|
||||
<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>,
|
||||
to make sure they run before the stamp file's modification time gets reset indicating a completed
|
||||
update.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionFirstBoot=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. This condition may be used to conditionalize units on
|
||||
whether the system is booting up with an unpopulated <filename>/etc</filename> 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>
|
||||
</listitem>
|
||||
</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>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionPathExistsGlob=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionPathExistsGlob=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname>, but checks for the existence of at least one file or
|
||||
directory matching the specified globbing pattern.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionPathIsDirectory=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionPathIsDirectory=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a
|
||||
directory.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionPathIsSymbolicLink=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionPathIsSymbolicLink=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a symbolic
|
||||
link.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionPathIsMountPoint=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionPathIsMountPoint=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname> but verifies that a certain path exists and is a mount
|
||||
point.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionPathIsReadWrite=</varname></term>
|
||||
|
||||
<listitem><para><varname>ConditionPathIsReadWrite=</varname> is similar to
|
||||
<varname>ConditionPathExists=</varname> but verifies that the underlying file system is readable
|
||||
and writable (i.e. not mounted read-only).</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<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><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></literal>. On bare-metal systems compares the amount of
|
||||
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><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||
<literal>>=</literal>, <literal>></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
|
||||
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 the container and not the physically available ones.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>AssertArchitecture=</varname></term>
|
||||
<term><varname>AssertVirtualization=</varname></term>
|
||||
<term><varname>AssertHost=</varname></term>
|
||||
<term><varname>AssertKernelCommandLine=</varname></term>
|
||||
<term><varname>AssertKernelVersion=</varname></term>
|
||||
<term><varname>AssertSecurity=</varname></term>
|
||||
<term><varname>AssertCapability=</varname></term>
|
||||
<term><varname>AssertACPower=</varname></term>
|
||||
<term><varname>AssertNeedsUpdate=</varname></term>
|
||||
<term><varname>AssertFirstBoot=</varname></term>
|
||||
<term><varname>AssertPathExists=</varname></term>
|
||||
<term><varname>AssertPathExistsGlob=</varname></term>
|
||||
<term><varname>AssertPathIsDirectory=</varname></term>
|
||||
<term><varname>AssertPathIsSymbolicLink=</varname></term>
|
||||
<term><varname>AssertPathIsMountPoint=</varname></term>
|
||||
<term><varname>AssertPathIsReadWrite=</varname></term>
|
||||
<term><varname>AssertDirectoryNotEmpty=</varname></term>
|
||||
<term><varname>AssertFileNotEmpty=</varname></term>
|
||||
<term><varname>AssertFileIsExecutable=</varname></term>
|
||||
<term><varname>AssertUser=</varname></term>
|
||||
<term><varname>AssertGroup=</varname></term>
|
||||
<term><varname>AssertControlGroupController=</varname></term>
|
||||
|
||||
<listitem><para>Similar to the <varname>ConditionArchitecture=</varname>,
|
||||
<varname>ConditionVirtualization=</varname>, …, condition settings described above, these settings
|
||||
add assertion checks to the start-up of the unit. However, unlike the conditions settings, any
|
||||
assertion setting that is not met results in failure of the start job (which means this is logged
|
||||
loudly). Note that hitting a configured assertion does not cause the unit to enter the
|
||||
<literal>failed</literal> state (or in fact result in any state change of the unit), it affects
|
||||
only the job queued for it. Use assertion expressions for units that cannot operate when specific
|
||||
requirements are not met, and when this is something the administrator or user should look
|
||||
into.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -18,9 +18,10 @@
|
|||
#include <linux/if_link.h>
|
||||
#include <linux/if_macsec.h>
|
||||
#include <linux/if_tunnel.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/l2tp.h>
|
||||
#include <linux/nexthop.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
#include <linux/veth.h>
|
||||
#include <linux/wireguard.h>
|
||||
|
||||
|
@ -733,6 +734,18 @@ static const NLTypeSystem rtnl_nexthop_type_system = {
|
|||
.types = rtnl_nexthop_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_qdisc_types[] = {
|
||||
[TCA_KIND] = { .type = NETLINK_TYPE_STRING },
|
||||
[TCA_OPTIONS] = { .size = sizeof(struct tc_netem_qopt) },
|
||||
[TCA_INGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
|
||||
[TCA_EGRESS_BLOCK] = { .type = NETLINK_TYPE_U32 },
|
||||
};
|
||||
|
||||
static const NLTypeSystem rtnl_qdisc_type_system = {
|
||||
.count = ELEMENTSOF(rtnl_qdisc_types),
|
||||
.types = rtnl_qdisc_types,
|
||||
};
|
||||
|
||||
static const NLType rtnl_types[] = {
|
||||
[NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 },
|
||||
[NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) },
|
||||
|
@ -758,6 +771,9 @@ static const NLType rtnl_types[] = {
|
|||
[RTM_NEWNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
|
||||
[RTM_DELNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
|
||||
[RTM_GETNEXTHOP] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
|
||||
[RTM_NEWQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
|
||||
[RTM_DELQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
|
||||
[RTM_GETQDISC] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
|
||||
};
|
||||
|
||||
const NLTypeSystem rtnl_type_system_root = {
|
||||
|
|
|
@ -41,6 +41,10 @@ static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) {
|
|||
return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE);
|
||||
}
|
||||
|
||||
static inline bool rtnl_message_type_is_qdisc(uint16_t type) {
|
||||
return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
|
||||
}
|
||||
|
||||
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
|
||||
int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
|
||||
|
||||
|
|
|
@ -1033,3 +1033,46 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(const sd_netlink_m
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) {
|
||||
struct tcmsg *tcm;
|
||||
int r;
|
||||
|
||||
assert_return(rtnl_message_type_is_qdisc(nlmsg_type), -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
r = message_new(rtnl, ret, nlmsg_type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (nlmsg_type == RTM_NEWQDISC)
|
||||
(*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
|
||||
|
||||
tcm = NLMSG_DATA((*ret)->hdr);
|
||||
tcm->tcm_family = tcm_family;
|
||||
tcm->tcm_ifindex = tcm_ifindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent) {
|
||||
struct tcmsg *tcm;
|
||||
|
||||
assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
tcm = NLMSG_DATA(m->hdr);
|
||||
tcm->tcm_parent = parent;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) {
|
||||
struct tcmsg *tcm;
|
||||
|
||||
assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL);
|
||||
|
||||
tcm = NLMSG_DATA(m->hdr);
|
||||
tcm->tcm_handle = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -105,6 +105,12 @@ sources = files('''
|
|||
networkd-util.h
|
||||
networkd-wifi.c
|
||||
networkd-wifi.h
|
||||
tc/netem.c
|
||||
tc/netem.h
|
||||
tc/qdisc.c
|
||||
tc/qdisc.h
|
||||
tc/tc-util.c
|
||||
tc/tc-util.h
|
||||
'''.split())
|
||||
|
||||
systemd_networkd_sources = files('networkd.c')
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "tmpfile-util.h"
|
||||
#include "udev-util.h"
|
||||
#include "util.h"
|
||||
#include "tc/qdisc.h"
|
||||
#include "virt.h"
|
||||
|
||||
uint32_t link_get_vrf_table(Link *link) {
|
||||
|
@ -1094,6 +1095,9 @@ void link_check_ready(Link *link) {
|
|||
if (!link->routing_policy_rules_configured)
|
||||
return;
|
||||
|
||||
if (!link->qdiscs_configured)
|
||||
return;
|
||||
|
||||
if (link_has_carrier(link) || !link->network->configure_without_carrier) {
|
||||
|
||||
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
|
||||
|
@ -2580,6 +2584,28 @@ static int link_drop_config(Link *link) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int link_configure_qdiscs(Link *link) {
|
||||
QDiscs *qdisc;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
link->qdiscs_configured = false;
|
||||
link->qdisc_messages = 0;
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
|
||||
r = qdisc_configure(link, qdisc);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (link->qdisc_messages == 0)
|
||||
link->qdiscs_configured = true;
|
||||
else
|
||||
log_link_debug(link, "Configuring QDiscs");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_configure(Link *link) {
|
||||
int r;
|
||||
|
||||
|
@ -2587,6 +2613,10 @@ static int link_configure(Link *link) {
|
|||
assert(link->network);
|
||||
assert(link->state == LINK_STATE_INITIALIZED);
|
||||
|
||||
r = link_configure_qdiscs(link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (link->iftype == ARPHRD_CAN)
|
||||
return link_configure_can(link);
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ typedef struct Link {
|
|||
unsigned nexthop_messages;
|
||||
unsigned routing_policy_rule_messages;
|
||||
unsigned routing_policy_rule_remove_messages;
|
||||
unsigned qdisc_messages;
|
||||
unsigned enslaving;
|
||||
|
||||
Set *addresses;
|
||||
|
@ -113,6 +114,7 @@ typedef struct Link {
|
|||
bool static_routes_ready:1;
|
||||
bool static_nexthops_configured:1;
|
||||
bool routing_policy_rules_configured:1;
|
||||
bool qdiscs_configured:1;
|
||||
bool setting_mtu:1;
|
||||
|
||||
LIST_HEAD(Address, pool_addresses);
|
||||
|
|
|
@ -13,6 +13,8 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
|
|||
#include "networkd-ndisc.h"
|
||||
#include "networkd-network.h"
|
||||
#include "vlan-util.h"
|
||||
#include "tc/qdisc.h"
|
||||
#include "tc/netem.h"
|
||||
%}
|
||||
struct ConfigPerfItem;
|
||||
%null_strings
|
||||
|
@ -241,6 +243,11 @@ CAN.BitRate, config_parse_si_size,
|
|||
CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point)
|
||||
CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us)
|
||||
CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
|
||||
TrafficControlQueueingDiscipline.Parent, config_parse_tc_qdiscs_parent, 0, 0
|
||||
TrafficControlQueueingDiscipline.NetworkEmulatorDelaySec, config_parse_tc_network_emulator_delay, 0, 0
|
||||
TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_tc_network_emulator_delay, 0, 0
|
||||
TrafficControlQueueingDiscipline.NetworkEmulatorLossRate, config_parse_tc_network_emulator_loss_rate, 0, 0
|
||||
TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit, config_parse_tc_network_emulator_packet_limit, 0, 0
|
||||
/* backwards compatibility: do not add new entries to this section */
|
||||
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
|
||||
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
|
||||
|
|
|
@ -471,6 +471,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
|||
"IPv6PrefixDelegation\0"
|
||||
"IPv6Prefix\0"
|
||||
"IPv6RoutePrefix\0"
|
||||
"TrafficControlQueueingDiscipline\0"
|
||||
"CAN\0",
|
||||
config_item_perf_lookup, network_network_gperf_lookup,
|
||||
CONFIG_PARSE_WARN, network);
|
||||
|
@ -663,6 +664,7 @@ static Network *network_free(Network *network) {
|
|||
hashmap_free(network->address_labels_by_section);
|
||||
hashmap_free(network->prefixes_by_section);
|
||||
hashmap_free(network->rules_by_section);
|
||||
ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
|
||||
|
||||
if (network->manager &&
|
||||
network->manager->duids_requesting_uuid)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "networkd-util.h"
|
||||
#include "ordered-set.h"
|
||||
#include "resolve-util.h"
|
||||
#include "tc/qdisc.h"
|
||||
|
||||
typedef enum IPv6PrivacyExtensions {
|
||||
/* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
|
||||
|
@ -265,6 +266,7 @@ struct Network {
|
|||
Hashmap *prefixes_by_section;
|
||||
Hashmap *route_prefixes_by_section;
|
||||
Hashmap *rules_by_section;
|
||||
OrderedHashmap *qdiscs_by_section;
|
||||
|
||||
/* All kinds of DNS configuration */
|
||||
struct in_addr_data *dns;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -36,7 +36,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
|
|||
switch (state) {
|
||||
case KMOD_MODULE_BUILTIN:
|
||||
log_full(verbose ? LOG_INFO : LOG_DEBUG,
|
||||
"Module '%s' is builtin", kmod_module_get_name(mod));
|
||||
"Module '%s' is built in", kmod_module_get_name(mod));
|
||||
break;
|
||||
|
||||
case KMOD_MODULE_LIVE:
|
||||
|
|
|
@ -202,6 +202,10 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_type(const sd_netlink_message *m
|
|||
int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, unsigned flags);
|
||||
int sd_rtnl_message_routing_policy_rule_get_flags(const sd_netlink_message *m, unsigned *flags);
|
||||
|
||||
int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
|
||||
int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
|
||||
int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle);
|
||||
|
||||
/* genl */
|
||||
int sd_genl_socket_open(sd_netlink **nl);
|
||||
int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m);
|
||||
|
|
|
@ -262,3 +262,9 @@ DNS=
|
|||
[NextHop]
|
||||
Id=
|
||||
Gateway=
|
||||
[TrafficControlQueueingDiscipline]
|
||||
Parent=
|
||||
NetworkEmulatorDelaySec=
|
||||
NetworkEmulatorDelayJitterSec=
|
||||
NetworkEmulatorLossRate=
|
||||
NetworkEmulatorPacketLimit=
|
||||
|
|
|
@ -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
|
|
@ -1485,15 +1485,16 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
'25-gre-tunnel-remote-any.netdev',
|
||||
'25-ip6gre-tunnel-remote-any.netdev',
|
||||
'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-next.network',
|
||||
'25-neighbor-ipv6.network',
|
||||
'25-neighbor-ip-dummy.network',
|
||||
'25-neighbor-ip.network',
|
||||
'25-nexthop.network',
|
||||
'25-link-local-addressing-no.network',
|
||||
'25-link-local-addressing-yes.network',
|
||||
'25-link-section-unmanaged.network',
|
||||
'25-qdisc.network',
|
||||
'25-route-ipv6-src.network',
|
||||
'25-route-static.network',
|
||||
'25-gateway-static.network',
|
||||
|
@ -2051,6 +2052,17 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
|
|||
print(output)
|
||||
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):
|
||||
links = [
|
||||
'dummy98',
|
||||
|
|
Loading…
Reference in New Issue