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>
|
</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
|
||||||
|
|
|
@ -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><</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><</literal>,
|
||||||
<literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
|
<literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
|
||||||
<literal>></literal> a relative version comparison is done, otherwise the specified string is
|
<literal>></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><</literal>, <literal><=</literal>, <literal>=</literal>,
|
and writable (i.e. not mounted read-only).</para>
|
||||||
<literal>!=</literal>, <literal>>=</literal>, <literal>></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><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
<literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>,
|
||||||
<literal>>=</literal>, <literal>></literal>. Compares the number of CPUs in the CPU affinity mask
|
<literal>>=</literal>, <literal>></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><</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
|
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>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 = {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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) {
|
switch (state) {
|
||||||
case KMOD_MODULE_BUILTIN:
|
case KMOD_MODULE_BUILTIN:
|
||||||
log_full(verbose ? LOG_INFO : LOG_DEBUG,
|
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;
|
break;
|
||||||
|
|
||||||
case KMOD_MODULE_LIVE:
|
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_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);
|
||||||
|
|
|
@ -262,3 +262,9 @@ DNS=
|
||||||
[NextHop]
|
[NextHop]
|
||||||
Id=
|
Id=
|
||||||
Gateway=
|
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-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',
|
||||||
|
|
Loading…
Reference in New Issue