Compare commits

...

10 Commits

Author SHA1 Message Date
Adrian Vovk 9b5fbfe78f
Merge 3e2b6fd389 into 66d044b560 2024-11-06 17:03:13 +01:00
Luca Boccassi 66d044b560 Update NEWS for recent PRs 2024-11-06 15:50:59 +00:00
Michele Dionisio d865abf9eb networkd: add possibility to specify MulticastIGMPVersion 2024-11-06 15:50:27 +00:00
Luca Boccassi f72fe2d73c
Grammar and formatting for DeviceTree docs (#35050) 2024-11-06 15:13:18 +00:00
Zbigniew Jędrzejewski-Szmek 78ed1e973c docs/TPM2_PCR_MEASUREMENTS: drop quotes from around section titles
The section headers used quotes as if the strings were some constants. But
AFAICT, those are just normal plain-text titles. Also lowercase them, because
this is almost like a table and it's easier to read without capitalization.
2024-11-06 15:02:24 +01:00
Zbigniew Jędrzejewski-Szmek 265488414c tree-wide: use Device*T*ree spelling
We used both, in fact "Devicetree" was more common. But we have a general rule
that we capitalize all words in names and also we have a DeviceTree=
configuration setting, which we cannot change. If we use two different
spelllings, this will make it harder for people to use the correct one in
config files. So use the "DeviceTree" spelling everywhere.
2024-11-06 15:00:55 +01:00
Zbigniew Jędrzejewski-Szmek bc11463e8e man/systemd-stub: rework the description of sections
The text added for .dtbauto/.hwids was very hard to grok. This rewords it to be
proper English. No semantic changes are intended.

When updating this, I noticed that the interaction of multi-profile UKIs and
dtb autoselection is very unclear, a FIXME is added.
2024-11-06 14:40:21 +01:00
Adrian Vovk 3e2b6fd389
sysupdate: Add --stream flag for major version bumps
Basically, distros that maintain more than one release stream (i.e.
multiple stable versions, a beta channel, etc) can put the others'
transfer definitions into /usr/lib/sysupdate@$STREAM.d/, and the admin
can pass --stream= on the CLI to switch to this new stream.

Part of https://github.com/systemd/systemd/issues/33345

SQUASHME: s/--next/--streams=<stream>/
2024-10-30 22:00:30 -04:00
Adrian Vovk 6f6d29290e
Warn admins about danger of overriding sysupdate.d
Overriding settings of systemd-sysupdate is quite dangerous - OS vendor
might make a change that is incompatible with the settings in /etc, and
unlike most other config incompatibilities this has the potential to
accidentally wipe the wrong partition during an update. Not good. So
let's warn admins.
2024-10-30 21:54:49 -04:00
Adrian Vovk 19bac76b1a
sysupdate: Document components more visibly
Right now components aren't particularly well documented. Let's make
the feature a bit more visible.
2024-10-30 21:54:48 -04:00
22 changed files with 439 additions and 94 deletions

35
NEWS
View File

@ -186,7 +186,7 @@ CHANGES WITH 257 in spe:
* The systemd.machine_id= kernel command line parameter interpreted by
PID 1 now supports an additional special value: if "firmware" is
specified the machine ID is initialized from the SMBIOS/Devicetree
specified the machine ID is initialized from the SMBIOS/DeviceTree
system UUID. (Previously this was already done in VM environments,
this extends the concept to any system, but only on explicit request
via this option.)
@ -276,10 +276,10 @@ CHANGES WITH 257 in spe:
show up as .device units in systemd.
* The firmware_node/sun sysfs attribute will now be used (if available)
for naming slot-based network interfaces,
i.e. ID_NET_NAME_SLOT. Moreover the interface aliases specified in
Devicetree are now searched for both on the interfaces parent device
(as before) and the device itself (new).
for naming slot-based network interfaces, i.e. ID_NET_NAME_SLOT.
Moreover the interface aliases specified in DeviceTree are now
searched for both on the interface's parent device (as before) and
the device itself (new).
* Various USB hardware wallets have are now recognized by udev via a
.hwdb file, and get the ID_HARDWARE_WALLET= property set, which
@ -384,6 +384,16 @@ CHANGES WITH 257 in spe:
reset one, and so on which only differ in kernel command line, but
nothing else).
* New .dtbauto and .hwids sections are now documented and supported in
systemd-measure, ukify, systemd-stub, and systemd-boot. A single UKI
can contain multiple .dtbauto sections, and the 'compatible' string
therein will be compared with the equivalent field in the DTB
provided by the firmware, if present. If absent, SMBIOS will be used
to calculate hardware IDs and compare them with the content of
.hwids. This allows including multiple DTBs in a single UKI, with
the bootloader automatically selecting the correct one for the
current hardware.
* ukify gained an --extend switch to import an existing UKI to
be extended, and a --measure-base= switch to support measurement
of multi-profile UKIs.
@ -646,6 +656,9 @@ CHANGES WITH 257 in spe:
* systemd-tmpfiles --purge switch now requires specification of at
least one tmpfiles.d/ drop-in file.
* tmpfiles.d gained a new '?' specifier for the 'L' type to create a
symlink only if the source exists, and gracefully skip otherwise.
* The new Linux mseal(), listmount(), statmount() syscalls have been
added to relevant system call groups.
@ -2012,7 +2025,7 @@ CHANGES WITH 255:
respective SBAT sections, so that they can be revoked individually if
needed.
* systemd-boot will no longer load unverified Devicetree blobs when UEFI
* systemd-boot will no longer load unverified DeviceTree blobs when UEFI
SecureBoot is enabled. For more details see:
https://github.com/systemd/systemd/security/advisories/GHSA-6m6p-rjcq-334c
@ -2033,7 +2046,7 @@ CHANGES WITH 255:
command-line addons before measuring them in TPM2 PCR 12, in a single
measurement, instead of measuring them individually.
* systemd-stub will now measure and load Devicetree Blob addons, which
* systemd-stub will now measure and load DeviceTree Blob addons, which
are searched and loaded following the same model as the existing
kernel command-line addons.
@ -2041,7 +2054,7 @@ CHANGES WITH 255:
passed from systemd-boot when running inside Confidential VMs with UEFI
SecureBoot enabled.
* systemd-stub will now load a Devicetree blob even if the firmware did
* systemd-stub will now load a DeviceTree blob even if the firmware did
not load any beforehand (e.g.: for ACPI systems).
* ukify is no longer considered experimental, and now ships in /usr/bin/.
@ -2222,6 +2235,10 @@ CHANGES WITH 255:
specific devices explicitly. NetworkManager will soon implement a
similar logic.
* .network files gained a new MulticastIGMPVersion= setting in the
[Network] section, to control sysctl's
/proc/sys/net/ipv4/conf/INTERFACE/force_igmp_version setting.
systemctl:
* systemctl is-failed now checks the system state if no unit is
@ -4355,7 +4372,7 @@ CHANGES WITH 252 🎃:
* 'udevadm wait' will now listen to kernel uevents too when called with
--initialized=no.
* When naming network devices udev will now consult the Devicetree
* When naming network devices udev will now consult the DeviceTree
"alias" fields for the device.
* systemd-udev will now create infiniband/by-path and

View File

@ -41,7 +41,7 @@ used for new, additional measurements.
## PCR Measurements Made by `systemd-boot` (UEFI)
### PCS 5, `EV_EVENT_TAG`, "loader.conf"
### PCS 5, `EV_EVENT_TAG`, `loader.conf`
The content of `systemd-boot`'s configuration file, `loader/loader.conf`, is
measured as a tagged event.
@ -52,7 +52,7 @@ measured as a tagged event.
**Measured hash** covers the content of `loader.conf` as it is read from the ESP.
### PCR 12, `EV_IPL`, "Kernel Command Line"
### PCR 12, `EV_IPL`, kernel command line
If the kernel command line was specified explicitly (by the user or in a Boot
Loader Specification Type #1 file), the kernel command line passed to the
@ -70,7 +70,7 @@ trailing NUL bytes).
## PCR Measurements Made by `systemd-stub` (UEFI)
### PCR 11, `EV_IPL`, "PE Section Name"
### PCR 11, `EV_IPL`, PE section name
A measurement is made for each PE section of the UKI that is defined by the
[UKI
@ -87,7 +87,7 @@ both types of records appear interleaved in the event log.
**Measured hash** covers the PE section name in ASCII (*including* a trailing NUL byte!).
### PCR 11, `EV_IPL`, "PE Section Data"
### PCR 11, `EV_IPL`, PE section data
Happens once for each UKI-defined PE section of the UKI, in the canonical UKI
PE section order, as per the UKI specification, see above.
@ -96,7 +96,7 @@ PE section order, as per the UKI specification, see above.
**Measured hash** covers the (binary) PE section contents.
### PCR 12, `EV_IPL`, "Kernel Command Line"
### PCR 12, `EV_IPL`, kernel command line
Might happen up to three times, for kernel command lines from:
@ -110,37 +110,37 @@ UTF-16.
**Measured hash** covers the literal kernel command line in UTF-16 (without any
trailing NUL bytes).
### PCR 12, `EV_EVENT_TAG`, "Devicetrees"
### PCR 12, `EV_EVENT_TAG`, DeviceTrees
Devicetree addons are measured individually as a tagged event.
DeviceTree addons are measured individually as a tagged event.
**Event Tag** `0x6c46f751`
**Description** the addon filename.
**Description** is the addon filename.
**Measured hash** covers the content of the Devicetree.
**Measured hash** covers the content of the DeviceTree.
### PCR 12, `EV_EVENT_TAG`, "Initrd addons"
### PCR 12, `EV_EVENT_TAG`, initrd addons
Initrd addons are measured individually as a tagged event.
**Event Tag** `0x49dffe0f`
**Description** the addon filename.
**Description** is the addon filename.
**Measured hash** covers the contents of the initrd.
### PCR 12, `EV_EVENT_TAG`, "Ucode addons"
### PCR 12, `EV_EVENT_TAG`, ucode addons
Ucode addons are measured individually as a tagged event.
**Event Tag** `0xdac08e1a`
**Description** the addon filename.
**Description** is the addon filename.
**Measured hash** covers the contents of the ucode initrd.
### PCR 12, `EV_IPL`, "Per-UKI Credentials initrd"
### PCR 12, `EV_IPL`, per-uki credentials initrd
**Description** in the event log record is the constant string "Credentials
initrd" in UTF-16.
@ -148,7 +148,7 @@ initrd" in UTF-16.
**Measured hash** covers the per-UKI credentials cpio archive (which is generated
on-the-fly by `systemd-stub`).
### PCR 12, `EV_IPL`, "Global Credentials initrd"
### PCR 12, `EV_IPL`, global credentials initrd
**Description** in the event log record is the constant string "Global
credentials initrd" in UTF-16.
@ -156,7 +156,7 @@ credentials initrd" in UTF-16.
**Measured hash** covers the global credentials cpio archive (which is generated
on-the-fly by `systemd-stub`).
### PCR 13, `EV_IPL`, "sysext initrd"
### PCR 13, `EV_IPL`, sysext initrd
**Description** in the event log record is the constant string "System extension
initrd" in UTF-16.
@ -166,7 +166,7 @@ on-the-fly by `systemd-stub`).
## PCR Measurements Made by `systemd-pcrextend` (Userspace)
### PCR 11, "Boot Phases"
### PCR 11, boot phases
The `systemd-pcrphase.service`, `systemd-pcrphase-initrd.service`,
`systemd-pcrphase-sysinit.service` services will measure the boot phase reached
@ -178,7 +178,7 @@ choose to define additional/different phases.)
**Measured hash** covers the phase string (in UTF-8, without trailing NUL
bytes).
### PCR 15, "Machine ID"
### PCR 15, machine ID
The `systemd-pcrmachine.service` service will measure the machine ID (as read
from `/etc/machine-id`) during boot.
@ -187,7 +187,7 @@ from `/etc/machine-id`) during boot.
formatted in hexadecimal lowercase characters (in UTF-8, without trailing NUL
bytes).
### PCR 15, "File System"
### PCR 15, file system
The `systemd-pcrfs-root.service` and `systemd-pcrfs@.service` services will
measure a string identifying a specific file system, typically covering the
@ -200,7 +200,7 @@ without trailing NUL bytes).
## PCR Measurements Made by `systemd-cryptsetup` (Userspace)
### PCR 15, "Volume Key"
### PCR 15, volume key
The `systemd-cryptsetup@.service` service will measure a key derived from the
LUKS volume key of a specific encrypted volume, typically covering the backing

View File

@ -59,58 +59,66 @@
<!-- Let's keep this in the canonical order we also measure the sections by, i.e. as in
src/fundamental/uki.h's UnifiedSection enum -->
<listitem><para>A <literal>.linux</literal> section with the ELF Linux kernel
image. (Required)</para></listitem>
<listitem><para>A <literal>.linux</literal> section with the ELF Linux kernel image.
This section is required.</para></listitem>
<listitem><para>An <literal>.osrel</literal> section with OS release information, i.e. the contents of
the <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file
of the OS the kernel belongs to.</para></listitem>
<listitem><para>An optional <literal>.osrel</literal> section with OS release information, i.e. the
contents of the
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file of
the OS the kernel belongs to.</para></listitem>
<listitem><para>A <literal>.cmdline</literal> section with the kernel command line to pass to the
invoked kernel.</para></listitem>
<listitem><para>An optional <literal>.cmdline</literal> section with the kernel command line to pass to
the invoked kernel.</para></listitem>
<listitem><para>An <literal>.initrd</literal> section with the initrd.</para></listitem>
<listitem><para>An optional <literal>.initrd</literal> section with the initrd.</para></listitem>
<listitem><para>A <literal>.ucode</literal> section with an initrd containing microcode, to be handed
to the kernel before any other initrd. This initrd must not be compressed.</para></listitem>
<listitem><para>An optional <literal>.ucode</literal> section with an initrd containing microcode, to
be handed to the kernel before any other initrd. This initrd must not be compressed.</para></listitem>
<listitem><para>A <literal>.splash</literal> section with an image (in the Windows
<listitem><para>An optional <literal>.splash</literal> section with an image (in the Windows
<filename>.BMP</filename> format) to show on screen before invoking the kernel.</para></listitem>
<listitem><para>A <literal>.dtb</literal> section with a compiled binary DeviceTree.</para></listitem>
<listitem><para>An optional <literal>.dtb</literal> section with a compiled binary DeviceTree.
</para></listitem>
<listitem><para>Zero or more <literal>.dtbauto</literal> sections. Stub will always try to find first matching one.
Matching process extracts first <varname>compatible</varname> string from <literal>.dtbauto</literal>
section and compares it with the first Devicetree's <varname>compatible</varname> string supplied by
the firmware in configuration tables. If firmware does not provide Devicetree, matching with
<varname>.hwids</varname> section will be used instead. Stub will use SMBIOS data to calculate hardware
IDs of the machine (as per <ulink url="https://learn.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer">specification</ulink>),
then it will proceed to trying to find any of them in <literal>.hwids</literal> section and will use first
matching entry's <varname>compatible</varname> as a search key among the <literal>.dtbauto</literal>
entries, in a similar fashion as the use of <varname>compatible</varname> string read from the firmware
provided Devicetree was described before. First matching <literal>.dtbauto</literal> section will be
<listitem><para>Zero or more <literal>.dtbauto</literal> sections. <filename>systemd-stub</filename>
will always use the first matching one. The match is performed by taking the first DeviceTree's
<varname>compatible</varname> string supplied by the firmware in configuration tables and comparing it
with the first <varname>compatible</varname> string from each of the <literal>.dtbauto</literal>
sections. If the firmware does not provide a DeviceTree, the match is done using the
<varname>.hwids</varname> section instead. After selecting a <literal>.hwids</literal> section (see the
description below), the <varname>compatible</varname> string from that section will be used to perform
the same matching procedure. If a match is found, that <literal>.dtbauto</literal> section will be
loaded and will override <varname>.dtb</varname> if present.</para></listitem>
<listitem><para>A <literal>.hwids</literal> section with hardware IDs of the machines to match Devicetrees (refer to <literal>.dtbauto</literal> section description).</para></listitem>
<listitem><para>Zero or more <literal>.hwids</literal> sections with hardware IDs of the machines to
match DeviceTrees. <filename>systemd-stub</filename> will use the SMBIOS data to calculate hardware IDs
of the machine (as per <ulink
url="https://learn.microsoft.com/en-us/windows-hardware/drivers/install/specifying-hardware-ids-for-a-computer">specification</ulink>),
and then it will try to find any of them in each of the <literal>.hwids</literal> sections. The first
matching section will be used.</para></listitem>
<listitem><para>A <literal>.uname</literal> section with the kernel version information, i.e. the
output of <command>uname -r</command> for the kernel included in the <literal>.linux</literal>
<listitem><para>An optional <literal>.uname</literal> section with the kernel version information, i.e.
the output of <command>uname -r</command> for the kernel included in the <literal>.linux</literal>
section.</para></listitem>
<listitem><para>An <literal>.sbat</literal> section with
<ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">SBAT</ulink> revocation
metadata.</para></listitem>
<listitem><para>An optional <literal>.sbat</literal> section with
<ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">SBAT</ulink> revocation metadata.
</para></listitem>
<listitem><para>A <literal>.pcrsig</literal> section with a set of cryptographic signatures for the
expected TPM2 PCR values after the kernel has been booted, in JSON format. This is useful for
<listitem><para>An optional <literal>.pcrsig</literal> section with a set of cryptographic signatures
for the expected TPM2 PCR values after the kernel has been booted, in JSON format. This is useful for
implementing TPM2 policies that bind disk encryption and similar to kernels that are signed by a
specific key.</para></listitem>
<listitem><para>A <literal>.pcrpkey</literal> section with a public key in the PEM format matching the
signature data in the <literal>.pcrsig</literal> section.</para></listitem>
<listitem><para>An optional <literal>.pcrpkey</literal> section with a public key in the PEM format
matching the signature data in the <literal>.pcrsig</literal> section.</para></listitem>
</itemizedlist>
<para>In a basic UKI, the sections listed above appear at most once. In a multi-profile UKI,
<!-- FIXME: how does .dtauto/.hwids matching interact with profiles? -->
<para>In a basic UKI, the sections listed above appear at most once, with the exception of
<literal>.dtbauto</literal> and <literal>.hwids</literal> sections. In a multi-profile UKI,
multiple sets of these sections are present in a single file and form "profiles",
one of which can be selected at boot. For this, the PE section <literal>.profile</literal> is
defined to be used as the separator between sets of sections. The
@ -206,7 +214,7 @@
<listitem><para>Similarly, files
<filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename> are loaded and verified as
PE binaries and specific sections are loaded from them. Addons are used to pass additional kernel
command line parameters (<literal>.cmdline</literal> section), or Devicetree blobs
command line parameters (<literal>.cmdline</literal> section), or DeviceTree blobs
(<literal>.dtb</literal> section), additional initrds (<literal>.initrd</literal> section),
and microcode updates (<literal>.ucode</literal> section). Addons allow those resources to be passed
regardless of the kernel version being booted, for example allowing platform vendors to ship

View File

@ -190,6 +190,17 @@
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
<varlistentry>
<term><option>streams</option></term>
<listitem><para>Lists streams that can be updated. This enumerates the
<filename>/var/cache/systemd/sysupdate@*.d/</filename> and <filename>/usr/lib/sysupdate@*.d/</filename>
directories that contain transfer definitions. This command is useful to list possible parameters
for <option>--stream=</option> (see below).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
@ -225,7 +236,8 @@
updated together in a synchronous fashion. Simply define multiple transfer files within the same
<filename>sysupdate.d/</filename> directory for these cases.</para>
<para>This option may not be combined with <option>--definitions=</option>.</para>
<para>This option may not be combined with <option>--definitions=</option> or
<option>--stream=</option>.</para>
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
@ -237,11 +249,29 @@
are read from this directory instead of <filename>/usr/lib/sysupdate.d/*.conf</filename>,
<filename>/etc/sysupdate.d/*.conf</filename>, and <filename>/run/sysupdate.d/*.conf</filename>.</para>
<para>This option may not be combined with <option>--component=</option>.</para>
<para>This option may not be combined with <option>--component=</option> or
<option>--stream=</option>.</para>
<xi:include href="version-info.xml" xpointer="v251"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--stream=</option></term>
<listitem><para>Selects the update stream to use. Takes a stream name as argument. This alters
the search logic for transfer definitions to look in
<filename>/usr/lib/sysupdate@<replaceable>stream</replaceable>.d/</filename> and
<filename>/var/cache/systemd/sysupdate@<replaceable>stream</replaceable>.d/</filename> instead of
<filename>/usr/lib/sysupdate.d</filename>.
Note that administrator-controlled directories (i.e. <filename>/etc/sysupdate.d/</filename>, etc) are
still loaded as usual.</para>
<para>This option may not be combined with <option>--definitions=</option> or
<option>--component=</option>.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--root=</option></term>

View File

@ -123,7 +123,7 @@
<row>
<entry><replaceable>prefix</replaceable><constant>d</constant><replaceable>number</replaceable></entry>
<entry>Devicetree alias index</entry>
<entry>DeviceTree alias index</entry>
</row>
</tbody>

View File

@ -1004,6 +1004,27 @@ DuplicateAddressDetection=none</programlisting></para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>MulticastIGMPVersion=</varname></term>
<listitem>
<para>Configures IPv4 Multicast IGMP Version to be used, and controls the value of
<filename>/proc/sys/net/ipv4/conf/<replaceable>INTERFACE</replaceable>/force_igmp_version</filename>.
Takes one of <literal>no</literal>,
<literal>v1</literal>, <literal>v2</literal>, or <literal>v3</literal>.
When <literal>no</literal>, no enforcement of an IGMP version will be applied, IGMPv1/v2 fallback are allowed, will back to
IGMPv3 mode again if all IGMPv1/v2 Querier Present timer expire.
When <literal>v1</literal>, use of IGMP version 1 will be enforced, and IGMPv1 report will be replied even if IGMPv2/v3
queries are received.
When <literal>v2</literal>, use of IGMP version 2 will be enforced, and IGMPv2 report will be replied if an IGMPv2/v3 query
is received, but fallback to IGMPv1 if an IGMPv1 query is received.
When <literal>v3</literal>, use of IGMP version 3 will be enforced, and the same reaction will be done as <literal>no</literal>.
Defaults to unset, and the sysctl value will be unchanged.
</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>IPv4AcceptLocal=</varname></term>
<listitem>

View File

@ -65,6 +65,52 @@
<citerefentry><refentrytitle>sysupdate.features</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para>
<para>Sometimes, distributions need to update certain parts of themselves independently from the normal
update cycle.
For example, the <ulink url="https://github.com/rhboot/shim">UEFI Shim loader</ulink> (necessary for
UEFI Secure Boot support in many cases) has its own release cycle, requires code signatures from a
third-party, and in general is not tied to a distribution's update cycle.
Support for this scenario is provided by "components", which allow distributions to define transfer
definitions that receive updates independently from the base OS.
Components are defined in <filename>/usr/lib/sysupdate.<replaceable>component</replaceable>.d/</filename>,
and have corresponding override directories for administrators (i.e.
<filename>/etc/sysupdate.<replaceable>component</replaceable>.d/</filename>, etc).</para>
<para>Some distributions may wish to maintain multiple update "streams" at a time, for example to offer
a beta/nightly update channel, or to distribute security updates to multiple major versions at a time.
Users of such distributions may wish to remain on their current stream, and switch streams at some future
point in time.
A distribution with multiple update streams should ship the transfer definitions for each stream in the
<filename>/usr/lib/sysupdate@<replaceable>stream</replaceable>.d/</filename> or
<filename>/var/cache/systemd/sysupdate@<replaceable>stream</replaceable>.d/</filename> directories.
For example, a distribution with multiple stable branches can ship the next major release's transfer
definitions in the current release's
<filename>/usr/lib/sysupdate@foobarOS-<replaceable>next</replaceable>.d/</filename> directory, and users
can switch to it by updating the <literal>foobarOS-<replaceable>next</replaceable></literal> stream.
How exactly these stream definition directories are delivered is up to distributions: they can stabilize
transfer definitions a version in advance and ship the stream definitions from day one, or they can ship
these files as part of a regular security patch that users will install anyway, or they can use a
component as described above to update the stream definitions under <filename>/var/cache/</filename>
independently from the host system.
Note that the presence of a stream definition directory does not imply the availability of an upgrade on
that stream; it just defines where to look and if an update is found on the remote how to install it.
Also note that the normal administrator override files (i.e. transfer definitions, feature definitions,
or drop-ins found in <filname>/etc/sysupdate.d/</filname>, <filename>/run/sysupdate.d/</filename>, etc)
are applied over top of the definitions found in the stream definition directory.
This is done because a the stream definition directory turns into the normal definition directory
(<filename>/usr/lib/sysupdate.d/</filename>) when that stream is switched to.</para>
<para>System Administrators must take <emphasis>extreme</emphasis> care when overriding any transfer or
optional feature definitions, other than to turn on or off features!
As with any configuration defined in <filename>/usr</filename> and overridden in
<filename>/etc</filename>, an update to the host system can break the administrator overrides.
However, <command>systemd-sysupdate</command> is uniquely destructive: a broken configuration could
prevent the system from updating (best case), or completely destroy an installation by wiping the wrong
partition.
Distributions must take care to avoid breaking systems where overrides exist only to turn on or off
optional features; supporting (or choosing not to) everything else is up to distribution policy.
<emphasis>You have been warned.</emphasis></para>
<para>Each <filename>*.transfer</filename> file contains three sections: [Transfer], [Source] and [Target].</para>
</refsect1>

View File

@ -64,12 +64,18 @@
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0"
#define CONF_PATHS(n) \
#define CONF_PATHS_ADMIN(n) \
"/etc/" n, \
"/run/" n, \
"/run/" n
#define CONF_PATHS_SYSTEM(n) \
"/usr/local/lib/" n, \
"/usr/lib/" n
#define CONF_PATHS(n) \
CONF_PATHS_ADMIN(n), \
CONF_PATHS_SYSTEM(n)
#define CONF_PATHS_STRV(n) \
STRV_MAKE(CONF_PATHS(n))

View File

@ -54,7 +54,7 @@ libraries (such as OpenSSL or gnu-efi) are linked, embedded, or used.
## Additional Resources
BLS Type #1 entries allow the user to load two types of additional resources that can affect the system
before `ExitBootServices()` has been called — kernel command line arguments and Devicetree blobs — that are
before `ExitBootServices()` has been called — kernel command line arguments and DeviceTree blobs — that are
not validated before use, as they do not carry signatures. For this reason, when SecureBoot is enabled,
loading these resources is automatically disabled. There is no override for this security mechanism, neither
at build time nor at runtime. Note that initrds are also not verified in BLS Type #1 configurations, for
@ -62,7 +62,7 @@ compatibility with how SecureBoot has been traditionally handled on Linux-based
only load them after `ExitBootServices()` has been called.
Another mechanism is supported by `systemd-boot` and `systemd-stub` to add additional payloads to the boot
process: "addons". Addons are PE signed binaries that can carry kernel command line arguments or Devicetree
process: "addons". Addons are PE signed binaries that can carry kernel command line arguments or DeviceTree
blobs (more payload types might be added in the future).
In contrast to the user-specified additions in the Type #1 case
described above, these addons are loaded through the UEFI image loading protocol, and thus are subject to

View File

@ -178,8 +178,8 @@ bool firmware_devicetree_exists(void) {
return !!find_configuration_table(MAKE_GUID_PTR(EFI_DTB_TABLE));
}
/* This function checks if the firmware provided Devicetree
* and a UKI provided Devicetree contain the same first entry
/* This function checks if the firmware provided DeviceTree
* and a UKI provided DeviceTree contain the same first entry
* on their respective "compatible" fields (which usually defines
* the actual device model). More specifically, given the FW/UKI
* "compatible" property pair:

View File

@ -98,7 +98,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --initrd=PATH Path to initrd image file %7$s .initrd\n"
" --ucode=PATH Path to microcode image file %7$s .ucode\n"
" --splash=PATH Path to splash bitmap file %7$s .splash\n"
" --dtb=PATH Path to Devicetree file %7$s .dtb\n"
" --dtb=PATH Path to DeviceTree file %7$s .dtb\n"
" --uname=PATH Path to 'uname -r' file %7$s .uname\n"
" --sbat=PATH Path to SBAT file %7$s .sbat\n"
" --pcrpkey=PATH Path to public key for PCR signatures %7$s .pcrpkey\n"

View File

@ -47,7 +47,7 @@ enum {
/* The tag used for EV_EVENT_TAG event log records covering the boot loader config */
#define LOADER_CONF_EVENT_TAG_ID UINT32_C(0xf5bc582a)
/* The tag used for EV_EVENT_TAG event log records covering Devicetree blobs */
/* The tag used for EV_EVENT_TAG event log records covering DeviceTree blobs */
#define DEVICETREE_ADDON_EVENT_TAG_ID UINT32_C(0x6c46f751)
/* The tag used for EV_EVENT_TAG event log records covering initrd addons */

View File

@ -502,7 +502,7 @@ try_devicetree:
return NULL;
}
/* Note that the Devicetree specification uses the very same vocabulary
/* Note that the DeviceTree specification uses the very same vocabulary
* of chassis types as we do, hence we do not need to translate these types:
*
* https://github.com/devicetree-org/devicetree-specification/blob/master/source/chapter3-devicenodes.rst */

View File

@ -150,6 +150,7 @@ Network.IPv4ProxyARPPrivateVLAN, config_parse_tristate,
Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp)
Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0
Network.IPv4ReversePathFilter, config_parse_ip_reverse_path_filter, 0, offsetof(Network, ipv4_rp_filter)
Network.MulticastIGMPVersion, config_parse_ipv4_force_igmp_version, 0, offsetof(Network, ipv4_force_igmp_version)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
Network.IgnoreCarrierLoss, config_parse_ignore_carrier_loss, 0, 0

View File

@ -481,6 +481,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
.proxy_arp = -1,
.proxy_arp_pvlan = -1,
.ipv4_rp_filter = _IP_REVERSE_PATH_FILTER_INVALID,
.ipv4_force_igmp_version = _IPV4_FORCE_IGMP_VERSION_INVALID,
.ndisc = -1,
.ndisc_use_redirect = true,

View File

@ -340,6 +340,7 @@ struct Network {
uint32_t ipv6_mtu;
IPv6PrivacyExtensions ipv6_privacy_extensions;
IPReversePathFilter ipv4_rp_filter;
IPv4ForceIgmpVersion ipv4_force_igmp_version;
int ipv6_proxy_ndp;
Set *ipv6_proxy_ndp_addresses;

View File

@ -408,6 +408,18 @@ static int link_set_ipv4_rp_filter(Link *link) {
return sysctl_write_ip_property_int(AF_INET, link->ifname, "rp_filter", link->network->ipv4_rp_filter, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv4_force_igmp_version(Link *link) {
assert(link);
if (!link_is_configured_for_family(link, AF_INET))
return 0;
if (link->network->ipv4_force_igmp_version < 0)
return 0;
return sysctl_write_ip_property_int(AF_INET, link->ifname, "force_igmp_version", link->network->ipv4_force_igmp_version, manager_get_sysctl_shadow(link->manager));
}
static int link_set_ipv6_privacy_extensions(Link *link) {
IPv6PrivacyExtensions val;
@ -723,6 +735,10 @@ int link_set_sysctl(Link *link) {
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv4 reverse path filtering for interface, ignoring: %m");
r = link_set_ipv4_force_igmp_version(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot set IPv4 force igmp version, ignoring: %m");
r = link_set_ipv4_promote_secondaries(link);
if (r < 0)
log_link_warning_errno(link, r, "Cannot enable promote_secondaries for interface, ignoring: %m");
@ -770,3 +786,13 @@ int config_parse_ip_forward_deprecated(
"and the same settings in .network files for per-interface setting.");
return 0;
}
static const char* const ipv4_force_igmp_version_table[_IPV4_FORCE_IGMP_VERSION_MAX] = {
[IPV4_FORCE_IGMP_VERSION_NO] = "no",
[IPV4_FORCE_IGMP_VERSION_1] = "v1",
[IPV4_FORCE_IGMP_VERSION_2] = "v2",
[IPV4_FORCE_IGMP_VERSION_3] = "v3",
};
DEFINE_STRING_TABLE_LOOKUP(ipv4_force_igmp_version, IPv4ForceIgmpVersion);
DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv4_force_igmp_version, ipv4_force_igmp_version, IPv4ForceIgmpVersion);

View File

@ -53,3 +53,18 @@ IPReversePathFilter ip_reverse_path_filter_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_reverse_path_filter);
CONFIG_PARSER_PROTOTYPE(config_parse_ip_forward_deprecated);
typedef enum IPv4ForceIgmpVersion {
/* These values map to the kernel's /proc/sys/net/ipv4/conf/INTERFACE/force_igmp_version values. Do not reorder! */
IPV4_FORCE_IGMP_VERSION_NO = 0,
IPV4_FORCE_IGMP_VERSION_1 = 1,
IPV4_FORCE_IGMP_VERSION_2 = 2,
IPV4_FORCE_IGMP_VERSION_3 = 3,
_IPV4_FORCE_IGMP_VERSION_MAX,
_IPV4_FORCE_IGMP_VERSION_INVALID = -EINVAL,
} IPv4ForceIgmpVersion;
const char* ipv4_force_igmp_version_to_string(IPv4ForceIgmpVersion i) _const_;
IPv4ForceIgmpVersion ipv4_force_igmp_version_from_string(const char *s) _pure_;
CONFIG_PARSER_PROTOTYPE(config_parse_ipv4_force_igmp_version);

View File

@ -47,6 +47,7 @@ char *arg_root = NULL;
static char *arg_image = NULL;
static bool arg_reboot = false;
static char *arg_component = NULL;
static char *arg_stream = NULL;
static int arg_verify = -1;
static ImagePolicy *arg_image_policy = NULL;
static bool arg_offline = false;
@ -56,6 +57,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_component, freep);
STATIC_DESTRUCTOR_REGISTER(arg_stream, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_transfer_source, freep);
@ -180,7 +182,54 @@ static int context_read_definitions(Context *c, const char* node) {
if (arg_definitions)
dirs = strv_new(arg_definitions);
else if (arg_component) {
else if (arg_stream) {
/* Ultimately we end up with a search path along the lines of: /etc/sysupdate.d/,
* /run/sysupdate.d/, /var/cache/systemd/sysupdate@<stream>.d, /usr/lib/sysupdate@<stream>.d.
* This is very unusual! It seems wrong! But this is the correct behavior. When a
* `systemd-sysupdate --stream=` update is completed, /usr/lib/sysupdate@<stream>.d (or its
* /var/cache alternative) turns into /usr/lib/sysupdate.d, but the admin overrides remain
* untouched. So if we did this any differently, we'd end up in a situation where the admin's
* settings are ignored when first installing a major upgrade but then suddenly considered
* again once the update is completed. In my opinion, that behavior would be more unexpected
* and dangerous than what is implemented here!
*
* Is this a big and surprising footgun for the admin? Yes. But frankly, so is overriding
* anything relating to sysupdate. If an admin has overrides that do anything other than
* turning on/off optional features, they've already aimed a ballistic missile at their
* installation. It'll detonate either immediately when trying to switch streams (as
* implemented now), or when updating to the first patch of the new stream (the alternative);
* the installation is doomed either way. And failing immediately during a major OS upgrade
* seems a lot more preferable, and something that admins will be more prepared for, than a
* subsequent security patch suddenly bricking installations. */
char **admin = STRV_MAKE(CONF_PATHS_ADMIN("sysupdate.d"));
char **system = STRV_MAKE("/var/cache/systemd/", CONF_PATHS_SYSTEM(""));
size_t i = 0;
dirs = new0(char*, strv_length(admin) + strv_length(system) + 1);
if (!dirs)
return log_oom();
STRV_FOREACH(dir, admin) {
char *d;
d = strdup(*dir);
if (!d)
return log_oom();
dirs[i++] = d;
}
STRV_FOREACH(dir, system) {
char *j;
j = strjoin(*dir, "sysupdate@", arg_stream, ".d");
if (!j)
return log_oom();
dirs[i++] = j;
}
} else if (arg_component) {
char **l = CONF_PATHS_STRV("");
size_t i = 0;
@ -247,6 +296,11 @@ static int context_read_definitions(Context *c, const char* node) {
"No transfer definitions for component '%s' found.",
arg_component);
if (arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No transfer definitions for stream '%s' found.",
arg_stream);
return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
"No transfer definitions found.");
}
@ -1532,22 +1586,45 @@ static int component_name_valid(const char *c) {
return filename_is_valid(j);
}
static int verb_components(int argc, char **argv, void *userdata) {
static int stream_name_valid(const char *s) {
_cleanup_free_ char *j = NULL;
/* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */
if (isempty(s))
return false;
if (string_has_cc(s, NULL))
return false;
if (!utf8_is_valid(s))
return false;
j = strjoin("sysupdate@", s, ".d");
if (!j)
return -ENOMEM;
return filename_is_valid(j);
}
static int walk_search_paths(char **paths, bool component, char ***ret, bool *ret_has_default) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL;
_cleanup_set_free_ Set *names = NULL;
_cleanup_free_ char **z = NULL; /* We use simple free() rather than strv_free() here, since set_free() will free the strings for us */
char **l = CONF_PATHS_STRV("");
bool has_default_component = false;
_cleanup_free_ char **names_strv = NULL; /* free() b/c the set still owns the values */
_cleanup_strv_free_ char **names_dup = NULL;
bool has_default = false;
int r;
assert(argc <= 1);
assert(paths);
assert(ret);
assert(ret_has_default);
r = process_image(/* ro= */ false, &mounted_dir, &loop_device);
if (r < 0)
return r;
STRV_FOREACH(i, l) {
STRV_FOREACH(i, paths) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *p = NULL;
@ -1577,11 +1654,11 @@ static int verb_components(int argc, char **argv, void *userdata) {
continue;
if (streq(de->d_name, "sysupdate.d")) {
has_default_component = true;
has_default = true;
continue;
}
e = startswith(de->d_name, "sysupdate.");
e = startswith(de->d_name, component ? "sysupdate." : "sysupdate@");
if (!e)
continue;
@ -1593,26 +1670,51 @@ static int verb_components(int argc, char **argv, void *userdata) {
if (!n)
return log_oom();
r = component_name_valid(n);
if (component)
r = component_name_valid(n);
else
r = stream_name_valid(n);
if (r < 0)
return log_error_errno(r, "Unable to validate component name: %m");
return log_error_errno(r, "Unable to validate %s name: %m",
component ? "component" : "stream");
if (r == 0)
continue;
r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n));
if (r < 0 && r != -EEXIST)
return log_error_errno(r, "Failed to add component to set: %m");
return log_error_errno(r, "Failed to add %s to set: %m",
component ? "component" : "stream");
}
}
z = set_get_strv(names);
if (!z)
names_strv = set_get_strv(names);
if (!names_strv)
return log_oom();
strv_sort(z);
names_dup = strv_copy(names_strv);
if (!names_dup)
return log_oom();
strv_sort(names_dup);
*ret = TAKE_PTR(names_dup);
*ret_has_default = has_default;
return 0;
}
static int verb_components(int argc, char **argv, void *userdata) {
_cleanup_strv_free_ char **names = NULL;
bool has_default_component = false;
int r;
assert(argc <= 1);
r = walk_search_paths(CONF_PATHS_STRV(""), true, &names, &has_default_component);
if (r < 0)
return r;
if (!sd_json_format_enabled(arg_json_format_flags)) {
if (!has_default_component && set_isempty(names)) {
if (!has_default_component && strv_isempty(names)) {
log_info("No components defined.");
return 0;
}
@ -1621,13 +1723,53 @@ static int verb_components(int argc, char **argv, void *userdata) {
printf("%s<default>%s\n",
ansi_highlight(), ansi_normal());
STRV_FOREACH(i, z)
STRV_FOREACH(i, names)
puts(*i);
} else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_BOOLEAN("default", has_default_component),
SD_JSON_BUILD_PAIR_STRV("components", z));
SD_JSON_BUILD_PAIR_STRV("components", names));
if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m");
r = sd_json_variant_dump(json, arg_json_format_flags, stdout, NULL);
if (r < 0)
return log_error_errno(r, "Failed to print JSON: %m");
}
return 0;
}
static int verb_streams(int argc, char **argv, void *userdata) {
char **dirs = STRV_MAKE("/var/cache/systemd/", CONF_PATHS_SYSTEM(""));
_cleanup_strv_free_ char **names = NULL;
bool has_default_stream = false;
int r;
assert(argc <= 1);
r = walk_search_paths(dirs, false, &names, &has_default_stream);
if (r < 0)
return r;
if (FLAGS_SET(arg_json_format_flags, SD_JSON_FORMAT_OFF)) {
if (!has_default_stream && strv_isempty(names)) {
log_info("No streams defined.");
return 0;
}
if (has_default_stream)
printf("%s<default>%s\n",
ansi_highlight(), ansi_normal());
STRV_FOREACH(i, names)
puts(*i);
} else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL;
r = sd_json_buildo(&json, SD_JSON_BUILD_PAIR_BOOLEAN("default", has_default_stream),
SD_JSON_BUILD_PAIR_STRV("streams", names));
if (r < 0)
return log_error_errno(r, "Failed to create JSON: %m");
@ -1659,11 +1801,13 @@ static int verb_help(int argc, char **argv, void *userdata) {
" currently booted\n"
" reboot Reboot if a newer version is installed than booted\n"
" components Show list of components\n"
" streams Show list of streams\n"
" -h --help Show this help\n"
" --version Show package version\n"
"\n%3$sOptions:%4$s\n"
" -C --component=NAME Select component to update\n"
" --definitions=DIR Find transfer definitions in specified directory\n"
" --stream=STREAM Select stream to switch to\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --image=PATH Operate on disk image as filesystem root\n"
" --image-policy=POLICY\n"
@ -1698,6 +1842,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_NO_LEGEND,
ARG_SYNC,
ARG_DEFINITIONS,
ARG_STREAM,
ARG_JSON,
ARG_ROOT,
ARG_IMAGE,
@ -1714,6 +1859,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "no-legend", no_argument, NULL, ARG_NO_LEGEND },
{ "definitions", required_argument, NULL, ARG_DEFINITIONS },
{ "stream", required_argument, NULL, ARG_STREAM },
{ "instances-max", required_argument, NULL, 'm' },
{ "sync", required_argument, NULL, ARG_SYNC },
{ "json", required_argument, NULL, ARG_JSON },
@ -1770,6 +1916,24 @@ static int parse_argv(int argc, char *argv[]) {
return r;
break;
case ARG_STREAM:
if (isempty(optarg)) {
arg_stream = mfree(arg_stream);
break;
}
r = stream_name_valid(optarg);
if (r < 0)
return log_error_errno(r, "Failed to determine if stream name is valid: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Stream name invalid: %s", optarg);
r = free_and_strdup_warn(&arg_stream, optarg);
if (r < 0)
return r;
break;
case ARG_JSON:
r = parse_json_argument(optarg, &arg_json_format_flags);
if (r <= 0)
@ -1856,6 +2020,12 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_definitions && arg_component)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined.");
if (arg_definitions && arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --stream= switches may not be combined.");
if (arg_component && arg_stream)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --component= and --stream= switches may not be combined.");
return 1;
}
@ -1864,6 +2034,7 @@ static int sysupdate_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "list", VERB_ANY, 2, VERB_DEFAULT, verb_list },
{ "components", VERB_ANY, 1, 0, verb_components },
{ "streams", VERB_ANY, 1, 0, verb_streams },
{ "features", VERB_ANY, 2, 0, verb_features },
{ "check-new", VERB_ANY, 1, 0, verb_check_new },
{ "update", VERB_ANY, 2, 0, verb_update },

View File

@ -924,7 +924,7 @@ static int names_devicetree(sd_device *dev, const char *prefix, EventMode mode)
char str[ALTIFNAMSIZ];
if (snprintf_ok(str, sizeof str, "%sd%u", prefix, i))
udev_builtin_add_property(dev, mode, "ID_NET_NAME_ONBOARD", str);
log_device_debug(dev, "Devicetree identifier: alias_index=%u %s \"%s\"",
log_device_debug(dev, "DeviceTree identifier: alias_index=%u %s \"%s\"",
i, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), str + strlen(prefix));
return 0;
}

View File

@ -13,3 +13,4 @@ IPv6ProxyNDP=yes
IPv6AcceptRA=no
IPv4AcceptLocal=yes
IPv4ReversePathFilter=no
MulticastIGMPVersion=v1

View File

@ -4381,6 +4381,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
self.check_ipv4_sysctl_attr('dummy98', 'proxy_arp_pvlan', '1')
self.check_ipv4_sysctl_attr('dummy98', 'accept_local', '1')
self.check_ipv4_sysctl_attr('dummy98', 'rp_filter', '0')
self.check_ipv4_sysctl_attr('dummy98', 'force_igmp_version', '1')
copy_network_unit('25-sysctl.network.d/25-ipv6-privacy-extensions.conf')
networkctl_reload()