summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2022-08-10 17:35:21 +0200
committerThomas Haller <thaller@redhat.com>2022-08-11 10:12:02 +0200
commit7c0de1517ce589e9fa2837eeb705b788d829d055 (patch)
tree0797e5d3b087166ddf530439606f88fe0d7a17d4
parent3a603c87647c36c9f7d24dc727c81618ad06fca3 (diff)
downloadNetworkManager-7c0de1517ce589e9fa2837eeb705b788d829d055.tar.gz
systemd: update code from upstream (2022-08-10)
This is a direct dump from systemd git. $ git clean -fdx && \ git cat-file -p HEAD | sed '1,/^======$/ d' | bash - && \ git add . ====== SYSTEMD_DIR=../systemd COMMIT=e8d0eb3915ac33cc0d3da87a836cee6e61645227 ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/libnm-systemd-core/src/ \ :/src/libnm-systemd-shared/src/ \ :/src/libnm-std-aux/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1" } nm_copy_sd_core() { mkdir -p "./src/libnm-systemd-core/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1" } nm_copy_sd_stdaux() { mkdir -p "./src/libnm-std-aux/" cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/arp-util.c" nm_copy_sd_core "src/libsystemd-network/arp-util.h" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h" nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h" nm_copy_sd_core "src/libsystemd-network/lldp-network.c" nm_copy_sd_core "src/libsystemd-network/lldp-network.h" nm_copy_sd_core "src/libsystemd-network/lldp-rx-internal.h" nm_copy_sd_core "src/libsystemd-network/network-common.c" nm_copy_sd_core "src/libsystemd-network/network-common.h" nm_copy_sd_core "src/libsystemd-network/network-internal.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-lldp-rx.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-option.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-lldp-rx.h" nm_copy_sd_core "src/systemd/sd-lldp.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/async.h" nm_copy_sd_shared "src/basic/cgroup-util.h" nm_copy_sd_shared "src/basic/dns-def.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/errno-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/format-util.c" nm_copy_sd_shared "src/basic/format-util.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/glyph-util.c" nm_copy_sd_shared "src/basic/glyph-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/inotify-util.c" nm_copy_sd_shared "src/basic/inotify-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/locale-util.c" nm_copy_sd_shared "src/basic/locale-util.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/memory-util.c" nm_copy_sd_shared "src/basic/memory-util.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_fcntl.h" nm_copy_sd_shared "src/basic/missing_random.h" nm_copy_sd_shared "src/basic/missing_socket.h" nm_copy_sd_shared "src/basic/missing_stat.h" nm_copy_sd_shared "src/basic/missing_syscall.h" nm_copy_sd_shared "src/basic/missing_type.h" nm_copy_sd_shared "src/basic/ordered-set.c" nm_copy_sd_shared "src/basic/ordered-set.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/ratelimit.c" nm_copy_sd_shared "src/basic/ratelimit.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/signal-util.c" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sort-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/strxcpyx.c" nm_copy_sd_shared "src/basic/strxcpyx.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/user-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/util.c" nm_copy_sd_shared "src/basic/util.h" nm_copy_sd_shared "src/fundamental/macro-fundamental.h" nm_copy_sd_shared "src/fundamental/sha256.c" nm_copy_sd_shared "src/fundamental/sha256.h" nm_copy_sd_shared "src/fundamental/string-util-fundamental.c" nm_copy_sd_shared "src/fundamental/string-util-fundamental.h" nm_copy_sd_shared "src/shared/dns-domain.c" nm_copy_sd_shared "src/shared/dns-domain.h" nm_copy_sd_shared "src/shared/log-link.h" nm_copy_sd_shared "src/shared/web-util.c" nm_copy_sd_shared "src/shared/web-util.h" nm_copy_sd_stdaux "src/basic/unaligned.h"
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c55
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h7
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h84
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c255
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h108
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h1
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c14
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c2280
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c30
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c34
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c76
-rw-r--r--src/libnm-systemd-core/src/systemd/_sd-common.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/alloc-util.h18
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.c9
-rw-r--r--src/libnm-systemd-shared/src/basic/ether-addr-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/fd-util.c2
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.c4
-rw-r--r--src/libnm-systemd-shared/src/basic/hash-funcs.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/hashmap.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.c14
-rw-r--r--src/libnm-systemd-shared/src/basic/in-addr-util.h1
-rw-r--r--src/libnm-systemd-shared/src/basic/list.h3
-rw-r--r--src/libnm-systemd-shared/src/basic/macro.h4
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.c4
-rw-r--r--src/libnm-systemd-shared/src/basic/path-util.h12
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.c34
-rw-r--r--src/libnm-systemd-shared/src/basic/process-util.h2
-rw-r--r--src/libnm-systemd-shared/src/basic/set.h7
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.c20
-rw-r--r--src/libnm-systemd-shared/src/basic/stat-util.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/strv.h9
-rw-r--r--src/libnm-systemd-shared/src/basic/time-util.c2
-rw-r--r--src/libnm-systemd-shared/src/fundamental/macro-fundamental.h50
34 files changed, 273 insertions, 2888 deletions
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
index 326b09ac5e..68f6a7cb3c 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
@@ -63,20 +63,26 @@ int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) {
return 0;
}
-static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) {
+static int dhcp_identifier_set_duid_llt(
+ const struct hw_addr_data *hw_addr,
+ uint16_t arp_type,
+ usec_t t,
+ struct duid *ret_duid,
+ size_t *ret_len) {
+
uint16_t time_from_2000y;
- assert(addr);
+ assert(hw_addr);
assert(ret_duid);
assert(ret_len);
- if (addr_len == 0)
+ if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
+ assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
@@ -88,33 +94,38 @@ static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, ui
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
- memcpy(ret_duid->llt.haddr, addr, addr_len);
+ memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length);
- *ret_len = offsetof(struct duid, llt.haddr) + addr_len;
+ *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
return 0;
}
-static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) {
- assert(addr);
+static int dhcp_identifier_set_duid_ll(
+ const struct hw_addr_data *hw_addr,
+ uint16_t arp_type,
+ struct duid *ret_duid,
+ size_t *ret_len) {
+
+ assert(hw_addr);
assert(ret_duid);
assert(ret_len);
- if (addr_len == 0)
+ if (hw_addr->length == 0)
return -EOPNOTSUPP;
if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
+ assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
+ assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
else
return -EOPNOTSUPP;
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
- memcpy(ret_duid->ll.haddr, addr, addr_len);
+ memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length);
- *ret_len = offsetof(struct duid, ll.haddr) + addr_len;
+ *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
return 0;
}
@@ -174,8 +185,7 @@ static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len)
int dhcp_identifier_set_duid(
DUIDType duid_type,
- const uint8_t *addr,
- size_t addr_len,
+ const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
@@ -184,11 +194,11 @@ int dhcp_identifier_set_duid(
switch (duid_type) {
case DUID_TYPE_LLT:
- return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len);
+ return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len);
case DUID_TYPE_EN:
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
case DUID_TYPE_LL:
- return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len);
+ return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len);
case DUID_TYPE_UUID:
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
default:
@@ -198,8 +208,7 @@ int dhcp_identifier_set_duid(
int dhcp_identifier_set_iaid(
int ifindex,
- const uint8_t *mac,
- size_t mac_len,
+ const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret) {
@@ -212,6 +221,10 @@ int dhcp_identifier_set_iaid(
uint64_t id;
int r;
+ assert(ifindex > 0);
+ assert(hw_addr);
+ assert(ret);
+
if (udev_available() && !use_mac) {
/* udev should be around */
@@ -240,7 +253,7 @@ int dhcp_identifier_set_iaid(
id = siphash24(name, strlen(name), HASH_KEY.bytes);
else
/* fall back to MAC address if no predictable name available */
- id = siphash24(mac, mac_len, HASH_KEY.bytes);
+ id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
id32 = (id & 0xffffffff) ^ (id >> 32);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
index 697ba3bfbb..8acb8c3210 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h
@@ -3,6 +3,7 @@
#include "sd-id128.h"
+#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
#include "time-util.h"
@@ -58,8 +59,7 @@ int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
int dhcp_identifier_set_duid(
DUIDType duid_type,
- const uint8_t *addr,
- size_t addr_len,
+ const struct hw_addr_data *hw_addr,
uint16_t arp_type,
usec_t llt_time,
bool test_mode,
@@ -67,8 +67,7 @@ int dhcp_identifier_set_duid(
size_t *ret_len);
int dhcp_identifier_set_iaid(
int ifindex,
- const uint8_t *mac,
- size_t mac_len,
+ const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
bool use_mac,
void *ret);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h
deleted file mode 100644
index 466d8e4b3f..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
- Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <stdint.h>
-
-#include "sd-dhcp-client.h"
-
-#include "dhcp-protocol.h"
-#include "network-common.h"
-#include "socket-util.h"
-
-typedef struct sd_dhcp_option {
- unsigned n_ref;
-
- uint8_t option;
- void *data;
- size_t length;
-} sd_dhcp_option;
-
-typedef struct DHCPServerData {
- struct in_addr *addr;
- size_t size;
-} DHCPServerData;
-
-extern const struct hash_ops dhcp_option_hash_ops;
-
-typedef struct sd_dhcp_client sd_dhcp_client;
-
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
- const uint8_t *mac_addr, size_t mac_addr_len,
- const uint8_t *bcast_addr, size_t bcast_addr_len,
- uint16_t arp_type, uint16_t port);
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type);
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
- const void *packet, size_t len);
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
- const void *packet, size_t len);
-
-int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload,
- uint8_t code, size_t optlen, const void *optval);
-int dhcp_option_find_option(uint8_t *options, size_t length, uint8_t wanted_code, size_t *ret_offset);
-int dhcp_option_remove_option(uint8_t *options, size_t buflen, uint8_t option_code);
-
-typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
- const void *option, void *userdata);
-
-int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
-
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
- uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
- size_t optlen, size_t *optoffset);
-
-uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
-
-void dhcp_packet_append_ip_headers(DHCPPacket *packet, be32_t source_addr,
- uint16_t source, be32_t destination_addr,
- uint16_t destination, uint16_t len, int ip_service_type);
-
-int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, uint16_t port);
-
-void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode);
-
-/* If we are invoking callbacks of a dhcp-client, ensure unreffing the
- * client from the callback doesn't destroy the object we are working
- * on */
-#define DHCP_CLIENT_DONT_DESTROY(client) \
- _cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
-
-#define log_dhcp_client_errno(client, error, fmt, ...) \
- log_interface_prefix_full_errno( \
- "DHCPv4 client: ", \
- sd_dhcp_client, client, \
- error, fmt, ##__VA_ARGS__)
-#define log_dhcp_client(client, fmt, ...) \
- log_interface_prefix_full_errno_zerook( \
- "DHCPv4 client: ", \
- sd_dhcp_client, client, \
- 0, fmt, ##__VA_ARGS__)
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c
deleted file mode 100644
index 7abdaa515b..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c
+++ /dev/null
@@ -1,255 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
- Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <errno.h>
-#include <net/ethernet.h>
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <stdio.h>
-#include <string.h>
-#include <linux/filter.h>
-#include <linux/if_infiniband.h>
-#include <linux/if_packet.h>
-
-#include "dhcp-internal.h"
-#include "fd-util.h"
-#include "socket-util.h"
-#include "unaligned.h"
-
-static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
- uint32_t xid,
- const uint8_t *bcast_addr,
- size_t bcast_addr_len,
- const struct ether_addr *eth_mac,
- uint16_t arp_type, uint8_t dhcp_hlen,
- uint16_t port) {
- struct sock_filter filter[] = {
- BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0), /* A <- packet length */
- BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(DHCPPacket), 1, 0), /* packet >= DHCPPacket ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.protocol)), /* A <- IP protocol */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 1, 0), /* IP protocol == UDP ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags */
- BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x20), /* A <- A & 0x20 (More Fragments bit) */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, ip.frag_off)), /* A <- Flags + Fragment offset */
- BPF_STMT(BPF_ALU + BPF_AND + BPF_K, 0x1fff), /* A <- A & 0x1fff (Fragment offset) */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, udp.dest)), /* A <- UDP destination port */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 1, 0), /* UDP destination port == DHCP client port ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.op)), /* A <- DHCP op */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, BOOTREPLY, 1, 0), /* op == BOOTREPLY ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
-
- /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
- * compare chaddr for ETH_ALEN bytes. */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8), /* A (the MAC address length) == ETH_ALEN ? */
- BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])), /* X <- 4 bytes of client's MAC */
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])), /* X <- remainder of client's MAC */
- BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0), /* A == X ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
-
- BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */
- BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */
- BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */
- BPF_STMT(BPF_RET + BPF_K, UINT32_MAX), /* accept */
- };
- struct sock_fprog fprog = {
- .len = ELEMENTSOF(filter),
- .filter = filter
- };
- _cleanup_close_ int s = -1;
- int r;
-
- assert(ifindex > 0);
- assert(link);
-
- s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- r = setsockopt_int(s, SOL_PACKET, PACKET_AUXDATA, true);
- if (r < 0)
- return r;
-
- r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
- if (r < 0)
- return -errno;
-
- link->ll = (struct sockaddr_ll) {
- .sll_family = AF_PACKET,
- .sll_protocol = htobe16(ETH_P_IP),
- .sll_ifindex = ifindex,
- .sll_hatype = htobe16(arp_type),
- .sll_halen = bcast_addr_len,
- };
- memcpy(link->ll.sll_addr, bcast_addr, bcast_addr_len); /* We may overflow link->ll. link->ll_buffer ensures we have enough space. */
-
- r = bind(s, &link->sa, SOCKADDR_LL_LEN(link->ll));
- if (r < 0)
- return -errno;
-
- return TAKE_FD(s);
-}
-
-int dhcp_network_bind_raw_socket(
- int ifindex,
- union sockaddr_union *link,
- uint32_t xid,
- const uint8_t *mac_addr,
- size_t mac_addr_len,
- const uint8_t *bcast_addr,
- size_t bcast_addr_len,
- uint16_t arp_type,
- uint16_t port) {
-
- static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
- /* Default broadcast address for IPoIB */
- static const uint8_t ib_bcast[] = {
- 0x00, 0xff, 0xff, 0xff, 0xff, 0x12, 0x40, 0x1b,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0xff, 0xff
- };
- struct ether_addr eth_mac = { { 0, 0, 0, 0, 0, 0 } };
- const uint8_t *default_bcast_addr;
- size_t expected_bcast_addr_len;
- uint8_t dhcp_hlen = 0;
-
- if (arp_type == ARPHRD_ETHER) {
- assert_return(mac_addr_len == ETH_ALEN, -EINVAL);
- memcpy(&eth_mac, mac_addr, ETH_ALEN);
- dhcp_hlen = ETH_ALEN;
-
- default_bcast_addr = eth_bcast;
- expected_bcast_addr_len = ETH_ALEN;
- } else if (arp_type == ARPHRD_INFINIBAND) {
- default_bcast_addr = ib_bcast;
- expected_bcast_addr_len = INFINIBAND_ALEN;
- } else
- return -EINVAL;
-
- if (bcast_addr && bcast_addr_len > 0)
- assert_return(bcast_addr_len == expected_bcast_addr_len, -EINVAL);
- else {
- bcast_addr = default_bcast_addr;
- bcast_addr_len = expected_bcast_addr_len;
- }
-
- return _bind_raw_socket(ifindex, link, xid, bcast_addr, bcast_addr_len,
- &eth_mac, arp_type, dhcp_hlen, port);
-}
-
-int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) {
- union sockaddr_union src = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(port),
- .in.sin_addr.s_addr = address,
- };
- _cleanup_close_ int s = -1;
- int r;
-
- s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
- if (s < 0)
- return -errno;
-
- if (ip_service_type >= 0)
- r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
- else
- r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
- if (r < 0)
- return r;
-
- r = setsockopt_int(s, SOL_SOCKET, SO_REUSEADDR, true);
- if (r < 0)
- return r;
-
- if (ifindex > 0) {
- r = socket_bind_to_ifindex(s, ifindex);
- if (r < 0)
- return r;
- }
-
- if (port == DHCP_PORT_SERVER) {
- r = setsockopt_int(s, SOL_SOCKET, SO_BROADCAST, true);
- if (r < 0)
- return r;
- if (address == INADDR_ANY) {
- /* IP_PKTINFO filter should not be applied when packets are
- allowed to enter/leave through the interface other than
- DHCP server sits on(BindToInterface option). */
- r = setsockopt_int(s, IPPROTO_IP, IP_PKTINFO, true);
- if (r < 0)
- return r;
- }
- } else {
- r = setsockopt_int(s, IPPROTO_IP, IP_FREEBIND, true);
- if (r < 0)
- return r;
- }
-
- if (bind(s, &src.sa, sizeof(src.in)) < 0)
- return -errno;
-
- return TAKE_FD(s);
-}
-
-int dhcp_network_send_raw_socket(
- int s,
- const union sockaddr_union *link,
- const void *packet,
- size_t len) {
-
- /* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
- * function should fail with negative errno. */
-
- assert(link);
- assert(packet);
- assert(len > 0);
-
- if (sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)) < 0)
- return -errno;
-
- return 0;
-}
-
-int dhcp_network_send_udp_socket(
- int s,
- be32_t address,
- uint16_t port,
- const void *packet,
- size_t len) {
-
- union sockaddr_union dest = {
- .in.sin_family = AF_INET,
- .in.sin_port = htobe16(port),
- .in.sin_addr.s_addr = address,
- };
-
- assert(s >= 0);
- assert(packet);
- assert(len > 0);
-
- if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
- return -errno;
-
- return 0;
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h
deleted file mode 100644
index dd54bcf6ee..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
- Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-#include <stdint.h>
-
-#include "macro.h"
-#include "sparse-endian.h"
-
-struct DHCPMessage {
- uint8_t op;
- uint8_t htype;
- uint8_t hlen;
- uint8_t hops;
- be32_t xid;
- be16_t secs;
- be16_t flags;
- be32_t ciaddr;
- be32_t yiaddr;
- be32_t siaddr;
- be32_t giaddr;
- uint8_t chaddr[16];
- uint8_t sname[64];
- uint8_t file[128];
- be32_t magic;
- uint8_t options[0];
-} _packed_;
-
-typedef struct DHCPMessage DHCPMessage;
-
-struct DHCPPacket {
- struct iphdr ip;
- struct udphdr udp;
- DHCPMessage dhcp;
-} _packed_;
-
-typedef struct DHCPPacket DHCPPacket;
-
-#define DHCP_IP_SIZE (int32_t)(sizeof(struct iphdr))
-#define DHCP_IP_UDP_SIZE (int32_t)(sizeof(struct udphdr) + DHCP_IP_SIZE)
-#define DHCP_MESSAGE_SIZE (int32_t)(sizeof(DHCPMessage))
-#define DHCP_DEFAULT_MIN_SIZE 576 /* the minimum internet hosts must be able to receive */
-#define DHCP_MIN_OPTIONS_SIZE (DHCP_DEFAULT_MIN_SIZE - DHCP_IP_UDP_SIZE - DHCP_MESSAGE_SIZE)
-#define DHCP_MAGIC_COOKIE (uint32_t)(0x63825363)
-
-enum {
- DHCP_PORT_SERVER = 67,
- DHCP_PORT_CLIENT = 68,
-};
-
-enum DHCPState {
- DHCP_STATE_INIT = 0,
- DHCP_STATE_SELECTING = 1,
- DHCP_STATE_INIT_REBOOT = 2,
- DHCP_STATE_REBOOTING = 3,
- DHCP_STATE_REQUESTING = 4,
- DHCP_STATE_BOUND = 5,
- DHCP_STATE_RENEWING = 6,
- DHCP_STATE_REBINDING = 7,
- DHCP_STATE_STOPPED = 8,
-};
-
-typedef enum DHCPState DHCPState;
-
-enum {
- BOOTREQUEST = 1,
- BOOTREPLY = 2,
-};
-
-enum {
- DHCP_DISCOVER = 1, /* [RFC2132] */
- DHCP_OFFER = 2, /* [RFC2132] */
- DHCP_REQUEST = 3, /* [RFC2132] */
- DHCP_DECLINE = 4, /* [RFC2132] */
- DHCP_ACK = 5, /* [RFC2132] */
- DHCP_NAK = 6, /* [RFC2132] */
- DHCP_RELEASE = 7, /* [RFC2132] */
- DHCP_INFORM = 8, /* [RFC2132] */
- DHCP_FORCERENEW = 9, /* [RFC3203] */
- DHCPLEASEQUERY = 10, /* [RFC4388] */
- DHCPLEASEUNASSIGNED = 11, /* [RFC4388] */
- DHCPLEASEUNKNOWN = 12, /* [RFC4388] */
- DHCPLEASEACTIVE = 13, /* [RFC4388] */
- DHCPBULKLEASEQUERY = 14, /* [RFC6926] */
- DHCPLEASEQUERYDONE = 15, /* [RFC6926] */
- DHCPACTIVELEASEQUERY = 16, /* [RFC7724] */
- DHCPLEASEQUERYSTATUS = 17, /* [RFC7724] */
- DHCPTLS = 18, /* [RFC7724] */
-};
-
-enum {
- DHCP_OVERLOAD_FILE = 1,
- DHCP_OVERLOAD_SNAME = 2,
-};
-
-#define DHCP_MAX_FQDN_LENGTH 255
-
-enum {
- DHCP_FQDN_FLAG_S = (1 << 0),
- DHCP_FQDN_FLAG_O = (1 << 1),
- DHCP_FQDN_FLAG_E = (1 << 2),
- DHCP_FQDN_FLAG_N = (1 << 3),
-};
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
index 176391ebec..65f6cb057f 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -71,6 +71,7 @@ struct sd_dhcp6_client {
char **vendor_class;
OrderedHashmap *extra_options;
OrderedSet *vendor_options;
+ bool rapid_commit;
struct sd_dhcp6_lease *lease;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
index 63d3f60513..5e91e86f53 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
@@ -508,7 +508,7 @@ int dhcp6_option_parse(
if (buflen < offsetof(DHCP6Option, data))
return -EBADMSG;
- if (*offset >= buflen - offsetof(DHCP6Option, data))
+ if (*offset > buflen - offsetof(DHCP6Option, data))
return -EBADMSG;
len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len));
@@ -518,14 +518,14 @@ int dhcp6_option_parse(
*ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code));
*ret_option_data_len = len;
- *ret_option_data = buf + *offset + offsetof(DHCP6Option, data);
+ *ret_option_data = len == 0 ? NULL : buf + *offset + offsetof(DHCP6Option, data);
*offset += offsetof(DHCP6Option, data) + len;
return 0;
}
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
- assert(data);
+ assert(data || data_len == 0);
if (data_len < sizeof(uint16_t))
return -EBADMSG;
@@ -803,7 +803,7 @@ int dhcp6_option_parse_addresses(
struct in6_addr **addrs,
size_t *count) {
- assert(optval);
+ assert(optval || optlen == 0);
assert(addrs);
assert(count);
@@ -826,8 +826,8 @@ static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
int r;
assert(data);
- assert(*data);
assert(len);
+ assert(*data || *len == 0);
assert(ret);
optval = *data;
@@ -891,7 +891,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r
_cleanup_free_ char *domain = NULL;
int r;
- assert(optval);
+ assert(optval || optlen == 0);
assert(ret);
r = parse_domain(&optval, &optlen, &domain);
@@ -910,7 +910,7 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha
_cleanup_strv_free_ char **names = NULL;
int r;
- assert(optval);
+ assert(optval || optlen == 0);
assert(ret);
if (optlen <= 1)
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
deleted file mode 100644
index d2c6361cb8..0000000000
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
+++ /dev/null
@@ -1,2280 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
- Copyright © 2013 Intel Corporation. All rights reserved.
-***/
-
-#include <errno.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <linux/if_infiniband.h>
-
-#include "sd-dhcp-client.h"
-
-#include "alloc-util.h"
-#include "dhcp-identifier.h"
-#include "dhcp-internal.h"
-#include "dhcp-lease-internal.h"
-#include "dhcp-protocol.h"
-#include "dns-domain.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "hostname-util.h"
-#include "io-util.h"
-#include "memory-util.h"
-#include "network-common.h"
-#include "random-util.h"
-#include "set.h"
-#include "sort-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "time-util.h"
-#include "utf8.h"
-#include "web-util.h"
-
-#define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN) /* Arbitrary limit */
-#define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
-
-#define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
-#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 {
- uint8_t type;
- union {
- struct {
- /* 0: Generic (non-LL) (RFC 2132) */
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ gen;
- struct {
- /* 1: Ethernet Link-Layer (RFC 2132) */
- uint8_t haddr[ETH_ALEN];
- } _packed_ eth;
- struct {
- /* 2 - 254: ARP/Link-Layer (RFC 2132) */
- uint8_t haddr[0];
- } _packed_ ll;
- struct {
- /* 255: Node-specific (RFC 4361) */
- be32_t iaid;
- struct duid duid;
- } _packed_ ns;
- struct {
- uint8_t data[MAX_CLIENT_ID_LEN];
- } _packed_ raw;
- };
-} _packed_ sd_dhcp_client_id;
-
-struct sd_dhcp_client {
- unsigned n_ref;
-
- DHCPState state;
- sd_event *event;
- int event_priority;
- sd_event_source *timeout_resend;
- int ifindex;
- char *ifname;
- int fd;
- uint16_t port;
- union sockaddr_union link;
- sd_event_source *receive_message;
- bool request_broadcast;
- Set *req_opts;
- bool anonymize;
- be32_t last_addr;
- uint8_t mac_addr[MAX_MAC_ADDR_LEN];
- size_t mac_addr_len;
- uint8_t bcast_addr[MAX_MAC_ADDR_LEN];
- size_t bcast_addr_len;
- uint16_t arp_type;
- sd_dhcp_client_id client_id;
- size_t client_id_len;
- char *hostname;
- char *vendor_class_identifier;
- char *mudurl;
- char **user_class;
- uint32_t mtu;
- uint32_t fallback_lease_lifetime;
- uint32_t xid;
- usec_t start_time;
- usec_t t1_time;
- usec_t t2_time;
- usec_t expire_time;
- uint64_t attempt;
- uint64_t max_attempts;
- OrderedHashmap *extra_options;
- OrderedHashmap *vendor_options;
- usec_t request_sent;
- sd_event_source *timeout_t1;
- sd_event_source *timeout_t2;
- sd_event_source *timeout_expire;
- sd_dhcp_client_callback_t callback;
- void *userdata;
- sd_dhcp_lease *lease;
- usec_t start_delay;
- int ip_service_type;
-
- /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
- bool test_mode;
-};
-
-static const uint8_t default_req_opts[] = {
- SD_DHCP_OPTION_SUBNET_MASK,
- SD_DHCP_OPTION_ROUTER,
- SD_DHCP_OPTION_HOST_NAME,
- SD_DHCP_OPTION_DOMAIN_NAME,
- SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
-};
-
-/* RFC7844 section 3:
- MAY contain the Parameter Request List option.
- RFC7844 section 3.6:
- The client intending to protect its privacy SHOULD only request a
- minimal number of options in the PRL and SHOULD also randomly shuffle
- the ordering of option codes in the PRL. If this random ordering
- cannot be implemented, the client MAY order the option codes in the
- PRL by option code number (lowest to highest).
-*/
-/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
-static const uint8_t default_req_opts_anonymize[] = {
- SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
- SD_DHCP_OPTION_ROUTER, /* 3 */
- SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
- SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
- SD_DHCP_OPTION_ROUTER_DISCOVERY, /* 31 */
- SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
- SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
- SD_DHCP_OPTION_NETBIOS_NAME_SERVER, /* 44 */
- SD_DHCP_OPTION_NETBIOS_NODE_TYPE, /* 46 */
- SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
- SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
- SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
- SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
-};
-
-static int client_receive_message_raw(
- sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata);
-static int client_receive_message_udp(
- sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata);
-static void client_stop(sd_dhcp_client *client, int error);
-
-int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
- const sd_dhcp_client_id *client_id = data;
- _cleanup_free_ char *t = NULL;
- int r = 0;
-
- assert_return(data, -EINVAL);
- assert_return(len >= 1, -EINVAL);
- assert_return(ret, -EINVAL);
-
- len -= 1;
- if (len > MAX_CLIENT_ID_LEN)
- return -EINVAL;
-
- switch (client_id->type) {
- case 0:
- if (utf8_is_printable((char *) client_id->gen.data, len))
- r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
- else
- r = asprintf(&t, "DATA");
- break;
- case 1:
- if (len != sizeof_field(sd_dhcp_client_id, eth))
- return -EINVAL;
-
- r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
- client_id->eth.haddr[0],
- client_id->eth.haddr[1],
- client_id->eth.haddr[2],
- client_id->eth.haddr[3],
- client_id->eth.haddr[4],
- client_id->eth.haddr[5]);
- break;
- case 2 ... 254:
- r = asprintf(&t, "ARP/LL");
- break;
- case 255:
- if (len < 6)
- return -EINVAL;
-
- uint32_t iaid = be32toh(client_id->ns.iaid);
- uint16_t duid_type = be16toh(client_id->ns.duid.type);
- if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
- return -EINVAL;
-
- r = asprintf(&t, "IAID:0x%x/DUID", iaid);
- break;
- }
-
- if (r < 0)
- return -ENOMEM;
- *ret = TAKE_PTR(t);
- return 0;
-}
-
-int sd_dhcp_client_set_callback(
- sd_dhcp_client *client,
- sd_dhcp_client_callback_t cb,
- void *userdata) {
-
- assert_return(client, -EINVAL);
-
- client->callback = cb;
- client->userdata = userdata;
-
- return 0;
-}
-
-int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast) {
- assert_return(client, -EINVAL);
-
- client->request_broadcast = broadcast;
-
- return 0;
-}
-
-int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
- assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
-
- switch (option) {
-
- case SD_DHCP_OPTION_PAD:
- case SD_DHCP_OPTION_OVERLOAD:
- case SD_DHCP_OPTION_MESSAGE_TYPE:
- case SD_DHCP_OPTION_PARAMETER_REQUEST_LIST:
- case SD_DHCP_OPTION_END:
- return -EINVAL;
-
- default:
- break;
- }
-
- return set_ensure_put(&client->req_opts, NULL, UINT8_TO_PTR(option));
-}
-
-int sd_dhcp_client_set_request_address(
- sd_dhcp_client *client,
- const struct in_addr *last_addr) {
-
- assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
-
- if (last_addr)
- client->last_addr = last_addr->s_addr;
- else
- client->last_addr = INADDR_ANY;
-
- return 0;
-}
-
-int sd_dhcp_client_set_ifindex(sd_dhcp_client *client, int ifindex) {
- assert_return(client, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
- assert_return(ifindex > 0, -EINVAL);
-
- client->ifindex = ifindex;
- return 0;
-}
-
-int sd_dhcp_client_set_ifname(sd_dhcp_client *client, const char *ifname) {
- assert_return(client, -EINVAL);
- assert_return(ifname, -EINVAL);
-
- if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
- return -EINVAL;
-
- return free_and_strdup(&client->ifname, ifname);
-}
-
-int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret) {
- int r;
-
- assert_return(client, -EINVAL);
-
- r = get_ifname(client->ifindex, &client->ifname);
- if (r < 0)
- return r;
-
- if (ret)
- *ret = client->ifname;
-
- return 0;
-}
-
-int sd_dhcp_client_set_mac(
- sd_dhcp_client *client,
- const uint8_t *addr,
- const uint8_t *bcast_addr,
- size_t addr_len,
- uint16_t arp_type) {
-
- DHCP_CLIENT_DONT_DESTROY(client);
- bool need_restart = false;
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(addr, -EINVAL);
- assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
- assert_return(arp_type > 0, -EINVAL);
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(addr_len == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
- else
- return -EINVAL;
-
- if (client->mac_addr_len == addr_len &&
- memcmp(&client->mac_addr, addr, addr_len) == 0 &&
- (client->bcast_addr_len > 0) == !!bcast_addr &&
- (!bcast_addr || memcmp(&client->bcast_addr, bcast_addr, addr_len) == 0))
- return 0;
-
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Changing MAC address on running DHCP client, restarting");
- need_restart = true;
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- }
-
- memcpy(&client->mac_addr, addr, addr_len);
- client->mac_addr_len = addr_len;
- client->arp_type = arp_type;
- client->bcast_addr_len = 0;
-
- if (bcast_addr) {
- memcpy(&client->bcast_addr, bcast_addr, addr_len);
- client->bcast_addr_len = addr_len;
- }
-
- if (need_restart && client->state != DHCP_STATE_STOPPED) {
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
-
- return 0;
-}
-
-int sd_dhcp_client_get_client_id(
- sd_dhcp_client *client,
- uint8_t *type,
- const uint8_t **data,
- size_t *data_len) {
-
- assert_return(client, -EINVAL);
- assert_return(type, -EINVAL);
- assert_return(data, -EINVAL);
- assert_return(data_len, -EINVAL);
-
- if (client->client_id_len) {
- *type = client->client_id.type;
- *data = client->client_id.raw.data;
- *data_len = client->client_id_len - sizeof(client->client_id.type);
- } else {
- *type = 0;
- *data = NULL;
- *data_len = 0;
- }
-
- return 0;
-}
-
-int sd_dhcp_client_set_client_id(
- sd_dhcp_client *client,
- uint8_t type,
- const uint8_t *data,
- size_t data_len) {
-
- DHCP_CLIENT_DONT_DESTROY(client);
- bool need_restart = false;
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(data, -EINVAL);
- assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
-
- if (client->client_id_len == data_len + sizeof(client->client_id.type) &&
- client->client_id.type == type &&
- memcmp(&client->client_id.raw.data, data, data_len) == 0)
- return 0;
-
- /* For hardware types, log debug message about unexpected data length.
- *
- * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
- * the last 8 bytes of the address are stable and suitable to put into
- * the client-id. The caller is advised to account for that. */
- if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
- (type == ARPHRD_INFINIBAND && data_len != 8))
- log_dhcp_client(client, "Changing client ID to hardware type %u with "
- "unexpected address length %zu",
- type, data_len);
-
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Changing client ID on running DHCP "
- "client, restarting");
- need_restart = true;
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- }
-
- client->client_id.type = type;
- memcpy(&client->client_id.raw.data, data, data_len);
- client->client_id_len = data_len + sizeof (client->client_id.type);
-
- if (need_restart && client->state != DHCP_STATE_STOPPED) {
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
-
- return 0;
-}
-
-/**
- * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid
- * without further modification. Otherwise, if duid_type is supported, DUID
- * is set based on that type. Otherwise, an error is returned.
- */
-static int dhcp_client_set_iaid_duid_internal(
- sd_dhcp_client *client,
- bool iaid_append,
- bool iaid_set,
- uint32_t iaid,
- DUIDType duid_type,
- const void *duid,
- size_t duid_len,
- usec_t llt_time) {
-
- DHCP_CLIENT_DONT_DESTROY(client);
- int r;
- size_t len;
-
- assert_return(client, -EINVAL);
- assert_return(duid_len == 0 || duid, -EINVAL);
-
- if (duid) {
- r = dhcp_validate_duid_len(duid_type, duid_len, true);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to validate length of DUID: %m");
- }
-
- zero(client->client_id);
- client->client_id.type = 255;
-
- if (iaid_append) {
- if (iaid_set)
- client->client_id.ns.iaid = htobe32(iaid);
- else {
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr,
- client->mac_addr_len,
- /* legacy_unstable_byteorder = */ true,
- /* use_mac = */ client->test_mode,
- &client->client_id.ns.iaid);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
- }
- }
-
- if (duid) {
- client->client_id.ns.duid.type = htobe16(duid_type);
- memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
- len = sizeof(client->client_id.ns.duid.type) + duid_len;
-
- } else {
- r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
- client->arp_type, llt_time, client->test_mode,
- &client->client_id.ns.duid, &len);
- if (r == -EOPNOTSUPP)
- return log_dhcp_client_errno(client, r,
- "Failed to set %s. MAC address is not set or "
- "interface type is not supported.",
- duid_type_to_string(duid_type));
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
- duid_type_to_string(duid_type));
- }
-
- client->client_id_len = sizeof(client->client_id.type) + len +
- (iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
-
- if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) {
- log_dhcp_client(client, "Configured %sDUID, restarting.", iaid_append ? "IAID+" : "");
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to restart DHCPv4 client: %m");
- }
-
- return 0;
-}
-
-int sd_dhcp_client_set_iaid_duid(
- sd_dhcp_client *client,
- bool iaid_set,
- uint32_t iaid,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len) {
- return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, duid_type, duid, duid_len, 0);
-}
-
-int sd_dhcp_client_set_iaid_duid_llt(
- sd_dhcp_client *client,
- bool iaid_set,
- uint32_t iaid,
- usec_t llt_time) {
- return dhcp_client_set_iaid_duid_internal(client, true, iaid_set, iaid, DUID_TYPE_LLT, NULL, 0, llt_time);
-}
-
-int sd_dhcp_client_set_duid(
- sd_dhcp_client *client,
- uint16_t duid_type,
- const void *duid,
- size_t duid_len) {
- return dhcp_client_set_iaid_duid_internal(client, false, false, 0, duid_type, duid, duid_len, 0);
-}
-
-int sd_dhcp_client_set_duid_llt(
- sd_dhcp_client *client,
- usec_t llt_time) {
- return dhcp_client_set_iaid_duid_internal(client, false, false, 0, DUID_TYPE_LLT, NULL, 0, llt_time);
-}
-
-void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode) {
- assert(client);
-
- client->test_mode = test_mode;
-}
-
-int sd_dhcp_client_set_hostname(
- sd_dhcp_client *client,
- const char *hostname) {
-
- assert_return(client, -EINVAL);
-
- /* Make sure hostnames qualify as DNS and as Linux hostnames */
- if (hostname &&
- !(hostname_is_valid(hostname, 0) && dns_name_is_valid(hostname) > 0))
- return -EINVAL;
-
- return free_and_strdup(&client->hostname, hostname);
-}
-
-int sd_dhcp_client_set_vendor_class_identifier(
- sd_dhcp_client *client,
- const char *vci) {
-
- assert_return(client, -EINVAL);
-
- return free_and_strdup(&client->vendor_class_identifier, vci);
-}
-
-int sd_dhcp_client_set_mud_url(
- sd_dhcp_client *client,
- const char *mudurl) {
-
- assert_return(client, -EINVAL);
- assert_return(mudurl, -EINVAL);
- assert_return(strlen(mudurl) <= 255, -EINVAL);
- assert_return(http_url_is_valid(mudurl), -EINVAL);
-
- return free_and_strdup(&client->mudurl, mudurl);
-}
-
-int sd_dhcp_client_set_user_class(
- sd_dhcp_client *client,
- char * const *user_class) {
-
- char **s = NULL;
-
- assert_return(client, -EINVAL);
- assert_return(!strv_isempty(user_class), -EINVAL);
-
- STRV_FOREACH(p, user_class) {
- size_t n = strlen(*p);
-
- if (n > 255 || n == 0)
- return -EINVAL;
- }
-
- s = strv_copy(user_class);
- if (!s)
- return -ENOMEM;
-
- return strv_free_and_replace(client->user_class, s);
-}
-
-int sd_dhcp_client_set_client_port(
- sd_dhcp_client *client,
- uint16_t port) {
-
- assert_return(client, -EINVAL);
-
- client->port = port;
-
- return 0;
-}
-
-int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu) {
- assert_return(client, -EINVAL);
- assert_return(mtu >= DHCP_DEFAULT_MIN_SIZE, -ERANGE);
-
- client->mtu = mtu;
-
- return 0;
-}
-
-int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempts) {
- assert_return(client, -EINVAL);
-
- client->max_attempts = max_attempts;
-
- return 0;
-}
-
-int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(v, -EINVAL);
-
- r = ordered_hashmap_ensure_put(&client->extra_options, &dhcp_option_hash_ops, UINT_TO_PTR(v->option), v);
- if (r < 0)
- return r;
-
- sd_dhcp_option_ref(v);
- return 0;
-}
-
-int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(v, -EINVAL);
-
- r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
- if (r < 0)
- return -ENOMEM;
-
- r = ordered_hashmap_put(client->vendor_options, v, v);
- if (r < 0)
- return r;
-
- sd_dhcp_option_ref(v);
-
- return 1;
-}
-
-int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
- assert_return(client, -EINVAL);
-
- if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING))
- return -EADDRNOTAVAIL;
-
- if (ret)
- *ret = client->lease;
-
- return 0;
-}
-
-int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
- assert_return(client, -EINVAL);
-
- client->ip_service_type = type;
-
- return 0;
-}
-
-int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
- assert_return(client, -EINVAL);
- assert_return(fallback_lease_lifetime > 0, -EINVAL);
-
- client->fallback_lease_lifetime = fallback_lease_lifetime;
-
- return 0;
-}
-
-static int client_notify(sd_dhcp_client *client, int event) {
- assert(client);
-
- if (client->callback)
- return client->callback(client, event, client->userdata);
-
- return 0;
-}
-
-static int client_initialize(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
-
- client->receive_message = sd_event_source_disable_unref(client->receive_message);
-
- client->fd = safe_close(client->fd);
-
- (void) event_source_disable(client->timeout_resend);
- (void) event_source_disable(client->timeout_t1);
- (void) event_source_disable(client->timeout_t2);
- (void) event_source_disable(client->timeout_expire);
-
- client->attempt = 0;
-
- client->state = DHCP_STATE_INIT;
- client->xid = 0;
-
- client->lease = sd_dhcp_lease_unref(client->lease);
-
- return 0;
-}
-
-static void client_stop(sd_dhcp_client *client, int error) {
- assert(client);
-
- if (error < 0)
- log_dhcp_client_errno(client, error, "STOPPED: %m");
- else if (error == SD_DHCP_CLIENT_EVENT_STOP)
- log_dhcp_client(client, "STOPPED");
- else
- log_dhcp_client(client, "STOPPED: Unknown event");
-
- client_notify(client, error);
-
- client_initialize(client);
-}
-
-/* RFC2131 section 4.1:
- * retransmission delays should include -1 to +1 sec of random 'fuzz'. */
-#define RFC2131_RANDOM_FUZZ \
- ((int64_t)(random_u64() % (2 * USEC_PER_SEC)) - (int64_t)USEC_PER_SEC)
-
-/* RFC2131 section 4.1:
- * for retransmission delays, timeout should start at 4s then double
- * each attempt with max of 64s, with -1 to +1 sec of random 'fuzz' added.
- * This assumes the first call will be using attempt 1. */
-static usec_t client_compute_request_timeout(usec_t now, uint64_t attempt) {
- usec_t timeout = (UINT64_C(1) << MIN(attempt + 1, UINT64_C(6))) * USEC_PER_SEC;
-
- return usec_sub_signed(usec_add(now, timeout), RFC2131_RANDOM_FUZZ);
-}
-
-/* RFC2131 section 4.4.5:
- * T1 defaults to (0.5 * duration_of_lease).
- * T2 defaults to (0.875 * duration_of_lease). */
-#define T1_DEFAULT(lifetime) ((lifetime) / 2)
-#define T2_DEFAULT(lifetime) (((lifetime) * 7) / 8)
-
-/* RFC2131 section 4.4.5:
- * the client SHOULD wait one-half of the remaining time until T2 (in RENEWING state)
- * and one-half of the remaining lease time (in REBINDING state), down to a minimum
- * of 60 seconds.
- * Note that while the default T1/T2 initial times do have random 'fuzz' applied,
- * the RFC sec 4.4.5 does not mention adding any fuzz to retries. */
-static usec_t client_compute_reacquisition_timeout(usec_t now, usec_t expire) {
- return now + MAX(usec_sub_unsigned(expire, now) / 2, 60 * USEC_PER_SEC);
-}
-
-static int cmp_uint8(const uint8_t *a, const uint8_t *b) {
- return CMP(*a, *b);
-}
-
-static int client_message_init(
- sd_dhcp_client *client,
- DHCPPacket **ret,
- uint8_t type,
- size_t *_optlen,
- size_t *_optoffset) {
-
- _cleanup_free_ DHCPPacket *packet = NULL;
- size_t optlen, optoffset, size;
- be16_t max_size;
- usec_t time_now;
- uint16_t secs;
- int r;
-
- assert(client);
- assert(client->start_time);
- assert(ret);
- assert(_optlen);
- assert(_optoffset);
- assert(IN_SET(type, DHCP_DISCOVER, DHCP_REQUEST, DHCP_RELEASE, DHCP_DECLINE));
-
- optlen = DHCP_MIN_OPTIONS_SIZE;
- size = sizeof(DHCPPacket) + optlen;
-
- packet = malloc0(size);
- if (!packet)
- return -ENOMEM;
-
- r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
- client->arp_type, client->mac_addr_len, client->mac_addr,
- optlen, &optoffset);
- if (r < 0)
- return r;
-
- /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
- refuse to issue an DHCP lease if 'secs' is set to zero */
- r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- return r;
- assert(time_now >= client->start_time);
-
- /* seconds between sending first and last DISCOVER
- * must always be strictly positive to deal with broken servers */
- secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
- packet->dhcp.secs = htobe16(secs);
-
- /* RFC2131 section 4.1
- A client that cannot receive unicast IP datagrams until its protocol
- software has been configured with an IP address SHOULD set the
- BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
- DHCPREQUEST messages that client sends. The BROADCAST bit will
- provide a hint to the DHCP server and BOOTP relay agent to broadcast
- any messages to the client on the client's subnet.
-
- Note: some interfaces needs this to be enabled, but some networks
- needs this to be disabled as broadcasts are filteretd, so this
- needs to be configurable */
- if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
- packet->dhcp.flags = htobe16(0x8000);
-
- /* If no client identifier exists, construct an RFC 4361-compliant one */
- if (client->client_id_len == 0) {
- size_t duid_len;
-
- client->client_id.type = 255;
-
- r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len,
- /* legacy_unstable_byteorder = */ true,
- /* use_mac = */ client->test_mode,
- &client->client_id.ns.iaid);
- if (r < 0)
- return r;
-
- r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
- if (r < 0)
- return r;
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid_len;
- }
-
- /* Some DHCP servers will refuse to issue an DHCP lease if the Client
- Identifier option is not set */
- if (client->client_id_len) {
- r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_CLIENT_IDENTIFIER,
- client->client_id_len,
- &client->client_id);
- if (r < 0)
- return r;
- }
-
- /* RFC2131 section 3.5:
- in its initial DHCPDISCOVER or DHCPREQUEST message, a
- client may provide the server with a list of specific
- parameters the client is interested in. If the client
- includes a list of parameters in a DHCPDISCOVER message,
- it MUST include that list in any subsequent DHCPREQUEST
- messages.
- */
-
- /* RFC7844 section 3:
- MAY contain the Parameter Request List option. */
- /* NOTE: in case that there would be an option to do not send
- * any PRL at all, the size should be checked before sending */
- if (!set_isempty(client->req_opts) && type != DHCP_RELEASE) {
- _cleanup_free_ uint8_t *opts = NULL;
- size_t n_opts, i = 0;
- void *val;
-
- n_opts = set_size(client->req_opts);
- opts = new(uint8_t, n_opts);
- if (!opts)
- return -ENOMEM;
-
- SET_FOREACH(val, client->req_opts)
- opts[i++] = PTR_TO_UINT8(val);
- assert(i == n_opts);
-
- /* For anonymizing the request, let's sort the options. */
- typesafe_qsort(opts, n_opts, cmp_uint8);
-
- r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
- n_opts, opts);
- if (r < 0)
- return r;
- }
-
- /* RFC2131 section 3.5:
- The client SHOULD include the ’maximum DHCP message size’ option to
- let the server know how large the server may make its DHCP messages.
-
- Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
- than the defined default size unless the Maximum Message Size option
- is explicitly set
-
- RFC3442 "Requirements to Avoid Sizing Constraints":
- Because a full routing table can be quite large, the standard 576
- octet maximum size for a DHCP message may be too short to contain
- some legitimate Classless Static Route options. Because of this,
- clients implementing the Classless Static Route option SHOULD send a
- Maximum DHCP Message Size [4] option if the DHCP client's TCP/IP
- stack is capable of receiving larger IP datagrams. In this case, the
- client SHOULD set the value of this option to at least the MTU of the
- interface that the client is configuring. The client MAY set the
- value of this option higher, up to the size of the largest UDP packet
- it is prepared to accept. (Note that the value specified in the
- Maximum DHCP Message Size option is the total maximum packet size,
- including IP and UDP headers.)
- */
- /* RFC7844 section 3:
- SHOULD NOT contain any other option. */
- if (!client->anonymize && type != DHCP_RELEASE) {
- max_size = htobe16(size);
- r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
- SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
- 2, &max_size);
- if (r < 0)
- return r;
- }
-
- *_optlen = optlen;
- *_optoffset = optoffset;
- *ret = TAKE_PTR(packet);
-
- return 0;
-}
-
-static int client_append_fqdn_option(
- DHCPMessage *message,
- size_t optlen,
- size_t *optoffset,
- const char *fqdn) {
-
- uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
- int r;
-
- buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
- DHCP_FQDN_FLAG_E; /* Canonical wire format */
- buffer[1] = 0; /* RCODE1 (deprecated) */
- buffer[2] = 0; /* RCODE2 (deprecated) */
-
- r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3, false);
- if (r > 0)
- r = dhcp_option_append(message, optlen, optoffset, 0,
- SD_DHCP_OPTION_FQDN, 3 + r, buffer);
-
- return r;
-}
-
-static int dhcp_client_send_raw(
- sd_dhcp_client *client,
- DHCPPacket *packet,
- size_t len) {
-
- dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
- INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
-
- return dhcp_network_send_raw_socket(client->fd, &client->link,
- packet, len);
-}
-
-static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
- sd_dhcp_option *j;
- int r;
-
- assert(client);
-
- if (client->hostname) {
- /* According to RFC 4702 "clients that send the Client FQDN option in
- their messages MUST NOT also send the Host Name option". Just send
- one of the two depending on the hostname type.
- */
- if (dns_name_is_single_label(client->hostname)) {
- /* it is unclear from RFC 2131 if client should send hostname in
- DHCPDISCOVER but dhclient does and so we do as well
- */
- r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
- SD_DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
- } else
- r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
- client->hostname);
- if (r < 0)
- return r;
- }
-
- if (client->vendor_class_identifier) {
- r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
- SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
- strlen(client->vendor_class_identifier),
- client->vendor_class_identifier);
- if (r < 0)
- return r;
- }
-
- if (client->mudurl) {
- r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
- SD_DHCP_OPTION_MUD_URL,
- strlen(client->mudurl),
- client->mudurl);
- if (r < 0)
- return r;
- }
-
- if (client->user_class) {
- r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
- SD_DHCP_OPTION_USER_CLASS,
- strv_length(client->user_class),
- client->user_class);
- if (r < 0)
- return r;
- }
-
- ORDERED_HASHMAP_FOREACH(j, client->extra_options) {
- r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
- j->option, j->length, j->data);
- if (r < 0)
- return r;
- }
-
- if (!ordered_hashmap_isempty(client->vendor_options)) {
- r = dhcp_option_append(
- &packet->dhcp, optlen, optoffset, 0,
- SD_DHCP_OPTION_VENDOR_SPECIFIC,
- ordered_hashmap_size(client->vendor_options), client->vendor_options);
- if (r < 0)
- return r;
- }
-
-
- return 0;
-}
-
-static int client_send_discover(sd_dhcp_client *client) {
- _cleanup_free_ DHCPPacket *discover = NULL;
- size_t optoffset, optlen;
- int r;
-
- assert(client);
- assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
- r = client_message_init(client, &discover, DHCP_DISCOVER,
- &optlen, &optoffset);
- if (r < 0)
- return r;
-
- /* the client may suggest values for the network address
- and lease time in the DHCPDISCOVER message. The client may include
- the ’requested IP address’ option to suggest that a particular IP
- address be assigned, and may include the ’IP address lease time’
- option to suggest the lease time it would like.
- */
- /* RFC7844 section 3:
- SHOULD NOT contain any other option. */
- if (!client->anonymize && client->last_addr != INADDR_ANY) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->last_addr);
- if (r < 0)
- return r;
- }
-
- r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
- if (r < 0)
- return r;
-
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
-
- /* We currently ignore:
- The client SHOULD wait a random time between one and ten seconds to
- desynchronize the use of DHCP at startup.
- */
- r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
- if (r < 0)
- return r;
-
- log_dhcp_client(client, "DISCOVER");
-
- return 0;
-}
-
-static int client_send_request(sd_dhcp_client *client) {
- _cleanup_free_ DHCPPacket *request = NULL;
- size_t optoffset, optlen;
- int r;
-
- assert(client);
-
- r = client_message_init(client, &request, DHCP_REQUEST, &optlen, &optoffset);
- if (r < 0)
- return r;
-
- switch (client->state) {
- /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
- SELECTING should be REQUESTING)
- */
-
- case DHCP_STATE_REQUESTING:
- /* Client inserts the address of the selected server in ’server
- identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
- filled in with the yiaddr value from the chosen DHCPOFFER.
- */
-
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_SERVER_IDENTIFIER,
- 4, &client->lease->server_address);
- if (r < 0)
- return r;
-
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->lease->address);
- if (r < 0)
- return r;
-
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
- option MUST be filled in with client’s notion of its previously
- assigned address. ’ciaddr’ MUST be zero.
- */
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
- 4, &client->last_addr);
- if (r < 0)
- return r;
- break;
-
- case DHCP_STATE_RENEWING:
- /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
- option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
- client’s IP address.
- */
-
- case DHCP_STATE_REBINDING:
- /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
- option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
- client’s IP address.
-
- This message MUST be broadcast to the 0xffffffff IP broadcast address.
- */
- request->dhcp.ciaddr = client->lease->address;
-
- break;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
- case DHCP_STATE_STOPPED:
- return -EINVAL;
- }
-
- r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
- if (r < 0)
- return r;
-
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
-
- if (client->state == DHCP_STATE_RENEWING)
- r = dhcp_network_send_udp_socket(client->fd,
- client->lease->server_address,
- DHCP_PORT_SERVER,
- &request->dhcp,
- sizeof(DHCPMessage) + optoffset);
- else
- r = dhcp_client_send_raw(client, request, sizeof(DHCPPacket) + optoffset);
- if (r < 0)
- return r;
-
- switch (client->state) {
-
- case DHCP_STATE_REQUESTING:
- log_dhcp_client(client, "REQUEST (requesting)");
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- log_dhcp_client(client, "REQUEST (init-reboot)");
- break;
-
- case DHCP_STATE_RENEWING:
- log_dhcp_client(client, "REQUEST (renewing)");
- break;
-
- case DHCP_STATE_REBINDING:
- log_dhcp_client(client, "REQUEST (rebinding)");
- break;
-
- default:
- log_dhcp_client(client, "REQUEST (invalid)");
- break;
- }
-
- return 0;
-}
-
-static int client_start(sd_dhcp_client *client);
-
-static int client_timeout_resend(
- sd_event_source *s,
- uint64_t usec,
- void *userdata) {
-
- sd_dhcp_client *client = userdata;
- DHCP_CLIENT_DONT_DESTROY(client);
- usec_t next_timeout;
- uint64_t time_now;
- int r;
-
- assert(s);
- assert(client);
- assert(client->event);
-
- r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- goto error;
-
- switch (client->state) {
-
- case DHCP_STATE_RENEWING:
- next_timeout = client_compute_reacquisition_timeout(time_now, client->t2_time);
- break;
-
- case DHCP_STATE_REBINDING:
- next_timeout = client_compute_reacquisition_timeout(time_now, client->expire_time);
- break;
-
- case DHCP_STATE_REBOOTING:
- /* start over as we did not receive a timely ack or nak */
- r = client_initialize(client);
- if (r < 0)
- goto error;
-
- r = client_start(client);
- if (r < 0)
- goto error;
-
- log_dhcp_client(client, "REBOOTED");
- return 0;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_SELECTING:
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_BOUND:
- if (client->attempt >= client->max_attempts)
- goto error;
-
- client->attempt++;
- next_timeout = client_compute_request_timeout(time_now, client->attempt);
- break;
-
- case DHCP_STATE_STOPPED:
- r = -EINVAL;
- goto error;
-
- default:
- assert_not_reached();
- }
-
- r = event_reset_time(client->event, &client->timeout_resend,
- CLOCK_BOOTTIME,
- next_timeout, 10 * USEC_PER_MSEC,
- client_timeout_resend, client,
- client->event_priority, "dhcp4-resend-timer", true);
- if (r < 0)
- goto error;
-
- switch (client->state) {
- case DHCP_STATE_INIT:
- r = client_send_discover(client);
- if (r >= 0) {
- client->state = DHCP_STATE_SELECTING;
- client->attempt = 0;
- } else if (client->attempt >= client->max_attempts)
- goto error;
-
- break;
-
- case DHCP_STATE_SELECTING:
- r = client_send_discover(client);
- if (r < 0 && client->attempt >= client->max_attempts)
- goto error;
-
- break;
-
- case DHCP_STATE_INIT_REBOOT:
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
- r = client_send_request(client);
- if (r < 0 && client->attempt >= client->max_attempts)
- goto error;
-
- if (client->state == DHCP_STATE_INIT_REBOOT)
- client->state = DHCP_STATE_REBOOTING;
-
- client->request_sent = time_now;
- break;
-
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_BOUND:
- break;
-
- case DHCP_STATE_STOPPED:
- r = -EINVAL;
- goto error;
- }
-
- if (client->attempt >= TRANSIENT_FAILURE_ATTEMPTS)
- client_notify(client, SD_DHCP_CLIENT_EVENT_TRANSIENT_FAILURE);
-
- return 0;
-
-error:
- client_stop(client, r);
-
- /* Errors were dealt with when stopping the client, don't spill
- errors into the event loop handler */
- return 0;
-}
-
-static int client_initialize_io_events(
- sd_dhcp_client *client,
- sd_event_io_handler_t io_callback) {
-
- int r;
-
- assert(client);
- assert(client->event);
-
- r = sd_event_add_io(client->event, &client->receive_message,
- client->fd, EPOLLIN, io_callback,
- client);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_priority(client->receive_message,
- client->event_priority);
- if (r < 0)
- goto error;
-
- r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message");
- if (r < 0)
- goto error;
-
-error:
- if (r < 0)
- client_stop(client, r);
-
- return 0;
-}
-
-static int client_initialize_time_events(sd_dhcp_client *client) {
- uint64_t usec = 0;
- int r;
-
- assert(client);
- assert(client->event);
-
- if (client->start_delay > 0) {
- assert_se(sd_event_now(client->event, CLOCK_BOOTTIME, &usec) >= 0);
- usec += client->start_delay;
- }
-
- r = event_reset_time(client->event, &client->timeout_resend,
- CLOCK_BOOTTIME,
- usec, 0,
- client_timeout_resend, client,
- client->event_priority, "dhcp4-resend-timer", true);
- if (r < 0)
- client_stop(client, r);
-
- return 0;
-
-}
-
-static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) {
- client_initialize_io_events(client, io_callback);
- client_initialize_time_events(client);
-
- return 0;
-}
-
-static int client_start_delayed(sd_dhcp_client *client) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(client->event, -EINVAL);
- assert_return(client->ifindex > 0, -EINVAL);
- assert_return(client->fd < 0, -EBUSY);
- assert_return(client->xid == 0, -EINVAL);
- assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT), -EBUSY);
-
- client->xid = random_u32();
-
- r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
- client->mac_addr, client->mac_addr_len,
- client->bcast_addr, client->bcast_addr_len,
- client->arp_type, client->port);
- if (r < 0) {
- client_stop(client, r);
- return r;
- }
- client->fd = r;
-
- if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT))
- client->start_time = now(CLOCK_BOOTTIME);
-
- return client_initialize_events(client, client_receive_message_raw);
-}
-
-static int client_start(sd_dhcp_client *client) {
- client->start_delay = 0;
- return client_start_delayed(client);
-}
-
-static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_dhcp_client *client = userdata;
- DHCP_CLIENT_DONT_DESTROY(client);
-
- log_dhcp_client(client, "EXPIRED");
-
- client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
-
- /* lease was lost, start over if not freed or stopped in callback */
- if (client->state != DHCP_STATE_STOPPED) {
- client_initialize(client);
- client_start(client);
- }
-
- return 0;
-}
-
-static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_dhcp_client *client = userdata;
- DHCP_CLIENT_DONT_DESTROY(client);
- int r;
-
- assert(client);
-
- client->receive_message = sd_event_source_disable_unref(client->receive_message);
- client->fd = safe_close(client->fd);
-
- client->state = DHCP_STATE_REBINDING;
- client->attempt = 0;
-
- r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
- client->mac_addr, client->mac_addr_len,
- client->bcast_addr, client->bcast_addr_len,
- client->arp_type, client->port);
- if (r < 0) {
- client_stop(client, r);
- return 0;
- }
- client->fd = r;
-
- return client_initialize_events(client, client_receive_message_raw);
-}
-
-static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) {
- sd_dhcp_client *client = userdata;
- DHCP_CLIENT_DONT_DESTROY(client);
-
- if (client->lease)
- client->state = DHCP_STATE_RENEWING;
- else if (client->state != DHCP_STATE_INIT)
- client->state = DHCP_STATE_INIT_REBOOT;
- client->attempt = 0;
-
- return client_initialize_time_events(client);
-}
-
-static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) {
- _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
- int r;
-
- r = dhcp_lease_new(&lease);
- if (r < 0)
- return r;
-
- if (client->client_id_len) {
- r = dhcp_lease_set_client_id(lease,
- (uint8_t *) &client->client_id,
- client->client_id_len);
- if (r < 0)
- return r;
- }
-
- r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL);
- if (r != DHCP_OFFER) {
- log_dhcp_client(client, "received message was not an OFFER, ignoring");
- return -ENOMSG;
- }
-
- lease->next_server = offer->siaddr;
- lease->address = offer->yiaddr;
-
- if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
- lease->lifetime = client->fallback_lease_lifetime;
-
- if (lease->address == 0 ||
- lease->server_address == 0 ||
- lease->lifetime == 0) {
- log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring");
- return -ENOMSG;
- }
-
- if (!lease->have_subnet_mask) {
- r = dhcp_lease_set_default_subnet_mask(lease);
- if (r < 0) {
- log_dhcp_client(client,
- "received lease lacks subnet mask, "
- "and a fallback one cannot be generated, ignoring");
- return -ENOMSG;
- }
- }
-
- sd_dhcp_lease_unref(client->lease);
- client->lease = TAKE_PTR(lease);
-
- if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0)
- return -ENOMSG;
-
- log_dhcp_client(client, "OFFER");
-
- return 0;
-}
-
-static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) {
- int r;
-
- r = dhcp_option_parse(force, len, NULL, NULL, NULL);
- if (r != DHCP_FORCERENEW)
- return -ENOMSG;
-
-#if 0
- log_dhcp_client(client, "FORCERENEW");
-
- return 0;
-#else
- /* FIXME: Ignore FORCERENEW requests until we implement RFC3118 (Authentication for DHCP
- * Messages) and/or RFC6704 (Forcerenew Nonce Authentication), as unauthenticated FORCERENEW
- * requests causes a security issue (TALOS-2020-1142, CVE-2020-13529). */
- log_dhcp_client(client, "Received FORCERENEW, ignoring.");
- return -ENOMSG;
-#endif
-}
-
-static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
- if (a->address != b->address)
- return false;
-
- if (a->subnet_mask != b->subnet_mask)
- return false;
-
- if (a->router_size != b->router_size)
- return false;
-
- for (size_t i = 0; i < a->router_size; i++)
- if (a->router[i].s_addr != b->router[i].s_addr)
- return false;
-
- return true;
-}
-
-static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
- _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
- _cleanup_free_ char *error_message = NULL;
- int r;
-
- r = dhcp_lease_new(&lease);
- if (r < 0)
- return r;
-
- if (client->client_id_len) {
- r = dhcp_lease_set_client_id(lease,
- (uint8_t *) &client->client_id,
- client->client_id_len);
- if (r < 0)
- return r;
- }
-
- r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message);
- if (r == DHCP_NAK) {
- log_dhcp_client(client, "NAK: %s", strna(error_message));
- return -EADDRNOTAVAIL;
- }
-
- if (r != DHCP_ACK) {
- log_dhcp_client(client, "received message was not an ACK, ignoring");
- return -ENOMSG;
- }
-
- lease->next_server = ack->siaddr;
-
- lease->address = ack->yiaddr;
-
- if (lease->address == INADDR_ANY ||
- lease->server_address == INADDR_ANY ||
- lease->lifetime == 0) {
- log_dhcp_client(client, "received lease lacks address, server "
- "address or lease lifetime, ignoring");
- return -ENOMSG;
- }
-
- if (lease->subnet_mask == INADDR_ANY) {
- r = dhcp_lease_set_default_subnet_mask(lease);
- if (r < 0) {
- log_dhcp_client(client,
- "received lease lacks subnet mask, "
- "and a fallback one cannot be generated, ignoring");
- return -ENOMSG;
- }
- }
-
- r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
- if (client->lease) {
- if (lease_equal(client->lease, lease))
- r = SD_DHCP_CLIENT_EVENT_RENEW;
- else
- r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
-
- client->lease = sd_dhcp_lease_unref(client->lease);
- }
-
- client->lease = TAKE_PTR(lease);
-
- log_dhcp_client(client, "ACK");
-
- return r;
-}
-
-static int client_set_lease_timeouts(sd_dhcp_client *client) {
- usec_t time_now;
- int r;
-
- assert(client);
- assert(client->event);
- assert(client->lease);
- assert(client->lease->lifetime);
-
- /* don't set timers for infinite leases */
- if (client->lease->lifetime == 0xffffffff) {
- (void) event_source_disable(client->timeout_t1);
- (void) event_source_disable(client->timeout_t2);
- (void) event_source_disable(client->timeout_expire);
-
- return 0;
- }
-
- r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- return r;
- assert(client->request_sent <= time_now);
-
- /* verify that 0 < t2 < lifetime */
- if (client->lease->t2 == 0 || client->lease->t2 >= client->lease->lifetime)
- client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
- /* verify that 0 < t1 < lifetime */
- if (client->lease->t1 == 0 || client->lease->t1 >= client->lease->t2)
- client->lease->t1 = T1_DEFAULT(client->lease->lifetime);
- /* now, if t1 >= t2, t1 *must* be T1_DEFAULT, since the previous check
- * could not evalate to false if t1 >= t2; so setting t2 to T2_DEFAULT
- * guarantees t1 < t2. */
- if (client->lease->t1 >= client->lease->t2)
- client->lease->t2 = T2_DEFAULT(client->lease->lifetime);
-
- client->expire_time = client->request_sent + client->lease->lifetime * USEC_PER_SEC;
- client->t1_time = client->request_sent + client->lease->t1 * USEC_PER_SEC;
- client->t2_time = client->request_sent + client->lease->t2 * USEC_PER_SEC;
-
- /* RFC2131 section 4.4.5:
- * Times T1 and T2 SHOULD be chosen with some random "fuzz".
- * Since the RFC doesn't specify here the exact 'fuzz' to use,
- * we use the range from section 4.1: -1 to +1 sec. */
- client->t1_time = usec_sub_signed(client->t1_time, RFC2131_RANDOM_FUZZ);
- client->t2_time = usec_sub_signed(client->t2_time, RFC2131_RANDOM_FUZZ);
-
- /* after fuzzing, ensure t2 is still >= t1 */
- client->t2_time = MAX(client->t1_time, client->t2_time);
-
- /* arm lifetime timeout */
- r = event_reset_time(client->event, &client->timeout_expire,
- CLOCK_BOOTTIME,
- client->expire_time, 10 * USEC_PER_MSEC,
- client_timeout_expire, client,
- client->event_priority, "dhcp4-lifetime", true);
- if (r < 0)
- return r;
-
- /* don't arm earlier timeouts if this has already expired */
- if (client->expire_time <= time_now)
- return 0;
-
- log_dhcp_client(client, "lease expires in %s",
- FORMAT_TIMESPAN(client->expire_time - time_now, USEC_PER_SEC));
-
- /* arm T2 timeout */
- r = event_reset_time(client->event, &client->timeout_t2,
- CLOCK_BOOTTIME,
- client->t2_time, 10 * USEC_PER_MSEC,
- client_timeout_t2, client,
- client->event_priority, "dhcp4-t2-timeout", true);
- if (r < 0)
- return r;
-
- /* don't arm earlier timeout if this has already expired */
- if (client->t2_time <= time_now)
- return 0;
-
- log_dhcp_client(client, "T2 expires in %s",
- FORMAT_TIMESPAN(client->t2_time - time_now, USEC_PER_SEC));
-
- /* arm T1 timeout */
- r = event_reset_time(client->event, &client->timeout_t1,
- CLOCK_BOOTTIME,
- client->t1_time, 10 * USEC_PER_MSEC,
- client_timeout_t1, client,
- client->event_priority, "dhcp4-t1-timer", true);
- if (r < 0)
- return r;
-
- if (client->t1_time > time_now)
- log_dhcp_client(client, "T1 expires in %s",
- FORMAT_TIMESPAN(client->t1_time - time_now, USEC_PER_SEC));
-
- return 0;
-}
-
-static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) {
- DHCP_CLIENT_DONT_DESTROY(client);
- int r, notify_event;
-
- assert(client);
- assert(client->event);
- assert(message);
-
- switch (client->state) {
- case DHCP_STATE_SELECTING:
-
- r = client_handle_offer(client, message, len);
- if (r == -ENOMSG)
- return 0; /* invalid message, let's ignore it */
- if (r < 0)
- goto error;
-
- client->state = DHCP_STATE_REQUESTING;
- client->attempt = 0;
-
- r = event_reset_time(client->event, &client->timeout_resend,
- CLOCK_BOOTTIME,
- 0, 0,
- client_timeout_resend, client,
- client->event_priority, "dhcp4-resend-timer", true);
- break;
-
- case DHCP_STATE_REBOOTING:
- case DHCP_STATE_REQUESTING:
- case DHCP_STATE_RENEWING:
- case DHCP_STATE_REBINDING:
-
- r = client_handle_ack(client, message, len);
- if (r == -ENOMSG)
- return 0; /* invalid message, let's ignore it */
- if (r == -EADDRNOTAVAIL) {
- /* got a NAK, let's restart the client */
- client_notify(client, SD_DHCP_CLIENT_EVENT_EXPIRED);
-
- r = client_initialize(client);
- if (r < 0)
- goto error;
-
- r = client_start_delayed(client);
- if (r < 0)
- goto error;
-
- log_dhcp_client(client, "REBOOT in %s", FORMAT_TIMESPAN(client->start_delay, USEC_PER_SEC));
-
- client->start_delay = CLAMP(client->start_delay * 2,
- RESTART_AFTER_NAK_MIN_USEC, RESTART_AFTER_NAK_MAX_USEC);
- return 0;
- }
- if (r < 0)
- goto error;
-
- if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING))
- notify_event = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
- else
- notify_event = r;
-
- client->start_delay = 0;
- (void) event_source_disable(client->timeout_resend);
- client->receive_message = sd_event_source_disable_unref(client->receive_message);
- client->fd = safe_close(client->fd);
-
- client->state = DHCP_STATE_BOUND;
- client->attempt = 0;
-
- client->last_addr = client->lease->address;
-
- r = client_set_lease_timeouts(client);
- if (r < 0) {
- log_dhcp_client(client, "could not set lease timeouts");
- goto error;
- }
-
- r = dhcp_network_bind_udp_socket(client->ifindex, client->lease->address, client->port, client->ip_service_type);
- if (r < 0) {
- log_dhcp_client(client, "could not bind UDP socket");
- goto error;
- }
-
- client->fd = r;
-
- client_initialize_io_events(client, client_receive_message_udp);
-
- if (IN_SET(client->state, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING) &&
- notify_event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE)
- /* FIXME: hmm, maybe this is a bug... */
- log_dhcp_client(client, "client_handle_ack() returned SD_DHCP_CLIENT_EVENT_IP_ACQUIRE while DHCP client is %s the address, skipping callback.",
- client->state == DHCP_STATE_RENEWING ? "renewing" : "rebinding");
- else
- client_notify(client, notify_event);
- break;
-
- case DHCP_STATE_BOUND:
- r = client_handle_forcerenew(client, message, len);
- if (r == -ENOMSG)
- return 0; /* invalid message, let's ignore it */
- if (r < 0)
- goto error;
-
- r = client_timeout_t1(NULL, 0, client);
- break;
-
- case DHCP_STATE_INIT:
- case DHCP_STATE_INIT_REBOOT:
- r = 0;
- break;
-
- case DHCP_STATE_STOPPED:
- r = -EINVAL;
- goto error;
- default:
- assert_not_reached();
- }
-
-error:
- if (r < 0)
- client_stop(client, r);
-
- return r;
-}
-
-static int client_receive_message_udp(
- sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata) {
-
- sd_dhcp_client *client = userdata;
- _cleanup_free_ DHCPMessage *message = NULL;
- const uint8_t *expected_chaddr = NULL;
- uint8_t expected_hlen = 0;
- ssize_t len, buflen;
-
- assert(s);
- assert(client);
-
- buflen = next_datagram_size_fd(fd);
- if (buflen < 0) {
- if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
- return 0;
-
- log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
- return 0;
- }
-
- message = malloc0(buflen);
- if (!message)
- return -ENOMEM;
-
- len = recv(fd, message, buflen, 0);
- if (len < 0) {
- if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
- return 0;
-
- log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
- return 0;
- }
- if ((size_t) len < sizeof(DHCPMessage)) {
- log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
- return 0;
- }
-
- if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) {
- log_dhcp_client(client, "Not a DHCP message: ignoring");
- return 0;
- }
-
- if (message->op != BOOTREPLY) {
- log_dhcp_client(client, "Not a BOOTREPLY message: ignoring");
- return 0;
- }
-
- if (message->htype != client->arp_type) {
- log_dhcp_client(client, "Packet type does not match client type");
- return 0;
- }
-
- if (client->arp_type == ARPHRD_ETHER) {
- expected_hlen = ETH_ALEN;
- expected_chaddr = &client->mac_addr[0];
- }
-
- if (message->hlen != expected_hlen) {
- log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen);
- return 0;
- }
-
- if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) {
- log_dhcp_client(client, "Received chaddr does not match expected: ignoring");
- return 0;
- }
-
- if (client->state != DHCP_STATE_BOUND &&
- be32toh(message->xid) != client->xid) {
- /* in BOUND state, we may receive FORCERENEW with xid set by server,
- so ignore the xid in this case */
- log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring",
- be32toh(message->xid), client->xid);
- return 0;
- }
-
- log_dhcp_client(client, "Received message from UDP socket, processing.");
- (void) client_handle_message(client, message, len);
- return 0;
-}
-
-static int client_receive_message_raw(
- sd_event_source *s,
- int fd,
- uint32_t revents,
- void *userdata) {
-
- sd_dhcp_client *client = userdata;
- _cleanup_free_ DHCPPacket *packet = NULL;
- CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
- struct iovec iov = {};
- struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = &control,
- .msg_controllen = sizeof(control),
- };
- struct cmsghdr *cmsg;
- bool checksum = true;
- ssize_t buflen, len;
- int r;
-
- assert(s);
- assert(client);
-
- buflen = next_datagram_size_fd(fd);
- if (buflen < 0) {
- if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
- return 0;
-
- log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
- return 0;
- }
-
- packet = malloc0(buflen);
- if (!packet)
- return -ENOMEM;
-
- iov = IOVEC_MAKE(packet, buflen);
-
- len = recvmsg_safe(fd, &msg, 0);
- if (len < 0) {
- if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
- return 0;
-
- log_dhcp_client_errno(client, len, "Could not receive message from raw socket, ignoring: %m");
- return 0;
- }
- if ((size_t) len < sizeof(DHCPPacket))
- return 0;
-
- cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
- if (cmsg) {
- struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
- checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
- }
-
- r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
- if (r < 0)
- return 0;
-
- len -= DHCP_IP_UDP_SIZE;
-
- log_dhcp_client(client, "Received message from RAW socket, processing.");
- (void) client_handle_message(client, &packet->dhcp, len);
- return 0;
-}
-
-int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
- assert_return(client->fd >= 0, -EINVAL);
-
- if (!client->lease)
- return 0;
-
- client->start_delay = 0;
- client->attempt = 1;
- client->state = DHCP_STATE_RENEWING;
-
- return client_initialize_time_events(client);
-}
-
-int sd_dhcp_client_is_running(sd_dhcp_client *client) {
- if (!client)
- return 0;
-
- return !IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED);
-}
-
-int sd_dhcp_client_start(sd_dhcp_client *client) {
- int r;
-
- assert_return(client, -EINVAL);
-
- r = client_initialize(client);
- if (r < 0)
- return r;
-
- /* RFC7844 section 3.3:
- SHOULD perform a complete four-way handshake, starting with a
- DHCPDISCOVER, to obtain a new address lease. If the client can
- ascertain that this is exactly the same network to which it was
- previously connected, and if the link-layer address did not change,
- the client MAY issue a DHCPREQUEST to try to reclaim the current
- address. */
- if (client->last_addr && !client->anonymize)
- client->state = DHCP_STATE_INIT_REBOOT;
-
- r = client_start(client);
- if (r >= 0)
- log_dhcp_client(client, "STARTED on ifindex %i", client->ifindex);
-
- return r;
-}
-
-int sd_dhcp_client_send_release(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
- assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
- assert_return(client->lease, -EUNATCH);
-
- _cleanup_free_ DHCPPacket *release = NULL;
- size_t optoffset, optlen;
- int r;
-
- r = client_message_init(client, &release, DHCP_RELEASE, &optlen, &optoffset);
- if (r < 0)
- return r;
-
- /* Fill up release IP and MAC */
- release->dhcp.ciaddr = client->lease->address;
- memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
- r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
-
- r = dhcp_network_send_udp_socket(client->fd,
- client->lease->server_address,
- DHCP_PORT_SERVER,
- &release->dhcp,
- sizeof(DHCPMessage) + optoffset);
- if (r < 0)
- return r;
-
- log_dhcp_client(client, "RELEASE");
-
- return 0;
-}
-
-int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
- assert_return(client->state != DHCP_STATE_STOPPED, -ESTALE);
- assert_return(client->lease, -EUNATCH);
-
- _cleanup_free_ DHCPPacket *release = NULL;
- size_t optoffset, optlen;
- int r;
-
- r = client_message_init(client, &release, DHCP_DECLINE, &optlen, &optoffset);
- if (r < 0)
- return r;
-
- release->dhcp.ciaddr = client->lease->address;
- memcpy(&release->dhcp.chaddr, &client->mac_addr, client->mac_addr_len);
-
- r = dhcp_option_append(&release->dhcp, optlen, &optoffset, 0,
- SD_DHCP_OPTION_END, 0, NULL);
- if (r < 0)
- return r;
-
- r = dhcp_network_send_udp_socket(client->fd,
- client->lease->server_address,
- DHCP_PORT_SERVER,
- &release->dhcp,
- sizeof(DHCPMessage) + optoffset);
- if (r < 0)
- return r;
-
- log_dhcp_client(client, "DECLINE");
-
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
-
- if (client->state != DHCP_STATE_STOPPED) {
- r = sd_dhcp_client_start(client);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int sd_dhcp_client_stop(sd_dhcp_client *client) {
- if (!client)
- return 0;
-
- DHCP_CLIENT_DONT_DESTROY(client);
-
- client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- client->state = DHCP_STATE_STOPPED;
-
- return 0;
-}
-
-int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) {
- int r;
-
- assert_return(client, -EINVAL);
- assert_return(!client->event, -EBUSY);
-
- if (event)
- client->event = sd_event_ref(event);
- else {
- r = sd_event_default(&client->event);
- if (r < 0)
- return 0;
- }
-
- client->event_priority = priority;
-
- return 0;
-}
-
-int sd_dhcp_client_detach_event(sd_dhcp_client *client) {
- assert_return(client, -EINVAL);
-
- client->event = sd_event_unref(client->event);
-
- return 0;
-}
-
-sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) {
- assert_return(client, NULL);
-
- return client->event;
-}
-
-static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
- if (!client)
- return NULL;
-
- log_dhcp_client(client, "FREE");
-
- client_initialize(client);
-
- client->timeout_resend = sd_event_source_unref(client->timeout_resend);
- client->timeout_t1 = sd_event_source_unref(client->timeout_t1);
- client->timeout_t2 = sd_event_source_unref(client->timeout_t2);
- client->timeout_expire = sd_event_source_unref(client->timeout_expire);
-
- sd_dhcp_client_detach_event(client);
-
- set_free(client->req_opts);
- free(client->hostname);
- free(client->vendor_class_identifier);
- free(client->mudurl);
- client->user_class = strv_free(client->user_class);
- ordered_hashmap_free(client->extra_options);
- ordered_hashmap_free(client->vendor_options);
- free(client->ifname);
- return mfree(client);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_client, sd_dhcp_client, dhcp_client_free);
-
-int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
- const uint8_t *opts;
- size_t n_opts;
- int r;
-
- assert_return(ret, -EINVAL);
-
- _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = new(sd_dhcp_client, 1);
- if (!client)
- return -ENOMEM;
-
- *client = (sd_dhcp_client) {
- .n_ref = 1,
- .state = DHCP_STATE_INIT,
- .ifindex = -1,
- .fd = -1,
- .mtu = DHCP_DEFAULT_MIN_SIZE,
- .port = DHCP_PORT_CLIENT,
- .anonymize = !!anonymize,
- .max_attempts = UINT64_MAX,
- .ip_service_type = -1,
- };
- /* NOTE: this could be moved to a function. */
- if (anonymize) {
- n_opts = ELEMENTSOF(default_req_opts_anonymize);
- opts = default_req_opts_anonymize;
- } else {
- n_opts = ELEMENTSOF(default_req_opts);
- opts = default_req_opts;
- }
-
- for (size_t i = 0; i < n_opts; i++) {
- r = sd_dhcp_client_set_request_option(client, opts[i]);
- if (r < 0)
- return r;
- }
-
- *ret = TAKE_PTR(client);
-
- return 0;
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index 4ca5159106..273dd35e04 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -116,9 +116,8 @@ int sd_dhcp6_client_set_mac(
return 0;
}
- memcpy(client->hw_addr.bytes, addr, addr_len);
- client->hw_addr.length = addr_len;
client->arp_type = arp_type;
+ hw_addr_set(&client->hw_addr, addr, addr_len);
return 0;
}
@@ -216,8 +215,8 @@ static int dhcp6_client_set_duid_internal(
client->duid_len = sizeof(client->duid.type) + duid_len;
} else {
- r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length,
- client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len);
+ r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time,
+ client->test_mode, &client->duid, &client->duid_len);
if (r == -EOPNOTSUPP)
return log_dhcp6_client_errno(client, r,
"Failed to set %s. MAC address is not set or "
@@ -300,7 +299,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
if (client->iaid_set)
return 0;
- r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length,
+ r = dhcp_identifier_set_iaid(client->ifindex, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
/* use_mac = */ client->test_mode,
&iaid);
@@ -491,6 +490,14 @@ int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transactio
return 0;
}
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+
+ client->rapid_commit = enable;
+ return 0;
+}
+
int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
assert_return(client, -EINVAL);
@@ -714,9 +721,11 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
break;
case DHCP6_STATE_SOLICITATION:
- r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
- if (r < 0)
- return r;
+ if (client->rapid_commit) {
+ r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
+ if (r < 0)
+ return r;
+ }
r = client_append_common_options_in_managed_mode(client, &opt, &optlen,
&client->ia_na, &client->ia_pd);
@@ -1160,6 +1169,10 @@ static int client_process_advertise_or_rapid_commit_reply(
if (message->type == DHCP6_MESSAGE_REPLY) {
bool rapid_commit;
+ if (!client->rapid_commit)
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+ "Received unexpected reply message, even we sent a solicit message without the rapid commit option, ignoring.");
+
r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit);
if (r < 0)
return r;
@@ -1467,6 +1480,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
.ifindex = -1,
.request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
.fd = -1,
+ .rapid_commit = true,
};
*ret = TAKE_PTR(client);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
index 2475cdb7ed..f588514cb6 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
@@ -467,7 +467,9 @@ static int dhcp6_lease_parse_message(
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r,
+ "Failed to parse option header at offset %zu of total length %zu: %m",
+ offset, len);
switch (optcode) {
case SD_DHCP6_OPTION_CLIENTID:
@@ -477,7 +479,7 @@ static int dhcp6_lease_parse_message(
r = dhcp6_lease_set_clientid(lease, optval, optlen);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r, "Failed to set client ID: %m");
break;
@@ -488,17 +490,17 @@ static int dhcp6_lease_parse_message(
r = dhcp6_lease_set_serverid(lease, optval, optlen);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r, "Failed to set server ID: %m");
break;
case SD_DHCP6_OPTION_PREFERENCE:
if (optlen != 1)
- return -EINVAL;
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received invalid length for preference.");
r = dhcp6_lease_set_preference(lease, optval[0]);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r, "Failed to set preference: %m");
break;
@@ -507,7 +509,7 @@ static int dhcp6_lease_parse_message(
r = dhcp6_option_parse_status(optval, optlen, &msg);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r, "Failed to parse status code: %m");
if (r > 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
@@ -527,9 +529,11 @@ static int dhcp6_lease_parse_message(
r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
if (r == -ENOMEM)
- return r;
- if (r < 0)
+ return log_oom_debug();
+ if (r < 0) {
+ log_dhcp6_client_errno(client, r, "Failed to parse IA_NA option, ignoring: %m");
continue;
+ }
if (lease->ia_na) {
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
@@ -550,9 +554,11 @@ static int dhcp6_lease_parse_message(
r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
if (r == -ENOMEM)
- return r;
- if (r < 0)
+ return log_oom_debug();
+ if (r < 0) {
+ log_dhcp6_client_errno(client, r, "Failed to parse IA_PD option, ignoring: %m");
continue;
+ }
if (lease->ia_pd) {
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
@@ -564,9 +570,12 @@ static int dhcp6_lease_parse_message(
break;
}
case SD_DHCP6_OPTION_RAPID_COMMIT:
+ if (optlen != 0)
+ log_dhcp6_client(client, "Received rapid commit option with an invalid length (%zu), ignoring.", optlen);
+
r = dhcp6_lease_set_rapid_commit(lease);
if (r < 0)
- return r;
+ return log_dhcp6_client_errno(client, r, "Failed to set rapid commit flag: %m");
break;
@@ -607,7 +616,8 @@ static int dhcp6_lease_parse_message(
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
if (optlen != 4)
- return -EINVAL;
+ return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+ "Received information refresh time option with an invalid length (%zu).", optlen);
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
break;
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index cea1c009d6..027a8dffa1 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -49,19 +49,19 @@ static bool event_source_is_offline(sd_event_source *s) {
}
static const char* const event_source_type_table[_SOURCE_EVENT_SOURCE_TYPE_MAX] = {
- [SOURCE_IO] = "io",
- [SOURCE_TIME_REALTIME] = "realtime",
- [SOURCE_TIME_BOOTTIME] = "bootime",
- [SOURCE_TIME_MONOTONIC] = "monotonic",
+ [SOURCE_IO] = "io",
+ [SOURCE_TIME_REALTIME] = "realtime",
+ [SOURCE_TIME_BOOTTIME] = "bootime",
+ [SOURCE_TIME_MONOTONIC] = "monotonic",
[SOURCE_TIME_REALTIME_ALARM] = "realtime-alarm",
[SOURCE_TIME_BOOTTIME_ALARM] = "boottime-alarm",
- [SOURCE_SIGNAL] = "signal",
- [SOURCE_CHILD] = "child",
- [SOURCE_DEFER] = "defer",
- [SOURCE_POST] = "post",
- [SOURCE_EXIT] = "exit",
- [SOURCE_WATCHDOG] = "watchdog",
- [SOURCE_INOTIFY] = "inotify",
+ [SOURCE_SIGNAL] = "signal",
+ [SOURCE_CHILD] = "child",
+ [SOURCE_DEFER] = "defer",
+ [SOURCE_POST] = "post",
+ [SOURCE_EXIT] = "exit",
+ [SOURCE_WATCHDOG] = "watchdog",
+ [SOURCE_INOTIFY] = "inotify",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(event_source_type, int);
@@ -124,10 +124,10 @@ struct sd_event {
Hashmap *inotify_data; /* indexed by priority */
/* A list of inode structures that still have an fd open, that we need to close before the next loop iteration */
- LIST_HEAD(struct inode_data, inode_data_to_close);
+ LIST_HEAD(struct inode_data, inode_data_to_close_list);
/* A list of inotify objects that already have events buffered which aren't processed yet */
- LIST_HEAD(struct inotify_data, inotify_data_buffered);
+ LIST_HEAD(struct inotify_data, buffered_inotify_data_list);
pid_t original_pid;
@@ -420,6 +420,8 @@ fail:
}
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_event, sd_event, event_free);
+#define PROTECT_EVENT(e) \
+ _unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e);
_public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
if (s)
@@ -1692,7 +1694,7 @@ static void event_free_inotify_data(sd_event *e, struct inotify_data *d) {
assert(hashmap_isempty(d->wd));
if (d->buffer_filled > 0)
- LIST_REMOVE(buffered, e->inotify_data_buffered, d);
+ LIST_REMOVE(buffered, e->buffered_inotify_data_list, d);
hashmap_free(d->inodes);
hashmap_free(d->wd);
@@ -1801,10 +1803,10 @@ static void event_free_inode_data(
if (!d)
return;
- assert(LIST_IS_EMPTY(d->event_sources));
+ assert(!d->event_sources);
if (d->fd >= 0) {
- LIST_REMOVE(to_close, e->inode_data_to_close, d);
+ LIST_REMOVE(to_close, e->inode_data_to_close_list, d);
safe_close(d->fd);
}
@@ -1864,7 +1866,7 @@ static void event_gc_inode_data(
if (!d)
return;
- if (!LIST_IS_EMPTY(d->event_sources))
+ if (d->event_sources)
return;
inotify_data = d->inotify_data;
@@ -2066,7 +2068,7 @@ static int event_add_inotify_fd_internal(
}
}
- LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
+ LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
}
/* Link our event source to the inode data object */
@@ -2139,12 +2141,9 @@ static sd_event_source* event_source_free(sd_event_source *s) {
* we still retain a valid event source object after
* the callback. */
- if (s->dispatching) {
- if (s->type == SOURCE_IO)
- source_io_unregister(s);
-
+ if (s->dispatching)
source_disconnect(s);
- } else
+ else
source_free(s);
return NULL;
@@ -2353,7 +2352,7 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
goto fail;
}
- LIST_PREPEND(to_close, s->event->inode_data_to_close, new_inode_data);
+ LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
}
/* Move the event source to the new inode data structure */
@@ -3419,7 +3418,7 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t
assert(n > 0);
d->buffer_filled = (size_t) n;
- LIST_PREPEND(buffered, e->inotify_data_buffered, d);
+ LIST_PREPEND(buffered, e->buffered_inotify_data_list, d);
return 1;
}
@@ -3437,7 +3436,7 @@ static void event_inotify_data_drop(sd_event *e, struct inotify_data *d, size_t
d->buffer_filled -= sz;
if (d->buffer_filled == 0)
- LIST_REMOVE(buffered, e->inotify_data_buffered, d);
+ LIST_REMOVE(buffered, e->buffered_inotify_data_list, d);
}
static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
@@ -3530,7 +3529,7 @@ static int process_inotify(sd_event *e) {
assert(e);
- LIST_FOREACH(buffered, d, e->inotify_data_buffered) {
+ LIST_FOREACH(buffered, d, e->buffered_inotify_data_list) {
r = event_inotify_data_process(e, d);
if (r < 0)
return r;
@@ -3542,8 +3541,8 @@ static int process_inotify(sd_event *e) {
}
static int source_dispatch(sd_event_source *s) {
- _cleanup_(sd_event_unrefp) sd_event *saved_event = NULL;
EventSourceType saved_type;
+ sd_event *saved_event;
int r = 0;
assert(s);
@@ -3555,7 +3554,8 @@ static int source_dispatch(sd_event_source *s) {
/* Similarly, store a reference to the event loop object, so that we can still access it after the
* callback might have invalidated/disconnected the event source. */
- saved_event = sd_event_ref(s->event);
+ saved_event = s->event;
+ PROTECT_EVENT(saved_event);
/* Check if we hit the ratelimit for this event source, and if so, let's disable it. */
assert(!s->ratelimited);
@@ -3756,7 +3756,7 @@ static int dispatch_exit(sd_event *e) {
return 0;
}
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
e->iteration++;
e->state = SD_EVENT_EXITING;
r = source_dispatch(p);
@@ -3827,11 +3827,11 @@ static void event_close_inode_data_fds(sd_event *e) {
* for the inode). Hence, let's close them when entering the first iteration after they were added, as a
* compromise. */
- while ((d = e->inode_data_to_close)) {
+ while ((d = e->inode_data_to_close_list)) {
assert(d->fd >= 0);
d->fd = safe_close(d->fd);
- LIST_REMOVE(to_close, e->inode_data_to_close, d);
+ LIST_REMOVE(to_close, e->inode_data_to_close_list, d);
}
}
@@ -3850,7 +3850,7 @@ _public_ int sd_event_prepare(sd_event *e) {
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);
+ PROTECT_EVENT(e);
if (e->exit_requested)
goto pending;
@@ -3885,7 +3885,7 @@ _public_ int sd_event_prepare(sd_event *e) {
event_close_inode_data_fds(e);
- if (event_next_pending(e) || e->need_process_child || !LIST_IS_EMPTY(e->inotify_data_buffered))
+ if (event_next_pending(e) || e->need_process_child || e->buffered_inotify_data_list)
goto pending;
e->state = SD_EVENT_ARMED;
@@ -3965,7 +3965,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t
n_event_max = MALLOC_ELEMENTSOF(e->event_queue);
/* If we still have inotify data buffered, then query the other fds, but don't wait on it */
- if (!LIST_IS_EMPTY(e->inotify_data_buffered))
+ if (e->buffered_inotify_data_list)
timeout = 0;
for (;;) {
@@ -4180,7 +4180,7 @@ _public_ int sd_event_dispatch(sd_event *e) {
p = event_next_pending(e);
if (p) {
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
e->state = SD_EVENT_RUNNING;
r = source_dispatch(p);
@@ -4232,7 +4232,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
}
/* 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);
+ PROTECT_EVENT(e);
r = sd_event_prepare(e);
if (r == 0)
@@ -4262,7 +4262,7 @@ _public_ int sd_event_loop(sd_event *e) {
assert_return(!event_pid_changed(e), -ECHILD);
assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
- _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+ PROTECT_EVENT(e);
while (e->state != SD_EVENT_FINISHED) {
r = sd_event_run(e, UINT64_MAX);
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index 38449463e2..6f657c2254 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -85,7 +85,7 @@ typedef void (*_sd_destroy_t)(void *userdata);
#endif
#ifndef _SD_ARRAY_STATIC
-# if __STDC_VERSION__ >= 199901L
+# if __STDC_VERSION__ >= 199901L && !defined(__cplusplus)
# define _SD_ARRAY_STATIC static
# else
# define _SD_ARRAY_STATIC
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
index 7fe60c356c..2c66c51b78 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -262,6 +262,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
int request);
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
sd_dhcp6_option *v);
+int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable);
int sd_dhcp6_client_get_lease(
sd_dhcp6_client *client,
diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h
index 13dab0304f..b38db7d473 100644
--- a/src/libnm-systemd-shared/src/basic/alloc-util.h
+++ b/src/libnm-systemd-shared/src/basic/alloc-util.h
@@ -50,16 +50,30 @@ typedef void* (*mfree_func_t)(void *p);
#define malloc0(n) (calloc(1, (n) ?: 1))
-#define free_and_replace(a, b) \
+#define free_and_replace_full(a, b, free_func) \
({ \
typeof(a)* _a = &(a); \
typeof(b)* _b = &(b); \
- free(*_a); \
+ free_func(*_a); \
*_a = *_b; \
*_b = NULL; \
0; \
})
+#define free_and_replace(a, b) \
+ free_and_replace_full(a, b, free)
+
+/* This is similar to free_and_replace_full(), but NULL is not assigned to 'b', and its reference counter is
+ * increased. */
+#define unref_and_replace_full(a, b, ref_func, unref_func) \
+ ({ \
+ typeof(a)* _a = &(a); \
+ typeof(b) _b = ref_func(b); \
+ unref_func(*_a); \
+ *_a = _b; \
+ 0; \
+ })
+
void* memdup(const void *p, size_t l) _alloc_(2);
void* memdup_suffix0(const void *p, size_t l); /* We can't use _alloc_() here, since we return a buffer one byte larger than the specified size */
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.c b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
index c609ea6ce0..0a6a54f8f1 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.c
@@ -33,6 +33,15 @@ char *hw_addr_to_string_full(
return buffer;
}
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length) {
+ assert(addr);
+ assert(length <= HW_ADDR_MAX_SIZE);
+
+ addr->length = length;
+ memcpy_safe(addr->bytes, bytes, length);
+ return addr;
+}
+
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b) {
int r;
diff --git a/src/libnm-systemd-shared/src/basic/ether-addr-util.h b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
index 32f45fe813..83ed77d634 100644
--- a/src/libnm-systemd-shared/src/basic/ether-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/ether-addr-util.h
@@ -52,6 +52,8 @@ static inline char *hw_addr_to_string(const struct hw_addr_data *addr, char buff
#define HW_ADDR_NULL ((const struct hw_addr_data){})
+struct hw_addr_data *hw_addr_set(struct hw_addr_data *addr, const uint8_t *bytes, size_t length);
+
void hw_addr_hash_func(const struct hw_addr_data *p, struct siphash *state);
int hw_addr_compare(const struct hw_addr_data *a, const struct hw_addr_data *b);
static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_addr_data *b) {
diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c
index 6c1de92a26..00591d6c2d 100644
--- a/src/libnm-systemd-shared/src/basic/fd-util.c
+++ b/src/libnm-systemd-shared/src/basic/fd-util.c
@@ -3,7 +3,9 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/btrfs.h>
+#if WANT_LINUX_FS_H
#include <linux/fs.h>
+#endif
#include <linux/magic.h>
#include <sys/ioctl.h>
#include <sys/resource.h>
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.c b/src/libnm-systemd-shared/src/basic/hash-funcs.c
index 6addb76f1b..5fac467185 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.c
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.c
@@ -4,6 +4,7 @@
#include "hash-funcs.h"
#include "path-util.h"
+#include "strv.h"
void string_hash_func(const char *p, struct siphash *state) {
siphash24_compress(p, strlen(p) + 1, state);
@@ -15,6 +16,9 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
char, string_hash_func, string_compare_func, free,
void, free);
+DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
+ char, string_hash_func, string_compare_func, free,
+ char*, strv_free);
void path_hash_func(const char *q, struct siphash *state) {
bool add_slash = false;
diff --git a/src/libnm-systemd-shared/src/basic/hash-funcs.h b/src/libnm-systemd-shared/src/basic/hash-funcs.h
index c537c6af7e..c14302ec72 100644
--- a/src/libnm-systemd-shared/src/basic/hash-funcs.h
+++ b/src/libnm-systemd-shared/src/basic/hash-funcs.h
@@ -78,6 +78,7 @@ void string_hash_func(const char *p, struct siphash *state);
extern const struct hash_ops string_hash_ops;
extern const struct hash_ops string_hash_ops_free;
extern const struct hash_ops string_hash_ops_free_free;
+extern const struct hash_ops string_hash_ops_free_strv_free;
void path_hash_func(const char *p, struct siphash *state);
extern const struct hash_ops path_hash_ops;
diff --git a/src/libnm-systemd-shared/src/basic/hashmap.h b/src/libnm-systemd-shared/src/basic/hashmap.h
index eafc08f658..91b3fe862b 100644
--- a/src/libnm-systemd-shared/src/basic/hashmap.h
+++ b/src/libnm-systemd-shared/src/basic/hashmap.h
@@ -90,12 +90,7 @@ OrderedHashmap* _ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DE
#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS)
#define hashmap_free_and_replace(a, b) \
- ({ \
- hashmap_free(a); \
- (a) = (b); \
- (b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, hashmap_free)
HashmapBase* _hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
static inline Hashmap* hashmap_free(Hashmap *h) {
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c
index 6f8ffaf259..fe356c6d15 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.c
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c
@@ -49,6 +49,20 @@ bool in4_addr_is_link_local(const struct in_addr *a) {
return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
}
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a) {
+ assert(a);
+
+ if (!in4_addr_is_link_local(a))
+ return false;
+
+ /* 169.254.0.0/24 and 169.254.255.0/24 must not be used for the dynamic IPv4LL assignment.
+ * See RFC 3927 Section 2.1:
+ * The IPv4 prefix 169.254/16 is registered with the IANA for this purpose. The first 256 and last
+ * 256 addresses in the 169.254/16 prefix are reserved for future use and MUST NOT be selected by a
+ * host using this dynamic configuration mechanism. */
+ return !IN_SET(be32toh(a->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
+}
+
bool in6_addr_is_link_local(const struct in6_addr *a) {
assert(a);
diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h
index c1e7ef965d..fbc60436c7 100644
--- a/src/libnm-systemd-shared/src/basic/in-addr-util.h
+++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h
@@ -44,6 +44,7 @@ static inline bool in_addr_data_is_set(const struct in_addr_data *a) {
int in_addr_is_multicast(int family, const union in_addr_union *u);
bool in4_addr_is_link_local(const struct in_addr *a);
+bool in4_addr_is_link_local_dynamic(const struct in_addr *a);
bool in6_addr_is_link_local(const struct in6_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h
index 58e83a6cb2..ca30039690 100644
--- a/src/libnm-systemd-shared/src/basic/list.h
+++ b/src/libnm-systemd-shared/src/basic/list.h
@@ -170,9 +170,6 @@
i != (p); \
i = i->name##_next ? i->name##_next : (head))
-#define LIST_IS_EMPTY(head) \
- (!(head))
-
/* Join two lists tail to head: a->b, c->d to a->b->c->d and de-initialise second list */
#define LIST_JOIN(name,a,b) \
do { \
diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h
index bcac55620e..237117db12 100644
--- a/src/libnm-systemd-shared/src/basic/macro.h
+++ b/src/libnm-systemd-shared/src/basic/macro.h
@@ -87,10 +87,6 @@
_Pragma("GCC diagnostic push")
#endif
-#define DISABLE_WARNING_FLOAT_EQUAL \
- _Pragma("GCC diagnostic push"); \
- _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
-
#define DISABLE_WARNING_TYPE_LIMITS \
_Pragma("GCC diagnostic push"); \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c
index 94527eff4c..e40ab3f5b6 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.c
+++ b/src/libnm-systemd-shared/src/basic/path-util.c
@@ -1055,7 +1055,7 @@ int path_extract_filename(const char *path, char **ret) {
return -ENOMEM;
*ret = TAKE_PTR(a);
- return strlen(c) > (size_t)r ? O_DIRECTORY : 0;
+ return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
}
int path_extract_directory(const char *path, char **ret) {
@@ -1109,7 +1109,7 @@ bool filename_is_valid(const char *p) {
if (isempty(p))
return false;
- if (dot_or_dot_dot(p))
+ if (dot_or_dot_dot(p)) /* Yes, in this context we consider "." and ".." invalid */
return false;
e = strchrnul(p, '/');
diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h
index 553aa4fb58..bb20087221 100644
--- a/src/libnm-systemd-shared/src/basic/path-util.h
+++ b/src/libnm-systemd-shared/src/basic/path-util.h
@@ -43,12 +43,16 @@
#endif
static inline bool is_path(const char *p) {
- assert(p);
+ if (!p) /* A NULL pointer is definitely not a path */
+ return false;
+
return strchr(p, '/');
}
static inline bool path_is_absolute(const char *p) {
- assert(p);
+ if (!p) /* A NULL pointer is definitely not an absolute path */
+ return false;
+
return p[0] == '/';
}
@@ -157,10 +161,10 @@ int path_extract_directory(const char *path, char **ret);
bool filename_is_valid(const char *p) _pure_;
bool path_is_valid_full(const char *p, bool accept_dot_dot) _pure_;
static inline bool path_is_valid(const char *p) {
- return path_is_valid_full(p, true);
+ return path_is_valid_full(p, /* accept_dot_dot= */ true);
}
static inline bool path_is_safe(const char *p) {
- return path_is_valid_full(p, false);
+ return path_is_valid_full(p, /* accept_dot_dot= */ false);
}
bool path_is_normalized(const char *p) _pure_;
diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c
index 6980e0c4f6..3dd6f4df59 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.c
+++ b/src/libnm-systemd-shared/src/basic/process-util.c
@@ -1147,7 +1147,7 @@ void reset_cached_pid(void) {
pid_t getpid_cached(void) {
static bool installed = false;
- pid_t current_value;
+ pid_t current_value = CACHED_PID_UNSET;
/* getpid_cached() is much like getpid(), but caches the value in local memory, to avoid having to invoke a
* system call each time. This restores glibc behaviour from before 2.24, when getpid() was unconditionally
@@ -1158,7 +1158,13 @@ pid_t getpid_cached(void) {
* https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c579f48edba88380635ab98cb612030e3ed8691e
*/
- current_value = __sync_val_compare_and_swap(&cached_pid, CACHED_PID_UNSET, CACHED_PID_BUSY);
+ __atomic_compare_exchange_n(
+ &cached_pid,
+ &current_value,
+ CACHED_PID_BUSY,
+ false,
+ __ATOMIC_SEQ_CST,
+ __ATOMIC_SEQ_CST);
switch (current_value) {
@@ -1579,6 +1585,30 @@ bool invoked_as(char *argv[], const char *token) {
return strstr(last_path_component(argv[0]), token);
}
+bool invoked_by_systemd(void) {
+ int r;
+
+ /* If the process is directly executed by PID1 (e.g. ExecStart= or generator), systemd-importd,
+ * or systemd-homed, then $SYSTEMD_EXEC_PID= is set, and read the command line. */
+ const char *e = getenv("SYSTEMD_EXEC_PID");
+ if (!e)
+ return false;
+
+ if (streq(e, "*"))
+ /* For testing. */
+ return true;
+
+ pid_t p;
+ r = parse_pid(e, &p);
+ if (r < 0) {
+ /* We know that systemd sets the variable correctly. Something else must have set it. */
+ log_debug_errno(r, "Failed to parse \"SYSTEMD_EXEC_PID=%s\", ignoring: %m", e);
+ return false;
+ }
+
+ return getpid_cached() == p;
+}
+
_noreturn_ void freeze(void) {
log_close();
diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h
index c07575e580..f8c374a310 100644
--- a/src/libnm-systemd-shared/src/basic/process-util.h
+++ b/src/libnm-systemd-shared/src/basic/process-util.h
@@ -190,6 +190,8 @@ int setpriority_closest(int priority);
bool invoked_as(char *argv[], const char *token);
+bool invoked_by_systemd(void);
+
_noreturn_ void freeze(void);
bool argv_looks_like_help(int argc, char **argv);
diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h
index 52cf63e2dd..618e729744 100644
--- a/src/libnm-systemd-shared/src/basic/set.h
+++ b/src/libnm-systemd-shared/src/basic/set.h
@@ -6,12 +6,7 @@
#include "macro.h"
#define set_free_and_replace(a, b) \
- ({ \
- set_free(a); \
- (a) = (b); \
- (b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, set_free)
Set* _set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c
index 64c2f80f3c..c31b4d89d0 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.c
+++ b/src/libnm-systemd-shared/src/basic/stat-util.c
@@ -35,31 +35,23 @@ int is_symlink(const char *path) {
return !!S_ISLNK(info.st_mode);
}
-int is_dir(const char* path, bool follow) {
+int is_dir_full(int atfd, const char* path, bool follow) {
struct stat st;
int r;
- assert(path);
+ assert(atfd >= 0 || atfd == AT_FDCWD);
+ assert(atfd >= 0 || path);
- if (follow)
- r = stat(path, &st);
+ if (path)
+ r = fstatat(atfd, path, &st, follow ? 0 : AT_SYMLINK_NOFOLLOW);
else
- r = lstat(path, &st);
+ r = fstat(atfd, &st);
if (r < 0)
return -errno;
return !!S_ISDIR(st.st_mode);
}
-int is_dir_fd(int fd) {
- struct stat st;
-
- if (fstat(fd, &st) < 0)
- return -errno;
-
- return !!S_ISDIR(st.st_mode);
-}
-
int is_device_node(const char *path) {
struct stat info;
diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h
index 7f0b3dc0af..56f15534aa 100644
--- a/src/libnm-systemd-shared/src/basic/stat-util.h
+++ b/src/libnm-systemd-shared/src/basic/stat-util.h
@@ -13,8 +13,13 @@
#include "missing_stat.h"
int is_symlink(const char *path);
-int is_dir(const char *path, bool follow);
-int is_dir_fd(int fd);
+int is_dir_full(int atfd, const char *fname, bool follow);
+static inline int is_dir(const char *path, bool follow) {
+ return is_dir_full(AT_FDCWD, path, follow);
+}
+static inline int is_dir_fd(int fd) {
+ return is_dir_full(fd, NULL, false);
+}
int is_device_node(const char *path);
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h
index 072739df35..87ec6337bd 100644
--- a/src/libnm-systemd-shared/src/basic/strv.h
+++ b/src/libnm-systemd-shared/src/basic/strv.h
@@ -258,14 +258,7 @@ int strv_extend_n(char ***l, const char *value, size_t n);
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
#define strv_free_and_replace(a, b) \
- ({ \
- char ***_a = &(a); \
- char ***_b = &(b); \
- strv_free(*_a); \
- (*_a) = (*_b); \
- (*_b) = NULL; \
- 0; \
- })
+ free_and_replace_full(a, b, strv_free)
extern const struct hash_ops string_strv_hash_ops;
int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value HASHMAP_DEBUG_PARAMS);
diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c
index abbc4ad5cd..26d59de123 100644
--- a/src/libnm-systemd-shared/src/basic/time-util.c
+++ b/src/libnm-systemd-shared/src/basic/time-util.c
@@ -591,7 +591,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
t = b;
}
- n = MIN((size_t) k, l);
+ n = MIN((size_t) k, l-1);
l -= n;
p += n;
diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
index 3c38afcab8..8b483f0b50 100644
--- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
+++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h
@@ -42,7 +42,7 @@
# define _alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__)))
#endif
-#if __GNUC__ >= 7 || __clang__
+#if __GNUC__ >= 7 || (defined(__clang__) && __clang_major__ >= 10)
# define _fallthrough_ __attribute__((__fallthrough__))
#else
# define _fallthrough_
@@ -95,6 +95,20 @@
_expr_; \
})
+#define ASSERT_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert(_expr_ >= _zero); \
+ _expr_; \
+ })
+
+#define ASSERT_SE_NONNEG(expr) \
+ ({ \
+ typeof(expr) _expr_ = (expr), _zero = 0; \
+ assert_se(_expr_ >= _zero); \
+ _expr_; \
+ })
+
#define assert_cc(expr) static_assert(expr, #expr)
@@ -106,10 +120,10 @@
* on this macro will run concurrently to all other code conditionalized
* the same way, there's no ordering or completion enforced. */
#define ONCE __ONCE(UNIQ_T(_once_, UNIQ))
-#define __ONCE(o) \
- ({ \
- static bool (o) = false; \
- __sync_bool_compare_and_swap(&(o), false, true); \
+#define __ONCE(o) \
+ ({ \
+ static bool (o) = false; \
+ __atomic_exchange_n(&(o), true, __ATOMIC_SEQ_CST); \
})
#undef MAX
@@ -186,6 +200,19 @@
MIN(_c, z); \
})
+/* Returns true if the passed integer is a positive power of two */
+#define CONST_ISPOWEROF2(x) \
+ ((x) > 0 && ((x) & ((x) - 1)) == 0)
+
+#define ISPOWEROF2(x) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(x), \
+ CONST_ISPOWEROF2(x), \
+ ({ \
+ const typeof(x) _x = (x); \
+ CONST_ISPOWEROF2(_x); \
+ }))
+
#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b))
#define __LESS_BY(aq, a, bq, b) \
({ \
@@ -300,16 +327,7 @@
})
static inline size_t ALIGN_TO(size_t l, size_t ali) {
- /* Check that alignment is exponent of 2 */
-#if SIZE_MAX == UINT_MAX
- assert(__builtin_popcount(ali) == 1);
-#elif SIZE_MAX == ULONG_MAX
- assert(__builtin_popcountl(ali) == 1);
-#elif SIZE_MAX == ULLONG_MAX
- assert(__builtin_popcountll(ali) == 1);
-#else
- #error "Unexpected size_t"
-#endif
+ assert(ISPOWEROF2(ali));
if (l > SIZE_MAX - (ali - 1))
return SIZE_MAX; /* indicate overflow */
@@ -330,7 +348,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
__builtin_choose_expr( \
__builtin_constant_p(l) && \
__builtin_constant_p(ali) && \
- __builtin_popcountll(ali) == 1 && /* is power of 2? */ \
+ CONST_ISPOWEROF2(ali) && \
(l <= SIZE_MAX - (ali - 1)), /* overflow? */ \
((l) + (ali) - 1) & ~((ali) - 1), \
VOID_0)