Fixes an assertion failure "pd->type == SD_DHCP6_OPTION_IA_PD" in dhcp6_option_append_pd().
Something similar was done in commit 26a63b8132 ('sd-dhcp6-client: Fix
sending prefix delegation request (#17136)'). The justification is
probably the same.
With LTO, the compiler might think that the variable is uninitialized
(from NetworkManager's fork, with gcc-11.2.1-1.fc35):
src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c: In function 'sd_event_add_inotify':
src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c:2120: error: 's' may be used uninitialized in this function [-Werror=maybe-uninitialized]
2120 | *ret = s;
|
src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c:2102: note: 's' was declared here
2102 | sd_event_source *s;
|
lto1: all warnings being treated as errors
In particular, that would happen for codepaths where event_add_inotify_fd_internal()
returns `-errno`, and the compiler cannot be sure that the returned value will
be negative. Technically, the compiler is right, but we rely on libc functions
to set errno correctly, so this only happens in code paths, where something
bad already happend.
While LTO is prone to such false warnings, we are largely able to build systemd
without warnings. So it is feasible and we should make the effort of working
around warnings as they appear.
<limits.h> calls this ULLONG_MAX. It's not clear to me where ULONGLONG_MAX
can be found. This seems to be just a mistake.
Fixes: c7ed718720 ('macro: handle overflow in ALIGN_TO() somewhat reasonably')
When compiling with CFLAGS='-Werror=maybe-uninitialized -Og' we get a
warning about uninitialized "next_timeout" variable.
Avoid the warning by adding an (unreachable) "default" label.
Fixes: c24288d21e ("sd-dhcp-client: correct dhcpv4 renew/rebind retransmit timeouts")
The variable is always initialized, but the compiler might not notice
that. With gcc-9.2.1-1.fc31:
$ CFLAGS='-Werror=maybe-uninitialized -Og' meson build
$ ninja -C build
[...]
../src/basic/tmpfile-util.c: In function ‘mkostemp_safe’:
../src/basic/tmpfile-util.c:76:12: error: ‘fd’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
76 | if (fd < 0)
| ^
C's strerror() function does not return a "const char *" pointer
for the string. That has historic reasons and C99 even comments
that "[t]he array pointed to shall not be modified by the program".
Make the strerror_safe() wrapper correct this and be more strict
in this regard.
FRA_TUN_ID is a 64 big endian integer. Fix the policy.
FRA_TUN_ID is unused by networkd, hence I think this bug
has no actual consequences.
Fixes: bce67bbee3
inet_ntop() is not documented to be thread-safe, so it should not
be used in the DHCP library. Arguably, glibc uses a thread local
buffer, so indeed there is no problem with a suitable libc. Anyway,
just avoid it.
The DHCP client should not pre-filter addresses beyond what RFC
requires. If a client's user (like networkd) wishes to skip/filter
certain addresses, it's their responsibility.
The point of this is that the DHCP library does not hide/abstract
information that might be relevant for certain users. For example,
NetworkManager exposes DHCP options in its API. When doing that, the
options should be close to the actual lease.
This is related to commit d9ec2e632d
(dhcp4: filter bogus DNS/NTP server addresses silently).
The Router DHCP option may contain a list of one or more
routers ([1]). Extend the API of sd_dhcp_lease to return a
list instead of only the first.
Note that networkd still only uses the first router (if present).
Aside from extending the internal API of the DHCP client, there
is almost no change in behavior. The only visible difference in
behavior is that the "ROUTER" variable in the lease file is now a
list of addresses.
Note how RFC 2132 does not define certain IP addresses as invalid for the
router option. Still, previously sd_dhcp_lease_get_router() would never
return a "0.0.0.0" address. In fact, the previous API could not
differenciate whether no router option was present, whether it
was invalid, or whether its first router was "0.0.0.0". No longer let
the DHCP client library impose additional restrictions that are not
part of RFC. Instead, the caller should handle this. The patch does
that, and networkd only consideres the first router entry if it is not
"0.0.0.0".
[1] https://tools.ietf.org/html/rfc2132#section-3.5
deserialize_in_addrs() allocates the buffer before trying to parse
the IP address. Since a parsing error is silently ignored, the returned
size might be zero. In such a case we shouldn't return any buffer.
Anyway, there was no leak, because there are only two callers like
r = deserialize_in_addrs(&lease->dns, dns);
which both keep the unused buffer and later release it.
Note that deserialize_in_addrs() doesn't free the pointer before
reassigning the new output. The caller must take care to to pass
"ret" with an allocated buffer that would be leaked when returning
the result.
- RTA_OIF has no business in the routing-rule policy. It is numerical
identical to FRA_GOTO. Fix using the correct enum value. Note that
RTA_OIF/FRA_GOTO was not used by networkd, and the type was already
correct at uint32. So, there is no change in behavior.
- RTA_GATEWAY also does not belong to the routing-rules. It is numerical
identical to FRA_UNUSED2. Obviously, that value is unused as well,
so there is no actual change in behavior either. In particular
that is because:
- kernel would not send messages with FRA_UNUSED2 attribute.
- networkd would not try to parse/send RTA_GATEWAY/FRA_UNUSED2
attributes.
Fixes: bce67bbee3
The "chaddr" field is 16 bytes long, with "hlen" being the
length of the address.
https://tools.ietf.org/html/rfc2131#section-4.3.1 says:
The server MUST return to the client:
...
o Any parameters specific to this client (as identified by
the contents of 'chaddr' or 'client identifier' in the DHCPDISCOVER
or DHCPREQUEST message), e.g., as configured by the network
administrator,
It's not clear, whether only the first 'hlen' bytes of 'chaddr'
must correspond or all 16 bytes.
Note that https://tools.ietf.org/html/rfc4390#section-2.1 says for IPoIB
"chaddr" (client hardware address) field MUST be zeroed.
with having "hlen" zero. This indicates that at least in this case, the
bytes after "hlen" would matter.
As the DHCP client always sets the trailing bytes to zero, we would expect
that the server also replies as such and we could just compare all 16 bytes.
However, let's be liberal and accept any padding here.
This in practice only changes behavior for infiniband, where we
previously would enforce that the first ETH_ALEN bytes are zero.
That seems arbitrary for IPoIB. We should either check all bytes or
none of them. Let's do the latter and don't enforce RFC 4390 in this
regard.
internal_hashmap_first_key_and_value() returns the first value, or %NULL
if the hashmap is empty.
However, hashmaps may contain %NULL values. That means, a caller getting
%NULL doesn't know whether the hashmap is empty or whether the first
value is %NULL.
For example, a caller may be tempted to do something like:
if ((val = hashmap_steal_first_key_and_value (h, (void **) key))) {
// process first entry.
}
But this is only correct if the caller made sure that the hash is either
not empty or contains no NULL values.
Anyway, since a %NULL return value can signal an empty hash or a %NULL
value, it seems error prone to leave the key output argument
uninitialized in situations that the caller cannot clearly distinguish
(without making additional assumptions).
GCC 8.2 with LTO and -O2 emits a false warning:
src/basic/hashmap.c: In function 'internal_hashmap_free.constprop':
src/basic/hashmap.c:898:33: error: 'k' may be used uninitialized in this function [-Werror=maybe-uninitialized]
free_key(k);
^
Avoid it by initializing the variable.
"src/basic/time-util.c" is part of src/basic and should not include
"src/shared/serialize.h". It is one of the few cases where this
is done. Also, it's not even required.
There are various functions to set the DUID of a DHCPv6 client.
However, none of them allows to set arbitrary data. The closest is
sd_dhcp6_client_set_duid(), which would still do validation of the
DUID's content via dhcp_validate_duid_len().
Relax the validation and only log a debug message if the DUID
does not validate.
Note that dhcp_validate_duid_len() already is not very strict. For example
with DUID_TYPE_LLT it only ensures that the length is suitable to contain
hwtype and time. It does not further check that the length of hwaddr is non-zero
or suitable for hwtype. Also, non-well-known DUID types are accepted for
extensibility. Why reject certain DUIDs but allowing clearly wrong formats
otherwise?
The validation and failure should happen earlier, when accepting the
unsuitable DUID. At that point, there is more context of what is wrong,
and a better failure reason (or warning) can be reported to the user. Rejecting
the DUID when setting up the DHCPv6 client seems not optimal, in particular
because the DHCPv6 client does not care about actual content of the
DUID and treats it as opaque blob.
Also, NetworkManager (which uses this code) allows to configure the entire
binary DUID in binary. It intentionally does not validate the binary
content any further. Hence, it needs to be able to set _invalid_ DUIDs,
provided that some basic constraints are satisfied (like the maximum length).
sd_dhcp6_client_set_duid() has two callers: both set the DUID obtained
from link_get_duid(), which comes from configuration.
`man networkd.conf` says: "The configured DHCP DUID should conform to
the specification in RFC 3315, RFC 6355.". It does not not state that
it MUST conform.
Note that dhcp_validate_duid_len() has another caller: DHCPv4's
dhcp_client_set_iaid_duid_internal(). In this case, continue with
strict validation, as the callers are more controlled. Also, there is
already sd_dhcp_client_set_client_id() which can be used to bypass
this check and set arbitrary client identifiers.
sd_dhcp_client_set_client_id() is the only API for setting a raw client-id.
All other setters are more restricted and only allow to set a type 255 DUID.
Also, dhcp4_set_client_identifier() is the only caller, which already
does:
r = sd_dhcp_client_set_client_id(link->dhcp_client,
ARPHRD_ETHER,
(const uint8_t *) &link->mac,
sizeof(link->mac));
and hence ensures that the data length is indeed ETH_ALEN.
Drop additional input validation from sd_dhcp_client_set_client_id(). The client-id
is an opaque blob, and if a caller wishes to set type 1 (ethernet) or type 32
(infiniband) with unexpected address length, it should be allowed. The actual
client-id is not relevant to the DHCP client, and it's the responsibility of the
caller to generate a suitable client-id.
For example, in NetworkManager you can configure all the bytes of the
client-id, including such _invalid_ settings. I think it makes sense,
to allow the user to fully configure the identifier. Even if such configuration
would be rejected, it would be the responsibility of the higher layers (including
a sensible error message to the user) and not fail later during
sd_dhcp_client_set_client_id().
Still log a debug message if the length is unexpected.
u32ctz() was undefined for zero due to __builtin_ctz() [1].
Explicitly check for zero to make the behavior defined.
Note that this issue only affected in4_addr_netmask_to_prefixlen()
which is the only caller.
It may seem slightly odd, to return 32 (bits) for utz(0). But that
is what in4_addr_netmask_to_prefixlen() needs, and it probably makes
the most sense here.
[1] https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
Fixes: ba91431154
An earlier commit 0e408b82b (dhcp6-client: handle IAID with value zero)
introduced a flag to sd_dhcp6_client to distinguish between an unset
IAID and a value set to zero.
However, that was not sufficient and broke leaving the setting
uninitialized in networkd configuration. The configuration parsing
also must distinguish between the default, unset value and an
explict zero configuration.
Fixes: 0e408b82b8
Since sd_dhcp_lease_get_routes() returns the list of all routes,
the caller may need to differenciate whether the route was option
33 (static-routes) or 121 (classless-static-route).
Add an accessor for the internal field.
config_parse_iaid(), dhcp_identifier_set_iaid() and sd_dhcp6_client_set_iaid() all
allow for the IAID to be zero. Also, RFC 3315 makes no mention that zero
would be invalid.
However, client_ensure_iaid() would take an IAID of zero as a sign that
the values was unset. Fix that by keeping track whether IAID is
initialized.
The previous code did htole64() followed by unaligned_write_be32() (the
XOR and shift in between is endianness agnostic). That means, on every
architeture there is always exactly one byte swap and the iaid is
dependent on endianness.
Since dhcp_identifier_set_iaid() is part of the DUID generation
algorithm, this cannot be fixed without changing the client-id.
In particular, as the client-id already depends on the machine-id (and
is thus inherrently host-specific), it is better to stick to the current
behavior.
However, add a parameter to switch between old and new behaviour.
Since the new behavior is unused, the only real purpose of this
change is to self-document the oddity of the function.
Fixes: 933f9caeeb
Previously, together with kill_dots true, patch like
".", "./.", ".//.//" would all return an empty string.
That is wrong. There must be one "." left to reference
the current directory.
Also, the comment with examples was wrong.
Arguably, libsystemd-network is (still) entirely internal API.
However there is the aim of maybe exposing it as public API.
For that reason, it cannot include internal headers from
"src/basic/".
Note how files "src/systemd/sd-*.h" don't include any systemd
headers which don't themself have an "sd-" prefix.
Fixes: d89a400ed6
An assertion in dhcp_network_bind_raw_socket() is triggered when
starting an sd_dhcp_client without setting a MAC address first.
- sd_dhcp_client_start()
- client_start()
- client_start_delayed()
- dhcp_network_bind_raw_socket()
In that case, the arp-type and MAC address is still unset. Note that
dhcp_network_bind_raw_socket() already checks for a valid arp-type
and MAC address below, so we should just gracefully return -EINVAL.
Maybe sd_dhcp_client_start() should fail earlier when starting without
MAC address. But the failure here will be correctly propagated and
the start aborted.
Fixes: 76253e73f9
sfeatures is a "struct ethtool_sfeatures". Use sizeof() on the correct
data type.
Since "struct ethtool_gstrings" is larger than "struct ethtool_sfeatures",
this had no serious consequences.
Fixes: 50725d10e3
The "features" fields is parsed as a tristate value. The values
are thus not of type NetDevFeature enum but int. The NetDevFeature
enum is instead the index for the features array.
Adjust the type. In practice, this had no impact because NetDevFeature
enum commonly has size of int.
Also, don't use memset() 0xFF to initilize the int with -1. While
it works correctly in practice, it feels ugly.
Using strlen() to declare a buffer results in a variable-length array,
even if the compiler likely optimizes it to be a compile time constant.
When building with -Wvla, certain versions of gcc complain about such
buffers. Compiling with -Wvla has the advantage of preventing variably
length array, which defeat static asserts that are implemented by
declaring an array of negative length.
While the compiler likely optimizes strlen(x) for string literals,
it is not a constant expression.
Hence,
char buffer[strlen("OPTION_000") + 1];
declares a variable-length array. STRLEN() can be used instead
when a constant espression is needed.
It's not entirely identical to strlen(), as STRLEN("a\0") counts 2.
Also, it only works with string literals and the macro enforces
that the argument is a literal.
Very few parts of the systemd source require <math.h> or "libm.so".
Linking libbasic with -lm drags the mathematical library in for all
systemd components, and in turn for all users of systemd libraries.
It's just unneeded.
Callers of dhcp_validate_duid_len() know that they must not pass
a zero duid_len. Thus asserting against that is appropriate.
On the other hand, they are not aware of the maximum allowed length
of a duid, as that is the reason why they call dhcp_validate_duid_len()
in the first place. So dhcp_validate_duid_len() should just signal a
regular error.
Thereby, change assert_return() to an assert() as this is an internal
function.
lldp_start_timer() was only called during sd_lldp_get_neighbors().
Ensure that the timer is (re-)started when a new neighbor appears.
Otherwise, the timer is not started when relying on the events alone.
Fixes: 34437b4f9c
Initialize auto variables with cleanup attribute, otherwise we
get a compiler warning with -fexceptions.
./configure CFLAGS='-Wmaybe-uninitialized -fexceptions -O2'
Factor our common code to set/clear the client lease.
Resetting the lease involves canceling old timers.
Also, most importantly, when resetting the lease
in client_receive_advertise(), ensure to cancel
the timers too. It is not immediately clear, why
at that point no timers would be pending.
The lease-unref from sd_dhcp6_client_unref() can
be omited, because of the preceeding client_reset()
call.
sd_dhcp6_client_new() tried to set the DUID based on the machine id.
If the host has no /etc/machine-id, the constructor would fail
making it impossible to create an sd_dhcp6_client instance.
Relax this and create a DUID only later as needed. This way a caller
caller can workaround a missing machine-id file and set a DUID of his
choosing via sd_dhcp6_client_set_duid().