1
0
mirror of https://github.com/systemd/systemd synced 2025-12-27 19:34:47 +01:00

Compare commits

...

37 Commits

Author SHA1 Message Date
Zbigniew Jędrzejewski-Szmek
0a67dd8310
Merge pull request #17692 from yuwata/ipv4ll
network: fold ipv4ll fallback modes into normal ipv4ll addressing
2020-12-01 21:59:30 +01:00
Zbigniew Jędrzejewski-Szmek
f319b2b1b0
Merge pull request #17703 from poettering/event-ratelimit
sd-event: add a concept of ratelimiting
2020-12-01 21:47:43 +01:00
Zbigniew Jędrzejewski-Szmek
946f3d868c
Merge pull request #17524 from poettering/fileio-offset
cryptsetup: if keyfile is specified as AF_UNIX socket in the fs, connect to it, and read key data from it
2020-12-01 21:38:52 +01:00
Lennart Poettering
a303686fc1 man: document new ratelimiting APIs 2020-12-01 15:15:39 +01:00
Michal Sekletár
d586f642fd core: prevent excessive /proc/self/mountinfo parsing 2020-12-01 15:15:39 +01:00
Michal Sekletár
68d8906517 test: add ratelimiting test
(Taken from Michal's #17274 by Lennart, and slightly adjusted)
2020-12-01 15:15:39 +01:00
Lennart Poettering
b6d5481b3d sd-event: add ability to ratelimit event sources
Let's a concept of "rate limiting" to event sources: if specific event
sources fire too often in some time interval temporarily take them
offline, and take them back online once the interval passed.

This is a simple scheme of avoiding starvation of event sources if some
event source fires too often.

This introduces the new conceptual states of "offline" and "online" for
event sources: an event source is "online" only when enabled *and* not
ratelimited, and offline in all other cases. An event source that is
online hence has its fds registered in the epoll, its signals in the
signalfd and so on.
2020-12-01 15:11:24 +01:00
Lennart Poettering
f41315fceb sd-event: remove earliest_index/latest_index into common part of event source objects
So far we used these fields to organize the earliest/latest timer event
priority queue.  In a follow-up commit we want to introduce ratelimiting
to event sources, at which point we want any kind of event source to be
able to trigger time wakeups, and hence they all need to be included in
the earliest/latest prioqs.  Thus, in preparation let's make this
generic.

No change in behaviour, just some shifting around of struct members from
the type-specific to the generic part.
2020-12-01 15:10:50 +01:00
Lennart Poettering
cad143a8f2 sd-event: follow coding style with naming return parameter 2020-12-01 15:10:50 +01:00
Lennart Poettering
f814c871e6 sd-event: ref event loop while in sd_event_prepare() ot sd_event_run()
sd_event_prepare() invokes callbacks that might drop the last user ref
on our event loop. Let's make sure we keep an explicit ref around it, so
that we won't end up with an invalid pointer. Similar in sd_event_run().

Basically, any function that is publically callable that might end up
invoking callbacks should ref the relevant objects to be protected
against callbacks destroying these objects while we still want to access
them. We did this correctly in sd_event_dispatch() and sd_event_loop(),
but these are not the only ones which are callable from the outside.
2020-12-01 15:10:50 +01:00
Lennart Poettering
e6a7bee538 sd-event: let's suffix last_run/last_log with "_usec"
Otherwise it's a bit confusing what this is about: two timestamps.
2020-12-01 15:10:50 +01:00
Lennart Poettering
1e45e3fecc sd-event: split out code to add/remove timer event sources to earliest/latest prioq
Just some refactoring that makes code prettier, and will come handy
later, because we can reuse these functions at more places.
2020-12-01 15:10:50 +01:00
Lennart Poettering
41c63f36c3 sd-event: split clock data allocation out of sd_event_add_time()
Just some simple refactoring, that will make things easier for us later.
But it looks better this way even without the later function reuse.
2020-12-01 15:10:50 +01:00
Lennart Poettering
f80a5d6a86 sd-event: mention that two debug logged events are ignored 2020-12-01 14:36:06 +01:00
Lennart Poettering
8c437318b8 update TODO 2020-12-01 14:27:33 +01:00
Lennart Poettering
59d6f7b097 man: drop comment about ECC vs. RSA and Yubikey
The comment is pointless, ECC systematically doesn't allow
encryption/decryption directly, only RSA does that. If you want to use
ECC for asymmetric encryption/decryption you have to combine it with key
exchange scheme and symmetric scheme. This all is not a limitation of
the Yubikey, hence don't claim so. It's just how ECC is.
2020-12-01 14:27:33 +01:00
Lennart Poettering
96e9a9a4e6 man: document how cryptsetup keys may be acquired via AF_UNIX sockets 2020-12-01 14:27:12 +01:00
Lennart Poettering
e060ed32e4 cryptsetup: modify keyfile search logic to use read_file_full() too
Let's move the 3rd way how cryptsetup acquires key files to
read_file_full() too.

Since load_key_file()'s raison d'etre now is just the search path logic,
let's rename the function to find_key_file().
2020-12-01 14:27:01 +01:00
Lennart Poettering
e2c2f868b2 cryptsetup: port cryptsetup's main key file logic over to read_full_file_full()
Previously, we'd load the file with libcryptsetup's calls. Let's do that
in our own, so that we can make use of READ_FULL_FILE_CONNECT_SOCKET,
i.e. read in keys via AF_UNIX sockets, so that people can plug key
providers into our logic.

This provides functionality similar to Debian's keyscript= crypttab
option (see → #3007), as it allows key scripts to be run as socket
activated services, that have stdout connected to the activated socket.
In contrast to traditional keyscript= support this logic runs stuff out
of process however, which is beneficial, since it allows sandboxing and
similar.
2020-12-01 14:17:47 +01:00
Lennart Poettering
4d1bb8f397 cryptsetup: port PKCS#11 code to read key file with read_full_file()
Now that we can read from offsets/with size, let's port the cryptsetup
PKCS#11 key file logic over to read_full_file_full().
2020-12-01 14:17:47 +01:00
Lennart Poettering
986311c2da fileio: teach read_full_file_full() to read from offset/with maximum size 2020-12-01 14:17:47 +01:00
Lennart Poettering
c61f46fe31 journal-remote: suffix cmdline option that expects arg with = 2020-12-01 14:17:47 +01:00
Lennart Poettering
ce82de671f man: mention that --key= is about *secret* keys 2020-12-01 14:17:47 +01:00
Lennart Poettering
e5de42e6f2 journal-remote: use READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE when reading PEM secret key
It's secret data, hence use the appropriate flags.
2020-12-01 14:17:46 +01:00
Lennart Poettering
8b3c3a4973 repart: warn about world writable key files
We have easy support for this, hence use it for privileged key data.
2020-12-01 14:17:46 +01:00
Lennart Poettering
ae9cf30b2f dissect-image: use simple version of read_full_file() where we can 2020-12-01 14:17:46 +01:00
Lennart Poettering
f362fe731b journal-remote: erase secret PEM key from memory after use 2020-12-01 14:17:46 +01:00
Yu Watanabe
53ec5dd028 network: use IN_SET() macro
Follow-up for 1d370b2c182505ff8033fccbebcc56621d305220.
2020-11-30 12:41:41 +09:00
Yu Watanabe
0d0799daf4 network: treat IPv4LL is one of dynamic addressing protocol
This makes an IPv4LL address optional when multiple dynamic
addressing protocols are enabled.
2020-11-30 12:41:41 +09:00
Yu Watanabe
0b4b66cc53 network: simplify the condition about ipv4ll is enabled or not 2020-11-30 12:41:41 +09:00
Yu Watanabe
d19b993983 network: shorten link_check_ready() a bit 2020-11-30 12:41:41 +09:00
Yu Watanabe
8ccae2dd2d network: stop IPv4LL engine when DHCPv4 address is successfully acquired 2020-11-30 12:41:30 +09:00
Zbigniew Jędrzejewski-Szmek
0107b769b1 networkd: start ipv4ll when dhcp has trouble getting a lease
Fixes #13316.
2020-11-30 12:37:36 +09:00
Zbigniew Jędrzejewski-Szmek
fb536bc5da sd-dhcp-client: report transient DHCP failure to the caller
So far we only reported major state transitions like failure to acquire
the message. Let's report the initial failure after a few timeouts in
a new event type.

The number of timeouts is hardcoded as 3, since Windows seems to be using
that. I don't think we need to make this configurable out of the box. A
reasonable default may be enough.
2020-11-30 12:37:36 +09:00
Zbigniew Jędrzejewski-Szmek
3ca1fab70a networkd: merge ll addressing fallback modes into normal "boolean" values
They are not really boolean, because we have both ipv4 and ipv6, but
for each protocol we have either unset, no, and yes.

From https://github.com/systemd/systemd/issues/13316#issuecomment-582906817:
LinkLocalAddressing must be a boolean option, at least for ipv4:
- LinkLocalAddressing=no => no LL at all.

- LinkLocalAddressing=yes + Static Address => invalid configuration, warn and
  interpret as LinkLocalAddressing=no, no LL at all.

(we check that during parsing and reject)

- LinkLocalAddressing=yes + DHCP => LL process should be subordinated to the
  DHCP one, an LL address must be acquired at start or after a short N
  unsuccessful DHCP attemps, and must not stop DHCP to keeping trying. When a
  DHCP address is acquired, drop the LL address. If the DHCP address is lost,
  re-adquire a new LL address.

(next patch will move in this direction)

- LinkLocalAddressing=fallback has no reason to exist, because LL address must
  always be allocated as a fallback option when using DHCP. Having both DHCP
  and LL address at the same time is an RFC violation, so
  LinkLocalAdressing=yes correctly implemented is already the "fallback"
  behavior. The fallback option must be deprecated and if present in older
  configs must be interpreted as LinkLocalAddressing=yes.

(removed)

- And for IPv6, the LinkLocalAddress option has any sense at all? IPv6-LL
  address aren't required to be always set for every IPv6 enabled interface (in
  this case, coexisting with static or dynamic address if any)? Shouldn't be
  always =yes?

(good question)

This effectively reverts 29e81083bd2fcb2dbf83f67ef358c7d25adf7e9d. There is no
special "fallback" mode now, so the check doesn't make sense anymore.
2020-11-30 12:37:36 +09:00
Zbigniew Jędrzejewski-Szmek
e4dc0845bc sd-dhcp-client: minor simplification 2020-11-30 12:37:36 +09:00
Zbigniew Jędrzejewski-Szmek
573b02f5c1 man: adjust description of MaxAttempts
The code was changed in 715cedfbf03a2eb1d4dca5d1b2b876e52a3b652d to allow more than
six attempts and the old description stopped making sense.
2020-11-30 12:37:36 +09:00
48 changed files with 1104 additions and 421 deletions

3
TODO
View File

@ -76,9 +76,6 @@ Features:
* make use of new glibc 2.32 APIs sigabbrev_np() and strerrorname_np(). * make use of new glibc 2.32 APIs sigabbrev_np() and strerrorname_np().
* cryptsetup: if keyfile specified in crypttab is AF_UNIX socket, connect to it
and read from it (like we do elsewhere with READ_FULL_FILE_CONNECT_SOCKET)
* when main nspawn supervisor process gets suspended due to SIGSTOP/SIGTTOU or * when main nspawn supervisor process gets suspended due to SIGSTOP/SIGTTOU or
so, freeze the payload too. so, freeze the payload too.

View File

@ -53,25 +53,36 @@
it is opened as a LUKS device; otherwise, it is assumed to be in it is opened as a LUKS device; otherwise, it is assumed to be in
raw dm-crypt (plain mode) format.</para> raw dm-crypt (plain mode) format.</para>
<para>The first field contains the name of the resulting encrypted volume; its block device is set up <para>The four fields of <filename>/etc/crypttab</filename> are defined as follows:</para>
below <filename>/dev/mapper/</filename>.</para>
<para>The second field contains a path to the underlying block <orderedlist>
device or file, or a specification of a block device via
<literal>UUID=</literal> followed by the UUID.</para>
<para>The third field specifies an absolute path to a file with the encryption key. Optionally, <listitem><para>The first field contains the name of the resulting volume with decrypted data; its
the path may be followed by <literal>:</literal> and an fstab device specification (e.g. starting with block device is set up below <filename>/dev/mapper/</filename>.</para></listitem>
<literal>LABEL=</literal> or similar); in which case the path is taken relative to the device file system
root. If the field is not present or is <literal>none</literal> or <literal>-</literal>, a key file
named after the volume to unlock (i.e. the first column of the line), suffixed with
<filename>.key</filename> is automatically loaded from the <filename>/etc/cryptsetup-keys.d/</filename>
and <filename>/run/cryptsetup-keys.d/</filename> directories, if present. Otherwise, the password has to
be manually entered during system boot. For swap encryption, <filename>/dev/urandom</filename> may be
used as key file.</para>
<para>The fourth field, if present, is a comma-delimited list of <listitem><para>The second field contains a path to the underlying block
options. The following options are recognized:</para> device or file, or a specification of a block device via
<literal>UUID=</literal> followed by the UUID.</para></listitem>
<listitem><para>The third field specifies an absolute path to a file with the encryption
key. Optionally, the path may be followed by <literal>:</literal> and an fstab device specification
(e.g. starting with <literal>LABEL=</literal> or similar); in which case the path is taken relative to
the device file system root. If the field is not present or is <literal>none</literal> or
<literal>-</literal>, a key file named after the volume to unlock (i.e. the first column of the line),
suffixed with <filename>.key</filename> is automatically loaded from the
<filename>/etc/cryptsetup-keys.d/</filename> and <filename>/run/cryptsetup-keys.d/</filename>
directories, if present. Otherwise, the password has to be manually entered during system boot. For
swap encryption, <filename>/dev/urandom</filename> may be used as key file, resulting in a randomized
key.</para>
<para>If the specified key file path refers to an <constant>AF_UNIX</constant> stream socket in the
file system, the key is acquired by connecting to the socket and reading it from the connection. This
allows the implementation of a service to provide key information dynamically, at the moment when it is
needed. For details see below.</para></listitem>
<listitem><para>The fourth field, if present, is a comma-delimited list of options. The suppported
options are listed below.</para></listitem>
</orderedlist>
<variablelist class='fstab-options'> <variablelist class='fstab-options'>
@ -499,6 +510,34 @@
<citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
</refsect1> </refsect1>
<refsect1>
<title><constant>AF_UNIX</constant> Key Files</title>
<para>If the key file path (as specified in the third column of <filename>/etc/crypttab</filename>
entries, see above) refers to an <constant>AF_UNIX</constant> stream socket in the file system, the key
is acquired by connecting to the socket and reading the key from the connection. The connection is made
from an <constant>AF_UNIX</constant> socket name in the abstract namespace, see <citerefentry
project='man-pages'><refentrytitle>unix</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
details. The source socket name is chosen according the following format:</para>
<programlisting><constant>NUL</constant> <replaceable>RANDOM</replaceable> <literal>/cryptsetup/</literal> <replaceable>VOLUME</replaceable></programlisting>
<para>In other words: a <constant>NUL</constant> byte (as required for abstract namespace sockets),
followed by a random string (consisting of alphabenumeric characters only), followed by the literal
string <literal>/cryptsetup/</literal>, followed by the name of the volume to acquire they key
for. Example (for a volume <literal>myvol</literal>):</para>
<example><programlisting>\0d7067f78d9827418/cryptsetup/myvol</programlisting></example>
<para>Services listening on the <constant>AF_UNIX</constant> stream socket may query the source socket
name with <citerefentry
project='man-pages'><refentrytitle>getpeername</refentrytitle><manvolnum>2</manvolnum></citerefentry>,
and use it to determine which key to send, allowing a single listening socket to serve keys for a
multitude of volumes. If the PKCS#11 logic is used (see below) the socket source name is picked in
identical fashion, except that the literal string <literal>/cryptsetup-pkcs11/</literal> is used. This is
done so that services providing key material know that not a secret key is requested but an encrypted key
that will be decrypted via the PKCS#11 logic to acquire the final secret key.</para>
</refsect1>
<refsect1> <refsect1>
<title>Examples</title> <title>Examples</title>
<example> <example>
@ -529,7 +568,6 @@ external /dev/sda3 keyfile:LABEL=keydev keyfile-timeout=10s,cipher=xchac
<para>A few notes on the above:</para> <para>A few notes on the above:</para>
<itemizedlist> <itemizedlist>
<listitem><para>We use RSA (and not ECC), since Yubikeys support PKCS#11 Decrypt() only for RSA keys</para></listitem>
<listitem><para>We use RSA2048, which is the longest key size current Yubikeys support</para></listitem> <listitem><para>We use RSA2048, which is the longest key size current Yubikeys support</para></listitem>
<listitem><para>LUKS key size must be shorter than 2048bit due to RSA padding, hence we use 128 bytes</para></listitem> <listitem><para>LUKS key size must be shorter than 2048bit due to RSA padding, hence we use 128 bytes</para></listitem>
<listitem><para>We use Yubikey key slot 9d, since that's apparently the keyslot to use for decryption purposes, <listitem><para>We use Yubikey key slot 9d, since that's apparently the keyslot to use for decryption purposes,

View File

@ -582,6 +582,10 @@ manpages = [
'SD_EVENT_PRIORITY_NORMAL', 'SD_EVENT_PRIORITY_NORMAL',
'sd_event_source_get_priority'], 'sd_event_source_get_priority'],
''], ''],
['sd_event_source_set_ratelimit',
'3',
['sd_event_source_get_ratelimit', 'sd_event_source_is_ratelimited'],
''],
['sd_event_source_set_userdata', '3', ['sd_event_source_get_userdata'], ''], ['sd_event_source_set_userdata', '3', ['sd_event_source_get_userdata'], ''],
['sd_event_source_unref', ['sd_event_source_unref',
'3', '3',

View File

@ -56,6 +56,7 @@
<citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_ratelimit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
@ -147,6 +148,7 @@
<citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_get_pending</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_source_set_prepare</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_ratelimit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_set_watchdog</refentrytitle><manvolnum>3</manvolnum></citerefentry>,

View File

@ -147,7 +147,8 @@
<citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_add_child</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>, <citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> <citerefentry><refentrytitle>sd_event_source_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_ratelimit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para> </para>
</refsect1> </refsect1>

View File

@ -0,0 +1,148 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="sd_event_source_set_ratelimit" xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>sd_event_source_set_ratelimit</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>sd_event_source_set_ratelimit</refentrytitle>
<manvolnum>3</manvolnum>
</refmeta>
<refnamediv>
<refname>sd_event_source_set_ratelimit</refname>
<refname>sd_event_source_get_ratelimit</refname>
<refname>sd_event_source_is_ratelimited</refname>
<refpurpose>Configure rate limiting on event sources</refpurpose>
</refnamediv>
<refsynopsisdiv>
<funcsynopsis>
<funcsynopsisinfo>#include &lt;systemd/sd-event.h&gt;</funcsynopsisinfo>
<funcprototype>
<funcdef>int <function>sd_event_source_set_ratelimit</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>uint64_t <parameter>interval_usec</parameter></paramdef>
<paramdef>unsigned <parameter>burst</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_get_ratelimit</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
<paramdef>uint64_t* <parameter>ret_interval_usec</parameter></paramdef>
<paramdef>unsigned* <parameter>ret_burst</parameter></paramdef>
</funcprototype>
<funcprototype>
<funcdef>int <function>sd_event_source_is_ratelimited</function></funcdef>
<paramdef>sd_event_source *<parameter>source</parameter></paramdef>
</funcprototype>
</funcsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><function>sd_event_source_set_ratelimit()</function> may be used to enforce rate limiting on an
event source. When used an event source will be temporarily turned off when it fires more often then a
specified burst number within a specified time interval. This is useful as simple mechanism to avoid
event source starvation if high priority event sources fire very frequently.</para>
<para>Pass the event source to operate on as first argument, a time interval in microseconds as second
argument and a maximum dispatch limit ("burst") as third parameter. Whenever the event source is
dispatched more often than the specified burst within the specified interval it is placed in a mode
similar to being disabled with
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
and the <constant>SD_EVENT_OFF</constant> parameter. However it is disabled only temporarily once the
specified interval is over regular operation resumes. It is again disabled temporarily once the specified rate
limiting is hit the next time. If either the interval or the burst value are specified as zero, rate
limiting is turned off. By default event sources do not have rate limiting enabled. Note that rate
limiting and disabling via <function>sd_event_source_set_enabled()</function> are independent of each
other, and an event source will only effect event loop wake-ups and is dispatched while it both is
enabled and rate limiting is not in effect.</para>
<para><function>sd_event_source_get_ratelimit()</function> may be used to query the current rate limiting
parameters set on the event source object <parameter>source</parameter>. The previously set interval and
burst vales are returned in the second and third argument.</para>
<para><function>sd_event_source_is_ratelimited()</function> may be used to query whether the event source
is currently affected by rate limiting, i.e. it has recently hit the rate limit and is currently
temporarily disabled due to that.</para>
<para>Rate limiting is currently implemented for I/O, timer, signal, defer and inotify event
sources.</para>
</refsect1>
<refsect1>
<title>Return Value</title>
<para>On success, <function>sd_event_source_set_ratelimit()</function> and
<function>sd_event_source_get_ratelimit()</function> return a non-negative integer. On failure, they
return a negative errno-style error code. <function>sd_event_source_is_ratelimited</function> returns
zero if rate limiting is currently not in effect and greater than zero if it is in effect; it returns a
negative errno-style error code on failure.</para>
<refsect2>
<title>Errors</title>
<para>Returned errors may indicate the following problems:</para>
<variablelist>
<varlistentry>
<term><constant>-EINVAL</constant></term>
<listitem><para><parameter>source</parameter> is not a valid pointer to an
<structname>sd_event_source</structname> object.
</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ECHILD</constant></term>
<listitem><para>The event loop has been created in a different process.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-EDOM</constant></term>
<listitem><para>It was attempted to use the rate limiting feature on an event source type that does
not support rate limiting.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENOEXEC</constant></term>
<listitem><para><function>sd_event_source_get_ratelimit()</function> was called on a event source
that doesn't have rate limiting configured.</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<xi:include href="libsystemd-pkgconfig.xml" />
<refsect1>
<title>See Also</title>
<para>
<citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_io</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_time</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_inotify</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_add_defer</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
<citerefentry><refentrytitle>sd_event_source_set_enabled</refentrytitle><manvolnum>3</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>

View File

@ -68,8 +68,8 @@
<term><option>--key=</option></term> <term><option>--key=</option></term>
<listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the
server key corresponding to the certificate specified with <option>--cert=</option> from. The key secret server key corresponding to the certificate specified with <option>--cert=</option> from. The
must be in PEM format.</para></listitem> key must be in PEM format.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -180,7 +180,7 @@
<varlistentry> <varlistentry>
<term><option>--key=</option></term> <term><option>--key=</option></term>
<listitem><para> Takes a path to a SSL key file in PEM format. Defaults to <listitem><para>Takes a path to a SSL secret key file in PEM format. Defaults to
<filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>. This option can be used with <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>. This option can be used with
<option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
in the file system a connection is made to it and the key read from it.</para></listitem> in the file system a connection is made to it and the key read from it.</para></listitem>

View File

@ -364,17 +364,15 @@
<varlistentry> <varlistentry>
<term><varname>LinkLocalAddressing=</varname></term> <term><varname>LinkLocalAddressing=</varname></term>
<listitem> <listitem>
<para>Enables link-local address autoconfiguration. Accepts <literal>yes</literal>, <para>Enables link-local address autoconfiguration. Accepts <option>yes</option>,
<literal>no</literal>, <literal>ipv4</literal>, <literal>ipv6</literal>, <option>no</option>, <option>ipv4</option>, and <option>ipv6</option>. An IPv6 link-local address
<literal>fallback</literal>, or <literal>ipv4-fallback</literal>. If is configured when <option>yes</option> or <option>ipv6</option>. An IPv4 link-local address is
<literal>fallback</literal> or <literal>ipv4-fallback</literal> is specified, then an IPv4 configured when <option>yes</option> or <option>ipv4</option> and when DHCPv4 autoconfiguration
link-local address is configured only when DHCPv4 fails. If <literal>fallback</literal>, has been unsuccessful for some time. (IPv4 link-local address autoconfiguration will usually
an IPv6 link-local address is always configured, and if <literal>ipv4-fallback</literal>, happen in parallel with repeated attempts to acquire a DHCPv4 lease).</para>
the address is not configured. Note that, the fallback mechanism works only when DHCPv4
client is enabled, that is, it requires <literal>DHCP=yes</literal> or <para>Defaults to <option>no</option> when <varname>Bridge=yes</varname> is set, and
<literal>DHCP=ipv4</literal>. If <varname>Bridge=</varname> is set, defaults to <option>ipv6</option> otherwise.</para>
<literal>no</literal>, and if not, defaults to <literal>ipv6</literal>.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -1664,9 +1662,10 @@ IPv6Token=prefixstable:2002:da8:1::</programlisting></para>
<term><varname>MaxAttempts=</varname></term> <term><varname>MaxAttempts=</varname></term>
<listitem> <listitem>
<para>Specifies how many times the DHCPv4 client configuration should be attempted. Takes a <para>Specifies how many times the DHCPv4 client configuration should be attempted. Takes a
number or <literal>infinity</literal>. Defaults to <literal>infinity</literal>. number or <literal>infinity</literal>. Defaults to <literal>infinity</literal>. Note that the
Note that the time between retries is increased exponentially, so the network will not be time between retries is increased exponentially, up to approximately one per minute, so the
overloaded even if this number is high.</para> network will not be overloaded even if this number is high. The default is suitable in most
circumstances.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -472,12 +472,13 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re
int read_full_stream_full( int read_full_stream_full(
FILE *f, FILE *f,
const char *filename, const char *filename,
uint64_t offset,
size_t size,
ReadFullFileFlags flags, ReadFullFileFlags flags,
char **ret_contents, char **ret_contents,
size_t *ret_size) { size_t *ret_size) {
_cleanup_free_ char *buf = NULL; _cleanup_free_ char *buf = NULL;
struct stat st;
size_t n, n_next, l; size_t n, n_next, l;
int fd, r; int fd, r;
@ -485,32 +486,45 @@ int read_full_stream_full(
assert(ret_contents); assert(ret_contents);
assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)); assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
n_next = LINE_MAX; /* Start size */ if (offset != UINT64_MAX && offset > LONG_MAX)
return -ERANGE;
n_next = size != SIZE_MAX ? size : LINE_MAX; /* Start size */
fd = fileno(f); fd = fileno(f);
if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see
* optimize our buffering */ * fmemopen()), let's optimize our buffering */
struct stat st;
if (fstat(fd, &st) < 0) if (fstat(fd, &st) < 0)
return -errno; return -errno;
if (S_ISREG(st.st_mode)) { if (S_ISREG(st.st_mode)) {
if (size == SIZE_MAX) {
uint64_t rsize =
LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset);
/* Safety check */ /* Safety check */
if (st.st_size > READ_FULL_BYTES_MAX) if (rsize > READ_FULL_BYTES_MAX)
return -E2BIG; return -E2BIG;
/* Start with the right file size. Note that we increase the size /* Start with the right file size. Note that we increase the size to read
* to read here by one, so that the first read attempt already * here by one, so that the first read attempt already makes us notice the
* makes us notice the EOF. */ * EOF. If the reported size of the file is zero, we avoid this logic
if (st.st_size > 0) * however, since quite likely it might be a virtual file in procfs that all
n_next = st.st_size + 1; * report a zero file size. */
if (st.st_size > 0)
n_next = rsize + 1;
}
if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) if (flags & READ_FULL_FILE_WARN_WORLD_READABLE)
(void) warn_file_is_world_accessible(filename, &st, NULL, 0); (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
} }
} }
if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0)
return -errno;
n = l = 0; n = l = 0;
for (;;) { for (;;) {
char *t; char *t;
@ -547,6 +561,11 @@ int read_full_stream_full(
if (feof(f)) if (feof(f))
break; break;
if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */
assert(l == size);
break;
}
assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
/* Safety check */ /* Safety check */
@ -605,15 +624,18 @@ finalize:
int read_full_file_full( int read_full_file_full(
int dir_fd, int dir_fd,
const char *filename, const char *filename,
uint64_t offset,
size_t size,
ReadFullFileFlags flags, ReadFullFileFlags flags,
const char *bind_name, const char *bind_name,
char **contents, size_t *size) { char **ret_contents,
size_t *ret_size) {
_cleanup_fclose_ FILE *f = NULL; _cleanup_fclose_ FILE *f = NULL;
int r; int r;
assert(filename); assert(filename);
assert(contents); assert(ret_contents);
r = xfopenat(dir_fd, filename, "re", 0, &f); r = xfopenat(dir_fd, filename, "re", 0, &f);
if (r < 0) { if (r < 0) {
@ -628,6 +650,10 @@ int read_full_file_full(
if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET))
return -ENXIO; return -ENXIO;
/* Seeking is not supported on AF_UNIX sockets */
if (offset != UINT64_MAX)
return -ESPIPE;
if (dir_fd == AT_FDCWD) if (dir_fd == AT_FDCWD)
r = sockaddr_un_set_path(&sa.un, filename); r = sockaddr_un_set_path(&sa.un, filename);
else { else {
@ -681,7 +707,7 @@ int read_full_file_full(
(void) __fsetlocking(f, FSETLOCKING_BYCALLER); (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
return read_full_stream_full(f, filename, flags, contents, size); return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size);
} }
int executable_is_script(const char *path, char **interpreter) { int executable_is_script(const char *path, char **interpreter) {

View File

@ -60,14 +60,14 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin
int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4);
int read_one_line_file(const char *filename, char **line); int read_one_line_file(const char *filename, char **line);
int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size); int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size);
static inline int read_full_file(const char *filename, char **contents, size_t *size) { static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size); return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size);
} }
int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size);
int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size);
static inline int read_full_stream(FILE *f, char **contents, size_t *size) { static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) {
return read_full_stream_full(f, NULL, 0, contents, size); return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size);
} }
int verify_file(const char *fn, const char *blob, bool accept_extra_nl); int verify_file(const char *fn, const char *blob, bool accept_extra_nl);

View File

@ -28,13 +28,12 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ #define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \
scope type name##_from_string(const char *s) { \ scope type name##_from_string(const char *s) { \
int b; \
if (!s) \ if (!s) \
return -1; \ return -1; \
b = parse_boolean(s); \ int b = parse_boolean(s); \
if (b == 0) \ if (b == 0) \
return (type) 0; \ return (type) 0; \
else if (b > 0) \ if (b > 0) \
return yes; \ return yes; \
return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \
} }

View File

@ -2576,7 +2576,7 @@ static int acquire_credentials(
if (source) if (source)
r = read_full_file_full(AT_FDCWD, source, flags, bindname, &data, &size); r = read_full_file_full(AT_FDCWD, source, UINT64_MAX, SIZE_MAX, flags, bindname, &data, &size);
else else
r = -ENOENT; r = -ENOENT;
if (r == -ENOENT && if (r == -ENOENT &&

View File

@ -1855,6 +1855,12 @@ static void mount_enumerate(Manager *m) {
goto fail; goto fail;
} }
r = sd_event_source_set_ratelimit(m->mount_event_source, 1 * USEC_PER_SEC, 5);
if (r < 0) {
log_error_errno(r, "Failed to enable rate limit for mount events: %m");
goto fail;
}
(void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch");
} }

View File

@ -1,29 +1,18 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <unistd.h>
#include "cryptsetup-keyfile.h" #include "cryptsetup-keyfile.h"
#include "fd-util.h" #include "fileio.h"
#include "format-util.h"
#include "memory-util.h"
#include "path-util.h" #include "path-util.h"
#include "stat-util.h"
#include "strv.h" #include "strv.h"
#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */ int find_key_file(
int load_key_file(
const char *key_file, const char *key_file,
char **search_path, char **search_path,
size_t key_file_size, const char *bindname,
uint64_t key_file_offset,
void **ret_key, void **ret_key,
size_t *ret_key_size) { size_t *ret_key_size) {
_cleanup_(erase_and_freep) char *buffer = NULL; char **i;
_cleanup_free_ char *discovered_path = NULL;
_cleanup_close_ int fd = -1;
ssize_t n;
int r; int r;
assert(key_file); assert(key_file);
@ -31,80 +20,38 @@ int load_key_file(
assert(ret_key_size); assert(ret_key_size);
if (strv_isempty(search_path) || path_is_absolute(key_file)) { if (strv_isempty(search_path) || path_is_absolute(key_file)) {
fd = open(key_file, O_RDONLY|O_CLOEXEC);
if (fd < 0)
return log_error_errno(errno, "Failed to load key file '%s': %m", key_file);
} else {
char **i;
STRV_FOREACH(i, search_path) { r = read_full_file_full(
_cleanup_free_ char *joined; AT_FDCWD, key_file, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
joined = path_join(*i, key_file); bindname,
if (!joined) (char**) ret_key, ret_key_size);
return log_oom();
fd = open(joined, O_RDONLY|O_CLOEXEC);
if (fd >= 0) {
discovered_path = TAKE_PTR(joined);
break;
}
if (errno != ENOENT)
return log_error_errno(errno, "Failed to load key file '%s': %m", joined);
}
if (!discovered_path) {
/* Search path supplied, but file not found, report by returning NULL, but not failing */
*ret_key = NULL;
*ret_key_size = 0;
return 0;
}
assert(fd >= 0);
key_file = discovered_path;
}
if (key_file_size == 0) {
struct stat st;
if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat key file '%s': %m", key_file);
r = stat_verify_regular(&st);
if (r < 0) if (r < 0)
return log_error_errno(r, "Key file is not a regular file: %m"); return log_error_errno(r, "Failed to load key file '%s': %m", key_file);
if (st.st_size == 0) return 1;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
"Key file larger (%s) than allowed maximum size (%s), refusing.",
format_bytes(buf1, sizeof(buf1), st.st_size),
format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
}
if (key_file_offset >= (uint64_t) st.st_size)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
key_file_size = st.st_size - key_file_offset;
} }
buffer = malloc(key_file_size); STRV_FOREACH(i, search_path) {
if (!buffer) _cleanup_free_ char *joined;
return log_oom();
if (key_file_offset > 0) joined = path_join(*i, key_file);
n = pread(fd, buffer, key_file_size, key_file_offset); if (!joined)
else return log_oom();
n = read(fd, buffer, key_file_size);
if (n < 0)
return log_error_errno(errno, "Failed to read key file '%s': %m", key_file);
if (n == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
*ret_key = TAKE_PTR(buffer); r = read_full_file_full(
*ret_key_size = (size_t) n; AT_FDCWD, joined, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
bindname,
(char**) ret_key, ret_key_size);
if (r >= 0)
return 1;
if (r != -ENOENT)
return log_error_errno(r, "Failed to load key file '%s': %m", key_file);
}
return 1; /* Search path supplied, but file not found, report by returning NULL, but not failing */
*ret_key = NULL;
*ret_key_size = 0;
return 0;
} }

View File

@ -4,10 +4,9 @@
#include <inttypes.h> #include <inttypes.h>
#include <sys/types.h> #include <sys/types.h>
int load_key_file( int find_key_file(
const char *key_file, const char *key_file,
char **search_path, char **search_path,
size_t key_file_size, const char *bindname,
uint64_t key_file_offset,
void **ret_key, void **ret_key,
size_t *ret_key_size); size_t *ret_key_size);

View File

@ -10,13 +10,14 @@
#include "alloc-util.h" #include "alloc-util.h"
#include "ask-password-api.h" #include "ask-password-api.h"
#include "cryptsetup-pkcs11.h" #include "cryptsetup-pkcs11.h"
#include "cryptsetup-keyfile.h"
#include "escape.h" #include "escape.h"
#include "fd-util.h" #include "fd-util.h"
#include "fileio.h"
#include "format-util.h" #include "format-util.h"
#include "macro.h" #include "macro.h"
#include "memory-util.h" #include "memory-util.h"
#include "pkcs11-util.h" #include "pkcs11-util.h"
#include "random-util.h"
#include "stat-util.h" #include "stat-util.h"
#include "strv.h" #include "strv.h"
@ -95,6 +96,7 @@ static int pkcs11_callback(
} }
int decrypt_pkcs11_key( int decrypt_pkcs11_key(
const char *volume_name,
const char *friendly_name, const char *friendly_name,
const char *pkcs11_uri, const char *pkcs11_uri,
const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */ const char *key_file, /* We either expect key_file and associated parameters to be set (for file keys) … */
@ -126,7 +128,19 @@ int decrypt_pkcs11_key(
data.free_encrypted_key = false; data.free_encrypted_key = false;
} else { } else {
r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size); _cleanup_free_ char *bindname = NULL;
/* If we read the key via AF_UNIX, make this client recognizable */
if (asprintf(&bindname, "@%" PRIx64"/cryptsetup-pkcs11/%s", random_u64(), volume_name) < 0)
return log_oom();
r = read_full_file_full(
AT_FDCWD, key_file,
key_file_offset == 0 ? UINT64_MAX : key_file_offset,
key_file_size == 0 ? SIZE_MAX : key_file_size,
READ_FULL_FILE_CONNECT_SOCKET,
bindname,
(char**) &data.encrypted_key, &data.encrypted_key_size);
if (r < 0) if (r < 0)
return r; return r;

View File

@ -9,6 +9,7 @@
#if HAVE_P11KIT #if HAVE_P11KIT
int decrypt_pkcs11_key( int decrypt_pkcs11_key(
const char *volume_name,
const char *friendly_name, const char *friendly_name,
const char *pkcs11_uri, const char *pkcs11_uri,
const char *key_file, const char *key_file,
@ -23,6 +24,7 @@ int decrypt_pkcs11_key(
#else #else
static inline int decrypt_pkcs11_key( static inline int decrypt_pkcs11_key(
const char *volume_name,
const char *friendly_name, const char *friendly_name,
const char *pkcs11_uri, const char *pkcs11_uri,
const char *key_file, const char *key_file,

View File

@ -29,6 +29,7 @@
#include "path-util.h" #include "path-util.h"
#include "pkcs11-util.h" #include "pkcs11-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "random-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -550,6 +551,15 @@ static int attach_tcrypt(
return 0; return 0;
} }
static char *make_bindname(const char *volume) {
char *s;
if (asprintf(&s, "@%" PRIx64"/cryptsetup/%s", random_u64(), volume) < 0)
return NULL;
return s;
}
static int attach_luks_or_plain_or_bitlk( static int attach_luks_or_plain_or_bitlk(
struct crypt_device *cd, struct crypt_device *cd,
const char *name, const char *name,
@ -636,6 +646,7 @@ static int attach_luks_or_plain_or_bitlk(
bool processed = false; bool processed = false;
r = decrypt_pkcs11_key( r = decrypt_pkcs11_key(
name,
friendly, friendly,
arg_pkcs11_uri, arg_pkcs11_uri,
key_file, arg_keyfile_size, arg_keyfile_offset, key_file, arg_keyfile_size, arg_keyfile_offset,
@ -735,13 +746,30 @@ static int attach_luks_or_plain_or_bitlk(
return log_error_errno(r, "Failed to activate: %m"); return log_error_errno(r, "Failed to activate: %m");
} else if (key_file) { } else if (key_file) {
r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); _cleanup_(erase_and_freep) char *kfdata = NULL;
if (r == -EPERM) { _cleanup_free_ char *bindname = NULL;
log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); size_t kfsize;
/* If we read the key via AF_UNIX, make this client recognizable */
bindname = make_bindname(name);
if (!bindname)
return log_oom();
r = read_full_file_full(
AT_FDCWD, key_file,
arg_keyfile_offset == 0 ? UINT64_MAX : arg_keyfile_offset,
arg_keyfile_size == 0 ? SIZE_MAX : arg_keyfile_size,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
bindname,
&kfdata, &kfsize);
if (r == -ENOENT) {
log_error_errno(r, "Failed to activate, key file '%s' missing.", key_file);
return -EAGAIN; /* Log actual error, but return EAGAIN */ return -EAGAIN; /* Log actual error, but return EAGAIN */
} }
if (r == -EINVAL) {
log_error_errno(r, "Failed to activate with key file '%s'. (Key file missing?)", key_file); r = crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags);
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file);
return -EAGAIN; /* Log actual error, but return EAGAIN */ return -EAGAIN; /* Log actual error, but return EAGAIN */
} }
if (r < 0) if (r < 0)
@ -880,16 +908,22 @@ static int run(int argc, char *argv[]) {
(void) mlockall(MCL_FUTURE); (void) mlockall(MCL_FUTURE);
if (!key_file) { if (!key_file) {
_cleanup_free_ char *bindname = NULL;
const char *fn; const char *fn;
bindname = make_bindname(argv[2]);
if (!bindname)
return log_oom();
/* If a key file is not explicitly specified, search for a key in a well defined /* If a key file is not explicitly specified, search for a key in a well defined
* search path, and load it. */ * search path, and load it. */
fn = strjoina(argv[2], ".key"); fn = strjoina(argv[2], ".key");
r = load_key_file(fn, r = find_key_file(
STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"), fn,
0, 0, /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */ STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"),
&key_data, &key_data_size); bindname,
&key_data, &key_data_size);
if (r < 0) if (r < 0)
return r; return r;
if (r > 0) if (r > 0)

View File

@ -22,6 +22,7 @@
#include "log.h" #include "log.h"
#include "logs-show.h" #include "logs-show.h"
#include "main-func.h" #include "main-func.h"
#include "memory-util.h"
#include "microhttpd-util.h" #include "microhttpd-util.h"
#include "os-util.h" #include "os-util.h"
#include "parse-util.h" #include "parse-util.h"
@ -37,7 +38,7 @@ static char *arg_cert_pem = NULL;
static char *arg_trust_pem = NULL; static char *arg_trust_pem = NULL;
static const char *arg_directory = NULL; static const char *arg_directory = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_key_pem, freep); STATIC_DESTRUCTOR_REGISTER(arg_key_pem, erase_and_freep);
STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep); STATIC_DESTRUCTOR_REGISTER(arg_cert_pem, freep);
STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep); STATIC_DESTRUCTOR_REGISTER(arg_trust_pem, freep);
@ -896,7 +897,11 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_key_pem) if (arg_key_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Key file specified twice"); "Key file specified twice");
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_key_pem, NULL); r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&arg_key_pem, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read key file: %m"); return log_error_errno(r, "Failed to read key file: %m");
assert(arg_key_pem); assert(arg_key_pem);
@ -906,7 +911,11 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_cert_pem) if (arg_cert_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Certificate file specified twice"); "Certificate file specified twice");
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_cert_pem, NULL); r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&arg_cert_pem, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read certificate file: %m"); return log_error_errno(r, "Failed to read certificate file: %m");
assert(arg_cert_pem); assert(arg_cert_pem);
@ -917,14 +926,18 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_trust_pem) if (arg_trust_pem)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"CA certificate file specified twice"); "CA certificate file specified twice");
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, NULL, &arg_trust_pem, NULL); r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&arg_trust_pem, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read CA certificate file: %m"); return log_error_errno(r, "Failed to read CA certificate file: %m");
assert(arg_trust_pem); assert(arg_trust_pem);
break; break;
#else #else
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --trust is not available."); "Option --trust= is not available.");
#endif #endif
case 'D': case 'D':
arg_directory = optarg; arg_directory = optarg;

View File

@ -13,6 +13,7 @@
#include "journal-remote-write.h" #include "journal-remote-write.h"
#include "journal-remote.h" #include "journal-remote.h"
#include "main-func.h" #include "main-func.h"
#include "memory-util.h"
#include "pretty-print.h" #include "pretty-print.h"
#include "process-util.h" #include "process-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
@ -1077,12 +1078,20 @@ static int parse_argv(int argc, char *argv[]) {
static int load_certificates(char **key, char **cert, char **trust) { static int load_certificates(char **key, char **cert, char **trust) {
int r; int r;
r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, key, NULL); r = read_full_file_full(
AT_FDCWD, arg_key ?: PRIV_KEY_FILE, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
NULL,
key, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read key from file '%s': %m", return log_error_errno(r, "Failed to read key from file '%s': %m",
arg_key ?: PRIV_KEY_FILE); arg_key ?: PRIV_KEY_FILE);
r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, cert, NULL); r = read_full_file_full(
AT_FDCWD, arg_cert ?: CERT_FILE, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
cert, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read certificate from file '%s': %m", return log_error_errno(r, "Failed to read certificate from file '%s': %m",
arg_cert ?: CERT_FILE); arg_cert ?: CERT_FILE);
@ -1090,7 +1099,11 @@ static int load_certificates(char **key, char **cert, char **trust) {
if (arg_trust_all) if (arg_trust_all)
log_info("Certificate checking disabled."); log_info("Certificate checking disabled.");
else { else {
r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, NULL, trust, NULL); r = read_full_file_full(
AT_FDCWD, arg_trust ?: TRUST_FILE, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
trust, NULL);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read CA certificate file '%s': %m", return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
arg_trust ?: TRUST_FILE); arg_trust ?: TRUST_FILE);
@ -1106,7 +1119,8 @@ static int load_certificates(char **key, char **cert, char **trust) {
static int run(int argc, char **argv) { static int run(int argc, char **argv) {
_cleanup_(journal_remote_server_destroy) RemoteServer s = {}; _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
_cleanup_(notify_on_cleanup) const char *notify_message = NULL; _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
_cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL; _cleanup_(erase_and_freep) char *key = NULL;
_cleanup_free_ char *cert = NULL, *trust = NULL;
int r; int r;
log_show_color(true); log_show_color(true);

View File

@ -38,6 +38,9 @@
#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC) #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
#define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE) #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
#define TRANSIENT_FAILURE_ATTEMPTS 3 /* Arbitrary limit: how many attempts are considered enough to report
* transient failure. */
typedef struct sd_dhcp_client_id { typedef struct sd_dhcp_client_id {
uint8_t type; uint8_t type;
union { union {
@ -1187,7 +1190,7 @@ static int client_timeout_resend(
sd_dhcp_client *client = userdata; sd_dhcp_client *client = userdata;
DHCP_CLIENT_DONT_DESTROY(client); DHCP_CLIENT_DONT_DESTROY(client);
usec_t next_timeout = 0; usec_t next_timeout;
uint64_t time_now; uint64_t time_now;
uint32_t time_left; uint32_t time_left;
int r; int r;
@ -1203,17 +1206,14 @@ static int client_timeout_resend(
switch (client->state) { switch (client->state) {
case DHCP_STATE_RENEWING: case DHCP_STATE_RENEWING:
time_left = (client->lease->t2 - client->lease->t1) / 2; time_left = (client->lease->t2 - client->lease->t1) / 2;
if (time_left < 60) if (time_left < 60)
time_left = 60; time_left = 60;
next_timeout = time_now + time_left * USEC_PER_SEC; next_timeout = time_now + time_left * USEC_PER_SEC;
break; break;
case DHCP_STATE_REBINDING: case DHCP_STATE_REBINDING:
time_left = (client->lease->lifetime - client->lease->t2) / 2; time_left = (client->lease->lifetime - client->lease->t2) / 2;
if (time_left < 60) if (time_left < 60)
time_left = 60; time_left = 60;
@ -1230,24 +1230,20 @@ static int client_timeout_resend(
r = client_start(client); r = client_start(client);
if (r < 0) if (r < 0)
goto error; goto error;
else {
log_dhcp_client(client, "REBOOTED"); log_dhcp_client(client, "REBOOTED");
return 0; return 0;
}
case DHCP_STATE_INIT: case DHCP_STATE_INIT:
case DHCP_STATE_INIT_REBOOT: case DHCP_STATE_INIT_REBOOT:
case DHCP_STATE_SELECTING: case DHCP_STATE_SELECTING:
case DHCP_STATE_REQUESTING: case DHCP_STATE_REQUESTING:
case DHCP_STATE_BOUND: case DHCP_STATE_BOUND:
if (client->attempt >= client->max_attempts)
if (client->attempt < client->max_attempts)
client->attempt++;
else
goto error; goto error;
client->attempt++;
next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC; next_timeout = time_now + ((UINT64_C(1) << MIN(client->attempt, (uint64_t) 6)) - 1) * USEC_PER_SEC;
break; break;
case DHCP_STATE_STOPPED: case DHCP_STATE_STOPPED:
@ -1295,12 +1291,10 @@ static int client_timeout_resend(
client->state = DHCP_STATE_REBOOTING; client->state = DHCP_STATE_REBOOTING;
client->request_sent = time_now; client->request_sent = time_now;
break; break;
case DHCP_STATE_REBOOTING: case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND: case DHCP_STATE_BOUND:
break; break;
case DHCP_STATE_STOPPED: case DHCP_STATE_STOPPED:
@ -1308,6 +1302,9 @@ static int client_timeout_resend(
goto error; goto error;
} }
if (client->attempt >= TRANSIENT_FAILURE_ATTEMPTS)
client_notify(client, SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE);
return 0; return 0;
error: error:

View File

@ -736,3 +736,10 @@ global:
sd_device_has_current_tag; sd_device_has_current_tag;
sd_device_set_sysattr_valuef; sd_device_set_sysattr_valuef;
} LIBSYSTEMD_246; } LIBSYSTEMD_246;
LIBSYSTEMD_248 {
global:
sd_event_source_set_ratelimit;
sd_event_source_get_ratelimit;
sd_event_source_is_ratelimited;
} LIBSYSTEMD_246;

View File

@ -11,6 +11,7 @@
#include "hashmap.h" #include "hashmap.h"
#include "list.h" #include "list.h"
#include "prioq.h" #include "prioq.h"
#include "ratelimit.h"
typedef enum EventSourceType { typedef enum EventSourceType {
SOURCE_IO, SOURCE_IO,
@ -61,6 +62,7 @@ struct sd_event_source {
bool dispatching:1; bool dispatching:1;
bool floating:1; bool floating:1;
bool exit_on_failure:1; bool exit_on_failure:1;
bool ratelimited:1;
int64_t priority; int64_t priority;
unsigned pending_index; unsigned pending_index;
@ -72,6 +74,13 @@ struct sd_event_source {
LIST_FIELDS(sd_event_source, sources); LIST_FIELDS(sd_event_source, sources);
RateLimit rate_limit;
/* These are primarily fields relevant for time event sources, but since any event source can
* effectively become one when rate-limited, this is part of the common fields. */
unsigned earliest_index;
unsigned latest_index;
union { union {
struct { struct {
sd_event_io_handler_t callback; sd_event_io_handler_t callback;
@ -84,8 +93,6 @@ struct sd_event_source {
struct { struct {
sd_event_time_handler_t callback; sd_event_time_handler_t callback;
usec_t next, accuracy; usec_t next, accuracy;
unsigned earliest_index;
unsigned latest_index;
} time; } time;
struct { struct {
sd_event_signal_handler_t callback; sd_event_signal_handler_t callback;

View File

@ -37,6 +37,16 @@ static bool EVENT_SOURCE_WATCH_PIDFD(sd_event_source *s) {
s->child.options == WEXITED; s->child.options == WEXITED;
} }
static bool event_source_is_online(sd_event_source *s) {
assert(s);
return s->enabled != SD_EVENT_OFF && !s->ratelimited;
}
static bool event_source_is_offline(sd_event_source *s) {
assert(s);
return s->enabled == SD_EVENT_OFF || s->ratelimited;
}
static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = { static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
[SOURCE_IO] = "io", [SOURCE_IO] = "io",
[SOURCE_TIME_REALTIME] = "realtime", [SOURCE_TIME_REALTIME] = "realtime",
@ -55,7 +65,25 @@ static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX]
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int); DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
#define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) #define EVENT_SOURCE_IS_TIME(t) \
IN_SET((t), \
SOURCE_TIME_REALTIME, \
SOURCE_TIME_BOOTTIME, \
SOURCE_TIME_MONOTONIC, \
SOURCE_TIME_REALTIME_ALARM, \
SOURCE_TIME_BOOTTIME_ALARM)
#define EVENT_SOURCE_CAN_RATE_LIMIT(t) \
IN_SET((t), \
SOURCE_IO, \
SOURCE_TIME_REALTIME, \
SOURCE_TIME_BOOTTIME, \
SOURCE_TIME_MONOTONIC, \
SOURCE_TIME_REALTIME_ALARM, \
SOURCE_TIME_BOOTTIME_ALARM, \
SOURCE_SIGNAL, \
SOURCE_DEFER, \
SOURCE_INOTIFY)
struct sd_event { struct sd_event {
unsigned n_ref; unsigned n_ref;
@ -81,7 +109,7 @@ struct sd_event {
Hashmap *signal_data; /* indexed by priority */ Hashmap *signal_data; /* indexed by priority */
Hashmap *child_sources; Hashmap *child_sources;
unsigned n_enabled_child_sources; unsigned n_online_child_sources;
Set *post_sources; Set *post_sources;
@ -120,7 +148,7 @@ struct sd_event {
LIST_HEAD(sd_event_source, sources); LIST_HEAD(sd_event_source, sources);
usec_t last_run, last_log; usec_t last_run_usec, last_log_usec;
unsigned delays[sizeof(usec_t) * 8]; unsigned delays[sizeof(usec_t) * 8];
}; };
@ -146,6 +174,11 @@ static int pending_prioq_compare(const void *a, const void *b) {
if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
return 1; return 1;
/* Non rate-limited ones first. */
r = CMP(!!x->ratelimited, !!y->ratelimited);
if (r != 0)
return r;
/* Lower priority values first */ /* Lower priority values first */
r = CMP(x->priority, y->priority); r = CMP(x->priority, y->priority);
if (r != 0) if (r != 0)
@ -168,6 +201,11 @@ static int prepare_prioq_compare(const void *a, const void *b) {
if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF) if (x->enabled == SD_EVENT_OFF && y->enabled != SD_EVENT_OFF)
return 1; return 1;
/* Non rate-limited ones first. */
r = CMP(!!x->ratelimited, !!y->ratelimited);
if (r != 0)
return r;
/* Move most recently prepared ones last, so that we can stop /* Move most recently prepared ones last, so that we can stop
* preparing as soon as we hit one that has already been * preparing as soon as we hit one that has already been
* prepared in the current iteration */ * prepared in the current iteration */
@ -179,12 +217,30 @@ static int prepare_prioq_compare(const void *a, const void *b) {
return CMP(x->priority, y->priority); return CMP(x->priority, y->priority);
} }
static usec_t time_event_source_next(const sd_event_source *s) {
assert(s);
/* We have two kinds of event sources that have elapsation times associated with them: the actual
* time based ones and the ones for which a ratelimit can be in effect (where we want to be notified
* once the ratelimit time window ends). Let's return the next elapsing time depending on what we are
* looking at here. */
if (s->ratelimited) { /* If rate-limited the next elapsation is when the ratelimit time window ends */
assert(s->rate_limit.begin != 0);
assert(s->rate_limit.interval != 0);
return usec_add(s->rate_limit.begin, s->rate_limit.interval);
}
/* Otherwise this must be a time event source, if not ratelimited */
if (EVENT_SOURCE_IS_TIME(s->type))
return s->time.next;
return USEC_INFINITY;
}
static int earliest_time_prioq_compare(const void *a, const void *b) { static int earliest_time_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b; const sd_event_source *x = a, *y = b;
assert(EVENT_SOURCE_IS_TIME(x->type));
assert(x->type == y->type);
/* Enabled ones first */ /* Enabled ones first */
if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
return -1; return -1;
@ -198,19 +254,30 @@ static int earliest_time_prioq_compare(const void *a, const void *b) {
return 1; return 1;
/* Order by time */ /* Order by time */
return CMP(x->time.next, y->time.next); return CMP(time_event_source_next(x), time_event_source_next(y));
} }
static usec_t time_event_source_latest(const sd_event_source *s) { static usec_t time_event_source_latest(const sd_event_source *s) {
return usec_add(s->time.next, s->time.accuracy); assert(s);
if (s->ratelimited) { /* For ratelimited stuff the earliest and the latest time shall actually be the
* same, as we should avoid adding additional inaccuracy on an inaccuracy time
* window */
assert(s->rate_limit.begin != 0);
assert(s->rate_limit.interval != 0);
return usec_add(s->rate_limit.begin, s->rate_limit.interval);
}
/* Must be a time event source, if not ratelimited */
if (EVENT_SOURCE_IS_TIME(s->type))
return usec_add(s->time.next, s->time.accuracy);
return USEC_INFINITY;
} }
static int latest_time_prioq_compare(const void *a, const void *b) { static int latest_time_prioq_compare(const void *a, const void *b) {
const sd_event_source *x = a, *y = b; const sd_event_source *x = a, *y = b;
assert(EVENT_SOURCE_IS_TIME(x->type));
assert(x->type == y->type);
/* Enabled ones first */ /* Enabled ones first */
if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF) if (x->enabled != SD_EVENT_OFF && y->enabled == SD_EVENT_OFF)
return -1; return -1;
@ -380,7 +447,7 @@ static void source_io_unregister(sd_event_source *s) {
return; return;
if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL) < 0) if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL) < 0)
log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m",
strna(s->description), event_source_type_to_string(s->type)); strna(s->description), event_source_type_to_string(s->type));
s->io.registered = false; s->io.registered = false;
@ -422,7 +489,7 @@ static void source_child_pidfd_unregister(sd_event_source *s) {
if (EVENT_SOURCE_WATCH_PIDFD(s)) if (EVENT_SOURCE_WATCH_PIDFD(s))
if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->child.pidfd, NULL) < 0) if (epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->child.pidfd, NULL) < 0)
log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll: %m", log_debug_errno(errno, "Failed to remove source %s (type %s) from epoll, ignoring: %m",
strna(s->description), event_source_type_to_string(s->type)); strna(s->description), event_source_type_to_string(s->type));
s->child.registered = false; s->child.registered = false;
@ -661,12 +728,12 @@ static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig)
* and possibly drop the signalfd for it. */ * and possibly drop the signalfd for it. */
if (sig == SIGCHLD && if (sig == SIGCHLD &&
e->n_enabled_child_sources > 0) e->n_online_child_sources > 0)
return; return;
if (e->signal_sources && if (e->signal_sources &&
e->signal_sources[sig] && e->signal_sources[sig] &&
e->signal_sources[sig]->enabled != SD_EVENT_OFF) event_source_is_online(e->signal_sources[sig]))
return; return;
/* /*
@ -713,13 +780,32 @@ static void event_source_time_prioq_reshuffle(sd_event_source *s) {
struct clock_data *d; struct clock_data *d;
assert(s); assert(s);
assert(EVENT_SOURCE_IS_TIME(s->type));
/* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy, /* Called whenever the event source's timer ordering properties changed, i.e. time, accuracy,
* pending, enable state. Makes sure the two prioq's are ordered properly again. */ * pending, enable state. Makes sure the two prioq's are ordered properly again. */
assert_se(d = event_get_clock_data(s->event, s->type));
prioq_reshuffle(d->earliest, s, &s->time.earliest_index); if (s->ratelimited)
prioq_reshuffle(d->latest, s, &s->time.latest_index); d = &s->event->monotonic;
else {
assert(EVENT_SOURCE_IS_TIME(s->type));
assert_se(d = event_get_clock_data(s->event, s->type));
}
prioq_reshuffle(d->earliest, s, &s->earliest_index);
prioq_reshuffle(d->latest, s, &s->latest_index);
d->needs_rearm = true;
}
static void event_source_time_prioq_remove(
sd_event_source *s,
struct clock_data *d) {
assert(s);
assert(d);
prioq_remove(d->earliest, s, &s->earliest_index);
prioq_remove(d->latest, s, &s->latest_index);
s->earliest_index = s->latest_index = PRIOQ_IDX_NULL;
d->needs_rearm = true; d->needs_rearm = true;
} }
@ -745,17 +831,18 @@ static void source_disconnect(sd_event_source *s) {
case SOURCE_TIME_BOOTTIME: case SOURCE_TIME_BOOTTIME:
case SOURCE_TIME_MONOTONIC: case SOURCE_TIME_MONOTONIC:
case SOURCE_TIME_REALTIME_ALARM: case SOURCE_TIME_REALTIME_ALARM:
case SOURCE_TIME_BOOTTIME_ALARM: { case SOURCE_TIME_BOOTTIME_ALARM:
struct clock_data *d; /* Only remove this event source from the time event source here if it is not ratelimited. If
* it is ratelimited, we'll remove it below, separately. Why? Because the clock used might
* differ: ratelimiting always uses CLOCK_MONOTONIC, but timer events might use any clock */
d = event_get_clock_data(s->event, s->type); if (!s->ratelimited) {
assert(d); struct clock_data *d;
assert_se(d = event_get_clock_data(s->event, s->type));
event_source_time_prioq_remove(s, d);
}
prioq_remove(d->earliest, s, &s->time.earliest_index);
prioq_remove(d->latest, s, &s->time.latest_index);
d->needs_rearm = true;
break; break;
}
case SOURCE_SIGNAL: case SOURCE_SIGNAL:
if (s->signal.sig > 0) { if (s->signal.sig > 0) {
@ -770,9 +857,9 @@ static void source_disconnect(sd_event_source *s) {
case SOURCE_CHILD: case SOURCE_CHILD:
if (s->child.pid > 0) { if (s->child.pid > 0) {
if (s->enabled != SD_EVENT_OFF) { if (event_source_is_online(s)) {
assert(s->event->n_enabled_child_sources > 0); assert(s->event->n_online_child_sources > 0);
s->event->n_enabled_child_sources--; s->event->n_online_child_sources--;
} }
(void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid)); (void) hashmap_remove(s->event->child_sources, PID_TO_PTR(s->child.pid));
@ -842,6 +929,9 @@ static void source_disconnect(sd_event_source *s) {
if (s->prepare) if (s->prepare)
prioq_remove(s->event->prepare, s, &s->prepare_index); prioq_remove(s->event->prepare, s, &s->prepare_index);
if (s->ratelimited)
event_source_time_prioq_remove(s, &s->event->monotonic);
event = TAKE_PTR(s->event); event = TAKE_PTR(s->event);
LIST_REMOVE(sources, event->sources, s); LIST_REMOVE(sources, event->sources, s);
event->n_sources--; event->n_sources--;
@ -1088,6 +1178,52 @@ static int time_exit_callback(sd_event_source *s, uint64_t usec, void *userdata)
return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata)); return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
} }
static int setup_clock_data(sd_event *e, struct clock_data *d, clockid_t clock) {
int r;
assert(d);
if (d->fd < 0) {
r = event_setup_timer_fd(e, d, clock);
if (r < 0)
return r;
}
r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare);
if (r < 0)
return r;
r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
if (r < 0)
return r;
return 0;
}
static int event_source_time_prioq_put(
sd_event_source *s,
struct clock_data *d) {
int r;
assert(s);
assert(d);
r = prioq_put(d->earliest, s, &s->earliest_index);
if (r < 0)
return r;
r = prioq_put(d->latest, s, &s->latest_index);
if (r < 0) {
assert_se(prioq_remove(d->earliest, s, &s->earliest_index) > 0);
s->earliest_index = PRIOQ_IDX_NULL;
return r;
}
d->needs_rearm = true;
return 0;
}
_public_ int sd_event_add_time( _public_ int sd_event_add_time(
sd_event *e, sd_event *e,
sd_event_source **ret, sd_event_source **ret,
@ -1118,23 +1254,12 @@ _public_ int sd_event_add_time(
if (!callback) if (!callback)
callback = time_exit_callback; callback = time_exit_callback;
d = event_get_clock_data(e, type); assert_se(d = event_get_clock_data(e, type));
assert(d);
r = prioq_ensure_allocated(&d->earliest, earliest_time_prioq_compare); r = setup_clock_data(e, d, clock);
if (r < 0) if (r < 0)
return r; return r;
r = prioq_ensure_allocated(&d->latest, latest_time_prioq_compare);
if (r < 0)
return r;
if (d->fd < 0) {
r = event_setup_timer_fd(e, d, clock);
if (r < 0)
return r;
}
s = source_new(e, !ret, type); s = source_new(e, !ret, type);
if (!s) if (!s)
return -ENOMEM; return -ENOMEM;
@ -1142,17 +1267,11 @@ _public_ int sd_event_add_time(
s->time.next = usec; s->time.next = usec;
s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy; s->time.accuracy = accuracy == 0 ? DEFAULT_ACCURACY_USEC : accuracy;
s->time.callback = callback; s->time.callback = callback;
s->time.earliest_index = s->time.latest_index = PRIOQ_IDX_NULL; s->earliest_index = s->latest_index = PRIOQ_IDX_NULL;
s->userdata = userdata; s->userdata = userdata;
s->enabled = SD_EVENT_ONESHOT; s->enabled = SD_EVENT_ONESHOT;
d->needs_rearm = true; r = event_source_time_prioq_put(s, d);
r = prioq_put(d->earliest, s, &s->time.earliest_index);
if (r < 0)
return r;
r = prioq_put(d->latest, s, &s->time.latest_index);
if (r < 0) if (r < 0)
return r; return r;
@ -1285,7 +1404,7 @@ _public_ int sd_event_add_child(
if (!callback) if (!callback)
callback = child_exit_callback; callback = child_exit_callback;
if (e->n_enabled_child_sources == 0) { if (e->n_online_child_sources == 0) {
/* Caller must block SIGCHLD before using us to watch children, even if pidfd is available, /* Caller must block SIGCHLD before using us to watch children, even if pidfd is available,
* for compatibility with pre-pidfd and because we don't want the reap the child processes * for compatibility with pre-pidfd and because we don't want the reap the child processes
* ourselves, i.e. call waitid(), and don't want Linux' default internal logic for that to * ourselves, i.e. call waitid(), and don't want Linux' default internal logic for that to
@ -1350,7 +1469,7 @@ _public_ int sd_event_add_child(
e->need_process_child = true; e->need_process_child = true;
} }
e->n_enabled_child_sources++; e->n_online_child_sources++;
if (ret) if (ret)
*ret = s; *ret = s;
@ -1382,7 +1501,7 @@ _public_ int sd_event_add_child_pidfd(
if (!callback) if (!callback)
callback = child_exit_callback; callback = child_exit_callback;
if (e->n_enabled_child_sources == 0) { if (e->n_online_child_sources == 0) {
r = signal_is_blocked(SIGCHLD); r = signal_is_blocked(SIGCHLD);
if (r < 0) if (r < 0)
return r; return r;
@ -1432,7 +1551,7 @@ _public_ int sd_event_add_child_pidfd(
e->need_process_child = true; e->need_process_child = true;
} }
e->n_enabled_child_sources++; e->n_online_child_sources++;
if (ret) if (ret)
*ret = s; *ret = s;
@ -2018,7 +2137,7 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
if (s->io.fd == fd) if (s->io.fd == fd)
return 0; return 0;
if (s->enabled == SD_EVENT_OFF) { if (event_source_is_offline(s)) {
s->io.fd = fd; s->io.fd = fd;
s->io.registered = false; s->io.registered = false;
} else { } else {
@ -2085,7 +2204,7 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events)
if (r < 0) if (r < 0)
return r; return r;
if (s->enabled != SD_EVENT_OFF) { if (event_source_is_online(s)) {
r = source_io_register(s, s->enabled, events); r = source_io_register(s, s->enabled, events);
if (r < 0) if (r < 0)
return r; return r;
@ -2188,7 +2307,7 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
event_gc_inode_data(s->event, old_inode_data); event_gc_inode_data(s->event, old_inode_data);
} else if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { } else if (s->type == SOURCE_SIGNAL && event_source_is_online(s)) {
struct signal_data *old, *d; struct signal_data *old, *d;
/* Move us from the signalfd belonging to the old /* Move us from the signalfd belonging to the old
@ -2225,29 +2344,39 @@ fail:
return r; return r;
} }
_public_ int sd_event_source_get_enabled(sd_event_source *s, int *m) { _public_ int sd_event_source_get_enabled(sd_event_source *s, int *ret) {
assert_return(s, -EINVAL); assert_return(s, -EINVAL);
assert_return(!event_pid_changed(s->event), -ECHILD); assert_return(!event_pid_changed(s->event), -ECHILD);
if (m) if (ret)
*m = s->enabled; *ret = s->enabled;
return s->enabled != SD_EVENT_OFF; return s->enabled != SD_EVENT_OFF;
} }
static int event_source_disable(sd_event_source *s) { static int event_source_offline(
sd_event_source *s,
int enabled,
bool ratelimited) {
bool was_offline;
int r; int r;
assert(s); assert(s);
assert(s->enabled != SD_EVENT_OFF); assert(enabled == SD_EVENT_OFF || ratelimited);
/* Unset the pending flag when this event source is disabled */ /* Unset the pending flag when this event source is disabled */
if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { if (s->enabled != SD_EVENT_OFF &&
enabled == SD_EVENT_OFF &&
!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
r = source_set_pending(s, false); r = source_set_pending(s, false);
if (r < 0) if (r < 0)
return r; return r;
} }
s->enabled = SD_EVENT_OFF; was_offline = event_source_is_offline(s);
s->enabled = enabled;
s->ratelimited = ratelimited;
switch (s->type) { switch (s->type) {
@ -2268,8 +2397,10 @@ static int event_source_disable(sd_event_source *s) {
break; break;
case SOURCE_CHILD: case SOURCE_CHILD:
assert(s->event->n_enabled_child_sources > 0); if (!was_offline) {
s->event->n_enabled_child_sources--; assert(s->event->n_online_child_sources > 0);
s->event->n_online_child_sources--;
}
if (EVENT_SOURCE_WATCH_PIDFD(s)) if (EVENT_SOURCE_WATCH_PIDFD(s))
source_child_pidfd_unregister(s); source_child_pidfd_unregister(s);
@ -2290,26 +2421,42 @@ static int event_source_disable(sd_event_source *s) {
assert_not_reached("Wut? I shouldn't exist."); assert_not_reached("Wut? I shouldn't exist.");
} }
return 0; return 1;
} }
static int event_source_enable(sd_event_source *s, int enable) { static int event_source_online(
sd_event_source *s,
int enabled,
bool ratelimited) {
bool was_online;
int r; int r;
assert(s); assert(s);
assert(IN_SET(enable, SD_EVENT_ON, SD_EVENT_ONESHOT)); assert(enabled != SD_EVENT_OFF || !ratelimited);
assert(s->enabled == SD_EVENT_OFF);
/* Unset the pending flag when this event source is enabled */ /* Unset the pending flag when this event source is enabled */
if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { if (s->enabled == SD_EVENT_OFF &&
enabled != SD_EVENT_OFF &&
!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
r = source_set_pending(s, false); r = source_set_pending(s, false);
if (r < 0) if (r < 0)
return r; return r;
} }
/* Are we really ready for onlining? */
if (enabled == SD_EVENT_OFF || ratelimited) {
/* Nope, we are not ready for onlining, then just update the precise state and exit */
s->enabled = enabled;
s->ratelimited = ratelimited;
return 0;
}
was_online = event_source_is_online(s);
switch (s->type) { switch (s->type) {
case SOURCE_IO: case SOURCE_IO:
r = source_io_register(s, enable, s->io.events); r = source_io_register(s, enabled, s->io.events);
if (r < 0) if (r < 0)
return r; return r;
break; break;
@ -2327,7 +2474,7 @@ static int event_source_enable(sd_event_source *s, int enable) {
if (EVENT_SOURCE_WATCH_PIDFD(s)) { if (EVENT_SOURCE_WATCH_PIDFD(s)) {
/* yes, we have pidfd */ /* yes, we have pidfd */
r = source_child_pidfd_register(s, enable); r = source_child_pidfd_register(s, enabled);
if (r < 0) if (r < 0)
return r; return r;
} else { } else {
@ -2340,8 +2487,8 @@ static int event_source_enable(sd_event_source *s, int enable) {
} }
} }
s->event->n_enabled_child_sources++; if (!was_online)
s->event->n_online_child_sources++;
break; break;
case SOURCE_TIME_REALTIME: case SOURCE_TIME_REALTIME:
@ -2359,7 +2506,8 @@ static int event_source_enable(sd_event_source *s, int enable) {
assert_not_reached("Wut? I shouldn't exist."); assert_not_reached("Wut? I shouldn't exist.");
} }
s->enabled = enable; s->enabled = enabled;
s->ratelimited = ratelimited;
/* Non-failing operations below */ /* Non-failing operations below */
switch (s->type) { switch (s->type) {
@ -2379,7 +2527,7 @@ static int event_source_enable(sd_event_source *s, int enable) {
break; break;
} }
return 0; return 1;
} }
_public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
@ -2397,7 +2545,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
return 0; return 0;
if (m == SD_EVENT_OFF) if (m == SD_EVENT_OFF)
r = event_source_disable(s); r = event_source_offline(s, m, s->ratelimited);
else { else {
if (s->enabled != SD_EVENT_OFF) { if (s->enabled != SD_EVENT_OFF) {
/* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the /* Switching from "on" to "oneshot" or back? If that's the case, we can take a shortcut, the
@ -2406,7 +2554,7 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
return 0; return 0;
} }
r = event_source_enable(s, m); r = event_source_online(s, m, s->ratelimited);
} }
if (r < 0) if (r < 0)
return r; return r;
@ -2663,6 +2811,96 @@ _public_ void *sd_event_source_set_userdata(sd_event_source *s, void *userdata)
return ret; return ret;
} }
static int event_source_enter_ratelimited(sd_event_source *s) {
int r;
assert(s);
/* When an event source becomes ratelimited, we place it in the CLOCK_MONOTONIC priority queue, with
* the end of the rate limit time window, much as if it was a timer event source. */
if (s->ratelimited)
return 0; /* Already ratelimited, this is a NOP hence */
/* Make sure we can install a CLOCK_MONOTONIC event further down. */
r = setup_clock_data(s->event, &s->event->monotonic, CLOCK_MONOTONIC);
if (r < 0)
return r;
/* Timer event sources are already using the earliest/latest queues for the timer scheduling. Let's
* first remove them from the prioq appropriate for their own clock, so that we can use the prioq
* fields of the event source then for adding it to the CLOCK_MONOTONIC prioq instead. */
if (EVENT_SOURCE_IS_TIME(s->type))
event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type));
/* Now, let's add the event source to the monotonic clock instead */
r = event_source_time_prioq_put(s, &s->event->monotonic);
if (r < 0)
goto fail;
/* And let's take the event source officially offline */
r = event_source_offline(s, s->enabled, /* ratelimited= */ true);
if (r < 0) {
event_source_time_prioq_remove(s, &s->event->monotonic);
goto fail;
}
event_source_pp_prioq_reshuffle(s);
log_debug("Event source %p (%s) entered rate limit state.", s, strna(s->description));
return 0;
fail:
/* Reinstall time event sources in the priority queue as before. This shouldn't fail, since the queue
* space for it should already be allocated. */
if (EVENT_SOURCE_IS_TIME(s->type))
assert_se(event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type)) >= 0);
return r;
}
static int event_source_leave_ratelimit(sd_event_source *s) {
int r;
assert(s);
if (!s->ratelimited)
return 0;
/* Let's take the event source out of the monotonic prioq first. */
event_source_time_prioq_remove(s, &s->event->monotonic);
/* Let's then add the event source to its native clock prioq again — if this is a timer event source */
if (EVENT_SOURCE_IS_TIME(s->type)) {
r = event_source_time_prioq_put(s, event_get_clock_data(s->event, s->type));
if (r < 0)
goto fail;
}
/* Let's try to take it online again. */
r = event_source_online(s, s->enabled, /* ratelimited= */ false);
if (r < 0) {
/* Do something roughly sensible when this failed: undo the two prioq ops above */
if (EVENT_SOURCE_IS_TIME(s->type))
event_source_time_prioq_remove(s, event_get_clock_data(s->event, s->type));
goto fail;
}
event_source_pp_prioq_reshuffle(s);
ratelimit_reset(&s->rate_limit);
log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
return 0;
fail:
/* Do something somewhat reasonable when we cannot move an event sources out of ratelimited mode:
* simply put it back in it, maybe we can then process it more successfully next iteration. */
assert_se(event_source_time_prioq_put(s, &s->event->monotonic) >= 0);
return r;
}
static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) { static usec_t sleep_between(sd_event *e, usec_t a, usec_t b) {
usec_t c; usec_t c;
assert(e); assert(e);
@ -2760,7 +2998,7 @@ static int event_arm_timer(
d->needs_rearm = false; d->needs_rearm = false;
a = prioq_peek(d->earliest); a = prioq_peek(d->earliest);
if (!a || a->enabled == SD_EVENT_OFF || a->time.next == USEC_INFINITY) { if (!a || a->enabled == SD_EVENT_OFF || time_event_source_next(a) == USEC_INFINITY) {
if (d->fd < 0) if (d->fd < 0)
return 0; return 0;
@ -2779,7 +3017,7 @@ static int event_arm_timer(
b = prioq_peek(d->latest); b = prioq_peek(d->latest);
assert_se(b && b->enabled != SD_EVENT_OFF); assert_se(b && b->enabled != SD_EVENT_OFF);
t = sleep_between(e, a->time.next, time_event_source_latest(b)); t = sleep_between(e, time_event_source_next(a), time_event_source_latest(b));
if (d->next == t) if (d->next == t)
return 0; return 0;
@ -2857,10 +3095,22 @@ static int process_timer(
for (;;) { for (;;) {
s = prioq_peek(d->earliest); s = prioq_peek(d->earliest);
if (!s || if (!s || time_event_source_next(s) > n)
s->time.next > n || break;
s->enabled == SD_EVENT_OFF ||
s->pending) if (s->ratelimited) {
/* This is an event sources whose ratelimit window has ended. Let's turn it on
* again. */
assert(s->ratelimited);
r = event_source_leave_ratelimit(s);
if (r < 0)
return r;
continue;
}
if (s->enabled == SD_EVENT_OFF || s->pending)
break; break;
r = source_set_pending(s, true); r = source_set_pending(s, true);
@ -2905,7 +3155,7 @@ static int process_child(sd_event *e) {
if (s->pending) if (s->pending)
continue; continue;
if (s->enabled == SD_EVENT_OFF) if (event_source_is_offline(s))
continue; continue;
if (s->child.exited) if (s->child.exited)
@ -2952,7 +3202,7 @@ static int process_pidfd(sd_event *e, sd_event_source *s, uint32_t revents) {
if (s->pending) if (s->pending)
return 0; return 0;
if (s->enabled == SD_EVENT_OFF) if (event_source_is_offline(s))
return 0; return 0;
if (!EVENT_SOURCE_WATCH_PIDFD(s)) if (!EVENT_SOURCE_WATCH_PIDFD(s))
@ -3112,7 +3362,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
if (s->enabled == SD_EVENT_OFF) if (event_source_is_offline(s))
continue; continue;
r = source_set_pending(s, true); r = source_set_pending(s, true);
@ -3148,7 +3398,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
* sources if IN_IGNORED or IN_UNMOUNT is set. */ * sources if IN_IGNORED or IN_UNMOUNT is set. */
LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
if (s->enabled == SD_EVENT_OFF) if (event_source_is_offline(s))
continue; continue;
if ((d->buffer.ev.mask & (IN_IGNORED|IN_UNMOUNT)) == 0 && if ((d->buffer.ev.mask & (IN_IGNORED|IN_UNMOUNT)) == 0 &&
@ -3202,6 +3452,16 @@ static int source_dispatch(sd_event_source *s) {
* callback might have invalidated/disconnected the event source. */ * callback might have invalidated/disconnected the event source. */
saved_event = sd_event_ref(s->event); saved_event = sd_event_ref(s->event);
/* Check if we hit the ratelimit for this event source, if so, let's disable it. */
assert(!s->ratelimited);
if (!ratelimit_below(&s->rate_limit)) {
r = event_source_enter_ratelimited(s);
if (r < 0)
return r;
return 1;
}
if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) { if (!IN_SET(s->type, SOURCE_DEFER, SOURCE_EXIT)) {
r = source_set_pending(s, false); r = source_set_pending(s, false);
if (r < 0) if (r < 0)
@ -3215,7 +3475,7 @@ static int source_dispatch(sd_event_source *s) {
* post sources as pending */ * post sources as pending */
SET_FOREACH(z, s->event->post_sources) { SET_FOREACH(z, s->event->post_sources) {
if (z->enabled == SD_EVENT_OFF) if (event_source_is_offline(z))
continue; continue;
r = source_set_pending(z, true); r = source_set_pending(z, true);
@ -3335,7 +3595,7 @@ static int event_prepare(sd_event *e) {
sd_event_source *s; sd_event_source *s;
s = prioq_peek(e->prepare); s = prioq_peek(e->prepare);
if (!s || s->prepare_iteration == e->iteration || s->enabled == SD_EVENT_OFF) if (!s || s->prepare_iteration == e->iteration || event_source_is_offline(s))
break; break;
s->prepare_iteration = e->iteration; s->prepare_iteration = e->iteration;
@ -3370,18 +3630,17 @@ static int event_prepare(sd_event *e) {
static int dispatch_exit(sd_event *e) { static int dispatch_exit(sd_event *e) {
sd_event_source *p; sd_event_source *p;
_cleanup_(sd_event_unrefp) sd_event *ref = NULL;
int r; int r;
assert(e); assert(e);
p = prioq_peek(e->exit); p = prioq_peek(e->exit);
if (!p || p->enabled == SD_EVENT_OFF) { if (!p || event_source_is_offline(p)) {
e->state = SD_EVENT_FINISHED; e->state = SD_EVENT_FINISHED;
return 0; return 0;
} }
ref = sd_event_ref(e); _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
e->iteration++; e->iteration++;
e->state = SD_EVENT_EXITING; e->state = SD_EVENT_EXITING;
r = source_dispatch(p); r = source_dispatch(p);
@ -3398,7 +3657,7 @@ static sd_event_source* event_next_pending(sd_event *e) {
if (!p) if (!p)
return NULL; return NULL;
if (p->enabled == SD_EVENT_OFF) if (event_source_is_offline(p))
return NULL; return NULL;
return p; return p;
@ -3477,6 +3736,9 @@ _public_ int sd_event_prepare(sd_event *e) {
* syscalls */ * syscalls */
assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO); assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
if (e->exit_requested) if (e->exit_requested)
goto pending; goto pending;
@ -3682,9 +3944,8 @@ _public_ int sd_event_dispatch(sd_event *e) {
p = event_next_pending(e); p = event_next_pending(e);
if (p) { if (p) {
_cleanup_(sd_event_unrefp) sd_event *ref = NULL; _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
ref = sd_event_ref(e);
e->state = SD_EVENT_RUNNING; e->state = SD_EVENT_RUNNING;
r = source_dispatch(p); r = source_dispatch(p);
e->state = SD_EVENT_INITIAL; e->state = SD_EVENT_INITIAL;
@ -3718,29 +3979,32 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
if (e->profile_delays && e->last_run) { if (e->profile_delays && e->last_run_usec != 0) {
usec_t this_run; usec_t this_run;
unsigned l; unsigned l;
this_run = now(CLOCK_MONOTONIC); this_run = now(CLOCK_MONOTONIC);
l = u64log2(this_run - e->last_run); l = u64log2(this_run - e->last_run_usec);
assert(l < ELEMENTSOF(e->delays)); assert(l < ELEMENTSOF(e->delays));
e->delays[l]++; e->delays[l]++;
if (this_run - e->last_log >= 5*USEC_PER_SEC) { if (this_run - e->last_log_usec >= 5*USEC_PER_SEC) {
event_log_delays(e); event_log_delays(e);
e->last_log = this_run; e->last_log_usec = this_run;
} }
} }
/* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
_unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
r = sd_event_prepare(e); r = sd_event_prepare(e);
if (r == 0) if (r == 0)
/* There was nothing? Then wait... */ /* There was nothing? Then wait... */
r = sd_event_wait(e, timeout); r = sd_event_wait(e, timeout);
if (e->profile_delays) if (e->profile_delays)
e->last_run = now(CLOCK_MONOTONIC); e->last_run_usec = now(CLOCK_MONOTONIC);
if (r > 0) { if (r > 0) {
/* There's something now, then let's dispatch it */ /* There's something now, then let's dispatch it */
@ -3755,7 +4019,6 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
} }
_public_ int sd_event_loop(sd_event *e) { _public_ int sd_event_loop(sd_event *e) {
_cleanup_(sd_event_unrefp) sd_event *ref = NULL;
int r; int r;
assert_return(e, -EINVAL); assert_return(e, -EINVAL);
@ -3763,7 +4026,7 @@ _public_ int sd_event_loop(sd_event *e) {
assert_return(!event_pid_changed(e), -ECHILD); assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
ref = sd_event_ref(e); _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
while (e->state != SD_EVENT_FINISHED) { while (e->state != SD_EVENT_FINISHED) {
r = sd_event_run(e, (uint64_t) -1); r = sd_event_run(e, (uint64_t) -1);
@ -4008,3 +4271,53 @@ _public_ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b) {
s->exit_on_failure = b; s->exit_on_failure = b;
return 1; return 1;
} }
_public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval, unsigned burst) {
int r;
assert_return(s, -EINVAL);
/* Turning on ratelimiting on event source types that don't support it, is a loggable offense. Doing
* so is a programming error. */
assert_return(EVENT_SOURCE_CAN_RATE_LIMIT(s->type), -EDOM);
/* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
* non-ratelimited. */
r = event_source_leave_ratelimit(s);
if (r < 0)
return r;
s->rate_limit = (RateLimit) { interval, burst };
return 0;
}
_public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
assert_return(s, -EINVAL);
/* Querying whether an event source has ratelimiting configured is not a loggable offsense, hence
* don't use assert_return(). Unlike turning on ratelimiting it's not really a programming error */
if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type))
return -EDOM;
if (!ratelimit_configured(&s->rate_limit))
return -ENOEXEC;
if (ret_interval)
*ret_interval = s->rate_limit.interval;
if (ret_burst)
*ret_burst = s->rate_limit.burst;
return 0;
}
_public_ int sd_event_source_is_ratelimited(sd_event_source *s) {
assert_return(s, -EINVAL);
if (!EVENT_SOURCE_CAN_RATE_LIMIT(s->type))
return false;
if (!ratelimit_configured(&s->rate_limit))
return false;
return s->ratelimited;
}

View File

@ -589,8 +589,100 @@ static void test_pidfd(void) {
sd_event_unref(e); sd_event_unref(e);
} }
static int ratelimit_io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
unsigned *c = (unsigned*) userdata;
*c += 1;
return 0;
}
static int ratelimit_time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
int r;
r = sd_event_source_set_enabled(s, SD_EVENT_ON);
if (r < 0)
log_warning_errno(r, "Failed to turn on notify event source: %m");
r = sd_event_source_set_time(s, usec + 1000);
if (r < 0)
log_error_errno(r, "Failed to restart watchdog event source: %m");
unsigned *c = (unsigned*) userdata;
*c += 1;
return 0;
}
static void test_ratelimit(void) {
_cleanup_close_pair_ int p[2] = {-1, -1};
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
uint64_t interval;
unsigned count, burst;
assert_se(sd_event_default(&e) >= 0);
assert_se(pipe2(p, O_CLOEXEC|O_NONBLOCK) >= 0);
assert_se(sd_event_add_io(e, &s, p[0], EPOLLIN, ratelimit_io_handler, &count) >= 0);
assert_se(sd_event_source_set_description(s, "test-ratelimit-io") >= 0);
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 5) >= 0);
assert_se(sd_event_source_get_ratelimit(s, &interval, &burst) >= 0);
assert_se(interval == 1 * USEC_PER_SEC && burst == 5);
assert_se(write(p[1], "1", 1) == 1);
count = 0;
for (unsigned i = 0; i < 10; i++) {
log_debug("slow loop iteration %u", i);
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
assert_se(usleep(250 * USEC_PER_MSEC) >= 0);
}
assert_se(sd_event_source_is_ratelimited(s) == 0);
assert_se(count == 10);
log_info("ratelimit_io_handler: called %d times, event source not ratelimited", count);
assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0);
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 5) >= 0);
count = 0;
for (unsigned i = 0; i < 10; i++) {
log_debug("fast event loop iteration %u", i);
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
assert_se(usleep(10) >= 0);
}
log_info("ratelimit_io_handler: called %d times, event source got ratelimited", count);
assert_se(count < 10);
s = sd_event_source_unref(s);
safe_close_pair(p);
count = 0;
assert_se(sd_event_add_time_relative(e, &s, CLOCK_MONOTONIC, 1000, 1, ratelimit_time_handler, &count) >= 0);
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) == 0);
do {
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
} while (!sd_event_source_is_ratelimited(s));
log_info("ratelimit_time_handler: called %d times, event source got ratelimited", count);
assert_se(count == 10);
/* In order to get rid of active rate limit client needs to disable it explicitely */
assert_se(sd_event_source_set_ratelimit(s, 0, 0) >= 0);
assert_se(!sd_event_source_is_ratelimited(s));
assert_se(sd_event_source_set_ratelimit(s, 1 * USEC_PER_SEC, 10) >= 0);
do {
assert_se(sd_event_run(e, UINT64_MAX) >= 0);
} while (!sd_event_source_is_ratelimited(s));
log_info("ratelimit_time_handler: called 10 more times, event source got ratelimited");
assert_se(count == 20);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO); test_setup_logging(LOG_DEBUG);
test_basic(true); /* test with pidfd */ test_basic(true); /* test with pidfd */
test_basic(false); /* test without pidfd */ test_basic(false); /* test without pidfd */
@ -603,5 +695,7 @@ int main(int argc, char *argv[]) {
test_pidfd(); test_pidfd();
test_ratelimit();
return 0; return 0;
} }

View File

@ -986,7 +986,7 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
(void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0); (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
r = read_full_file_full( r = read_full_file_full(
AT_FDCWD, sa->key_file, AT_FDCWD, sa->key_file, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
NULL, (char **) &key, &key_len); NULL, (char **) &key, &key_len);
if (r < 0) if (r < 0)

View File

@ -869,7 +869,7 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
(void) warn_file_is_world_accessible(filename, NULL, NULL, 0); (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
r = read_full_file_full( r = read_full_file_full(
AT_FDCWD, filename, AT_FDCWD, filename, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
NULL, &key, &key_len); NULL, &key, &key_len);
if (r < 0) if (r < 0)

View File

@ -62,11 +62,9 @@ static struct DUID fallback_duid = { .type = DUID_TYPE_EN };
DUID* link_get_duid(Link *link) { DUID* link_get_duid(Link *link) {
if (link->network->duid.type != _DUID_TYPE_INVALID) if (link->network->duid.type != _DUID_TYPE_INVALID)
return &link->network->duid; return &link->network->duid;
else if (link->hw_addr.length == 0 && else if (link->hw_addr.length == 0 && IN_SET(link->manager->duid.type, DUID_TYPE_LLT, DUID_TYPE_LL))
(link->manager->duid.type == DUID_TYPE_LLT || /* Fallback to DUID that works without MAC address.
link->manager->duid.type == DUID_TYPE_LL)) * This is useful for tunnel devices without MAC address. */
/* Fallback to DUID that works without mac addresses.
* This is useful for tunnel devices without mac address. */
return &fallback_duid; return &fallback_duid;
else else
return &link->manager->duid; return &link->manager->duid;

View File

@ -70,6 +70,10 @@ static void dhcp4_check_ready(Link *link) {
return; return;
} }
r = sd_ipv4ll_stop(link->ipv4ll);
if (r < 0)
log_link_warning_errno(link, r, "Failed to drop IPv4 link-local address, ignoring: %m");
link_check_ready(link); link_check_ready(link);
} }
@ -1048,10 +1052,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
switch (event) { switch (event) {
case SD_DHCP_CLIENT_EVENT_STOP: case SD_DHCP_CLIENT_EVENT_STOP:
if (link->ipv4ll) {
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4)) {
assert(link->ipv4ll);
log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address"); log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address");
r = sd_ipv4ll_start(link->ipv4ll); r = sd_ipv4ll_start(link->ipv4ll);
@ -1136,6 +1137,17 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
return -ENOMSG; return -ENOMSG;
} }
break; break;
case SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE:
if (link->ipv4ll && !sd_ipv4ll_is_running(link->ipv4ll)) {
log_link_debug(link, "Problems acquiring DHCP lease, acquiring IPv4 link-local address");
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
}
break;
default: default:
if (event < 0) if (event < 0)
log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); log_link_warning_errno(link, event, "DHCP error: Client failed: %m");

View File

@ -148,7 +148,7 @@ int ipv4ll_configure(Link *link) {
assert(link); assert(link);
if (!link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) if (!link_ipv4ll_enabled(link))
return 0; return 0;
if (!link->ipv4ll) { if (!link->ipv4ll) {

View File

@ -55,9 +55,8 @@
#include "util.h" #include "util.h"
#include "vrf.h" #include "vrf.h"
bool link_ipv4ll_enabled(Link *link, AddressFamily mask) { bool link_ipv4ll_enabled(Link *link) {
assert(link); assert(link);
assert((mask & ~(ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) == 0);
if (link->flags & IFF_LOOPBACK) if (link->flags & IFF_LOOPBACK)
return false; return false;
@ -80,7 +79,7 @@ bool link_ipv4ll_enabled(Link *link, AddressFamily mask) {
if (link->network->bond) if (link->network->bond)
return false; return false;
return link->network->link_local & mask; return link->network->link_local & ADDRESS_FAMILY_IPV4;
} }
bool link_ipv6ll_enabled(Link *link) { bool link_ipv6ll_enabled(Link *link) {
@ -734,77 +733,51 @@ void link_check_ready(Link *link) {
if (link->state == LINK_STATE_CONFIGURED) if (link->state == LINK_STATE_CONFIGURED)
return; return;
if (link->state != LINK_STATE_CONFIGURING) { if (link->state != LINK_STATE_CONFIGURING)
log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state)); return (void) log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state));
return;
}
if (!link->network) if (!link->network)
return; return;
if (!link->addresses_configured) { if (!link->addresses_configured)
log_link_debug(link, "%s(): static addresses are not configured.", __func__); return (void) log_link_debug(link, "%s(): static addresses are not configured.", __func__);
return;
}
if (!link->neighbors_configured) { if (!link->neighbors_configured)
log_link_debug(link, "%s(): static neighbors are not configured.", __func__); return (void) log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
return;
}
SET_FOREACH(a, link->addresses) SET_FOREACH(a, link->addresses)
if (!address_is_ready(a)) { if (!address_is_ready(a)) {
_cleanup_free_ char *str = NULL; _cleanup_free_ char *str = NULL;
(void) in_addr_to_string(a->family, &a->in_addr, &str); (void) in_addr_to_string(a->family, &a->in_addr, &str);
log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen); return (void) log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen);
return;
} }
if (!link->static_routes_configured) { if (!link->static_routes_configured)
log_link_debug(link, "%s(): static routes are not configured.", __func__); return (void) log_link_debug(link, "%s(): static routes are not configured.", __func__);
return;
}
if (!link->static_nexthops_configured) { if (!link->static_nexthops_configured)
log_link_debug(link, "%s(): static nexthops are not configured.", __func__); return (void) log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
return;
}
if (!link->routing_policy_rules_configured) { if (!link->routing_policy_rules_configured)
log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__); return (void) log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__);
return;
}
if (!link->tc_configured) { if (!link->tc_configured)
log_link_debug(link, "%s(): traffic controls are not configured.", __func__); return (void) log_link_debug(link, "%s(): traffic controls are not configured.", __func__);
return;
}
if (!link->sr_iov_configured) { if (!link->sr_iov_configured)
log_link_debug(link, "%s(): SR-IOV is not configured.", __func__); return (void) log_link_debug(link, "%s(): SR-IOV is not configured.", __func__);
return;
}
if (!link->bridge_mdb_configured) { if (!link->bridge_mdb_configured)
log_link_debug(link, "%s(): Bridge MDB is not configured.", __func__); return (void) log_link_debug(link, "%s(): Bridge MDB is not configured.", __func__);
return;
}
if (link_has_carrier(link) || !link->network->configure_without_carrier) { if (link_has_carrier(link) || !link->network->configure_without_carrier) {
bool has_ndisc_address = false; bool has_ndisc_address = false;
NDiscAddress *n; NDiscAddress *n;
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) {
log_link_debug(link, "%s(): IPv4LL is not configured.", __func__);
return;
}
if (link_ipv6ll_enabled(link) && if (link_ipv6ll_enabled(link) &&
in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) { in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
log_link_debug(link, "%s(): IPv6LL is not configured.", __func__); return (void) log_link_debug(link, "%s(): IPv6LL is not configured.", __func__);
return;
}
SET_FOREACH(n, link->ndisc_addresses) SET_FOREACH(n, link->ndisc_addresses)
if (!n->marked) { if (!n->marked) {
@ -812,28 +785,26 @@ void link_check_ready(Link *link) {
break; break;
} }
if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) && if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_ipv4ll_enabled(link)) &&
!link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address && !link->dhcp_address && set_isempty(link->dhcp6_addresses) && !has_ndisc_address &&
!(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { !link->ipv4ll_address_configured)
log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no dynamic address is assigned yet.", __func__); /* When DHCP[46] or IPv4LL is enabled, at least one address is acquired by them. */
return; return (void) log_link_debug(link, "%s(): DHCP4, DHCP6 or IPv4LL is enabled but no dynamic address is assigned yet.", __func__);
}
if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || link_ipv6_accept_ra_enabled(link)) { if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) ||
link_ipv6_accept_ra_enabled(link) || link_ipv4ll_enabled(link)) {
if (!link->dhcp4_configured && if (!link->dhcp4_configured &&
!(link->dhcp6_address_configured && link->dhcp6_route_configured) && !(link->dhcp6_address_configured && link->dhcp6_route_configured) &&
!(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) && !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
!(link->ndisc_addresses_configured && link->ndisc_routes_configured) && !(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
!(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) { !link->ipv4ll_address_configured)
/* When DHCP or RA is enabled, at least one protocol must provide an address, or /* When DHCP[46], NDisc, or IPv4LL is enabled, at least one protocol must be finished. */
* an IPv4ll fallback address must be configured. */ return (void) log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
return;
}
log_link_debug(link, "%s(): dhcp4:%s dhcp6_addresses:%s dhcp_routes:%s dhcp_pd_addresses:%s dhcp_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s", log_link_debug(link, "%s(): dhcp4:%s ipv4ll:%s dhcp6_addresses:%s dhcp_routes:%s dhcp_pd_addresses:%s dhcp_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s",
__func__, __func__,
yes_no(link->dhcp4_configured), yes_no(link->dhcp4_configured),
yes_no(link->ipv4ll_address_configured),
yes_no(link->dhcp6_address_configured), yes_no(link->dhcp6_address_configured),
yes_no(link->dhcp6_route_configured), yes_no(link->dhcp6_route_configured),
yes_no(link->dhcp6_pd_address_configured), yes_no(link->dhcp6_pd_address_configured),
@ -844,8 +815,6 @@ void link_check_ready(Link *link) {
} }
link_enter_configured(link); link_enter_configured(link);
return;
} }
static int link_set_static_configs(Link *link) { static int link_set_static_configs(Link *link) {
@ -1230,22 +1199,19 @@ static int link_acquire_ipv4_conf(Link *link) {
assert(link->manager); assert(link->manager);
assert(link->manager->event); assert(link->manager->event);
if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4)) {
assert(link->ipv4ll);
log_link_debug(link, "Acquiring IPv4 link-local address");
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
}
if (link->dhcp_client) { if (link->dhcp_client) {
log_link_debug(link, "Acquiring DHCPv4 lease"); log_link_debug(link, "Acquiring DHCPv4 lease");
r = sd_dhcp_client_start(link->dhcp_client); r = sd_dhcp_client_start(link->dhcp_client);
if (r < 0) if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
} else if (link->ipv4ll) {
log_link_debug(link, "Acquiring IPv4 link-local address");
r = sd_ipv4ll_start(link->ipv4ll);
if (r < 0)
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
} }
return 0; return 0;

View File

@ -231,7 +231,7 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
int link_set_mtu(Link *link, uint32_t mtu); int link_set_mtu(Link *link, uint32_t mtu);
bool link_ipv4ll_enabled(Link *link, AddressFamily mask); bool link_ipv4ll_enabled(Link *link);
int link_stop_engines(Link *link, bool may_keep_dhcp); int link_stop_engines(Link *link, bool may_keep_dhcp);

View File

@ -210,13 +210,6 @@ int network_verify(Network *network) {
if (network->link_local < 0) if (network->link_local < 0)
network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6; network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6;
if (FLAGS_SET(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4) &&
!FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4)) {
log_warning("%s: fallback assignment of IPv4 link local address is enabled but DHCPv4 is disabled. "
"Disabling the fallback assignment.", network->filename);
SET_FLAG(network->link_local, ADDRESS_FAMILY_FALLBACK_IPV4, false);
}
/* IPMasquerade=yes implies IPForward=yes */ /* IPMasquerade=yes implies IPForward=yes */
if (network->ip_masquerade) if (network->ip_masquerade)
network->ip_forward |= ADDRESS_FAMILY_IPV4; network->ip_forward |= ADDRESS_FAMILY_IPV4;

View File

@ -15,15 +15,6 @@ static const char* const address_family_table[_ADDRESS_FAMILY_MAX] = {
[ADDRESS_FAMILY_IPV6] = "ipv6", [ADDRESS_FAMILY_IPV6] = "ipv6",
}; };
static const char* const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = {
[ADDRESS_FAMILY_NO] = "no",
[ADDRESS_FAMILY_YES] = "yes",
[ADDRESS_FAMILY_IPV4] = "ipv4",
[ADDRESS_FAMILY_IPV6] = "ipv6",
[ADDRESS_FAMILY_FALLBACK] = "fallback",
[ADDRESS_FAMILY_FALLBACK_IPV4] = "ipv4-fallback",
};
static const char* const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = { static const char* const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = {
[ADDRESS_FAMILY_YES] = "both", [ADDRESS_FAMILY_YES] = "both",
[ADDRESS_FAMILY_IPV4] = "ipv4", [ADDRESS_FAMILY_IPV4] = "ipv4",
@ -47,7 +38,15 @@ static const char* const dhcp_lease_server_type_table[_SD_DHCP_LEASE_SERVER_TYPE
}; };
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES); DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES);
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family, AddressFamily, ADDRESS_FAMILY_YES);
AddressFamily link_local_address_family_from_string(const char *s) {
if (streq_ptr(s, "fallback")) /* compat name */
return ADDRESS_FAMILY_YES;
if (streq_ptr(s, "fallback-ipv4")) /* compat name */
return ADDRESS_FAMILY_IPV4;
return address_family_from_string(s);
}
DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily); DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily);
DEFINE_STRING_TABLE_LOOKUP(duplicate_address_detection_address_family, AddressFamily); DEFINE_STRING_TABLE_LOOKUP(duplicate_address_detection_address_family, AddressFamily);
DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family, DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family,

View File

@ -16,8 +16,6 @@ typedef enum AddressFamily {
ADDRESS_FAMILY_IPV4 = 1 << 0, ADDRESS_FAMILY_IPV4 = 1 << 0,
ADDRESS_FAMILY_IPV6 = 1 << 1, ADDRESS_FAMILY_IPV6 = 1 << 1,
ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6, ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6,
ADDRESS_FAMILY_FALLBACK_IPV4 = 1 << 2,
ADDRESS_FAMILY_FALLBACK = ADDRESS_FAMILY_FALLBACK_IPV4 | ADDRESS_FAMILY_IPV6,
_ADDRESS_FAMILY_MAX, _ADDRESS_FAMILY_MAX,
_ADDRESS_FAMILY_INVALID = -1, _ADDRESS_FAMILY_INVALID = -1,
} AddressFamily; } AddressFamily;
@ -34,7 +32,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_address_family_with_kernel);
const char *address_family_to_string(AddressFamily b) _const_; const char *address_family_to_string(AddressFamily b) _const_;
AddressFamily address_family_from_string(const char *s) _pure_; AddressFamily address_family_from_string(const char *s) _pure_;
const char *link_local_address_family_to_string(AddressFamily b) _const_;
AddressFamily link_local_address_family_from_string(const char *s) _pure_; AddressFamily link_local_address_family_from_string(const char *s) _pure_;
const char *routing_policy_rule_address_family_to_string(AddressFamily b) _const_; const char *routing_policy_rule_address_family_to_string(AddressFamily b) _const_;

View File

@ -1589,7 +1589,10 @@ static int parse_argv(int argc, char *argv[]) {
return log_oom(); return log_oom();
} }
r = read_full_file_full(AT_FDCWD, j ?: p, flags, NULL, &data, &size); r = read_full_file_full(AT_FDCWD, j ?: p, UINT64_MAX, SIZE_MAX,
flags,
NULL,
&data, &size);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read credential '%s': %m", j ?: p); return log_error_errno(r, "Failed to read credential '%s': %m", j ?: p);

View File

@ -3621,7 +3621,11 @@ static int parse_argv(int argc, char *argv[]) {
_cleanup_(erase_and_freep) char *k = NULL; _cleanup_(erase_and_freep) char *k = NULL;
size_t n = 0; size_t n = 0;
r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_SECURE|READ_FULL_FILE_CONNECT_SOCKET, NULL, &k, &n); r = read_full_file_full(
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&k, &n);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read key file '%s': %m", optarg); return log_error_errno(r, "Failed to read key file '%s': %m", optarg);

View File

@ -2115,7 +2115,7 @@ int verity_settings_load(
if (verity->root_hash && !verity->root_hash_sig) { if (verity->root_hash && !verity->root_hash_sig) {
if (root_hash_sig_path) { if (root_hash_sig_path) {
r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); r = read_full_file(root_hash_sig_path, (char**) &root_hash_sig, &root_hash_sig_size);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
@ -2131,7 +2131,7 @@ int verity_settings_load(
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
r = read_full_file_full(AT_FDCWD, p, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
if (r >= 0) if (r >= 0)
@ -2145,7 +2145,7 @@ int verity_settings_load(
if (!p) if (!p)
return -ENOMEM; return -ENOMEM;
r = read_full_file_full(AT_FDCWD, p, 0, NULL, (char**) &root_hash_sig, &root_hash_sig_size); r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
if (r < 0 && r != -ENOENT) if (r < 0 && r != -ENOENT)
return r; return r;
if (r >= 0) if (r >= 0)

View File

@ -3195,7 +3195,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla
if (f) if (f)
r = read_full_stream(f, &text, NULL); r = read_full_stream(f, &text, NULL);
else if (path) else if (path)
r = read_full_file_full(dir_fd, path, 0, NULL, &text, NULL); r = read_full_file_full(dir_fd, path, UINT64_MAX, SIZE_MAX, 0, NULL, &text, NULL);
else else
return -EINVAL; return -EINVAL;
if (r < 0) if (r < 0)

View File

@ -40,6 +40,8 @@ enum {
SD_DHCP_CLIENT_EVENT_EXPIRED = 3, SD_DHCP_CLIENT_EVENT_EXPIRED = 3,
SD_DHCP_CLIENT_EVENT_RENEW = 4, SD_DHCP_CLIENT_EVENT_RENEW = 4,
SD_DHCP_CLIENT_EVENT_SELECTING = 5, SD_DHCP_CLIENT_EVENT_SELECTING = 5,
SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE = 6, /* Sent when we have not received a reply after the first few attempts.
* The client may want to start acquiring link-local addresses. */
}; };
enum { enum {

View File

@ -162,6 +162,9 @@ int sd_event_source_get_floating(sd_event_source *s);
int sd_event_source_set_floating(sd_event_source *s, int b); int sd_event_source_set_floating(sd_event_source *s, int b);
int sd_event_source_get_exit_on_failure(sd_event_source *s); int sd_event_source_get_exit_on_failure(sd_event_source *s);
int sd_event_source_set_exit_on_failure(sd_event_source *s, int b); int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
int sd_event_source_is_ratelimited(sd_event_source *s);
/* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */ /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);

View File

@ -911,8 +911,8 @@ static void test_read_full_file_socket(void) {
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
assert_se(read_full_file_full(AT_FDCWD, j, 0, NULL, &data, &size) == -ENXIO); assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO);
assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0);
assert_se(size == strlen(TEST_STR)); assert_se(size == strlen(TEST_STR));
assert_se(streq(data, TEST_STR)); assert_se(streq(data, TEST_STR));
@ -920,6 +920,50 @@ static void test_read_full_file_socket(void) {
#undef TEST_STR #undef TEST_STR
} }
static void test_read_full_file_offset_size(void) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_(unlink_and_freep) char *fn = NULL;
_cleanup_free_ char *rbuf = NULL;
size_t rbuf_size;
uint8_t buf[4711];
random_bytes(buf, sizeof(buf));
assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0);
assert_se(f = fopen(fn, "we"));
assert_se(fwrite(buf, 1, sizeof(buf), f) == sizeof(buf));
assert_se(fflush_and_check(f) >= 0);
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == sizeof(buf));
assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
rbuf = mfree(rbuf);
assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == 128);
assert_se(memcmp(buf, rbuf, rbuf_size) == 0);
rbuf = mfree(rbuf);
assert_se(read_full_file_full(AT_FDCWD, fn, 1234, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == sizeof(buf) - 1234);
assert_se(memcmp(buf + 1234, rbuf, rbuf_size) == 0);
rbuf = mfree(rbuf);
assert_se(read_full_file_full(AT_FDCWD, fn, 2345, 777, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == 777);
assert_se(memcmp(buf + 2345, rbuf, rbuf_size) == 0);
rbuf = mfree(rbuf);
assert_se(read_full_file_full(AT_FDCWD, fn, 4700, 20, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == 11);
assert_se(memcmp(buf + 4700, rbuf, rbuf_size) == 0);
rbuf = mfree(rbuf);
assert_se(read_full_file_full(AT_FDCWD, fn, 10000, 99, 0, NULL, &rbuf, &rbuf_size) >= 0);
assert_se(rbuf_size == 0);
rbuf = mfree(rbuf);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG); test_setup_logging(LOG_DEBUG);
@ -946,6 +990,7 @@ int main(int argc, char *argv[]) {
test_read_line4(); test_read_line4();
test_read_nul_string(); test_read_nul_string();
test_read_full_file_socket(); test_read_full_file_socket();
test_read_full_file_offset_size();
return 0; return 0;
} }

View File

@ -100,7 +100,11 @@ static int run(int argc, char *argv[]) {
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]); return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]);
} else { } else {
r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, NULL, &hash_sig, &hash_sig_size); r = read_full_file_full(
AT_FDCWD, argv[6], UINT64_MAX, SIZE_MAX,
READ_FULL_FILE_CONNECT_SOCKET,
NULL,
&hash_sig, &hash_sig_size);
if (r < 0) if (r < 0)
return log_error_errno(r, "Failed to read root hash signature: %m"); return log_error_errno(r, "Failed to read root hash signature: %m");
} }

View File

@ -4,5 +4,4 @@ Name=veth99
[Network] [Network]
DHCP=yes DHCP=yes
IPv6AcceptRA=yes IPv6AcceptRA=yes
LinkLocalAddressing=yes
VRF=vrf99 VRF=vrf99

View File

@ -3,5 +3,5 @@ Name=veth99
[Network] [Network]
DHCP=ipv4 DHCP=ipv4
LinkLocalAddressing=fallback LinkLocalAddressing=yes
IPv6AcceptRA=no IPv6AcceptRA=no

View File

@ -3,7 +3,7 @@ Name=veth99
[Network] [Network]
DHCP=ipv4 DHCP=ipv4
LinkLocalAddressing=fallback LinkLocalAddressing=yes
IPv6AcceptRA=no IPv6AcceptRA=no
[DHCPv4] [DHCPv4]

View File

@ -3428,8 +3428,8 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
'dhcp-client-use-dns-yes.network', 'dhcp-client-use-dns-yes.network',
'dhcp-client-use-domains.network', 'dhcp-client-use-domains.network',
'dhcp-client-vrf.network', 'dhcp-client-vrf.network',
'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network', 'dhcp-client-with-ipv4ll-with-dhcp-server.network',
'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network', 'dhcp-client-with-ipv4ll-without-dhcp-server.network',
'dhcp-client-with-static-address.network', 'dhcp-client-with-static-address.network',
'dhcp-client.network', 'dhcp-client.network',
'dhcp-server-decline.network', 'dhcp-server-decline.network',
@ -3925,7 +3925,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print('## ip address show vrf vrf99') print('## ip address show vrf vrf99')
output = check_output('ip address show vrf vrf99') output = check_output('ip address show vrf vrf99')
print(output) print(output)
self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)') self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
self.assertRegex(output, 'inet6 .* scope link') self.assertRegex(output, 'inet6 .* scope link')
@ -3933,7 +3932,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print('## ip address show dev veth99') print('## ip address show dev veth99')
output = check_output('ip address show dev veth99') output = check_output('ip address show dev veth99')
print(output) print(output)
self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)') self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (dynamic noprefixroute|noprefixroute dynamic)')
self.assertRegex(output, 'inet6 .* scope link') self.assertRegex(output, 'inet6 .* scope link')
@ -3942,7 +3940,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
output = check_output('ip route show vrf vrf99') output = check_output('ip route show vrf vrf99')
print(output) print(output)
self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.') self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
self.assertRegex(output, '169.254.0.0/16 dev veth99 proto kernel scope link src 169.254')
self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5') self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5')
self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 dev veth99 proto dhcp') self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 dev veth99 proto dhcp')
self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5') self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
@ -3995,9 +3992,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
print(output) print(output)
self.assertRegex(output, 'onlink') self.assertRegex(output, 'onlink')
def test_dhcp_client_with_ipv4ll_fallback_with_dhcp_server(self): def test_dhcp_client_with_ipv4ll_with_dhcp_server(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network') 'dhcp-client-with-ipv4ll-with-dhcp-server.network')
start_networkd() start_networkd()
self.wait_online(['veth-peer:carrier']) self.wait_online(['veth-peer:carrier'])
start_dnsmasq(lease_time='2m') start_dnsmasq(lease_time='2m')
@ -4032,9 +4029,9 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
search_words_in_dnsmasq_log('DHCPOFFER', show_all=True) search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
def test_dhcp_client_with_ipv4ll_fallback_without_dhcp_server(self): def test_dhcp_client_with_ipv4ll_without_dhcp_server(self):
copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network') 'dhcp-client-with-ipv4ll-without-dhcp-server.network')
start_networkd() start_networkd()
self.wait_online(['veth99:degraded', 'veth-peer:routable']) self.wait_online(['veth99:degraded', 'veth-peer:routable'])