summaryrefslogtreecommitdiffstats
path: root/src/libsystemd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 03:50:40 +0000
commitfc53809803cd2bc2434e312b19a18fa36776da12 (patch)
treeb4b43bd6538f51965ce32856e9c053d0f90919c8 /src/libsystemd
parentAdding upstream version 255.5. (diff)
downloadsystemd-fc53809803cd2bc2434e312b19a18fa36776da12.tar.xz
systemd-fc53809803cd2bc2434e312b19a18fa36776da12.zip
Adding upstream version 256.upstream/256
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/libsystemd-network/dhcp-client-id-internal.h60
-rw-r--r--src/libsystemd-network/dhcp-duid-internal.h (renamed from src/libsystemd-network/dhcp-identifier.h)60
-rw-r--r--src/libsystemd-network/dhcp-identifier.c209
-rw-r--r--src/libsystemd-network/dhcp-lease-internal.h6
-rw-r--r--src/libsystemd-network/dhcp-network.c9
-rw-r--r--src/libsystemd-network/dhcp-option.c4
-rw-r--r--src/libsystemd-network/dhcp-server-internal.h33
-rw-r--r--src/libsystemd-network/dhcp-server-lease-internal.h45
-rw-r--r--src/libsystemd-network/dhcp6-internal.h10
-rw-r--r--src/libsystemd-network/dhcp6-network.c18
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h8
-rw-r--r--src/libsystemd-network/fuzz-dhcp-client.c20
-rw-r--r--src/libsystemd-network/fuzz-dhcp-server.c41
-rw-r--r--src/libsystemd-network/fuzz-dhcp6-client.c4
-rw-r--r--src/libsystemd-network/fuzz-lldp-rx.c9
-rw-r--r--src/libsystemd-network/fuzz-ndisc-rs.c74
-rw-r--r--src/libsystemd-network/icmp6-packet.c131
-rw-r--r--src/libsystemd-network/icmp6-packet.h34
-rw-r--r--src/libsystemd-network/icmp6-test-util.c (renamed from src/libsystemd-network/icmp6-util-unix.c)21
-rw-r--r--src/libsystemd-network/icmp6-test-util.h (renamed from src/libsystemd-network/icmp6-util-unix.h)3
-rw-r--r--src/libsystemd-network/icmp6-util.c129
-rw-r--r--src/libsystemd-network/icmp6-util.h24
-rw-r--r--src/libsystemd-network/lldp-neighbor.c69
-rw-r--r--src/libsystemd-network/lldp-neighbor.h2
-rw-r--r--src/libsystemd-network/lldp-rx-internal.h3
-rw-r--r--src/libsystemd-network/meson.build22
-rw-r--r--src/libsystemd-network/ndisc-internal.h1
-rw-r--r--src/libsystemd-network/ndisc-neighbor-internal.h21
-rw-r--r--src/libsystemd-network/ndisc-option.c1502
-rw-r--r--src/libsystemd-network/ndisc-option.h330
-rw-r--r--src/libsystemd-network/ndisc-protocol.c34
-rw-r--r--src/libsystemd-network/ndisc-protocol.h31
-rw-r--r--src/libsystemd-network/ndisc-redirect-internal.h21
-rw-r--r--src/libsystemd-network/ndisc-router-internal.h37
-rw-r--r--src/libsystemd-network/ndisc-router-solicit-internal.h18
-rw-r--r--src/libsystemd-network/ndisc-router.c913
-rw-r--r--src/libsystemd-network/ndisc-router.h49
-rw-r--r--src/libsystemd-network/radv-internal.h16
-rw-r--r--src/libsystemd-network/sd-dhcp-client-id.c196
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c248
-rw-r--r--src/libsystemd-network/sd-dhcp-duid.c288
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c50
-rw-r--r--src/libsystemd-network/sd-dhcp-server-lease.c513
-rw-r--r--src/libsystemd-network/sd-dhcp-server.c349
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c80
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c3
-rw-r--r--src/libsystemd-network/sd-ipv4acd.c9
-rw-r--r--src/libsystemd-network/sd-ipv4ll.c3
-rw-r--r--src/libsystemd-network/sd-lldp-rx.c25
-rw-r--r--src/libsystemd-network/sd-ndisc-neighbor.c126
-rw-r--r--src/libsystemd-network/sd-ndisc-redirect.c128
-rw-r--r--src/libsystemd-network/sd-ndisc-router-solicit.c83
-rw-r--r--src/libsystemd-network/sd-ndisc-router.c344
-rw-r--r--src/libsystemd-network/sd-ndisc.c329
-rw-r--r--src/libsystemd-network/sd-radv.c463
-rw-r--r--src/libsystemd-network/test-acd.c5
-rw-r--r--src/libsystemd-network/test-dhcp-client.c25
-rw-r--r--src/libsystemd-network/test-dhcp-server.c46
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c12
-rw-r--r--src/libsystemd-network/test-ipv4ll-manual.c5
-rw-r--r--src/libsystemd-network/test-ipv4ll.c35
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c213
-rw-r--r--src/libsystemd-network/test-ndisc-rs.c272
-rw-r--r--src/libsystemd-network/test-ndisc-send.c449
-rw-r--r--src/libsystemd/libsystemd.sym9
-rw-r--r--src/libsystemd/meson.build8
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.c3
-rw-r--r--src/libsystemd/sd-bus/bus-common-errors.h3
-rw-r--r--src/libsystemd/sd-bus/bus-container.c2
-rw-r--r--src/libsystemd/sd-bus/bus-control.c139
-rw-r--r--src/libsystemd/sd-bus/bus-convenience.c38
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c149
-rw-r--r--src/libsystemd/sd-bus/bus-creds.h18
-rw-r--r--src/libsystemd/sd-bus/bus-dump.c2
-rw-r--r--src/libsystemd/sd-bus/bus-internal.h4
-rw-r--r--src/libsystemd/sd-bus/bus-message.c4
-rw-r--r--src/libsystemd/sd-bus/bus-objects.c2
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c112
-rw-r--r--src/libsystemd/sd-bus/bus-track.c2
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c102
-rw-r--r--src/libsystemd/sd-bus/test-bus-chat.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-cleanup.c2
-rw-r--r--src/libsystemd/sd-bus/test-bus-creds.c24
-rw-r--r--src/libsystemd/sd-bus/test-bus-error.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-marshal.c4
-rw-r--r--src/libsystemd/sd-bus/test-bus-objects.c3
-rw-r--r--src/libsystemd/sd-bus/test-bus-peersockaddr.c55
-rw-r--r--src/libsystemd/sd-daemon/sd-daemon.c75
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c10
-rw-r--r--src/libsystemd/sd-device/device-monitor.c23
-rw-r--r--src/libsystemd/sd-device/device-private.c125
-rw-r--r--src/libsystemd/sd-device/device-private.h3
-rw-r--r--src/libsystemd/sd-device/device-util.c39
-rw-r--r--src/libsystemd/sd-device/device-util.h13
-rw-r--r--src/libsystemd/sd-device/sd-device.c113
-rw-r--r--src/libsystemd/sd-device/test-device-util.c78
-rw-r--r--src/libsystemd/sd-device/test-sd-device-monitor.c4
-rw-r--r--src/libsystemd/sd-device/test-sd-device-thread.c3
-rw-r--r--src/libsystemd/sd-device/test-sd-device.c10
-rw-r--r--src/libsystemd/sd-event/event-source.h3
-rw-r--r--src/libsystemd/sd-event/event-util.c26
-rw-r--r--src/libsystemd/sd-event/event-util.h6
-rw-r--r--src/libsystemd/sd-event/sd-event.c62
-rw-r--r--src/libsystemd/sd-event/test-event.c48
-rw-r--r--src/libsystemd/sd-hwdb/sd-hwdb.c8
-rw-r--r--src/libsystemd/sd-id128/id128-util.c82
-rw-r--r--src/libsystemd/sd-id128/id128-util.h3
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c52
-rw-r--r--src/libsystemd/sd-journal/catalog.c91
-rw-r--r--src/libsystemd/sd-journal/catalog.h2
-rw-r--r--src/libsystemd/sd-journal/fsprg.c208
-rw-r--r--src/libsystemd/sd-journal/fsprg.h25
-rw-r--r--src/libsystemd/sd-journal/journal-authenticate.c68
-rw-r--r--src/libsystemd/sd-journal/journal-file.c104
-rw-r--r--src/libsystemd/sd-journal/journal-file.h4
-rw-r--r--src/libsystemd/sd-journal/journal-internal.h18
-rw-r--r--src/libsystemd/sd-journal/journal-send.c27
-rw-r--r--src/libsystemd/sd-journal/journal-send.h17
-rw-r--r--src/libsystemd/sd-journal/journal-verify.c3
-rw-r--r--src/libsystemd/sd-journal/sd-journal.c566
-rw-r--r--src/libsystemd/sd-journal/test-journal-append.c4
-rw-r--r--src/libsystemd/sd-journal/test-journal-enum.c6
-rw-r--r--src/libsystemd/sd-journal/test-journal-flush.c92
-rw-r--r--src/libsystemd/sd-journal/test-journal-init.c11
-rw-r--r--src/libsystemd/sd-journal/test-journal-interleaving.c279
-rw-r--r--src/libsystemd/sd-journal/test-journal-match.c44
-rw-r--r--src/libsystemd/sd-journal/test-journal-stream.c20
-rw-r--r--src/libsystemd/sd-journal/test-journal-verify.c6
-rw-r--r--src/libsystemd/sd-journal/test-journal.c12
-rw-r--r--src/libsystemd/sd-login/sd-login.c7
-rw-r--r--src/libsystemd/sd-login/test-login.c17
-rw-r--r--src/libsystemd/sd-netlink/netlink-message-rtnl.c41
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c29
-rw-r--r--src/libsystemd/sd-netlink/netlink-types-genl.c1
-rw-r--r--src/libsystemd/sd-netlink/netlink-types-rtnl.c13
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.c421
-rw-r--r--src/libsystemd/sd-netlink/netlink-util.h56
-rw-r--r--src/libsystemd/sd-netlink/sd-netlink.c8
-rw-r--r--src/libsystemd/sd-netlink/test-netlink.c14
-rw-r--r--src/libsystemd/sd-network/network-util.c59
-rw-r--r--src/libsystemd/sd-network/network-util.h30
-rw-r--r--src/libsystemd/sd-network/sd-network.c2
-rw-r--r--src/libsystemd/sd-path/sd-path.c46
143 files changed, 8232 insertions, 3917 deletions
diff --git a/src/libsystemd-network/dhcp-client-id-internal.h b/src/libsystemd-network/dhcp-client-id-internal.h
new file mode 100644
index 0000000..655f17b
--- /dev/null
+++ b/src/libsystemd-network/dhcp-client-id-internal.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-client-id.h"
+
+#include "dhcp-duid-internal.h"
+#include "json.h"
+#include "macro.h"
+#include "siphash24.h"
+#include "sparse-endian.h"
+
+/* RFC 2132 section 9.14: its minimum length is 2.
+ * Note, its maximum is not mentioend in the RFC. Hence, 255. */
+#define MIN_CLIENT_ID_LEN 2
+#define MAX_CLIENT_ID_LEN 255
+#define MIN_CLIENT_ID_DATA_LEN (MIN_CLIENT_ID_LEN - sizeof(uint8_t))
+#define MAX_CLIENT_ID_DATA_LEN (MAX_CLIENT_ID_LEN - sizeof(uint8_t))
+
+typedef struct sd_dhcp_client_id {
+ size_t size;
+ union {
+ struct {
+ uint8_t type;
+ union {
+ struct {
+ /* 0: Generic (non-LL) (RFC 2132) */
+ uint8_t data[MAX_CLIENT_ID_DATA_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;
+ uint8_t data[MAX_CLIENT_ID_DATA_LEN];
+ };
+ } _packed_ id;
+ uint8_t raw[MAX_CLIENT_ID_LEN];
+ };
+} sd_dhcp_client_id;
+
+static inline bool client_id_size_is_valid(size_t size) {
+ return size >= MIN_CLIENT_ID_LEN && size <= MAX_CLIENT_ID_LEN;
+}
+
+static inline bool client_id_data_size_is_valid(size_t size) {
+ return size >= MIN_CLIENT_ID_DATA_LEN && size <= MAX_CLIENT_ID_DATA_LEN;
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state);
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b);
+
+int json_dispatch_client_id(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata);
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-duid-internal.h
index 96db588..f8bc15c 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-duid-internal.h
@@ -2,31 +2,31 @@
#pragma once
#include "sd-device.h"
+#include "sd-dhcp-duid.h"
#include "sd-id128.h"
#include "ether-addr-util.h"
#include "macro.h"
#include "sparse-endian.h"
-#include "time-util.h"
-#include "unaligned.h"
#define SYSTEMD_PEN 43793
typedef enum DUIDType {
- DUID_TYPE_LLT = 1,
- DUID_TYPE_EN = 2,
- DUID_TYPE_LL = 3,
- DUID_TYPE_UUID = 4,
+ DUID_TYPE_LLT = SD_DUID_TYPE_LLT,
+ DUID_TYPE_EN = SD_DUID_TYPE_EN,
+ DUID_TYPE_LL = SD_DUID_TYPE_LL,
+ DUID_TYPE_UUID = SD_DUID_TYPE_UUID,
_DUID_TYPE_MAX,
- _DUID_TYPE_INVALID = -EINVAL,
- _DUID_TYPE_FORCE_U16 = UINT16_MAX,
+ _DUID_TYPE_INVALID = -EINVAL,
} DUIDType;
/* RFC 8415 section 11.1:
* A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of
* octets that make up the actual identifier. The length of the DUID (not including the type code) is at
* least 1 octet and at most 128 octets. */
+#define MIN_DUID_DATA_LEN 1
#define MAX_DUID_DATA_LEN 128
+#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN)
#define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN)
/* https://tools.ietf.org/html/rfc3315#section-9.1 */
@@ -53,35 +53,31 @@ struct duid {
/* DUID_TYPE_UUID */
sd_id128_t uuid;
} _packed_ uuid;
- struct {
- uint8_t data[MAX_DUID_DATA_LEN];
- } _packed_ raw;
+ uint8_t data[MAX_DUID_DATA_LEN];
};
} _packed_;
-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);
-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);
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len);
-int dhcp_identifier_set_duid_raw(
- DUIDType duid_type,
- const uint8_t *buf,
- size_t buf_len,
- struct duid *ret_duid,
- size_t *ret_len);
+typedef struct sd_dhcp_duid {
+ size_t size;
+ union {
+ struct duid duid;
+ uint8_t raw[MAX_DUID_LEN];
+ };
+} sd_dhcp_duid;
+
+static inline bool duid_size_is_valid(size_t size) {
+ return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN;
+}
+
+static inline bool duid_data_size_is_valid(size_t size) {
+ return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
+}
+
+const char *duid_type_to_string(DUIDType t) _const_;
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
+
int dhcp_identifier_set_iaid(
sd_device *dev,
const struct hw_addr_data *hw_addr,
bool legacy_unstable_byteorder,
void *ret);
-
-const char *duid_type_to_string(DUIDType t) _const_;
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
deleted file mode 100644
index f65cdbe..0000000
--- a/src/libsystemd-network/dhcp-identifier.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <linux/if_infiniband.h>
-#include <net/ethernet.h>
-#include <net/if_arp.h>
-
-#include "dhcp-identifier.h"
-#include "netif-util.h"
-#include "network-common.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-table.h"
-
-#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
-#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
-#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
-
-static const char * const duid_type_table[_DUID_TYPE_MAX] = {
- [DUID_TYPE_LLT] = "DUID-LLT",
- [DUID_TYPE_EN] = "DUID-EN/Vendor",
- [DUID_TYPE_LL] = "DUID-LL",
- [DUID_TYPE_UUID] = "UUID",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
-
-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(hw_addr);
- assert(ret_duid);
- assert(ret_len);
-
- if (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL);
- else
- return -EOPNOTSUPP;
-
- if (t < USEC_2000)
- time_from_2000y = 0;
- else
- time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
-
- 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, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length;
-
- return 0;
-}
-
-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 (hw_addr->length == 0)
- return -EOPNOTSUPP;
-
- if (arp_type == ARPHRD_ETHER)
- assert_return(hw_addr->length == ETH_ALEN, -EINVAL);
- else if (arp_type == ARPHRD_INFINIBAND)
- 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, hw_addr->bytes, hw_addr->length);
-
- *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length;
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- bool test_mode;
- uint64_t hash;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- test_mode = network_test_mode_enabled();
-
- if (!test_mode) {
- r = sd_id128_get_machine(&machine_id);
- if (r < 0)
- return r;
- } else
- /* For tests, especially for fuzzers, reproducibility is important.
- * Hence, use a static and constant machine ID.
- * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
- machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN);
- unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN);
-
- /* a bit of snake-oil perhaps, but no need to expose the machine-id
- * directly; duid->en.id might not be aligned, so we need to copy */
- hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
- memcpy(ret_duid->en.id, &hash, sizeof(hash));
-
- *ret_len = offsetof(struct duid, en.id) + sizeof(hash);
-
- if (test_mode)
- assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0);
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
- sd_id128_t machine_id;
- int r;
-
- assert(ret_duid);
- assert(ret_len);
-
- r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
- if (r < 0)
- return r;
-
- unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
- memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
-
- *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
-
- return 0;
-}
-
-int dhcp_identifier_set_duid_raw(
- DUIDType duid_type,
- const uint8_t *buf,
- size_t buf_len,
- struct duid *ret_duid,
- size_t *ret_len) {
-
- assert(buf || buf_len == 0);
- assert(ret_duid);
- assert(ret_len);
-
- if (duid_type < 0 || duid_type > UINT16_MAX)
- return -EINVAL;
-
- if (buf_len > MAX_DUID_DATA_LEN)
- return -EINVAL;
-
- unaligned_write_be16(&ret_duid->type, duid_type);
- memcpy_safe(ret_duid->raw.data, buf, buf_len);
-
- *ret_len = offsetof(struct duid, raw.data) + buf_len;
- return 0;
-}
-
-int dhcp_identifier_set_iaid(
- sd_device *dev,
- const struct hw_addr_data *hw_addr,
- bool legacy_unstable_byteorder,
- void *ret) {
-
- const char *name = NULL;
- uint32_t id32;
- uint64_t id;
-
- assert(hw_addr);
- assert(ret);
-
- if (dev)
- name = net_get_persistent_name(dev);
- if (name)
- id = siphash24(name, strlen(name), HASH_KEY.bytes);
- else
- /* fall back to MAC address if no predictable name available */
- id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
-
- id32 = (id & 0xffffffff) ^ (id >> 32);
-
- if (legacy_unstable_byteorder)
- /* for historical reasons (a bug), the bits were swapped and thus
- * the result was endianness dependent. Preserve that behavior. */
- id32 = bswap_32(id32);
- else
- /* the fixed behavior returns a stable byte order. Since LE is expected
- * to be more common, swap the bytes on LE to give the same as legacy
- * behavior. */
- id32 = be32toh(id32);
-
- unaligned_write_ne32(ret, id32);
- return 0;
-}
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index a3d8bb4..b7bc142 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -8,6 +8,7 @@
#include "sd-dhcp-client.h"
#include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-option.h"
#include "list.h"
#include "time-util.h"
@@ -67,8 +68,7 @@ struct sd_dhcp_lease {
char *root_path;
char *captive_portal;
- void *client_id;
- size_t client_id_len;
+ sd_dhcp_client_id client_id;
void *vendor_specific;
size_t vendor_specific_len;
@@ -92,7 +92,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo
void dhcp_lease_set_timestamp(sd_dhcp_lease *lease, const triple_timestamp *timestamp);
int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id);
#define dhcp_lease_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_dhcp_lease_ref, sd_dhcp_lease_unref)
diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c
index 1f4ad09..2e73983 100644
--- a/src/libsystemd-network/dhcp-network.c
+++ b/src/libsystemd-network/dhcp-network.c
@@ -3,15 +3,16 @@
Copyright © 2013 Intel Corporation. All rights reserved.
***/
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/filter.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_packet.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-network.h"
#include "dhcp-protocol.h"
diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c
index 5679091..e4ba77a 100644
--- a/src/libsystemd-network/dhcp-option.c
+++ b/src/libsystemd-network/dhcp-option.c
@@ -285,7 +285,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
int r;
while (offset < buflen) {
- code = options[offset ++];
+ code = options[offset++];
switch (code) {
case SD_DHCP_OPTION_PAD:
@@ -298,7 +298,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
if (buflen < offset + 1)
return -ENOBUFS;
- len = options[offset ++];
+ len = options[offset++];
if (buflen < offset + len)
return -EINVAL;
diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
index da9e56b..0b2fa96 100644
--- a/src/libsystemd-network/dhcp-server-internal.h
+++ b/src/libsystemd-network/dhcp-server-internal.h
@@ -8,6 +8,7 @@
#include "sd-dhcp-server.h"
#include "sd-event.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-option.h"
#include "network-common.h"
#include "ordered-set.h"
@@ -24,25 +25,6 @@ typedef enum DHCPRawOption {
_DHCP_RAW_OPTION_DATA_INVALID,
} DHCPRawOption;
-typedef struct DHCPClientId {
- size_t length;
- uint8_t *data;
-} DHCPClientId;
-
-typedef struct DHCPLease {
- sd_dhcp_server *server;
-
- DHCPClientId client_id;
-
- uint8_t htype; /* e.g. ARPHRD_ETHER */
- uint8_t hlen; /* e.g. ETH_ALEN */
- be32_t address;
- be32_t gateway;
- uint8_t chaddr[16];
- usec_t expiration;
- char *hostname;
-} DHCPLease;
-
struct sd_dhcp_server {
unsigned n_ref;
@@ -93,6 +75,9 @@ struct sd_dhcp_server {
char *agent_circuit_id;
char *agent_remote_id;
+
+ int lease_dir_fd;
+ char *lease_file;
};
typedef struct DHCPRequest {
@@ -100,7 +85,7 @@ typedef struct DHCPRequest {
DHCPMessage *message;
/* options */
- DHCPClientId client_id;
+ sd_dhcp_client_id client_id;
size_t max_optlen;
be32_t server_id;
be32_t requested_ip;
@@ -113,20 +98,12 @@ typedef struct DHCPRequest {
triple_timestamp timestamp;
} DHCPRequest;
-extern const struct hash_ops dhcp_lease_hash_ops;
-
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length, const triple_timestamp *timestamp);
int dhcp_server_send_packet(sd_dhcp_server *server,
DHCPRequest *req, DHCPPacket *packet,
int type, size_t optoffset);
-void client_id_hash_func(const DHCPClientId *p, struct siphash *state);
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b);
-
-DHCPLease *dhcp_lease_free(DHCPLease *lease);
-DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPLease*, dhcp_lease_free);
-
#define log_dhcp_server_errno(server, error, fmt, ...) \
log_interface_prefix_full_errno( \
"DHCPv4 server: ", \
diff --git a/src/libsystemd-network/dhcp-server-lease-internal.h b/src/libsystemd-network/dhcp-server-lease-internal.h
new file mode 100644
index 0000000..7626552
--- /dev/null
+++ b/src/libsystemd-network/dhcp-server-lease-internal.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-server-lease.h"
+
+#include "dhcp-client-id-internal.h"
+#include "dhcp-server-internal.h"
+#include "json.h"
+#include "time-util.h"
+
+typedef struct sd_dhcp_server_lease {
+ unsigned n_ref;
+
+ sd_dhcp_server *server;
+
+ sd_dhcp_client_id client_id;
+
+ uint8_t htype; /* e.g. ARPHRD_ETHER */
+ uint8_t hlen; /* e.g. ETH_ALEN */
+ be32_t address;
+ be32_t gateway;
+ uint8_t chaddr[16];
+ usec_t expiration;
+ char *hostname;
+} sd_dhcp_server_lease;
+
+extern const struct hash_ops dhcp_server_lease_hash_ops;
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static);
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration);
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server);
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req);
+
+int dhcp_server_bound_leases_append_json(sd_dhcp_server *server, JsonVariant **v);
+int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant **v);
+
+int dhcp_server_save_leases(sd_dhcp_server *server);
+int dhcp_server_load_leases(sd_dhcp_server *server);
+int dhcp_server_leases_file_get_server_address(
+ int dir_fd,
+ const char *path,
+ struct in_addr *ret_address,
+ uint8_t *ret_prefixlen);
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index e5b3b13..ecd62ea 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -11,7 +11,7 @@
#include "sd-event.h"
#include "sd-dhcp6-client.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
@@ -64,8 +64,7 @@ struct sd_dhcp6_client {
DHCP6IA ia_na;
DHCP6IA ia_pd;
DHCP6RequestIA request_ia;
- struct duid duid;
- size_t duid_len;
+ sd_dhcp_duid duid;
be16_t *req_opts;
size_t n_req_opts;
char *fqdn;
@@ -85,9 +84,8 @@ struct sd_dhcp6_client {
bool send_release;
};
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
- const void *packet, size_t len);
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *address, const void *packet, size_t len);
int dhcp6_client_send_message(sd_dhcp6_client *client);
int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c
index a3e4e19..0aa8469 100644
--- a/src/libsystemd-network/dhcp6-network.c
+++ b/src/libsystemd-network/dhcp6-network.c
@@ -17,9 +17,10 @@
#include "fd-util.h"
#include "socket-util.h"
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *local_address) {
union sockaddr_union src = {
.in6.sin6_family = AF_INET6,
+ .in6.sin6_addr = *ASSERT_PTR(local_address),
.in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
.in6.sin6_scope_id = ifindex,
};
@@ -27,9 +28,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
int r;
assert(ifindex > 0);
- assert(local_address);
-
- src.in6.sin6_addr = *local_address;
s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
if (s < 0)
@@ -58,20 +56,14 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
return TAKE_FD(s);
}
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
- const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
union sockaddr_union dest = {
.in6.sin6_family = AF_INET6,
+ .in6.sin6_addr = *ASSERT_PTR(server_address),
.in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
};
- int r;
-
- assert(server_address);
- memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
-
- r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
- if (r < 0)
+ if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
return -errno;
return 0;
diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
index c70f932..ab75bad 100644
--- a/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libsystemd-network/dhcp6-protocol.h
@@ -28,9 +28,11 @@ typedef struct DHCP6Message DHCP6Message;
#define DHCP6_MIN_OPTIONS_SIZE \
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
-#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, \
+ } } } )
enum {
DHCP6_PORT_SERVER = 547,
diff --git a/src/libsystemd-network/fuzz-dhcp-client.c b/src/libsystemd-network/fuzz-dhcp-client.c
index 384972f..72787c4 100644
--- a/src/libsystemd-network/fuzz-dhcp-client.c
+++ b/src/libsystemd-network/fuzz-dhcp-client.c
@@ -7,8 +7,12 @@
#include "sd-dhcp-client.c"
#include "alloc-util.h"
+#include "dhcp-lease-internal.h"
#include "dhcp-network.h"
+#include "fs-util.h"
#include "fuzz.h"
+#include "network-internal.h"
+#include "tmpfile-util.h"
int dhcp_network_bind_raw_socket(
int ifindex,
@@ -52,6 +56,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
+ _cleanup_(unlink_tempfilep) char lease_file[] = "/tmp/fuzz-dhcp-client.XXXXXX";
+ _cleanup_close_ int fd = -1;
int res, r;
assert_se(setenv("SYSTEMD_NETWORK_TEST_MODE", "1", 1) >= 0);
@@ -75,8 +82,19 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
client->xid = 2;
client->state = DHCP_STATE_SELECTING;
- (void) client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size, NULL);
+ if (client_handle_offer_or_rapid_ack(client, (DHCPMessage*) data, size, NULL) < 0)
+ goto end;
+ fd = mkostemp_safe(lease_file);
+ assert_se(fd >= 0);
+
+ r = dhcp_lease_save(client->lease, lease_file);
+ assert_se(r >= 0);
+
+ r = dhcp_lease_load(&lease, lease_file);
+ assert_se(r >= 0);
+
+end:
assert_se(sd_dhcp_client_stop(client) >= 0);
return 0;
diff --git a/src/libsystemd-network/fuzz-dhcp-server.c b/src/libsystemd-network/fuzz-dhcp-server.c
index fddb3a5..c8b0378 100644
--- a/src/libsystemd-network/fuzz-dhcp-server.c
+++ b/src/libsystemd-network/fuzz-dhcp-server.c
@@ -7,6 +7,9 @@
#include "sd-dhcp-server.c"
#include "fuzz.h"
+#include "path-util.h"
+#include "rm-rf.h"
+#include "tmpfile-util.h"
/* stub out network so that the server doesn't send */
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) {
@@ -18,40 +21,31 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
}
static int add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
int r;
assert(server);
- lease = new(DHCPLease, 1);
+ lease = new(sd_dhcp_server_lease, 1);
if (!lease)
return -ENOMEM;
- *lease = (DHCPLease) {
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
.address = htobe32(UINT32_C(10) << 24 | i),
.chaddr = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
- .expiration = UINT64_MAX,
+ .expiration = usec_add(now(CLOCK_BOOTTIME), USEC_PER_DAY),
.gateway = server_address->s_addr,
.hlen = ETH_ALEN,
.htype = ARPHRD_ETHER,
- .client_id.length = 2,
+ .client_id.size = 2,
};
- lease->client_id.data = new(uint8_t, lease->client_id.length);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- lease->client_id.data[0] = 2;
- lease->client_id.data[1] = i;
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return r;
+ lease->client_id.raw[0] = 2;
+ lease->client_id.raw[1] = i;
- r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
if (r < 0)
return r;
@@ -71,9 +65,11 @@ static int add_static_lease(sd_dhcp_server *server, uint8_t i) {
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
_cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
struct in_addr address = { .s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))};
_cleanup_free_ uint8_t *duped = NULL;
+ _cleanup_close_ int dir_fd = -EBADF;
if (size < sizeof(DHCPMessage))
return 0;
@@ -82,8 +78,12 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(duped = memdup(data, size));
+ dir_fd = mkdtemp_open(NULL, 0, &tmpdir);
+ assert_se(dir_fd >= 0);
+
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
+ assert_se(sd_dhcp_server_set_lease_file(server, dir_fd, "leases") >= 0);
server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY);
assert_se(server->fd >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0);
@@ -98,5 +98,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
(void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size, NULL);
+ assert_se(dhcp_server_save_leases(server) >= 0);
+ server->bound_leases_by_address = hashmap_free(server->bound_leases_by_address);
+ server->bound_leases_by_client_id = hashmap_free(server->bound_leases_by_client_id);
+ assert_se(dhcp_server_load_leases(server) >= 0);
+
return 0;
}
diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c
index 2d42844..2b6e335 100644
--- a/src/libsystemd-network/fuzz-dhcp6-client.c
+++ b/src/libsystemd-network/fuzz-dhcp6-client.c
@@ -12,11 +12,11 @@
static int test_dhcp_fd[2] = EBADF_PAIR;
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
return len;
}
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int index, const struct in6_addr *local_address) {
assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0);
return TAKE_FD(test_dhcp_fd[0]);
}
diff --git a/src/libsystemd-network/fuzz-lldp-rx.c b/src/libsystemd-network/fuzz-lldp-rx.c
index 844957c..dad1038 100644
--- a/src/libsystemd-network/fuzz-lldp-rx.c
+++ b/src/libsystemd-network/fuzz-lldp-rx.c
@@ -9,6 +9,8 @@
#include "fd-util.h"
#include "fuzz.h"
#include "lldp-network.h"
+#include "lldp-rx-internal.h"
+#include "memstream-util.h"
static int test_fd[2] = EBADF_PAIR;
@@ -22,6 +24,9 @@ int lldp_network_bind_raw_socket(int ifindex) {
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_(memstream_done) MemStream m = {};
+ FILE *f;
if (outside_size_range(size, 0, 2048))
return 0;
@@ -37,6 +42,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(write(test_fd[1], data, size) == (ssize_t) size);
assert_se(sd_event_run(e, 0) >= 0);
+ assert_se(lldp_rx_build_neighbors_json(lldp_rx, &v) >= 0);
+ assert_se(f = memstream_init(&m));
+ (void) json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR, f, NULL);
+
assert_se(sd_lldp_rx_stop(lldp_rx) >= 0);
assert_se(sd_lldp_rx_detach_event(lldp_rx) >= 0);
test_fd[1] = safe_close(test_fd[1]);
diff --git a/src/libsystemd-network/fuzz-ndisc-rs.c b/src/libsystemd-network/fuzz-ndisc-rs.c
index a89e2b0..e6ee768 100644
--- a/src/libsystemd-network/fuzz-ndisc-rs.c
+++ b/src/libsystemd-network/fuzz-ndisc-rs.c
@@ -5,26 +5,24 @@
#include <unistd.h>
#include "sd-ndisc.h"
+#include "sd-radv.h"
#include "alloc-util.h"
#include "fd-util.h"
#include "fuzz.h"
-#include "icmp6-util-unix.h"
+#include "icmp6-packet.h"
+#include "icmp6-test-util.h"
#include "ndisc-internal.h"
+#include "ndisc-option.h"
#include "socket-util.h"
-int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+static void test_with_sd_ndisc(const uint8_t *data, size_t size) {
struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
};
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
- if (outside_size_range(size, 0, 2048))
- return 0;
-
- fuzz_setup_logging();
-
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_ndisc_new(&nd) >= 0);
assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
@@ -34,7 +32,67 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert_se(write(test_fd[1], data, size) == (ssize_t) size);
(void) sd_event_run(e, UINT64_MAX);
assert_se(sd_ndisc_stop(nd) >= 0);
- close(test_fd[1]);
+ test_fd[1] = safe_close(test_fd[1]);
+ TAKE_FD(test_fd[0]); /* It should be already closed by sd_ndisc_stop(). */
+}
+
+static void test_with_sd_radv(const uint8_t *data, size_t size) {
+ struct ether_addr mac_addr = {
+ .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+ };
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
+
+ assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
+
+ assert_se(sd_event_new(&e) >= 0);
+ assert_se(sd_radv_new(&ra) >= 0);
+ assert_se(sd_radv_attach_event(ra, e, 0) >= 0);
+ assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
+ assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
+ assert_se(sd_radv_start(ra) >= 0);
+ assert_se(write(test_fd[0], data, size) == (ssize_t) size);
+ (void) sd_event_run(e, UINT64_MAX);
+ assert_se(sd_radv_stop(ra) >= 0);
+ test_fd[0] = safe_close(test_fd[0]);
+ TAKE_FD(test_fd[1]); /* It should be already closed by sd_radv_stop(). */
+}
+
+static void test_with_icmp6_packet(const uint8_t *data, size_t size) {
+ _cleanup_close_pair_ int fd_pair[2] = EBADF_PAIR;
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ _cleanup_set_free_ Set *options = NULL;
+
+ assert_se(socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, fd_pair) >= 0);
+ assert_se(write(fd_pair[1], data, size) == (ssize_t) size);
+
+ if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
+ return;
+
+ if (ndisc_parse_options(packet, &options) < 0)
+ return;
+
+ if (ndisc_send(fd_pair[1], &IN6_ADDR_ALL_ROUTERS_MULTICAST,
+ icmp6_packet_get_header(packet), options, /* timestamp = */ 0) < 0)
+ return;
+
+ packet = icmp6_packet_unref(packet);
+ options = set_free(options);
+
+ if (icmp6_packet_receive(fd_pair[0], &packet) < 0)
+ return;
+
+ assert_se(ndisc_parse_options(packet, &options) >= 0);
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (outside_size_range(size, 0, 2048))
+ return 0;
+
+ fuzz_setup_logging();
+ test_with_sd_ndisc(data, size);
+ test_with_sd_radv(data, size);
+ test_with_icmp6_packet(data, size);
return 0;
}
diff --git a/src/libsystemd-network/icmp6-packet.c b/src/libsystemd-network/icmp6-packet.c
new file mode 100644
index 0000000..02865a4
--- /dev/null
+++ b/src/libsystemd-network/icmp6-packet.c
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "icmp6-packet.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "iovec-util.h"
+#include "network-common.h"
+#include "socket-util.h"
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(ICMP6Packet, icmp6_packet, mfree);
+
+static ICMP6Packet* icmp6_packet_new(size_t size) {
+ ICMP6Packet *p;
+
+ if (size > SIZE_MAX - offsetof(ICMP6Packet, raw_packet))
+ return NULL;
+
+ p = malloc0(offsetof(ICMP6Packet, raw_packet) + size);
+ if (!p)
+ return NULL;
+
+ p->raw_size = size;
+ p->n_ref = 1;
+
+ return p;
+}
+
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr) {
+ assert(p);
+
+ if (addr)
+ p->sender_address = *addr;
+ else
+ p->sender_address = (const struct in6_addr) {};
+
+ return 0;
+}
+
+int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
+ assert(p);
+
+ if (in6_addr_is_null(&p->sender_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = p->sender_address;
+ return 0;
+}
+
+int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret) {
+ assert(p);
+ assert(ret);
+
+ if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock) || !clock_supported(clock))
+ return -EOPNOTSUPP;
+
+ if (!triple_timestamp_is_set(&p->timestamp))
+ return -ENODATA;
+
+ *ret = triple_timestamp_by_clock(&p->timestamp, clock);
+ return 0;
+}
+
+const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
+ assert(p);
+
+ if (p->raw_size < sizeof(struct icmp6_hdr))
+ return NULL;
+
+ return (const struct icmp6_hdr*) p->raw_packet;
+}
+
+int icmp6_packet_get_type(ICMP6Packet *p) {
+ const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
+ if (!hdr)
+ return -EBADMSG;
+
+ return hdr->icmp6_type;
+}
+
+static int icmp6_packet_verify(ICMP6Packet *p) {
+ const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
+ if (!hdr)
+ return -EBADMSG;
+
+ if (hdr->icmp6_code != 0)
+ return -EBADMSG;
+
+ /* Drop any overly large packets early. We are not interested in jumbograms,
+ * which could cause excessive processing. */
+ if (p->raw_size > ICMP6_MAX_NORMAL_PAYLOAD_SIZE)
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+int icmp6_packet_receive(int fd, ICMP6Packet **ret) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *p = NULL;
+ ssize_t len;
+ int r;
+
+ assert(fd >= 0);
+ assert(ret);
+
+ len = next_datagram_size_fd(fd);
+ if (len < 0)
+ return (int) len;
+
+ p = icmp6_packet_new(len);
+ if (!p)
+ return -ENOMEM;
+
+ r = icmp6_receive(fd, p->raw_packet, p->raw_size, &p->sender_address, &p->timestamp);
+ if (r == -EADDRNOTAVAIL)
+ return log_debug_errno(r, "ICMPv6: Received a packet from neither link-local nor null address.");
+ if (r == -EMULTIHOP)
+ return log_debug_errno(r, "ICMPv6: Received a packet with an invalid hop limit.");
+ if (r == -EPFNOSUPPORT)
+ return log_debug_errno(r, "ICMPv6: Received a packet with an invalid source address.");
+ if (r < 0)
+ return log_debug_errno(r, "ICMPv6: Unexpected error while receiving a packet: %m");
+
+ r = icmp6_packet_verify(p);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
diff --git a/src/libsystemd-network/icmp6-packet.h b/src/libsystemd-network/icmp6-packet.h
new file mode 100644
index 0000000..b402255
--- /dev/null
+++ b/src/libsystemd-network/icmp6-packet.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <netinet/in.h>
+
+#include "macro.h"
+#include "time-util.h"
+
+typedef struct ICMP6Pakcet {
+ unsigned n_ref;
+
+ struct in6_addr sender_address;
+ struct triple_timestamp timestamp;
+
+ size_t raw_size;
+ uint8_t raw_packet[];
+} ICMP6Packet;
+
+ICMP6Packet* icmp6_packet_ref(ICMP6Packet *p);
+ICMP6Packet* icmp6_packet_unref(ICMP6Packet *p);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
+
+/* IPv6 Header is 40 bytes and reserves 2 bytes to represent the Payload Length. Thus, the max payload size,
+ * including extension headers, is 65535 bytes (2^16 - 1). Jumbograms can be larger (2^32 - 1). */
+#define ICMP6_MAX_NORMAL_PAYLOAD_SIZE 65535
+
+int icmp6_packet_set_sender_address(ICMP6Packet *p, const struct in6_addr *addr);
+int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
+int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
+const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p);
+int icmp6_packet_get_type(ICMP6Packet *p);
+
+int icmp6_packet_receive(int fd, ICMP6Packet **ret);
diff --git a/src/libsystemd-network/icmp6-util-unix.c b/src/libsystemd-network/icmp6-test-util.c
index 01edb85..3c78109 100644
--- a/src/libsystemd-network/icmp6-util-unix.c
+++ b/src/libsystemd-network/icmp6-test-util.c
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <unistd.h>
#include "fd-util.h"
-#include "icmp6-util-unix.h"
+#include "icmp6-test-util.h"
-send_ra_t send_ra_function = NULL;
int test_fd[2] = EBADF_PAIR;
static struct in6_addr dummy_link_local = {
@@ -16,22 +16,15 @@ static struct in6_addr dummy_link_local = {
},
};
-int icmp6_bind_router_solicitation(int ifindex) {
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
+int icmp6_bind(int ifindex, bool is_router) {
+ if (!is_router && socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
return -errno;
- return test_fd[0];
+ return test_fd[is_router];
}
-int icmp6_bind_router_advertisement(int ifindex) {
- return test_fd[1];
-}
-
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- if (!send_ra_function)
- return 0;
-
- return send_ra_function(0);
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
+ return writev(fd, iov, n_iov);
}
int icmp6_receive(
diff --git a/src/libsystemd-network/icmp6-util-unix.h b/src/libsystemd-network/icmp6-test-util.h
index a9cb05a..d7b0cc8 100644
--- a/src/libsystemd-network/icmp6-util-unix.h
+++ b/src/libsystemd-network/icmp6-test-util.h
@@ -3,7 +3,4 @@
#include "icmp6-util.h"
-typedef int (*send_ra_t)(uint8_t flags);
-
-extern send_ra_t send_ra_function;
extern int test_fd[2];
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
index 72c20ba..75a6489 100644
--- a/src/libsystemd-network/icmp6-util.c
+++ b/src/libsystemd-network/icmp6-util.c
@@ -3,7 +3,10 @@
Copyright © 2014 Intel Corporation. All rights reserved.
***/
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/if_packet.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
@@ -11,8 +14,6 @@
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
-#include <net/if.h>
-#include <linux/if_packet.h>
#include "fd-util.h"
#include "icmp6-util.h"
@@ -21,37 +22,44 @@
#include "network-common.h"
#include "socket-util.h"
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
-
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
-
-static int icmp6_bind_router_message(const struct icmp6_filter *filter,
- const struct ipv6_mreq *mreq) {
- int ifindex = mreq->ipv6mr_interface;
+int icmp6_bind(int ifindex, bool is_router) {
+ struct icmp6_filter filter = {};
+ struct ipv6_mreq mreq;
_cleanup_close_ int s = -EBADF;
int r;
- assert(filter);
- assert(mreq);
+ assert(ifindex > 0);
+
+ ICMP6_FILTER_SETBLOCKALL(&filter);
+ if (is_router) {
+ mreq = (struct ipv6_mreq) {
+ .ipv6mr_multiaddr = IN6_ADDR_ALL_ROUTERS_MULTICAST,
+ .ipv6mr_interface = ifindex,
+ };
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
+ } else {
+ mreq = (struct ipv6_mreq) {
+ .ipv6mr_multiaddr = IN6_ADDR_ALL_NODES_MULTICAST,
+ .ipv6mr_interface = ifindex,
+ };
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+ ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter);
+ ICMP6_FILTER_SETPASS(ND_REDIRECT, &filter);
+ }
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
if (s < 0)
return -errno;
- if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter)) < 0)
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) < 0)
return -errno;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq)) < 0)
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
return -errno;
- /* RFC 3315, section 6.7, bullet point 2 may indicate that an
- IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
- Empirical experiments indicates otherwise and therefore an
- IPV6_MULTICAST_IF socket option is used here instead */
+ /* RFC 3315, section 6.7, bullet point 2 may indicate that an IPV6_PKTINFO socket option also applies
+ * for ICMPv6 multicast. Empirical experiments indicates otherwise and therefore an IPV6_MULTICAST_IF
+ * socket option is used here instead. */
r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex);
if (r < 0)
return r;
@@ -83,63 +91,21 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
return TAKE_FD(s);
}
-int icmp6_bind_router_solicitation(int ifindex) {
- struct icmp6_filter filter = {};
- struct ipv6_mreq mreq = {
- .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
- .ipv6mr_interface = ifindex,
- };
-
- ICMP6_FILTER_SETBLOCKALL(&filter);
- ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
-
- return icmp6_bind_router_message(&filter, &mreq);
-}
-
-int icmp6_bind_router_advertisement(int ifindex) {
- struct icmp6_filter filter = {};
- struct ipv6_mreq mreq = {
- .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- .ipv6mr_interface = ifindex,
- };
-
- ICMP6_FILTER_SETBLOCKALL(&filter);
- ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
-
- return icmp6_bind_router_message(&filter, &mreq);
-}
-
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- struct sockaddr_in6 dst = {
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov) {
+ struct sockaddr_in6 sa = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
- };
- struct {
- struct nd_router_solicit rs;
- struct nd_opt_hdr rs_opt;
- struct ether_addr rs_opt_mac;
- } _packed_ rs = {
- .rs.nd_rs_type = ND_ROUTER_SOLICIT,
- .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
- .rs_opt.nd_opt_len = 1,
- };
- struct iovec iov = {
- .iov_base = &rs,
- .iov_len = sizeof(rs),
+ .sin6_addr = *ASSERT_PTR(dst),
};
struct msghdr msg = {
- .msg_name = &dst,
- .msg_namelen = sizeof(dst),
- .msg_iov = &iov,
- .msg_iovlen = 1,
+ .msg_name = &sa,
+ .msg_namelen = sizeof(struct sockaddr_in6),
+ .msg_iov = (struct iovec*) iov,
+ .msg_iovlen = n_iov,
};
- assert(s >= 0);
- assert(ether_addr);
-
- rs.rs_opt_mac = *ether_addr;
+ assert(fd >= 0);
- if (sendmsg(s, &msg, 0) < 0)
+ if (sendmsg(fd, &msg, 0) < 0)
return -errno;
return 0;
@@ -155,7 +121,7 @@ int icmp6_receive(
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
CMSG_SPACE_TIMEVAL) control = {};
- struct iovec iov = {};
+ struct iovec iov = { buffer, size };
union sockaddr_union sa = {};
struct msghdr msg = {
.msg_name = &sa.sa,
@@ -165,11 +131,8 @@ int icmp6_receive(
.msg_control = &control,
.msg_controllen = sizeof(control),
};
- struct in6_addr addr = {};
ssize_t len;
- iov = IOVEC_MAKE(buffer, size);
-
len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
if (len < 0)
return (int) len;
@@ -177,17 +140,11 @@ int icmp6_receive(
if ((size_t) len != size)
return -EINVAL;
- if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
- sa.in6.sin6_family == AF_INET6) {
-
- addr = sa.in6.sin6_addr;
- if (!in6_addr_is_link_local(&addr) && !in6_addr_is_null(&addr))
- return -EADDRNOTAVAIL;
-
- } else if (msg.msg_namelen > 0)
+ if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6)
return -EPFNOSUPPORT;
- /* namelen == 0 only happens when running the test-suite over a socketpair */
+ if (!in6_addr_is_link_local(&sa.in6.sin6_addr) && !in6_addr_is_null(&sa.in6.sin6_addr))
+ return -EADDRNOTAVAIL;
assert(!(msg.msg_flags & MSG_TRUNC));
@@ -198,6 +155,6 @@ int icmp6_receive(
if (ret_timestamp)
triple_timestamp_from_cmsg(ret_timestamp, &msg);
if (ret_sender)
- *ret_sender = addr;
+ *ret_sender = sa.in6.sin6_addr;
return 0;
}
diff --git a/src/libsystemd-network/icmp6-util.h b/src/libsystemd-network/icmp6-util.h
index 0a9ecb4..9e5063f 100644
--- a/src/libsystemd-network/icmp6-util.h
+++ b/src/libsystemd-network/icmp6-util.h
@@ -6,20 +6,26 @@
***/
#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <stdbool.h>
+#include <sys/uio.h>
#include "time-util.h"
-#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_ROUTERS_MULTICAST \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, \
+ } } } )
-#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
- { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+#define IN6_ADDR_ALL_NODES_MULTICAST \
+ ((const struct in6_addr) { { { \
+ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, \
+ } } } )
-int icmp6_bind_router_solicitation(int ifindex);
-int icmp6_bind_router_advertisement(int ifindex);
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+int icmp6_bind(int ifindex, bool is_router);
+int icmp6_send(int fd, const struct in6_addr *dst, const struct iovec *iov, size_t n_iov);
int icmp6_receive(
int fd,
void *buffer,
diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c
index af61c9b..a4384ac 100644
--- a/src/libsystemd-network/lldp-neighbor.c
+++ b/src/libsystemd-network/lldp-neighbor.c
@@ -14,10 +14,10 @@ static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash
assert(id);
assert(state);
- siphash24_compress(id->chassis_id, id->chassis_id_size, state);
- siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
- siphash24_compress(id->port_id, id->port_id_size, state);
- siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
+ siphash24_compress_safe(id->chassis_id, id->chassis_id_size, state);
+ siphash24_compress_typesafe(id->chassis_id_size, state);
+ siphash24_compress_safe(id->port_id, id->port_id_size, state);
+ siphash24_compress_typesafe(id->port_id_size, state);
}
int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
@@ -376,17 +376,6 @@ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_a
return 0;
}
-int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
- assert_return(n, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(size, -EINVAL);
-
- *ret = LLDP_NEIGHBOR_RAW(n);
- *size = n->raw_size;
-
- return 0;
-}
-
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
assert_return(n, -EINVAL);
assert_return(type, -EINVAL);
@@ -640,28 +629,6 @@ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret
return 0;
}
-int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
- _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
- int r;
-
- assert_return(ret, -EINVAL);
- assert_return(raw || raw_size <= 0, -EINVAL);
-
- n = lldp_neighbor_new(raw_size);
- if (!n)
- return -ENOMEM;
-
- memcpy_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
-
- r = lldp_neighbor_parse(n);
- if (r < 0)
- return r;
-
- *ret = TAKE_PTR(n);
-
- return r;
-}
-
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
assert_return(n, -EINVAL);
@@ -793,3 +760,31 @@ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_
*ret = triple_timestamp_by_clock(&n->timestamp, clock);
return 0;
}
+
+int lldp_neighbor_build_json(sd_lldp_neighbor *n, JsonVariant **ret) {
+ const char *chassis_id = NULL, *port_id = NULL, *port_description = NULL,
+ *system_name = NULL, *system_description = NULL;
+ uint16_t cc = 0;
+ bool valid_cc;
+
+ assert(n);
+ assert(ret);
+
+ (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id);
+ (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id);
+ (void) sd_lldp_neighbor_get_port_description(n, &port_description);
+ (void) sd_lldp_neighbor_get_system_name(n, &system_name);
+ (void) sd_lldp_neighbor_get_system_description(n, &system_description);
+
+ valid_cc = sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0;
+
+ return json_build(ret, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("ChassisID", chassis_id),
+ JSON_BUILD_PAIR_BYTE_ARRAY("RawChassisID", n->id.chassis_id, n->id.chassis_id_size),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("PortID", port_id),
+ JSON_BUILD_PAIR_BYTE_ARRAY("RawPortID", n->id.port_id, n->id.port_id_size),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("PortDescription", port_description),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemName", system_name),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("SystemDescription", system_description),
+ JSON_BUILD_PAIR_CONDITION(valid_cc, "EnabledCapabilities", JSON_BUILD_UNSIGNED(cc))));
+}
diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h
index 016286b..06ba4c7 100644
--- a/src/libsystemd-network/lldp-neighbor.h
+++ b/src/libsystemd-network/lldp-neighbor.h
@@ -8,6 +8,7 @@
#include "sd-lldp-rx.h"
#include "hash-funcs.h"
+#include "json.h"
#include "lldp-rx-internal.h"
#include "time-util.h"
@@ -90,3 +91,4 @@ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size);
int lldp_neighbor_parse(sd_lldp_neighbor *n);
void lldp_neighbor_start_ttl(sd_lldp_neighbor *n);
bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b);
+int lldp_neighbor_build_json(sd_lldp_neighbor *n, JsonVariant **ret);
diff --git a/src/libsystemd-network/lldp-rx-internal.h b/src/libsystemd-network/lldp-rx-internal.h
index 83d0bc4..e914c6b 100644
--- a/src/libsystemd-network/lldp-rx-internal.h
+++ b/src/libsystemd-network/lldp-rx-internal.h
@@ -5,6 +5,7 @@
#include "sd-lldp-rx.h"
#include "hashmap.h"
+#include "json.h"
#include "network-common.h"
#include "prioq.h"
@@ -36,6 +37,8 @@ struct sd_lldp_rx {
const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_;
sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_;
+int lldp_rx_build_neighbors_json(sd_lldp_rx *lldp_rx, JsonVariant **ret);
+
#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \
log_interface_prefix_full_errno( \
"LLDP Rx: ", \
diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
index 93186e2..718495c 100644
--- a/src/libsystemd-network/meson.build
+++ b/src/libsystemd-network/meson.build
@@ -2,22 +2,24 @@
sources = files(
'arp-util.c',
- 'dhcp-identifier.c',
'dhcp-network.c',
'dhcp-option.c',
'dhcp-packet.c',
'dhcp6-network.c',
'dhcp6-option.c',
'dhcp6-protocol.c',
+ 'icmp6-packet.c',
'icmp6-util.c',
'lldp-neighbor.c',
'lldp-network.c',
- 'ndisc-protocol.c',
- 'ndisc-router.c',
+ 'ndisc-option.c',
'network-common.c',
'network-internal.c',
+ 'sd-dhcp-client-id.c',
'sd-dhcp-client.c',
+ 'sd-dhcp-duid.c',
'sd-dhcp-lease.c',
+ 'sd-dhcp-server-lease.c',
'sd-dhcp-server.c',
'sd-dhcp6-client.c',
'sd-dhcp6-lease.c',
@@ -26,6 +28,10 @@ sources = files(
'sd-lldp-rx.c',
'sd-lldp-tx.c',
'sd-ndisc.c',
+ 'sd-ndisc-neighbor.c',
+ 'sd-ndisc-redirect.c',
+ 'sd-ndisc-router.c',
+ 'sd-ndisc-router-solicit.c',
'sd-radv.c',
)
@@ -85,16 +91,20 @@ executables += [
network_test_template + {
'sources' : files(
'test-ndisc-ra.c',
- 'icmp6-util-unix.c',
+ 'icmp6-test-util.c',
),
},
network_test_template + {
'sources' : files(
'test-ndisc-rs.c',
- 'icmp6-util-unix.c',
+ 'icmp6-test-util.c',
),
},
network_test_template + {
+ 'sources' : files('test-ndisc-send.c'),
+ 'type' : 'manual',
+ },
+ network_test_template + {
'sources' : files('test-sd-dhcp-lease.c'),
},
network_fuzz_template + {
@@ -115,7 +125,7 @@ executables += [
network_fuzz_template + {
'sources' : files(
'fuzz-ndisc-rs.c',
- 'icmp6-util-unix.c',
+ 'icmp6-test-util.c',
),
},
]
diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h
index 615de0d..4e82fd5 100644
--- a/src/libsystemd-network/ndisc-internal.h
+++ b/src/libsystemd-network/ndisc-internal.h
@@ -24,6 +24,7 @@ struct sd_ndisc {
sd_event *event;
int event_priority;
+ struct in6_addr link_local_addr;
struct ether_addr mac_addr;
sd_event_source *recv_event_source;
diff --git a/src/libsystemd-network/ndisc-neighbor-internal.h b/src/libsystemd-network/ndisc-neighbor-internal.h
new file mode 100644
index 0000000..aee6556
--- /dev/null
+++ b/src/libsystemd-network/ndisc-neighbor-internal.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_neighbor {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ uint32_t flags;
+ struct in6_addr target_address;
+
+ Set *options;
+};
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet);
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na);
diff --git a/src/libsystemd-network/ndisc-option.c b/src/libsystemd-network/ndisc-option.c
new file mode 100644
index 0000000..901a3b3
--- /dev/null
+++ b/src/libsystemd-network/ndisc-option.c
@@ -0,0 +1,1502 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "dns-domain.h"
+#include "ether-addr-util.h"
+#include "hostname-util.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "iovec-util.h"
+#include "missing_network.h"
+#include "ndisc-option.h"
+#include "network-common.h"
+#include "strv.h"
+#include "unaligned.h"
+
+/* RFC does not say anything about the maximum number of options, but let's limit the number of options for
+ * safety. Typically, the number of options in an ICMPv6 message should be only a few. */
+#define MAX_OPTIONS 128
+
+int ndisc_option_parse(
+ ICMP6Packet *p,
+ size_t offset,
+ uint8_t *ret_type,
+ size_t *ret_len,
+ const uint8_t **ret_opt) {
+
+ assert(p);
+
+ if (offset == p->raw_size)
+ return -ESPIPE; /* end of the packet */
+
+ if (offset > p->raw_size)
+ return -EBADMSG;
+
+ if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
+ return -EBADMSG;
+
+ assert_cc(alignof(struct nd_opt_hdr) == 1);
+ const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
+ if (hdr->nd_opt_len == 0)
+ return -EBADMSG;
+
+ size_t len = hdr->nd_opt_len * 8;
+ if (p->raw_size - offset < len)
+ return -EBADMSG;
+
+ if (ret_type)
+ *ret_type = hdr->nd_opt_type;
+ if (ret_len)
+ *ret_len = len;
+ if (ret_opt)
+ *ret_opt = p->raw_packet + offset;
+
+ return 0;
+}
+
+static sd_ndisc_option* ndisc_option_new(uint8_t type, size_t offset) {
+ sd_ndisc_option *p = new0(sd_ndisc_option, 1); /* use new0() here to make the fuzzers silent. */
+ if (!p)
+ return NULL;
+
+ /* As the same reason in the above, do not use the structured initializer here. */
+ p->type = type;
+ p->offset = offset;
+
+ return p;
+}
+
+static void ndisc_raw_done(sd_ndisc_raw *raw) {
+ if (!raw)
+ return;
+
+ free(raw->bytes);
+}
+
+static void ndisc_rdnss_done(sd_ndisc_rdnss *rdnss) {
+ if (!rdnss)
+ return;
+
+ free(rdnss->addresses);
+}
+
+static void ndisc_dnssl_done(sd_ndisc_dnssl *dnssl) {
+ if (!dnssl)
+ return;
+
+ strv_free(dnssl->domains);
+}
+
+sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option) {
+ if (!option)
+ return NULL;
+
+ switch (option->type) {
+ case 0:
+ ndisc_raw_done(&option->raw);
+ break;
+
+ case SD_NDISC_OPTION_RDNSS:
+ ndisc_rdnss_done(&option->rdnss);
+ break;
+
+ case SD_NDISC_OPTION_DNSSL:
+ ndisc_dnssl_done(&option->dnssl);
+ break;
+
+ case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+ free(option->captive_portal);
+ break;
+ }
+
+ return mfree(option);
+}
+
+static int ndisc_option_compare_func(const sd_ndisc_option *x, const sd_ndisc_option *y) {
+ int r;
+
+ assert(x);
+ assert(y);
+
+ r = CMP(x->type, y->type);
+ if (r != 0)
+ return r;
+
+ switch (x->type) {
+ case 0:
+ return memcmp_nn(x->raw.bytes, x->raw.length, y->raw.bytes, y->raw.length);
+
+ case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+ case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
+ case SD_NDISC_OPTION_REDIRECTED_HEADER:
+ case SD_NDISC_OPTION_MTU:
+ case SD_NDISC_OPTION_HOME_AGENT:
+ case SD_NDISC_OPTION_FLAGS_EXTENSION:
+ case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+ /* These options cannot be specified multiple times. */
+ return 0;
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ /* Should not specify the same prefix multiple times. */
+ r = CMP(x->prefix.prefixlen, y->prefix.prefixlen);
+ if (r != 0)
+ return r;
+
+ return memcmp(&x->prefix.address, &y->prefix.address, sizeof(struct in6_addr));
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ r = CMP(x->route.prefixlen, y->route.prefixlen);
+ if (r != 0)
+ return r;
+
+ return memcmp(&x->route.address, &y->route.address, sizeof(struct in6_addr));
+
+ case SD_NDISC_OPTION_PREF64:
+ r = CMP(x->prefix64.prefixlen, y->prefix64.prefixlen);
+ if (r != 0)
+ return r;
+
+ return memcmp(&x->prefix64.prefix, &y->prefix64.prefix, sizeof(struct in6_addr));
+
+ default:
+ /* DNSSL, RDNSS, and other unsupported options can be specified multiple times. */
+ return trivial_compare_func(x, y);
+ }
+}
+
+static void ndisc_option_hash_func(const sd_ndisc_option *option, struct siphash *state) {
+ assert(option);
+ assert(state);
+
+ siphash24_compress_typesafe(option->type, state);
+
+ switch (option->type) {
+ case 0:
+ siphash24_compress(option->raw.bytes, option->raw.length, state);
+ break;
+
+ case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+ case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
+ case SD_NDISC_OPTION_REDIRECTED_HEADER:
+ case SD_NDISC_OPTION_MTU:
+ case SD_NDISC_OPTION_HOME_AGENT:
+ case SD_NDISC_OPTION_FLAGS_EXTENSION:
+ case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+ break;
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ siphash24_compress_typesafe(option->prefix.prefixlen, state);
+ siphash24_compress_typesafe(option->prefix.address, state);
+ break;
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ siphash24_compress_typesafe(option->route.prefixlen, state);
+ siphash24_compress_typesafe(option->route.address, state);
+ break;
+
+ case SD_NDISC_OPTION_PREF64:
+ siphash24_compress_typesafe(option->prefix64.prefixlen, state);
+ siphash24_compress_typesafe(option->prefix64.prefix, state);
+ break;
+
+ default:
+ trivial_hash_func(option, state);
+ }
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ ndisc_option_hash_ops,
+ sd_ndisc_option,
+ ndisc_option_hash_func,
+ ndisc_option_compare_func,
+ ndisc_option_free);
+
+static int ndisc_option_consume(Set **options, sd_ndisc_option *p) {
+ assert(options);
+ assert(p);
+
+ if (set_size(*options) >= MAX_OPTIONS) {
+ ndisc_option_free(p);
+ return -ETOOMANYREFS; /* recognizable error code */
+ }
+
+ return set_ensure_consume(options, &ndisc_option_hash_ops, p);
+}
+
+int ndisc_option_set_raw(Set **options, size_t length, const uint8_t *bytes) {
+ _cleanup_free_ uint8_t *copy = NULL;
+
+ assert(options);
+ assert(bytes);
+
+ if (length == 0)
+ return -EINVAL;
+
+ copy = newdup(uint8_t, bytes, length);
+ if (!copy)
+ return -ENOMEM;
+
+ sd_ndisc_option *p = ndisc_option_new(/* type = */ 0, /* offset = */ 0);
+ if (!p)
+ return -ENOMEM;
+
+ p->raw = (sd_ndisc_raw) {
+ .bytes = TAKE_PTR(copy),
+ .length = length,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_build_raw(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(option->type == 0);
+ assert(ret);
+
+ _cleanup_free_ uint8_t *buf = newdup(uint8_t, option->raw.bytes, option->raw.length);
+ if (!buf)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_link_layer_address(Set **options, uint8_t type, size_t offset, const struct ether_addr *mac) {
+ assert(options);
+ assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
+
+ if (!mac || ether_addr_is_null(mac)) {
+ ndisc_option_remove_by_type(*options, type);
+ return 0;
+ }
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, type);
+ if (p) {
+ /* offset == 0 means that we are now building a packet to be sent, and in that case we allow
+ * to override the option we previously set.
+ * offset != 0 means that we are now parsing a received packet, and we refuse to override
+ * conflicting options. */
+ if (offset != 0)
+ return -EEXIST;
+
+ p->mac = *mac;
+ return 0;
+ }
+
+ p = ndisc_option_new(type, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->mac = *mac;
+
+ return set_ensure_consume(options, &ndisc_option_hash_ops, p);
+}
+
+static int ndisc_option_parse_link_layer_address(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (len != sizeof(struct ether_addr) + 2)
+ return -EBADMSG;
+
+ if (!IN_SET(opt[0], SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS))
+ return -EBADMSG;
+
+ struct ether_addr mac;
+ memcpy(&mac, opt + 2, sizeof(struct ether_addr));
+
+ if (ether_addr_is_null(&mac))
+ return -EBADMSG;
+
+ return ndisc_option_add_link_layer_address(options, opt[0], offset, &mac);
+}
+
+static int ndisc_option_build_link_layer_address(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(IN_SET(option->type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
+ assert(ret);
+
+ assert_cc(2 + sizeof(struct ether_addr) == 8);
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, 2 + sizeof(struct ether_addr));
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = option->type;
+ buf[1] = 1;
+ memcpy(buf + 2, &option->mac, sizeof(struct ether_addr));
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_prefix_internal(
+ Set **options,
+ size_t offset,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until) {
+
+ assert(options);
+ assert(address);
+
+ if (prefixlen > 128)
+ return -EINVAL;
+
+ struct in6_addr addr = *address;
+ in6_addr_mask(&addr, prefixlen);
+
+ /* RFC 4861 and 4862 only state that link-local prefix should be ignored.
+ * But here we also ignore null and multicast addresses. */
+ if (in6_addr_is_link_local(&addr) || in6_addr_is_null(&addr) || in6_addr_is_multicast(&addr))
+ return -EINVAL;
+
+ if (preferred_lifetime > valid_lifetime)
+ return -EINVAL;
+
+ if (preferred_until > valid_until)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+ .prefix.prefixlen = prefixlen,
+ .prefix.address = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->prefix.flags = flags;
+ p->prefix.valid_lifetime = valid_lifetime;
+ p->prefix.preferred_lifetime = preferred_lifetime;
+ p->prefix.valid_until = valid_until;
+ p->prefix.preferred_until = preferred_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_PREFIX_INFORMATION, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->prefix = (sd_ndisc_prefix) {
+ .flags = flags,
+ .prefixlen = prefixlen,
+ .address = addr,
+ .valid_lifetime = valid_lifetime,
+ .preferred_lifetime = preferred_lifetime,
+ .valid_until = valid_until,
+ .preferred_until = preferred_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_prefix(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ const struct nd_opt_prefix_info *pi = (const struct nd_opt_prefix_info*) ASSERT_PTR(opt);
+
+ assert(options);
+
+ if (len != sizeof(struct nd_opt_prefix_info))
+ return -EBADMSG;
+
+ if (pi->nd_opt_pi_type != SD_NDISC_OPTION_PREFIX_INFORMATION)
+ return -EBADMSG;
+
+ usec_t valid = be32_sec_to_usec(pi->nd_opt_pi_valid_time, /* max_as_infinity = */ true);
+ usec_t pref = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true);
+
+ /* We only support 64 bits interface identifier for addrconf. */
+ uint8_t flags = pi->nd_opt_pi_flags_reserved;
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO) && pi->nd_opt_pi_prefix_len != 64)
+ flags &= ~ND_OPT_PI_FLAG_AUTO;
+
+ return ndisc_option_add_prefix(options, offset, flags,
+ pi->nd_opt_pi_prefix_len, &pi->nd_opt_pi_prefix,
+ valid, pref);
+}
+
+static int ndisc_option_build_prefix(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_PREFIX_INFORMATION);
+ assert(ret);
+
+ assert_cc(sizeof(struct nd_opt_prefix_info) % 8 == 0);
+
+ _cleanup_free_ struct nd_opt_prefix_info *buf = new(struct nd_opt_prefix_info, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ usec_t valid = MIN(option->prefix.valid_lifetime,
+ usec_sub_unsigned(option->prefix.valid_until, timestamp));
+ usec_t pref = MIN3(valid,
+ option->prefix.preferred_lifetime,
+ usec_sub_unsigned(option->prefix.preferred_until, timestamp));
+
+ *buf = (struct nd_opt_prefix_info) {
+ .nd_opt_pi_type = SD_NDISC_OPTION_PREFIX_INFORMATION,
+ .nd_opt_pi_len = sizeof(struct nd_opt_prefix_info) / 8,
+ .nd_opt_pi_prefix_len = option->prefix.prefixlen,
+ .nd_opt_pi_flags_reserved = option->prefix.flags,
+ .nd_opt_pi_valid_time = usec_to_be32_sec(valid),
+ .nd_opt_pi_preferred_time = usec_to_be32_sec(pref),
+ .nd_opt_pi_prefix = option->prefix.address,
+ };
+
+ *ret = (uint8_t*) TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_redirected_header(Set **options, size_t offset, const struct ip6_hdr *hdr) {
+ assert(options);
+
+ if (!hdr) {
+ ndisc_option_remove_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ return 0;
+ }
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_REDIRECTED_HEADER, offset);
+ if (!p)
+ return -ENOMEM;
+
+ /* For safety, here we copy only IPv6 header. */
+ memcpy(&p->hdr, hdr, sizeof(struct ip6_hdr));
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_redirected_header(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (len < sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr))
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_REDIRECTED_HEADER)
+ return -EBADMSG;
+
+ return ndisc_option_add_redirected_header(options, offset, (const struct ip6_hdr*) (opt + sizeof(struct nd_opt_rd_hdr)));
+}
+
+static int ndisc_option_build_redirected_header(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_REDIRECTED_HEADER);
+ assert(ret);
+
+ assert_cc((sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr)) % 8 == 0);
+
+ size_t len = DIV_ROUND_UP(sizeof(struct nd_opt_rd_hdr) + sizeof(struct ip6_hdr), 8);
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ uint8_t *p;
+ p = mempcpy(buf,
+ &(const struct nd_opt_rd_hdr) {
+ .nd_opt_rh_type = SD_NDISC_OPTION_REDIRECTED_HEADER,
+ .nd_opt_rh_len = len,
+ },
+ sizeof(struct nd_opt_rd_hdr));
+ memcpy(p, &option->hdr, sizeof(struct ip6_hdr));
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_mtu(Set **options, size_t offset, uint32_t mtu) {
+ assert(options);
+
+ if (mtu < IPV6_MIN_MTU)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_MTU);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->mtu = mtu;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_MTU, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->mtu = mtu;
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_mtu(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ const struct nd_opt_mtu *pm = (const struct nd_opt_mtu*) ASSERT_PTR(opt);
+
+ assert(options);
+
+ if (len != sizeof(struct nd_opt_mtu))
+ return -EBADMSG;
+
+ if (pm->nd_opt_mtu_type != SD_NDISC_OPTION_MTU)
+ return -EBADMSG;
+
+ return ndisc_option_add_mtu(options, offset, be32toh(pm->nd_opt_mtu_mtu));
+}
+
+static int ndisc_option_build_mtu(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_MTU);
+ assert(ret);
+
+ assert_cc(sizeof(struct nd_opt_mtu) % 8 == 0);
+
+ _cleanup_free_ struct nd_opt_mtu *buf = new(struct nd_opt_mtu, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ *buf = (struct nd_opt_mtu) {
+ .nd_opt_mtu_type = SD_NDISC_OPTION_MTU,
+ .nd_opt_mtu_len = sizeof(struct nd_opt_mtu) / 8,
+ .nd_opt_mtu_mtu = htobe32(option->mtu),
+ };
+
+ *ret = (uint8_t*) TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_home_agent_internal(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ assert(options);
+
+ if (lifetime > UINT16_MAX * USEC_PER_SEC)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_HOME_AGENT);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->home_agent = (sd_ndisc_home_agent) {
+ .preference = preference,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_HOME_AGENT, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->home_agent = (sd_ndisc_home_agent) {
+ .preference = preference,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_home_agent(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ const struct nd_opt_home_agent_info *p = (const struct nd_opt_home_agent_info*) ASSERT_PTR(opt);
+
+ assert(options);
+
+ if (len != sizeof(struct nd_opt_home_agent_info))
+ return -EBADMSG;
+
+ if (p->nd_opt_home_agent_info_type != SD_NDISC_OPTION_HOME_AGENT)
+ return -EBADMSG;
+
+ return ndisc_option_add_home_agent(
+ options, offset,
+ be16toh(p->nd_opt_home_agent_info_preference),
+ be16_sec_to_usec(p->nd_opt_home_agent_info_lifetime, /* max_as_infinity = */ false));
+}
+
+static int ndisc_option_build_home_agent(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_HOME_AGENT);
+ assert(ret);
+
+ assert_cc(sizeof(struct nd_opt_home_agent_info) % 8 == 0);
+
+ usec_t lifetime = MIN(option->home_agent.lifetime,
+ usec_sub_unsigned(option->home_agent.valid_until, timestamp));
+
+ _cleanup_free_ struct nd_opt_home_agent_info *buf = new(struct nd_opt_home_agent_info, 1);
+ if (!buf)
+ return -ENOMEM;
+
+ *buf = (struct nd_opt_home_agent_info) {
+ .nd_opt_home_agent_info_type = SD_NDISC_OPTION_HOME_AGENT,
+ .nd_opt_home_agent_info_len = sizeof(struct nd_opt_home_agent_info) / 8,
+ .nd_opt_home_agent_info_preference = htobe16(option->home_agent.preference),
+ .nd_opt_home_agent_info_lifetime = usec_to_be16_sec(lifetime),
+ };
+
+ *ret = (uint8_t*) TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_route_internal(
+ Set **options,
+ size_t offset,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ assert(options);
+ assert(prefix);
+
+ if (prefixlen > 128)
+ return -EINVAL;
+
+ /* RFC 4191 section 2.3
+ * Prf (Route Preference)
+ * 2-bit signed integer. The Route Preference indicates whether to prefer the router associated with
+ * this prefix over others, when multiple identical prefixes (for different routers) have been
+ * received. If the Reserved (10) value is received, the Route Information Option MUST be ignored. */
+ if (!IN_SET(preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
+ return -EINVAL;
+
+ struct in6_addr addr = *prefix;
+ in6_addr_mask(&addr, prefixlen);
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_ROUTE_INFORMATION,
+ .route.prefixlen = prefixlen,
+ .route.address = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->route.preference = preference;
+ p->route.lifetime = lifetime;
+ p->route.valid_until = valid_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_ROUTE_INFORMATION, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->route = (sd_ndisc_route) {
+ .preference = preference,
+ .prefixlen = prefixlen,
+ .address = addr,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_route(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (!IN_SET(len, 1*8, 2*8, 3*8))
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_ROUTE_INFORMATION)
+ return -EBADMSG;
+
+ uint8_t prefixlen = opt[2];
+ if (prefixlen > 128)
+ return -EBADMSG;
+
+ if (len < (size_t) (DIV_ROUND_UP(prefixlen, 64) + 1) * 8)
+ return -EBADMSG;
+
+ uint8_t preference = (opt[3] >> 3) & 0x03;
+ usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
+
+ struct in6_addr prefix;
+ memcpy(&prefix, opt + 8, len - 8);
+ in6_addr_mask(&prefix, prefixlen);
+
+ return ndisc_option_add_route(options, offset, preference, prefixlen, &prefix, lifetime);
+}
+
+static int ndisc_option_build_route(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_ROUTE_INFORMATION);
+ assert(option->route.prefixlen <= 128);
+ assert(ret);
+
+ size_t len = 1 + DIV_ROUND_UP(option->route.prefixlen, 64);
+ be32_t lifetime = usec_to_be32_sec(MIN(option->route.lifetime,
+ usec_sub_unsigned(option->route.valid_until, timestamp)));
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = SD_NDISC_OPTION_ROUTE_INFORMATION;
+ buf[1] = len;
+ buf[2] = option->route.prefixlen;
+ buf[3] = option->route.preference << 3;
+ memcpy(buf + 4, &lifetime, sizeof(be32_t));
+ memcpy_safe(buf + 8, &option->route.address, (len - 1) * 8);
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_rdnss_internal(
+ Set **options,
+ size_t offset,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ assert(options);
+ assert(addresses);
+
+ if (n_addresses == 0)
+ return -EINVAL;
+
+ _cleanup_free_ struct in6_addr *addrs = newdup(struct in6_addr, addresses, n_addresses);
+ if (!addrs)
+ return -ENOMEM;
+
+ sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_RDNSS, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->rdnss = (sd_ndisc_rdnss) {
+ .n_addresses = n_addresses,
+ .addresses = TAKE_PTR(addrs),
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_rdnss(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (len < 8 + sizeof(struct in6_addr) || (len % sizeof(struct in6_addr)) != 8)
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_RDNSS)
+ return -EBADMSG;
+
+ usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
+ size_t n_addrs = len / sizeof(struct in6_addr);
+
+ return ndisc_option_add_rdnss(options, offset, n_addrs, (const struct in6_addr*) (opt + 8), lifetime);
+}
+
+static int ndisc_option_build_rdnss(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_RDNSS);
+ assert(ret);
+
+ size_t len = option->rdnss.n_addresses * 2 + 1;
+ be32_t lifetime = usec_to_be32_sec(MIN(option->rdnss.lifetime,
+ usec_sub_unsigned(option->rdnss.valid_until, timestamp)));
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = SD_NDISC_OPTION_RDNSS;
+ buf[1] = len;
+ buf[2] = 0;
+ buf[3] = 0;
+ memcpy(buf + 4, &lifetime, sizeof(be32_t));
+ memcpy(buf + 8, option->rdnss.addresses, sizeof(struct in6_addr) * option->rdnss.n_addresses);
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_flags_extension(Set **options, size_t offset, uint64_t flags) {
+ assert(options);
+
+ if ((flags & UINT64_C(0x00ffffffffffff00)) != flags)
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_FLAGS_EXTENSION);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->extended_flags = flags;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_FLAGS_EXTENSION, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->extended_flags = flags;
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_flags_extension(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (len != 8)
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_FLAGS_EXTENSION)
+ return -EBADMSG;
+
+ uint64_t flags = (unaligned_read_be64(opt) & UINT64_C(0xffffffffffff0000)) >> 8;
+ return ndisc_option_add_flags_extension(options, offset, flags);
+}
+
+static int ndisc_option_build_flags_extension(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_FLAGS_EXTENSION);
+ assert(ret);
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, 8);
+ if (!buf)
+ return 0;
+
+ unaligned_write_be64(buf, (option->extended_flags & UINT64_C(0x00ffffffffffff00)) << 8);
+ buf[0] = SD_NDISC_OPTION_FLAGS_EXTENSION;
+ buf[1] = 1;
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_dnssl_internal(
+ Set **options,
+ size_t offset, char *
+ const *domains,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ int r;
+
+ assert(options);
+
+ if (strv_isempty(domains))
+ return -EINVAL;
+
+ STRV_FOREACH(s, domains) {
+ r = dns_name_is_valid(*s);
+ if (r < 0)
+ return r;
+
+ if (is_localhost(*s) || dns_name_is_root(*s))
+ return -EINVAL;
+ }
+
+ _cleanup_strv_free_ char **copy = strv_copy(domains);
+ if (!copy)
+ return -ENOMEM;
+
+ sd_ndisc_option *p = ndisc_option_new(SD_NDISC_OPTION_DNSSL, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->dnssl = (sd_ndisc_dnssl) {
+ .domains = TAKE_PTR(copy),
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_dnssl(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ int r;
+
+ assert(options);
+ assert(opt);
+
+ if (len < 2*8)
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_DNSSL)
+ return -EBADMSG;
+
+ usec_t lifetime = unaligned_be32_sec_to_usec(opt + 4, /* max_as_infinity = */ true);
+
+ _cleanup_strv_free_ char **l = NULL;
+ _cleanup_free_ char *e = NULL;
+ size_t n = 0;
+ for (size_t c, pos = 8; pos < len; pos += c) {
+
+ c = opt[pos];
+ pos++;
+
+ if (c == 0) {
+ /* Found NUL termination */
+
+ if (n > 0) {
+ _cleanup_free_ char *normalized = NULL;
+
+ e[n] = 0;
+ r = dns_name_normalize(e, 0, &normalized);
+ if (r < 0)
+ return r;
+
+ /* Ignore the root domain name or "localhost" and friends */
+ if (!is_localhost(normalized) && !dns_name_is_root(normalized)) {
+ r = strv_consume(&l, TAKE_PTR(normalized));
+ if (r < 0)
+ return r;
+ }
+ }
+
+ n = 0;
+ continue;
+ }
+
+ /* Check for compression (which is not allowed) */
+ if (c > 63)
+ return -EBADMSG;
+
+ if (pos + c >= len)
+ return -EBADMSG;
+
+ if (!GREEDY_REALLOC(e, n + (n != 0) + DNS_LABEL_ESCAPED_MAX + 1U))
+ return -ENOMEM;
+
+ if (n != 0)
+ e[n++] = '.';
+
+ r = dns_label_escape((const char*) (opt + pos), c, e + n, DNS_LABEL_ESCAPED_MAX);
+ if (r < 0)
+ return r;
+
+ n += r;
+ }
+
+ if (n > 0) /* Not properly NUL terminated */
+ return -EBADMSG;
+
+ return ndisc_option_add_dnssl(options, offset, l, lifetime);
+}
+
+ static int ndisc_option_build_dnssl(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ int r;
+
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_DNSSL);
+ assert(ret);
+
+ size_t len = 8;
+ STRV_FOREACH(s, option->dnssl.domains)
+ len += strlen(*s) + 2;
+ len = DIV_ROUND_UP(len, 8);
+
+ be32_t lifetime = usec_to_be32_sec(MIN(option->dnssl.lifetime,
+ usec_sub_unsigned(option->dnssl.valid_until, timestamp)));
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = SD_NDISC_OPTION_DNSSL;
+ buf[1] = len;
+ buf[2] = 0;
+ buf[3] = 0;
+ memcpy(buf + 4, &lifetime, sizeof(be32_t));
+
+ size_t remaining = len * 8 - 8;
+ uint8_t *p = buf + 8;
+
+ STRV_FOREACH(s, option->dnssl.domains) {
+ r = dns_name_to_wire_format(*s, p, remaining, /* canonical = */ false);
+ if (r < 0)
+ return r;
+
+ assert(remaining >= (size_t) r);
+ p += r;
+ remaining -= r;
+ }
+
+ memzero(p, remaining);
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+int ndisc_option_add_captive_portal(Set **options, size_t offset, const char *portal) {
+ assert(options);
+
+ if (isempty(portal))
+ return -EINVAL;
+
+ if (!in_charset(portal, URI_VALID))
+ return -EINVAL;
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(*options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ return free_and_strdup(&p->captive_portal, portal);
+ }
+
+ _cleanup_free_ char *copy = strdup(portal);
+ if (!copy)
+ return -ENOMEM;
+
+ p = ndisc_option_new(SD_NDISC_OPTION_CAPTIVE_PORTAL, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->captive_portal = TAKE_PTR(copy);
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_captive_portal(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+
+ if (len < 8)
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_CAPTIVE_PORTAL)
+ return -EBADMSG;
+
+ _cleanup_free_ char *portal = memdup_suffix0(opt + 2, len - 2);
+ if (!portal)
+ return -ENOMEM;
+
+ size_t size = strlen(portal);
+ if (size == 0)
+ return -EBADMSG;
+
+ /* Check that the message is not truncated by an embedded NUL.
+ * NUL padding to a multiple of 8 is expected. */
+ if (DIV_ROUND_UP(size + 2, 8) * 8 != len && DIV_ROUND_UP(size + 3, 8) * 8 != len)
+ return -EBADMSG;
+
+ return ndisc_option_add_captive_portal(options, offset, portal);
+}
+
+static int ndisc_option_build_captive_portal(const sd_ndisc_option *option, uint8_t **ret) {
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_CAPTIVE_PORTAL);
+ assert(ret);
+
+ size_t len_portal = strlen(option->captive_portal);
+ size_t len = DIV_ROUND_UP(len_portal + 1 + 2, 8);
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, len * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = SD_NDISC_OPTION_CAPTIVE_PORTAL;
+ buf[1] = len;
+
+ uint8_t *p = mempcpy(buf + 2, option->captive_portal, len_portal);
+ size_t remaining = len * 8 - 2 - len_portal;
+
+ memzero(p, remaining);
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
+ [PREFIX_LENGTH_CODE_96] = 96,
+ [PREFIX_LENGTH_CODE_64] = 64,
+ [PREFIX_LENGTH_CODE_56] = 56,
+ [PREFIX_LENGTH_CODE_48] = 48,
+ [PREFIX_LENGTH_CODE_40] = 40,
+ [PREFIX_LENGTH_CODE_32] = 32,
+};
+
+int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) {
+ for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++)
+ if (prefix_length_code_to_prefix_length[i] == prefixlen) {
+ if (ret)
+ *ret = i;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int pref64_lifetime_and_plc_parse(uint16_t lifetime_and_plc, uint8_t *ret_prefixlen, usec_t *ret_lifetime) {
+ uint16_t plc = lifetime_and_plc & PREF64_PLC_MASK;
+ if (plc >= _PREFIX_LENGTH_CODE_MAX)
+ return -EINVAL;
+
+ if (ret_prefixlen)
+ *ret_prefixlen = prefix_length_code_to_prefix_length[plc];
+ if (ret_lifetime)
+ *ret_lifetime = (lifetime_and_plc & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
+ return 0;
+}
+
+int ndisc_option_add_prefix64_internal(
+ Set **options,
+ size_t offset,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+
+ int r;
+
+ assert(options);
+ assert(prefix);
+
+ r = pref64_prefix_length_to_plc(prefixlen, NULL);
+ if (r < 0)
+ return r;
+
+ if (lifetime > PREF64_MAX_LIFETIME_USEC)
+ return -EINVAL;
+
+ struct in6_addr addr = *prefix;
+ in6_addr_mask(&addr, prefixlen);
+
+ sd_ndisc_option *p = ndisc_option_get(
+ *options,
+ &(const sd_ndisc_option) {
+ .type = SD_NDISC_OPTION_PREF64,
+ .prefix64.prefixlen = prefixlen,
+ .prefix64.prefix = addr,
+ });
+ if (p) {
+ if (offset != 0)
+ return -EEXIST;
+
+ p->prefix64.lifetime = lifetime;
+ p->prefix64.valid_until = valid_until;
+ return 0;
+ }
+
+ p = ndisc_option_new(SD_NDISC_OPTION_PREF64, offset);
+ if (!p)
+ return -ENOMEM;
+
+ p->prefix64 = (sd_ndisc_prefix64) {
+ .prefixlen = prefixlen,
+ .prefix = addr,
+ .lifetime = lifetime,
+ .valid_until = valid_until,
+ };
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_option_parse_prefix64(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ int r;
+
+ assert(options);
+ assert(opt);
+
+ if (len != 2*8)
+ return -EBADMSG;
+
+ if (opt[0] != SD_NDISC_OPTION_PREF64)
+ return -EBADMSG;
+
+ uint8_t prefixlen;
+ usec_t lifetime;
+ r = pref64_lifetime_and_plc_parse(unaligned_read_be16(opt + 2), &prefixlen, &lifetime);
+ if (r < 0)
+ return r;
+
+ struct in6_addr prefix;
+ memcpy(&prefix, opt + 4, len - 4);
+ in6_addr_mask(&prefix, prefixlen);
+
+ return ndisc_option_add_prefix64(options, offset, prefixlen, &prefix, lifetime);
+}
+
+static int ndisc_option_build_prefix64(const sd_ndisc_option *option, usec_t timestamp, uint8_t **ret) {
+ int r;
+
+ assert(option);
+ assert(option->type == SD_NDISC_OPTION_PREF64);
+ assert(ret);
+
+ uint8_t code;
+ r = pref64_prefix_length_to_plc(option->prefix64.prefixlen, &code);
+ if (r < 0)
+ return r;
+
+ uint16_t lifetime = (uint16_t) DIV_ROUND_UP(MIN(option->prefix64.lifetime,
+ usec_sub_unsigned(option->prefix64.valid_until, timestamp)),
+ USEC_PER_SEC) & PREF64_SCALED_LIFETIME_MASK;
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, 2 * 8);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = SD_NDISC_OPTION_PREF64;
+ buf[1] = 2;
+ unaligned_write_be16(buf + 2, lifetime | code);
+ memcpy(buf + 4, &option->prefix64.prefix, 12);
+
+ *ret = TAKE_PTR(buf);
+ return 0;
+}
+
+static int ndisc_option_parse_default(Set **options, size_t offset, size_t len, const uint8_t *opt) {
+ assert(options);
+ assert(opt);
+ assert(len > 0);
+
+ sd_ndisc_option *p = ndisc_option_new(opt[0], offset);
+ if (!p)
+ return -ENOMEM;
+
+ return ndisc_option_consume(options, p);
+}
+
+static int ndisc_header_size(uint8_t icmp6_type) {
+ switch (icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ return sizeof(struct nd_router_solicit);
+ case ND_ROUTER_ADVERT:
+ return sizeof(struct nd_router_advert);
+ case ND_NEIGHBOR_SOLICIT:
+ return sizeof(struct nd_neighbor_solicit);
+ case ND_NEIGHBOR_ADVERT:
+ return sizeof(struct nd_neighbor_advert);
+ case ND_REDIRECT:
+ return sizeof(struct nd_redirect);
+ default:
+ return -EINVAL;
+ }
+}
+
+int ndisc_parse_options(ICMP6Packet *packet, Set **ret_options) {
+ _cleanup_set_free_ Set *options = NULL;
+ int r;
+
+ assert(packet);
+ assert(ret_options);
+
+ r = icmp6_packet_get_type(packet);
+ if (r < 0)
+ return r;
+
+ r = ndisc_header_size(r);
+ if (r < 0)
+ return -EBADMSG;
+ size_t header_size = r;
+
+ if (packet->raw_size < header_size)
+ return -EBADMSG;
+
+ for (size_t length, offset = header_size; offset < packet->raw_size; offset += length) {
+ uint8_t type;
+ const uint8_t *opt;
+
+ r = ndisc_option_parse(packet, offset, &type, &length, &opt);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse NDisc option header: %m");
+
+ switch (type) {
+ case 0:
+ r = -EBADMSG;
+ break;
+
+ case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+ case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
+ r = ndisc_option_parse_link_layer_address(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ r = ndisc_option_parse_prefix(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_REDIRECTED_HEADER:
+ r = ndisc_option_parse_redirected_header(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_MTU:
+ r = ndisc_option_parse_mtu(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_HOME_AGENT:
+ r = ndisc_option_parse_home_agent(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ r = ndisc_option_parse_route(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_RDNSS:
+ r = ndisc_option_parse_rdnss(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_FLAGS_EXTENSION:
+ r = ndisc_option_parse_flags_extension(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_DNSSL:
+ r = ndisc_option_parse_dnssl(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+ r = ndisc_option_parse_captive_portal(&options, offset, length, opt);
+ break;
+
+ case SD_NDISC_OPTION_PREF64:
+ r = ndisc_option_parse_prefix64(&options, offset, length, opt);
+ break;
+
+ default:
+ r = ndisc_option_parse_default(&options, offset, length, opt);
+ }
+ if (r == -ENOMEM)
+ return log_oom_debug();
+ if (r < 0)
+ log_debug_errno(r, "Failed to parse NDisc option %u, ignoring: %m", type);
+ }
+
+ *ret_options = TAKE_PTR(options);
+ return 0;
+}
+
+int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret) {
+ assert(IN_SET(type, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, SD_NDISC_OPTION_TARGET_LL_ADDRESS));
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(options, type);
+ if (!p)
+ return -ENODATA;
+
+ if (ret)
+ *ret = p->mac;
+ return 0;
+}
+
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp) {
+ int r;
+
+ assert(fd >= 0);
+ assert(dst);
+ assert(hdr);
+
+ size_t n;
+ _cleanup_free_ sd_ndisc_option **list = NULL;
+ r = set_dump_sorted(options, (void***) &list, &n);
+ if (r < 0)
+ return r;
+
+ struct iovec *iov = NULL;
+ size_t n_iov = 0;
+ CLEANUP_ARRAY(iov, n_iov, iovec_array_free);
+
+ iov = new(struct iovec, 1 + n);
+ if (!iov)
+ return -ENOMEM;
+
+ r = ndisc_header_size(hdr->icmp6_type);
+ if (r < 0)
+ return r;
+ size_t hdr_size = r;
+
+ _cleanup_free_ uint8_t *copy = newdup(uint8_t, hdr, hdr_size);
+ if (!copy)
+ return -ENOMEM;
+
+ iov[n_iov++] = IOVEC_MAKE(TAKE_PTR(copy), hdr_size);
+
+ FOREACH_ARRAY(p, list, n) {
+ _cleanup_free_ uint8_t *buf = NULL;
+ sd_ndisc_option *option = *p;
+
+ switch (option->type) {
+ case 0:
+ r = ndisc_option_build_raw(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
+ case SD_NDISC_OPTION_TARGET_LL_ADDRESS:
+ r = ndisc_option_build_link_layer_address(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ r = ndisc_option_build_prefix(option, timestamp, &buf);
+ break;
+
+ case SD_NDISC_OPTION_REDIRECTED_HEADER:
+ r = ndisc_option_build_redirected_header(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_MTU:
+ r = ndisc_option_build_mtu(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_HOME_AGENT:
+ r = ndisc_option_build_home_agent(option, timestamp, &buf);
+ break;
+
+ case SD_NDISC_OPTION_ROUTE_INFORMATION:
+ r = ndisc_option_build_route(option, timestamp, &buf);
+ break;
+
+ case SD_NDISC_OPTION_RDNSS:
+ r = ndisc_option_build_rdnss(option, timestamp, &buf);
+ break;
+
+ case SD_NDISC_OPTION_FLAGS_EXTENSION:
+ r = ndisc_option_build_flags_extension(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_DNSSL:
+ r = ndisc_option_build_dnssl(option, timestamp, &buf);
+ break;
+
+ case SD_NDISC_OPTION_CAPTIVE_PORTAL:
+ r = ndisc_option_build_captive_portal(option, &buf);
+ break;
+
+ case SD_NDISC_OPTION_PREF64:
+ r = ndisc_option_build_prefix64(option, timestamp, &buf);
+ break;
+
+ default:
+ continue;
+ }
+ if (r == -ENOMEM)
+ return log_oom_debug();
+ if (r < 0)
+ log_debug_errno(r, "Failed to build NDisc option %u, ignoring: %m", option->type);
+
+ iov[n_iov++] = IOVEC_MAKE(buf, buf[1] * 8);
+ TAKE_PTR(buf);
+ }
+
+ return icmp6_send(fd, dst, iov, n_iov);
+}
diff --git a/src/libsystemd-network/ndisc-option.h b/src/libsystemd-network/ndisc-option.h
new file mode 100644
index 0000000..d7bd861
--- /dev/null
+++ b/src/libsystemd-network/ndisc-option.h
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <sys/uio.h>
+
+#include "sd-ndisc-protocol.h"
+
+#include "icmp6-packet.h"
+#include "macro.h"
+#include "set.h"
+#include "time-util.h"
+
+typedef struct sd_ndisc_raw {
+ uint8_t *bytes;
+ size_t length;
+} sd_ndisc_raw;
+
+/* Mostly equivalent to struct nd_opt_prefix_info, but using usec_t. */
+typedef struct sd_ndisc_prefix {
+ uint8_t flags;
+ uint8_t prefixlen;
+ struct in6_addr address;
+ usec_t valid_lifetime;
+ usec_t preferred_lifetime;
+ /* timestamp in CLOCK_BOOTTIME, used when sending option for adjusting lifetime. */
+ usec_t valid_until;
+ usec_t preferred_until;
+} sd_ndisc_prefix;
+
+typedef struct sd_ndisc_home_agent {
+ uint16_t preference;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_home_agent;
+
+typedef struct sd_ndisc_route {
+ uint8_t preference;
+ uint8_t prefixlen;
+ struct in6_addr address;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_route;
+
+typedef struct sd_ndisc_rdnss {
+ size_t n_addresses;
+ struct in6_addr *addresses;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_rdnss;
+
+typedef struct sd_ndisc_dnssl {
+ char **domains;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_dnssl;
+
+typedef struct sd_ndisc_prefix64 {
+ uint8_t prefixlen;
+ struct in6_addr prefix;
+ usec_t lifetime;
+ usec_t valid_until;
+} sd_ndisc_prefix64;
+
+typedef struct sd_ndisc_option {
+ uint8_t type;
+ size_t offset;
+
+ union {
+ sd_ndisc_raw raw; /* for testing or unsupported options */
+ struct ether_addr mac; /* SD_NDISC_OPTION_SOURCE_LL_ADDRESS or SD_NDISC_OPTION_TARGET_LL_ADDRESS */
+ sd_ndisc_prefix prefix; /* SD_NDISC_OPTION_PREFIX_INFORMATION */
+ struct ip6_hdr hdr; /* SD_NDISC_OPTION_REDIRECTED_HEADER */
+ uint32_t mtu; /* SD_NDISC_OPTION_MTU */
+ sd_ndisc_home_agent home_agent; /* SD_NDISC_OPTION_HOME_AGENT */
+ sd_ndisc_route route; /* SD_NDISC_OPTION_ROUTE_INFORMATION */
+ sd_ndisc_rdnss rdnss; /* SD_NDISC_OPTION_RDNSS */
+ uint64_t extended_flags; /* SD_NDISC_OPTION_FLAGS_EXTENSION */
+ sd_ndisc_dnssl dnssl; /* SD_NDISC_OPTION_DNSSL */
+ char *captive_portal; /* SD_NDISC_OPTION_CAPTIVE_PORTAL */
+ sd_ndisc_prefix64 prefix64; /* SD_NDISC_OPTION_PREF64 */
+ };
+} sd_ndisc_option;
+
+/* RFC 8781: PREF64 or (NAT64 prefix) */
+#define PREF64_SCALED_LIFETIME_MASK 0xfff8
+#define PREF64_PLC_MASK 0x0007
+#define PREF64_MAX_LIFETIME_USEC (65528 * USEC_PER_SEC)
+
+typedef enum PrefixLengthCode {
+ PREFIX_LENGTH_CODE_96,
+ PREFIX_LENGTH_CODE_64,
+ PREFIX_LENGTH_CODE_56,
+ PREFIX_LENGTH_CODE_48,
+ PREFIX_LENGTH_CODE_40,
+ PREFIX_LENGTH_CODE_32,
+ _PREFIX_LENGTH_CODE_MAX,
+ _PREFIX_LENGTH_CODE_INVALID = -EINVAL,
+} PrefixLengthCode;
+
+/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PREFIX_LEN (Prefix Length Code): 3-bit unsigned integer */
+struct nd_opt_prefix64_info {
+ uint8_t type;
+ uint8_t length;
+ uint16_t lifetime_and_plc;
+ uint8_t prefix[12];
+} _packed_;
+
+int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret);
+
+sd_ndisc_option* ndisc_option_free(sd_ndisc_option *option);
+
+int ndisc_option_parse(
+ ICMP6Packet *p,
+ size_t offset,
+ uint8_t *ret_type,
+ size_t *ret_len,
+ const uint8_t **ret_opt);
+
+int ndisc_parse_options(ICMP6Packet *p, Set **ret_options);
+
+static inline sd_ndisc_option* ndisc_option_get(Set *options, const sd_ndisc_option *p) {
+ return set_get(options, ASSERT_PTR(p));
+}
+static inline sd_ndisc_option* ndisc_option_get_by_type(Set *options, uint8_t type) {
+ return ndisc_option_get(options, &(const sd_ndisc_option) { .type = type });
+}
+int ndisc_option_get_mac(Set *options, uint8_t type, struct ether_addr *ret);
+
+static inline void ndisc_option_remove(Set *options, const sd_ndisc_option *p) {
+ ndisc_option_free(set_remove(options, ASSERT_PTR(p)));
+}
+static inline void ndisc_option_remove_by_type(Set *options, uint8_t type) {
+ ndisc_option_remove(options, &(const sd_ndisc_option) { .type = type });
+}
+
+int ndisc_option_set_raw(
+ Set **options,
+ size_t length,
+ const uint8_t *bytes);
+int ndisc_option_add_link_layer_address(
+ Set **options,
+ uint8_t type,
+ size_t offset,
+ const struct ether_addr *mac);
+static inline int ndisc_option_set_link_layer_address(
+ Set **options,
+ uint8_t type,
+ const struct ether_addr *mac) {
+ return ndisc_option_add_link_layer_address(options, type, 0, mac);
+}
+int ndisc_option_add_prefix_internal(
+ Set **options,
+ size_t offset,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until);
+static inline int ndisc_option_add_prefix(
+ Set **options,
+ size_t offset,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime) {
+ return ndisc_option_add_prefix_internal(options, offset, flags, prefixlen, address,
+ valid_lifetime, preferred_lifetime,
+ USEC_INFINITY, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix(
+ Set **options,
+ uint8_t flags,
+ uint8_t prefixlen,
+ const struct in6_addr *address,
+ usec_t valid_lifetime,
+ usec_t preferred_lifetime,
+ usec_t valid_until,
+ usec_t preferred_until) {
+ return ndisc_option_add_prefix_internal(options, 0, flags, prefixlen, address,
+ valid_lifetime, preferred_lifetime,
+ valid_until, preferred_until);
+}
+int ndisc_option_add_redirected_header(
+ Set **options,
+ size_t offset,
+ const struct ip6_hdr *hdr);
+int ndisc_option_add_mtu(
+ Set **options,
+ size_t offset,
+ uint32_t mtu);
+static inline int ndisc_option_set_mtu(
+ Set **options,
+ uint32_t mtu) {
+ return ndisc_option_add_mtu(options, 0, mtu);
+}
+int ndisc_option_add_home_agent_internal(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_home_agent(
+ Set **options,
+ size_t offset,
+ uint16_t preference,
+ usec_t lifetime) {
+ return ndisc_option_add_home_agent_internal(options, offset, preference, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_home_agent(
+ Set **options,
+ uint16_t preference,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_home_agent_internal(options, 0, preference, lifetime, valid_until);
+}
+int ndisc_option_add_route_internal(
+ Set **options,
+ size_t offset,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_route(
+ Set **options,
+ size_t offset,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime) {
+ return ndisc_option_add_route_internal(options, offset, preference, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_route(
+ Set **options,
+ uint8_t preference,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_route_internal(options, 0, preference, prefixlen, prefix, lifetime, valid_until);
+}
+int ndisc_option_add_rdnss_internal(
+ Set **options,
+ size_t offset,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_rdnss(
+ Set **options,
+ size_t offset,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime) {
+ return ndisc_option_add_rdnss_internal(options, offset, n_addresses, addresses, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_rdnss(
+ Set **options,
+ size_t n_addresses,
+ const struct in6_addr *addresses,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_rdnss_internal(options, 0, n_addresses, addresses, lifetime, valid_until);
+}
+int ndisc_option_add_flags_extension(
+ Set **options,
+ size_t offset,
+ uint64_t flags);
+int ndisc_option_add_dnssl_internal(
+ Set **options,
+ size_t offset,
+ char * const *domains,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_dnssl(
+ Set **options,
+ size_t offset,
+ char * const *domains,
+ usec_t lifetime) {
+ return ndisc_option_add_dnssl_internal(options, offset, domains, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_dnssl(
+ Set **options,
+ char * const *domains,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_dnssl_internal(options, 0, domains, lifetime, valid_until);
+}
+int ndisc_option_add_captive_portal(
+ Set **options,
+ size_t offset,
+ const char *portal);
+static inline int ndisc_option_set_captive_portal(
+ Set **options,
+ const char *portal) {
+ return ndisc_option_add_captive_portal(options, 0, portal);
+}
+int ndisc_option_add_prefix64_internal(
+ Set **options,
+ size_t offset,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until);
+static inline int ndisc_option_add_prefix64(
+ Set **options,
+ size_t offset,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime) {
+ return ndisc_option_add_prefix64_internal(options, offset, prefixlen, prefix, lifetime, USEC_INFINITY);
+}
+static inline int ndisc_option_set_prefix64(
+ Set **options,
+ uint8_t prefixlen,
+ const struct in6_addr *prefix,
+ usec_t lifetime,
+ usec_t valid_until) {
+ return ndisc_option_add_prefix64_internal(options, 0, prefixlen, prefix, lifetime, valid_until);
+}
+
+int ndisc_send(int fd, const struct in6_addr *dst, const struct icmp6_hdr *hdr, Set *options, usec_t timestamp);
diff --git a/src/libsystemd-network/ndisc-protocol.c b/src/libsystemd-network/ndisc-protocol.c
deleted file mode 100644
index fae4a58..0000000
--- a/src/libsystemd-network/ndisc-protocol.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "ndisc-protocol.h"
-
-static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
- [PREFIX_LENGTH_CODE_96] = 96,
- [PREFIX_LENGTH_CODE_64] = 64,
- [PREFIX_LENGTH_CODE_56] = 56,
- [PREFIX_LENGTH_CODE_48] = 48,
- [PREFIX_LENGTH_CODE_40] = 40,
- [PREFIX_LENGTH_CODE_32] = 32,
-};
-
-int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret) {
- plc &= PREF64_PLC_MASK;
- if (plc >= _PREFIX_LENGTH_CODE_MAX)
- return -EINVAL;
-
- if (ret)
- *ret = prefix_length_code_to_prefix_length[plc];
- return 0;
-}
-
-int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) {
- assert(ret);
-
- for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++)
- if (prefix_length_code_to_prefix_length[i] == prefixlen) {
- *ret = i;
- return 0;
- }
-
- return -EINVAL;
-}
diff --git a/src/libsystemd-network/ndisc-protocol.h b/src/libsystemd-network/ndisc-protocol.h
deleted file mode 100644
index 8e403e3..0000000
--- a/src/libsystemd-network/ndisc-protocol.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include "time-util.h"
-
-/* RFC 8781: PREF64 or (NAT64 prefix) */
-#define PREF64_SCALED_LIFETIME_MASK 0xfff8
-#define PREF64_PLC_MASK 0x0007
-#define PREF64_MAX_LIFETIME_USEC (65528 * USEC_PER_SEC)
-
-typedef enum PrefixLengthCode {
- PREFIX_LENGTH_CODE_96,
- PREFIX_LENGTH_CODE_64,
- PREFIX_LENGTH_CODE_56,
- PREFIX_LENGTH_CODE_48,
- PREFIX_LENGTH_CODE_40,
- PREFIX_LENGTH_CODE_32,
- _PREFIX_LENGTH_CODE_MAX,
- _PREFIX_LENGTH_CODE_INVALID = -EINVAL,
-} PrefixLengthCode;
-
-/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PREFIX_LEN (Prefix Length Code): 3-bit unsigned integer */
-struct nd_opt_prefix64_info {
- uint8_t type;
- uint8_t length;
- uint16_t lifetime_and_plc;
- uint8_t prefix[12];
-} __attribute__((__packed__));
-
-int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret);
-int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret);
diff --git a/src/libsystemd-network/ndisc-redirect-internal.h b/src/libsystemd-network/ndisc-redirect-internal.h
new file mode 100644
index 0000000..e35263a
--- /dev/null
+++ b/src/libsystemd-network/ndisc-redirect-internal.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_redirect {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ struct in6_addr target_address;
+ struct in6_addr destination_address;
+
+ Set *options;
+};
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet);
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd);
diff --git a/src/libsystemd-network/ndisc-router-internal.h b/src/libsystemd-network/ndisc-router-internal.h
new file mode 100644
index 0000000..6df72fd
--- /dev/null
+++ b/src/libsystemd-network/ndisc-router-internal.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+/***
+ Copyright © 2014 Intel Corporation. All rights reserved.
+***/
+
+#include "sd-ndisc.h"
+
+#include "icmp6-packet.h"
+#include "ndisc-option.h"
+#include "time-util.h"
+
+struct sd_ndisc_router {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ /* From RA header */
+ uint8_t hop_limit;
+ uint8_t flags;
+ uint8_t preference;
+ usec_t lifetime_usec;
+ usec_t reachable_time_usec;
+ usec_t retransmission_time_usec;
+
+ /* Options */
+ Set *options;
+ Iterator iterator;
+ sd_ndisc_option *current_option;
+};
+
+sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet);
+int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt);
+
+int ndisc_router_flags_to_string(uint64_t flags, char **ret);
+const char* ndisc_router_preference_to_string(int s) _const_;
diff --git a/src/libsystemd-network/ndisc-router-solicit-internal.h b/src/libsystemd-network/ndisc-router-solicit-internal.h
new file mode 100644
index 0000000..6f0b0af
--- /dev/null
+++ b/src/libsystemd-network/ndisc-router-solicit-internal.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-radv.h"
+
+#include "icmp6-packet.h"
+#include "set.h"
+
+struct sd_ndisc_router_solicit {
+ unsigned n_ref;
+
+ ICMP6Packet *packet;
+
+ Set *options;
+};
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet);
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs);
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c
deleted file mode 100644
index 5162df7..0000000
--- a/src/libsystemd-network/ndisc-router.c
+++ /dev/null
@@ -1,913 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
- Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include <netinet/icmp6.h>
-
-#include "sd-ndisc.h"
-
-#include "alloc-util.h"
-#include "dns-domain.h"
-#include "hostname-util.h"
-#include "memory-util.h"
-#include "missing_network.h"
-#include "ndisc-internal.h"
-#include "ndisc-protocol.h"
-#include "ndisc-router.h"
-#include "strv.h"
-
-DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
-
-sd_ndisc_router *ndisc_router_new(size_t raw_size) {
- sd_ndisc_router *rt;
-
- if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router)))
- return NULL;
-
- rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
- if (!rt)
- return NULL;
-
- rt->raw_size = raw_size;
- rt->n_ref = 1;
-
- return rt;
-}
-
-int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (in6_addr_is_null(&rt->address))
- return -ENODATA;
-
- *ret = rt->address;
- return 0;
-}
-
-int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
- assert_return(clock_supported(clock), -EOPNOTSUPP);
- assert_return(ret, -EINVAL);
-
- if (!triple_timestamp_is_set(&rt->timestamp))
- return -ENODATA;
-
- *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
- return 0;
-}
-
-#define DEFINE_GET_TIMESTAMP(name) \
- int sd_ndisc_router_##name##_timestamp( \
- sd_ndisc_router *rt, \
- clockid_t clock, \
- uint64_t *ret) { \
- \
- usec_t s, t; \
- int r; \
- \
- assert_return(rt, -EINVAL); \
- assert_return(ret, -EINVAL); \
- \
- r = sd_ndisc_router_##name(rt, &s); \
- if (r < 0) \
- return r; \
- \
- r = sd_ndisc_router_get_timestamp(rt, clock, &t); \
- if (r < 0) \
- return r; \
- \
- *ret = time_span_to_stamp(s, t); \
- return 0; \
- }
-
-DEFINE_GET_TIMESTAMP(get_lifetime);
-DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
-DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
-DEFINE_GET_TIMESTAMP(route_get_lifetime);
-DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
-DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
-DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
-
-int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(ret_size, -EINVAL);
-
- *ret = NDISC_ROUTER_RAW(rt);
- *ret_size = rt->raw_size;
-
- return 0;
-}
-
-static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) {
- uint16_t lifetime_and_plc;
-
- assert(p);
-
- if (length != sizeof(struct nd_opt_prefix64_info))
- return false;
-
- lifetime_and_plc = be16toh(p->lifetime_and_plc);
- if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0)
- return false;
-
- return true;
-}
-
-int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
- struct nd_router_advert *a;
- const uint8_t *p;
- bool has_mtu = false, has_flag_extension = false;
- size_t left;
-
- assert(rt);
-
- if (rt->raw_size < sizeof(struct nd_router_advert))
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Too small to be a router advertisement, ignoring.");
-
- /* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
- a = NDISC_ROUTER_RAW(rt);
-
- if (a->nd_ra_type != ND_ROUTER_ADVERT)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Received ND packet that is not a router advertisement, ignoring.");
-
- if (a->nd_ra_code != 0)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Received ND packet with wrong RA code, ignoring.");
-
- rt->hop_limit = a->nd_ra_curhoplimit;
- rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
- rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
- rt->icmp6_ratelimit_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
-
- rt->preference = (rt->flags >> 3) & 3;
- if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
- rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
-
- p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
- left = rt->raw_size - sizeof(struct nd_router_advert);
-
- for (;;) {
- uint8_t type;
- size_t length;
-
- if (left == 0)
- break;
-
- if (left < 2)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Option lacks header, ignoring datagram.");
-
- type = p[0];
- length = p[1] * 8;
-
- if (length == 0)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Zero-length option, ignoring datagram.");
- if (left < length)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Option truncated, ignoring datagram.");
-
- switch (type) {
-
- case SD_NDISC_OPTION_PREFIX_INFORMATION:
-
- if (length != 4*8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Prefix option of invalid size, ignoring datagram.");
-
- if (p[2] > 128)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Bad prefix length, ignoring datagram.");
-
- break;
-
- case SD_NDISC_OPTION_MTU: {
- uint32_t m;
-
- if (has_mtu) {
- log_ndisc(nd, "MTU option specified twice, ignoring.");
- break;
- }
-
- if (length != 8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "MTU option of invalid size, ignoring datagram.");
-
- m = be32toh(*(uint32_t*) (p + 4));
- if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
- rt->mtu = m;
-
- has_mtu = true;
- break;
- }
-
- case SD_NDISC_OPTION_ROUTE_INFORMATION:
- if (length < 1*8 || length > 3*8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Route information option of invalid size, ignoring datagram.");
-
- if (p[2] > 128)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Bad route prefix length, ignoring datagram.");
-
- break;
-
- case SD_NDISC_OPTION_RDNSS:
- if (length < 3*8 || (length % (2*8)) != 1*8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size.");
-
- break;
-
- case SD_NDISC_OPTION_FLAGS_EXTENSION:
-
- if (has_flag_extension) {
- log_ndisc(nd, "Flags extension option specified twice, ignoring.");
- break;
- }
-
- if (length < 1*8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "Flags extension option has invalid size.");
-
- /* Add in the additional flags bits */
- rt->flags |=
- ((uint64_t) p[2] << 8) |
- ((uint64_t) p[3] << 16) |
- ((uint64_t) p[4] << 24) |
- ((uint64_t) p[5] << 32) |
- ((uint64_t) p[6] << 40) |
- ((uint64_t) p[7] << 48);
-
- has_flag_extension = true;
- break;
-
- case SD_NDISC_OPTION_DNSSL:
- if (length < 2*8)
- return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "DNSSL option has invalid size.");
-
- break;
- case SD_NDISC_OPTION_PREF64: {
- if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length))
- log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
- "PREF64 prefix has invalid prefix length.");
- break;
- }}
-
- p += length, left -= length;
- }
-
- rt->rindex = sizeof(struct nd_router_advert);
- return 0;
-}
-
-int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- *ret = rt->hop_limit;
- return 0;
-}
-
-int sd_ndisc_router_get_icmp6_ratelimit(sd_ndisc_router *rt, uint64_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- *ret = rt->icmp6_ratelimit_usec;
- return 0;
-}
-
-int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- *ret = rt->flags;
- return 0;
-}
-
-int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- *ret = rt->lifetime_usec;
- return 0;
-}
-
-int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- *ret = rt->preference;
- return 0;
-}
-
-int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (rt->mtu <= 0)
- return -ENODATA;
-
- *ret = rt->mtu;
- return 0;
-}
-
-int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
- assert_return(rt, -EINVAL);
-
- assert(rt->raw_size >= sizeof(struct nd_router_advert));
- rt->rindex = sizeof(struct nd_router_advert);
-
- return rt->rindex < rt->raw_size;
-}
-
-int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
- size_t length;
-
- assert_return(rt, -EINVAL);
-
- if (rt->rindex == rt->raw_size) /* EOF */
- return -ESPIPE;
-
- if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
- return -EBADMSG;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (rt->rindex + length > rt->raw_size)
- return -EBADMSG;
-
- rt->rindex += length;
- return rt->rindex < rt->raw_size;
-}
-
-int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- if (rt->rindex == rt->raw_size) /* EOF */
- return -ESPIPE;
-
- if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
- return -EBADMSG;
-
- *ret = NDISC_ROUTER_OPTION_TYPE(rt);
- return 0;
-}
-
-int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
- uint8_t k;
- int r;
-
- assert_return(rt, -EINVAL);
-
- r = sd_ndisc_router_option_get_type(rt, &k);
- if (r < 0)
- return r;
-
- return type == k;
-}
-
-int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_size) {
- size_t length;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(ret_size, -EINVAL);
-
- /* Note that this returns the full option, including the option header */
-
- if (rt->rindex + 2 > rt->raw_size)
- return -EBADMSG;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (rt->rindex + length > rt->raw_size)
- return -EBADMSG;
-
- *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
- *ret_size = length;
-
- return 0;
-}
-
-static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
- struct nd_opt_prefix_info *ri;
- size_t length;
- int r;
-
- assert(rt);
- assert(ret);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (length != sizeof(struct nd_opt_prefix_info))
- return -EBADMSG;
-
- ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
- if (ri->nd_opt_pi_prefix_len > 128)
- return -EBADMSG;
-
- *ret = ri;
- return 0;
-}
-
-int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- struct nd_opt_prefix_info *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_prefix_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = be32_sec_to_usec(ri->nd_opt_pi_valid_time, /* max_as_infinity = */ true);
- return 0;
-}
-
-int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- struct nd_opt_prefix_info *pi;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- *ret = be32_sec_to_usec(pi->nd_opt_pi_preferred_time, /* max_as_infinity = */ true);
- return 0;
-}
-
-int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
- struct nd_opt_prefix_info *pi;
- uint8_t flags;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- flags = pi->nd_opt_pi_flags_reserved;
-
- if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
- log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
- flags &= ~ND_OPT_PI_FLAG_AUTO;
- }
-
- *ret = flags;
- return 0;
-}
-
-int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
- struct nd_opt_prefix_info *pi;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- *ret = pi->nd_opt_pi_prefix;
- return 0;
-}
-
-int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
- struct nd_opt_prefix_info *pi;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- if (pi->nd_opt_pi_prefix_len > 128)
- return -EBADMSG;
-
- *ret = pi->nd_opt_pi_prefix_len;
- return 0;
-}
-
-static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
- uint8_t *ri;
- size_t length;
- int r;
-
- assert(rt);
- assert(ret);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (length < 1*8 || length > 3*8)
- return -EBADMSG;
-
- ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
-
- if (ri[2] > 128)
- return -EBADMSG;
-
- *ret = ri;
- return 0;
-}
-
-int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_route_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
- return 0;
-}
-
-int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_route_info(rt, &ri);
- if (r < 0)
- return r;
-
- zero(*ret);
- memcpy(ret, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
-
- return 0;
-}
-
-int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_route_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = ri[2];
- return 0;
-}
-
-int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_route_info(rt, &ri);
- if (r < 0)
- return r;
-
- if (!IN_SET((ri[3] >> 3) & 3, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH))
- return -EOPNOTSUPP;
-
- *ret = (ri[3] >> 3) & 3;
- return 0;
-}
-
-static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
- size_t length;
- int r;
-
- assert(rt);
- assert(ret);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (length < 3*8 || (length % (2*8)) != 1*8)
- return -EBADMSG;
-
- *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
- return 0;
-}
-
-int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_rdnss_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = (const struct in6_addr*) (ri + 8);
- return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
-}
-
-int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_rdnss_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
- return 0;
-}
-
-static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
- size_t length;
- int r;
-
- assert(rt);
- assert(ret);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (length < 2*8)
- return -EBADMSG;
-
- *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
- return 0;
-}
-
-int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
- _cleanup_strv_free_ char **l = NULL;
- _cleanup_free_ char *e = NULL;
- size_t n = 0, left;
- uint8_t *ri, *p;
- bool first = true;
- int r;
- unsigned k = 0;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_dnssl_info(rt, &ri);
- if (r < 0)
- return r;
-
- p = ri + 8;
- left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
-
- for (;;) {
- if (left == 0) {
-
- if (n > 0) /* Not properly NUL terminated */
- return -EBADMSG;
-
- break;
- }
-
- if (*p == 0) {
- /* Found NUL termination */
-
- if (n > 0) {
- _cleanup_free_ char *normalized = NULL;
-
- e[n] = 0;
- r = dns_name_normalize(e, 0, &normalized);
- if (r < 0)
- return r;
-
- /* Ignore the root domain name or "localhost" and friends */
- if (!is_localhost(normalized) &&
- !dns_name_is_root(normalized)) {
-
- if (strv_push(&l, normalized) < 0)
- return -ENOMEM;
-
- normalized = NULL;
- k++;
- }
- }
-
- n = 0;
- first = true;
- p++, left--;
- continue;
- }
-
- /* Check for compression (which is not allowed) */
- if (*p > 63)
- return -EBADMSG;
-
- if (1U + *p + 1U > left)
- return -EBADMSG;
-
- if (!GREEDY_REALLOC(e, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
- return -ENOMEM;
-
- if (first)
- first = false;
- else
- e[n++] = '.';
-
- r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
- if (r < 0)
- return r;
-
- n += r;
-
- left -= 1 + *p;
- p += 1 + *p;
- }
-
- if (strv_isempty(l)) {
- *ret = NULL;
- return 0;
- }
-
- *ret = TAKE_PTR(l);
-
- return k;
-}
-
-int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- uint8_t *ri;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_dnssl_info(rt, &ri);
- if (r < 0)
- return r;
-
- *ret = unaligned_be32_sec_to_usec(ri + 4, /* max_as_infinity = */ true);
- return 0;
-}
-
-int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret, size_t *ret_size) {
- int r;
- const char *nd_opt_captive_portal;
- size_t length;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
- assert_return(ret_size, -EINVAL);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_CAPTIVE_PORTAL);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- r = sd_ndisc_router_option_get_raw(rt, (void *)&nd_opt_captive_portal, &length);
- if (r < 0)
- return r;
-
- /* The length field has units of 8 octets */
- assert(length % 8 == 0);
- if (length == 0)
- return -EBADMSG;
-
- /* Check that the message is not truncated by an embedded NUL.
- * NUL padding to a multiple of 8 is expected. */
- size_t size = strnlen(nd_opt_captive_portal + 2, length - 2);
- if (DIV_ROUND_UP(size + 2, 8) != length / 8)
- return -EBADMSG;
-
- /* Let's not return an empty buffer */
- if (size == 0) {
- *ret = NULL;
- *ret_size = 0;
- return 0;
- }
-
- *ret = nd_opt_captive_portal + 2;
- *ret_size = size;
-
- return 0;
-}
-
-static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) {
- struct nd_opt_prefix64_info *ri;
- size_t length;
- int r;
-
- assert(rt);
- assert(ret);
-
- r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64);
- if (r < 0)
- return r;
- if (r == 0)
- return -EMEDIUMTYPE;
-
- length = NDISC_ROUTER_OPTION_LENGTH(rt);
- if (length != sizeof(struct nd_opt_prefix64_info))
- return -EBADMSG;
-
- ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
- if (!pref64_option_verify(ri, length))
- return -EBADMSG;
-
- *ret = ri;
- return 0;
-}
-
-int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret) {
- struct nd_opt_prefix64_info *pi;
- struct in6_addr a = {};
- unsigned prefixlen;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_pref64_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen);
- if (r < 0)
- return r;
-
- memcpy(&a, pi->prefix, sizeof(pi->prefix));
- in6_addr_mask(&a, prefixlen);
- /* extra safety check for refusing malformed prefix. */
- if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0)
- return -EBADMSG;
-
- *ret = a;
- return 0;
-}
-
-int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
- struct nd_opt_prefix64_info *pi;
- uint16_t lifetime_prefix_len;
- uint8_t prefix_len;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_pref64_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
- pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len);
-
- *ret = prefix_len;
- return 0;
-}
-
-int sd_ndisc_router_prefix64_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
- struct nd_opt_prefix64_info *pi;
- uint16_t lifetime_prefix_len;
- int r;
-
- assert_return(rt, -EINVAL);
- assert_return(ret, -EINVAL);
-
- r = get_pref64_prefix_info(rt, &pi);
- if (r < 0)
- return r;
-
- lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
-
- *ret = (lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK) * USEC_PER_SEC;
- return 0;
-}
diff --git a/src/libsystemd-network/ndisc-router.h b/src/libsystemd-network/ndisc-router.h
deleted file mode 100644
index 0a55e1a..0000000
--- a/src/libsystemd-network/ndisc-router.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-/***
- Copyright © 2014 Intel Corporation. All rights reserved.
-***/
-
-#include "sd-ndisc.h"
-
-#include "time-util.h"
-
-struct sd_ndisc_router {
- unsigned n_ref;
-
- triple_timestamp timestamp;
- struct in6_addr address;
-
- /* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */
- size_t raw_size;
-
- /* The current read index for the iterative option interface */
- size_t rindex;
-
- uint64_t flags;
- unsigned preference;
- uint64_t lifetime_usec;
-
- uint8_t hop_limit;
- uint32_t mtu;
- uint64_t icmp6_ratelimit_usec;
-};
-
-static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
- return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
-}
-
-static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) {
- return ((uint8_t*) NDISC_ROUTER_RAW(rt)) + rt->rindex;
-}
-
-static inline uint8_t NDISC_ROUTER_OPTION_TYPE(const sd_ndisc_router *rt) {
- return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[0];
-}
-static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) {
- return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8;
-}
-
-sd_ndisc_router *ndisc_router_new(size_t raw_size);
-int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt);
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index d6cec90..0a535dd 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -10,7 +10,7 @@
#include "sd-radv.h"
#include "list.h"
-#include "ndisc-protocol.h"
+#include "ndisc-option.h"
#include "network-common.h"
#include "sparse-endian.h"
#include "time-util.h"
@@ -43,9 +43,11 @@
#define RADV_MAX_ROUTER_LIFETIME_USEC (9000 * USEC_PER_SEC)
#define RADV_DEFAULT_ROUTER_LIFETIME_USEC (3 * RADV_DEFAULT_MAX_TIMEOUT_USEC)
/* RFC 4861 section 4.2.
- * Retrans Timer
+ * Reachable Time and Retrans Timer
* 32-bit unsigned integer. The time, in milliseconds. */
-#define RADV_MAX_RETRANSMIT_USEC (UINT32_MAX * USEC_PER_MSEC)
+#define RADV_MAX_UINT32_MSEC_USEC (UINT32_MAX * USEC_PER_MSEC)
+#define RADV_MAX_REACHABLE_TIME_USEC RADV_MAX_UINT32_MSEC_USEC
+#define RADV_MAX_RETRANSMIT_USEC RADV_MAX_UINT32_MSEC_USEC
/* draft-ietf-6man-slaac-renum-02 section 4.1.1.
* AdvPreferredLifetime: max(AdvDefaultLifetime, 3 * MaxRtrAdvInterval)
* AdvValidLifetime: 2 * AdvPreferredLifetime */
@@ -79,11 +81,10 @@
/* Pref64 option type (RFC8781, section 4) */
#define RADV_OPT_PREF64 38
-enum RAdvState {
+typedef enum RAdvState {
RADV_STATE_IDLE = 0,
RADV_STATE_ADVERTISING = 1,
-};
-typedef enum RAdvState RAdvState;
+} RAdvState;
struct sd_radv_opt_dns {
uint8_t type;
@@ -98,6 +99,7 @@ struct sd_radv {
int ifindex;
char *ifname;
+ struct in6_addr ipv6ll;
sd_event *event;
int event_priority;
@@ -105,7 +107,9 @@ struct sd_radv {
struct ether_addr mac_addr;
uint8_t hop_limit;
uint8_t flags;
+ uint8_t preference;
uint32_t mtu;
+ usec_t reachable_usec;
usec_t retransmit_usec;
usec_t lifetime_usec; /* timespan */
diff --git a/src/libsystemd-network/sd-dhcp-client-id.c b/src/libsystemd-network/sd-dhcp-client-id.c
new file mode 100644
index 0000000..cab04f0
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp-client-id.c
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "dhcp-client-id-internal.h"
+#include "iovec-util.h"
+#include "unaligned.h"
+#include "utf8.h"
+
+int sd_dhcp_client_id_clear(sd_dhcp_client_id *client_id) {
+ assert_return(client_id, -EINVAL);
+
+ *client_id = (sd_dhcp_client_id) {};
+ return 0;
+}
+
+int sd_dhcp_client_id_is_set(const sd_dhcp_client_id *client_id) {
+ if (!client_id)
+ return false;
+
+ return client_id_size_is_valid(client_id->size);
+}
+
+int sd_dhcp_client_id_get(const sd_dhcp_client_id *client_id, uint8_t *ret_type, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret_type, -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ *ret_type = client_id->id.type;
+ *ret_data = client_id->id.data;
+ *ret_size = client_id->size - offsetof(typeof(client_id->id), data);
+ return 0;
+}
+
+int sd_dhcp_client_id_get_raw(const sd_dhcp_client_id *client_id, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ /* Unlike sd_dhcp_client_id_get(), this returns whole client ID including its type. */
+
+ *ret_data = client_id->raw;
+ *ret_size = client_id->size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set(
+ sd_dhcp_client_id *client_id,
+ uint8_t type,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ if (!client_id_data_size_is_valid(data_size))
+ return -EINVAL;
+
+ client_id->id.type = type;
+ memcpy(client_id->id.data, data, data_size);
+
+ client_id->size = offsetof(typeof(client_id->id), data) + data_size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set_raw(
+ sd_dhcp_client_id *client_id,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ /* Unlike sd_dhcp_client_id_set(), this takes whole client ID including its type. */
+
+ if (!client_id_size_is_valid(data_size))
+ return -EINVAL;
+
+ memcpy(client_id->raw, data, data_size);
+
+ client_id->size = data_size;
+ return 0;
+}
+
+int sd_dhcp_client_id_set_iaid_duid(
+ sd_dhcp_client_id *client_id,
+ uint32_t iaid,
+ sd_dhcp_duid *duid) {
+
+ assert_return(client_id, -EINVAL);
+ assert_return(duid, -EINVAL);
+ assert_return(sd_dhcp_duid_is_set(duid), -ESTALE);
+
+ client_id->id.type = 255;
+ unaligned_write_be32(&client_id->id.ns.iaid, iaid);
+ memcpy(&client_id->id.ns.duid, &duid->duid, duid->size);
+
+ client_id->size = offsetof(typeof(client_id->id), ns.duid) + duid->size;
+ return 0;
+}
+
+int sd_dhcp_client_id_to_string(const sd_dhcp_client_id *client_id, char **ret) {
+ _cleanup_free_ char *t = NULL;
+ size_t len;
+ int r;
+
+ assert_return(sd_dhcp_client_id_is_set(client_id), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ len = client_id->size - offsetof(typeof(client_id->id), data);
+
+ switch (client_id->id.type) {
+ case 0:
+ if (utf8_is_printable((char *) client_id->id.gen.data, len))
+ r = asprintf(&t, "%.*s", (int) len, client_id->id.gen.data);
+ else
+ r = asprintf(&t, "DATA");
+ break;
+ case 1:
+ if (len == sizeof_field(sd_dhcp_client_id, id.eth))
+ r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x",
+ client_id->id.eth.haddr[0],
+ client_id->id.eth.haddr[1],
+ client_id->id.eth.haddr[2],
+ client_id->id.eth.haddr[3],
+ client_id->id.eth.haddr[4],
+ client_id->id.eth.haddr[5]);
+ else
+ r = asprintf(&t, "ETHER");
+ break;
+ case 2 ... 254:
+ r = asprintf(&t, "ARP/LL");
+ break;
+ case 255:
+ if (len < sizeof(uint32_t))
+ r = asprintf(&t, "IAID/DUID");
+ else {
+ uint32_t iaid = be32toh(client_id->id.ns.iaid);
+ /* TODO: check and stringify DUID */
+ r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+ }
+ break;
+ default:
+ assert_not_reached();
+ }
+ if (r < 0)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(t);
+ return 0;
+}
+
+int sd_dhcp_client_id_to_string_from_raw(const void *data, size_t data_size, char **ret) {
+ sd_dhcp_client_id client_id;
+ int r;
+
+ assert_return(data, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_dhcp_client_id_set_raw(&client_id, data, data_size);
+ if (r < 0)
+ return r;
+
+ return sd_dhcp_client_id_to_string(&client_id, ret);
+}
+
+void client_id_hash_func(const sd_dhcp_client_id *client_id, struct siphash *state) {
+ assert(sd_dhcp_client_id_is_set(client_id));
+ assert(state);
+
+ siphash24_compress_typesafe(client_id->size, state);
+ siphash24_compress(client_id->raw, client_id->size, state);
+}
+
+int client_id_compare_func(const sd_dhcp_client_id *a, const sd_dhcp_client_id *b) {
+ assert(sd_dhcp_client_id_is_set(a));
+ assert(sd_dhcp_client_id_is_set(b));
+
+ return memcmp_nn(a->raw, a->size, b->raw, b->size);
+}
+
+int json_dispatch_client_id(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+ sd_dhcp_client_id *client_id = ASSERT_PTR(userdata);
+ _cleanup_(iovec_done) struct iovec iov = {};
+ int r;
+
+ r = json_dispatch_byte_array_iovec(name, variant, flags, &iov);
+ if (r < 0)
+ return r;
+
+ r = sd_dhcp_client_id_set_raw(client_id, iov.iov_base, iov.iov_len);
+ if (r < 0)
+ return json_log(variant, flags, r, "Failed to set DHCP client ID from JSON field '%s': %m", strna(name));
+
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 24bcd74..1eb8509 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -15,8 +15,8 @@
#include "alloc-util.h"
#include "device-util.h"
+#include "dhcp-client-id-internal.h"
#include "dhcp-client-internal.h"
-#include "dhcp-identifier.h"
#include "dhcp-lease-internal.h"
#include "dhcp-network.h"
#include "dhcp-option.h"
@@ -39,7 +39,6 @@
#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)
@@ -48,32 +47,6 @@
#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;
@@ -89,6 +62,7 @@ struct sd_dhcp_client {
int fd;
uint16_t port;
+ uint16_t server_port;
union sockaddr_union link;
sd_event_source *receive_message;
bool request_broadcast;
@@ -100,7 +74,6 @@ struct sd_dhcp_client {
struct hw_addr_data bcast_addr;
uint16_t arp_type;
sd_dhcp_client_id client_id;
- size_t client_id_len;
char *hostname;
char *vendor_class_identifier;
char *mudurl;
@@ -181,58 +154,6 @@ static int client_receive_message_udp(
static void client_stop(sd_dhcp_client *client, int error);
static int client_restart(sd_dhcp_client *client);
-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))
- 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]);
- else
- r = asprintf(&t, "ETHER");
- break;
- case 2 ... 254:
- r = asprintf(&t, "ARP/LL");
- break;
- case 255:
- if (len < sizeof(uint32_t))
- r = asprintf(&t, "IAID/DUID");
- else {
- uint32_t iaid = be32toh(client_id->ns.iaid);
- /* TODO: check and stringify DUID */
- r = asprintf(&t, "IAID:0x%x/DUID", iaid);
- }
- break;
- }
- if (r < 0)
- return -ENOMEM;
-
- *ret = TAKE_PTR(t);
- return 0;
-}
-
int dhcp_client_set_state_callback(
sd_dhcp_client *client,
sd_dhcp_client_callback_t cb,
@@ -363,34 +284,14 @@ int sd_dhcp_client_set_mac(
return 0;
}
-int sd_dhcp_client_get_client_id(
- sd_dhcp_client *client,
- uint8_t *ret_type,
- const uint8_t **ret_data,
- size_t *ret_data_len) {
-
+int sd_dhcp_client_get_client_id(sd_dhcp_client *client, const sd_dhcp_client_id **ret) {
assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (client->client_id_len > 0) {
- if (client->client_id_len <= offsetof(sd_dhcp_client_id, raw.data))
- return -EINVAL;
-
- if (ret_type)
- *ret_type = client->client_id.type;
- if (ret_data)
- *ret_data = client->client_id.raw.data;
- if (ret_data_len)
- *ret_data_len = client->client_id_len - offsetof(sd_dhcp_client_id, raw.data);
- return 1;
- }
-
- if (ret_type)
- *ret_type = 0;
- if (ret_data)
- *ret_data = NULL;
- if (ret_data_len)
- *ret_data_len = 0;
+ if (!sd_dhcp_client_id_is_set(&client->client_id))
+ return -ENODATA;
+ *ret = &client->client_id;
return 0;
}
@@ -403,7 +304,7 @@ int sd_dhcp_client_set_client_id(
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
assert_return(data, -EINVAL);
- assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL);
+ assert_return(client_id_data_size_is_valid(data_len), -EINVAL);
/* For hardware types, log debug message about unexpected data length.
*
@@ -416,42 +317,28 @@ int sd_dhcp_client_set_client_id(
"Changing client ID to hardware type %u with unexpected address length %zu",
type, data_len);
- 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);
-
- return 0;
+ return sd_dhcp_client_id_set(&client->client_id, type, data, data_len);
}
-/**
- * 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(
+static int dhcp_client_set_iaid_duid(
sd_dhcp_client *client,
bool iaid_set,
- uint32_t iaid) {
+ uint32_t iaid,
+ sd_dhcp_duid *duid) {
int r;
- assert_return(client, -EINVAL);
- assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
-
- zero(client->client_id);
- client->client_id.type = 255;
-
- if (iaid_set)
- client->client_id.ns.iaid = htobe32(iaid);
- else {
+ if (!iaid_set) {
r = dhcp_identifier_set_iaid(client->dev, &client->hw_addr,
/* legacy_unstable_byteorder = */ true,
- &client->client_id.ns.iaid);
+ &iaid);
if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set IAID: %m");
+ return r;
+
+ iaid = be32toh(iaid);
}
- return 0;
+ return sd_dhcp_client_id_set_iaid_duid(&client->client_id, iaid, duid);
}
int sd_dhcp_client_set_iaid_duid_llt(
@@ -460,23 +347,17 @@ int sd_dhcp_client_set_iaid_duid_llt(
uint32_t iaid,
usec_t llt_time) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_llt(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_ll(
@@ -484,23 +365,17 @@ int sd_dhcp_client_set_iaid_duid_ll(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_ll(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_en(
@@ -508,23 +383,17 @@ int sd_dhcp_client_set_iaid_duid_en(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_en(&duid);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_uuid(
@@ -532,23 +401,17 @@ int sd_dhcp_client_set_iaid_duid_uuid(
bool iaid_set,
uint32_t iaid) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set_uuid(&duid);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_iaid_duid_raw(
@@ -556,27 +419,21 @@ int sd_dhcp_client_set_iaid_duid_raw(
bool iaid_set,
uint32_t iaid,
uint16_t duid_type,
- const uint8_t *duid,
- size_t duid_len) {
+ const uint8_t *duid_data,
+ size_t duid_data_len) {
- size_t len;
+ sd_dhcp_duid duid;
int r;
assert_return(client, -EINVAL);
assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
- assert_return(duid || duid_len == 0, -EINVAL);
+ assert_return(duid_data || duid_data_len == 0, -EINVAL);
- r = dhcp_client_set_iaid(client, iaid_set, iaid);
+ r = sd_dhcp_duid_set(&duid, duid_type, duid_data, duid_data_len);
if (r < 0)
return r;
- r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len);
- if (r < 0)
- return log_dhcp_client_errno(client, r, "Failed to set DUID: %m");
-
- client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len;
-
- return 0;
+ return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid);
}
int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) {
@@ -660,6 +517,18 @@ int sd_dhcp_client_set_client_port(
return 0;
}
+int sd_dhcp_client_set_port(
+ sd_dhcp_client *client,
+ uint16_t port) {
+
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp_client_is_running(client), -EBUSY);
+
+ client->server_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_MIN_PACKET_SIZE, -ERANGE);
@@ -925,8 +794,8 @@ static int client_message_init(
Identifier option is not set */
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_CLIENT_IDENTIFIER,
- client->client_id_len,
- &client->client_id);
+ client->client_id.size,
+ client->client_id.raw);
if (r < 0)
return r;
@@ -1035,7 +904,7 @@ static int dhcp_client_send_raw(
size_t len) {
dhcp_packet_append_ip_headers(packet, INADDR_ANY, client->port,
- INADDR_BROADCAST, DHCP_PORT_SERVER, len, client->ip_service_type);
+ INADDR_BROADCAST, client->server_port, len, client->ip_service_type);
return dhcp_network_send_raw_socket(client->fd, &client->link,
packet, len);
@@ -1257,7 +1126,7 @@ static int client_send_request(sd_dhcp_client *client) {
if (client->state == DHCP_STATE_RENEWING)
r = dhcp_network_send_udp_socket(client->fd,
client->lease->server_address,
- DHCP_PORT_SERVER,
+ client->server_port,
&request->dhcp,
sizeof(DHCPMessage) + optoffset);
else
@@ -1599,10 +1468,8 @@ static int client_parse_message(
if (r < 0)
return r;
- if (client->client_id_len > 0) {
- r = dhcp_lease_set_client_id(lease,
- (uint8_t *) &client->client_id,
- client->client_id_len);
+ if (sd_dhcp_client_id_is_set(&client->client_id)) {
+ r = dhcp_lease_set_client_id(lease, &client->client_id);
if (r < 0)
return r;
}
@@ -2302,7 +2169,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
return r;
/* If no client identifier exists, construct an RFC 4361-compliant one */
- if (client->client_id_len == 0) {
+ if (!sd_dhcp_client_id_is_set(&client->client_id)) {
r = sd_dhcp_client_set_iaid_duid_en(client, /* iaid_set = */ false, /* iaid = */ 0);
if (r < 0)
return r;
@@ -2349,7 +2216,7 @@ int sd_dhcp_client_send_release(sd_dhcp_client *client) {
r = dhcp_network_send_udp_socket(client->fd,
client->lease->server_address,
- DHCP_PORT_SERVER,
+ client->server_port,
&release->dhcp,
sizeof(DHCPMessage) + optoffset);
if (r < 0)
@@ -2383,7 +2250,7 @@ int sd_dhcp_client_send_decline(sd_dhcp_client *client) {
r = dhcp_network_send_udp_socket(client->fd,
client->lease->server_address,
- DHCP_PORT_SERVER,
+ client->server_port,
&release->dhcp,
sizeof(DHCPMessage) + optoffset);
if (r < 0)
@@ -2528,6 +2395,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
.fd = -EBADF,
.mtu = DHCP_MIN_PACKET_SIZE,
.port = DHCP_PORT_CLIENT,
+ .server_port = DHCP_PORT_SERVER,
.anonymize = !!anonymize,
.max_discover_attempts = UINT64_MAX,
.max_request_attempts = 5,
diff --git a/src/libsystemd-network/sd-dhcp-duid.c b/src/libsystemd-network/sd-dhcp-duid.c
new file mode 100644
index 0000000..4782ec6
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp-duid.c
@@ -0,0 +1,288 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <linux/if_infiniband.h>
+#include <net/ethernet.h>
+#include <net/if_arp.h>
+
+#include "dhcp-duid-internal.h"
+#include "hexdecoct.h"
+#include "netif-util.h"
+#include "network-common.h"
+#include "siphash24.h"
+#include "string-table.h"
+#include "unaligned.h"
+
+#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
+#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
+#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
+
+static const char * const duid_type_table[_DUID_TYPE_MAX] = {
+ [DUID_TYPE_LLT] = "DUID-LLT",
+ [DUID_TYPE_EN] = "DUID-EN/Vendor",
+ [DUID_TYPE_LL] = "DUID-LL",
+ [DUID_TYPE_UUID] = "UUID",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType);
+
+int sd_dhcp_duid_clear(sd_dhcp_duid *duid) {
+ assert_return(duid, -EINVAL);
+
+ *duid = (sd_dhcp_duid) {};
+ return 0;
+}
+
+int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) {
+ if (!duid)
+ return false;
+
+ return duid_size_is_valid(duid->size);
+}
+
+int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_type, -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ *ret_type = be16toh(duid->duid.type);
+ *ret_data = duid->duid.data;
+ *ret_size = duid->size - offsetof(struct duid, data);
+ return 0;
+}
+
+int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) {
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret_data, -EINVAL);
+ assert_return(ret_size, -EINVAL);
+
+ /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */
+
+ *ret_data = duid->raw;
+ *ret_size = duid->size;
+ return 0;
+}
+
+int sd_dhcp_duid_set(
+ sd_dhcp_duid *duid,
+ uint16_t duid_type,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ if (!duid_data_size_is_valid(data_size))
+ return -EINVAL;
+
+ unaligned_write_be16(&duid->duid.type, duid_type);
+ memcpy(duid->duid.data, data, data_size);
+
+ duid->size = offsetof(struct duid, data) + data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_raw(
+ sd_dhcp_duid *duid,
+ const void *data,
+ size_t data_size) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(data, -EINVAL);
+
+ /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */
+
+ if (!duid_size_is_valid(data_size))
+ return -EINVAL;
+
+ memcpy(duid->raw, data, data_size);
+
+ duid->size = data_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_llt(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type,
+ uint64_t usec) {
+
+ uint16_t time_from_2000y;
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT);
+ unaligned_write_be16(&duid->duid.llt.htype, arp_type);
+ unaligned_write_be32(&duid->duid.llt.time, time_from_2000y);
+ memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_ll(
+ sd_dhcp_duid *duid,
+ const void *hw_addr,
+ size_t hw_addr_size,
+ uint16_t arp_type) {
+
+ assert_return(duid, -EINVAL);
+ assert_return(hw_addr, -EINVAL);
+
+ if (arp_type == ARPHRD_ETHER)
+ assert_return(hw_addr_size == ETH_ALEN, -EINVAL);
+ else if (arp_type == ARPHRD_INFINIBAND)
+ assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL);
+ else
+ return -EOPNOTSUPP;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL);
+ unaligned_write_be16(&duid->duid.ll.htype, arp_type);
+ memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size);
+
+ duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size;
+ return 0;
+}
+
+int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ bool test_mode;
+ uint64_t hash;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ test_mode = network_test_mode_enabled();
+
+ if (!test_mode) {
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return r;
+ } else
+ /* For tests, especially for fuzzers, reproducibility is important.
+ * Hence, use a static and constant machine ID.
+ * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
+ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN);
+ unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN);
+
+ /* a bit of snake-oil perhaps, but no need to expose the machine-id
+ * directly; duid->en.id might not be aligned, so we need to copy */
+ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
+ memcpy(duid->duid.en.id, &hash, sizeof(hash));
+
+ duid->size = offsetof(struct duid, en.id) + sizeof(hash);
+
+ if (test_mode)
+ assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0);
+
+ return 0;
+}
+
+int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) {
+ sd_id128_t machine_id;
+ int r;
+
+ assert_return(duid, -EINVAL);
+
+ r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
+ if (r < 0)
+ return r;
+
+ unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID);
+ memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id));
+
+ duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
+ return 0;
+}
+
+int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret) {
+ _cleanup_free_ char *p = NULL, *x = NULL;
+ const char *t;
+
+ assert(data);
+ assert(ret);
+
+ if (!duid_data_size_is_valid(data_size))
+ return -EINVAL;
+
+ x = hexmem(data, data_size);
+ if (!x)
+ return -ENOMEM;
+
+ t = duid_type_to_string(type);
+ if (!t)
+ return asprintf(ret, "%04x:%s", htobe16(type), x);
+
+ p = strjoin(t, ":", x);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
+int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret) {
+ uint16_t type;
+ const void *data;
+ size_t data_size;
+ int r;
+
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_dhcp_duid_get(duid, &type, &data, &data_size);
+ if (r < 0)
+ return r;
+
+ return dhcp_duid_to_string_internal(type, data, data_size, ret);
+}
+
+int dhcp_identifier_set_iaid(
+ sd_device *dev,
+ const struct hw_addr_data *hw_addr,
+ bool legacy_unstable_byteorder,
+ void *ret) {
+
+ const char *name = NULL;
+ uint32_t id32;
+ uint64_t id;
+
+ assert(hw_addr);
+ assert(ret);
+
+ if (dev)
+ name = net_get_persistent_name(dev);
+ if (name)
+ id = siphash24(name, strlen(name), HASH_KEY.bytes);
+ else
+ /* fall back to MAC address if no predictable name available */
+ id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes);
+
+ id32 = (id & 0xffffffff) ^ (id >> 32);
+
+ if (legacy_unstable_byteorder)
+ /* for historical reasons (a bug), the bits were swapped and thus
+ * the result was endianness dependent. Preserve that behavior. */
+ id32 = bswap_32(id32);
+ else
+ /* the fixed behavior returns a stable byte order. Since LE is expected
+ * to be more common, swap the bytes on LE to give the same as legacy
+ * behavior. */
+ id32 = be32toh(id32);
+
+ unaligned_write_ne32(ret, id32);
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 202d75f..37f4b3b 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -420,7 +420,6 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
free(lease->static_routes);
free(lease->classless_routes);
- free(lease->client_id);
free(lease->vendor_specific);
strv_free(lease->search_domains);
free(lease->sixrd_br_addresses);
@@ -1070,8 +1069,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
_cleanup_fclose_ FILE *f = NULL;
struct in_addr address;
const struct in_addr *addresses;
- const void *client_id, *data;
- size_t client_id_len, data_len;
+ const void *data;
+ size_t data_len;
const char *string;
uint16_t mtu;
_cleanup_free_ sd_dhcp_route **routes = NULL;
@@ -1187,11 +1186,10 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
if (r >= 0)
fprintf(f, "TIMEZONE=%s\n", string);
- r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
- if (r >= 0) {
+ if (sd_dhcp_client_id_is_set(&lease->client_id)) {
_cleanup_free_ char *client_id_hex = NULL;
- client_id_hex = hexmem(client_id, client_id_len);
+ client_id_hex = hexmem(lease->client_id.raw, lease->client_id.size);
if (!client_id_hex)
return -ENOMEM;
fprintf(f, "CLIENTID=%s\n", client_id_hex);
@@ -1482,13 +1480,20 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
}
if (client_id_hex) {
- r = unhexmem(client_id_hex, SIZE_MAX, &lease->client_id, &lease->client_id_len);
+ _cleanup_free_ void *data = NULL;
+ size_t data_size;
+
+ r = unhexmem(client_id_hex, &data, &data_size);
if (r < 0)
log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex);
+
+ r = sd_dhcp_client_id_set_raw(&lease->client_id, data, data_size);
+ if (r < 0)
+ log_debug_errno(r, "Failed to assign client ID, ignoring: %m");
}
if (vendor_specific_hex) {
- r = unhexmem(vendor_specific_hex, SIZE_MAX, &lease->vendor_specific, &lease->vendor_specific_len);
+ r = unhexmem(vendor_specific_hex, &lease->vendor_specific, &lease->vendor_specific_len);
if (r < 0)
log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex);
}
@@ -1500,7 +1505,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
if (!options[i])
continue;
- r = unhexmem(options[i], SIZE_MAX, &data, &len);
+ r = unhexmem(options[i], &data, &len);
if (r < 0) {
log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]);
continue;
@@ -1541,36 +1546,25 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) {
return 0;
}
-int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) {
+int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id **ret) {
assert_return(lease, -EINVAL);
- assert_return(client_id, -EINVAL);
- assert_return(client_id_len, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (!lease->client_id)
+ if (!sd_dhcp_client_id_is_set(&lease->client_id))
return -ENODATA;
- *client_id = lease->client_id;
- *client_id_len = lease->client_id_len;
+ *ret = &lease->client_id;
return 0;
}
-int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) {
+int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const sd_dhcp_client_id *client_id) {
assert_return(lease, -EINVAL);
- assert_return(client_id || client_id_len <= 0, -EINVAL);
-
- if (client_id_len <= 0)
- lease->client_id = mfree(lease->client_id);
- else {
- void *p;
- p = memdup(client_id, client_id_len);
- if (!p)
- return -ENOMEM;
+ if (!sd_dhcp_client_id_is_set(client_id))
+ return sd_dhcp_client_id_clear(&lease->client_id);
- free_and_replace(lease->client_id, p);
- lease->client_id_len = client_id_len;
- }
+ lease->client_id = *client_id;
return 0;
}
diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c
new file mode 100644
index 0000000..2f84d51
--- /dev/null
+++ b/src/libsystemd-network/sd-dhcp-server-lease.c
@@ -0,0 +1,513 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dhcp-server-lease-internal.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "mkdir.h"
+#include "tmpfile-util.h"
+
+static sd_dhcp_server_lease* dhcp_server_lease_free(sd_dhcp_server_lease *lease) {
+ if (!lease)
+ return NULL;
+
+ if (lease->server) {
+ hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
+ hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
+ }
+
+ free(lease->hostname);
+ return mfree(lease);
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp_server_lease, sd_dhcp_server_lease, dhcp_server_lease_free);
+
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ dhcp_server_lease_hash_ops,
+ sd_dhcp_client_id,
+ client_id_hash_func,
+ client_id_compare_func,
+ sd_dhcp_server_lease,
+ sd_dhcp_server_lease_unref);
+
+int dhcp_server_put_lease(sd_dhcp_server *server, sd_dhcp_server_lease *lease, bool is_static) {
+ int r;
+
+ assert(server);
+ assert(lease);
+
+ lease->server = server; /* This must be set before hashmap_put(). */
+
+ r = hashmap_ensure_put(is_static ? &server->static_leases_by_client_id : &server->bound_leases_by_client_id,
+ &dhcp_server_lease_hash_ops, &lease->client_id, lease);
+ if (r < 0)
+ return r;
+
+ r = hashmap_ensure_put(is_static ? &server->static_leases_by_address : &server->bound_leases_by_address,
+ NULL, UINT32_TO_PTR(lease->address), lease);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int dhcp_server_set_lease(sd_dhcp_server *server, be32_t address, DHCPRequest *req, usec_t expiration) {
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+ int r;
+
+ assert(server);
+ assert(address != 0);
+ assert(req);
+ assert(expiration != 0);
+
+ /* If a lease for the host already exists, update it. */
+ lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
+ if (lease) {
+ if (lease->address != address) {
+ hashmap_remove_value(server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
+ lease->address = address;
+
+ r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ if (r < 0)
+ return r;
+ }
+
+ lease->expiration = expiration;
+
+ TAKE_PTR(lease);
+ return 0;
+ }
+
+ /* Otherwise, add a new lease. */
+
+ lease = new(sd_dhcp_server_lease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
+ .address = address,
+ .client_id = req->client_id,
+ .htype = req->message->htype,
+ .hlen = req->message->hlen,
+ .gateway = req->message->giaddr,
+ .expiration = expiration,
+ };
+
+ memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
+
+ if (req->hostname) {
+ lease->hostname = strdup(req->hostname);
+ if (!lease->hostname)
+ return -ENOMEM;
+ }
+
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lease);
+ return 0;
+}
+
+int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
+ sd_dhcp_server_lease *lease;
+ usec_t time_now;
+ int r;
+
+ assert(server);
+
+ r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
+ if (lease->expiration < time_now) {
+ log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
+ sd_dhcp_server_lease_unref(lease);
+ }
+
+ return 0;
+}
+
+sd_dhcp_server_lease* dhcp_server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req) {
+ sd_dhcp_server_lease *static_lease;
+ sd_dhcp_client_id client_id;
+
+ assert(server);
+ assert(req);
+
+ static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
+ if (static_lease)
+ goto verify;
+
+ /* when no lease is found based on the client id fall back to chaddr */
+ if (!client_id_data_size_is_valid(req->message->hlen))
+ return NULL;
+
+ if (sd_dhcp_client_id_set(&client_id, /* type = */ 1, req->message->chaddr, req->message->hlen) < 0)
+ return NULL;
+
+ static_lease = hashmap_get(server->static_leases_by_client_id, &client_id);
+ if (!static_lease)
+ return NULL;
+
+verify:
+ /* Check if the address is in the same subnet. */
+ if ((static_lease->address & server->netmask) != server->subnet)
+ return NULL;
+
+ /* Check if the address is different from the server address. */
+ if (static_lease->address == server->address)
+ return NULL;
+
+ return static_lease;
+}
+
+int sd_dhcp_server_set_static_lease(
+ sd_dhcp_server *server,
+ const struct in_addr *address,
+ uint8_t *client_id_raw,
+ size_t client_id_size) {
+
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+ sd_dhcp_client_id client_id;
+ int r;
+
+ assert_return(server, -EINVAL);
+ assert_return(client_id_raw, -EINVAL);
+ assert_return(client_id_size_is_valid(client_id_size), -EINVAL);
+ assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
+
+ r = sd_dhcp_client_id_set_raw(&client_id, client_id_raw, client_id_size);
+ if (r < 0)
+ return r;
+
+ /* Static lease with an empty or omitted address is a valid entry,
+ * the server removes any static lease with the specified mac address. */
+ if (!address || address->s_addr == 0) {
+ sd_dhcp_server_lease_unref(hashmap_get(server->static_leases_by_client_id, &client_id));
+ return 0;
+ }
+
+ lease = new(sd_dhcp_server_lease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
+ .address = address->s_addr,
+ .client_id = client_id,
+ };
+
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ true);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lease);
+ return 0;
+}
+
+static int dhcp_server_lease_append_json(sd_dhcp_server_lease *lease, JsonVariant **ret) {
+ assert(lease);
+ assert(ret);
+
+ return json_build(ret,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_BYTE_ARRAY("ClientId", lease->client_id.raw, lease->client_id.size),
+ JSON_BUILD_PAIR_IN4_ADDR_NON_NULL("Address", &(struct in_addr) { .s_addr = lease->address }),
+ JSON_BUILD_PAIR_STRING_NON_EMPTY("Hostname", lease->hostname)));
+}
+
+int dhcp_server_bound_leases_append_json(sd_dhcp_server *server, JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+ sd_dhcp_server_lease *lease;
+ usec_t now_b, now_r;
+ int r;
+
+ assert(server);
+ assert(v);
+
+ r = sd_event_now(server->event, CLOCK_BOOTTIME, &now_b);
+ if (r < 0)
+ return r;
+
+ r = sd_event_now(server->event, CLOCK_REALTIME, &now_r);
+ if (r < 0)
+ return r;
+
+ HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ r = dhcp_server_lease_append_json(lease, &w);
+ if (r < 0)
+ return r;
+
+ usec_t exp_r = map_clock_usec_raw(lease->expiration, now_b, now_r);
+
+ r = json_variant_merge_objectb(&w,
+ JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_UNSIGNED("ExpirationUSec", lease->expiration),
+ JSON_BUILD_PAIR_UNSIGNED("ExpirationRealtimeUSec", exp_r)));
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&array, w);
+ if (r < 0)
+ return r;
+ }
+
+ return json_variant_set_field_non_null(v, "Leases", array);
+}
+
+int dhcp_server_static_leases_append_json(sd_dhcp_server *server, JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
+ sd_dhcp_server_lease *lease;
+ int r;
+
+ assert(server);
+ assert(v);
+
+ HASHMAP_FOREACH(lease, server->static_leases_by_client_id) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ r = dhcp_server_lease_append_json(lease, &w);
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&array, w);
+ if (r < 0)
+ return r;
+ }
+
+ return json_variant_set_field_non_null(v, "StaticLeases", array);
+}
+
+int dhcp_server_save_leases(sd_dhcp_server *server) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ _cleanup_free_ char *temp_path = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ sd_id128_t boot_id;
+ int r;
+
+ assert(server);
+
+ if (!server->lease_file)
+ return 0;
+
+ if (hashmap_isempty(server->bound_leases_by_client_id)) {
+ if (unlink(server->lease_file) < 0 && errno != ENOENT)
+ return -errno;
+
+ return 0;
+ }
+
+ r = sd_id128_get_boot(&boot_id);
+ if (r < 0)
+ return r;
+
+ r = json_build(&v, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_ID128("BootID", boot_id),
+ JSON_BUILD_PAIR_IN4_ADDR("Address", &(struct in_addr) { .s_addr = server->address }),
+ JSON_BUILD_PAIR_UNSIGNED("PrefixLength",
+ in4_addr_netmask_to_prefixlen(&(struct in_addr) { .s_addr = server->netmask }))));
+ if (r < 0)
+ return r;
+
+ r = dhcp_server_bound_leases_append_json(server, &v);
+ if (r < 0)
+ return r;
+
+ r = mkdirat_parents(server->lease_dir_fd, server->lease_file, 0755);
+ if (r < 0)
+ return r;
+
+ r = fopen_temporary_at(server->lease_dir_fd, server->lease_file, &f, &temp_path);
+ if (r < 0)
+ return r;
+
+ (void) fchmod(fileno(f), 0644);
+
+ r = json_variant_dump(v, JSON_FORMAT_NEWLINE | JSON_FORMAT_FLUSH, f, /* prefix = */ NULL);
+ if (r < 0)
+ goto failure;
+
+ r = conservative_renameat(server->lease_dir_fd, temp_path, server->lease_dir_fd, server->lease_file);
+ if (r < 0)
+ goto failure;
+
+ return 0;
+
+failure:
+ (void) unlinkat(server->lease_dir_fd, temp_path, /* flags = */ 0);
+ return r;
+}
+
+static int json_dispatch_dhcp_lease(sd_dhcp_server *server, JsonVariant *v, bool use_boottime) {
+ static const JsonDispatch dispatch_table_boottime[] = {
+ { "ClientId", JSON_VARIANT_ARRAY, json_dispatch_client_id, offsetof(sd_dhcp_server_lease, client_id), JSON_MANDATORY },
+ { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(sd_dhcp_server_lease, address), JSON_MANDATORY },
+ { "Hostname", JSON_VARIANT_STRING, json_dispatch_string, offsetof(sd_dhcp_server_lease, hostname), 0 },
+ { "ExpirationUSec", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(sd_dhcp_server_lease, expiration), JSON_MANDATORY },
+ { "ExpirationRealtimeUSec", _JSON_VARIANT_TYPE_INVALID, NULL, 0, JSON_MANDATORY },
+ {}
+ }, dispatch_table_realtime[] = {
+ { "ClientId", JSON_VARIANT_ARRAY, json_dispatch_client_id, offsetof(sd_dhcp_server_lease, client_id), JSON_MANDATORY },
+ { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(sd_dhcp_server_lease, address), JSON_MANDATORY },
+ { "Hostname", JSON_VARIANT_STRING, json_dispatch_string, offsetof(sd_dhcp_server_lease, hostname), 0 },
+ { "ExpirationUSec", _JSON_VARIANT_TYPE_INVALID, NULL, 0, JSON_MANDATORY },
+ { "ExpirationRealtimeUSec", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(sd_dhcp_server_lease, expiration), JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_(sd_dhcp_server_lease_unrefp) sd_dhcp_server_lease *lease = NULL;
+ usec_t now_b;
+ int r;
+
+ assert(server);
+ assert(v);
+
+ lease = new(sd_dhcp_server_lease, 1);
+ if (!lease)
+ return -ENOMEM;
+
+ *lease = (sd_dhcp_server_lease) {
+ .n_ref = 1,
+ };
+
+ r = json_dispatch(v, use_boottime ? dispatch_table_boottime : dispatch_table_realtime, JSON_ALLOW_EXTENSIONS, lease);
+ if (r < 0)
+ return r;
+
+ r = sd_event_now(server->event, CLOCK_BOOTTIME, &now_b);
+ if (r < 0)
+ return r;
+
+ if (use_boottime) {
+ if (lease->expiration < now_b)
+ return 0; /* already expired */
+ } else {
+ usec_t now_r;
+
+ r = sd_event_now(server->event, CLOCK_REALTIME, &now_r);
+ if (r < 0)
+ return r;
+
+ if (lease->expiration < now_r)
+ return 0; /* already expired */
+
+ lease->expiration = map_clock_usec_raw(lease->expiration, now_r, now_b);
+ }
+
+ r = dhcp_server_put_lease(server, lease, /* is_static = */ false);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lease);
+ return 0;
+}
+
+typedef struct SavedInfo {
+ sd_id128_t boot_id;
+ struct in_addr address;
+ uint8_t prefixlen;
+ JsonVariant *leases;
+} SavedInfo;
+
+static void saved_info_done(SavedInfo *info) {
+ if (!info)
+ return;
+
+ json_variant_unref(info->leases);
+}
+
+static int load_leases_file(int dir_fd, const char *path, SavedInfo *ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ int r;
+
+ assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
+ assert(path);
+ assert(ret);
+
+ r = json_parse_file_at(
+ /* f = */ NULL,
+ dir_fd,
+ path,
+ /* flags = */ 0,
+ &v,
+ /* ret_line = */ NULL,
+ /* ret_column = */ NULL);
+ if (r < 0)
+ return r;
+
+ static const JsonDispatch dispatch_lease_file_table[] = {
+ { "BootID", JSON_VARIANT_STRING, json_dispatch_id128, offsetof(SavedInfo, boot_id), JSON_MANDATORY },
+ { "Address", JSON_VARIANT_ARRAY, json_dispatch_in_addr, offsetof(SavedInfo, address), JSON_MANDATORY },
+ { "PrefixLength", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint8, offsetof(SavedInfo, prefixlen), JSON_MANDATORY },
+ { "Leases", JSON_VARIANT_ARRAY, json_dispatch_variant, offsetof(SavedInfo, leases), JSON_MANDATORY },
+ {}
+ };
+
+ return json_dispatch(v, dispatch_lease_file_table, JSON_ALLOW_EXTENSIONS, ret);
+}
+
+int dhcp_server_load_leases(sd_dhcp_server *server) {
+ _cleanup_(saved_info_done) SavedInfo info = {};
+ sd_id128_t boot_id;
+ size_t n, m;
+ int r;
+
+ assert(server);
+ assert(server->event);
+
+ if (!server->lease_file)
+ return 0;
+
+ r = load_leases_file(server->lease_dir_fd, server->lease_file, &info);
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
+ return r;
+
+ r = sd_id128_get_boot(&boot_id);
+ if (r < 0)
+ return r;
+
+ n = hashmap_size(server->bound_leases_by_client_id);
+
+ JsonVariant *i;
+ JSON_VARIANT_ARRAY_FOREACH(i, info.leases)
+ RET_GATHER(r, json_dispatch_dhcp_lease(server, i, /* use_boottime = */ sd_id128_equal(info.boot_id, boot_id)));
+
+ m = hashmap_size(server->bound_leases_by_client_id);
+ assert(m >= n);
+ log_dhcp_server(server, "Loaded %zu lease(s) from %s.", m - n, server->lease_file);
+
+ return r;
+}
+
+int dhcp_server_leases_file_get_server_address(
+ int dir_fd,
+ const char *path,
+ struct in_addr *ret_address,
+ uint8_t *ret_prefixlen) {
+
+ _cleanup_(saved_info_done) SavedInfo info = {};
+ int r;
+
+ if (!ret_address && !ret_prefixlen)
+ return 0;
+
+ r = load_leases_file(dir_fd, path, &info);
+ if (r < 0)
+ return r;
+
+ if (ret_address)
+ *ret_address = info.address;
+ if (ret_prefixlen)
+ *ret_prefixlen = info.prefixlen;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index b87e4d6..c3b0f82 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -14,6 +14,7 @@
#include "dhcp-option.h"
#include "dhcp-packet.h"
#include "dhcp-server-internal.h"
+#include "dhcp-server-lease-internal.h"
#include "dns-domain.h"
#include "fd-util.h"
#include "in-addr-util.h"
@@ -21,6 +22,7 @@
#include "memory-util.h"
#include "network-common.h"
#include "ordered-set.h"
+#include "path-util.h"
#include "siphash24.h"
#include "string-util.h"
#include "unaligned.h"
@@ -29,20 +31,17 @@
#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
-DHCPLease *dhcp_lease_free(DHCPLease *lease) {
- if (!lease)
- return NULL;
+static void server_on_lease_change(sd_dhcp_server *server) {
+ int r;
- if (lease->server) {
- hashmap_remove_value(lease->server->bound_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->bound_leases_by_client_id, &lease->client_id, lease);
- hashmap_remove_value(lease->server->static_leases_by_address, UINT32_TO_PTR(lease->address), lease);
- hashmap_remove_value(lease->server->static_leases_by_client_id, &lease->client_id, lease);
- }
+ assert(server);
- free(lease->client_id.data);
- free(lease->hostname);
- return mfree(lease);
+ r = dhcp_server_save_leases(server);
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Failed to save leases, ignoring: %m");
+
+ if (server->callback)
+ server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
@@ -102,13 +101,6 @@ int sd_dhcp_server_configure_pool(
server->address = address->s_addr;
server->netmask = netmask;
server->subnet = address->s_addr & netmask;
-
- /* Drop any leases associated with the old address range */
- hashmap_clear(server->bound_leases_by_address);
- hashmap_clear(server->bound_leases_by_client_id);
-
- if (server->callback)
- server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
}
return 0;
@@ -127,38 +119,6 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) {
return in4_addr_is_set(&server->relay_target);
}
-void client_id_hash_func(const DHCPClientId *id, struct siphash *state) {
- assert(id);
- assert(id->length > 0);
- assert(id->data);
-
- siphash24_compress(&id->length, sizeof(id->length), state);
- siphash24_compress(id->data, id->length, state);
-}
-
-int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) {
- int r;
-
- assert(a->length > 0);
- assert(a->data);
- assert(b->length > 0);
- assert(b->data);
-
- r = CMP(a->length, b->length);
- if (r != 0)
- return r;
-
- return memcmp(a->data, b->data, a->length);
-}
-
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
- dhcp_lease_hash_ops,
- DHCPClientId,
- client_id_hash_func,
- client_id_compare_func,
- DHCPLease,
- dhcp_lease_free);
-
static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
assert(server);
@@ -184,6 +144,9 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
free(server->agent_circuit_id);
free(server->agent_remote_id);
+ safe_close(server->lease_dir_fd);
+ free(server->lease_file);
+
free(server->ifname);
return mfree(server);
}
@@ -212,6 +175,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
.default_lease_time = DHCP_DEFAULT_LEASE_TIME_USEC,
.max_lease_time = DHCP_MAX_LEASE_TIME_USEC,
.rapid_commit = true,
+ .lease_dir_fd = -EBADF,
};
*ret = TAKE_PTR(server);
@@ -786,16 +750,8 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
break;
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
- if (len >= 2) {
- uint8_t *data;
-
- data = memdup(option, len);
- if (!data)
- return -ENOMEM;
-
- free_and_replace(req->client_id.data, data);
- req->client_id.length = len;
- }
+ if (client_id_size_is_valid(len))
+ (void) sd_dhcp_client_id_set_raw(&req->client_id, option, len);
break;
case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
@@ -835,7 +791,6 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
if (!req)
return NULL;
- free(req->client_id.data);
free(req->hostname);
return mfree(req);
}
@@ -843,6 +798,8 @@ static DHCPRequest* dhcp_request_free(DHCPRequest *req) {
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free);
static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) {
+ int r;
+
assert(req);
assert(message);
@@ -852,39 +809,39 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes
return -EBADMSG;
/* set client id based on MAC address if client did not send an explicit one */
- if (!req->client_id.data) {
- uint8_t *data;
-
- if (message->hlen == 0)
+ if (!sd_dhcp_client_id_is_set(&req->client_id)) {
+ if (!client_id_data_size_is_valid(message->hlen))
return -EBADMSG;
- data = new0(uint8_t, message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- data[0] = 0x01;
- memcpy(data + 1, message->chaddr, message->hlen);
-
- req->client_id.length = message->hlen + 1;
- req->client_id.data = data;
+ r = sd_dhcp_client_id_set(&req->client_id, /* type = */ 1, message->chaddr, message->hlen);
+ if (r < 0)
+ return r;
}
if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) {
+ uint8_t type;
+ const void *data;
+ size_t size;
+
/* See RFC2131 section 4.1.1.
* hlen and chaddr may not be set for non-ethernet interface.
* Let's try to retrieve it from the client ID. */
- if (!req->client_id.data)
+ if (!sd_dhcp_client_id_is_set(&req->client_id))
return -EBADMSG;
- if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1)
+ r = sd_dhcp_client_id_get(&req->client_id, &type, &data, &size);
+ if (r < 0)
+ return r;
+
+ if (type != 1)
return -EBADMSG;
- if (req->client_id.data[0] != 0x01)
+ if (size > sizeof(message->chaddr))
return -EBADMSG;
- message->hlen = req->client_id.length - 1;
- memcpy(message->chaddr, req->client_id.data + 1, message->hlen);
+ memcpy(message->chaddr, data, size);
+ message->hlen = size;
}
if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE)
@@ -1020,44 +977,7 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag
return -EBADMSG;
}
-static int prepare_new_lease(DHCPLease **ret_lease, be32_t address, DHCPRequest *req, usec_t expiration) {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- assert(ret_lease);
- assert(address != 0);
- assert(req);
- assert(expiration != 0);
-
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address,
- .client_id.length = req->client_id.length,
- .htype = req->message->htype,
- .hlen = req->message->hlen,
- .gateway = req->message->giaddr,
- .expiration = expiration,
- };
- lease->client_id.data = memdup(req->client_id.data, req->client_id.length);
- if (!lease->client_id.data)
- return -ENOMEM;
-
- memcpy(lease->chaddr, req->message->chaddr, req->message->hlen);
-
- if (req->hostname) {
- lease->hostname = strdup(req->hostname);
- if (!lease->hostname)
- return -ENOMEM;
- }
-
- *ret_lease = TAKE_PTR(lease);
-
- return 0;
-}
-
-static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) {
+static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
usec_t expiration;
int r;
@@ -1069,30 +989,9 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
if (r < 0)
return r;
- if (existing_lease) {
- assert(existing_lease->server);
- assert(existing_lease->address == address);
- existing_lease->expiration = expiration;
-
- } else {
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
-
- r = prepare_new_lease(&lease, address, req, expiration);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
-
- lease->server = server; /* This must be set just before hashmap_put(). */
-
- r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
- if (r < 0)
- return log_dhcp_server_errno(server, r, "Could not save lease: %m");
-
- TAKE_PTR(lease);
- }
+ r = dhcp_server_set_lease(server, address, req, expiration);
+ if (r < 0)
+ return log_dhcp_server_errno(server, r, "Failed to create new lease: %m");
r = server_send_offer_or_ack(server, req, address, DHCP_ACK);
if (r < 0)
@@ -1100,32 +999,11 @@ static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLeas
log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid));
- if (server->callback)
- server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
+ server_on_lease_change(server);
return DHCP_ACK;
}
-static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) {
- DHCPLease *lease;
- usec_t time_now;
- int r;
-
- assert(server);
-
- r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now);
- if (r < 0)
- return r;
-
- HASHMAP_FOREACH(lease, server->bound_leases_by_client_id)
- if (lease->expiration < time_now) {
- log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address));
- dhcp_lease_free(lease);
- }
-
- return 0;
-}
-
static bool address_available(sd_dhcp_server *server, be32_t address) {
assert(server);
@@ -1137,46 +1015,12 @@ static bool address_available(sd_dhcp_server *server, be32_t address) {
return true;
}
-static int server_get_static_lease(sd_dhcp_server *server, const DHCPRequest *req, DHCPLease **ret) {
- DHCPLease *static_lease;
- _cleanup_free_ uint8_t *data = NULL;
-
- assert(server);
- assert(req);
- assert(ret);
-
- static_lease = hashmap_get(server->static_leases_by_client_id, &req->client_id);
- if (static_lease) {
- *ret = static_lease;
- return 0;
- }
-
- /* when no lease is found based on the client id fall back to chaddr */
- data = new(uint8_t, req->message->hlen + 1);
- if (!data)
- return -ENOMEM;
-
- /* set client id type to 1: Ethernet Link-Layer (RFC 2132) */
- data[0] = 0x01;
- memcpy(data + 1, req->message->chaddr, req->message->hlen);
-
- static_lease = hashmap_get(server->static_leases_by_client_id,
- &(DHCPClientId) {
- .length = req->message->hlen + 1,
- .data = data,
- });
-
- *ret = static_lease;
-
- return 0;
-}
-
#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length, const triple_timestamp *timestamp) {
_cleanup_(dhcp_request_freep) DHCPRequest *req = NULL;
_cleanup_free_ char *error_message = NULL;
- DHCPLease *existing_lease, *static_lease;
+ sd_dhcp_server_lease *existing_lease, *static_lease;
int type, r;
assert(server);
@@ -1204,9 +1048,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return r;
existing_lease = hashmap_get(server->bound_leases_by_client_id, &req->client_id);
- r = server_get_static_lease(server, req, &static_lease);
- if (r < 0)
- return r;
+ static_lease = dhcp_server_get_static_lease(server, req);
switch (type) {
@@ -1220,10 +1062,19 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 0;
/* for now pick a random free address from the pool */
- if (static_lease)
+ if (static_lease) {
+ if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(static_lease->address)))
+ /* The address is already assigned to another host. Refusing. */
+ return 0;
+
+ /* Found a matching static lease. */
address = static_lease->address;
- else if (existing_lease)
+
+ } else if (existing_lease && address_is_in_pool(server, existing_lease->address))
+
+ /* If we previously assigned an address to the host, then reuse it. */
address = existing_lease->address;
+
else {
struct siphash state;
uint64_t hash;
@@ -1252,7 +1103,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
return 0;
if (server->rapid_commit && req->rapid_commit)
- return server_ack_request(server, req, existing_lease, address);
+ return server_ack_request(server, req, address);
r = server_send_offer_or_ack(server, req, address, DHCP_OFFER);
if (r < 0)
@@ -1321,30 +1172,24 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
/* Silently ignore Rapid Commit option in REQUEST message. */
req->rapid_commit = false;
- /* disallow our own address */
- if (address == server->address)
- return 0;
-
if (static_lease) {
- /* Found a static lease for the client ID. */
-
if (static_lease->address != address)
- /* The client requested an address which is different from the static lease. Refuse. */
+ /* The client requested an address which is different from the static lease. Refusing. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
- }
-
- if (address_is_in_pool(server, address)) {
- /* The requested address is in the pool. */
-
- if (existing_lease && existing_lease->address != address)
- /* We previously assigned an address, but the client requested another one. Refuse. */
+ if (existing_lease != hashmap_get(server->bound_leases_by_address, UINT32_TO_PTR(address)))
+ /* The requested address is already assigned to another host. Refusing. */
return server_send_nak_or_ignore(server, init_reboot, req);
- return server_ack_request(server, req, existing_lease, address);
+ /* Found a static lease for the client ID. */
+ return server_ack_request(server, req, address);
}
+ if (address_is_in_pool(server, address))
+ /* The requested address is in the pool. */
+ return server_ack_request(server, req, address);
+
+ /* Refuse otherwise. */
return server_send_nak_or_ignore(server, init_reboot, req);
}
@@ -1358,10 +1203,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz
if (existing_lease->address != req->message->ciaddr)
return 0;
- dhcp_lease_free(existing_lease);
+ sd_dhcp_server_lease_unref(existing_lease);
- if (server->callback)
- server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
+ server_on_lease_change(server);
return 0;
}}
@@ -1515,6 +1359,10 @@ int sd_dhcp_server_start(sd_dhcp_server *server) {
goto on_error;
}
+ r = dhcp_server_load_leases(server);
+ if (r < 0)
+ log_dhcp_server_errno(server, r, "Failed to load lease file %s, ignoring: %m", strna(server->lease_file));
+
log_dhcp_server(server, "STARTED");
return 0;
@@ -1525,7 +1373,7 @@ on_error:
}
int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
- DHCPLease *lease;
+ sd_dhcp_server_lease *lease;
int r = 0;
assert_return(server, -EINVAL);
@@ -1740,55 +1588,32 @@ int sd_dhcp_server_set_relay_agent_information(
return 0;
}
-int sd_dhcp_server_set_static_lease(
- sd_dhcp_server *server,
- const struct in_addr *address,
- uint8_t *client_id,
- size_t client_id_size) {
-
- _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL;
+int sd_dhcp_server_set_lease_file(sd_dhcp_server *server, int dir_fd, const char *path) {
int r;
assert_return(server, -EINVAL);
- assert_return(client_id, -EINVAL);
- assert_return(client_id_size > 0, -EINVAL);
+ assert_return(!path || (dir_fd >= 0 || dir_fd == AT_FDCWD), -EBADF);
assert_return(!sd_dhcp_server_is_running(server), -EBUSY);
- /* Static lease with an empty or omitted address is a valid entry,
- * the server removes any static lease with the specified mac address. */
- if (!address || address->s_addr == 0) {
- DHCPClientId c;
-
- c = (DHCPClientId) {
- .length = client_id_size,
- .data = client_id,
- };
-
- dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c));
+ if (!path) {
+ /* When NULL, clear the previous assignment. */
+ server->lease_file = mfree(server->lease_file);
+ server->lease_dir_fd = safe_close(server->lease_dir_fd);
return 0;
}
- lease = new(DHCPLease, 1);
- if (!lease)
- return -ENOMEM;
-
- *lease = (DHCPLease) {
- .address = address->s_addr,
- .client_id.length = client_id_size,
- };
- lease->client_id.data = memdup(client_id, client_id_size);
- if (!lease->client_id.data)
- return -ENOMEM;
+ if (!path_is_safe(path))
+ return -EINVAL;
- lease->server = server; /* This must be set just before hashmap_put(). */
+ _cleanup_close_ int fd = fd_reopen(dir_fd, O_CLOEXEC | O_DIRECTORY | O_PATH);
+ if (fd < 0)
+ return fd;
- r = hashmap_ensure_put(&server->static_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease);
- if (r < 0)
- return r;
- r = hashmap_ensure_put(&server->static_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease);
+ r = free_and_strdup(&server->lease_file, path);
if (r < 0)
return r;
- TAKE_PTR(lease);
+ close_and_replace(server->lease_dir_fd, fd);
+
return 0;
}
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index c20367d..3e992d7 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -12,13 +12,12 @@
#include "alloc-util.h"
#include "device-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dns-domain.h"
#include "event-util.h"
#include "fd-util.h"
-#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
#include "iovec-util.h"
@@ -191,10 +190,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *
static int client_ensure_duid(sd_dhcp6_client *client) {
assert(client);
- if (client->duid_len != 0)
+ if (sd_dhcp_duid_is_set(&client->duid))
return 0;
- return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ return sd_dhcp6_client_set_duid_en(client);
}
/**
@@ -208,7 +207,7 @@ int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m");
@@ -221,7 +220,7 @@ int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m");
@@ -234,7 +233,7 @@ int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_en(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m");
@@ -247,7 +246,7 @@ int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
- r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set_uuid(&client->duid);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m");
@@ -261,46 +260,41 @@ int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, co
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
assert_return(duid || duid_len == 0, -EINVAL);
- r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len);
+ r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len);
if (r < 0)
return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m");
return 0;
}
-int sd_dhcp6_client_duid_as_string(
- sd_dhcp6_client *client,
- char **duid) {
- _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
- const char *v;
- int r;
+int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid) {
+ assert_return(client, -EINVAL);
+ assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
+ assert_return(sd_dhcp_duid_is_set(duid), -EINVAL);
+
+ client->duid = *duid;
+ return 0;
+}
+int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret) {
assert_return(client, -EINVAL);
- assert_return(client->duid_len > offsetof(struct duid, raw.data), -ENODATA);
- assert_return(duid, -EINVAL);
+ assert_return(ret, -EINVAL);
- v = duid_type_to_string(be16toh(client->duid.type));
- if (v) {
- s = strdup(v);
- if (!s)
- return -ENOMEM;
- } else {
- r = asprintf(&s, "%0x", client->duid.type);
- if (r < 0)
- return -ENOMEM;
- }
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
- t = hexmem(client->duid.raw.data, client->duid_len - offsetof(struct duid, raw.data));
- if (!t)
- return -ENOMEM;
+ *ret = &client->duid;
+ return 0;
+}
- p = strjoin(s, ":", t);
- if (!p)
- return -ENOMEM;
+int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) {
+ assert_return(client, -EINVAL);
+ assert_return(ret, -EINVAL);
- *duid = TAKE_PTR(p);
+ if (!sd_dhcp_duid_is_set(&client->duid))
+ return -ENODATA;
- return 0;
+ return sd_dhcp_duid_to_string(&client->duid, ret);
}
int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
@@ -517,7 +511,6 @@ int sd_dhcp6_client_set_rapid_commit(sd_dhcp6_client *client, int enable) {
int sd_dhcp6_client_set_send_release(sd_dhcp6_client *client, int enable) {
assert_return(client, -EINVAL);
- assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
client->send_release = enable;
return 0;
@@ -750,8 +743,6 @@ static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *
int dhcp6_client_send_message(sd_dhcp6_client *client) {
_cleanup_free_ uint8_t *buf = NULL;
- struct in6_addr all_servers =
- IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
struct sd_dhcp6_option *j;
usec_t elapsed_usec, time_now;
be16_t elapsed_time;
@@ -825,9 +816,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
- assert(client->duid_len > 0);
+ assert(sd_dhcp_duid_is_set(&client->duid));
r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID,
- client->duid_len, &client->duid);
+ client->duid.size, &client->duid.duid);
if (r < 0)
return r;
@@ -846,7 +837,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
if (r < 0)
return r;
- r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
+ r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
if (r < 0)
return r;
@@ -1337,7 +1328,7 @@ static int client_receive_message(
return 0;
}
if ((size_t) len < sizeof(DHCP6Message)) {
- log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
+ log_dhcp6_client(client, "Too small to be DHCPv6 message: ignoring");
return 0;
}
@@ -1413,7 +1404,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
r = client_send_release(client);
if (r < 0)
log_dhcp6_client_errno(client, r,
- "Failed to send DHCP6 release message, ignoring: %m");
+ "Failed to send DHCPv6 release message, ignoring: %m");
client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
@@ -1424,7 +1415,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
}
int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
- assert_return(client, -EINVAL);
+ if (!client)
+ return false;
return client->state != DHCP6_STATE_STOPPED;
}
diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c
index 674248b..e5d6547 100644
--- a/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libsystemd-network/sd-dhcp6-lease.c
@@ -10,6 +10,7 @@
#include "dhcp6-lease-internal.h"
#include "network-common.h"
#include "strv.h"
+#include "unaligned.h"
#define IRT_DEFAULT (1 * USEC_PER_DAY)
#define IRT_MINIMUM (600 * USEC_PER_SEC)
@@ -866,7 +867,7 @@ static int dhcp6_lease_parse_message(
"%s message does not contain client ID. Ignoring.",
dhcp6_message_type_to_string(message->type));
- if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0)
+ if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0)
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
"The client ID in %s message does not match. Ignoring.",
dhcp6_message_type_to_string(message->type));
diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c
index 0cc37a6..51d2b22 100644
--- a/src/libsystemd-network/sd-ipv4acd.c
+++ b/src/libsystemd-network/sd-ipv4acd.c
@@ -564,7 +564,8 @@ int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
}
int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
- assert_return(acd, false);
+ if (!acd)
+ return false;
return acd->state != IPV4ACD_STATE_INIT;
}
@@ -585,6 +586,12 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
+ r = sd_event_get_state(acd->event);
+ if (r < 0)
+ return r;
+ if (r == SD_EVENT_FINISHED)
+ return -ESTALE;
+
r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
if (r < 0)
return r;
diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c
index a29279e..5bf9833 100644
--- a/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/libsystemd-network/sd-ipv4ll.c
@@ -206,7 +206,8 @@ int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
}
int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
- assert_return(ll, false);
+ if (!ll)
+ return false;
return sd_ipv4acd_is_running(ll->acd);
}
diff --git a/src/libsystemd-network/sd-lldp-rx.c b/src/libsystemd-network/sd-lldp-rx.c
index 2fc9a55..74000ff 100644
--- a/src/libsystemd-network/sd-lldp-rx.c
+++ b/src/libsystemd-network/sd-lldp-rx.c
@@ -10,6 +10,7 @@
#include "ether-addr-util.h"
#include "event-util.h"
#include "fd-util.h"
+#include "json.h"
#include "lldp-neighbor.h"
#include "lldp-network.h"
#include "lldp-rx-internal.h"
@@ -490,6 +491,30 @@ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
return k;
}
+int lldp_rx_build_neighbors_json(sd_lldp_rx *lldp_rx, JsonVariant **ret) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+ int r;
+
+ assert(lldp_rx);
+ assert(ret);
+
+ sd_lldp_neighbor *n;
+ HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ r = lldp_neighbor_build_json(n, &w);
+ if (r < 0)
+ return r;
+
+ r = json_variant_append_array(&v, w);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(v);
+ return 0;
+}
+
int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
assert_return(lldp_rx, -EINVAL);
assert_return(m > 0, -EINVAL);
diff --git a/src/libsystemd-network/sd-ndisc-neighbor.c b/src/libsystemd-network/sd-ndisc-neighbor.c
new file mode 100644
index 0000000..1bb6ebf
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-neighbor.c
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-option.h"
+
+static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
+ if (!na)
+ return NULL;
+
+ icmp6_packet_unref(na->packet);
+ set_free(na->options);
+ return mfree(na);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
+
+sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
+ sd_ndisc_neighbor *na;
+
+ assert(packet);
+
+ na = new(sd_ndisc_neighbor, 1);
+ if (!na)
+ return NULL;
+
+ *na = (sd_ndisc_neighbor) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return na;
+}
+
+int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
+ int r;
+
+ assert(na);
+
+ if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a neighbor advertisement, ignoring datagram.");
+
+ /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
+ const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
+ assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
+ assert(a->nd_na_code == 0);
+
+ na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
+ na->target_address = a->nd_na_target;
+
+ /* RFC 4861 section 4.4:
+ * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
+ * prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
+ * address has changed. The Target Address MUST NOT be a multicast address.
+ *
+ * Here, we only check if the target address is a link-layer address (or a null address, for safety)
+ * when the message is an unsolicited neighbor advertisement. */
+ if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
+ if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received ND packet with an invalid target address (%s), ignoring datagram.",
+ IN6_ADDR_TO_STRING(&na->target_address));
+
+ r = ndisc_parse_options(na->packet, &na->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
+
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return icmp6_packet_get_sender_address(na->packet, ret);
+}
+
+int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ if (in6_addr_is_null(&na->target_address))
+ /* fall back to the sender address, for safety. */
+ return sd_ndisc_neighbor_get_sender_address(na, ret);
+
+ if (ret)
+ *ret = na->target_address;
+ return 0;
+}
+
+int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
+ assert_return(na, -EINVAL);
+
+ return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
+ assert_return(na, -EINVAL);
+
+ if (ret)
+ *ret = na->flags;
+ return 0;
+}
+
+int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
+}
+
+int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
+}
+
+int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
+ assert_return(na, -EINVAL);
+
+ return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
+}
diff --git a/src/libsystemd-network/sd-ndisc-redirect.c b/src/libsystemd-network/sd-ndisc-redirect.c
new file mode 100644
index 0000000..a1fceb2
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-redirect.c
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-option.h"
+#include "ndisc-redirect-internal.h"
+
+static sd_ndisc_redirect* ndisc_redirect_free(sd_ndisc_redirect *rd) {
+ if (!rd)
+ return NULL;
+
+ icmp6_packet_unref(rd->packet);
+ set_free(rd->options);
+ return mfree(rd);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_redirect, sd_ndisc_redirect, ndisc_redirect_free);
+
+sd_ndisc_redirect* ndisc_redirect_new(ICMP6Packet *packet) {
+ sd_ndisc_redirect *rd;
+
+ assert(packet);
+
+ rd = new(sd_ndisc_redirect, 1);
+ if (!rd)
+ return NULL;
+
+ *rd = (sd_ndisc_redirect) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return rd;
+}
+
+int ndisc_redirect_parse(sd_ndisc *nd, sd_ndisc_redirect *rd) {
+ int r;
+
+ assert(rd);
+ assert(rd->packet);
+
+ if (rd->packet->raw_size < sizeof(struct nd_redirect))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a redirect message, ignoring.");
+
+ const struct nd_redirect *a = (const struct nd_redirect*) rd->packet->raw_packet;
+ assert(a->nd_rd_type == ND_REDIRECT);
+ assert(a->nd_rd_code == 0);
+
+ rd->target_address = a->nd_rd_target;
+ rd->destination_address = a->nd_rd_dst;
+
+ /* RFC 4861 section 8.1
+ * The ICMP Destination Address field in the redirect message does not contain a multicast address. */
+ if (in6_addr_is_null(&rd->destination_address) || in6_addr_is_multicast(&rd->destination_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received Redirect message with an invalid destination address, ignoring datagram: %m");
+
+ /* RFC 4861 section 8.1
+ * The ICMP Target Address is either a link-local address (when redirected to a router) or the same
+ * as the ICMP Destination Address (when redirected to the on-link destination). */
+ if (!in6_addr_is_link_local(&rd->target_address) && !in6_addr_equal(&rd->target_address, &rd->destination_address))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Received Redirect message with an invalid target address, ignoring datagram: %m");
+
+ r = ndisc_parse_options(rd->packet, &rd->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in Redirect message, ignoring datagram: %m");
+
+ return 0;
+}
+
+int sd_ndisc_redirect_set_sender_address(sd_ndisc_redirect *rd, const struct in6_addr *addr) {
+ assert_return(rd, -EINVAL);
+
+ return icmp6_packet_set_sender_address(rd->packet, addr);
+}
+
+int sd_ndisc_redirect_get_sender_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ return icmp6_packet_get_sender_address(rd->packet, ret);
+}
+
+int sd_ndisc_redirect_get_target_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ if (in6_addr_is_null(&rd->target_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = rd->target_address;
+ return 0;
+}
+
+int sd_ndisc_redirect_get_destination_address(sd_ndisc_redirect *rd, struct in6_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ if (in6_addr_is_null(&rd->destination_address))
+ return -ENODATA;
+
+ if (ret)
+ *ret = rd->destination_address;
+ return 0;
+}
+
+int sd_ndisc_redirect_get_target_mac(sd_ndisc_redirect *rd, struct ether_addr *ret) {
+ assert_return(rd, -EINVAL);
+
+ return ndisc_option_get_mac(rd->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_redirect_get_redirected_header(sd_ndisc_redirect *rd, struct ip6_hdr *ret) {
+ assert_return(rd, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rd->options, SD_NDISC_OPTION_REDIRECTED_HEADER);
+ if (!p)
+ return -ENODATA;
+
+ if (ret)
+ *ret = p->hdr;
+ return 0;
+}
diff --git a/src/libsystemd-network/sd-ndisc-router-solicit.c b/src/libsystemd-network/sd-ndisc-router-solicit.c
new file mode 100644
index 0000000..04e7c26
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-router-solicit.c
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/icmp6.h>
+
+#include "sd-radv.h"
+
+#include "alloc-util.h"
+#include "in-addr-util.h"
+#include "ndisc-option.h"
+#include "ndisc-router-solicit-internal.h"
+#include "radv-internal.h"
+
+static sd_ndisc_router_solicit* ndisc_router_solicit_free(sd_ndisc_router_solicit *rs) {
+ if (!rs)
+ return NULL;
+
+ icmp6_packet_unref(rs->packet);
+ set_free(rs->options);
+ return mfree(rs);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router_solicit, sd_ndisc_router_solicit, ndisc_router_solicit_free);
+
+sd_ndisc_router_solicit* ndisc_router_solicit_new(ICMP6Packet *packet) {
+ sd_ndisc_router_solicit *rs;
+
+ assert(packet);
+
+ rs = new(sd_ndisc_router_solicit, 1);
+ if (!rs)
+ return NULL;
+
+ *rs = (sd_ndisc_router_solicit) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ };
+
+ return rs;
+}
+
+int ndisc_router_solicit_parse(sd_radv *ra, sd_ndisc_router_solicit *rs) {
+ int r;
+
+ assert(rs);
+ assert(rs->packet);
+
+ if (rs->packet->raw_size < sizeof(struct nd_router_solicit))
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a router solicit, ignoring.");
+
+ const struct nd_router_solicit *a = (const struct nd_router_solicit*) rs->packet->raw_packet;
+ assert(a);
+ assert(a->nd_rs_type == ND_ROUTER_SOLICIT);
+ assert(a->nd_rs_code == 0);
+
+ r = ndisc_parse_options(rs->packet, &rs->options);
+ if (r < 0)
+ return log_radv_errno(ra, r, "Failed to parse NDisc options in router solicit, ignoring datagram: %m");
+
+ /* RFC 4861 section 4.1.
+ * Source link-layer address:
+ * The link-layer address of the sender, if known. MUST NOT be included if the Source
+ * Address is the unspecified address. Otherwise, it SHOULD be included on link
+ * layers that have addresses. */
+ if (ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, NULL) >= 0&&
+ sd_ndisc_router_solicit_get_sender_address(rs, NULL) == -ENODATA)
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG),
+ "Router Solicitation message from null address unexpectedly contains source link-layer address option, ignoring datagaram.");
+
+ return 0;
+}
+
+int sd_ndisc_router_solicit_get_sender_address(sd_ndisc_router_solicit *rs, struct in6_addr *ret) {
+ assert_return(rs, -EINVAL);
+
+ return icmp6_packet_get_sender_address(rs->packet, ret);
+}
+
+int sd_ndisc_router_solicit_get_sender_mac(sd_ndisc_router_solicit *rs, struct ether_addr *ret) {
+ assert_return(rs, -EINVAL);
+
+ return ndisc_option_get_mac(rs->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret);
+}
diff --git a/src/libsystemd-network/sd-ndisc-router.c b/src/libsystemd-network/sd-ndisc-router.c
new file mode 100644
index 0000000..9ca737d
--- /dev/null
+++ b/src/libsystemd-network/sd-ndisc-router.c
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/***
+ Copyright © 2014 Intel Corporation. All rights reserved.
+***/
+
+#include <netinet/icmp6.h>
+
+#include "sd-ndisc.h"
+
+#include "alloc-util.h"
+#include "ndisc-internal.h"
+#include "ndisc-router-internal.h"
+#include "string-table.h"
+
+static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
+ if (!rt)
+ return NULL;
+
+ icmp6_packet_unref(rt->packet);
+ set_free(rt->options);
+ return mfree(rt);
+}
+
+DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
+
+sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
+ sd_ndisc_router *rt;
+
+ assert(packet);
+
+ rt = new(sd_ndisc_router, 1);
+ if (!rt)
+ return NULL;
+
+ *rt = (sd_ndisc_router) {
+ .n_ref = 1,
+ .packet = icmp6_packet_ref(packet),
+ .iterator = ITERATOR_FIRST,
+ };
+
+ return rt;
+}
+
+int sd_ndisc_router_set_sender_address(sd_ndisc_router *rt, const struct in6_addr *addr) {
+ assert_return(rt, -EINVAL);
+
+ return icmp6_packet_set_sender_address(rt->packet, addr);
+}
+
+int sd_ndisc_router_get_sender_address(sd_ndisc_router *rt, struct in6_addr *ret) {
+ assert_return(rt, -EINVAL);
+
+ return icmp6_packet_get_sender_address(rt->packet, ret);
+}
+
+int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ return icmp6_packet_get_timestamp(rt->packet, clock, ret);
+}
+
+#define DEFINE_GET_TIMESTAMP(name) \
+ int sd_ndisc_router_##name##_timestamp( \
+ sd_ndisc_router *rt, \
+ clockid_t clock, \
+ uint64_t *ret) { \
+ \
+ usec_t s, t; \
+ int r; \
+ \
+ assert_return(rt, -EINVAL); \
+ assert_return(ret, -EINVAL); \
+ \
+ r = sd_ndisc_router_##name(rt, &s); \
+ if (r < 0) \
+ return r; \
+ \
+ r = sd_ndisc_router_get_timestamp(rt, clock, &t); \
+ if (r < 0) \
+ return r; \
+ \
+ *ret = time_span_to_stamp(s, t); \
+ return 0; \
+ }
+
+DEFINE_GET_TIMESTAMP(get_lifetime);
+DEFINE_GET_TIMESTAMP(prefix_get_valid_lifetime);
+DEFINE_GET_TIMESTAMP(prefix_get_preferred_lifetime);
+DEFINE_GET_TIMESTAMP(route_get_lifetime);
+DEFINE_GET_TIMESTAMP(rdnss_get_lifetime);
+DEFINE_GET_TIMESTAMP(dnssl_get_lifetime);
+DEFINE_GET_TIMESTAMP(prefix64_get_lifetime);
+
+int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
+ const struct nd_router_advert *a;
+ int r;
+
+ assert(rt);
+ assert(rt->packet);
+
+ if (rt->packet->raw_size < sizeof(struct nd_router_advert))
+ return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "Too small to be a router advertisement, ignoring.");
+
+ a = (const struct nd_router_advert*) rt->packet->raw_packet;
+ assert(a->nd_ra_type == ND_ROUTER_ADVERT);
+ assert(a->nd_ra_code == 0);
+
+ rt->hop_limit = a->nd_ra_curhoplimit;
+ rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
+ rt->lifetime_usec = be16_sec_to_usec(a->nd_ra_router_lifetime, /* max_as_infinity = */ false);
+ rt->reachable_time_usec = be32_msec_to_usec(a->nd_ra_reachable, /* mas_as_infinity = */ false);
+ rt->retransmission_time_usec = be32_msec_to_usec(a->nd_ra_retransmit, /* max_as_infinity = */ false);
+
+ /* RFC 4191 section 2.2
+ * Prf (Default Router Preference)
+ * 2-bit signed integer. Indicates whether to prefer this router over other default routers. If the
+ * Router Lifetime is zero, the preference value MUST be set to (00) by the sender and MUST be
+ * ignored by the receiver. If the Reserved (10) value is received, the receiver MUST treat the value
+ * as if it were (00). */
+ rt->preference = (rt->flags >> 3) & 3;
+ if (rt->preference == SD_NDISC_PREFERENCE_RESERVED)
+ rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
+
+ r = ndisc_parse_options(rt->packet, &rt->options);
+ if (r < 0)
+ return log_ndisc_errno(nd, r, "Failed to parse NDisc options in router advertisement message, ignoring: %m");
+
+ return 0;
+}
+
+int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->hop_limit;
+ return 0;
+}
+
+int sd_ndisc_router_get_reachable_time(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->reachable_time_usec;
+ return 0;
+}
+
+int sd_ndisc_router_get_retransmission_time(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->retransmission_time_usec;
+ return 0;
+}
+
+int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_FLAGS_EXTENSION);
+
+ *ret = rt->flags | (p ? p->extended_flags : 0);
+ return 0;
+}
+
+int ndisc_router_flags_to_string(uint64_t flags, char **ret) {
+ _cleanup_free_ char *s = NULL;
+
+ assert(ret);
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_MANAGED) &&
+ !strextend_with_separator(&s, ", ", "managed"))
+ return -ENOMEM;
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_OTHER) &&
+ !strextend_with_separator(&s, ", ", "other"))
+ return -ENOMEM;
+
+ if (FLAGS_SET(flags, ND_RA_FLAG_HOME_AGENT) &&
+ !strextend_with_separator(&s, ", ", "home-agent"))
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(s);
+ return 0;
+}
+
+int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint64_t *ret) {
+ assert_return(rt, -EINVAL);
+
+ if (ret)
+ *ret = rt->lifetime_usec;
+
+ return rt->lifetime_usec > 0; /* Indicate if the router is still valid or not. */
+}
+
+int sd_ndisc_router_get_preference(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ *ret = rt->preference;
+ return 0;
+}
+
+static const char* const ndisc_router_preference_table[] = {
+ [SD_NDISC_PREFERENCE_LOW] = "low",
+ [SD_NDISC_PREFERENCE_MEDIUM] = "medium",
+ [SD_NDISC_PREFERENCE_HIGH] = "high",
+ [SD_NDISC_PREFERENCE_RESERVED] = "reserved",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(ndisc_router_preference, int);
+
+int sd_ndisc_router_get_sender_mac(sd_ndisc_router *rt, struct ether_addr *ret) {
+ assert_return(rt, -EINVAL);
+
+ return ndisc_option_get_mac(rt->options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, ret);
+}
+
+int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_MTU);
+ if (!p)
+ return -ENODATA;
+
+ *ret = p->mtu;
+ return 0;
+}
+
+int sd_ndisc_router_get_captive_portal(sd_ndisc_router *rt, const char **ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ sd_ndisc_option *p = ndisc_option_get_by_type(rt->options, SD_NDISC_OPTION_CAPTIVE_PORTAL);
+ if (!p)
+ return -ENODATA;
+
+ *ret = p->captive_portal;
+ return 0;
+}
+
+int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
+ assert_return(rt, -EINVAL);
+
+ rt->iterator = ITERATOR_FIRST;
+ return sd_ndisc_router_option_next(rt);
+}
+
+int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
+ assert_return(rt, -EINVAL);
+
+ return set_iterate(rt->options, &rt->iterator, (void**) &rt->current_option);
+}
+
+int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ if (!rt->current_option)
+ return -ENODATA;
+
+ *ret = rt->current_option->type;
+ return 0;
+}
+
+int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
+ uint8_t t;
+ int r;
+
+ assert_return(rt, -EINVAL);
+
+ r = sd_ndisc_router_option_get_type(rt, &t);
+ if (r < 0)
+ return r;
+
+ return t == type;
+}
+
+int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const uint8_t **ret, size_t *ret_size) {
+ assert_return(rt, -EINVAL);
+
+ if (!rt->current_option)
+ return -ENODATA;
+
+ return ndisc_option_parse(rt->packet, rt->current_option->offset, NULL, ret_size, ret);
+}
+
+#define DEFINE_GETTER(name, type, element, element_type) \
+ int sd_ndisc_router_##name##_get_##element( \
+ sd_ndisc_router *rt, \
+ element_type *ret) { \
+ \
+ int r; \
+ \
+ assert_return(rt, -EINVAL); \
+ assert_return(ret, -EINVAL); \
+ \
+ r = sd_ndisc_router_option_is_type(rt, type); \
+ if (r < 0) \
+ return r; \
+ if (r == 0) \
+ return -EMEDIUMTYPE; \
+ \
+ *ret = rt->current_option->name.element; \
+ return 0; \
+ }
+
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, flags, uint8_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, prefixlen, uint8_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, address, struct in6_addr);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, valid_lifetime, uint64_t);
+DEFINE_GETTER(prefix, SD_NDISC_OPTION_PREFIX_INFORMATION, preferred_lifetime, uint64_t);
+
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, preference, uint8_t);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, prefixlen, uint8_t);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, address, struct in6_addr);
+DEFINE_GETTER(route, SD_NDISC_OPTION_ROUTE_INFORMATION, lifetime, uint64_t);
+
+DEFINE_GETTER(rdnss, SD_NDISC_OPTION_RDNSS, lifetime, uint64_t);
+
+int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ *ret = rt->current_option->rdnss.addresses;
+ return (int) rt->current_option->rdnss.n_addresses;
+}
+
+DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, domains, char**);
+DEFINE_GETTER(dnssl, SD_NDISC_OPTION_DNSSL, lifetime, uint64_t);
+
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefixlen, uint8_t);
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, prefix, struct in6_addr);
+DEFINE_GETTER(prefix64, SD_NDISC_OPTION_PREF64, lifetime, uint64_t);
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index 1beed5d..ca15f94 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -9,13 +9,16 @@
#include "sd-ndisc.h"
#include "alloc-util.h"
+#include "ether-addr-util.h"
#include "event-util.h"
#include "fd-util.h"
#include "icmp6-util.h"
#include "in-addr-util.h"
#include "memory-util.h"
#include "ndisc-internal.h"
-#include "ndisc-router.h"
+#include "ndisc-neighbor-internal.h"
+#include "ndisc-redirect-internal.h"
+#include "ndisc-router-internal.h"
#include "network-common.h"
#include "random-util.h"
#include "socket-util.h"
@@ -25,13 +28,15 @@
#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
- [SD_NDISC_EVENT_TIMEOUT] = "timeout",
- [SD_NDISC_EVENT_ROUTER] = "router",
+ [SD_NDISC_EVENT_TIMEOUT] = "timeout",
+ [SD_NDISC_EVENT_ROUTER] = "router",
+ [SD_NDISC_EVENT_NEIGHBOR] = "neighbor",
+ [SD_NDISC_EVENT_REDIRECT] = "redirect",
};
DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event_t);
-static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event_t event, sd_ndisc_router *rt) {
+static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event_t event, void *message) {
assert(ndisc);
assert(event >= 0 && event < _SD_NDISC_EVENT_MAX);
@@ -39,7 +44,14 @@ static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event_t event, sd_ndisc_rou
return (void) log_ndisc(ndisc, "Received '%s' event.", ndisc_event_to_string(event));
log_ndisc(ndisc, "Invoking callback for '%s' event.", ndisc_event_to_string(event));
- ndisc->callback(ndisc, event, rt, ndisc->userdata);
+ ndisc->callback(ndisc, event, message, ndisc->userdata);
+}
+
+int sd_ndisc_is_running(sd_ndisc *nd) {
+ if (!nd)
+ return false;
+
+ return sd_event_source_get_enabled(nd->recv_event_source, NULL) > 0;
}
int sd_ndisc_set_callback(
@@ -58,7 +70,7 @@ int sd_ndisc_set_callback(
int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
assert_return(nd, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
- assert_return(nd->fd < 0, -EBUSY);
+ assert_return(!sd_ndisc_is_running(nd), -EBUSY);
nd->ifindex = ifindex;
return 0;
@@ -89,6 +101,18 @@ int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret) {
return 0;
}
+int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr) {
+ assert_return(nd, -EINVAL);
+ assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
+
+ if (addr)
+ nd->link_local_addr = *addr;
+ else
+ zero(nd->link_local_addr);
+
+ return 0;
+}
+
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
assert_return(nd, -EINVAL);
@@ -104,7 +128,7 @@ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
int r;
assert_return(nd, -EINVAL);
- assert_return(nd->fd < 0, -EBUSY);
+ assert_return(!sd_ndisc_is_running(nd), -EBUSY);
assert_return(!nd->event, -EBUSY);
if (event)
@@ -123,7 +147,7 @@ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
int sd_ndisc_detach_event(sd_ndisc *nd) {
assert_return(nd, -EINVAL);
- assert_return(nd->fd < 0, -EBUSY);
+ assert_return(!sd_ndisc_is_running(nd), -EBUSY);
nd->event = sd_event_unref(nd->event);
return 0;
@@ -179,78 +203,207 @@ int sd_ndisc_new(sd_ndisc **ret) {
return 0;
}
-static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
+static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
+ _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
int r;
assert(nd);
- assert(rt);
+ assert(packet);
+
+ rt = ndisc_router_new(packet);
+ if (!rt)
+ return -ENOMEM;
r = ndisc_router_parse(nd, rt);
if (r < 0)
return r;
- log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %s",
- rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none",
- rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium",
- FORMAT_TIMESPAN(rt->lifetime_usec, USEC_PER_SEC));
+ (void) event_source_disable(nd->timeout_event_source);
+ (void) event_source_disable(nd->timeout_no_ra);
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *s = NULL;
+ struct in6_addr a;
+ uint64_t flags;
+ uint8_t pref;
+ usec_t lifetime;
+
+ r = sd_ndisc_router_get_sender_address(rt, &a);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_get_flags(rt, &flags);
+ if (r < 0)
+ return r;
+
+ r = ndisc_router_flags_to_string(flags, &s);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_get_preference(rt, &pref);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_get_lifetime(rt, &lifetime);
+ if (r < 0)
+ return r;
+
+ log_ndisc(nd, "Received Router Advertisement from %s: flags=0x%0*"PRIx64"(%s), preference=%s, lifetime=%s",
+ IN6_ADDR_TO_STRING(&a),
+ flags & UINT64_C(0x00ffffffffffff00) ? 14 : 2, flags, /* suppress too many zeros if no extension */
+ s ?: "none",
+ ndisc_router_preference_to_string(pref),
+ FORMAT_TIMESPAN(lifetime, USEC_PER_SEC));
+ }
ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt);
return 0;
}
+static int ndisc_handle_neighbor(sd_ndisc *nd, ICMP6Packet *packet) {
+ _cleanup_(sd_ndisc_neighbor_unrefp) sd_ndisc_neighbor *na = NULL;
+ int r;
+
+ assert(nd);
+ assert(packet);
+
+ na = ndisc_neighbor_new(packet);
+ if (!na)
+ return -ENOMEM;
+
+ r = ndisc_neighbor_parse(nd, na);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING) {
+ struct in6_addr a;
+
+ r = sd_ndisc_neighbor_get_sender_address(na, &a);
+ if (r < 0)
+ return r;
+
+ log_ndisc(nd, "Received Neighbor Advertisement from %s: Router=%s, Solicited=%s, Override=%s",
+ IN6_ADDR_TO_STRING(&a),
+ yes_no(sd_ndisc_neighbor_is_router(na) > 0),
+ yes_no(sd_ndisc_neighbor_is_solicited(na) > 0),
+ yes_no(sd_ndisc_neighbor_is_override(na) > 0));
+ }
+
+ ndisc_callback(nd, SD_NDISC_EVENT_NEIGHBOR, na);
+ return 0;
+}
+
+static int ndisc_handle_redirect(sd_ndisc *nd, ICMP6Packet *packet) {
+ _cleanup_(sd_ndisc_redirect_unrefp) sd_ndisc_redirect *rd = NULL;
+ int r;
+
+ assert(nd);
+ assert(packet);
+
+ rd = ndisc_redirect_new(packet);
+ if (!rd)
+ return -ENOMEM;
+
+ r = ndisc_redirect_parse(nd, rd);
+ if (r < 0)
+ return r;
+
+ if (DEBUG_LOGGING) {
+ struct in6_addr sender, target, dest;
+
+ r = sd_ndisc_redirect_get_sender_address(rd, &sender);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_target_address(rd, &target);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_redirect_get_destination_address(rd, &dest);
+ if (r < 0)
+ return r;
+
+ log_ndisc(nd, "Received Redirect message from %s: Target=%s, Destination=%s",
+ IN6_ADDR_TO_STRING(&sender),
+ IN6_ADDR_TO_STRING(&target),
+ IN6_ADDR_TO_STRING(&dest));
+ }
+
+ ndisc_callback(nd, SD_NDISC_EVENT_REDIRECT, rd);
+ return 0;
+}
+
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
sd_ndisc *nd = ASSERT_PTR(userdata);
- ssize_t buflen;
int r;
assert(s);
assert(nd->event);
- buflen = next_datagram_size_fd(fd);
- if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
+ r = icmp6_packet_receive(fd, &packet);
+ if (r < 0) {
+ log_ndisc_errno(nd, r, "Failed to receive ICMPv6 packet, ignoring: %m");
return 0;
- if (buflen < 0) {
- log_ndisc_errno(nd, buflen, "Failed to determine datagram size to read, ignoring: %m");
+ }
+
+ /* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states
+ * that hosts MUST discard messages with the null source address. */
+ if (in6_addr_is_null(&packet->sender_address)) {
+ log_ndisc(nd, "Received an ICMPv6 packet from null address, ignoring.");
return 0;
}
- rt = ndisc_router_new(buflen);
- if (!rt)
- return -ENOMEM;
+ if (in6_addr_equal(&packet->sender_address, &nd->link_local_addr)) {
+ log_ndisc(nd, "Received an ICMPv6 packet sent by the same interface, ignoring.");
+ return 0;
+ }
- r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address, &rt->timestamp);
- if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
+ r = icmp6_packet_get_type(packet);
+ if (r < 0) {
+ log_ndisc_errno(nd, r, "Received an invalid ICMPv6 packet, ignoring: %m");
return 0;
- if (r < 0)
- switch (r) {
- case -EADDRNOTAVAIL:
- log_ndisc(nd, "Received RA from neither link-local nor null address. Ignoring.");
- return 0;
+ }
- case -EMULTIHOP:
- log_ndisc(nd, "Received RA with invalid hop limit. Ignoring.");
- return 0;
+ switch (r) {
+ case ND_ROUTER_ADVERT:
+ (void) ndisc_handle_router(nd, packet);
+ break;
- case -EPFNOSUPPORT:
- log_ndisc(nd, "Received invalid source address from ICMPv6 socket. Ignoring.");
- return 0;
+ case ND_NEIGHBOR_ADVERT:
+ (void) ndisc_handle_neighbor(nd, packet);
+ break;
- default:
- log_ndisc_errno(nd, r, "Unexpected error while reading from ICMPv6, ignoring: %m");
- return 0;
- }
+ case ND_REDIRECT:
+ (void) ndisc_handle_redirect(nd, packet);
+ break;
- /* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states
- * that hosts MUST discard messages with the null source address. */
- if (in6_addr_is_null(&rt->address))
- log_ndisc(nd, "Received RA from null address. Ignoring.");
+ default:
+ log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
+ }
- (void) event_source_disable(nd->timeout_event_source);
- (void) ndisc_handle_datagram(nd, rt);
return 0;
}
+static int ndisc_send_router_solicitation(sd_ndisc *nd) {
+ static const struct nd_router_solicit header = {
+ .nd_rs_type = ND_ROUTER_SOLICIT,
+ };
+
+ _cleanup_set_free_ Set *options = NULL;
+ int r;
+
+ assert(nd);
+
+ if (!ether_addr_is_null(&nd->mac_addr)) {
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &nd->mac_addr);
+ if (r < 0)
+ return r;
+ }
+
+ return ndisc_send(nd->fd, &IN6_ADDR_ALL_ROUTERS_MULTICAST, &header.nd_rs_hdr, options, USEC_INFINITY);
+}
+
static usec_t ndisc_timeout_compute_random(usec_t val) {
/* compute a time that is random within ±10% of the given value */
return val - val / 10 +
@@ -284,7 +437,7 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
if (r < 0)
goto fail;
- r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
+ r = ndisc_send_router_solicitation(nd);
if (r < 0)
log_ndisc_errno(nd, r, "Failed to send Router Solicitation, next solicitation in %s, ignoring: %m",
FORMAT_TIMESPAN(nd->retransmit_time, USEC_PER_SEC));
@@ -316,7 +469,7 @@ int sd_ndisc_stop(sd_ndisc *nd) {
if (!nd)
return 0;
- if (nd->fd < 0)
+ if (!sd_ndisc_is_running(nd))
return 0;
log_ndisc(nd, "Stopping IPv6 Router Solicitation client");
@@ -325,50 +478,74 @@ int sd_ndisc_stop(sd_ndisc *nd) {
return 1;
}
-int sd_ndisc_start(sd_ndisc *nd) {
+static int ndisc_setup_recv_event(sd_ndisc *nd) {
int r;
- usec_t time_now;
- assert_return(nd, -EINVAL);
- assert_return(nd->event, -EINVAL);
- assert_return(nd->ifindex > 0, -EINVAL);
+ assert(nd);
+ assert(nd->event);
+ assert(nd->ifindex > 0);
- if (nd->fd >= 0)
- return 0;
+ _cleanup_close_ int fd = -EBADF;
+ fd = icmp6_bind(nd->ifindex, /* is_router = */ false);
+ if (fd < 0)
+ return fd;
- assert(!nd->recv_event_source);
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ r = sd_event_add_io(nd->event, &s, fd, EPOLLIN, ndisc_recv, nd);
+ if (r < 0)
+ return r;
- r = sd_event_now(nd->event, CLOCK_BOOTTIME, &time_now);
+ r = sd_event_source_set_priority(s, nd->event_priority);
if (r < 0)
- goto fail;
+ return r;
+
+ (void) sd_event_source_set_description(s, "ndisc-receive-router-message");
- nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
- if (nd->fd < 0)
- return nd->fd;
+ nd->fd = TAKE_FD(fd);
+ nd->recv_event_source = TAKE_PTR(s);
+ return 1;
+}
- r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd);
+static int ndisc_setup_timer(sd_ndisc *nd) {
+ int r;
+
+ assert(nd);
+ assert(nd->event);
+
+ r = event_reset_time_relative(nd->event, &nd->timeout_event_source,
+ CLOCK_BOOTTIME,
+ USEC_PER_SEC / 2, 1 * USEC_PER_SEC, /* See RFC 8415 sec. 18.2.1 */
+ ndisc_timeout, nd,
+ nd->event_priority, "ndisc-timeout", true);
if (r < 0)
- goto fail;
+ return r;
- r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority);
+ r = event_reset_time_relative(nd->event, &nd->timeout_no_ra,
+ CLOCK_BOOTTIME,
+ NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC,
+ ndisc_timeout_no_ra, nd,
+ nd->event_priority, "ndisc-timeout-no-ra", true);
if (r < 0)
- goto fail;
+ return r;
- (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
+ return 0;
+}
- r = event_reset_time(nd->event, &nd->timeout_event_source,
- CLOCK_BOOTTIME,
- time_now + USEC_PER_SEC / 2, 1 * USEC_PER_SEC, /* See RFC 8415 sec. 18.2.1 */
- ndisc_timeout, nd,
- nd->event_priority, "ndisc-timeout", true);
+int sd_ndisc_start(sd_ndisc *nd) {
+ int r;
+
+ assert_return(nd, -EINVAL);
+ assert_return(nd->event, -EINVAL);
+ assert_return(nd->ifindex > 0, -EINVAL);
+
+ if (sd_ndisc_is_running(nd))
+ return 0;
+
+ r = ndisc_setup_recv_event(nd);
if (r < 0)
goto fail;
- r = event_reset_time(nd->event, &nd->timeout_no_ra,
- CLOCK_BOOTTIME,
- time_now + NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC,
- ndisc_timeout_no_ra, nd,
- nd->event_priority, "ndisc-timeout-no-ra", true);
+ r = ndisc_setup_timer(nd);
if (r < 0)
goto fail;
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index 97d306c..c384d4e 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -19,6 +19,7 @@
#include "iovec-util.h"
#include "macro.h"
#include "memory-util.h"
+#include "ndisc-router-solicit-internal.h"
#include "network-common.h"
#include "radv-internal.h"
#include "random-util.h"
@@ -81,7 +82,8 @@ sd_event *sd_radv_get_event(sd_radv *ra) {
}
int sd_radv_is_running(sd_radv *ra) {
- assert_return(ra, false);
+ if (!ra)
+ return false;
return ra->state != RADV_STATE_IDLE;
}
@@ -121,30 +123,63 @@ static sd_radv *radv_free(sd_radv *ra) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free);
static bool router_lifetime_is_valid(usec_t lifetime_usec) {
+ assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
return lifetime_usec == 0 ||
(lifetime_usec >= RADV_MIN_ROUTER_LIFETIME_USEC &&
lifetime_usec <= RADV_MAX_ROUTER_LIFETIME_USEC);
}
-static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) {
+static int radv_send_router_on_stop(sd_radv *ra) {
+ static const struct nd_router_advert adv = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ };
+
+ _cleanup_set_free_ Set *options = NULL;
+ usec_t time_now;
+ int r;
+
+ assert(ra);
+
+ r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
+ if (r < 0)
+ return r;
+
+ if (!ether_addr_is_null(&ra->mac_addr)) {
+ r = ndisc_option_set_link_layer_address(&options, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &ra->mac_addr);
+ if (r < 0)
+ return r;
+ }
+
+ return ndisc_send(ra->fd, &IN6_ADDR_ALL_NODES_MULTICAST, &adv.nd_ra_hdr, options, time_now);
+}
+
+static int radv_send_router(sd_radv *ra, const struct in6_addr *dst) {
+ assert(ra);
+
struct sockaddr_in6 dst_addr = {
.sin6_family = AF_INET6,
- .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+ .sin6_addr = IN6_ADDR_ALL_NODES_MULTICAST,
+ };
+ struct nd_router_advert adv = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ .nd_ra_router_lifetime = usec_to_be16_sec(ra->lifetime_usec),
+ .nd_ra_reachable = usec_to_be32_msec(ra->reachable_usec),
+ .nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec),
};
- struct nd_router_advert adv = {};
struct {
struct nd_opt_hdr opthdr;
struct ether_addr slladdr;
} _packed_ opt_mac = {
.opthdr = {
.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
- .nd_opt_len = (sizeof(struct nd_opt_hdr) +
- sizeof(struct ether_addr) - 1) /8 + 1,
+ .nd_opt_len = DIV_ROUND_UP(sizeof(struct nd_opt_hdr) + sizeof(struct ether_addr), 8),
},
+ .slladdr = ra->mac_addr,
};
struct nd_opt_mtu opt_mtu = {
.nd_opt_mtu_type = ND_OPT_MTU,
.nd_opt_mtu_len = 1,
+ .nd_opt_mtu_mtu = htobe32(ra->mtu),
};
/* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, N pref64 prefixes, RDNSS,
* DNSSL, and home agent. */
@@ -157,9 +192,6 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
usec_t time_now;
int r;
- assert(ra);
- assert(router_lifetime_is_valid(lifetime_usec));
-
r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
return r;
@@ -167,25 +199,21 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
if (dst && in6_addr_is_set(dst))
dst_addr.sin6_addr = *dst;
- adv.nd_ra_type = ND_ROUTER_ADVERT;
+ /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
+ * simultaneously in the structured initializer in the above. */
adv.nd_ra_curhoplimit = ra->hop_limit;
- adv.nd_ra_retransmit = usec_to_be32_msec(ra->retransmit_usec);
- adv.nd_ra_flags_reserved = ra->flags;
- assert_cc(RADV_MAX_ROUTER_LIFETIME_USEC <= UINT16_MAX * USEC_PER_SEC);
- adv.nd_ra_router_lifetime = usec_to_be16_sec(lifetime_usec);
+ /* RFC 4191, Section 2.2,
+ * "...If the Router Lifetime is zero, the preference value MUST be set to (00) by the sender..." */
+ adv.nd_ra_flags_reserved = ra->flags | (ra->lifetime_usec > 0 ? (ra->preference << 3) : 0);
iov[msg.msg_iovlen++] = IOVEC_MAKE(&adv, sizeof(adv));
- /* MAC address is optional, either because the link does not use L2
- addresses or load sharing is desired. See RFC 4861, Section 4.2 */
- if (!ether_addr_is_null(&ra->mac_addr)) {
- opt_mac.slladdr = ra->mac_addr;
+ /* MAC address is optional, either because the link does not use L2 addresses or load sharing is
+ * desired. See RFC 4861, Section 4.2. */
+ if (!ether_addr_is_null(&ra->mac_addr))
iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mac, sizeof(opt_mac));
- }
- if (ra->mtu > 0) {
- opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
+ if (ra->mtu > 0)
iov[msg.msg_iovlen++] = IOVEC_MAKE(&opt_mtu, sizeof(opt_mtu));
- }
LIST_FOREACH(prefix, p, ra->prefixes) {
usec_t lifetime_valid_usec, lifetime_preferred_usec;
@@ -236,87 +264,90 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
return 0;
}
-static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_radv *ra = ASSERT_PTR(userdata);
- struct in6_addr src;
- triple_timestamp timestamp;
+static int radv_process_packet(sd_radv *ra, ICMP6Packet *packet) {
int r;
- assert(s);
- assert(ra->event);
+ assert(ra);
+ assert(packet);
- ssize_t buflen = next_datagram_size_fd(fd);
- if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
- return 0;
- if (buflen < 0) {
- log_radv_errno(ra, buflen, "Failed to determine datagram size to read, ignoring: %m");
- return 0;
- }
+ if (icmp6_packet_get_type(packet) != ND_ROUTER_SOLICIT)
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EBADMSG), "Received ICMP6 packet with unexpected type, ignoring.");
- _cleanup_free_ char *buf = new0(char, buflen);
- if (!buf)
- return -ENOMEM;
+ _cleanup_(sd_ndisc_router_solicit_unrefp) sd_ndisc_router_solicit *rs = NULL;
+ rs = ndisc_router_solicit_new(packet);
+ if (!rs)
+ return log_oom_debug();
- r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
- if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
- return 0;
+ r = ndisc_router_solicit_parse(ra, rs);
if (r < 0)
- switch (r) {
- case -EADDRNOTAVAIL:
- log_radv(ra, "Received RS from neither link-local nor null address. Ignoring");
- return 0;
+ return r;
- case -EMULTIHOP:
- log_radv(ra, "Received RS with invalid hop limit. Ignoring.");
- return 0;
+ struct in6_addr src;
+ r = sd_ndisc_router_solicit_get_sender_address(rs, &src);
+ if (r == -ENODATA) /* null address is allowed */
+ return sd_radv_send(ra); /* When an unsolicited RA, we need to also update timer. */
+ if (r < 0)
+ return log_radv_errno(ra, r, "Failed to get sender address of RS, ignoring: %m");
+ if (in6_addr_equal(&src, &ra->ipv6ll))
+ /* This should be definitely caused by a misconfiguration. If we send RA to ourself, the
+ * kernel complains about that. Let's ignore the packet. */
+ return log_radv_errno(ra, SYNTHETIC_ERRNO(EADDRINUSE), "Received RS from the same interface, ignoring.");
- case -EPFNOSUPPORT:
- log_radv(ra, "Received invalid source address from ICMPv6 socket. Ignoring.");
- return 0;
+ r = radv_send_router(ra, &src);
+ if (r < 0)
+ return log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", IN6_ADDR_TO_STRING(&src));
- default:
- log_radv_errno(ra, r, "Unexpected error receiving from ICMPv6 socket, ignoring: %m");
- return 0;
- }
+ log_radv(ra, "Sent solicited Router Advertisement to %s.", IN6_ADDR_TO_STRING(&src));
+ return 0;
+}
- if ((size_t) buflen < sizeof(struct nd_router_solicit)) {
- log_radv(ra, "Too short packet received, ignoring");
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ sd_radv *ra = ASSERT_PTR(userdata);
+ int r;
+
+ assert(fd >= 0);
+
+ r = icmp6_packet_receive(fd, &packet);
+ if (r < 0) {
+ log_radv_errno(ra, r, "Failed to receive ICMPv6 packet, ignoring: %m");
return 0;
}
- /* TODO: if the sender address is null, check that the message does not have the source link-layer
- * address option. See RFC 4861 Section 6.1.1. */
+ (void) radv_process_packet(ra, packet);
+ return 0;
+}
- const char *addr = IN6_ADDR_TO_STRING(&src);
+static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ sd_radv *ra = ASSERT_PTR(userdata);
- r = radv_send(ra, &src, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send solicited Router Advertisement to %s, ignoring: %m", addr);
- else
- log_radv(ra, "Sent solicited Router Advertisement to %s", addr);
+ if (sd_radv_send(ra) < 0)
+ (void) sd_radv_stop(ra);
return 0;
}
-static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+int sd_radv_send(sd_radv *ra) {
usec_t min_timeout, max_timeout, time_now, timeout;
- sd_radv *ra = ASSERT_PTR(userdata);
int r;
- assert(s);
- assert(ra->event);
+ assert_return(ra, -EINVAL);
+ assert_return(ra->event, -EINVAL);
+ assert_return(sd_radv_is_running(ra), -EINVAL);
assert(router_lifetime_is_valid(ra->lifetime_usec));
r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now);
if (r < 0)
- goto fail;
+ return r;
- r = radv_send(ra, NULL, ra->lifetime_usec);
+ r = radv_send_router(ra, NULL);
if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement, ignoring: %m");
+ return log_radv_errno(ra, r, "Unable to send Router Advertisement: %m");
+
+ ra->ra_sent++;
/* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
- if (ra->ra_sent < RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
+ if (ra->ra_sent <= RADV_MAX_INITIAL_RTR_ADVERTISEMENTS)
max_timeout = RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
else
max_timeout = RADV_DEFAULT_MAX_TIMEOUT_USEC;
@@ -340,47 +371,64 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
assert(min_timeout <= max_timeout * 3 / 4);
timeout = min_timeout + random_u64_range(max_timeout - min_timeout);
- log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+ log_radv(ra, "Sent unsolicited Router Advertisement. Next advertisement will be in %s.",
+ FORMAT_TIMESPAN(timeout, USEC_PER_SEC));
+
+ return event_reset_time(
+ ra->event, &ra->timeout_event_source,
+ CLOCK_BOOTTIME,
+ usec_add(time_now, timeout), MSEC_PER_SEC,
+ radv_timeout, ra,
+ ra->event_priority, "radv-timeout", true);
+}
- r = event_reset_time(ra->event, &ra->timeout_event_source,
- CLOCK_BOOTTIME,
- usec_add(time_now, timeout), MSEC_PER_SEC,
- radv_timeout, ra,
- ra->event_priority, "radv-timeout", true);
- if (r < 0)
- goto fail;
+int sd_radv_stop(sd_radv *ra) {
+ int r;
- ra->ra_sent++;
+ if (!sd_radv_is_running(ra))
+ return 0; /* Already stopped. */
- return 0;
+ log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
-fail:
- sd_radv_stop(ra);
+ /* RFC 4861, Section 6.2.5:
+ * the router SHOULD transmit one or more (but not more than MAX_FINAL_RTR_ADVERTISEMENTS) final
+ * multicast Router Advertisements on the interface with a Router Lifetime field of zero. */
+ r = radv_send_router_on_stop(ra);
+ if (r < 0)
+ log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
+
+ radv_reset(ra);
+ ra->fd = safe_close(ra->fd);
+ ra->state = RADV_STATE_IDLE;
return 0;
}
-int sd_radv_stop(sd_radv *ra) {
+static int radv_setup_recv_event(sd_radv *ra) {
int r;
- if (!ra)
- return 0;
+ assert(ra);
+ assert(ra->event);
+ assert(ra->ifindex > 0);
- if (ra->state == RADV_STATE_IDLE)
- return 0;
+ _cleanup_close_ int fd = -EBADF;
+ fd = icmp6_bind(ra->ifindex, /* is_router = */ true);
+ if (fd < 0)
+ return fd;
- log_radv(ra, "Stopping IPv6 Router Advertisement daemon");
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ r = sd_event_add_io(ra->event, &s, fd, EPOLLIN, radv_recv, ra);
+ if (r < 0)
+ return r;
- /* RFC 4861, Section 6.2.5, send at least one Router Advertisement
- with zero lifetime */
- r = radv_send(ra, NULL, 0);
+ r = sd_event_source_set_priority(s, ra->event_priority);
if (r < 0)
- log_radv_errno(ra, r, "Unable to send last Router Advertisement with router lifetime set to zero, ignoring: %m");
+ return r;
- radv_reset(ra);
- ra->fd = safe_close(ra->fd);
- ra->state = RADV_STATE_IDLE;
+ (void) sd_event_source_set_description(s, "radv-receive-message");
+ ra->fd = TAKE_FD(fd);
+ ra->recv_event_source = TAKE_PTR(s);
return 0;
}
@@ -391,8 +439,12 @@ int sd_radv_start(sd_radv *ra) {
assert_return(ra->event, -EINVAL);
assert_return(ra->ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return 0;
+ if (sd_radv_is_running(ra))
+ return 0; /* Already started. */
+
+ r = radv_setup_recv_event(ra);
+ if (r < 0)
+ goto fail;
r = event_reset_time(ra->event, &ra->timeout_event_source,
CLOCK_BOOTTIME,
@@ -402,22 +454,6 @@ int sd_radv_start(sd_radv *ra) {
if (r < 0)
goto fail;
- r = icmp6_bind_router_advertisement(ra->ifindex);
- if (r < 0)
- goto fail;
-
- ra->fd = r;
-
- r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
- if (r < 0)
- goto fail;
-
- r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
- if (r < 0)
- goto fail;
-
- (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
-
ra->state = RADV_STATE_ADVERTISING;
log_radv(ra, "Started IPv6 Router Advertisement daemon");
@@ -432,13 +468,10 @@ int sd_radv_start(sd_radv *ra) {
int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
assert_return(ra, -EINVAL);
+ assert_return(!sd_radv_is_running(ra), -EBUSY);
assert_return(ifindex > 0, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->ifindex = ifindex;
-
return 0;
}
@@ -467,11 +500,20 @@ int sd_radv_get_ifname(sd_radv *ra, const char **ret) {
return 0;
}
-int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+int sd_radv_set_link_local_address(sd_radv *ra, const struct in6_addr *addr) {
assert_return(ra, -EINVAL);
+ assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
+
+ if (addr)
+ ra->ipv6ll = *addr;
+ else
+ zero(ra->ipv6ll);
+
+ return 0;
+}
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+ assert_return(ra, -EINVAL);
if (mac_addr)
ra->mac_addr = *mac_addr;
@@ -493,22 +535,19 @@ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->hop_limit = hop_limit;
-
return 0;
}
-int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
+int sd_radv_set_reachable_time(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
+ ra->reachable_usec = usec;
+ return 0;
+}
- if (usec > RADV_MAX_RETRANSMIT_USEC)
- return -EINVAL;
+int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
+ assert_return(ra, -EINVAL);
ra->retransmit_usec = usec;
return 0;
@@ -517,89 +556,55 @@ int sd_radv_set_retransmit(sd_radv *ra, uint64_t usec) {
int sd_radv_set_router_lifetime(sd_radv *ra, uint64_t usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (!router_lifetime_is_valid(usec))
return -EINVAL;
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (usec == 0 &&
- (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
- return -EINVAL;
-
ra->lifetime_usec = usec;
return 0;
}
-int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+int sd_radv_set_managed_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, b);
return 0;
}
-int sd_radv_set_other_information(sd_radv *ra, int other) {
+int sd_radv_set_other_information(sd_radv *ra, int b) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
- SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
-
+ SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, b);
return 0;
}
-int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+int sd_radv_set_preference(sd_radv *ra, uint8_t preference) {
assert_return(ra, -EINVAL);
assert_return(IN_SET(preference,
SD_NDISC_PREFERENCE_LOW,
SD_NDISC_PREFERENCE_MEDIUM,
SD_NDISC_PREFERENCE_HIGH), -EINVAL);
- /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the preference value MUST be set
- * to (00) by the sender..." */
- if (ra->lifetime_usec == 0 && preference != SD_NDISC_PREFERENCE_MEDIUM)
- return -EINVAL;
-
- ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
-
+ ra->preference = preference;
return 0;
}
int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
-
return 0;
}
int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
-
return 0;
}
int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
assert_return(ra, -EINVAL);
- if (ra->state != RADV_STATE_IDLE)
- return -EBUSY;
-
if (lifetime_usec > RADV_HOME_AGENT_MAX_LIFETIME_USEC)
return -EINVAL;
@@ -609,7 +614,6 @@ int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint64_t lifetime_usec) {
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
sd_radv_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
@@ -621,19 +625,13 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
LIST_FOREACH(prefix, cur, ra->prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->opt.in6_addr,
- cur->opt.prefixlen,
- (const union in_addr_union*) &p->opt.in6_addr,
- p->opt.prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
+ if (!in6_addr_prefix_intersect(&cur->opt.in6_addr, cur->opt.prefixlen,
+ &p->opt.in6_addr, p->opt.prefixlen))
+ continue; /* no intersection */
if (cur->opt.prefixlen == p->opt.prefixlen) {
found = cur;
- break;
+ break; /* same prefix */
}
return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
@@ -667,19 +665,6 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
log_radv(ra, "Added prefix %s", addr_p);
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added prefix %s, ignoring: %m", addr_p);
- else
- log_radv(ra, "Sent Router Advertisement for added/updated prefix %s.", addr_p);
-
return 0;
}
@@ -710,35 +695,17 @@ void sd_radv_remove_prefix(
int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
sd_radv_route_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen);
-
- LIST_FOREACH(prefix, cur, ra->route_prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->opt.in6_addr,
- cur->opt.prefixlen,
- (const union in_addr_union*) &p->opt.in6_addr,
- p->opt.prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (cur->opt.prefixlen == p->opt.prefixlen) {
+ LIST_FOREACH(prefix, cur, ra->route_prefixes)
+ if (cur->opt.prefixlen == p->opt.prefixlen &&
+ in6_addr_equal(&cur->opt.in6_addr, &p->opt.in6_addr)) {
found = cur;
break;
}
- return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
- "IPv6 route prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->opt.in6_addr, cur->opt.prefixlen));
- }
-
if (found) {
/* p and cur may be equivalent. First increment the reference counter. */
sd_radv_route_prefix_ref(p);
@@ -751,7 +718,7 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
LIST_APPEND(prefix, ra->route_prefixes, p);
log_radv(ra, "Updated/replaced IPv6 route prefix %s (lifetime: %s)",
- strna(addr_p),
+ IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen),
FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
} else {
/* The route prefix is new. Let's simply add it. */
@@ -760,57 +727,26 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) {
LIST_APPEND(prefix, ra->route_prefixes, p);
ra->n_route_prefixes++;
- log_radv(ra, "Added route prefix %s", strna(addr_p));
+ log_radv(ra, "Added route prefix %s",
+ IN6_ADDR_PREFIX_TO_STRING(&p->opt.in6_addr, p->opt.prefixlen));
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added route prefix %s, ignoring: %m",
- strna(addr_p));
- else
- log_radv(ra, "Sent Router Advertisement for added route prefix %s.", strna(addr_p));
-
return 0;
}
int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
sd_radv_pref64_prefix *found = NULL;
- int r;
assert_return(ra, -EINVAL);
assert_return(p, -EINVAL);
- const char *addr_p = IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen);
-
- LIST_FOREACH(prefix, cur, ra->pref64_prefixes) {
- r = in_addr_prefix_intersect(AF_INET6,
- (const union in_addr_union*) &cur->in6_addr,
- cur->prefixlen,
- (const union in_addr_union*) &p->in6_addr,
- p->prefixlen);
- if (r < 0)
- return r;
- if (r == 0)
- continue;
-
- if (cur->prefixlen == p->prefixlen) {
+ LIST_FOREACH(prefix, cur, ra->pref64_prefixes)
+ if (cur->prefixlen == p->prefixlen &&
+ in6_addr_equal(&cur->in6_addr, &p->in6_addr)) {
found = cur;
break;
}
- return log_radv_errno(ra, SYNTHETIC_ERRNO(EEXIST),
- "IPv6 PREF64 prefix %s conflicts with %s, ignoring.",
- addr_p,
- IN6_ADDR_PREFIX_TO_STRING(&cur->in6_addr, cur->prefixlen));
- }
-
if (found) {
/* p and cur may be equivalent. First increment the reference counter. */
sd_radv_pref64_prefix_ref(p);
@@ -823,7 +759,7 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
LIST_APPEND(prefix, ra->pref64_prefixes, p);
log_radv(ra, "Updated/replaced IPv6 PREF64 prefix %s (lifetime: %s)",
- strna(addr_p),
+ IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen),
FORMAT_TIMESPAN(p->lifetime_usec, USEC_PER_SEC));
} else {
/* The route prefix is new. Let's simply add it. */
@@ -832,23 +768,10 @@ int sd_radv_add_pref64_prefix(sd_radv *ra, sd_radv_pref64_prefix *p) {
LIST_APPEND(prefix, ra->pref64_prefixes, p);
ra->n_pref64_prefixes++;
- log_radv(ra, "Added PREF64 prefix %s", strna(addr_p));
+ log_radv(ra, "Added PREF64 prefix %s",
+ IN6_ADDR_PREFIX_TO_STRING(&p->in6_addr, p->prefixlen));
}
- if (ra->state == RADV_STATE_IDLE)
- return 0;
-
- if (ra->ra_sent == 0)
- return 0;
-
- /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
- r = radv_send(ra, NULL, ra->lifetime_usec);
- if (r < 0)
- log_radv_errno(ra, r, "Unable to send Router Advertisement for added PREF64 prefix %s, ignoring: %m",
- strna(addr_p));
- else
- log_radv(ra, "Sent Router Advertisement for added PREF64 prefix %s.", strna(addr_p));
-
return 0;
}
diff --git a/src/libsystemd-network/test-acd.c b/src/libsystemd-network/test-acd.c
index 4b5ad70..6ed7260 100644
--- a/src/libsystemd-network/test-acd.c
+++ b/src/libsystemd-network/test-acd.c
@@ -1,11 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
+#include <net/if.h>
#include <errno.h>
+#include <linux/veth.h>
#include <stdlib.h>
#include <unistd.h>
-#include <linux/veth.h>
-#include <net/if.h>
#include "sd-event.h"
#include "sd-ipv4acd.h"
diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c
index e3f148d..5b4ce3e 100644
--- a/src/libsystemd-network/test-dhcp-client.c
+++ b/src/libsystemd-network/test-dhcp-client.c
@@ -17,7 +17,7 @@
#include "sd-event.h"
#include "alloc-util.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp-network.h"
#include "dhcp-option.h"
#include "dhcp-packet.h"
@@ -57,14 +57,14 @@ static void test_request_basic(sd_event *e) {
r = sd_dhcp_client_attach_event(client, e, 0);
assert_se(r >= 0);
- assert_se(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
- assert_se(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
- assert_se(sd_dhcp_client_set_ifindex(NULL, 0) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_request_option(NULL, 0) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_request_address(NULL, NULL) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_ifindex(NULL, 0) == -EINVAL);
assert_se(sd_dhcp_client_set_ifindex(client, 15) == 0);
- assert_se(sd_dhcp_client_set_ifindex(client, -42) == -EINVAL);
- assert_se(sd_dhcp_client_set_ifindex(client, -1) == -EINVAL);
- assert_se(sd_dhcp_client_set_ifindex(client, 0) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_ifindex(client, -42) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_ifindex(client, -1) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_client_set_ifindex(client, 0) == -EINVAL);
assert_se(sd_dhcp_client_set_ifindex(client, 1) == 0);
assert_se(sd_dhcp_client_set_hostname(client, "host") == 1);
@@ -165,19 +165,18 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us
switch (code) {
case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
{
+ sd_dhcp_duid duid;
uint32_t iaid;
- struct duid duid;
- size_t duid_len;
- assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0);
+ assert_se(sd_dhcp_duid_set_en(&duid) >= 0);
assert_se(dhcp_identifier_set_iaid(NULL, &hw_addr, /* legacy = */ true, &iaid) >= 0);
- assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len);
+ assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid.size);
assert_se(len == 19);
assert_se(((uint8_t*) option)[0] == 0xff);
assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0);
- assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0);
+ assert_se(memcmp((uint8_t*) option + 5, &duid.duid, duid.size) == 0);
break;
}
@@ -515,7 +514,7 @@ static void test_addr_acq(sd_event *e) {
callback_recv = test_addr_acq_recv_discover;
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
res = sd_dhcp_client_start(client);
diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
index b2e6034..ecd030e 100644
--- a/src/libsystemd-network/test-dhcp-server.c
+++ b/src/libsystemd-network/test-dhcp-server.c
@@ -17,7 +17,7 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) {
assert_se(sd_dhcp_server_new(&server, 1) >= 0);
- assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
+ ASSERT_RETURN_IS_CRITICAL(ret >= 0, assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret));
}
static int test_basic(bool bind_to_interface) {
@@ -41,20 +41,20 @@ static int test_basic(bool bind_to_interface) {
server->bind_to_interface = bind_to_interface;
assert_se(sd_dhcp_server_attach_event(server, event, 0) >= 0);
- assert_se(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_server_attach_event(server, event, 0) == -EBUSY);
assert_se(sd_dhcp_server_get_event(server) == event);
assert_se(sd_dhcp_server_detach_event(server) >= 0);
assert_se(!sd_dhcp_server_get_event(server));
assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
- assert_se(sd_dhcp_server_attach_event(server, NULL, 0) == -EBUSY);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_server_attach_event(server, NULL, 0) == -EBUSY);
assert_se(sd_dhcp_server_ref(server) == server);
assert_se(!sd_dhcp_server_unref(server));
- assert_se(sd_dhcp_server_start(server) == -EUNATCH);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_server_start(server) == -EUNATCH);
- assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
- assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
@@ -62,7 +62,9 @@ static int test_basic(bool bind_to_interface) {
test_pool(&address_lo, 1, 0);
r = sd_dhcp_server_start(server);
- if (r == -EPERM)
+ /* skip test if running in an environment with no full networking support, CONFIG_PACKET not
+ * compiled in kernel, nor af_packet module available. */
+ if (r == -EPERM || r == -EAFNOSUPPORT)
return r;
assert_se(r >= 0);
@@ -184,7 +186,7 @@ static void test_message_handler(void) {
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
test.option_server_id.address = htobe32(INADDR_LOOPBACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 4);
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
+ assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
@@ -200,7 +202,7 @@ static void test_message_handler(void) {
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30);
- assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == 0);
+ assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
/* request address reserved for static lease (unmatching client ID) */
test.option_client_id.id[6] = 'H';
@@ -223,7 +225,7 @@ static void test_message_handler(void) {
assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test), NULL) == DHCP_ACK);
}
-static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) {
+static uint64_t client_id_hash_helper(sd_dhcp_client_id *id, uint8_t key[HASH_KEY_SIZE]) {
struct siphash state;
siphash24_init(&state, key);
@@ -233,10 +235,10 @@ static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZ
}
static void test_client_id_hash(void) {
- DHCPClientId a = {
- .length = 4,
+ sd_dhcp_client_id a = {
+ .size = 4,
}, b = {
- .length = 4,
+ .size = 4,
};
uint8_t hash_key[HASH_KEY_SIZE] = {
'0', '1', '2', '3', '4', '5', '6', '7',
@@ -245,29 +247,25 @@ static void test_client_id_hash(void) {
log_debug("/* %s */", __func__);
- a.data = (uint8_t*)strdup("abcd");
- b.data = (uint8_t*)strdup("abcd");
+ memcpy(a.raw, "abcd", 4);
+ memcpy(b.raw, "abcd", 4);
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- a.length = 3;
+ a.size = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
- a.length = 4;
+ a.size = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- b.length = 3;
+ b.size = 3;
assert_se(client_id_compare_func(&a, &b) != 0);
- b.length = 4;
+ b.size = 4;
assert_se(client_id_compare_func(&a, &b) == 0);
assert_se(client_id_hash_helper(&a, hash_key) == client_id_hash_helper(&b, hash_key));
- free(b.data);
- b.data = (uint8_t*)strdup("abce");
+ memcpy(b.raw, "abce", 4);
assert_se(client_id_compare_func(&a, &b) != 0);
-
- free(a.data);
- free(b.data);
}
static void test_static_lease(void) {
diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c
index ae3cdb8..7afd464 100644
--- a/src/libsystemd-network/test-dhcp6-client.c
+++ b/src/libsystemd-network/test-dhcp6-client.c
@@ -13,7 +13,7 @@
#include "sd-dhcp6-client.h"
#include "sd-event.h"
-#include "dhcp-identifier.h"
+#include "dhcp-duid-internal.h"
#include "dhcp6-internal.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
@@ -25,6 +25,7 @@
#include "strv.h"
#include "tests.h"
#include "time-util.h"
+#include "unaligned.h"
#define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77
#define IA_ID_BYTES \
@@ -58,8 +59,7 @@
static const struct in6_addr local_address =
{ { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } };
-static const struct in6_addr mcast_address =
- IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+static const struct in6_addr mcast_address = IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS;
static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } };
static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } };
static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } };
@@ -1027,7 +1027,7 @@ static void test_client_callback(sd_dhcp6_client *client, int event, void *userd
}
}
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *a, const void *packet, size_t len) {
log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count);
assert_se(a);
@@ -1071,7 +1071,7 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet,
return len;
}
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *a) {
assert_se(ifindex == test_ifindex);
assert_se(a);
assert_se(in6_addr_equal(a, &local_address));
@@ -1086,7 +1086,7 @@ TEST(dhcp6_client) {
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
assert_se(sd_dhcp6_client_new(&client) >= 0);
diff --git a/src/libsystemd-network/test-ipv4ll-manual.c b/src/libsystemd-network/test-ipv4ll-manual.c
index 5dc6b10..b4e6d24 100644
--- a/src/libsystemd-network/test-ipv4ll-manual.c
+++ b/src/libsystemd-network/test-ipv4ll-manual.c
@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#include <errno.h>
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
+#include <errno.h>
+#include <linux/veth.h>
#include <stdlib.h>
#include <unistd.h>
-#include <linux/veth.h>
#include "sd-event.h"
#include "sd-ipv4ll.h"
diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c
index bb42930..e7dbd7f 100644
--- a/src/libsystemd-network/test-ipv4ll.c
+++ b/src/libsystemd-network/test-ipv4ll.c
@@ -85,33 +85,34 @@ static void test_public_api_setters(sd_event *e) {
assert_se(sd_ipv4ll_new(&ll) == 0);
assert_se(ll);
- assert_se(sd_ipv4ll_attach_event(NULL, NULL, 0) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_attach_event(NULL, NULL, 0) == -EINVAL);
assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0);
- assert_se(sd_ipv4ll_attach_event(ll, e, 0) == -EBUSY);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_attach_event(ll, e, 0) == -EBUSY);
- assert_se(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_callback(NULL, NULL, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, NULL, NULL) == 0);
- assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
address.s_addr |= htobe32(169U << 24 | 254U << 16);
- assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
address.s_addr |= htobe32(0x00FF);
- assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
address.s_addr |= htobe32(0xF000);
assert_se(sd_ipv4ll_set_address(ll, &address) == 0);
address.s_addr |= htobe32(0x0F00);
- assert_se(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_address(ll, &address) == -EINVAL);
- assert_se(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_address_seed(NULL, seed) == -EINVAL);
assert_se(sd_ipv4ll_set_address_seed(ll, seed) == 0);
- assert_se(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
- assert_se(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_mac(NULL, NULL) == -EINVAL);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_mac(ll, NULL) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0);
- assert_se(sd_ipv4ll_set_ifindex(NULL, -1) == -EINVAL);
- assert_se(sd_ipv4ll_set_ifindex(ll, -1) == -EINVAL);
- assert_se(sd_ipv4ll_set_ifindex(ll, -99) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_ifindex(NULL, -1) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_ifindex(ll, -1) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_set_ifindex(ll, -99) == -EINVAL);
assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0);
assert_se(sd_ipv4ll_ref(ll) == ll);
@@ -134,17 +135,17 @@ static void test_basic_request(sd_event *e, const struct in_addr *start_address)
assert_se(sd_ipv4ll_new(&ll) == 0);
if (in4_addr_is_set(start_address))
assert_se(sd_ipv4ll_set_address(ll, start_address) >= 0);
- assert_se(sd_ipv4ll_start(ll) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_attach_event(ll, e, 0) == 0);
- assert_se(sd_ipv4ll_start(ll) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_mac(ll, &mac_addr) == 0);
- assert_se(sd_ipv4ll_start(ll) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler,
basic_request_handler_userdata) == 0);
- assert_se(sd_ipv4ll_start(ll) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_ipv4ll_start(ll) == -EINVAL);
assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0);
assert_se(sd_ipv4ll_start(ll) == 1);
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index 23abe78..b2ea8f5 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -11,7 +11,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
-#include "icmp6-util-unix.h"
+#include "icmp6-test-util.h"
#include "socket-util.h"
#include "strv.h"
#include "tests.h"
@@ -20,37 +20,6 @@ static struct ether_addr mac_addr = {
.ether_addr_octet = { 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 }
};
-static uint8_t advertisement[] = {
- /* ICMPv6 Router Advertisement, no checksum */
- 0x86, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x00, 0xb4,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Source Link Layer Address Option */
- 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
- /* Prefix Information Option */
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
- 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Prefix Information Option */
- 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10,
- 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Prefix Information Option */
- 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
- 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
- 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- /* Recursive DNS Server Option */
- 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- /* DNS Search List Option */
- 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
- 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
- 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
static bool test_stopped;
static struct {
struct in6_addr address;
@@ -103,26 +72,26 @@ TEST(radv_prefix) {
assert_se(sd_radv_prefix_new(&p) >= 0);
- assert_se(sd_radv_prefix_set_onlink(NULL, true) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_onlink(NULL, true) < 0);
assert_se(sd_radv_prefix_set_onlink(p, true) >= 0);
assert_se(sd_radv_prefix_set_onlink(p, false) >= 0);
- assert_se(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0);
assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0);
assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0);
- assert_se(sd_radv_prefix_set_valid_lifetime(NULL, 1, 1) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_valid_lifetime(NULL, 1, 1) < 0);
assert_se(sd_radv_prefix_set_valid_lifetime(p, 0, 0) >= 0);
assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
assert_se(sd_radv_prefix_set_valid_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
- assert_se(sd_radv_prefix_set_preferred_lifetime(NULL, 1, 1) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_preferred_lifetime(NULL, 1, 1) < 0);
assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0, 0) >= 0);
assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
assert_se(sd_radv_prefix_set_preferred_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
- assert_se(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, NULL, 0) < 0);
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 0) < 0);
@@ -131,8 +100,8 @@ TEST(radv_prefix) {
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
assert_se(!sd_radv_prefix_unref(p));
}
@@ -142,13 +111,13 @@ TEST(radv_route_prefix) {
assert_se(sd_radv_route_prefix_new(&p) >= 0);
- assert_se(sd_radv_route_prefix_set_lifetime(NULL, 1, 1) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_lifetime(NULL, 1, 1) < 0);
assert_se(sd_radv_route_prefix_set_lifetime(p, 0, 0) >= 0);
assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_INFINITY) >= 0);
assert_se(sd_radv_route_prefix_set_lifetime(p, 300 * USEC_PER_SEC, USEC_PER_YEAR) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(NULL, NULL, 0) < 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(NULL, NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, NULL, 0) < 0);
assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 64) >= 0);
assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 0) >= 0);
@@ -157,8 +126,8 @@ TEST(radv_route_prefix) {
assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 3) >= 0);
assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 125) >= 0);
assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 128) >= 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
- assert_se(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 129) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_route_prefix_set_prefix(p, &prefix[0].address, 255) < 0);
assert_se(!sd_radv_route_prefix_unref(p));
}
@@ -168,8 +137,8 @@ TEST(radv_pref64_prefix) {
assert_se(sd_radv_pref64_prefix_new(&p) >= 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(NULL, NULL, 0, 0) < 0);
- assert_se(sd_radv_pref64_prefix_set_prefix(p, NULL, 0, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(NULL, NULL, 0, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_pref64_prefix_set_prefix(p, NULL, 0, 0) < 0);
assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 32, 300 * USEC_PER_SEC) >= 0);
assert_se(sd_radv_pref64_prefix_set_prefix(p, &prefix[0].address, 40, 300 * USEC_PER_SEC) >= 0);
@@ -190,60 +159,59 @@ TEST(radv) {
assert_se(sd_radv_new(&ra) >= 0);
assert_se(ra);
- assert_se(sd_radv_set_ifindex(NULL, 0) < 0);
- assert_se(sd_radv_set_ifindex(ra, 0) < 0);
- assert_se(sd_radv_set_ifindex(ra, -1) < 0);
- assert_se(sd_radv_set_ifindex(ra, -2) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(ra, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(ra, -1) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_ifindex(ra, -2) < 0);
assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
- assert_se(sd_radv_set_mac(NULL, NULL) < 0);
- assert_se(sd_radv_set_mac(ra, NULL) >= 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(NULL, NULL) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mac(ra, NULL) >= 0);
assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0);
- assert_se(sd_radv_set_mtu(NULL, 0) < 0);
- assert_se(sd_radv_set_mtu(ra, 0) < 0);
- assert_se(sd_radv_set_mtu(ra, 1279) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_mtu(ra, 1279) < 0);
assert_se(sd_radv_set_mtu(ra, 1280) >= 0);
assert_se(sd_radv_set_mtu(ra, ~0) >= 0);
- assert_se(sd_radv_set_hop_limit(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_hop_limit(NULL, 0) < 0);
assert_se(sd_radv_set_hop_limit(ra, 0) >= 0);
assert_se(sd_radv_set_hop_limit(ra, ~0) >= 0);
- assert_se(sd_radv_set_router_lifetime(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_router_lifetime(NULL, 0) < 0);
assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
assert_se(sd_radv_set_router_lifetime(ra, USEC_INFINITY) < 0);
assert_se(sd_radv_set_router_lifetime(ra, USEC_PER_YEAR) < 0);
assert_se(sd_radv_set_router_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_set_preference(NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_preference(NULL, 0) < 0);
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_LOW) >= 0);
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
- assert_se(sd_radv_set_preference(ra, ~0) < 0);
-
- assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 0) < 0);
- assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0);
- assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_preference(ra, ~0) < 0);
- assert_se(sd_radv_set_managed_information(NULL, true) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_managed_information(NULL, true) < 0);
assert_se(sd_radv_set_managed_information(ra, true) >= 0);
assert_se(sd_radv_set_managed_information(ra, false) >= 0);
- assert_se(sd_radv_set_other_information(NULL, true) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_other_information(NULL, true) < 0);
assert_se(sd_radv_set_other_information(ra, true) >= 0);
assert_se(sd_radv_set_other_information(ra, false) >= 0);
- assert_se(sd_radv_set_retransmit(NULL, 10 * USEC_PER_MSEC) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_reachable_time(NULL, 10 * USEC_PER_MSEC) < 0);
+ assert_se(sd_radv_set_reachable_time(ra, 10 * USEC_PER_MSEC) >= 0);
+ assert_se(sd_radv_set_reachable_time(ra, 0) >= 0);
+ assert_se(sd_radv_set_reachable_time(ra, USEC_INFINITY) >= 0);
+
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_retransmit(NULL, 10 * USEC_PER_MSEC) < 0);
assert_se(sd_radv_set_retransmit(ra, 10 * USEC_PER_MSEC) >= 0);
assert_se(sd_radv_set_retransmit(ra, 0) >= 0);
- assert_se(sd_radv_set_retransmit(ra, usec_add(UINT32_MAX * USEC_PER_MSEC, USEC_PER_MSEC)) < 0);
+ assert_se(sd_radv_set_retransmit(ra, USEC_INFINITY) >= 0);
- assert_se(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(NULL, 0, NULL, 0) < 0);
assert_se(sd_radv_set_rdnss(ra, 0, NULL, 0) >= 0);
- assert_se(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_rdnss(ra, 0, NULL, 128) < 0);
assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 0) >= 0);
assert_se(sd_radv_set_rdnss(ra, 600 * USEC_PER_SEC, &test_rdnss, 1) >= 0);
assert_se(sd_radv_set_rdnss(ra, 0, &test_rdnss, 1) >= 0);
@@ -254,15 +222,15 @@ TEST(radv) {
assert_se(sd_radv_set_dnssl(ra, 0, (char **)test_dnssl) >= 0);
assert_se(sd_radv_set_dnssl(ra, 600 * USEC_PER_SEC, (char **)test_dnssl) >= 0);
- assert_se(sd_radv_set_home_agent_information(NULL, true) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_information(NULL, true) < 0);
assert_se(sd_radv_set_home_agent_information(ra, true) >= 0);
assert_se(sd_radv_set_home_agent_information(ra, false) >= 0);
- assert_se(sd_radv_set_home_agent_preference(NULL, 10) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_preference(NULL, 10) < 0);
assert_se(sd_radv_set_home_agent_preference(ra, 10) >= 0);
assert_se(sd_radv_set_home_agent_preference(ra, 0) >= 0);
- assert_se(sd_radv_set_home_agent_lifetime(NULL, 300 * USEC_PER_SEC) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_radv_set_home_agent_lifetime(NULL, 300 * USEC_PER_SEC) < 0);
assert_se(sd_radv_set_home_agent_lifetime(ra, 300 * USEC_PER_SEC) >= 0);
assert_se(sd_radv_set_home_agent_lifetime(ra, 0) >= 0);
assert_se(sd_radv_set_home_agent_lifetime(ra, USEC_PER_DAY) < 0);
@@ -271,49 +239,96 @@ TEST(radv) {
assert_se(!ra);
}
-static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- sd_radv *ra = userdata;
- unsigned char buf[168];
- size_t i;
+static void dump_message(const uint8_t *buf, size_t len) {
+ assert(len >= sizeof(struct nd_router_advert));
- assert_se(read(test_fd[0], &buf, sizeof(buf)) == sizeof(buf));
+ printf("Received Router Advertisement with lifetime %i sec\n",
+ (buf[6] << 8) + buf[7]);
- /* router lifetime must be zero when test is stopped */
- if (test_stopped) {
- advertisement[6] = 0x00;
- advertisement[7] = 0x00;
- }
-
- printf ("Received Router Advertisement with lifetime %i\n",
- (advertisement[6] << 8) + advertisement[7]);
-
- /* test only up to buf size, rest is not yet implemented */
- for (i = 0; i < sizeof(buf); i++) {
+ for (size_t i = 0; i < len; i++) {
if (!(i % 8))
printf("%3zu: ", i);
printf("0x%02x", buf[i]);
- assert_se(buf[i] == advertisement[i]);
-
if ((i + 1) % 8)
printf(", ");
else
printf("\n");
}
+}
- if (test_stopped) {
- sd_event *e;
+static void verify_message(const uint8_t *buf, size_t len) {
+ static const uint8_t advertisement[] = {
+ /* ICMPv6 Router Advertisement, no checksum */
+ 0x86, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x00, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Source Link Layer Address Option */
+ 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
+ /* Prefix Information Option */
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Prefix Information Option */
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x0e, 0x10,
+ 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Prefix Information Option */
+ 0x03, 0x04, 0x30, 0xc0, 0x00, 0x00, 0x0e, 0x10,
+ 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Recursive DNS Server Option */
+ 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ /* DNS Search List Option */
+ 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
+ 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+
+ /* verify only up to known options, rest is not yet implemented */
+ for (size_t i = 0, m = MIN(len, sizeof(advertisement)); i < m; i++) {
+ if (test_stopped)
+ /* on stop, many header fields are zero */
+ switch (i) {
+ case 4: /* hop limit */
+ case 5: /* flags */
+ case 6 ... 7: /* router lifetime */
+ case 8 ... 11: /* reachable time */
+ case 12 ... 15: /* retrans timer */
+ assert_se(buf[i] == 0);
+ continue;
+ }
- e = sd_radv_get_event(ra);
- sd_event_exit(e, 0);
+ assert_se(buf[i] == advertisement[i]);
+ }
+}
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ sd_radv *ra = ASSERT_PTR(userdata);
+ _cleanup_free_ uint8_t *buf = NULL;
+ ssize_t buflen;
+
+ buflen = next_datagram_size_fd(fd);
+ assert_se(buflen >= 0);
+ assert_se(buf = new0(uint8_t, buflen));
+
+ assert_se(read(fd, buf, buflen) == buflen);
+
+ dump_message(buf, buflen);
+ verify_message(buf, buflen);
+
+ if (test_stopped) {
+ assert_se(sd_event_exit(sd_radv_get_event(ra), 0) >= 0);
return 0;
}
assert_se(sd_radv_stop(ra) >= 0);
test_stopped = true;
-
return 0;
}
@@ -365,7 +380,7 @@ TEST(ra) {
assert_se(sd_event_source_set_io_fd_own(recv_router_advertisement, true) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
- 2 * USEC_PER_SEC, 0,
+ 30 * USEC_PER_SEC, 0,
NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
assert_se(sd_radv_start(ra) >= 0);
diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c
index d94cc1c..66aad26 100644
--- a/src/libsystemd-network/test-ndisc-rs.c
+++ b/src/libsystemd-network/test-ndisc-rs.c
@@ -12,7 +12,8 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
-#include "icmp6-util-unix.h"
+#include "icmp6-packet.h"
+#include "icmp6-test-util.h"
#include "socket-util.h"
#include "strv.h"
#include "ndisc-internal.h"
@@ -23,21 +24,20 @@ static struct ether_addr mac_addr = {
};
static bool verbose = false;
-static sd_ndisc *test_timeout_nd;
static void router_dump(sd_ndisc_router *rt) {
struct in6_addr addr;
uint8_t hop_limit;
- usec_t t, lifetime;
+ usec_t t, lifetime, retrans_time;
uint64_t flags;
uint32_t mtu;
- unsigned preference;
+ uint8_t preference;
int r;
assert_se(rt);
log_info("--");
- assert_se(sd_ndisc_router_get_address(rt, &addr) >= 0);
+ assert_se(sd_ndisc_router_get_sender_address(rt, &addr) >= 0);
log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
@@ -65,6 +65,9 @@ static void router_dump(sd_ndisc_router *rt) {
assert_se(sd_ndisc_router_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
log_info("Lifetime: %s (%s)", FORMAT_TIMESPAN(lifetime, USEC_PER_SEC), FORMAT_TIMESTAMP(t));
+ assert_se(sd_ndisc_router_get_retransmission_time(rt, &retrans_time) >= 0);
+ log_info("Retransmission Time: %s", FORMAT_TIMESPAN(retrans_time, USEC_PER_SEC));
+
if (sd_ndisc_router_get_mtu(rt, &mtu) < 0)
log_info("No MTU set");
else
@@ -88,20 +91,19 @@ static void router_dump(sd_ndisc_router *rt) {
case SD_NDISC_OPTION_SOURCE_LL_ADDRESS:
case SD_NDISC_OPTION_TARGET_LL_ADDRESS: {
_cleanup_free_ char *c = NULL;
- const void *p;
+ const uint8_t *p;
size_t n;
assert_se(sd_ndisc_router_option_get_raw(rt, &p, &n) >= 0);
assert_se(n > 2);
- assert_se(c = hexmem((uint8_t*) p + 2, n - 2));
+ assert_se(c = hexmem(p + 2, n - 2));
log_info("Address: %s", c);
break;
}
case SD_NDISC_OPTION_PREFIX_INFORMATION: {
- unsigned prefix_len;
- uint8_t pfl;
+ uint8_t prefix_len, pfl;
struct in6_addr a;
assert_se(sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime) >= 0);
@@ -143,14 +145,12 @@ static void router_dump(sd_ndisc_router *rt) {
}
case SD_NDISC_OPTION_DNSSL: {
- _cleanup_strv_free_ char **l = NULL;
- int n, i;
+ char **l;
- n = sd_ndisc_router_dnssl_get_domains(rt, &l);
- assert_se(n > 0);
+ assert_se(sd_ndisc_router_dnssl_get_domains(rt, &l) >= 0);
- for (i = 0; i < n; i++)
- log_info("Domain: %s", l[i]);
+ STRV_FOREACH(s, l)
+ log_info("Domain: %s", *s);
assert_se(sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime) >= 0);
assert_se(sd_ndisc_router_dnssl_get_lifetime_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
@@ -164,18 +164,23 @@ static void router_dump(sd_ndisc_router *rt) {
static int send_ra(uint8_t flags) {
uint8_t advertisement[] = {
+ /* struct nd_router_advert */
0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* type = 0x03 (SD_NDISC_OPTION_PREFIX_INFORMATION), length = 32 */
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* type = 0x19 (SD_NDISC_OPTION_RDNSS), length = 24 */
0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ /* type = 0x1f (SD_NDISC_OPTION_DNSSL), length = 24 */
0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* type = 0x01 (SD_NDISC_OPTION_SOURCE_LL_ADDRESS), length = 8 */
0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
};
@@ -190,7 +195,7 @@ static int send_ra(uint8_t flags) {
return 0;
}
-static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata) {
+static void test_callback_ra(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
sd_event *e = userdata;
static unsigned idx = 0;
uint64_t flags_array[] = {
@@ -207,6 +212,8 @@ static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
if (event != SD_NDISC_EVENT_ROUTER)
return;
+ sd_ndisc_router *rt = ASSERT_PTR(message);
+
router_dump(rt);
assert_se(sd_ndisc_router_get_flags(rt, &flags) >= 0);
@@ -221,15 +228,22 @@ static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
return;
}
+ idx = 0;
sd_event_exit(e, 0);
}
+static int on_recv_rs(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+ return send_ra(0);
+}
+
TEST(rs) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
- send_ra_function = send_ra;
-
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_ndisc_new(&nd) >= 0);
@@ -239,7 +253,7 @@ TEST(rs) {
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
- assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
30 * USEC_PER_SEC, 0,
@@ -253,19 +267,219 @@ TEST(rs) {
assert_se(sd_ndisc_start(nd) >= 0);
+ assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs, nd) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
assert_se(sd_event_loop(e) >= 0);
- test_fd[1] = safe_close(test_fd[1]);
+ test_fd[1] = -EBADF;
}
-static int test_timeout_value(uint8_t flags) {
+static int send_ra_invalid_domain(uint8_t flags) {
+ uint8_t advertisement[] = {
+ /* struct nd_router_advert */
+ 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* type = 0x03 (SD_NDISC_OPTION_PREFIX_INFORMATION), length = 32 */
+ 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4,
+ 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* type = 0x19 (SD_NDISC_OPTION_RDNSS), length = 24 */
+ 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ /* type = 0x1f (SD_NDISC_OPTION_DNSSL), length = 112 */
+ 0x1f, 0x0e, 0xee, 0x68, 0xb0, 0xf4, 0x36, 0x39,
+ 0x2c, 0xbc, 0x0b, 0xbc, 0xa9, 0x97, 0x71, 0x37,
+ 0xad, 0x86, 0x80, 0x14, 0x2e, 0x58, 0xaa, 0x8a,
+ 0xb7, 0xa1, 0xbe, 0x91, 0x59, 0x00, 0xc4, 0xe8,
+ 0xdd, 0xd8, 0x6d, 0xe5, 0x4a, 0x7a, 0x71, 0x42,
+ 0x74, 0x45, 0x9e, 0x2e, 0xfd, 0x9d, 0x71, 0x1d,
+ 0xd0, 0xc0, 0x54, 0x0c, 0x4d, 0x1f, 0xbf, 0x90,
+ 0xd9, 0x79, 0x58, 0xc0, 0x1d, 0xa3, 0x39, 0xcf,
+ 0xb8, 0xec, 0xd2, 0xe4, 0xcd, 0xb6, 0x13, 0x2f,
+ 0xc0, 0x46, 0xe8, 0x07, 0x3f, 0xaa, 0x28, 0xa5,
+ 0x23, 0xf1, 0xf0, 0xca, 0xd3, 0x19, 0x3f, 0xfa,
+ 0x6c, 0x7c, 0xec, 0x1b, 0xcf, 0x71, 0xeb, 0xba,
+ 0x68, 0x1b, 0x8e, 0x7d, 0x93, 0x7e, 0x0b, 0x9f,
+ 0xdb, 0x12, 0x9c, 0x75, 0x22, 0x5f, 0x12, 0x00,
+ /* type = 0x01 (SD_NDISC_OPTION_SOURCE_LL_ADDRESS), length = 8 */
+ 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53,
+ };
+
+ advertisement[5] = flags;
+
+ printf("sizeof(nd_router_advert)=%zu\n", sizeof(struct nd_router_advert));
+
+ assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
+ sizeof(advertisement));
+
+ if (verbose)
+ printf(" sent RA with flag 0x%02x\n", flags);
+
+ return 0;
+}
+
+static int on_recv_rs_invalid_domain(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+ return send_ra_invalid_domain(0);
+}
+
+TEST(invalid_domain) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(nd);
+
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_ra, e) >= 0);
+
+ assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
+ 30 * USEC_PER_SEC, 0,
+ NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
+
+ assert_se(sd_ndisc_start(nd) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_invalid_domain, nd) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ test_fd[1] = -EBADF;
+}
+
+static void neighbor_dump(sd_ndisc_neighbor *na) {
+ struct in6_addr addr;
+ uint32_t flags;
+
+ assert_se(na);
+
+ log_info("--");
+ assert_se(sd_ndisc_neighbor_get_sender_address(na, &addr) >= 0);
+ log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
+
+ assert_se(sd_ndisc_neighbor_get_flags(na, &flags) >= 0);
+ log_info("Flags: Router:%s, Solicited:%s, Override: %s",
+ yes_no(flags & ND_NA_FLAG_ROUTER),
+ yes_no(flags & ND_NA_FLAG_SOLICITED),
+ yes_no(flags & ND_NA_FLAG_OVERRIDE));
+
+ assert_se(sd_ndisc_neighbor_is_router(na) == FLAGS_SET(flags, ND_NA_FLAG_ROUTER));
+ assert_se(sd_ndisc_neighbor_is_solicited(na) == FLAGS_SET(flags, ND_NA_FLAG_SOLICITED));
+ assert_se(sd_ndisc_neighbor_is_override(na) == FLAGS_SET(flags, ND_NA_FLAG_OVERRIDE));
+}
+
+static int send_na(uint32_t flags) {
+ uint8_t advertisement[] = {
+ /* struct nd_neighbor_advert */
+ 0x88, 0x00, 0xde, 0x83, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ /* type = 0x02 (SD_NDISC_OPTION_TARGET_LL_ADDRESS), length = 8 */
+ 0x01, 0x01, 'A', 'B', 'C', '1', '2', '3',
+ };
+
+ ((struct nd_neighbor_advert*) advertisement)->nd_na_flags_reserved = flags;
+
+ assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement));
+ if (verbose)
+ printf(" sent NA with flag 0x%02x\n", flags);
+
+ return 0;
+}
+
+static void test_callback_na(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata) {
+ sd_event *e = userdata;
+ static unsigned idx = 0;
+ uint32_t flags_array[] = {
+ 0,
+ 0,
+ ND_NA_FLAG_ROUTER,
+ ND_NA_FLAG_SOLICITED,
+ ND_NA_FLAG_SOLICITED | ND_NA_FLAG_OVERRIDE,
+ };
+ uint32_t flags;
+
+ assert_se(nd);
+
+ if (event != SD_NDISC_EVENT_NEIGHBOR)
+ return;
+
+ sd_ndisc_neighbor *rt = ASSERT_PTR(message);
+
+ neighbor_dump(rt);
+
+ assert_se(sd_ndisc_neighbor_get_flags(rt, &flags) >= 0);
+ assert_se(flags == flags_array[idx]);
+ idx++;
+
+ if (verbose)
+ printf(" got event 0x%02" PRIx32 "\n", flags);
+
+ if (idx < ELEMENTSOF(flags_array)) {
+ send_na(flags_array[idx]);
+ return;
+ }
+
+ idx = 0;
+ sd_event_exit(e, 0);
+}
+
+static int on_recv_rs_na(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ assert_se(icmp6_packet_receive(fd, &packet) >= 0);
+
+ return send_na(0);
+}
+
+TEST(na) {
+ _cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
+ _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
+
+ assert_se(sd_event_new(&e) >= 0);
+
+ assert_se(sd_ndisc_new(&nd) >= 0);
+ assert_se(nd);
+
+ assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
+
+ assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
+ assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
+ assert_se(sd_ndisc_set_callback(nd, test_callback_na, e) >= 0);
+
+ assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME,
+ 30 * USEC_PER_SEC, 0,
+ NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0);
+
+ assert_se(sd_ndisc_start(nd) >= 0);
+
+ assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_na, nd) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
+ assert_se(sd_event_loop(e) >= 0);
+
+ test_fd[1] = -EBADF;
+}
+
+static int on_recv_rs_timeout(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+ _cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
+ sd_ndisc *nd = ASSERT_PTR(userdata);
static int count = 0;
static usec_t last = 0;
- sd_ndisc *nd = test_timeout_nd;
usec_t min, max;
- assert_se(nd);
- assert_se(nd->event);
+ assert_se(icmp6_packet_receive(fd, &packet) >= 0);
if (++count >= 20)
sd_event_exit(nd->event, 0);
@@ -309,17 +523,14 @@ static int test_timeout_value(uint8_t flags) {
TEST(timeout) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
+ _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
- send_ra_function = test_timeout_value;
-
assert_se(sd_event_new(&e) >= 0);
assert_se(sd_ndisc_new(&nd) >= 0);
assert_se(nd);
- test_timeout_nd = nd;
-
assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0);
assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0);
@@ -331,9 +542,12 @@ TEST(timeout) {
assert_se(sd_ndisc_start(nd) >= 0);
+ assert_se(sd_event_add_io(e, &s, test_fd[1], EPOLLIN, on_recv_rs_timeout, nd) >= 0);
+ assert_se(sd_event_source_set_io_fd_own(s, true) >= 0);
+
assert_se(sd_event_loop(e) >= 0);
- test_fd[1] = safe_close(test_fd[1]);
+ test_fd[1] = -EBADF;
}
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/libsystemd-network/test-ndisc-send.c b/src/libsystemd-network/test-ndisc-send.c
new file mode 100644
index 0000000..1b1b27d
--- /dev/null
+++ b/src/libsystemd-network/test-ndisc-send.c
@@ -0,0 +1,449 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <getopt.h>
+
+#include "build.h"
+#include "ether-addr-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "main-func.h"
+#include "ndisc-option.h"
+#include "netlink-util.h"
+#include "network-common.h"
+#include "parse-util.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "time-util.h"
+
+static int arg_ifindex = 0;
+static int arg_icmp6_type = 0;
+static union in_addr_union arg_dest = IN_ADDR_NULL;
+static uint8_t arg_hop_limit = 0;
+static uint8_t arg_ra_flags = 0;
+static uint8_t arg_preference = false;
+static usec_t arg_lifetime = 0;
+static usec_t arg_reachable = 0;
+static usec_t arg_retransmit = 0;
+static uint32_t arg_na_flags = 0;
+static union in_addr_union arg_target_address = IN_ADDR_NULL;
+static union in_addr_union arg_redirect_destination = IN_ADDR_NULL;
+static bool arg_set_source_mac = false;
+static struct ether_addr arg_source_mac = {};
+static bool arg_set_target_mac = false;
+static struct ether_addr arg_target_mac = {};
+static struct ip6_hdr *arg_redirected_header = NULL;
+static bool arg_set_mtu = false;
+static uint32_t arg_mtu = 0;
+
+STATIC_DESTRUCTOR_REGISTER(arg_redirected_header, freep);
+
+static int parse_icmp6_type(const char *str) {
+ if (STR_IN_SET(str, "router-solicit", "rs", "RS"))
+ return ND_ROUTER_SOLICIT;
+ if (STR_IN_SET(str, "router-advertisement", "ra", "RA"))
+ return ND_ROUTER_ADVERT;
+ if (STR_IN_SET(str, "neighbor-solicit", "ns", "NS"))
+ return ND_NEIGHBOR_SOLICIT;
+ if (STR_IN_SET(str, "neighbor-advertisement", "na", "NA"))
+ return ND_NEIGHBOR_ADVERT;
+ if (STR_IN_SET(str, "redirect", "rd", "RD"))
+ return ND_REDIRECT;
+ return -EINVAL;
+}
+
+static int parse_preference(const char *str) {
+ if (streq(str, "low"))
+ return SD_NDISC_PREFERENCE_LOW;
+ if (streq(str, "medium"))
+ return SD_NDISC_PREFERENCE_MEDIUM;
+ if (streq(str, "high"))
+ return SD_NDISC_PREFERENCE_HIGH;
+ if (streq(str, "reserved"))
+ return SD_NDISC_PREFERENCE_RESERVED;
+ return -EINVAL;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ ARG_RA_HOP_LIMIT,
+ ARG_RA_MANAGED,
+ ARG_RA_OTHER,
+ ARG_RA_HOME_AGENT,
+ ARG_RA_PREFERENCE,
+ ARG_RA_LIFETIME,
+ ARG_RA_REACHABLE,
+ ARG_RA_RETRANSMIT,
+ ARG_NA_ROUTER,
+ ARG_NA_SOLICITED,
+ ARG_NA_OVERRIDE,
+ ARG_TARGET_ADDRESS,
+ ARG_REDIRECT_DESTINATION,
+ ARG_OPTION_SOURCE_LL,
+ ARG_OPTION_TARGET_LL,
+ ARG_OPTION_REDIRECTED_HEADER,
+ ARG_OPTION_MTU,
+ };
+
+ static const struct option options[] = {
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "interface", required_argument, NULL, 'i' },
+ { "type", required_argument, NULL, 't' },
+ { "dest", required_argument, NULL, 'd' },
+ /* For Router Advertisement */
+ { "hop-limit", required_argument, NULL, ARG_RA_HOP_LIMIT },
+ { "managed", required_argument, NULL, ARG_RA_MANAGED },
+ { "other", required_argument, NULL, ARG_RA_OTHER },
+ { "home-agent", required_argument, NULL, ARG_RA_HOME_AGENT },
+ { "preference", required_argument, NULL, ARG_RA_PREFERENCE },
+ { "lifetime", required_argument, NULL, ARG_RA_LIFETIME },
+ { "reachable-time", required_argument, NULL, ARG_RA_REACHABLE },
+ { "retransmit-timer", required_argument, NULL, ARG_RA_RETRANSMIT },
+ /* For Neighbor Advertisement */
+ { "is-router", required_argument, NULL, ARG_NA_ROUTER },
+ { "is-solicited", required_argument, NULL, ARG_NA_SOLICITED },
+ { "is-override", required_argument, NULL, ARG_NA_OVERRIDE },
+ /* For Neighbor Solicit, Neighbor Advertisement, and Redirect */
+ { "target-address", required_argument, NULL, ARG_TARGET_ADDRESS },
+ /* For Redirect */
+ { "redirect-destination", required_argument, NULL, ARG_REDIRECT_DESTINATION },
+ /* Options */
+ { "source-ll-address", required_argument, NULL, ARG_OPTION_SOURCE_LL },
+ { "target-ll-address", required_argument, NULL, ARG_OPTION_TARGET_LL },
+ { "redirected-header", required_argument, NULL, ARG_OPTION_REDIRECTED_HEADER },
+ { "mtu", required_argument, NULL, ARG_OPTION_MTU },
+ {}
+ };
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ int c, r;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "i:t:d:", options, NULL)) >= 0) {
+
+ switch (c) {
+
+ case ARG_VERSION:
+ return version();
+
+ case 'i':
+ r = rtnl_resolve_interface_or_warn(&rtnl, optarg);
+ if (r < 0)
+ return r;
+ arg_ifindex = r;
+ break;
+
+ case 't':
+ r = parse_icmp6_type(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse message type: %m");
+ arg_icmp6_type = r;
+ break;
+
+ case 'd':
+ r = in_addr_from_string(AF_INET6, optarg, &arg_dest);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse destination address: %m");
+ if (!in6_addr_is_link_local(&arg_dest.in6))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "The destination address %s is not a link-local address.", optarg);
+ break;
+
+ case ARG_RA_HOP_LIMIT:
+ r = safe_atou8(optarg, &arg_hop_limit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse hop limit: %m");
+ break;
+
+ case ARG_RA_MANAGED:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse managed flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_MANAGED, r);
+ break;
+
+ case ARG_RA_OTHER:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse other flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_OTHER, r);
+ break;
+
+ case ARG_RA_HOME_AGENT:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse home-agent flag: %m");
+ SET_FLAG(arg_ra_flags, ND_RA_FLAG_HOME_AGENT, r);
+ break;
+
+ case ARG_RA_PREFERENCE:
+ r = parse_preference(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse preference: %m");
+ arg_preference = r;
+ break;
+
+ case ARG_RA_LIFETIME:
+ r = parse_sec(optarg, &arg_lifetime);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse lifetime: %m");
+ break;
+
+ case ARG_RA_REACHABLE:
+ r = parse_sec(optarg, &arg_reachable);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse reachable time: %m");
+ break;
+
+ case ARG_RA_RETRANSMIT:
+ r = parse_sec(optarg, &arg_retransmit);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse retransmit timer: %m");
+ break;
+
+ case ARG_NA_ROUTER:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-router flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_ROUTER, r);
+ break;
+
+ case ARG_NA_SOLICITED:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-solicited flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_SOLICITED, r);
+ break;
+
+ case ARG_NA_OVERRIDE:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse is-override flag: %m");
+ SET_FLAG(arg_na_flags, ND_NA_FLAG_OVERRIDE, r);
+ break;
+
+ case ARG_TARGET_ADDRESS:
+ r = in_addr_from_string(AF_INET6, optarg, &arg_target_address);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse target address: %m");
+ break;
+
+ case ARG_REDIRECT_DESTINATION:
+ r = in_addr_from_string(AF_INET6, optarg, &arg_redirect_destination);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse destination address: %m");
+ break;
+
+ case ARG_OPTION_SOURCE_LL:
+ r = parse_boolean(optarg);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse source LL address option: %m");
+ arg_set_source_mac = r;
+ break;
+
+ case ARG_OPTION_TARGET_LL:
+ r = parse_ether_addr(optarg, &arg_target_mac);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse target LL address option: %m");
+ arg_set_target_mac = true;
+ break;
+
+ case ARG_OPTION_REDIRECTED_HEADER: {
+ _cleanup_free_ void *p = NULL;
+ size_t len;
+
+ r = unbase64mem(optarg, &p, &len);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse redirected header: %m");
+
+ if (len < sizeof(struct ip6_hdr))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid redirected header.");
+
+ arg_redirected_header = TAKE_PTR(p);
+ break;
+ }
+ case ARG_OPTION_MTU:
+ r = safe_atou32(optarg, &arg_mtu);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse MTU: %m");
+ arg_set_mtu = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+ }
+
+ if (arg_ifindex <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--interface/-i option is mandatory.");
+
+ if (arg_icmp6_type <= 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--type/-t option is mandatory.");
+
+ if (in6_addr_is_null(&arg_dest.in6)) {
+ if (IN_SET(arg_icmp6_type, ND_ROUTER_ADVERT, ND_NEIGHBOR_ADVERT, ND_REDIRECT))
+ arg_dest.in6 = IN6_ADDR_ALL_NODES_MULTICAST;
+ else
+ arg_dest.in6 = IN6_ADDR_ALL_ROUTERS_MULTICAST;
+ }
+
+ if (arg_set_source_mac) {
+ struct hw_addr_data hw_addr;
+
+ r = rtnl_get_link_info(&rtnl, arg_ifindex,
+ /* ret_iftype = */ NULL,
+ /* ret_flags = */ NULL,
+ /* ret_kind = */ NULL,
+ &hw_addr,
+ /* ret_permanent_hw_addr = */ NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get the source link-layer address: %m");
+
+ if (hw_addr.length != sizeof(struct ether_addr))
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported hardware address length %zu: %m",
+ hw_addr.length);
+
+ arg_source_mac = hw_addr.ether;
+ }
+
+ return 1;
+}
+
+static int send_icmp6(int fd, const struct icmp6_hdr *hdr) {
+ _cleanup_set_free_ Set *options = NULL;
+ int r;
+
+ assert(fd >= 0);
+ assert(hdr);
+
+ if (arg_set_source_mac) {
+ r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_SOURCE_LL_ADDRESS, &arg_source_mac);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_set_target_mac) {
+ r = ndisc_option_add_link_layer_address(&options, 0, SD_NDISC_OPTION_TARGET_LL_ADDRESS, &arg_target_mac);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_redirected_header) {
+ r = ndisc_option_add_redirected_header(&options, 0, arg_redirected_header);
+ if (r < 0)
+ return r;
+ }
+
+ if (arg_set_mtu) {
+ r = ndisc_option_add_mtu(&options, 0, arg_mtu);
+ if (r < 0)
+ return r;
+ }
+
+ return ndisc_send(fd, &arg_dest.in6, hdr, options, now(CLOCK_BOOTTIME));
+}
+
+static int send_router_solicit(int fd) {
+ struct nd_router_solicit hdr = {
+ .nd_rs_type = ND_ROUTER_SOLICIT,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_rs_hdr);
+}
+
+static int send_router_advertisement(int fd) {
+ struct nd_router_advert hdr = {
+ .nd_ra_type = ND_ROUTER_ADVERT,
+ .nd_ra_router_lifetime = usec_to_be16_sec(arg_lifetime),
+ .nd_ra_reachable = usec_to_be32_msec(arg_reachable),
+ .nd_ra_retransmit = usec_to_be32_msec(arg_retransmit),
+ };
+
+ assert(fd >= 0);
+
+ /* The nd_ra_curhoplimit and nd_ra_flags_reserved fields cannot specified with nd_ra_router_lifetime
+ * simultaneously in the structured initializer in the above. */
+ hdr.nd_ra_curhoplimit = arg_hop_limit;
+ hdr.nd_ra_flags_reserved = arg_ra_flags;
+
+ return send_icmp6(fd, &hdr.nd_ra_hdr);
+}
+
+static int send_neighbor_solicit(int fd) {
+ struct nd_neighbor_solicit hdr = {
+ .nd_ns_type = ND_NEIGHBOR_SOLICIT,
+ .nd_ns_target = arg_target_address.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_ns_hdr);
+}
+
+static int send_neighbor_advertisement(int fd) {
+ struct nd_neighbor_advert hdr = {
+ .nd_na_type = ND_NEIGHBOR_ADVERT,
+ .nd_na_flags_reserved = arg_na_flags,
+ .nd_na_target = arg_target_address.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_na_hdr);
+}
+
+static int send_redirect(int fd) {
+ struct nd_redirect hdr = {
+ .nd_rd_type = ND_REDIRECT,
+ .nd_rd_target = arg_target_address.in6,
+ .nd_rd_dst = arg_redirect_destination.in6,
+ };
+
+ assert(fd >= 0);
+
+ return send_icmp6(fd, &hdr.nd_rd_hdr);
+}
+
+static int run(int argc, char *argv[]) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ log_setup();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
+
+ fd = icmp6_bind(arg_ifindex, /* is_router = */ false);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to bind socket to interface: %m");
+
+ switch (arg_icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ return send_router_solicit(fd);
+ case ND_ROUTER_ADVERT:
+ return send_router_advertisement(fd);
+ case ND_NEIGHBOR_SOLICIT:
+ return send_neighbor_solicit(fd);
+ case ND_NEIGHBOR_ADVERT:
+ return send_neighbor_advertisement(fd);
+ case ND_REDIRECT:
+ return send_redirect(fd);
+ default:
+ assert_not_reached();
+ }
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 4113920..78b4453 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -834,3 +834,12 @@ global:
sd_id128_get_app_specific;
sd_device_enumerator_add_match_property_required;
} LIBSYSTEMD_254;
+
+LIBSYSTEMD_256 {
+global:
+ sd_bus_creds_get_pidfd_dup;
+ sd_bus_creds_new_from_pidfd;
+ sd_id128_get_invocation_app_specific;
+ sd_journal_stream_fd_with_namespace;
+ sd_event_source_get_inotify_path;
+} LIBSYSTEMD_255;
diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build
index 5d18f97..6d4337d 100644
--- a/src/libsystemd/meson.build
+++ b/src/libsystemd/meson.build
@@ -118,8 +118,7 @@ libsystemd_static = static_library(
libsystemd_sources,
include_directories : libsystemd_includes,
c_args : libsystemd_c_args,
- link_with : [libbasic,
- libbasic_compress],
+ link_with : [libbasic],
dependencies : [threads,
librt,
userspace],
@@ -159,6 +158,10 @@ libsystemd_tests += [
'sources' : files('sd-journal/test-journal-enum.c'),
'timeout' : 360,
},
+ {
+ 'sources' : files('sd-event/test-event.c'),
+ 'timeout' : 120,
+ }
]
############################################################
@@ -171,7 +174,6 @@ simple_tests += files(
'sd-device/test-device-util.c',
'sd-device/test-sd-device-monitor.c',
'sd-device/test-sd-device.c',
- 'sd-event/test-event.c',
'sd-journal/test-journal-flush.c',
'sd-journal/test-journal-interleaving.c',
'sd-journal/test-journal-stream.c',
diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c
index df26fd7..de12ec5 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.c
+++ b/src/libsystemd/sd-bus/bus-common-errors.c
@@ -105,11 +105,13 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NO_HARDWARE_SERIAL, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_FILE_IS_PROTECTED, EACCES),
SD_BUS_ERROR_MAP(BUS_ERROR_READ_ONLY_FILESYSTEM, EROFS),
SD_BUS_ERROR_MAP(BUS_ERROR_SPEED_METER_INACTIVE, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_UNMANAGED_INTERFACE, EOPNOTSUPP),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NETWORK_ALREADY_RELOADING, EBUSY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_HOME, EEXIST),
SD_BUS_ERROR_MAP(BUS_ERROR_UID_IN_USE, EEXIST),
@@ -146,6 +148,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_REFERENCED, EBADR),
SD_BUS_ERROR_MAP_END
};
diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h
index 3a0eef4..94dc85d 100644
--- a/src/libsystemd/sd-bus/bus-common-errors.h
+++ b/src/libsystemd/sd-bus/bus-common-errors.h
@@ -106,11 +106,13 @@
#define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress"
#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID"
+#define BUS_ERROR_NO_HARDWARE_SERIAL "org.freedesktop.hostname1.NoHardwareSerial"
#define BUS_ERROR_FILE_IS_PROTECTED "org.freedesktop.hostname1.FileIsProtected"
#define BUS_ERROR_READ_ONLY_FILESYSTEM "org.freedesktop.hostname1.ReadOnlyFilesystem"
#define BUS_ERROR_SPEED_METER_INACTIVE "org.freedesktop.network1.SpeedMeterInactive"
#define BUS_ERROR_UNMANAGED_INTERFACE "org.freedesktop.network1.UnmanagedInterface"
+#define BUS_ERROR_NETWORK_ALREADY_RELOADING "org.freedesktop.network1.AlreadyReloading"
#define BUS_ERROR_NO_SUCH_HOME "org.freedesktop.home1.NoSuchHome"
#define BUS_ERROR_UID_IN_USE "org.freedesktop.home1.UIDInUse"
@@ -151,5 +153,6 @@
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
#define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded"
+#define BUS_ERROR_HOME_NOT_REFERENCED "org.freedesktop.home1.HomeNotReferenced"
BUS_ERROR_MAP_ELF_USE(bus_common_errors);
diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c
index 4146a6e..2eca82b 100644
--- a/src/libsystemd/sd-bus/bus-container.c
+++ b/src/libsystemd/sd-bus/bus-container.c
@@ -34,7 +34,7 @@ int bus_container_connect_socket(sd_bus *b) {
log_debug("sd-bus: connecting bus%s%s to namespace of PID "PID_FMT"...",
b->description ? " " : "", strempty(b->description), b->nspid);
- r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
+ r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, &usernsfd, &rootfd);
if (r < 0)
return log_debug_errno(r, "Failed to open namespace of PID "PID_FMT": %m", b->nspid);
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index 1355e41..c420584 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -14,6 +14,7 @@
#include "bus-internal.h"
#include "bus-message.h"
#include "capability-util.h"
+#include "fd-util.h"
#include "process-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -430,7 +431,6 @@ _public_ int sd_bus_get_name_creds(
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply_unique = NULL, *reply = NULL;
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
const char *unique;
- pid_t pid = 0;
int r;
assert_return(bus, -EINVAL);
@@ -483,8 +483,9 @@ _public_ int sd_bus_get_name_creds(
}
if (mask != 0) {
+ bool need_pid, need_uid, need_gids, need_selinux, need_separate_calls, need_pidfd, need_augment;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- bool need_pid, need_uid, need_selinux, need_separate_calls;
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
c = bus_creds_new();
if (!c)
@@ -498,20 +499,25 @@ _public_ int sd_bus_get_name_creds(
c->mask |= SD_BUS_CREDS_UNIQUE_NAME;
}
- need_pid = (mask & SD_BUS_CREDS_PID) ||
- ((mask & SD_BUS_CREDS_AUGMENT) &&
- (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
- SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
- SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
- SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
- SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
- SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
- SD_BUS_CREDS_SELINUX_CONTEXT|
- SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)));
+ need_augment =
+ (mask & SD_BUS_CREDS_AUGMENT) &&
+ (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID|
+ SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID|
+ SD_BUS_CREDS_SUPPLEMENTARY_GIDS|
+ SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE|
+ SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|
+ SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS|
+ SD_BUS_CREDS_SELINUX_CONTEXT|
+ SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID|
+ SD_BUS_CREDS_PIDFD));
+
+ need_pid = (mask & SD_BUS_CREDS_PID) || need_augment;
need_uid = mask & SD_BUS_CREDS_EUID;
+ need_gids = mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT;
+ need_pidfd = (mask & SD_BUS_CREDS_PIDFD) || need_augment;
- if (need_pid + need_uid + need_selinux > 1) {
+ if (need_pid + need_uid + need_selinux + need_pidfd + need_gids > 1) {
/* If we need more than one of the credentials, then use GetConnectionCredentials() */
@@ -572,7 +578,9 @@ _public_ int sd_bus_get_name_creds(
if (r < 0)
return r;
- pid = p;
+ if (!pidref_is_set(&pidref))
+ pidref = PIDREF_MAKE_FROM_PID(p);
+
if (mask & SD_BUS_CREDS_PID) {
c->pid = p;
c->mask |= SD_BUS_CREDS_PID;
@@ -599,6 +607,69 @@ _public_ int sd_bus_get_name_creds(
r = sd_bus_message_exit_container(reply);
if (r < 0)
return r;
+ } else if (need_pidfd && streq(m, "ProcessFD")) {
+ int fd;
+
+ r = sd_bus_message_read(reply, "v", "h", &fd);
+ if (r < 0)
+ return r;
+
+ pidref_done(&pidref);
+ r = pidref_set_pidfd(&pidref, fd);
+ if (r < 0)
+ return r;
+
+ if (mask & SD_BUS_CREDS_PIDFD) {
+ fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+ if (fd < 0)
+ return -errno;
+
+ close_and_replace(c->pidfd, fd);
+ c->mask |= SD_BUS_CREDS_PIDFD;
+ }
+ } else if (need_gids && streq(m, "UnixGroupIDs")) {
+
+ /* Note that D-Bus actually only gives us a combined list of
+ * primary gid and supplementary gids. And we don't know
+ * which one the primary one is. We'll take the whole shebang
+ * hence and use it as the supplementary group list, and not
+ * initialize the primary gid field. This is slightly
+ * incorrect of course, but only slightly, as in effect if
+ * the primary gid is also listed in the supplementary gid
+ * it has zero effect. */
+
+ r = sd_bus_message_enter_container(reply, 'v', "au");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(reply, 'a', "u");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ uint32_t u;
+
+ r = sd_bus_message_read(reply, "u", &u);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (!GREEDY_REALLOC(c->supplementary_gids, c->n_supplementary_gids+1))
+ return -ENOMEM;
+
+ c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) u;
+ }
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_exit_container(reply);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS;
} else {
r = sd_bus_message_skip(reply, "v");
if (r < 0)
@@ -614,7 +685,7 @@ _public_ int sd_bus_get_name_creds(
if (r < 0)
return r;
- if (need_pid && pid == 0)
+ if (need_pid && !pidref_is_set(&pidref))
return -EPROTO;
}
@@ -642,7 +713,9 @@ _public_ int sd_bus_get_name_creds(
if (r < 0)
return r;
- pid = u;
+ if (!pidref_is_set(&pidref))
+ pidref = PIDREF_MAKE_FROM_PID(u);
+
if (mask & SD_BUS_CREDS_PID) {
c->pid = u;
c->mask |= SD_BUS_CREDS_PID;
@@ -710,9 +783,11 @@ _public_ int sd_bus_get_name_creds(
}
}
- r = bus_creds_add_more(c, mask, pid, 0);
- if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */
- return r;
+ if (pidref_is_set(&pidref)) {
+ r = bus_creds_add_more(c, mask, &pidref, 0);
+ if (r < 0 && r != -ESRCH) /* Return the error, but ignore ESRCH which just means the process is already gone */
+ return r;
+ }
}
if (creds)
@@ -765,8 +840,8 @@ not_found:
_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
- bool do_label, do_groups, do_sockaddr_peer;
- pid_t pid = 0;
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ bool do_label, do_groups, do_sockaddr_peer, do_pidfd;
int r;
assert_return(bus, -EINVAL);
@@ -786,9 +861,10 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
do_sockaddr_peer = bus->sockaddr_size_peer >= offsetof(struct sockaddr_un, sun_path) + 1 &&
bus->sockaddr_peer.sa.sa_family == AF_UNIX &&
bus->sockaddr_peer.un.sun_path[0] == 0;
+ do_pidfd = bus->pidfd >= 0 && (mask & SD_BUS_CREDS_PIDFD);
/* Avoid allocating anything if we have no chance of returning useful data */
- if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer)
+ if (!bus->ucred_valid && !do_label && !do_groups && !do_sockaddr_peer && !do_pidfd)
return -ENODATA;
c = bus_creds_new();
@@ -797,8 +873,10 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
if (bus->ucred_valid) {
if (pid_is_valid(bus->ucred.pid)) {
- pid = c->pid = bus->ucred.pid;
+ c->pid = bus->ucred.pid;
c->mask |= SD_BUS_CREDS_PID & mask;
+
+ pidref = PIDREF_MAKE_FROM_PID(c->pid);
}
if (uid_is_valid(bus->ucred.uid)) {
@@ -859,7 +937,20 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
}
}
- r = bus_creds_add_more(c, mask, pid, 0);
+ if (do_pidfd) {
+ c->pidfd = fcntl(bus->pidfd, F_DUPFD_CLOEXEC, 3);
+ if (c->pidfd < 0)
+ return -errno;
+
+ pidref_done(&pidref);
+ r = pidref_set_pidfd(&pidref, bus->pidfd);
+ if (r < 0)
+ return r;
+
+ c->mask |= SD_BUS_CREDS_PIDFD;
+ }
+
+ r = bus_creds_add_more(c, mask, &pidref, 0);
if (r < 0 && r != -ESRCH) /* If the process vanished, then don't complain, just return what we got */
return r;
diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c
index 989e577..14d8073 100644
--- a/src/libsystemd/sd-bus/bus-convenience.c
+++ b/src/libsystemd/sd-bus/bus-convenience.c
@@ -640,8 +640,8 @@ _public_ int sd_bus_set_property(
}
_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **ret) {
+ uint64_t missing;
sd_bus_creds *c;
- int r;
assert_return(call, -EINVAL);
assert_return(call->sealed, -EPERM);
@@ -653,36 +653,22 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b
return -ENOTCONN;
c = sd_bus_message_get_creds(call);
-
- /* All data we need? */
- if (c && (mask & ~SD_BUS_CREDS_AUGMENT & ~c->mask) == 0) {
+ if (c)
+ missing = mask & ~SD_BUS_CREDS_AUGMENT & ~c->mask;
+ else
+ missing = mask & ~SD_BUS_CREDS_AUGMENT;
+ if (missing == 0) { /* All data we need? */
*ret = sd_bus_creds_ref(c);
return 0;
}
- /* No data passed? Or not enough data passed to retrieve the missing bits? */
- if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
- /* We couldn't read anything from the call, let's try
- * to get it from the sender or peer. */
-
- if (call->sender)
- /* There's a sender, but the creds are missing. */
- return sd_bus_get_name_creds(call->bus, call->sender, mask, ret);
- else
- /* There's no sender. For direct connections
- * the credentials of the AF_UNIX peer matter,
- * which may be queried via sd_bus_get_owner_creds(). */
- return sd_bus_get_owner_creds(call->bus, mask, ret);
- }
-
- r = bus_creds_extend_by_pid(c, mask, ret);
- if (r == -ESRCH) {
- /* Process doesn't exist anymore? propagate the few things we have */
- *ret = sd_bus_creds_ref(c);
- return 0;
- }
+ /* There's a sender, use that */
+ if (call->sender && call->bus->bus_client)
+ return sd_bus_get_name_creds(call->bus, call->sender, mask, ret);
- return r;
+ /* There's no sender. For direct connections the credentials of the AF_UNIX peer matter, which may be
+ * queried via sd_bus_get_owner_creds(). */
+ return sd_bus_get_owner_creds(call->bus, mask, ret);
}
_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index c6d8caa..adbdeaa 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -53,6 +53,8 @@ void bus_creds_done(sd_bus_creds *c) {
* below. */
strv_free(c->cmdline_array);
+
+ safe_close(c->pidfd);
}
_public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
@@ -129,46 +131,72 @@ _public_ uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c) {
sd_bus_creds* bus_creds_new(void) {
sd_bus_creds *c;
- c = new0(sd_bus_creds, 1);
+ c = new(sd_bus_creds, 1);
if (!c)
return NULL;
- c->allocated = true;
- c->n_ref = 1;
+ *c = (sd_bus_creds) {
+ .allocated = true,
+ .n_ref = 1,
+ SD_BUS_CREDS_INIT_FIELDS,
+ };
+
return c;
}
-_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) {
+static int bus_creds_new_from_pidref(sd_bus_creds **ret, PidRef *pidref, uint64_t mask) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL;
int r;
- assert_return(pid >= 0, -EINVAL);
assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
assert_return(ret, -EINVAL);
- if (pid == 0)
- pid = getpid_cached();
-
c = bus_creds_new();
if (!c)
return -ENOMEM;
- r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0);
+ r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pidref, 0);
if (r < 0)
return r;
- /* Check if the process existed at all, in case we haven't
- * figured that out already */
- r = pid_is_alive(pid);
+ r = pidref_verify(pidref);
if (r < 0)
return r;
- if (r == 0)
- return -ESRCH;
*ret = TAKE_PTR(c);
return 0;
}
+_public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t mask) {
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ int r;
+
+ assert_return(pid >= 0, -EINVAL);
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
+ assert_return(ret, -EINVAL);
+
+ r = pidref_set_pid(&pidref, pid);
+ if (r < 0)
+ return r;
+
+ return bus_creds_new_from_pidref(ret, &pidref, mask);
+}
+
+_public_ int sd_bus_creds_new_from_pidfd(sd_bus_creds **ret, int pidfd, uint64_t mask) {
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+ int r;
+
+ assert_return(mask <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP);
+ assert_return(ret, -EINVAL);
+ assert_return(pidfd >= 0, -EBADF);
+
+ r = pidref_set_pidfd(&pidref, pidfd);
+ if (r < 0)
+ return r;
+
+ return bus_creds_new_from_pidref(ret, &pidref, mask);
+}
+
_public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) {
assert_return(c, -EINVAL);
assert_return(uid, -EINVAL);
@@ -280,6 +308,23 @@ _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
return 0;
}
+_public_ int sd_bus_creds_get_pidfd_dup(sd_bus_creds *c, int *ret_fd) {
+ _cleanup_close_ int copy = -EBADF;
+
+ assert_return(c, -EINVAL);
+ assert_return(ret_fd, -EINVAL);
+
+ if (!(c->mask & SD_BUS_CREDS_PIDFD))
+ return -ENODATA;
+
+ copy = fcntl(c->pidfd, F_DUPFD_CLOEXEC, 3);
+ if (copy < 0)
+ return -errno;
+
+ *ret_fd = TAKE_FD(copy);
+ return 0;
+}
+
_public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) {
assert_return(c, -EINVAL);
assert_return(ppid, -EINVAL);
@@ -731,7 +776,7 @@ static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
return -ENOMEM;
}
- for (i = 0; i < sz; i ++) {
+ for (i = 0; i < sz; i++) {
uint32_t v = 0;
for (j = 0; j < 8; ++j) {
@@ -750,7 +795,8 @@ static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
return 0;
}
-int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
+int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, PidRef *pidref, pid_t tid) {
+ _cleanup_(pidref_done) PidRef pidref_buf = PIDREF_NULL;
uint64_t missing;
int r;
@@ -761,12 +807,26 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
return 0;
/* Try to retrieve PID from creds if it wasn't passed to us */
- if (pid > 0) {
- c->pid = pid;
+ if (pidref_is_set(pidref)) {
+ if ((c->mask & SD_BUS_CREDS_PID) && c->pid != pidref->pid) /* Insist that things match if already set */
+ return -EBUSY;
+
+ c->pid = pidref->pid;
c->mask |= SD_BUS_CREDS_PID;
- } else if (c->mask & SD_BUS_CREDS_PID)
- pid = c->pid;
- else
+ } else if (c->mask & SD_BUS_CREDS_PIDFD) {
+ r = pidref_set_pidfd(&pidref_buf, c->pidfd);
+ if (r < 0)
+ return r;
+
+ pidref = &pidref_buf;
+
+ } else if (c->mask & SD_BUS_CREDS_PID) {
+ r = pidref_set_pid(&pidref_buf, c->pid);
+ if (r < 0)
+ return r;
+
+ pidref = &pidref_buf;
+ } else
/* Without pid we cannot do much... */
return 0;
@@ -784,6 +844,14 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
c->mask |= SD_BUS_CREDS_TID;
}
+ if ((missing & SD_BUS_CREDS_PIDFD) && pidref->fd >= 0) {
+ c->pidfd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
+ if (c->pidfd < 0)
+ return -errno;
+
+ c->mask |= SD_BUS_CREDS_PIDFD;
+ }
+
if (missing & (SD_BUS_CREDS_PPID |
SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID |
SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID |
@@ -794,13 +862,13 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
_cleanup_fclose_ FILE *f = NULL;
const char *p;
- p = procfs_file_alloca(pid, "status");
+ p = procfs_file_alloca(pidref->pid, "status");
f = fopen(p, "re");
if (!f) {
if (errno == ENOENT)
return -ESRCH;
- else if (!ERRNO_IS_PRIVILEGE(errno))
+ if (!ERRNO_IS_PRIVILEGE(errno))
return -errno;
} else {
@@ -958,7 +1026,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) {
const char *p;
- p = procfs_file_alloca(pid, "attr/current");
+ p = procfs_file_alloca(pidref->pid, "attr/current");
r = read_one_line_file(p, &c->label);
if (r < 0) {
if (!IN_SET(r, -ENOENT, -EINVAL, -EPERM, -EACCES))
@@ -968,7 +1036,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
if (missing & SD_BUS_CREDS_COMM) {
- r = pid_get_comm(pid, &c->comm);
+ r = pid_get_comm(pidref->pid, &c->comm);
if (r < 0) {
if (!ERRNO_IS_PRIVILEGE(r))
return r;
@@ -977,7 +1045,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
if (missing & SD_BUS_CREDS_EXE) {
- r = get_process_exe(pid, &c->exe);
+ r = get_process_exe(pidref->pid, &c->exe);
if (r == -ESRCH) {
/* Unfortunately we cannot really distinguish
* the case here where the process does not
@@ -998,7 +1066,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_CMDLINE) {
const char *p;
- p = procfs_file_alloca(pid, "cmdline");
+ p = procfs_file_alloca(pidref->pid, "cmdline");
r = read_full_virtual_file(p, &c->cmdline, &c->cmdline_size);
if (r == -ENOENT)
return -ESRCH;
@@ -1016,7 +1084,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) {
_cleanup_free_ char *p = NULL;
- if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pid, tid) < 0)
+ if (asprintf(&p, "/proc/"PID_FMT"/task/"PID_FMT"/comm", pidref->pid, tid) < 0)
return -ENOMEM;
r = read_one_line_file(p, &c->tid_comm);
@@ -1032,7 +1100,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_USER_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
if (!c->cgroup) {
- r = cg_pid_get_path(NULL, pid, &c->cgroup);
+ r = cg_pid_get_path(NULL, pidref->pid, &c->cgroup);
if (r < 0) {
if (!ERRNO_IS_PRIVILEGE(r))
return r;
@@ -1050,7 +1118,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
- r = audit_session_from_pid(pid, &c->audit_session_id);
+ r = audit_session_from_pid(pidref->pid, &c->audit_session_id);
if (r == -ENODATA) {
/* ENODATA means: no audit session id assigned */
c->audit_session_id = AUDIT_SESSION_INVALID;
@@ -1063,7 +1131,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
- r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
+ r = audit_loginuid_from_pid(pidref->pid, &c->audit_login_uid);
if (r == -ENODATA) {
/* ENODATA means: no audit login uid assigned */
c->audit_login_uid = UID_INVALID;
@@ -1076,7 +1144,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
if (missing & SD_BUS_CREDS_TTY) {
- r = get_ctty(pid, NULL, &c->tty);
+ r = get_ctty(pidref->pid, NULL, &c->tty);
if (r == -ENXIO) {
/* ENXIO means: process has no controlling TTY */
c->tty = NULL;
@@ -1088,16 +1156,12 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
c->mask |= SD_BUS_CREDS_TTY;
}
- /* In case only the exe path was to be read we cannot distinguish the case where the exe path was
- * unreadable because the process was a kernel thread, or when the process didn't exist at
- * all. Hence, let's do a final check, to be sure. */
- r = pid_is_alive(pid);
+ r = pidref_verify(pidref);
if (r < 0)
return r;
- if (r == 0)
- return -ESRCH;
- if (tid > 0 && tid != pid && pid_is_unwaited(tid) == 0)
+ /* Validate tid is still valid, too */
+ if (tid > 0 && tid != pidref->pid && pid_is_unwaited(tid) == 0)
return -ESRCH;
c->augmented = missing & c->mask;
@@ -1131,6 +1195,13 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
n->mask |= SD_BUS_CREDS_PID;
}
+ if (c->mask & mask & SD_BUS_CREDS_PIDFD) {
+ n->pidfd = fcntl(c->pidfd, F_DUPFD_CLOEXEC, 3);
+ if (n->pidfd < 0)
+ return -errno;
+ n->mask |= SD_BUS_CREDS_PIDFD;
+ }
+
if (c->mask & mask & SD_BUS_CREDS_TID) {
n->tid = c->tid;
n->mask |= SD_BUS_CREDS_TID;
diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h
index 7806d9e..f45de1c 100644
--- a/src/libsystemd/sd-bus/bus-creds.h
+++ b/src/libsystemd/sd-bus/bus-creds.h
@@ -5,6 +5,9 @@
#include "sd-bus.h"
+#include "pidref.h"
+#include "user-util.h"
+
struct sd_bus_creds {
bool allocated;
unsigned n_ref;
@@ -27,6 +30,7 @@ struct sd_bus_creds {
pid_t ppid;
pid_t pid;
pid_t tid;
+ int pidfd;
char *comm;
char *tid_comm;
@@ -63,10 +67,22 @@ struct sd_bus_creds {
char *description, *unescaped_description;
};
+#define SD_BUS_CREDS_INIT_FIELDS \
+ .uid = UID_INVALID, \
+ .euid = UID_INVALID, \
+ .suid = UID_INVALID, \
+ .fsuid = UID_INVALID, \
+ .gid = GID_INVALID, \
+ .egid = GID_INVALID, \
+ .sgid = GID_INVALID, \
+ .fsgid = GID_INVALID, \
+ .pidfd = -EBADF, \
+ .audit_login_uid = UID_INVALID
+
sd_bus_creds* bus_creds_new(void);
void bus_creds_done(sd_bus_creds *c);
-int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid);
+int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, PidRef *pidref, pid_t tid);
int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret);
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index 6d24f3b..aa46fec 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -355,6 +355,8 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) {
if (c->mask & SD_BUS_CREDS_PID)
fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix);
+ if (c->mask & SD_BUS_CREDS_PIDFD)
+ fprintf(f, "%sPIDFD=%syes%s", prefix, color, suffix);
if (c->mask & SD_BUS_CREDS_TID)
fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix);
if (c->mask & SD_BUS_CREDS_PPID) {
diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h
index 098a518..e0f4746 100644
--- a/src/libsystemd/sd-bus/bus-internal.h
+++ b/src/libsystemd/sd-bus/bus-internal.h
@@ -254,6 +254,9 @@ struct sd_bus {
char *address;
unsigned address_index;
+ uid_t connect_as_uid;
+ gid_t connect_as_gid;
+
int last_connect_error;
enum bus_auth auth;
@@ -269,6 +272,7 @@ struct sd_bus {
size_t n_groups;
union sockaddr_union sockaddr_peer;
socklen_t sockaddr_size_peer;
+ int pidfd;
uint64_t creds_mask;
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
index ab8b068..296f450 100644
--- a/src/libsystemd/sd-bus/bus-message.c
+++ b/src/libsystemd/sd-bus/bus-message.c
@@ -373,6 +373,7 @@ static int message_from_header(
if (!m)
return -ENOMEM;
+ m->creds = (sd_bus_creds) { SD_BUS_CREDS_INIT_FIELDS };
m->sealed = true;
m->header = buffer;
@@ -469,6 +470,7 @@ _public_ int sd_bus_message_new(
return -ENOMEM;
t->n_ref = 1;
+ t->creds = (sd_bus_creds) { SD_BUS_CREDS_INIT_FIELDS };
t->bus = sd_bus_ref(bus);
t->header = (struct bus_header*) ((uint8_t*) t + ALIGN(sizeof(struct sd_bus_message)));
t->header->endian = BUS_NATIVE_ENDIAN;
@@ -627,7 +629,7 @@ static int message_new_reply(
return r;
}
- t->dont_send = !!(call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED);
+ t->dont_send = FLAGS_SET(call->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED);
t->enforced_reply_signature = call->enforced_reply_signature;
/* let's copy the sensitive flag over. Let's do that as a safety precaution to keep a transaction
diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c
index c25c40f..e528987 100644
--- a/src/libsystemd/sd-bus/bus-objects.c
+++ b/src/libsystemd/sd-bus/bus-objects.c
@@ -1701,7 +1701,7 @@ static bool names_are_valid(const char *signature, const char **names, names_fla
if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0')
*flags |= NAMES_PRESENT;
- for (;*flags & NAMES_PRESENT;) {
+ while (*flags & NAMES_PRESENT) {
size_t l;
if (!*signature)
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 5ade8e9..07179e0 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -217,7 +217,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) {
/* And possibly check the third line, too */
if (b->accept_fd) {
l = lines[i++];
- b->can_fds = !!memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD");
+ b->can_fds = memory_startswith(l, lines[i] - l, "AGREE_UNIX_FD");
}
assert(i == n);
@@ -266,7 +266,7 @@ static int verify_anonymous_token(sd_bus *b, const char *p, size_t l) {
if (l % 2 != 0)
return 0;
- r = unhexmem(p, l, (void **) &token, &len);
+ r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len);
if (r < 0)
return 0;
@@ -298,7 +298,7 @@ static int verify_external_token(sd_bus *b, const char *p, size_t l) {
if (l % 2 != 0)
return 0;
- r = unhexmem(p, l, (void**) &token, &len);
+ r = unhexmem_full(p, l, /* secure = */ false, (void**) &token, &len);
if (r < 0)
return 0;
@@ -503,11 +503,38 @@ static int bus_socket_write_auth(sd_bus *b) {
if (b->prefer_writev)
k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
else {
+ CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control = {};
+
struct msghdr mh = {
.msg_iov = b->auth_iovec + b->auth_index,
.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index,
};
+ if (uid_is_valid(b->connect_as_uid) || gid_is_valid(b->connect_as_gid)) {
+
+ /* If we shall connect under some specific UID/GID, then synthesize an
+ * SCM_CREDENTIALS record accordingly. After all we want to adopt this UID/GID both
+ * for SO_PEERCRED (where we have to fork()) and SCM_CREDENTIALS (where we can just
+ * fake it via sendmsg()) */
+
+ struct ucred ucred = {
+ .pid = getpid_cached(),
+ .uid = uid_is_valid(b->connect_as_uid) ? b->connect_as_uid : getuid(),
+ .gid = gid_is_valid(b->connect_as_gid) ? b->connect_as_gid : getgid(),
+ };
+
+ mh.msg_control = &control;
+ mh.msg_controllen = sizeof(control);
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
+ *cmsg = (struct cmsghdr) {
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_CREDENTIALS,
+ .cmsg_len = CMSG_LEN(sizeof(struct ucred)),
+ };
+
+ memcpy(CMSG_DATA(cmsg), &ucred, sizeof(struct ucred));
+ }
+
k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
if (k < 0 && errno == ENOTSOCK) {
b->prefer_writev = true;
@@ -643,14 +670,20 @@ static void bus_get_peercred(sd_bus *b) {
/* Get the SELinux context of the peer */
r = getpeersec(b->input_fd, &b->label);
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
- log_debug_errno(r, "Failed to determine peer security context: %m");
+ log_debug_errno(r, "Failed to determine peer security context, ignoring: %m");
/* Get the list of auxiliary groups of the peer */
r = getpeergroups(b->input_fd, &b->groups);
if (r >= 0)
b->n_groups = (size_t) r;
else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT))
- log_debug_errno(r, "Failed to determine peer's group list: %m");
+ log_debug_errno(r, "Failed to determine peer's group list, ignoring: %m");
+
+ r = getpeerpidfd(b->input_fd);
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine peer pidfd, ignoring: %m");
+ else
+ close_and_replace(b->pidfd, r);
/* Let's query the peers socket address, it might carry information such as the peer's comm or
* description string */
@@ -943,6 +976,66 @@ static int bind_description(sd_bus *b, int fd, int family) {
return 0;
}
+static int connect_as(int fd, const struct sockaddr *sa, socklen_t salen, uid_t uid, gid_t gid) {
+ _cleanup_(close_pairp) int pfd[2] = EBADF_PAIR;
+ ssize_t n;
+ int r;
+
+ /* Shortcut if we are not supposed to drop privileges */
+ if (!uid_is_valid(uid) && !gid_is_valid(gid))
+ return RET_NERRNO(connect(fd, sa, salen));
+
+ /* This changes identity to the specified uid/gid and issues connect() as that. This is useful to
+ * make sure SO_PEERCRED reports the selected UID/GID rather than the usual one of the caller. */
+
+ if (pipe2(pfd, O_CLOEXEC) < 0)
+ return -errno;
+
+ r = safe_fork("(sd-setresuid)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, /* ret_pid= */ NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* child */
+
+ pfd[0] = safe_close(pfd[0]);
+
+ r = RET_NERRNO(setgroups(0, NULL));
+ if (r < 0)
+ goto child_finish;
+
+ if (gid_is_valid(gid)) {
+ r = RET_NERRNO(setresgid(gid, gid, gid));
+ if (r < 0)
+ goto child_finish;
+ }
+
+ if (uid_is_valid(uid)) {
+ r = RET_NERRNO(setresuid(uid, uid, uid));
+ if (r < 0)
+ goto child_finish;
+ }
+
+ r = RET_NERRNO(connect(fd, sa, salen));
+ if (r < 0)
+ goto child_finish;
+
+ r = 0;
+
+ child_finish:
+ n = write(pfd[1], &r, sizeof(r));
+ if (n != sizeof(r))
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ n = read(pfd[0], &r, sizeof(r));
+ if (n != sizeof(r))
+ return -EIO;
+
+ return r;
+}
+
int bus_socket_connect(sd_bus *b) {
bool inotify_done = false;
int r;
@@ -974,8 +1067,9 @@ int bus_socket_connect(sd_bus *b) {
b->output_fd = b->input_fd;
bus_socket_setup(b);
- if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) {
- if (errno == EINPROGRESS) {
+ r = connect_as(b->input_fd, &b->sockaddr.sa, b->sockaddr_size, b->connect_as_uid, b->connect_as_gid);
+ if (r < 0) {
+ if (r == -EINPROGRESS) {
/* If we have any inotify watches open, close them now, we don't need them anymore, as
* we have successfully initiated a connection */
@@ -988,7 +1082,7 @@ int bus_socket_connect(sd_bus *b) {
return 1;
}
- if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
+ if (IN_SET(r, -ENOENT, -ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */
b->watch_bind &&
b->sockaddr.sa.sa_family == AF_UNIX &&
b->sockaddr.un.sun_path[0] != 0) {
@@ -1016,7 +1110,7 @@ int bus_socket_connect(sd_bus *b) {
inotify_done = true;
} else
- return -errno;
+ return r;
} else
break;
}
diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c
index f9c59a1..6f6fa2d 100644
--- a/src/libsystemd/sd-bus/bus-track.c
+++ b/src/libsystemd/sd-bus/bus-track.c
@@ -69,7 +69,7 @@ static void bus_track_add_to_queue(sd_bus_track *track) {
return;
/* still referenced? */
- if (hashmap_size(track->names) > 0)
+ if (!hashmap_isempty(track->names))
return;
/* Nothing to call? */
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 8befc97..1a642cb 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -30,6 +30,7 @@
#include "constants.h"
#include "errno-util.h"
#include "fd-util.h"
+#include "format-util.h"
#include "glyph-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
@@ -151,6 +152,14 @@ void bus_close_inotify_fd(sd_bus *b) {
b->n_inotify_watches = 0;
}
+static void bus_close_fds(sd_bus *b) {
+ assert(b);
+
+ bus_close_io_fds(b);
+ bus_close_inotify_fd(b);
+ b->pidfd = safe_close(b->pidfd);
+}
+
static void bus_reset_queues(sd_bus *b) {
assert(b);
@@ -191,8 +200,7 @@ static sd_bus* bus_free(sd_bus *b) {
if (b->default_bus_ptr)
*b->default_bus_ptr = NULL;
- bus_close_io_fds(b);
- bus_close_inotify_fd(b);
+ bus_close_fds(b);
free(b->label);
free(b->groups);
@@ -256,7 +264,10 @@ _public_ int sd_bus_new(sd_bus **ret) {
.n_groups = SIZE_MAX,
.close_on_exit = true,
.ucred = UCRED_INVALID,
+ .pidfd = -EBADF,
.runtime_scope = _RUNTIME_SCOPE_INVALID,
+ .connect_as_uid = UID_INVALID,
+ .connect_as_gid = GID_INVALID,
};
/* We guarantee that wqueue always has space for at least one entry */
@@ -321,7 +332,7 @@ _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) {
assert_return(!bus->patch_sender, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->bus_client = !!b;
+ bus->bus_client = b;
return 0;
}
@@ -331,7 +342,7 @@ _public_ int sd_bus_set_monitor(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->is_monitor = !!b;
+ bus->is_monitor = b;
return 0;
}
@@ -341,7 +352,7 @@ _public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->accept_fd = !!b;
+ bus->accept_fd = b;
return 0;
}
@@ -353,7 +364,7 @@ _public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) {
/* This is not actually supported by any of our transports these days, but we do honour it for synthetic
* replies, and maybe one day classic D-Bus learns this too */
- bus->attach_timestamp = !!b;
+ bus->attach_timestamp = b;
return 0;
}
@@ -380,7 +391,7 @@ _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->is_server = !!b;
+ bus->is_server = b;
bus->server_id = server_id;
return 0;
}
@@ -391,7 +402,7 @@ _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->anonymous_auth = !!b;
+ bus->anonymous_auth = b;
return 0;
}
@@ -401,7 +412,7 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->trusted = !!b;
+ bus->trusted = b;
return 0;
}
@@ -419,7 +430,7 @@ _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) {
assert_return(bus = bus_resolve(bus), -ENOPKG);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->allow_interactive_authorization = !!b;
+ bus->allow_interactive_authorization = b;
return 0;
}
@@ -437,7 +448,7 @@ _public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->watch_bind = !!b;
+ bus->watch_bind = b;
return 0;
}
@@ -455,7 +466,7 @@ _public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) {
assert_return(bus->state == BUS_UNSET, -EPERM);
assert_return(!bus_origin_changed(bus), -ECHILD);
- bus->connected_signal = !!b;
+ bus->connected_signal = b;
return 0;
}
@@ -640,7 +651,7 @@ int bus_start_running(sd_bus *bus) {
static int parse_address_key(const char **p, const char *key, char **value) {
_cleanup_free_ char *r = NULL;
- size_t l, n = 0;
+ size_t n = 0;
const char *a;
assert(p);
@@ -648,17 +659,14 @@ static int parse_address_key(const char **p, const char *key, char **value) {
assert(value);
if (key) {
- l = strlen(key);
- if (strncmp(*p, key, l) != 0)
- return 0;
-
- if ((*p)[l] != '=')
+ a = startswith(*p, key);
+ if (!a || *a != '=')
return 0;
if (*value)
return -EINVAL;
- a = *p + l + 1;
+ a++;
} else
a = *p;
@@ -717,7 +725,7 @@ static void skip_address_key(const char **p) {
}
static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
- _cleanup_free_ char *path = NULL, *abstract = NULL;
+ _cleanup_free_ char *path = NULL, *abstract = NULL, *uids = NULL, *gids = NULL;
size_t l;
int r;
@@ -745,6 +753,18 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
else if (r > 0)
continue;
+ r = parse_address_key(p, "uid", &uids);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
+ r = parse_address_key(p, "gid", &gids);
+ if (r < 0)
+ return r;
+ else if (r > 0)
+ continue;
+
skip_address_key(p);
}
@@ -781,6 +801,17 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) {
b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
}
+ if (uids) {
+ r = parse_uid(uids, &b->connect_as_uid);
+ if (r < 0)
+ return r;
+ }
+ if (gids) {
+ r = parse_gid(gids, &b->connect_as_gid);
+ if (r < 0)
+ return r;
+ }
+
b->is_local = true;
return 0;
@@ -1102,8 +1133,7 @@ static int bus_start_address(sd_bus *b) {
assert(b);
for (;;) {
- bus_close_io_fds(b);
- bus_close_inotify_fd(b);
+ bus_close_fds(b);
bus_kill_exec(b);
@@ -1486,9 +1516,15 @@ interpret_port_as_machine_old_syntax:
return -ENOMEM;
}
- a = strjoin("unixexec:path=ssh,argv1=-xT", p ? ",argv2=-p,argv3=" : "", strempty(p),
- ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
- ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
+ const char *ssh = secure_getenv("SYSTEMD_SSH") ?: "ssh";
+ _cleanup_free_ char *ssh_escaped = bus_address_escape(ssh);
+ if (!ssh_escaped)
+ return -ENOMEM;
+
+ a = strjoin("unixexec:path=", ssh_escaped, ",argv1=-xT",
+ p ? ",argv2=-p,argv3=" : "", strempty(p),
+ ",argv", p ? "4" : "2", "=--,argv", p ? "5" : "3", "=", e,
+ ",argv", p ? "6" : "4", "=systemd-stdio-bridge", c);
if (!a)
return -ENOMEM;
@@ -1668,10 +1704,7 @@ static int user_and_machine_equivalent(const char *user_and_machine) {
return true;
/* Otherwise, we have to figure out our user id and name, and compare things with that. */
- char buf[DECIMAL_STR_MAX(uid_t)];
- xsprintf(buf, UID_FMT, uid);
-
- f = startswith(user_and_machine, buf);
+ f = startswith(user_and_machine, FORMAT_UID(uid));
if (!f) {
un = getusername_malloc();
if (!un)
@@ -1775,8 +1808,7 @@ _public_ void sd_bus_close(sd_bus *bus) {
* the bus object and the bus may be freed */
bus_reset_queues(bus);
- bus_close_io_fds(bus);
- bus_close_inotify_fd(bus);
+ bus_close_fds(bus);
}
_public_ sd_bus *sd_bus_close_unref(sd_bus *bus) {
@@ -4123,13 +4155,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
for (template_pos = path_template; *template_pos; ) {
const char *sep;
- size_t length;
+ size_t length, path_length;
char *label;
/* verify everything until the next '%' matches verbatim */
sep = strchrnul(template_pos, '%');
length = sep - template_pos;
- if (strncmp(path_pos, template_pos, length))
+ if (!strneq(path_pos, template_pos, length))
return 0;
path_pos += length;
@@ -4150,8 +4182,8 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template
/* verify the suffixes match */
sep = strchrnul(path_pos, '/');
- if (sep - path_pos < (ssize_t)length ||
- strncmp(sep - length, template_pos, length))
+ path_length = sep - path_pos;
+ if (length > path_length || !strneq(sep - length, template_pos, length))
return 0;
template_pos += length; /* skip over matched label */
diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c
index da1340f..d06853b 100644
--- a/src/libsystemd/sd-bus/test-bus-chat.c
+++ b/src/libsystemd/sd-bus/test-bus-chat.c
@@ -432,8 +432,7 @@ static void* client2(void *p) {
if (r < 0)
log_debug("Failed to issue method call: %s", bus_error_message(&error, r));
else {
- log_error("Slow call unexpectedly succeed.");
- r = -ENOANO;
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Slow call unexpectedly succeeded.");
goto finish;
}
diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c
index 3e14627..b569986 100644
--- a/src/libsystemd/sd-bus/test-bus-cleanup.c
+++ b/src/libsystemd/sd-bus/test-bus-cleanup.c
@@ -30,7 +30,7 @@ static void test_bus_fork(void) {
r = safe_fork("(bus-fork-test)", FORK_WAIT|FORK_LOG, NULL);
if (r == 0) {
assert_se(bus);
- assert_se(sd_bus_is_ready(bus) == -ECHILD);
+ ASSERT_RETURN_EXPECTED_SE(sd_bus_is_ready(bus) == -ECHILD);
assert_se(sd_bus_flush_close_unref(bus) == NULL);
assert_se(sd_bus_close_unref(bus) == NULL);
assert_se(sd_bus_unref(bus) == NULL);
diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c
index 13801be..7eb7a38 100644
--- a/src/libsystemd/sd-bus/test-bus-creds.c
+++ b/src/libsystemd/sd-bus/test-bus-creds.c
@@ -4,6 +4,7 @@
#include "bus-dump.h"
#include "cgroup-util.h"
+#include "errno-util.h"
#include "tests.h"
int main(int argc, char *argv[]) {
@@ -12,7 +13,7 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
- if (cg_unified() == -ENOMEDIUM)
+ if (IN_SET(cg_unified(), -ENOMEDIUM, -ENOENT))
return log_tests_skipped("/sys/fs/cgroup/ not available");
r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL);
@@ -24,11 +25,30 @@ int main(int argc, char *argv[]) {
creds = sd_bus_creds_unref(creds);
r = sd_bus_creds_new_from_pid(&creds, 1, _SD_BUS_CREDS_ALL);
- if (r != -EACCES) {
+ if (!ERRNO_IS_NEG_PRIVILEGE(r)) {
assert_se(r >= 0);
putchar('\n');
bus_creds_dump(creds, NULL, true);
}
+ creds = sd_bus_creds_unref(creds);
+
+ _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
+ r = sd_bus_default_system(&bus);
+ if (r < 0)
+ log_warning_errno(r, "Unable to connect to system bus, skipping rest of test.");
+ else {
+ const char *unique;
+
+ assert_se(sd_bus_get_unique_name(bus, &unique) >= 0);
+
+ r = sd_bus_get_name_creds(bus, unique, _SD_BUS_CREDS_ALL, &creds);
+ log_full_errno(r < 0 ? LOG_ERR : LOG_DEBUG, r, "sd_bus_get_name_creds: %m");
+ assert_se(r >= 0);
+
+ putchar('\n');
+ bus_creds_dump(creds, NULL, true);
+ }
+
return 0;
}
diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c
index a55f3f9..91045c0 100644
--- a/src/libsystemd/sd-bus/test-bus-error.c
+++ b/src/libsystemd/sd-bus/test-bus-error.c
@@ -213,8 +213,8 @@ TEST(errno_mapping_custom) {
assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT);
- assert_se(sd_bus_error_add_map(test_errors_bad1) == -EINVAL);
- assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_bus_error_add_map(test_errors_bad1) == -EINVAL);
+ ASSERT_RETURN_EXPECTED_SE(sd_bus_error_add_map(test_errors_bad2) == -EINVAL);
}
TEST(sd_bus_error_set_errnof) {
diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c
index 0044d33..92da627 100644
--- a/src/libsystemd/sd-bus/test-bus-marshal.c
+++ b/src/libsystemd/sd-bus/test-bus-marshal.c
@@ -42,8 +42,8 @@ static void test_bus_path_encode(void) {
assert_se(sd_bus_path_decode(a, "/waldo", &b) == 0 && b == NULL);
assert_se(sd_bus_path_decode(a, "/foo/bar", &b) > 0 && streq(b, "waldo"));
- assert_se(sd_bus_path_encode("xxxx", "waldo", &c) < 0);
- assert_se(sd_bus_path_encode("/foo/", "waldo", &c) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_bus_path_encode("xxxx", "waldo", &c) < 0);
+ ASSERT_RETURN_EXPECTED_SE(sd_bus_path_encode("/foo/", "waldo", &c) < 0);
assert_se(sd_bus_path_encode("/foo/bar", "", &c) >= 0 && streq(c, "/foo/bar/_"));
assert_se(sd_bus_path_decode(c, "/foo/bar", &d) > 0 && streq(d, ""));
diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c
index ccdd0d5..2847ba8 100644
--- a/src/libsystemd/sd-bus/test-bus-objects.c
+++ b/src/libsystemd/sd-bus/test-bus-objects.c
@@ -494,10 +494,9 @@ static int client(struct context *c) {
}
assert_se(sd_bus_message_exit_container(reply) >= 0);
- if (streq(path, "/value/a")) {
+ if (streq(path, "/value/a"))
/* ObjectManager must be here */
assert_se(found_object_manager_interface);
- }
} else
assert_se(sd_bus_message_skip(reply, "a{sa{sv}}") >= 0);
diff --git a/src/libsystemd/sd-bus/test-bus-peersockaddr.c b/src/libsystemd/sd-bus/test-bus-peersockaddr.c
index 79556e8..a7bba17 100644
--- a/src/libsystemd/sd-bus/test-bus-peersockaddr.c
+++ b/src/libsystemd/sd-bus/test-bus-peersockaddr.c
@@ -5,10 +5,39 @@
#include "sd-bus.h"
+#include "bus-dump.h"
+#include "bus-util.h"
#include "fd-util.h"
#include "process-util.h"
#include "socket-util.h"
+#include "sort-util.h"
#include "tests.h"
+#include "user-util.h"
+
+static bool gid_list_contained(const gid_t *a, size_t n, const gid_t *b, size_t m) {
+ assert_se(a || n == 0);
+ assert_se(b || m == 0);
+
+ /* Checks if every entry in a[] is also in b[] */
+
+ for (size_t i = 0; i < n; i++) {
+ size_t j;
+
+ for (j = 0; j < m; j++)
+ if (a[i] == b[j])
+ break;
+
+ if (j >= m)
+ return false;
+ }
+
+ return true;
+}
+
+static bool gid_list_same(const gid_t *a, size_t n, const gid_t *b, size_t m) {
+ return gid_list_contained(a, n, b, m) &&
+ gid_list_contained(b, m, a, n);
+}
static void *server(void *p) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
@@ -27,11 +56,13 @@ static void *server(void *p) {
assert_se(sd_bus_set_fd(bus, fd, fd) >= 0);
TAKE_FD(fd);
assert_se(sd_bus_set_server(bus, true, id) >= 0);
- assert_se(sd_bus_negotiate_creds(bus, 1, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION) >= 0);
+ assert_se(sd_bus_negotiate_creds(bus, 1, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_PIDFD|SD_BUS_CREDS_SUPPLEMENTARY_GIDS) >= 0);
assert_se(sd_bus_start(bus) >= 0);
- assert_se(sd_bus_get_owner_creds(bus, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION, &c) >= 0);
+ assert_se(sd_bus_get_owner_creds(bus, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM|SD_BUS_CREDS_DESCRIPTION|SD_BUS_CREDS_PIDFD|SD_BUS_CREDS_SUPPLEMENTARY_GIDS, &c) >= 0);
+
+ bus_creds_dump(c, /* f= */ NULL, /* terse= */ false);
uid_t u;
assert_se(sd_bus_creds_get_euid(c, &u) >= 0);
@@ -45,6 +76,26 @@ static void *server(void *p) {
assert_se(sd_bus_creds_get_pid(c, &pid) >= 0);
assert_se(pid == getpid_cached());
+ int pidfd = -EBADF;
+ if (sd_bus_creds_get_pidfd_dup(c, &pidfd) >= 0) {
+ _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+
+ assert_se(pidref_set_pidfd_take(&pidref, pidfd) >= 0);
+ assert_se(pidref.pid == getpid_cached());
+ }
+
+ const gid_t *gl = NULL;
+ int n;
+ n = sd_bus_creds_get_supplementary_gids(c, &gl);
+
+ if (n >= 0) {
+ _cleanup_free_ gid_t *gg = NULL;
+ r = getgroups_alloc(&gg);
+ assert_se(r >= 0);
+
+ assert_se(gid_list_same(gl, n, gg, r));
+ }
+
const char *comm;
assert_se(sd_bus_creds_get_comm(c, &comm) >= 0);
assert_se(pid_get_comm(0, &our_comm) >= 0);
diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c
index 6a60cde..4945d82 100644
--- a/src/libsystemd/sd-daemon/sd-daemon.c
+++ b/src/libsystemd/sd-daemon/sd-daemon.c
@@ -76,7 +76,7 @@ _public_ int sd_listen_fds(int unset_environment) {
goto finish;
}
- for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
+ for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
r = fd_cloexec(fd, true);
if (r < 0)
goto finish;
@@ -456,6 +456,7 @@ static int pid_notify_with_fds_internal(
const char *state,
const int *fds,
unsigned n_fds) {
+
SocketAddress address;
struct iovec iovec;
struct msghdr msghdr = {
@@ -464,19 +465,12 @@ static int pid_notify_with_fds_internal(
.msg_name = &address.sockaddr,
};
_cleanup_close_ int fd = -EBADF;
- struct cmsghdr *cmsg = NULL;
- const char *e;
- bool send_ucred;
- ssize_t n;
int type, r;
- if (!state)
- return -EINVAL;
-
- if (n_fds > 0 && !fds)
- return -EINVAL;
+ assert_return(state, -EINVAL);
+ assert_return(fds || n_fds == 0, -EINVAL);
- e = getenv("NOTIFY_SOCKET");
+ const char *e = getenv("NOTIFY_SOCKET");
if (!e)
return 0;
@@ -530,12 +524,14 @@ static int pid_notify_with_fds_internal(
iovec = IOVEC_MAKE_STRING(state);
- send_ucred =
+ bool send_ucred =
(pid != 0 && pid != getpid_cached()) ||
getuid() != geteuid() ||
getgid() != getegid();
if (n_fds > 0 || send_ucred) {
+ struct cmsghdr *cmsg;
+
/* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */
msghdr.msg_controllen =
(n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
@@ -569,6 +565,8 @@ static int pid_notify_with_fds_internal(
}
}
+ ssize_t n;
+
do {
/* First try with fake ucred data, as requested */
n = sendmsg(fd, &msghdr, MSG_NOSIGNAL);
@@ -594,6 +592,19 @@ static int pid_notify_with_fds_internal(
}
} while (!iovec_increment(msghdr.msg_iov, msghdr.msg_iovlen, n));
+ if (address.sockaddr.sa.sa_family == AF_VSOCK && IN_SET(type, SOCK_STREAM, SOCK_SEQPACKET)) {
+ /* For AF_VSOCK, we need to close the socket to signal the end of the message. */
+ if (shutdown(fd, SHUT_WR) < 0)
+ return log_debug_errno(errno, "Failed to shutdown notify socket: %m");
+
+ char c;
+ n = recv(fd, &c, sizeof(c), MSG_NOSIGNAL);
+ if (n < 0)
+ return log_debug_errno(errno, "Failed to wait for EOF on notify socket: %m");
+ if (n > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Unexpectedly received data on notify socket.");
+ }
+
return 1;
}
@@ -650,7 +661,7 @@ _public_ int sd_notify(int unset_environment, const char *state) {
_public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
_cleanup_free_ char *p = NULL;
- int r;
+ int r = 0, k;
if (format) {
va_list ap;
@@ -659,16 +670,20 @@ _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format
r = vasprintf(&p, format, ap);
va_end(ap);
- if (r < 0 || !p)
- return -ENOMEM;
+ if (r < 0 || !p) {
+ r = -ENOMEM;
+ p = mfree(p); /* If vasprintf failed, do not use the string,
+ * even if something was returned. */
+ }
}
- return sd_pid_notify(pid, unset_environment, p);
+ k = sd_pid_notify(pid, unset_environment, p);
+ return r < 0 ? r : k;
}
_public_ int sd_notifyf(int unset_environment, const char *format, ...) {
_cleanup_free_ char *p = NULL;
- int r;
+ int r = 0, k;
if (format) {
va_list ap;
@@ -677,11 +692,15 @@ _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
r = vasprintf(&p, format, ap);
va_end(ap);
- if (r < 0 || !p)
- return -ENOMEM;
+ if (r < 0 || !p) {
+ r = -ENOMEM;
+ p = mfree(p); /* If vasprintf failed, do not use the string,
+ * even if something was returned. */
+ }
}
- return sd_pid_notify(0, unset_environment, p);
+ k = sd_pid_notify(0, unset_environment, p);
+ return r < 0 ? r : k;
}
_public_ int sd_pid_notifyf_with_fds(
@@ -691,27 +710,31 @@ _public_ int sd_pid_notifyf_with_fds(
const char *format, ...) {
_cleanup_free_ char *p = NULL;
- int r;
+ int r = 0, k;
/* Paranoia check: we traditionally used 'unsigned' as array size, but we nowadays more correctly use
* 'size_t'. sd_pid_notifyf_with_fds() and sd_pid_notify_with_fds() are from different eras, hence
* differ in this. Let's catch resulting incompatibilites early, even though they are pretty much
* theoretic only */
if (n_fds > UINT_MAX)
- return -E2BIG;
+ r = -E2BIG;
- if (format) {
+ else if (format) {
va_list ap;
va_start(ap, format);
r = vasprintf(&p, format, ap);
va_end(ap);
- if (r < 0 || !p)
- return -ENOMEM;
+ if (r < 0 || !p) {
+ r = -ENOMEM;
+ p = mfree(p); /* If vasprintf failed, do not use the string,
+ * even if something was returned. */
+ }
}
- return sd_pid_notify_with_fds(pid, unset_environment, p, fds, n_fds);
+ k = sd_pid_notify_with_fds(pid, unset_environment, p, fds, n_fds);
+ return r < 0 ? r : k;
}
_public_ int sd_booted(void) {
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index 15c5c42..71ab3d8 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -370,12 +370,8 @@ static int enumerator_sort_devices(sd_device_enumerator *enumerator) {
HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) {
_cleanup_free_ char *p = NULL;
- const char *subsys;
- if (sd_device_get_subsystem(device, &subsys) < 0)
- continue;
-
- if (!streq(subsys, *prioritized_subsystem))
+ if (!device_in_subsystem(device, *prioritized_subsystem))
continue;
devices[n++] = sd_device_ref(device);
@@ -662,10 +658,8 @@ static int enumerator_add_parent_devices(
continue;
r = device_enumerator_add_device(enumerator, device);
- if (r < 0)
+ if (r <= 0) /* r == 0 means the device already exists, then no need to go further up. */
return r;
- if (r == 0) /* Exists already? Then no need to go further up. */
- return 0;
}
}
diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c
index bb4f9bd..a7ef03a 100644
--- a/src/libsystemd/sd-device/device-monitor.c
+++ b/src/libsystemd/sd-device/device-monitor.c
@@ -47,7 +47,7 @@ struct sd_device_monitor {
union sockaddr_union snl_trusted_sender;
bool bound;
- UidRange *mapped_userns_uid_range;
+ UIDRange *mapped_userns_uid_range;
Hashmap *subsystem_filter;
Set *tag_filter;
@@ -402,8 +402,7 @@ static sd_device_monitor *device_monitor_free(sd_device_monitor *m) {
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_monitor, sd_device_monitor, device_monitor_free);
static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
- const char *s, *subsystem, *d, *devtype = NULL;
- int r;
+ const char *s, *d;
assert(m);
assert(device);
@@ -411,20 +410,14 @@ static int check_subsystem_filter(sd_device_monitor *m, sd_device *device) {
if (hashmap_isempty(m->subsystem_filter))
return true;
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0)
- return r;
-
- r = sd_device_get_devtype(device, &devtype);
- if (r < 0 && r != -ENOENT)
- return r;
-
HASHMAP_FOREACH_KEY(d, s, m->subsystem_filter) {
- if (!streq(s, subsystem))
+ if (!device_in_subsystem(device, s))
continue;
- if (!d || streq_ptr(d, devtype))
- return true;
+ if (d && !device_is_devtype(device, d))
+ continue;
+
+ return true;
}
return false;
@@ -480,7 +473,7 @@ static bool check_sender_uid(sd_device_monitor *m, uid_t uid) {
return true;
if (!m->mapped_userns_uid_range) {
- r = uid_range_load_userns(&m->mapped_userns_uid_range, NULL);
+ r = uid_range_load_userns(/* path = */ NULL, UID_RANGE_USERNS_INSIDE, &m->mapped_userns_uid_range);
if (r < 0)
log_monitor_errno(m, r, "Failed to load UID ranges mapped to the current user namespace, ignoring: %m");
}
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 0edabfb..cd85ec9 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -768,55 +768,96 @@ static bool device_has_info(sd_device *device) {
return false;
}
+bool device_should_have_db(sd_device *device) {
+ assert(device);
+
+ if (device_has_info(device))
+ return true;
+
+ if (major(device->devnum) != 0)
+ return true;
+
+ if (device->ifindex != 0)
+ return true;
+
+ return false;
+}
+
void device_set_db_persist(sd_device *device) {
assert(device);
device->db_persist = true;
}
-int device_update_db(sd_device *device) {
+static int device_get_db_path(sd_device *device, char **ret) {
const char *id;
char *path;
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_(unlink_and_freep) char *path_tmp = NULL;
- bool has_info;
int r;
assert(device);
-
- has_info = device_has_info(device);
+ assert(ret);
r = device_get_device_id(device, &id);
if (r < 0)
return r;
- path = strjoina("/run/udev/data/", id);
+ path = path_join("/run/udev/data/", id);
+ if (!path)
+ return -ENOMEM;
+
+ *ret = path;
+ return 0;
+}
+
+int device_has_db(sd_device *device) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(device);
+
+ r = device_get_db_path(device, &path);
+ if (r < 0)
+ return r;
+
+ return access(path, F_OK) >= 0;
+}
+
+int device_update_db(sd_device *device) {
+ _cleanup_(unlink_and_freep) char *path = NULL, *path_tmp = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ assert(device);
/* do not store anything for otherwise empty devices */
- if (!has_info && major(device->devnum) == 0 && device->ifindex == 0) {
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
+ if (!device_should_have_db(device))
+ return device_delete_db(device);
- return 0;
- }
+ r = device_get_db_path(device, &path);
+ if (r < 0)
+ return r;
/* write a database file */
r = mkdir_parents(path, 0755);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r,
+ "sd-device: Failed to create parent directories of '%s': %m",
+ path);
r = fopen_temporary(path, &f, &path_tmp);
if (r < 0)
- return r;
+ return log_device_debug_errno(device, r,
+ "sd-device: Failed to create temporary file for database file '%s': %m",
+ path);
/* set 'sticky' bit to indicate that we should not clean the database when we transition from initrd
* to the real root */
- if (fchmod(fileno(f), device->db_persist ? 01644 : 0644) < 0) {
- r = -errno;
- goto fail;
- }
+ if (fchmod(fileno(f), device->db_persist ? 01644 : 0644) < 0)
+ return log_device_debug_errno(device, errno,
+ "sd-device: Failed to chmod temporary database file '%s': %m",
+ path_tmp);
- if (has_info) {
+ if (device_has_info(device)) {
const char *property, *value, *ct;
if (major(device->devnum) > 0) {
@@ -846,45 +887,55 @@ int device_update_db(sd_device *device) {
r = fflush_and_check(f);
if (r < 0)
- goto fail;
+ return log_device_debug_errno(device, r,
+ "sd-device: Failed to flush temporary database file '%s': %m",
+ path_tmp);
- if (rename(path_tmp, path) < 0) {
- r = -errno;
- goto fail;
- }
+ if (rename(path_tmp, path) < 0)
+ return log_device_debug_errno(device, errno,
+ "sd-device: Failed to rename temporary database file '%s' to '%s': %m",
+ path_tmp, path);
- path_tmp = mfree(path_tmp);
+ log_device_debug(device, "sd-device: Created database file '%s' for '%s'.", path, device->devpath);
- log_device_debug(device, "sd-device: Created %s file '%s' for '%s'", has_info ? "db" : "empty",
- path, device->devpath);
+ path_tmp = mfree(path_tmp);
+ path = mfree(path);
return 0;
-
-fail:
- (void) unlink(path);
-
- return log_device_debug_errno(device, r, "sd-device: Failed to create %s file '%s' for '%s'", has_info ? "db" : "empty", path, device->devpath);
}
int device_delete_db(sd_device *device) {
- const char *id;
- char *path;
+ _cleanup_free_ char *path = NULL;
int r;
assert(device);
- r = device_get_device_id(device, &id);
+ r = device_get_db_path(device, &path);
if (r < 0)
return r;
- path = strjoina("/run/udev/data/", id);
-
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
return 0;
}
+int device_read_db_internal(sd_device *device, bool force) {
+ _cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(device);
+
+ if (device->db_loaded || (!force && device->sealed))
+ return 0;
+
+ r = device_get_db_path(device, &path);
+ if (r < 0)
+ return r;
+
+ return device_read_db_internal_filename(device, path);
+}
+
static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
[SD_DEVICE_ADD] = "add",
[SD_DEVICE_REMOVE] = "remove",
diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h
index e8a6d52..85e8609 100644
--- a/src/libsystemd/sd-device/device-private.h
+++ b/src/libsystemd/sd-device/device-private.h
@@ -24,6 +24,7 @@ int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, uns
static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
}
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *ret);
@@ -61,6 +62,8 @@ int device_get_properties_strv(sd_device *device, char ***ret);
int device_clone_with_db(sd_device *device, sd_device **ret);
int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
+bool device_should_have_db(sd_device *device);
+int device_has_db(sd_device *device);
int device_update_db(sd_device *device);
int device_delete_db(sd_device *device);
int device_read_db_internal_filename(sd_device *device, const char *filename); /* For fuzzer */
diff --git a/src/libsystemd/sd-device/device-util.c b/src/libsystemd/sd-device/device-util.c
index 529eff2..123629c 100644
--- a/src/libsystemd/sd-device/device-util.c
+++ b/src/libsystemd/sd-device/device-util.c
@@ -9,7 +9,6 @@
int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- _cleanup_free_ char *s = NULL;
const char *devname;
int r;
@@ -26,15 +25,10 @@ int devname_from_devnum(mode_t mode, dev_t devnum, char **ret) {
if (r < 0)
return r;
- s = strdup(devname);
- if (!s)
- return -ENOMEM;
-
- *ret = TAKE_PTR(s);
- return 0;
+ return strdup_to(ret, devname);
}
-int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret) {
+int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_close_ int fd = -EBADF;
int r;
@@ -47,19 +41,16 @@ int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret) {
if (fd < 0)
return fd;
- if (ret) {
+ if (ret_devname) {
const char *devname;
- char *s;
r = sd_device_get_devname(dev, &devname);
if (r < 0)
return r;
- s = strdup(devname);
- if (!s)
- return -ENOMEM;
-
- *ret = s;
+ r = strdup_to(ret_devname, devname);
+ if (r < 0)
+ return r;
}
return TAKE_FD(fd);
@@ -139,3 +130,21 @@ char** device_make_log_fields(sd_device *device) {
return TAKE_PTR(strv);
}
+
+bool device_in_subsystem(sd_device *device, const char *subsystem) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_subsystem(device, &s);
+ return streq_ptr(s, subsystem);
+}
+
+bool device_is_devtype(sd_device *device, const char *devtype) {
+ const char *s = NULL;
+
+ assert(device);
+
+ (void) sd_device_get_devtype(device, &s);
+ return streq_ptr(s, devtype);
+}
diff --git a/src/libsystemd/sd-device/device-util.h b/src/libsystemd/sd-device/device-util.h
index bf86ddc..b17993d 100644
--- a/src/libsystemd/sd-device/device-util.h
+++ b/src/libsystemd/sd-device/device-util.h
@@ -10,6 +10,7 @@
#include "alloc-util.h"
#include "log.h"
#include "macro.h"
+#include "strv.h"
#define device_unref_and_replace(a, b) \
unref_and_replace_full(a, b, sd_device_ref, sd_device_unref)
@@ -99,6 +100,16 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
assert(st);
return devname_from_devnum(st->st_mode, st->st_rdev, ret);
}
-int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
+int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname);
char** device_make_log_fields(sd_device *device);
+
+bool device_in_subsystem(sd_device *device, const char *subsystem);
+bool device_is_devtype(sd_device *device, const char *devtype);
+
+static inline bool device_property_can_set(const char *property) {
+ return property &&
+ !STR_IN_SET(property,
+ "ACTION", "DEVLINKS", "DEVNAME", "DEVPATH", "DEVTYPE", "DRIVER",
+ "IFINDEX", "MAJOR", "MINOR", "SEQNUM", "SUBSYSTEM", "TAGS");
+}
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 01e66b4..d8d1518 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -214,7 +214,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
/* Only operate on sysfs, i.e. refuse going down into /sys/fs/cgroup/ or similar places where
* things are not arranged as kobjects in kernel, and hence don't necessarily have
* kobject/attribute structure. */
- r = getenv_bool_secure("SYSTEMD_DEVICE_VERIFY_SYSFS");
+ r = secure_getenv_bool("SYSTEMD_DEVICE_VERIFY_SYSFS");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_DEVICE_VERIFY_SYSFS value: %m");
if (r != 0) {
@@ -283,7 +283,7 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
_cleanup_free_ char *syspath = NULL;
- const char *t, *subsystem = NULL;
+ const char *t;
dev_t n;
int r;
@@ -314,10 +314,7 @@ int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum)
if (n != devnum)
return -ENXIO;
- r = sd_device_get_subsystem(dev, &subsystem);
- if (r < 0 && r != -ENOENT)
- return r;
- if (streq_ptr(subsystem, "block") != !!S_ISBLK(mode))
+ if (device_in_subsystem(dev, "block") != !!S_ISBLK(mode))
return -ENXIO;
*ret = TAKE_PTR(dev);
@@ -348,17 +345,11 @@ _public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
assert_return(ret, -EINVAL);
assert_return(ifname, -EINVAL);
- r = parse_ifindex(ifname);
- if (r > 0)
- return sd_device_new_from_ifindex(ret, r);
-
- if (ifname_valid(ifname)) {
- r = device_new_from_main_ifname(ret, ifname);
- if (r >= 0)
- return r;
- }
+ r = device_new_from_main_ifname(ret, ifname);
+ if (r >= 0)
+ return r;
- r = rtnl_resolve_link_alternative_name(NULL, ifname, &main_name);
+ r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL);
if (r < 0)
return r;
@@ -367,14 +358,15 @@ _public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
_public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
- char ifname[IF_NAMESIZE];
+ _cleanup_free_ char *ifname = NULL;
int r, i;
assert_return(ret, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
- if (format_ifname(ifindex, ifname) < 0)
- return -ENODEV;
+ r = rtnl_get_ifname_full(NULL, ifindex, &ifname, NULL);
+ if (r < 0)
+ return r;
r = device_new_from_main_ifname(&dev, ifname);
if (r < 0)
@@ -1222,37 +1214,27 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
return !!device->devtype;
}
-_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *subsystem, const char *devtype, sd_device **ret) {
- sd_device *parent = NULL;
+_public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
int r;
- assert_return(child, -EINVAL);
+ assert_return(device, -EINVAL);
assert_return(subsystem, -EINVAL);
- r = sd_device_get_parent(child, &parent);
- while (r >= 0) {
- const char *parent_subsystem = NULL;
+ for (;;) {
+ r = sd_device_get_parent(device, &device);
+ if (r < 0)
+ return r;
- (void) sd_device_get_subsystem(parent, &parent_subsystem);
- if (streq_ptr(parent_subsystem, subsystem)) {
- const char *parent_devtype = NULL;
+ if (!device_in_subsystem(device, subsystem))
+ continue;
- if (!devtype)
- break;
+ if (devtype && !device_is_devtype(device, devtype))
+ continue;
- (void) sd_device_get_devtype(parent, &parent_devtype);
- if (streq_ptr(parent_devtype, devtype))
- break;
- }
- r = sd_device_get_parent(parent, &parent);
+ if (ret)
+ *ret = device;
+ return 0;
}
-
- if (r < 0)
- return r;
-
- if (ret)
- *ret = parent;
- return 0;
}
_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
@@ -1778,24 +1760,6 @@ int device_read_db_internal_filename(sd_device *device, const char *filename) {
return 0;
}
-int device_read_db_internal(sd_device *device, bool force) {
- const char *id, *path;
- int r;
-
- assert(device);
-
- if (device->db_loaded || (!force && device->sealed))
- return 0;
-
- r = device_get_device_id(device, &id);
- if (r < 0)
- return r;
-
- path = strjoina("/run/udev/data/", id);
-
- return device_read_db_internal_filename(device, path);
-}
-
_public_ int sd_device_get_is_initialized(sd_device *device) {
int r;
@@ -2454,6 +2418,25 @@ int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, uns
return v > 0;
}
+int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value) {
+ const char *value;
+ int r;
+
+ r = sd_device_get_sysattr_value(device, sysattr, &value);
+ if (r < 0)
+ return r;
+
+ uint32_t v;
+ r = safe_atou32(value, &v);
+ if (r < 0)
+ return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
+
+ if (ret_value)
+ *ret_value = v;
+ /* We return "true" if the value is positive. */
+ return v > 0;
+}
+
int device_get_sysattr_bool(sd_device *device, const char *sysattr) {
const char *value;
int r;
@@ -2506,7 +2489,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
/* drop trailing newlines */
while (len > 0 && strchr(NEWLINE, _value[len - 1]))
- len --;
+ len--;
/* value length is limited to 4k */
if (len > 4096)
@@ -2609,7 +2592,7 @@ _public_ int sd_device_trigger_with_uuid(
_public_ int sd_device_open(sd_device *device, int flags) {
_cleanup_close_ int fd = -EBADF, fd2 = -EBADF;
- const char *devname, *subsystem = NULL;
+ const char *devname;
uint64_t q, diskseq = 0;
struct stat st;
dev_t devnum;
@@ -2630,10 +2613,6 @@ _public_ int sd_device_open(sd_device *device, int flags) {
if (r < 0)
return r;
- r = sd_device_get_subsystem(device, &subsystem);
- if (r < 0 && r != -ENOENT)
- return r;
-
fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0)
return -errno;
@@ -2644,7 +2623,7 @@ _public_ int sd_device_open(sd_device *device, int flags) {
if (st.st_rdev != devnum)
return -ENXIO;
- if (streq_ptr(subsystem, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
+ if (device_in_subsystem(device, "block") ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode))
return -ENXIO;
/* If flags has O_PATH, then we cannot check diskseq. Let's return earlier. */
diff --git a/src/libsystemd/sd-device/test-device-util.c b/src/libsystemd/sd-device/test-device-util.c
index bc8ab66..f7c9deb 100644
--- a/src/libsystemd/sd-device/test-device-util.c
+++ b/src/libsystemd/sd-device/test-device-util.c
@@ -1,23 +1,91 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "device-util.h"
+#include "mountpoint-util.h"
#include "tests.h"
TEST(log_device_full) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
int r;
+ (void) sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+
for (int level = LOG_ERR; level <= LOG_DEBUG; level++) {
- log_device_full(NULL, level, "test level=%d: %m", level);
+ log_device_full(dev, level, "test level=%d: %m", level);
- r = log_device_full_errno(NULL, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
+ r = log_device_full_errno(dev, level, EUCLEAN, "test level=%d errno=EUCLEAN: %m", level);
assert_se(r == -EUCLEAN);
- r = log_device_full_errno(NULL, level, 0, "test level=%d errno=0: %m", level);
+ r = log_device_full_errno(dev, level, 0, "test level=%d errno=0: %m", level);
assert_se(r == 0);
- r = log_device_full_errno(NULL, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA): %m", level);
+ r = log_device_full_errno(dev, level, SYNTHETIC_ERRNO(ENODATA), "test level=%d errno=S(ENODATA).", level);
assert_se(r == -ENODATA);
}
}
-DEFINE_TEST_MAIN(LOG_INFO);
+TEST(device_in_subsystem) {
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+ int r;
+
+ r = sd_device_new_from_subsystem_sysname(&dev, "net", "lo");
+ if (r == -ENODEV)
+ return (void) log_tests_skipped("net/lo does not exist");
+ assert_se(r >= 0);
+
+ assert_se(device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(!device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(!device_in_subsystem(dev, NULL));
+
+ dev = sd_device_unref(dev);
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+ assert_se(!device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(!device_in_subsystem(dev, NULL));
+
+ dev = sd_device_unref(dev);
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class") >= 0);
+ assert_se(!device_in_subsystem(dev, "net"));
+ assert_se(!device_in_subsystem(dev, "disk"));
+ assert_se(!device_in_subsystem(dev, "subsystem"));
+ assert_se(!device_in_subsystem(dev, ""));
+ assert_se(device_in_subsystem(dev, NULL));
+}
+
+TEST(device_is_devtype) {
+ _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+
+ assert_se(sd_device_enumerator_new(&e) >= 0);
+ assert_se(sd_device_enumerator_add_match_subsystem(e, "disk", true) >= 0);
+
+ FOREACH_DEVICE(e, d) {
+ const char *t;
+
+ assert_se(sd_device_get_devtype(d, &t) >= 0);
+ assert_se(device_is_devtype(d, t));
+ assert_se(!device_is_devtype(d, "hoge"));
+ assert_se(!device_is_devtype(d, ""));
+ assert_se(!device_is_devtype(d, NULL));
+ }
+
+ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net") >= 0);
+ assert_se(!device_is_devtype(dev, "hoge"));
+ assert_se(!device_is_devtype(dev, ""));
+ assert_se(device_is_devtype(dev, NULL));
+}
+
+static int intro(void) {
+ if (path_is_mount_point("/sys") <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
diff --git a/src/libsystemd/sd-device/test-sd-device-monitor.c b/src/libsystemd/sd-device/test-sd-device-monitor.c
index e124e00..3dbf987 100644
--- a/src/libsystemd/sd-device/test-sd-device-monitor.c
+++ b/src/libsystemd/sd-device/test-sd-device-monitor.c
@@ -10,6 +10,7 @@
#include "device-private.h"
#include "device-util.h"
#include "macro.h"
+#include "mountpoint-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -298,6 +299,9 @@ int main(int argc, char *argv[]) {
if (getuid() != 0)
return log_tests_skipped("not root");
+ if (path_is_mount_point("/sys") <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
if (path_is_read_only_fs("/sys") > 0)
return log_tests_skipped("Running in container");
diff --git a/src/libsystemd/sd-device/test-sd-device-thread.c b/src/libsystemd/sd-device/test-sd-device-thread.c
index c99d179..539dabd 100644
--- a/src/libsystemd/sd-device/test-sd-device-thread.c
+++ b/src/libsystemd/sd-device/test-sd-device-thread.c
@@ -8,6 +8,7 @@
#include "sd-device.h"
#include "device-util.h"
+#include "tests.h"
#define handle_error_errno(error, msg) \
({ \
@@ -30,6 +31,8 @@ int main(int argc, char *argv[]) {
int r;
r = sd_device_new_from_syspath(&loopback, "/sys/class/net/lo");
+ if (r == -ENODEV)
+ return log_tests_skipped("Loopback device not found");
if (r < 0)
return handle_error_errno(r, "Failed to create loopback device object");
diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c
index bce99b5..9fde1a0 100644
--- a/src/libsystemd/sd-device/test-sd-device.c
+++ b/src/libsystemd/sd-device/test-sd-device.c
@@ -11,6 +11,7 @@
#include "errno-util.h"
#include "fd-util.h"
#include "hashmap.h"
+#include "mountpoint-util.h"
#include "nulstr-util.h"
#include "path-util.h"
#include "rm-rf.h"
@@ -675,4 +676,11 @@ TEST(devname_from_devnum) {
}
}
-DEFINE_TEST_MAIN(LOG_INFO);
+static int intro(void) {
+ if (path_is_mount_point("/sys") <= 0)
+ return log_tests_skipped("/sys is not mounted");
+
+ return EXIT_SUCCESS;
+}
+
+DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);
diff --git a/src/libsystemd/sd-event/event-source.h b/src/libsystemd/sd-event/event-source.h
index f4e38d7..d05bcf0 100644
--- a/src/libsystemd/sd-event/event-source.h
+++ b/src/libsystemd/sd-event/event-source.h
@@ -189,6 +189,9 @@ struct inode_data {
* iteration. */
int fd;
+ /* The path that the fd points to. The field is optional. */
+ char *path;
+
/* The inotify "watch descriptor" */
int wd;
diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c
index a310122..862455e 100644
--- a/src/libsystemd/sd-event/event-util.c
+++ b/src/libsystemd/sd-event/event-util.c
@@ -151,3 +151,29 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
return 0;
}
+
+int event_add_child_pidref(
+ sd_event *e,
+ sd_event_source **s,
+ const PidRef *pid,
+ int options,
+ sd_event_child_handler_t callback,
+ void *userdata) {
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ if (pid->fd >= 0)
+ return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+
+ return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+}
+
+dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
+ assert(e);
+ assert(ts);
+
+ assert_se(sd_event_now(e, CLOCK_REALTIME, &ts->realtime) >= 0);
+ assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
+ return ts;
+}
diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h
index c185584..7002ca3 100644
--- a/src/libsystemd/sd-event/event-util.h
+++ b/src/libsystemd/sd-event/event-util.h
@@ -5,6 +5,8 @@
#include "sd-event.h"
+#include "pidref.h"
+
int event_reset_time(
sd_event *e,
sd_event_source **s,
@@ -32,3 +34,7 @@ static inline int event_source_disable(sd_event_source *s) {
}
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
+
+int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
+
+dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index b6899df..a1305ef 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/epoll.h>
+#if HAVE_PIDFD_OPEN
+#include <sys/pidfd.h>
+#endif
#include <sys/timerfd.h>
#include <sys/wait.h>
@@ -1165,10 +1168,10 @@ static int source_set_pending(sd_event_source *s, bool b) {
assert(s->inotify.inode_data->inotify_data);
if (b)
- s->inotify.inode_data->inotify_data->n_pending ++;
+ s->inotify.inode_data->inotify_data->n_pending++;
else {
assert(s->inotify.inode_data->inotify_data->n_pending > 0);
- s->inotify.inode_data->inotify_data->n_pending --;
+ s->inotify.inode_data->inotify_data->n_pending--;
}
}
@@ -1574,7 +1577,7 @@ static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *us
static bool shall_use_pidfd(void) {
/* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
- return getenv_bool_secure("SYSTEMD_PIDFD") != 0;
+ return secure_getenv_bool("SYSTEMD_PIDFD") != 0;
}
_public_ int sd_event_add_child(
@@ -1976,7 +1979,7 @@ _public_ int sd_event_add_memory_pressure(
env = secure_getenv("MEMORY_PRESSURE_WRITE");
if (env) {
- r = unbase64mem(env, SIZE_MAX, &write_buffer, &write_buffer_size);
+ r = unbase64mem(env, &write_buffer, &write_buffer_size);
if (r < 0)
return r;
}
@@ -2231,8 +2234,8 @@ static int inode_data_compare(const struct inode_data *x, const struct inode_dat
static void inode_data_hash_func(const struct inode_data *d, struct siphash *state) {
assert(d);
- siphash24_compress(&d->dev, sizeof(d->dev), state);
- siphash24_compress(&d->ino, sizeof(d->ino), state);
+ siphash24_compress_typesafe(d->dev, state);
+ siphash24_compress_typesafe(d->ino, state);
}
DEFINE_PRIVATE_HASH_OPS(inode_data_hash_ops, struct inode_data, inode_data_hash_func, inode_data_compare);
@@ -2272,6 +2275,7 @@ static void event_free_inode_data(
assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
}
+ free(d->path);
free(d);
}
@@ -2512,6 +2516,15 @@ static int event_add_inotify_fd_internal(
}
LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
+
+ _cleanup_free_ char *path = NULL;
+ r = fd_get_path(inode_data->fd, &path);
+ if (r < 0 && r != -ENOSYS) { /* The path is optional, hence ignore -ENOSYS. */
+ event_gc_inode_data(e, inode_data);
+ return r;
+ }
+
+ free_and_replace(inode_data->path, path);
}
/* Link our event source to the inode data object */
@@ -2797,6 +2810,13 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
}
LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
+
+ _cleanup_free_ char *path = NULL;
+ r = fd_get_path(new_inode_data->fd, &path);
+ if (r < 0 && r != -ENOSYS)
+ goto fail;
+
+ free_and_replace(new_inode_data->path, path);
}
/* Move the event source to the new inode data structure */
@@ -3281,13 +3301,29 @@ _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own)
return 0;
}
-_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
+_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret) {
assert_return(s, -EINVAL);
- assert_return(mask, -EINVAL);
+ assert_return(ret, -EINVAL);
assert_return(s->type == SOURCE_INOTIFY, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
- *mask = s->inotify.mask;
+ *ret = s->inotify.mask;
+ return 0;
+}
+
+_public_ int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret) {
+ assert_return(s, -EINVAL);
+ assert_return(ret, -EINVAL);
+ assert_return(s->type == SOURCE_INOTIFY, -EDOM);
+ assert_return(!event_origin_changed(s->event), -ECHILD);
+
+ if (!s->inotify.inode_data)
+ return -ESTALE; /* already disconnected. */
+
+ if (!s->inotify.inode_data->path)
+ return -ENOSYS; /* /proc was not mounted? */
+
+ *ret = s->inotify.inode_data->path;
return 0;
}
@@ -3999,7 +4035,7 @@ static int process_inotify(sd_event *e) {
if (r < 0)
return r;
if (r > 0)
- done ++;
+ done++;
}
return done;
@@ -4914,13 +4950,13 @@ _public_ int sd_event_get_state(sd_event *e) {
_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
assert_return(e, -EINVAL);
assert_return(e = event_resolve(e), -ENOPKG);
- assert_return(code, -EINVAL);
assert_return(!event_origin_changed(e), -ECHILD);
if (!e->exit_requested)
return -ENODATA;
- *code = e->exit_code;
+ if (code)
+ *code = e->exit_code;
return 0;
}
@@ -5037,7 +5073,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) {
}
}
- e->watchdog = !!b;
+ e->watchdog = b;
return e->watchdog;
fail:
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index cc3d84e..57dee39 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#if HAVE_PIDFD_OPEN
+#include <sys/pidfd.h>
+#endif
#include <sys/wait.h>
#include <unistd.h>
@@ -92,7 +95,7 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si,
assert_se(userdata == INT_TO_PTR('e'));
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2) >= 0);
pid = fork();
assert_se(pid >= 0);
@@ -142,7 +145,7 @@ static int defer_handler(sd_event_source *s, void *userdata) {
assert_se(userdata == INT_TO_PTR('d'));
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR1) >= 0);
assert_se(sd_event_add_signal(sd_event_source_get_event(s), &p, SIGUSR1, signal_handler, INT_TO_PTR('e')) >= 0);
assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0);
@@ -254,7 +257,7 @@ static void test_basic_one(bool with_pidfd) {
assert_se(sd_event_source_set_prepare(z, prepare_handler) >= 0);
/* Test for floating event sources */
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+1) >= 0);
assert_se(sd_event_add_signal(e, NULL, SIGRTMIN+1, NULL, NULL) >= 0);
assert_se(write(a[1], &ch, 1) >= 0);
@@ -346,7 +349,7 @@ TEST(rtqueue) {
assert_se(sd_event_default(&e) >= 0);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2) >= 0);
assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0);
assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0);
assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0);
@@ -396,6 +399,7 @@ struct inotify_context {
unsigned create_called[CREATE_EVENTS_MAX];
unsigned create_overflow;
unsigned n_create_events;
+ const char *path;
};
static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
@@ -422,10 +426,12 @@ static void maybe_exit(sd_event_source *s, struct inotify_context *c) {
}
static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
- struct inotify_context *c = userdata;
- const char *description;
+ struct inotify_context *c = ASSERT_PTR(userdata);
+ const char *path, *description;
unsigned bit, n;
+ assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
+
assert_se(sd_event_source_get_description(s, &description) >= 0);
assert_se(safe_atou(description, &n) >= 0);
@@ -433,11 +439,12 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
bit = 1U << n;
if (ev->mask & IN_Q_OVERFLOW) {
- log_info("inotify-handler <%s>: overflow", description);
+ log_info("inotify-handler for %s <%s>: overflow", path, description);
c->create_overflow |= bit;
} else if (ev->mask & IN_CREATE) {
+ assert_se(path_equal_or_inode_same(path, c->path, 0));
if (streq(ev->name, "sub"))
- log_debug("inotify-handler <%s>: create on %s", description, ev->name);
+ log_debug("inotify-handler for %s <%s>: create on %s", path, description, ev->name);
else {
unsigned i;
@@ -446,7 +453,7 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
c->create_called[i] |= bit;
}
} else if (ev->mask & IN_DELETE) {
- log_info("inotify-handler <%s>: delete of %s", description, ev->name);
+ log_info("inotify-handler for %s <%s>: delete of %s", path, description, ev->name);
assert_se(streq(ev->name, "sub"));
} else
assert_not_reached();
@@ -456,16 +463,19 @@ static int inotify_handler(sd_event_source *s, const struct inotify_event *ev, v
}
static int delete_self_handler(sd_event_source *s, const struct inotify_event *ev, void *userdata) {
- struct inotify_context *c = userdata;
+ struct inotify_context *c = ASSERT_PTR(userdata);
+ const char *path;
+
+ assert_se(sd_event_source_get_inotify_path(s, &path) >= 0);
if (ev->mask & IN_Q_OVERFLOW) {
- log_info("delete-self-handler: overflow");
+ log_info("delete-self-handler for %s: overflow", path);
c->delete_self_handler_called = true;
} else if (ev->mask & IN_DELETE_SELF) {
- log_info("delete-self-handler: delete-self");
+ log_info("delete-self-handler for %s: delete-self", path);
c->delete_self_handler_called = true;
} else if (ev->mask & IN_IGNORED) {
- log_info("delete-self-handler: ignore");
+ log_info("delete-self-handler for %s: ignore", path);
} else
assert_not_reached();
@@ -480,7 +490,7 @@ static void test_inotify_one(unsigned n_create_events) {
.n_create_events = n_create_events,
};
sd_event *e = NULL;
- const char *q;
+ const char *q, *pp;
unsigned i;
log_info("/* %s(%u) */", __func__, n_create_events);
@@ -488,6 +498,7 @@ static void test_inotify_one(unsigned n_create_events) {
assert_se(sd_event_default(&e) >= 0);
assert_se(mkdtemp_malloc("/tmp/test-inotify-XXXXXX", &p) >= 0);
+ context.path = p;
assert_se(sd_event_add_inotify(e, &a, p, IN_CREATE|IN_ONLYDIR, inotify_handler, &context) >= 0);
assert_se(sd_event_add_inotify(e, &b, p, IN_CREATE|IN_DELETE|IN_DONT_FOLLOW, inotify_handler, &context) >= 0);
@@ -500,6 +511,13 @@ static void test_inotify_one(unsigned n_create_events) {
assert_se(sd_event_source_set_description(b, "1") >= 0);
assert_se(sd_event_source_set_description(c, "2") >= 0);
+ assert_se(sd_event_source_get_inotify_path(a, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+ assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+ assert_se(sd_event_source_get_inotify_path(b, &pp) >= 0);
+ assert_se(path_equal_or_inode_same(pp, p, 0));
+
q = strjoina(p, "/sub");
assert_se(touch(q) >= 0);
assert_se(sd_event_add_inotify(e, &d, q, IN_DELETE_SELF, delete_self_handler, &context) >= 0);
@@ -556,7 +574,7 @@ TEST(pidfd) {
int pidfd;
pid_t pid, pid2;
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
pid = fork();
if (pid == 0)
diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c
index f163314..f623d47 100644
--- a/src/libsystemd/sd-hwdb/sd-hwdb.c
+++ b/src/libsystemd/sd-hwdb/sd-hwdb.c
@@ -313,15 +313,15 @@ static int hwdb_new(const char *path, sd_hwdb **ret) {
if (!hwdb->f)
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
- "hwdb.bin does not exist, please run 'systemd-hwdb update'");
+ "hwdb.bin does not exist, please run 'systemd-hwdb update'.");
}
if (fstat(fileno(hwdb->f), &hwdb->st) < 0)
return log_debug_errno(errno, "Failed to stat %s: %m", path);
if (hwdb->st.st_size < (off_t) offsetof(struct trie_header_f, strings_len) + 8)
- return log_debug_errno(SYNTHETIC_ERRNO(EIO), "File %s is too short: %m", path);
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO), "File %s is too short.", path);
if (file_offset_beyond_memory_size(hwdb->st.st_size))
- return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "File %s is too long: %m", path);
+ return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "File %s is too long.", path);
hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0);
if (hwdb->map == MAP_FAILED)
@@ -330,7 +330,7 @@ static int hwdb_new(const char *path, sd_hwdb **ret) {
if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 ||
(size_t) hwdb->st.st_size != le64toh(hwdb->head->file_size))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
- "Failed to recognize the format of %s", path);
+ "Failed to recognize the format of %s.", path);
log_debug("=== trie on-disk ===");
log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version));
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index b9714ee..298d21e 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -9,9 +9,12 @@
#include "hexdecoct.h"
#include "id128-util.h"
#include "io-util.h"
+#include "namespace-util.h"
+#include "process-util.h"
#include "sha256.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
#include "sync-util.h"
#include "virt.h"
@@ -192,11 +195,11 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) {
}
void id128_hash_func(const sd_id128_t *p, struct siphash *state) {
- siphash24_compress(p, sizeof(sd_id128_t), state);
+ siphash24_compress_typesafe(*p, state);
}
int id128_compare_func(const sd_id128_t *a, const sd_id128_t *b) {
- return memcmp(a, b, 16);
+ return memcmp(a, b, sizeof(sd_id128_t));
}
sd_id128_t id128_make_v4_uuid(sd_id128_t id) {
@@ -231,11 +234,15 @@ int id128_get_product(sd_id128_t *ret) {
* of the host */
return -ENOENT;
- r = id128_read("/sys/class/dmi/id/product_uuid", ID128_FORMAT_UUID, &uuid);
- if (r == -ENOENT)
- r = id128_read("/proc/device-tree/vm,uuid", ID128_FORMAT_UUID, &uuid);
- if (r == -ENOENT)
- r = id128_read("/sys/hypervisor/uuid", ID128_FORMAT_UUID, &uuid);
+ FOREACH_STRING(i,
+ "/sys/class/dmi/id/product_uuid", /* KVM */
+ "/proc/device-tree/vm,uuid", /* Device tree */
+ "/sys/hypervisor/uuid") { /* Xen */
+
+ r = id128_read(i, ID128_FORMAT_UUID, &uuid);
+ if (r != -ENOENT)
+ break;
+ }
if (r < 0)
return r;
@@ -263,3 +270,64 @@ sd_id128_t id128_digest(const void *data, size_t size) {
return id128_make_v4_uuid(id);
}
+
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) {
+ _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
+ _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+ pid_t pid, child;
+ sd_id128_t id;
+ ssize_t k;
+ int r;
+
+ assert(ret);
+
+ if (isempty(machine))
+ return sd_id128_get_boot(ret);
+
+ r = container_get_leader(machine, &pid);
+ if (r < 0)
+ return r;
+
+ r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
+ if (r < 0)
+ return r;
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+ return -errno;
+
+ r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
+ pidnsfd, mntnsfd, -1, -1, rootfd, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ pair[0] = safe_close(pair[0]);
+
+ r = id128_get_boot(&id);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL);
+ if (k != sizeof(id))
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ pair[1] = safe_close(pair[1]);
+
+ r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS)
+ return -EIO;
+
+ k = recv(pair[0], &id, sizeof(id), 0);
+ if (k != sizeof(id))
+ return -EIO;
+
+ if (sd_id128_is_null(id))
+ return -EIO;
+
+ *ret = id;
+ return 0;
+}
diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h
index 53ba50a..458d430 100644
--- a/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libsystemd/sd-id128/id128-util.h
@@ -49,6 +49,9 @@ int id128_get_product(sd_id128_t *ret);
sd_id128_t id128_digest(const void *data, size_t size);
+int id128_get_boot(sd_id128_t *ret);
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret);
+
/* A helper to check for the three relevant cases of "machine ID not initialized" */
#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \
IN_SET(r, \
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index 9fda79a..fc1107b 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -13,6 +13,7 @@
#include "hmac.h"
#include "id128-util.h"
#include "io-util.h"
+#include "keyring-util.h"
#include "macro.h"
#include "missing_syscall.h"
#include "missing_threads.h"
@@ -170,14 +171,24 @@ int id128_get_machine(const char *root, sd_id128_t *ret) {
return id128_read_fd(fd, ID128_FORMAT_PLAIN | ID128_REFUSE_NULL, ret);
}
+int id128_get_boot(sd_id128_t *ret) {
+ int r;
+
+ assert(ret);
+
+ r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, ret);
+ if (r == -ENOENT && proc_mounted() == 0)
+ return -ENOSYS;
+
+ return r;
+}
+
_public_ int sd_id128_get_boot(sd_id128_t *ret) {
static thread_local sd_id128_t saved_boot_id = {};
int r;
if (sd_id128_is_null(saved_boot_id)) {
- r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id);
- if (r == -ENOENT && proc_mounted() == 0)
- return -ENOSYS;
+ r = id128_get_boot(&saved_boot_id);
if (r < 0)
return r;
}
@@ -192,7 +203,6 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
char *d, *p, *g, *u, *e;
unsigned long perms;
key_serial_t key;
- size_t sz = 256;
uid_t uid;
gid_t gid;
int r, c;
@@ -211,24 +221,9 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
return -errno;
}
- for (;;) {
- description = new(char, sz);
- if (!description)
- return -ENOMEM;
-
- c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
- if (c < 0)
- return -errno;
-
- if ((size_t) c <= sz)
- break;
-
- sz = c;
- free(description);
- }
-
- /* The kernel returns a final NUL in the string, verify that. */
- assert(description[c-1] == 0);
+ r = keyring_describe(key, &description);
+ if (r < 0)
+ return r;
/* Chop off the final description string */
d = strrchr(description, ';');
@@ -380,3 +375,16 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
return sd_id128_get_app_specific(id, app_id, ret);
}
+
+_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ sd_id128_t id;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_invocation(&id);
+ if (r < 0)
+ return r;
+
+ return sd_id128_get_app_specific(id, app_id, ret);
+}
diff --git a/src/libsystemd/sd-journal/catalog.c b/src/libsystemd/sd-journal/catalog.c
index ae91534..a0b673f 100644
--- a/src/libsystemd/sd-journal/catalog.c
+++ b/src/libsystemd/sd-journal/catalog.c
@@ -16,6 +16,7 @@
#include "conf-files.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "hashmap.h"
#include "log.h"
#include "memory-util.h"
@@ -54,7 +55,7 @@ typedef struct CatalogItem {
} CatalogItem;
static void catalog_hash_func(const CatalogItem *i, struct siphash *state) {
- siphash24_compress(&i->id, sizeof(i->id), state);
+ siphash24_compress_typesafe(i->id, state);
siphash24_compress_string(i->language, state);
}
@@ -222,10 +223,7 @@ static int catalog_entry_lang(
const char* deflang,
char **ret) {
- size_t c;
- char *z;
-
- c = strlen(t);
+ size_t c = strlen(t);
if (c < 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"[%s:%u] Language too short.", filename, line);
@@ -242,12 +240,7 @@ static int catalog_entry_lang(
log_warning("[%s:%u] language differs from default for file", filename, line);
}
- z = strdup(t);
- if (!z)
- return -ENOMEM;
-
- *ret = z;
- return 0;
+ return strdup_to(ret, t);
}
int catalog_import_file(OrderedHashmap *h, const char *path) {
@@ -382,10 +375,8 @@ static int64_t write_catalog(
CatalogItem *items,
size_t n) {
+ _cleanup_(unlink_and_freep) char *p = NULL;
_cleanup_fclose_ FILE *w = NULL;
- _cleanup_free_ char *p = NULL;
- CatalogHeader header;
- size_t k;
int r;
r = mkdir_parents(database, 0755);
@@ -394,54 +385,35 @@ static int64_t write_catalog(
r = fopen_temporary(database, &w, &p);
if (r < 0)
- return log_error_errno(r, "Failed to open database for writing: %s: %m",
- database);
+ return log_error_errno(r, "Failed to open database for writing: %s: %m", database);
- header = (CatalogHeader) {
+ CatalogHeader header = {
.signature = CATALOG_SIGNATURE,
.header_size = htole64(CONST_ALIGN_TO(sizeof(CatalogHeader), 8)),
.catalog_item_size = htole64(sizeof(CatalogItem)),
.n_items = htole64(n),
};
- r = -EIO;
+ if (fwrite(&header, sizeof(header), 1, w) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write header.", p);
- k = fwrite(&header, 1, sizeof(header), w);
- if (k != sizeof(header)) {
- log_error("%s: failed to write header.", p);
- goto error;
- }
+ if (fwrite(items, sizeof(CatalogItem), n, w) != n)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write database.", p);
- k = fwrite(items, 1, n * sizeof(CatalogItem), w);
- if (k != n * sizeof(CatalogItem)) {
- log_error("%s: failed to write database.", p);
- goto error;
- }
-
- k = fwrite(sb->buf, 1, sb->len, w);
- if (k != sb->len) {
- log_error("%s: failed to write strings.", p);
- goto error;
- }
+ if (fwrite(sb->buf, sb->len, 1, w) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "%s: failed to write strings.", p);
r = fflush_and_check(w);
- if (r < 0) {
- log_error_errno(r, "%s: failed to write database: %m", p);
- goto error;
- }
+ if (r < 0)
+ return log_error_errno(r, "%s: failed to write database: %m", p);
(void) fchmod(fileno(w), 0644);
- if (rename(p, database) < 0) {
- r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
- goto error;
- }
+ if (rename(p, database) < 0)
+ return log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database);
+ p = mfree(p); /* free without unlinking */
return ftello(w);
-
-error:
- (void) unlink(p);
- return r;
}
int catalog_update(const char* database, const char* root, const char* const* dirs) {
@@ -472,11 +444,12 @@ int catalog_update(const char* database, const char* root, const char* const* di
return log_error_errno(r, "Failed to import file '%s': %m", *f);
}
- if (ordered_hashmap_size(h) <= 0) {
+ if (ordered_hashmap_isempty(h)) {
log_info("No items in catalog.");
return 0;
- } else
- log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
+ }
+
+ log_debug("Found %u items in catalog.", ordered_hashmap_size(h));
items = new(CatalogItem, ordered_hashmap_size(h));
if (!items)
@@ -607,15 +580,14 @@ static const char *find_id(void *p, sd_id128_t id) {
le64toh(f->offset);
}
-int catalog_get(const char* database, sd_id128_t id, char **_text) {
+int catalog_get(const char* database, sd_id128_t id, char **ret_text) {
_cleanup_close_ int fd = -EBADF;
void *p = NULL;
- struct stat st = {};
- char *text = NULL;
+ struct stat st;
int r;
const char *s;
- assert(_text);
+ assert(ret_text);
r = open_mmap(database, &fd, &st, &p);
if (r < 0)
@@ -627,18 +599,9 @@ int catalog_get(const char* database, sd_id128_t id, char **_text) {
goto finish;
}
- text = strdup(s);
- if (!text) {
- r = -ENOMEM;
- goto finish;
- }
-
- *_text = text;
- r = 0;
-
+ r = strdup_to(ret_text, s);
finish:
- if (p)
- munmap(p, st.st_size);
+ (void) munmap(p, st.st_size);
return r;
}
diff --git a/src/libsystemd/sd-journal/catalog.h b/src/libsystemd/sd-journal/catalog.h
index df27869..b5a97fa 100644
--- a/src/libsystemd/sd-journal/catalog.h
+++ b/src/libsystemd/sd-journal/catalog.h
@@ -11,7 +11,7 @@
int catalog_import_file(OrderedHashmap *h, const char *path);
int catalog_update(const char* database, const char* root, const char* const* dirs);
-int catalog_get(const char* database, sd_id128_t id, char **data);
+int catalog_get(const char* database, sd_id128_t id, char **ret_text);
int catalog_list(FILE *f, const char* database, bool oneline);
int catalog_list_items(FILE *f, const char* database, bool oneline, char **items);
int catalog_file_lang(const char *filename, char **lang);
diff --git a/src/libsystemd/sd-journal/fsprg.c b/src/libsystemd/sd-journal/fsprg.c
index e86be6a..85632b0 100644
--- a/src/libsystemd/sd-journal/fsprg.c
+++ b/src/libsystemd/sd-journal/fsprg.c
@@ -3,21 +3,6 @@
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
* Copyright © 2012 B. Poettering
* Contact: fsprg@point-at-infinity.org
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
/*
@@ -50,11 +35,11 @@ static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) {
unsigned len;
size_t nwritten;
- assert(gcry_mpi_cmp_ui(x, 0) >= 0);
- len = (gcry_mpi_get_nbits(x) + 7) / 8;
+ assert(sym_gcry_mpi_cmp_ui(x, 0) >= 0);
+ len = (sym_gcry_mpi_get_nbits(x) + 7) / 8;
assert(len <= buflen);
memzero(buf, buflen);
- gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
+ sym_gcry_mpi_print(GCRYMPI_FMT_USG, buf + (buflen - len), len, &nwritten, x);
assert(nwritten == len);
}
@@ -62,10 +47,10 @@ static gcry_mpi_t mpi_import(const void *buf, size_t buflen) {
gcry_mpi_t h;
_unused_ unsigned len;
- assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
- len = (gcry_mpi_get_nbits(h) + 7) / 8;
+ assert_se(sym_gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0);
+ len = (sym_gcry_mpi_get_nbits(h) + 7) / 8;
assert(len <= buflen);
- assert(gcry_mpi_cmp_ui(h, 0) >= 0);
+ assert(sym_gcry_mpi_cmp_ui(h, 0) >= 0);
return h;
}
@@ -102,30 +87,30 @@ static void det_randomize(void *buf, size_t buflen, const void *seed, size_t see
gcry_error_t err;
uint32_t ctr;
- olen = gcry_md_get_algo_dlen(RND_HASH);
- err = gcry_md_open(&hd, RND_HASH, 0);
+ olen = sym_gcry_md_get_algo_dlen(RND_HASH);
+ err = sym_gcry_md_open(&hd, RND_HASH, 0);
assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
- gcry_md_write(hd, seed, seedlen);
- gcry_md_putc(hd, (idx >> 24) & 0xff);
- gcry_md_putc(hd, (idx >> 16) & 0xff);
- gcry_md_putc(hd, (idx >> 8) & 0xff);
- gcry_md_putc(hd, (idx >> 0) & 0xff);
+ sym_gcry_md_write(hd, seed, seedlen);
+ sym_gcry_md_putc(hd, (idx >> 24) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 16) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 8) & 0xff);
+ sym_gcry_md_putc(hd, (idx >> 0) & 0xff);
for (ctr = 0; buflen; ctr++) {
- err = gcry_md_copy(&hd2, hd);
+ err = sym_gcry_md_copy(&hd2, hd);
assert_se(gcry_err_code(err) == GPG_ERR_NO_ERROR); /* This shouldn't happen */
- gcry_md_putc(hd2, (ctr >> 24) & 0xff);
- gcry_md_putc(hd2, (ctr >> 16) & 0xff);
- gcry_md_putc(hd2, (ctr >> 8) & 0xff);
- gcry_md_putc(hd2, (ctr >> 0) & 0xff);
- gcry_md_final(hd2);
+ sym_gcry_md_putc(hd2, (ctr >> 24) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 16) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 8) & 0xff);
+ sym_gcry_md_putc(hd2, (ctr >> 0) & 0xff);
+ sym_gcry_md_ctl(hd2, GCRYCTL_FINALIZE, NULL, 0);
cpylen = (buflen < olen) ? buflen : olen;
- memcpy(buf, gcry_md_read(hd2, RND_HASH), cpylen);
- gcry_md_close(hd2);
+ memcpy(buf, sym_gcry_md_read(hd2, RND_HASH), cpylen);
+ sym_gcry_md_close(hd2);
buf += cpylen;
buflen -= cpylen;
}
- gcry_md_close(hd);
+ sym_gcry_md_close(hd);
}
/* deterministically generate from seed/idx a prime of length `bits' that is 3 (mod 4) */
@@ -142,8 +127,8 @@ static gcry_mpi_t genprime3mod4(int bits, const void *seed, size_t seedlen, uint
buf[buflen - 1] |= 0x03; /* set lower two bits, to have result 3 (mod 4) */
p = mpi_import(buf, buflen);
- while (gcry_prime_check(p, 0))
- gcry_mpi_add_ui(p, p, 4);
+ while (sym_gcry_prime_check(p, 0))
+ sym_gcry_mpi_add_ui(p, p, 4);
return p;
}
@@ -157,8 +142,8 @@ static gcry_mpi_t gensquare(const gcry_mpi_t n, const void *seed, size_t seedlen
det_randomize(buf, buflen, seed, seedlen, idx);
buf[0] &= 0x7f; /* clear upper bit, so that we have x < n */
x = mpi_import(buf, buflen);
- assert(gcry_mpi_cmp(x, n) < 0);
- gcry_mpi_mulm(x, x, x, n);
+ assert(sym_gcry_mpi_cmp(x, n) < 0);
+ sym_gcry_mpi_mulm(x, x, x, n);
return x;
}
@@ -167,51 +152,51 @@ static gcry_mpi_t twopowmodphi(uint64_t m, const gcry_mpi_t p) {
gcry_mpi_t phi, r;
int n;
- phi = gcry_mpi_new(0);
- gcry_mpi_sub_ui(phi, p, 1);
+ phi = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_sub_ui(phi, p, 1);
/* count number of used bits in m */
for (n = 0; (1ULL << n) <= m; n++)
;
- r = gcry_mpi_new(0);
- gcry_mpi_set_ui(r, 1);
+ r = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_set_ui(r, 1);
while (n) { /* square and multiply algorithm for fast exponentiation */
n--;
- gcry_mpi_mulm(r, r, r, phi);
+ sym_gcry_mpi_mulm(r, r, r, phi);
if (m & ((uint64_t)1 << n)) {
- gcry_mpi_add(r, r, r);
- if (gcry_mpi_cmp(r, phi) >= 0)
- gcry_mpi_sub(r, r, phi);
+ sym_gcry_mpi_add(r, r, r);
+ if (sym_gcry_mpi_cmp(r, phi) >= 0)
+ sym_gcry_mpi_sub(r, r, phi);
}
}
- gcry_mpi_release(phi);
+ sym_gcry_mpi_release(phi);
return r;
}
/* Decompose $x \in Z_n$ into $(xp,xq) \in Z_p \times Z_q$ using Chinese Remainder Theorem */
static void CRT_decompose(gcry_mpi_t *xp, gcry_mpi_t *xq, const gcry_mpi_t x, const gcry_mpi_t p, const gcry_mpi_t q) {
- *xp = gcry_mpi_new(0);
- *xq = gcry_mpi_new(0);
- gcry_mpi_mod(*xp, x, p);
- gcry_mpi_mod(*xq, x, q);
+ *xp = sym_gcry_mpi_new(0);
+ *xq = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mod(*xp, x, p);
+ sym_gcry_mpi_mod(*xq, x, q);
}
/* Compose $(xp,xq) \in Z_p \times Z_q$ into $x \in Z_n$ using Chinese Remainder Theorem */
static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, const gcry_mpi_t p, const gcry_mpi_t q) {
gcry_mpi_t a, u;
- a = gcry_mpi_new(0);
- u = gcry_mpi_new(0);
- *x = gcry_mpi_new(0);
- gcry_mpi_subm(a, xq, xp, q);
- gcry_mpi_invm(u, p, q);
- gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
- gcry_mpi_mul(*x, p, a);
- gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
- gcry_mpi_release(a);
- gcry_mpi_release(u);
+ a = sym_gcry_mpi_new(0);
+ u = sym_gcry_mpi_new(0);
+ *x = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_subm(a, xq, xp, q);
+ sym_gcry_mpi_invm(u, p, q);
+ sym_gcry_mpi_mulm(a, a, u, q); /* a = (xq - xp) / p (mod q) */
+ sym_gcry_mpi_mul(*x, p, a);
+ sym_gcry_mpi_add(*x, *x, xp); /* x = p * ((xq - xp) / p mod q) + xp */
+ sym_gcry_mpi_release(a);
+ sym_gcry_mpi_release(u);
}
/******************************************************************************/
@@ -245,18 +230,21 @@ static uint16_t read_secpar(const void *buf) {
return 16 * (secpar + 1);
}
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned _secpar) {
uint8_t iseed[FSPRG_RECOMMENDED_SEEDLEN];
gcry_mpi_t n, p, q;
uint16_t secpar;
+ int r;
VALIDATE_SECPAR(_secpar);
secpar = _secpar;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
if (!seed) {
- gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
+ sym_gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM);
seed = iseed;
seedlen = FSPRG_RECOMMENDED_SEEDLEN;
}
@@ -271,25 +259,30 @@ void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigne
}
if (mpk) {
- n = gcry_mpi_new(0);
- gcry_mpi_mul(n, p, q);
- assert(gcry_mpi_get_nbits(n) == secpar);
+ n = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mul(n, p, q);
+ assert(sym_gcry_mpi_get_nbits(n) == secpar);
store_secpar(mpk + 0, secpar);
mpi_export(mpk + 2, secpar / 8, n);
- gcry_mpi_release(n);
+ sym_gcry_mpi_release(n);
}
- gcry_mpi_release(p);
- gcry_mpi_release(q);
+ sym_gcry_mpi_release(p);
+ sym_gcry_mpi_release(q);
+
+ return 0;
}
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen) {
gcry_mpi_t n, x;
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(mpk + 0);
n = mpi_import(mpk + 2, secpar / 8);
@@ -299,30 +292,37 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
memzero(state + 2 + 2 * secpar / 8, 8);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+
+ return 0;
}
-void FSPRG_Evolve(void *state) {
+int FSPRG_Evolve(void *state) {
gcry_mpi_t n, x;
uint16_t secpar;
uint64_t epoch;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(state + 0);
n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8);
x = mpi_import(state + 2 + 1 * secpar / 8, secpar / 8);
epoch = uint64_import(state + 2 + 2 * secpar / 8, 8);
- gcry_mpi_mulm(x, x, x, n);
+ sym_gcry_mpi_mulm(x, x, x, n);
epoch++;
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, x);
uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+
+ return 0;
}
uint64_t FSPRG_GetEpoch(const void *state) {
@@ -331,18 +331,21 @@ uint64_t FSPRG_GetEpoch(const void *state) {
return uint64_import(state + 2 + 2 * secpar / 8, 8);
}
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen) {
gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm;
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(msk + 0);
p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8);
q = mpi_import(msk + 2 + 1 * (secpar / 2) / 8, (secpar / 2) / 8);
- n = gcry_mpi_new(0);
- gcry_mpi_mul(n, p, q);
+ n = sym_gcry_mpi_new(0);
+ sym_gcry_mpi_mul(n, p, q);
x = gensquare(n, seed, seedlen, RND_GEN_X, secpar);
CRT_decompose(&xp, &xq, x, p, q); /* split (mod n) into (mod p) and (mod q) using CRT */
@@ -350,8 +353,8 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
kp = twopowmodphi(epoch, p); /* compute 2^epoch (mod phi(p)) */
kq = twopowmodphi(epoch, q); /* compute 2^epoch (mod phi(q)) */
- gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
- gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
+ sym_gcry_mpi_powm(xp, xp, kp, p); /* compute x^(2^epoch) (mod p) */
+ sym_gcry_mpi_powm(xq, xq, kq, q); /* compute x^(2^epoch) (mod q) */
CRT_compose(&xm, xp, xq, p, q); /* combine (mod p) and (mod q) to (mod n) using CRT */
@@ -360,22 +363,29 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed,
mpi_export(state + 2 + 1 * secpar / 8, secpar / 8, xm);
uint64_export(state + 2 + 2 * secpar / 8, 8, epoch);
- gcry_mpi_release(p);
- gcry_mpi_release(q);
- gcry_mpi_release(n);
- gcry_mpi_release(x);
- gcry_mpi_release(xp);
- gcry_mpi_release(xq);
- gcry_mpi_release(kp);
- gcry_mpi_release(kq);
- gcry_mpi_release(xm);
+ sym_gcry_mpi_release(p);
+ sym_gcry_mpi_release(q);
+ sym_gcry_mpi_release(n);
+ sym_gcry_mpi_release(x);
+ sym_gcry_mpi_release(xp);
+ sym_gcry_mpi_release(xq);
+ sym_gcry_mpi_release(kp);
+ sym_gcry_mpi_release(kq);
+ sym_gcry_mpi_release(xm);
+
+ return 0;
}
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) {
uint16_t secpar;
+ int r;
- initialize_libgcrypt(false);
+ r = initialize_libgcrypt(false);
+ if (r < 0)
+ return r;
secpar = read_secpar(state + 0);
det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx);
+
+ return 0;
}
diff --git a/src/libsystemd/sd-journal/fsprg.h b/src/libsystemd/sd-journal/fsprg.h
index d3d88aa..a0594cd 100644
--- a/src/libsystemd/sd-journal/fsprg.h
+++ b/src/libsystemd/sd-journal/fsprg.h
@@ -5,21 +5,6 @@
* fsprg v0.1 - (seekable) forward-secure pseudorandom generator
* Copyright © 2012 B. Poettering
* Contact: fsprg@point-at-infinity.org
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <inttypes.h>
@@ -39,22 +24,22 @@ size_t FSPRG_mpkinbytes(unsigned secpar) _const_;
size_t FSPRG_stateinbytes(unsigned secpar) _const_;
/* Setup msk and mpk. Providing seed != NULL makes this algorithm deterministic. */
-void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
+int FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigned secpar);
/* Initialize state deterministically in dependence on seed. */
/* Note: in case one wants to run only one GenState0 per GenMK it is safe to use
the same seed for both GenMK and GenState0.
*/
-void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
+int FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seedlen);
-void FSPRG_Evolve(void *state);
+int FSPRG_Evolve(void *state);
uint64_t FSPRG_GetEpoch(const void *state) _pure_;
/* Seek to any arbitrary state (by providing msk together with seed from GenState0). */
-void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
+int FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, size_t seedlen);
-void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
+int FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx);
#ifdef __cplusplus
}
diff --git a/src/libsystemd/sd-journal/journal-authenticate.c b/src/libsystemd/sd-journal/journal-authenticate.c
index 8e7533e..64f5739 100644
--- a/src/libsystemd/sd-journal/journal-authenticate.c
+++ b/src/libsystemd/sd-journal/journal-authenticate.c
@@ -71,7 +71,7 @@ int journal_file_append_tag(JournalFile *f) {
return r;
/* Get the HMAC tag and store it in the object */
- memcpy(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH);
+ memcpy(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH);
f->hmac_running = false;
return 0;
@@ -80,6 +80,7 @@ int journal_file_append_tag(JournalFile *f) {
int journal_file_hmac_start(JournalFile *f) {
uint8_t key[256 / 8]; /* Let's pass 256 bit from FSPRG to HMAC */
gcry_error_t err;
+ int r;
assert(f);
@@ -90,13 +91,17 @@ int journal_file_hmac_start(JournalFile *f) {
return 0;
/* Prepare HMAC for next cycle */
- gcry_md_reset(f->hmac);
- FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
- err = gcry_md_setkey(f->hmac, key, sizeof(key));
+ sym_gcry_md_reset(f->hmac);
+
+ r = FSPRG_GetKey(f->fsprg_state, key, sizeof(key), 0);
+ if (r < 0)
+ return r;
+
+ err = sym_gcry_md_setkey(f->hmac, key, sizeof(key));
if (gcry_err_code(err) != GPG_ERR_NO_ERROR)
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
- "gcry_md_setkey() failed with error code: %s",
- gcry_strerror(err));
+ "sym_gcry_md_setkey() failed with error code: %s",
+ sym_gcry_strerror(err));
f->hmac_running = true;
@@ -167,7 +172,10 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
if (epoch == goal)
return 0;
- FSPRG_Evolve(f->fsprg_state);
+ r = FSPRG_Evolve(f->fsprg_state);
+ if (r < 0)
+ return r;
+
epoch = FSPRG_GetEpoch(f->fsprg_state);
if (epoch < goal) {
r = journal_file_append_tag(f);
@@ -180,6 +188,7 @@ int journal_file_fsprg_evolve(JournalFile *f, uint64_t realtime) {
int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
void *msk;
uint64_t epoch;
+ int r;
assert(f);
@@ -195,10 +204,8 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
if (goal == epoch)
return 0;
- if (goal == epoch + 1) {
- FSPRG_Evolve(f->fsprg_state);
- return 0;
- }
+ if (goal == epoch + 1)
+ return FSPRG_Evolve(f->fsprg_state);
} else {
f->fsprg_state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
f->fsprg_state = malloc(f->fsprg_state_size);
@@ -209,10 +216,12 @@ int journal_file_fsprg_seek(JournalFile *f, uint64_t goal) {
log_debug("Seeking FSPRG key to %"PRIu64".", goal);
msk = alloca_safe(FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR));
- FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
- FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
- return 0;
+ r = FSPRG_GenMK(msk, NULL, f->fsprg_seed, f->fsprg_seed_size, FSPRG_RECOMMENDED_SECPAR);
+ if (r < 0)
+ return r;
+
+ return FSPRG_Seek(f->fsprg_state, goal, msk, f->fsprg_seed, f->fsprg_seed_size);
}
int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
@@ -260,25 +269,25 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
} else if (type > OBJECT_UNUSED && o->object.type != type)
return -EBADMSG;
- gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
+ sym_gcry_md_write(f->hmac, o, offsetof(ObjectHeader, payload));
switch (o->object.type) {
case OBJECT_DATA:
/* All but hash and payload are mutable */
- gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
- gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
+ sym_gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash));
+ sym_gcry_md_write(f->hmac, journal_file_data_payload_field(f, o), le64toh(o->object.size) - journal_file_data_payload_offset(f));
break;
case OBJECT_FIELD:
/* Same here */
- gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
- gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
+ sym_gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash));
+ sym_gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload));
break;
case OBJECT_ENTRY:
/* All */
- gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
+ sym_gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum));
break;
case OBJECT_FIELD_HASH_TABLE:
@@ -289,8 +298,8 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin
case OBJECT_TAG:
/* All but the tag itself */
- gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
- gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
+ sym_gcry_md_write(f->hmac, &o->tag.seqnum, sizeof(o->tag.seqnum));
+ sym_gcry_md_write(f->hmac, &o->tag.epoch, sizeof(o->tag.epoch));
break;
default:
return -EINVAL;
@@ -318,10 +327,10 @@ int journal_file_hmac_put_header(JournalFile *f) {
* tail_entry_monotonic, n_data, n_fields, n_tags,
* n_entry_arrays. */
- gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
- gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
- gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
- gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
+ sym_gcry_md_write(f->hmac, f->header->signature, offsetof(Header, state) - offsetof(Header, signature));
+ sym_gcry_md_write(f->hmac, &f->header->file_id, offsetof(Header, tail_entry_boot_id) - offsetof(Header, file_id));
+ sym_gcry_md_write(f->hmac, &f->header->seqnum_id, offsetof(Header, arena_size) - offsetof(Header, seqnum_id));
+ sym_gcry_md_write(f->hmac, &f->header->data_hash_table_offset, offsetof(Header, tail_object_offset) - offsetof(Header, data_hash_table_offset));
return 0;
}
@@ -406,13 +415,16 @@ int journal_file_fss_load(JournalFile *f) {
int journal_file_hmac_setup(JournalFile *f) {
gcry_error_t e;
+ int r;
if (!JOURNAL_HEADER_SEALED(f->header))
return 0;
- initialize_libgcrypt(true);
+ r = initialize_libgcrypt(true);
+ if (r < 0)
+ return r;
- e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
+ e = sym_gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC);
if (e != 0)
return -EOPNOTSUPP;
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index 08cbf86..7e941ed 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -20,6 +20,7 @@
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "gcrypt-util.h"
#include "id128-util.h"
#include "journal-authenticate.h"
#include "journal-def.h"
@@ -47,10 +48,6 @@
#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
#define MIN_COMPRESS_THRESHOLD (8ULL)
-#define U64_KB UINT64_C(1024)
-#define U64_MB (UINT64_C(1024) * U64_KB)
-#define U64_GB (UINT64_C(1024) * U64_MB)
-
/* This is the minimum journal file size */
#define JOURNAL_FILE_SIZE_MIN (512 * U64_KB) /* 512 KiB */
#define JOURNAL_COMPACT_SIZE_MAX ((uint64_t) UINT32_MAX) /* 4 GiB */
@@ -285,6 +282,8 @@ JournalFile* journal_file_close(JournalFile *f) {
assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL);
+ sd_event_source_disable_unref(f->post_change_timer);
+
if (f->cache_fd)
mmap_cache_fd_free(f->cache_fd);
@@ -309,7 +308,7 @@ JournalFile* journal_file_close(JournalFile *f) {
free(f->fsprg_seed);
if (f->hmac)
- gcry_md_close(f->hmac);
+ sym_gcry_md_close(f->hmac);
#endif
return mfree(f);
@@ -2249,7 +2248,6 @@ static int journal_file_link_entry(
f->header->tail_entry_monotonic = o->entry.monotonic;
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset))
f->header->tail_entry_offset = htole64(offset);
- f->newest_mtime = 0; /* we have a new tail entry now, explicitly invalidate newest boot id/timestamp info */
/* Link up the items */
for (uint64_t i = 0; i < n_items; i++) {
@@ -2762,7 +2760,7 @@ static int generic_array_get(
Object **ret_object, /* The found object. */
uint64_t *ret_offset) { /* The offset of the found object. */
- uint64_t a, t = 0, k;
+ uint64_t a, t = 0, k = 0; /* Explicit initialization of k to appease gcc */
ChainCacheItem *ci;
Object *o = NULL;
int r;
@@ -3052,7 +3050,7 @@ static int generic_array_bisect(
left = 0;
right = m - 1;
- if (direction == DIRECTION_UP) {
+ if (direction == DIRECTION_UP && left < right) {
/* If we're going upwards, the last entry of the previous array may pass the test,
* and the first entry of the current array may not pass. In that case, the last
* entry of the previous array must be returned. Hence, we need to test the first
@@ -3167,10 +3165,21 @@ previous:
if (direction == DIRECTION_DOWN)
return 0;
- /* Indicate to go to the previous array later. Note, do not move to the previous array here,
- * as that may invalidate the current array object in the mmap cache and
- * journal_file_entry_array_item() below may read invalid address. */
- i = UINT64_MAX;
+ /* Get the last entry of the previous array. */
+ r = bump_entry_array(f, NULL, a, first, DIRECTION_UP, &a);
+ if (r <= 0)
+ return r;
+
+ r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
+ if (r < 0)
+ return r;
+
+ p = journal_file_entry_array_n_items(f, array);
+ if (p == 0 || t < p)
+ return -EBADMSG;
+
+ t -= p;
+ i = p - 1;
found:
p = journal_file_entry_array_item(f, array, 0);
@@ -3180,27 +3189,6 @@ found:
/* Let's cache this item for the next invocation */
chain_cache_put(f->chain_cache, ci, first, a, p, t, i);
- if (i == UINT64_MAX) {
- uint64_t m;
-
- /* Get the last entry of the previous array. */
-
- r = bump_entry_array(f, NULL, a, first, DIRECTION_UP, &a);
- if (r <= 0)
- return r;
-
- r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &array);
- if (r < 0)
- return r;
-
- m = journal_file_entry_array_n_items(f, array);
- if (m == 0 || t < m)
- return -EBADMSG;
-
- t -= m;
- i = m - 1;
- }
-
p = journal_file_entry_array_item(f, array, i);
if (p == 0)
return -EBADMSG;
@@ -3267,7 +3255,7 @@ static int generic_array_bisect_for_data(
} else {
/* If we are going upwards, then we need to return the last object that passes the test.
- * When there is no object that passes the test, we need to return the the last object that
+ * When there is no object that passes the test, we need to return the last object that
* test_object() returns TEST_LEFT for. */
if (r == TEST_RIGHT)
return 0; /* Not only the 'extra' object, but also all objects in the chained arrays
@@ -3554,7 +3542,8 @@ int journal_file_next_entry(
p,
test_object_offset,
direction,
- ret_object ? &o : NULL, &q, &i);
+ NULL, &q, &i); /* Here, do not read entry object, as the result object
+ * may not be the one we want, and it may be broken. */
if (r <= 0)
return r;
@@ -3564,12 +3553,11 @@ int journal_file_next_entry(
* the same offset, and the index needs to be shifted. Otherwise, use the found object as is,
* as it is the nearest entry object from the input offset 'p'. */
- if (p != q)
- goto found;
-
- r = bump_array_index(&i, direction, n);
- if (r <= 0)
- return r;
+ if (p == q) {
+ r = bump_array_index(&i, direction, n);
+ if (r <= 0)
+ return r;
+ }
/* And jump to it */
r = generic_array_get(f, le64toh(f->header->entry_array_offset), i, direction, ret_object ? &o : NULL, &q);
@@ -3581,7 +3569,7 @@ int journal_file_next_entry(
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
"%s: entry array not properly ordered at entry index %" PRIu64,
f->path, i);
-found:
+
if (ret_object)
*ret_object = o;
if (ret_offset)
@@ -3813,35 +3801,35 @@ void journal_file_dump(JournalFile *f) {
case OBJECT_ENTRY:
assert(s);
- printf("Type: %s seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n",
- s,
- le64toh(o->entry.seqnum),
- le64toh(o->entry.monotonic),
- le64toh(o->entry.realtime));
+ log_info("Type: %s seqnum=%"PRIu64" monotonic=%"PRIu64" realtime=%"PRIu64"\n",
+ s,
+ le64toh(o->entry.seqnum),
+ le64toh(o->entry.monotonic),
+ le64toh(o->entry.realtime));
break;
case OBJECT_TAG:
assert(s);
- printf("Type: %s seqnum=%"PRIu64" epoch=%"PRIu64"\n",
- s,
- le64toh(o->tag.seqnum),
- le64toh(o->tag.epoch));
+ log_info("Type: %s seqnum=%"PRIu64" epoch=%"PRIu64"\n",
+ s,
+ le64toh(o->tag.seqnum),
+ le64toh(o->tag.epoch));
break;
default:
if (s)
- printf("Type: %s \n", s);
+ log_info("Type: %s \n", s);
else
- printf("Type: unknown (%i)", o->object.type);
+ log_info("Type: unknown (%i)", o->object.type);
break;
}
c = COMPRESSION_FROM_OBJECT(o);
if (c > COMPRESSION_NONE)
- printf("Flags: %s\n",
- compression_to_string(c));
+ log_info("Flags: %s\n",
+ compression_to_string(c));
if (p == le64toh(f->header->tail_object_offset))
p = 0;
@@ -4374,11 +4362,9 @@ int journal_file_archive(JournalFile *f, char **ret_previous_path) {
(void) fsync_directory_of_file(f->fd);
if (ret_previous_path)
- *ret_previous_path = f->path;
- else
- free(f->path);
+ *ret_previous_path = TAKE_PTR(f->path);
- f->path = TAKE_PTR(p);
+ free_and_replace(f->path, p);
/* Set as archive so offlining commits w/state=STATE_ARCHIVED. Previously we would set old_file->header->state
* to STATE_ARCHIVED directly here, but journal_file_set_offline() short-circuits when state != STATE_ONLINE,
diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h
index 81fafb9..8100388 100644
--- a/src/libsystemd/sd-journal/journal-file.h
+++ b/src/libsystemd/sd-journal/journal-file.h
@@ -129,7 +129,8 @@ typedef struct JournalFile {
uint64_t newest_monotonic_usec;
uint64_t newest_realtime_usec;
unsigned newest_boot_id_prioq_idx;
- usec_t newest_mtime;
+ uint64_t newest_entry_offset;
+ uint8_t newest_state;
} JournalFile;
typedef enum JournalFileFlags {
@@ -309,7 +310,6 @@ void journal_file_print_header(JournalFile *f);
int journal_file_archive(JournalFile *f, char **ret_previous_path);
int journal_file_parse_uid_from_filename(const char *path, uid_t *uid);
-JournalFile* journal_initiate_close(JournalFile *f, Set *deferred_closes);
int journal_file_dispose(int dir_fd, const char *fname);
diff --git a/src/libsystemd/sd-journal/journal-internal.h b/src/libsystemd/sd-journal/journal-internal.h
index 259aac8..b95080c 100644
--- a/src/libsystemd/sd-journal/journal-internal.h
+++ b/src/libsystemd/sd-journal/journal-internal.h
@@ -12,7 +12,7 @@
#include "journal-def.h"
#include "journal-file.h"
#include "list.h"
-#include "set.h"
+#include "prioq.h"
#define JOURNAL_FILES_MAX 7168u
@@ -62,12 +62,18 @@ struct Location {
};
struct Directory {
+ sd_journal *journal;
char *path;
int wd;
bool is_root;
unsigned last_seen_generation;
};
+typedef struct NewestByBootId {
+ sd_id128_t boot_id;
+ Prioq *prioq; /* JournalFile objects ordered by monotonic timestamp of last update. */
+} NewestByBootId;
+
struct sd_journal {
int toplevel_fd;
@@ -78,7 +84,10 @@ struct sd_journal {
OrderedHashmap *files;
IteratedCache *files_cache;
MMapCache *mmap;
- Hashmap *newest_by_boot_id; /* key: boot_id, value: prioq, ordered by monotonic timestamp of last update */
+
+ /* a bisectable array of NewestByBootId, ordered by boot id. */
+ NewestByBootId *newest_by_boot_id;
+ size_t n_newest_by_boot_id;
Location current_location;
@@ -86,6 +95,7 @@ struct sd_journal {
uint64_t current_field;
Match *level0, *level1, *level2;
+ Set *exclude_syslog_identifiers;
uint64_t origin_id;
@@ -128,6 +138,10 @@ struct sd_journal {
char *journal_make_match_string(sd_journal *j);
void journal_print_header(sd_journal *j);
+int journal_get_directories(sd_journal *j, char ***ret);
+
+int journal_add_match_pair(sd_journal *j, const char *field, const char *value);
+int journal_add_matchf(sd_journal *j, const char *format, ...) _printf_(2, 3);
#define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval) \
for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; )
diff --git a/src/libsystemd/sd-journal/journal-send.c b/src/libsystemd/sd-journal/journal-send.c
index be23b2f..7d02b57 100644
--- a/src/libsystemd/sd-journal/journal-send.c
+++ b/src/libsystemd/sd-journal/journal-send.c
@@ -121,7 +121,7 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
assert_return(priority <= 7, -EINVAL);
assert_return(format, -EINVAL);
- xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
+ xsprintf(p, "PRIORITY=%i", LOG_PRI(priority));
va_copy(aq, ap);
len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
@@ -398,20 +398,28 @@ _public_ int sd_journal_perror(const char *message) {
return fill_iovec_perror_and_send(message, 0, iovec);
}
-_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+_public_ int sd_journal_stream_fd_with_namespace(
+ const char *name_space,
+ const char *identifier,
+ int priority,
+ int level_prefix) {
+
_cleanup_close_ int fd = -EBADF;
- char *header;
- size_t l;
+ const char *path;
int r;
assert_return(priority >= 0, -EINVAL);
assert_return(priority <= 7, -EINVAL);
+ path = journal_stream_path(name_space);
+ if (!path)
+ return -EINVAL;
+
fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
if (fd < 0)
return -errno;
- r = connect_unix_path(fd, AT_FDCWD, "/run/systemd/journal/stdout");
+ r = connect_unix_path(fd, AT_FDCWD, path);
if (r < 0)
return r;
@@ -422,6 +430,9 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
identifier = strempty(identifier);
+ char *header;
+ size_t l;
+
l = strlen(identifier);
header = newa(char, l + 1 + 1 + 2 + 2 + 2 + 2 + 2);
@@ -446,6 +457,10 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve
return TAKE_FD(fd);
}
+_public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) {
+ return sd_journal_stream_fd_with_namespace(NULL, identifier, priority, level_prefix);
+}
+
_public_ int sd_journal_print_with_location(int priority, const char *file, const char *line, const char *func, const char *format, ...) {
int r;
va_list ap;
@@ -470,7 +485,7 @@ _public_ int sd_journal_printv_with_location(int priority, const char *file, con
assert_return(priority <= 7, -EINVAL);
assert_return(format, -EINVAL);
- xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
+ xsprintf(p, "PRIORITY=%i", LOG_PRI(priority));
va_copy(aq, ap);
len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
diff --git a/src/libsystemd/sd-journal/journal-send.h b/src/libsystemd/sd-journal/journal-send.h
index 24315e2..6fe6325 100644
--- a/src/libsystemd/sd-journal/journal-send.h
+++ b/src/libsystemd/sd-journal/journal-send.h
@@ -2,6 +2,23 @@
#pragma once
#include <stdbool.h>
+#include <stddef.h>
+
+#include "syslog-util.h"
int journal_fd_nonblock(bool nonblock);
void close_journal_fd(void);
+
+/* We declare sd_journal_stream_fd() as async-signal-safe. So instead of strjoin(), which calls malloc()
+ * internally, use a macro + alloca(). */
+#define journal_stream_path(log_namespace) \
+ ({ \
+ const char *_ns = (log_namespace), *_ret; \
+ if (!_ns) \
+ _ret = "/run/systemd/journal/stdout"; \
+ else if (log_namespace_name_valid(_ns)) \
+ _ret = strjoina("/run/systemd/journal.", _ns, "/stdout"); \
+ else \
+ _ret = NULL; \
+ _ret; \
+ })
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index b5ce55a..e852591 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -10,6 +10,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
+#include "gcrypt-util.h"
#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
@@ -1224,7 +1225,7 @@ int journal_file_verify(
if (r < 0)
goto fail;
- if (memcmp(o->tag.tag, gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
+ if (memcmp(o->tag.tag, sym_gcry_md_read(f->hmac, 0), TAG_LENGTH) != 0) {
error(p, "Tag failed verification");
r = -EBADMSG;
goto fail;
diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c
index ca1ef0c..0aa3726 100644
--- a/src/libsystemd/sd-journal/sd-journal.c
+++ b/src/libsystemd/sd-journal/sd-journal.c
@@ -38,12 +38,13 @@
#include "prioq.h"
#include "process-util.h"
#include "replace-var.h"
+#include "sort-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "syslog-util.h"
-#include "uid-alloc-range.h"
+#include "uid-classification.h"
#define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
@@ -232,7 +233,11 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
assert_return(!journal_origin_changed(j), -ECHILD);
assert_return(data, -EINVAL);
- if (size == 0)
+ /* If the size is unspecified, assume it's a string. Note: 0 is the public value we document for
+ * this, for historical reasons. Internally, we pretty widely started using SIZE_MAX for this in
+ * similar cases however, hence accept that too. And internally we actually prefer it, to make things
+ * less surprising. */
+ if (IN_SET(size, 0, SIZE_MAX))
size = strlen(data);
if (!match_is_valid(data, size))
@@ -324,6 +329,37 @@ fail:
return -ENOMEM;
}
+int journal_add_match_pair(sd_journal *j, const char *field, const char *value) {
+ _cleanup_free_ char *s = NULL;
+
+ assert(j);
+ assert(field);
+ assert(value);
+
+ s = strjoin(field, "=", value);
+ if (!s)
+ return -ENOMEM;
+
+ return sd_journal_add_match(j, s, SIZE_MAX);
+}
+
+int journal_add_matchf(sd_journal *j, const char *format, ...) {
+ _cleanup_free_ char *s = NULL;
+ va_list ap;
+ int r;
+
+ assert(j);
+ assert(format);
+
+ va_start(ap, format);
+ r = vasprintf(&s, format, ap);
+ va_end(ap);
+ if (r < 0)
+ return -ENOMEM;
+
+ return sd_journal_add_match(j, s, SIZE_MAX);
+}
+
_public_ int sd_journal_add_conjunction(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
@@ -413,6 +449,99 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
detach_location(j);
}
+static int newest_by_boot_id_compare(const NewestByBootId *a, const NewestByBootId *b) {
+ return id128_compare_func(&a->boot_id, &b->boot_id);
+}
+
+static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
+ NewestByBootId *found;
+
+ assert(j);
+ assert(f);
+
+ if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
+ return;
+
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ assert(found);
+
+ assert_se(prioq_remove(found->prioq, f, &f->newest_boot_id_prioq_idx) > 0);
+ f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
+
+ /* The prioq may be empty, but that should not cause any issue. Let's keep it. */
+}
+
+static void journal_clear_newest_by_boot_id(sd_journal *j) {
+ FOREACH_ARRAY(i, j->newest_by_boot_id, j->n_newest_by_boot_id) {
+ JournalFile *f;
+
+ while ((f = prioq_peek(i->prioq)))
+ journal_file_unlink_newest_by_boot_id(j, f);
+
+ prioq_free(i->prioq);
+ }
+
+ j->newest_by_boot_id = mfree(j->newest_by_boot_id);
+ j->n_newest_by_boot_id = 0;
+}
+
+static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
+ const JournalFile *x = a, *y = b;
+
+ return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
+}
+
+static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
+ NewestByBootId *found;
+ int r;
+
+ assert(j);
+ assert(f);
+
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = f->newest_boot_id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ if (found) {
+ /* There's already a priority queue for this boot ID */
+
+ if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
+ r = prioq_put(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
+ if (r < 0)
+ return r;
+ } else
+ prioq_reshuffle(found->prioq, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
+
+ } else {
+ _cleanup_(prioq_freep) Prioq *q = NULL;
+
+ /* No priority queue yet, then allocate one */
+
+ assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
+
+ q = prioq_new(journal_file_newest_monotonic_compare);
+ if (!q)
+ return -ENOMEM;
+
+ r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC(j->newest_by_boot_id, j->n_newest_by_boot_id + 1)) {
+ f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
+ return -ENOMEM;
+ }
+
+ j->newest_by_boot_id[j->n_newest_by_boot_id++] = (NewestByBootId) {
+ .boot_id = f->newest_boot_id,
+ .prioq = TAKE_PTR(q),
+ };
+
+ typesafe_qsort(j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+ }
+
+ return 0;
+}
+
static int journal_file_find_newest_for_boot_id(
sd_journal *j,
sd_id128_t id,
@@ -427,16 +556,17 @@ static int journal_file_find_newest_for_boot_id(
/* Before we use it, let's refresh the timestamp from the header, and reshuffle our prioq
* accordingly. We do this only a bunch of times, to not be caught in some update loop. */
for (unsigned n_tries = 0;; n_tries++) {
+ NewestByBootId *found;
JournalFile *f;
- Prioq *q;
- q = hashmap_get(j->newest_by_boot_id, &id);
- if (!q)
+ found = typesafe_bsearch(&(NewestByBootId) { .boot_id = id },
+ j->newest_by_boot_id, j->n_newest_by_boot_id, newest_by_boot_id_compare);
+
+ f = found ? prioq_peek(found->prioq) : NULL;
+ if (!f)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA),
"Requested delta for boot ID %s, but we have no information about that boot ID.", SD_ID128_TO_STRING(id));
- assert_se(f = prioq_peek(q)); /* we delete hashmap entries once the prioq is empty, so this must hold */
-
if (f == prev || n_tries >= 5) {
/* This was already the best answer in the previous run, or we tried too often, use it */
*ret = f;
@@ -449,6 +579,11 @@ static int journal_file_find_newest_for_boot_id(
r = journal_file_read_tail_timestamp(j, f);
if (r < 0)
return log_debug_errno(r, "Failed to read tail timestamp while trying to find newest journal file for boot ID %s.", SD_ID128_TO_STRING(id));
+ if (r == 0) {
+ /* No new entry found. */
+ *ret = f;
+ return 0;
+ }
/* Refreshing the timestamp we read might have reshuffled the prioq, hence let's check the
* prioq again and only use the information once we reached an equilibrium or hit a limit */
@@ -932,8 +1067,8 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
if (r < 0)
return r;
- for (unsigned i = 0; i < n_files; i++) {
- JournalFile *f = (JournalFile *)files[i];
+ FOREACH_ARRAY(_f, files, n_files) {
+ JournalFile *f = (JournalFile*) *_f;
bool found;
r = next_beyond_location(j, f, direction);
@@ -1373,17 +1508,6 @@ static void track_file_disposition(sd_journal *j, JournalFile *f) {
j->has_persistent_files = true;
}
-static const char *skip_slash(const char *p) {
-
- if (!p)
- return NULL;
-
- while (*p == '/')
- p++;
-
- return p;
-}
-
static int add_any_file(
sd_journal *j,
int fd,
@@ -1403,7 +1527,7 @@ static int add_any_file(
/* If there's a top-level fd defined make the path relative, explicitly, since otherwise
* openat() ignores the first argument. */
- fd = our_fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ fd = our_fd = openat(j->toplevel_fd, skip_leading_slash(path), O_RDONLY|O_CLOEXEC|O_NONBLOCK);
else
fd = our_fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0) {
@@ -1494,6 +1618,41 @@ error:
return r;
}
+int journal_get_directories(sd_journal *j, char ***ret) {
+ _cleanup_strv_free_ char **paths = NULL;
+ JournalFile *f;
+ const char *p;
+ size_t n = SIZE_MAX;
+ int r;
+
+ assert(j);
+ assert(ret);
+
+ /* This returns parent directories of opened journal files. */
+
+ ORDERED_HASHMAP_FOREACH_KEY(f, p, j->files) {
+ _cleanup_free_ char *d = NULL;
+
+ /* Ignore paths generated from fd. */
+ if (path_startswith(p, "/proc/"))
+ continue;
+
+ r = path_extract_directory(p, &d);
+ if (r < 0)
+ return r;
+
+ if (path_strv_contains(paths, d))
+ continue;
+
+ r = strv_extend_with_size(&paths, &n, d);
+ if (r < 0)
+ return r;
+ }
+
+ *ret = TAKE_PTR(paths);
+ return 0;
+}
+
static int add_file_by_name(
sd_journal *j,
const char *prefix,
@@ -1676,7 +1835,7 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) {
else
/* Open the specified directory relative to the toplevel fd. Enforce that the path specified is
* relative, by dropping the initial slash */
- d = xopendirat(j->toplevel_fd, skip_slash(path), 0);
+ d = xopendirat(j->toplevel_fd, skip_leading_slash(path), 0);
if (!d)
return -errno;
@@ -1684,6 +1843,100 @@ static int directory_open(sd_journal *j, const char *path, DIR **ret) {
return 0;
}
+static Directory* directory_free(Directory *d) {
+ if (!d)
+ return NULL;
+
+ if (d->journal) {
+ if (d->wd > 0 &&
+ hashmap_remove_value(d->journal->directories_by_wd, INT_TO_PTR(d->wd), d) &&
+ d->journal->inotify_fd >= 0)
+ (void) inotify_rm_watch(d->journal->inotify_fd, d->wd);
+
+ if (d->path)
+ hashmap_remove_value(d->journal->directories_by_path, d->path, d);
+ }
+
+ if (d->path) {
+ if (d->is_root)
+ log_debug("Root directory %s removed.", d->path);
+ else
+ log_debug("Directory %s removed.", d->path);
+
+ free(d->path);
+ }
+
+ return mfree(d);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Directory*, directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ directories_by_path_hash_ops,
+ char,
+ path_hash_func,
+ path_compare,
+ Directory,
+ directory_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ directories_by_wd_hash_ops,
+ void,
+ trivial_hash_func,
+ trivial_compare_func,
+ Directory,
+ directory_free);
+
+static int add_directory_impl(sd_journal *j, const char *path, bool is_root, Directory **ret) {
+ _cleanup_(directory_freep) Directory *m = NULL;
+ Directory *existing;
+ int r;
+
+ assert(j);
+ assert(path);
+ assert(ret);
+
+ existing = hashmap_get(j->directories_by_path, path);
+ if (existing) {
+ if (existing->is_root != is_root) {
+ /* Don't 'downgrade' from root directory */
+ *ret = NULL;
+ return 0;
+ }
+
+ *ret = existing;
+ return 1;
+ }
+
+ m = new(Directory, 1);
+ if (!m)
+ return -ENOMEM;
+
+ *m = (Directory) {
+ .journal = j,
+ .is_root = is_root,
+ .path = strdup(path),
+ .wd = -1,
+ };
+
+ if (!m->path)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(&j->directories_by_path, &directories_by_path_hash_ops, m->path, m);
+ if (r < 0)
+ return r;
+
+ j->current_invalidate_counter++;
+
+ if (is_root)
+ log_debug("Root directory %s added.", m->path);
+ else
+ log_debug("Directory %s added.", m->path);
+
+ *ret = TAKE_PTR(m);
+ return 1;
+}
+
static int add_directory(sd_journal *j, const char *prefix, const char *dirname);
static void directory_enumerate(sd_journal *j, Directory *m, DIR *d) {
@@ -1724,12 +1977,14 @@ static void directory_watch(sd_journal *j, Directory *m, int fd, uint32_t mask)
return;
}
- r = hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m);
- if (r == -EEXIST)
- log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+ r = hashmap_ensure_put(&j->directories_by_wd, &directories_by_wd_hash_ops, INT_TO_PTR(m->wd), m);
if (r < 0) {
- log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
- (void) inotify_rm_watch(j->inotify_fd, m->wd);
+ if (r == -EEXIST)
+ log_debug_errno(r, "Directory '%s' already being watched under a different path, ignoring: %m", m->path);
+ else {
+ log_debug_errno(r, "Failed to add watch for journal directory '%s' to hashmap, ignoring: %m", m->path);
+ (void) inotify_rm_watch(j->inotify_fd, m->wd);
+ }
m->wd = -1;
}
}
@@ -1775,32 +2030,11 @@ static int add_directory(
goto fail;
}
- m = hashmap_get(j->directories_by_path, path);
- if (!m) {
- m = new(Directory, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- *m = (Directory) {
- .is_root = false,
- .path = path,
- };
-
- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- path = NULL; /* avoid freeing in cleanup */
- j->current_invalidate_counter++;
-
- log_debug("Directory %s added.", m->path);
-
- } else if (m->is_root)
- return 0; /* Don't 'downgrade' from root directory */
+ r = add_directory_impl(j, path, /* is_root = */ false, &m);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
+ return 0;
m->last_seen_generation = j->generation;
@@ -1878,35 +2112,10 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
rewinddir(d);
}
- m = hashmap_get(j->directories_by_path, p);
- if (!m) {
- m = new0(Directory, 1);
- if (!m) {
- r = -ENOMEM;
- goto fail;
- }
-
- m->is_root = true;
-
- m->path = strdup(p);
- if (!m->path) {
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- if (hashmap_put(j->directories_by_path, m->path, m) < 0) {
- free(m->path);
- free(m);
- r = -ENOMEM;
- goto fail;
- }
-
- j->current_invalidate_counter++;
-
- log_debug("Root directory %s added.", m->path);
-
- } else if (!m->is_root)
+ r = add_directory_impl(j, p, /* is_root = */ true, &m);
+ if (r < 0)
+ goto fail;
+ if (r == 0)
return 0;
directory_watch(j, m, dirfd(d),
@@ -1928,27 +2137,6 @@ fail:
return r;
}
-static void remove_directory(sd_journal *j, Directory *d) {
- assert(j);
-
- if (d->wd > 0) {
- hashmap_remove(j->directories_by_wd, INT_TO_PTR(d->wd));
-
- if (j->inotify_fd >= 0)
- (void) inotify_rm_watch(j->inotify_fd, d->wd);
- }
-
- hashmap_remove(j->directories_by_path, d->path);
-
- if (d->is_root)
- log_debug("Root directory %s removed.", d->path);
- else
- log_debug("Directory %s removed.", d->path);
-
- free(d->path);
- free(d);
-}
-
static int add_search_paths(sd_journal *j) {
static const char search_paths[] =
@@ -2003,7 +2191,7 @@ static int allocate_inotify(sd_journal *j) {
return -errno;
}
- return hashmap_ensure_allocated(&j->directories_by_wd, NULL);
+ return 0;
}
static sd_journal *journal_new(int flags, const char *path, const char *namespace) {
@@ -2045,9 +2233,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac
return NULL;
j->files_cache = ordered_hashmap_iterated_cache_new(j->files);
- j->directories_by_path = hashmap_new(&path_hash_ops);
j->mmap = mmap_cache_new();
- if (!j->files_cache || !j->directories_by_path || !j->mmap)
+ if (!j->files_cache || !j->mmap)
return NULL;
return TAKE_PTR(j);
@@ -2059,7 +2246,8 @@ static sd_journal *journal_new(int flags, const char *path, const char *namespac
SD_JOURNAL_SYSTEM | \
SD_JOURNAL_CURRENT_USER | \
SD_JOURNAL_ALL_NAMESPACES | \
- SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE)
+ SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_namespace(sd_journal **ret, const char *namespace, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@@ -2085,7 +2273,9 @@ _public_ int sd_journal_open(sd_journal **ret, int flags) {
}
#define OPEN_CONTAINER_ALLOWED_FLAGS \
- (SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_SYSTEM)
+ (SD_JOURNAL_LOCAL_ONLY | \
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) {
_cleanup_free_ char *root = NULL, *class = NULL;
@@ -2129,7 +2319,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in
#define OPEN_DIRECTORY_ALLOWED_FLAGS \
(SD_JOURNAL_OS_ROOT | \
- SD_JOURNAL_SYSTEM | SD_JOURNAL_CURRENT_USER )
+ SD_JOURNAL_SYSTEM | \
+ SD_JOURNAL_CURRENT_USER | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@@ -2154,12 +2346,15 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f
return 0;
}
+#define OPEN_FILES_ALLOWED_FLAGS \
+ (SD_JOURNAL_ASSUME_IMMUTABLE)
+
_public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
int r;
assert_return(ret, -EINVAL);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~OPEN_FILES_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL, NULL);
if (!j)
@@ -2181,7 +2376,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla
(SD_JOURNAL_OS_ROOT | \
SD_JOURNAL_SYSTEM | \
SD_JOURNAL_CURRENT_USER | \
- SD_JOURNAL_TAKE_DIRECTORY_FD)
+ SD_JOURNAL_TAKE_DIRECTORY_FD | \
+ SD_JOURNAL_ASSUME_IMMUTABLE)
_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@@ -2219,6 +2415,9 @@ _public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) {
return 0;
}
+#define OPEN_FILES_FD_ALLOWED_FLAGS \
+ (SD_JOURNAL_ASSUME_IMMUTABLE)
+
_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) {
JournalFile *f;
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
@@ -2226,7 +2425,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd
assert_return(ret, -EINVAL);
assert_return(n_fds > 0, -EBADF);
- assert_return(flags == 0, -EINVAL);
+ assert_return((flags & ~OPEN_FILES_FD_ALLOWED_FLAGS) == 0, -EINVAL);
j = journal_new(flags, NULL, NULL);
if (!j)
@@ -2270,27 +2469,16 @@ fail:
}
_public_ void sd_journal_close(sd_journal *j) {
- Directory *d;
- Prioq *p;
-
if (!j || journal_origin_changed(j))
return;
- while ((p = hashmap_first(j->newest_by_boot_id)))
- journal_file_unlink_newest_by_boot_id(j, prioq_peek(p));
- hashmap_free(j->newest_by_boot_id);
+ journal_clear_newest_by_boot_id(j);
sd_journal_flush_matches(j);
ordered_hashmap_free_with_destructor(j->files, journal_file_close);
iterated_cache_free(j->files_cache);
- while ((d = hashmap_first(j->directories_by_path)))
- remove_directory(j, d);
-
- while ((d = hashmap_first(j->directories_by_wd)))
- remove_directory(j, d);
-
hashmap_free(j->directories_by_path);
hashmap_free(j->directories_by_wd);
@@ -2306,6 +2494,8 @@ _public_ void sd_journal_close(sd_journal *j) {
hashmap_free_free(j->errors);
+ set_free(j->exclude_syslog_identifiers);
+
free(j->path);
free(j->prefix);
free(j->namespace);
@@ -2314,84 +2504,6 @@ _public_ void sd_journal_close(sd_journal *j) {
free(j);
}
-static void journal_file_unlink_newest_by_boot_id(sd_journal *j, JournalFile *f) {
- JournalFile *nf;
- Prioq *p;
-
- assert(j);
- assert(f);
-
- if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) /* not linked currently, hence this is a NOP */
- return;
-
- assert_se(p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id));
- assert_se(prioq_remove(p, f, &f->newest_boot_id_prioq_idx) > 0);
-
- nf = prioq_peek(p);
- if (nf)
- /* There's still a member in the prioq? Then make sure the hashmap key now points to its
- * .newest_boot_id field (and not ours!). Not we only replace the memory of the key here, the
- * value of the key (and the data associated with it) remain the same. */
- assert_se(hashmap_replace(j->newest_by_boot_id, &nf->newest_boot_id, p) >= 0);
- else {
- assert_se(hashmap_remove(j->newest_by_boot_id, &f->newest_boot_id) == p);
- prioq_free(p);
- }
-
- f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
-}
-
-static int journal_file_newest_monotonic_compare(const void *a, const void *b) {
- const JournalFile *x = a, *y = b;
-
- return -CMP(x->newest_monotonic_usec, y->newest_monotonic_usec); /* Invert order, we want newest first! */
-}
-
-static int journal_file_reshuffle_newest_by_boot_id(sd_journal *j, JournalFile *f) {
- Prioq *p;
- int r;
-
- assert(j);
- assert(f);
-
- p = hashmap_get(j->newest_by_boot_id, &f->newest_boot_id);
- if (p) {
- /* There's already a priority queue for this boot ID */
-
- if (f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL) {
- r = prioq_put(p, f, &f->newest_boot_id_prioq_idx); /* Insert if we aren't in there yet */
- if (r < 0)
- return r;
- } else
- prioq_reshuffle(p, f, &f->newest_boot_id_prioq_idx); /* Reshuffle otherwise */
-
- } else {
- _cleanup_(prioq_freep) Prioq *q = NULL;
-
- /* No priority queue yet, then allocate one */
-
- assert(f->newest_boot_id_prioq_idx == PRIOQ_IDX_NULL); /* we can't be a member either */
-
- q = prioq_new(journal_file_newest_monotonic_compare);
- if (!q)
- return -ENOMEM;
-
- r = prioq_put(q, f, &f->newest_boot_id_prioq_idx);
- if (r < 0)
- return r;
-
- r = hashmap_ensure_put(&j->newest_by_boot_id, &id128_hash_ops, &f->newest_boot_id, q);
- if (r < 0) {
- f->newest_boot_id_prioq_idx = PRIOQ_IDX_NULL;
- return r;
- }
-
- TAKE_PTR(q);
- }
-
- return 0;
-}
-
static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
uint64_t offset, mo, rt;
sd_id128_t id;
@@ -2405,11 +2517,13 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
/* Tries to read the timestamp of the most recently written entry. */
- r = journal_file_fstat(f);
- if (r < 0)
- return r;
- if (f->newest_mtime == timespec_load(&f->last_stat.st_mtim))
- return 0; /* mtime didn't change since last time, don't bother */
+ if (FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE) && f->newest_entry_offset != 0)
+ return 0; /* We have already read the file, and we assume that the file is immutable. */
+
+ if (f->header->state == f->newest_state &&
+ f->header->state == STATE_ARCHIVED &&
+ f->newest_entry_offset != 0)
+ return 0; /* We have already read archived file. */
if (JOURNAL_HEADER_CONTAINS(f->header, tail_entry_offset)) {
offset = le64toh(READ_NOW(f->header->tail_entry_offset));
@@ -2420,6 +2534,8 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
}
if (offset == 0)
return -ENODATA; /* not a single object/entry, hence no tail timestamp */
+ if (offset == f->newest_entry_offset)
+ return 0; /* No new entry is added after we read last time. */
/* Move to the last object in the journal file, in the hope it is an entry (which it usually will
* be). If we lack the "tail_entry_offset" field in the header, we specify the type as OBJECT_UNUSED
@@ -2429,6 +2545,7 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
if (r < 0) {
log_debug_errno(r, "Failed to move to last object in journal file, ignoring: %m");
o = NULL;
+ offset = 0;
}
if (o && o->object.type == OBJECT_ENTRY) {
/* Yay, last object is an entry, let's use the data. */
@@ -2446,10 +2563,11 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
mo = le64toh(f->header->tail_entry_monotonic);
rt = le64toh(f->header->tail_entry_realtime);
id = f->header->tail_entry_boot_id;
+ offset = UINT64_MAX;
} else {
/* Otherwise let's find the last entry manually (this possibly means traversing the
* chain of entry arrays, till the end */
- r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, NULL);
+ r = journal_file_next_entry(f, 0, DIRECTION_UP, &o, offset == 0 ? &offset : NULL);
if (r < 0)
return r;
if (r == 0)
@@ -2464,6 +2582,17 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
if (mo > rt) /* monotonic clock is further ahead than realtime? that's weird, refuse to use the data */
return -ENODATA;
+ if (offset == f->newest_entry_offset) {
+ /* Cached data and the current one should be equivalent. */
+ if (!sd_id128_equal(f->newest_machine_id, f->header->machine_id) ||
+ !sd_id128_equal(f->newest_boot_id, id) ||
+ f->newest_monotonic_usec != mo ||
+ f->newest_realtime_usec != rt)
+ return -EBADMSG;
+
+ return 0; /* No new entry is added after we read last time. */
+ }
+
if (!sd_id128_equal(f->newest_boot_id, id))
journal_file_unlink_newest_by_boot_id(j, f);
@@ -2471,13 +2600,14 @@ static int journal_file_read_tail_timestamp(sd_journal *j, JournalFile *f) {
f->newest_monotonic_usec = mo;
f->newest_realtime_usec = rt;
f->newest_machine_id = f->header->machine_id;
- f->newest_mtime = timespec_load(&f->last_stat.st_mtim);
+ f->newest_entry_offset = offset;
+ f->newest_state = f->header->state;
r = journal_file_reshuffle_newest_by_boot_id(j, f);
if (r < 0)
return r;
- return 0;
+ return 1; /* Updated. */
}
_public_ int sd_journal_get_realtime_usec(sd_journal *j, uint64_t *ret) {
@@ -2526,9 +2656,7 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
if (r < 0)
return r;
- if (ret_boot_id)
- *ret_boot_id = o->entry.boot_id;
- else {
+ if (!ret_boot_id) {
sd_id128_t id;
r = sd_id128_get_boot(&id);
@@ -2545,6 +2673,8 @@ _public_ int sd_journal_get_monotonic_usec(sd_journal *j, uint64_t *ret, sd_id12
if (ret)
*ret = t;
+ if (ret_boot_id)
+ *ret_boot_id = o->entry.boot_id;
return 0;
}
@@ -2748,6 +2878,7 @@ _public_ int sd_journal_get_fd(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
if (j->no_inotify)
return -EMEDIUMTYPE;
@@ -2774,6 +2905,7 @@ _public_ int sd_journal_get_events(sd_journal *j) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
fd = sd_journal_get_fd(j);
if (fd < 0)
@@ -2787,6 +2919,7 @@ _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
assert_return(timeout_usec, -EINVAL);
fd = sd_journal_get_fd(j);
@@ -2839,7 +2972,7 @@ static void process_q_overflow(sd_journal *j) {
continue;
log_debug("Directory '%s' hasn't been seen in this enumeration, removing.", f->path);
- remove_directory(j, m);
+ directory_free(m);
}
log_debug("Reiteration complete.");
@@ -2875,7 +3008,7 @@ static void process_inotify_event(sd_journal *j, const struct inotify_event *e)
/* Event for a subdirectory */
if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT))
- remove_directory(j, d);
+ directory_free(d);
} else if (d->is_root && (e->mask & IN_ISDIR) && e->len > 0 && id128_is_valid(e->name)) {
@@ -2914,6 +3047,8 @@ _public_ int sd_journal_process(sd_journal *j) {
if (j->inotify_fd < 0) /* We have no inotify fd yet? Then there's noting to process. */
return 0;
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
+
j->last_process_usec = now(CLOCK_MONOTONIC);
j->last_invalidate_counter = j->current_invalidate_counter;
@@ -2942,6 +3077,7 @@ _public_ int sd_journal_wait(sd_journal *j, uint64_t timeout_usec) {
assert_return(j, -EINVAL);
assert_return(!journal_origin_changed(j), -ECHILD);
+ assert_return(!FLAGS_SET(j->flags, SD_JOURNAL_ASSUME_IMMUTABLE), -EUNATCH);
if (j->inotify_fd < 0) {
JournalFile *f;
diff --git a/src/libsystemd/sd-journal/test-journal-append.c b/src/libsystemd/sd-journal/test-journal-append.c
index 24b98c8..b7e7c78 100644
--- a/src/libsystemd/sd-journal/test-journal-append.c
+++ b/src/libsystemd/sd-journal/test-journal-append.c
@@ -62,7 +62,7 @@ static int journal_corrupt_and_append(uint64_t start_offset, uint64_t step) {
log_debug("Opening journal %s/system.journal", tempdir);
r = journal_file_open(
- /* fd= */ -1,
+ /* fd= */ -EBADF,
"system.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS,
@@ -114,7 +114,7 @@ static int journal_corrupt_and_append(uint64_t start_offset, uint64_t step) {
* the corrupted journal */
mj = journal_file_offline_close(mj);
r = journal_file_open(
- /* fd= */ -1,
+ /* fd= */ -EBADF,
"system.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS,
diff --git a/src/libsystemd/sd-journal/test-journal-enum.c b/src/libsystemd/sd-journal/test-journal-enum.c
index 03fe8e2..d101fe7 100644
--- a/src/libsystemd/sd-journal/test-journal-enum.c
+++ b/src/libsystemd/sd-journal/test-journal-enum.c
@@ -15,10 +15,10 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
- assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY) >= 0);
+ assert_se(sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", 0) >= 0);
- assert_se(sd_journal_add_match(j, "_UID=0", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "_TRANSPORT=syslog", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "_UID=0", SIZE_MAX) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
const void *d;
diff --git a/src/libsystemd/sd-journal/test-journal-flush.c b/src/libsystemd/sd-journal/test-journal-flush.c
index 3f07835..b06645a 100644
--- a/src/libsystemd/sd-journal/test-journal-flush.c
+++ b/src/libsystemd/sd-journal/test-journal-flush.c
@@ -7,6 +7,8 @@
#include "alloc-util.h"
#include "chattr-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
#include "journal-file-util.h"
#include "journal-internal.h"
#include "logs-show.h"
@@ -17,6 +19,83 @@
#include "tests.h"
#include "tmpfile-util.h"
+static int open_archive_file(sd_journal **ret) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_close_ int newest_fd = -EBADF;
+ unsigned long long newest_realtime = 0;
+ bool newest_is_system = false;
+ sd_id128_t machine_id;
+ const char *p;
+ int r;
+
+ r = sd_id128_get_machine(&machine_id);
+ if (r < 0)
+ return r;
+
+ p = strjoina("/var/log/journal/", SD_ID128_TO_STRING(machine_id), "/");
+
+ d = opendir(p);
+ if (!d)
+ return -errno;
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+ unsigned long long realtime;
+ bool is_system;
+ size_t q;
+ int fd;
+
+ if (!dirent_is_file_with_suffix(de, ".journal"))
+ continue;
+
+ is_system = startswith(de->d_name, "system@");
+ if (newest_is_system && !is_system)
+ continue;
+
+ q = strlen(de->d_name);
+
+ if (q < 1 + 32 + 1 + 16 + 1 + 16 + 8)
+ continue;
+
+ if (de->d_name[q-8-16-1] != '-' ||
+ de->d_name[q-8-16-1-16-1] != '-' ||
+ de->d_name[q-8-16-1-16-1-32-1] != '@')
+ continue;
+
+ if (sscanf(de->d_name + q-8-16, "%16llx.journal", &realtime) != 1)
+ continue;
+
+ if (newest_realtime >= realtime)
+ continue;
+
+ fd = openat(dirfd(d), de->d_name, O_CLOEXEC | O_NONBLOCK | O_RDONLY);
+ if (fd < 0) {
+ log_info_errno(errno, "Failed to open /var/log/journal/%s, ignoring: %m", de->d_name);
+ continue;
+ }
+
+ close_and_replace(newest_fd, fd);
+ newest_realtime = realtime;
+ newest_is_system = is_system;
+ }
+
+ if (newest_fd < 0)
+ return log_info_errno(SYNTHETIC_ERRNO(ENOENT), "No archive journal found.");
+
+ r = sd_journal_open_files_fd(ret, &newest_fd, 1, SD_JOURNAL_ASSUME_IMMUTABLE);
+
+ _cleanup_free_ char *path = NULL;
+ (void) fd_get_path(newest_fd, &path);
+
+ if (r < 0)
+ log_info_errno(r, "Failed to open %s, ignoring: %m", strna(path));
+ else {
+ log_info("Opened %s.", strna(path));
+ TAKE_FD(newest_fd);
+ }
+
+ return r;
+}
+
static void test_journal_flush_one(int argc, char *argv[]) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
_cleanup_free_ char *fn = NULL;
@@ -32,13 +111,16 @@ static void test_journal_flush_one(int argc, char *argv[]) {
assert_se(fn = path_join(dn, "test.journal"));
- r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0, 0644, 0, NULL, m, NULL, &new_journal);
+ r = journal_file_open(-EBADF, fn, O_CREAT|O_RDWR, 0, 0644, 0, NULL, m, NULL, &new_journal);
assert_se(r >= 0);
if (argc > 1)
- r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), 0);
- else
- r = sd_journal_open(&j, 0);
+ r = sd_journal_open_files(&j, (const char **) strv_skip(argv, 1), SD_JOURNAL_ASSUME_IMMUTABLE);
+ else {
+ r = open_archive_file(&j);
+ if (r < 0)
+ r = sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE);
+ }
assert_se(r == 0);
sd_journal_set_data_threshold(j, 0);
@@ -75,7 +157,7 @@ static void test_journal_flush_one(int argc, char *argv[]) {
/* Open the new journal before archiving and offlining the file. */
sd_journal_close(j);
- assert_se(sd_journal_open_directory(&j, dn, 0) >= 0);
+ assert_se(sd_journal_open_directory(&j, dn, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
/* Read the online journal. */
assert_se(sd_journal_seek_tail(j) >= 0);
diff --git a/src/libsystemd/sd-journal/test-journal-init.c b/src/libsystemd/sd-journal/test-journal-init.c
index c8a1977..ef66efd 100644
--- a/src/libsystemd/sd-journal/test-journal-init.c
+++ b/src/libsystemd/sd-journal/test-journal-init.c
@@ -31,12 +31,12 @@ int main(int argc, char *argv[]) {
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
for (i = 0; i < I; i++) {
- r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY | SD_JOURNAL_ASSUME_IMMUTABLE);
assert_se(r == 0);
sd_journal_close(j);
- r = sd_journal_open_directory(&j, t, 0);
+ r = sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE);
assert_se(r == 0);
assert_se(sd_journal_seek_head(j) == 0);
@@ -45,8 +45,8 @@ int main(int argc, char *argv[]) {
r = safe_fork("(journal-fork-test)", FORK_WAIT|FORK_LOG, NULL);
if (r == 0) {
assert_se(j);
- assert_se(sd_journal_get_realtime_usec(j, NULL) == -ECHILD);
- assert_se(sd_journal_seek_tail(j) == -ECHILD);
+ ASSERT_RETURN_EXPECTED_SE(sd_journal_get_realtime_usec(j, NULL) == -ECHILD);
+ ASSERT_RETURN_EXPECTED_SE(sd_journal_seek_tail(j) == -ECHILD);
assert_se(j->current_location.type == LOCATION_HEAD);
sd_journal_close(j);
_exit(EXIT_SUCCESS);
@@ -57,8 +57,7 @@ int main(int argc, char *argv[]) {
sd_journal_close(j);
j = NULL;
- r = sd_journal_open_directory(&j, t, SD_JOURNAL_LOCAL_ONLY);
- assert_se(r == -EINVAL);
+ ASSERT_RETURN_EXPECTED(assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_LOCAL_ONLY) == -EINVAL));
assert_se(j == NULL);
}
diff --git a/src/libsystemd/sd-journal/test-journal-interleaving.c b/src/libsystemd/sd-journal/test-journal-interleaving.c
index 8aeef8f..d98b3ce 100644
--- a/src/libsystemd/sd-journal/test-journal-interleaving.c
+++ b/src/libsystemd/sd-journal/test-journal-interleaving.c
@@ -43,7 +43,7 @@ static JournalFile *test_open_internal(const char *name, JournalFileFlags flags)
m = mmap_cache_new();
assert_se(m != NULL);
- assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, flags, 0644, UINT64_MAX, NULL, m, NULL, &f));
+ assert_ret(journal_file_open(-EBADF, name, O_RDWR|O_CREAT, flags, 0644, UINT64_MAX, NULL, m, NULL, &f));
return f;
}
@@ -74,9 +74,9 @@ static void test_done(const char *t) {
}
static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint64_t *seqnum, uint64_t *ret_offset) {
- _cleanup_free_ char *p = NULL, *q = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL, *s = NULL;
dual_timestamp ts;
- struct iovec iovec[2];
+ struct iovec iovec[3];
size_t n_iov = 0;
dual_timestamp_now(&ts);
@@ -92,6 +92,9 @@ static void append_number(JournalFile *f, int n, const sd_id128_t *boot_id, uint
assert_se(asprintf(&p, "NUMBER=%d", n) >= 0);
iovec[n_iov++] = IOVEC_MAKE_STRING(p);
+ assert_se(s = strjoin("LESS_THAN_FIVE=%d", yes_no(n < 5)));
+ iovec[n_iov++] = IOVEC_MAKE_STRING(s);
+
if (boot_id) {
assert_se(q = strjoin("_BOOT_ID=", SD_ID128_TO_STRING(*boot_id)));
iovec[n_iov++] = IOVEC_MAKE_STRING(q);
@@ -250,6 +253,37 @@ static void mkdtemp_chdir_chattr(char *path) {
(void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
}
+static void test_cursor(sd_journal *j) {
+ _cleanup_strv_free_ char **cursors = NULL;
+ int r;
+
+ assert_se(sd_journal_seek_head(j) >= 0);
+
+ for (;;) {
+ r = sd_journal_next(j);
+ assert_se(r >= 0);
+ if (r == 0)
+ break;
+
+ _cleanup_free_ char *cursor = NULL;
+ assert_se(sd_journal_get_cursor(j, &cursor) >= 0);
+ assert_se(sd_journal_test_cursor(j, cursor) > 0);
+ assert_se(strv_consume(&cursors, TAKE_PTR(cursor)) >= 0);
+ }
+
+ STRV_FOREACH(c, cursors) {
+ assert_se(sd_journal_seek_cursor(j, *c) >= 0);
+ assert_se(sd_journal_next(j) >= 0);
+ assert_se(sd_journal_test_cursor(j, *c) > 0);
+ }
+
+ assert_se(sd_journal_seek_head(j) >= 0);
+ STRV_FOREACH(c, cursors) {
+ assert_se(sd_journal_next(j) >= 0);
+ assert_se(sd_journal_test_cursor(j, *c) > 0);
+ }
+}
+
static void test_skip_one(void (*setup)(void)) {
char t[] = "/var/tmp/journal-skip-XXXXXX";
sd_journal *j;
@@ -260,14 +294,14 @@ static void test_skip_one(void (*setup)(void)) {
setup();
/* Seek to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_se(sd_journal_previous(j) == 0); /* no-op */
@@ -275,7 +309,7 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to head twice, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
assert_ret(sd_journal_seek_head(j));
@@ -284,7 +318,7 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to head, move to previous, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 1); /* pointing to the first entry */
@@ -292,7 +326,7 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to head, walk several steps, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 0); /* no-op */
@@ -304,14 +338,14 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to tail, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to tail twice, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
assert_ret(sd_journal_seek_tail(j));
@@ -320,7 +354,7 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to tail, move to next, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_previous(j) == 1); /* pointing to the last entry */
@@ -328,7 +362,7 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to tail, walk several steps, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0); /* no-op */
assert_se(sd_journal_next(j) == 0); /* no-op */
@@ -340,14 +374,14 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to tail, skip to head, iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_previous_skip(j, 9) == 9); /* pointing to the first entry. */
test_check_numbers_down(j, 9);
sd_journal_close(j);
/* Seek to tail, skip to head in a more complex way, then iterate down. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_tail(j));
assert_se(sd_journal_next(j) == 0);
assert_se(sd_journal_previous_skip(j, 4) == 4);
@@ -366,14 +400,14 @@ static void test_skip_one(void (*setup)(void)) {
sd_journal_close(j);
/* Seek to head, skip to tail, iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_next_skip(j, 9) == 9);
test_check_numbers_up(j, 9);
sd_journal_close(j);
/* Seek to head, skip to tail in a more complex way, then iterate up. */
- assert_ret(sd_journal_open_directory(&j, t, 0));
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
assert_ret(sd_journal_seek_head(j));
assert_se(sd_journal_previous(j) == 0);
assert_se(sd_journal_next_skip(j, 4) == 4);
@@ -391,6 +425,30 @@ static void test_skip_one(void (*setup)(void)) {
test_check_numbers_up(j, 9);
sd_journal_close(j);
+ /* For issue #31516. */
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=no", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=hoge", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+ test_cursor(j);
+ sd_journal_flush_matches(j);
+ assert_se(sd_journal_add_match(j, "LESS_THAN_FIVE=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=3", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=4", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=5", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=6", SIZE_MAX) >= 0);
+ test_cursor(j);
+
test_done(t);
}
@@ -401,7 +459,7 @@ TEST(skip) {
static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
char t[] = "/var/tmp/journal-boot-id-XXXXXX";
- sd_journal *j;
+ _cleanup_(sd_journal_closep) sd_journal *j = NULL;
_cleanup_free_ BootId *boots = NULL;
size_t n_boots;
@@ -409,28 +467,59 @@ static void test_boot_id_one(void (*setup)(void), size_t n_boots_expected) {
setup();
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_se(journal_get_boots(j, &boots, &n_boots) >= 0);
+ assert_ret(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE));
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ false, /* max_ids = */ SIZE_MAX,
+ &boots, &n_boots) >= 0);
assert_se(boots);
assert_se(n_boots == n_boots_expected);
- sd_journal_close(j);
- FOREACH_ARRAY(b, boots, n_boots) {
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_se(journal_find_boot_by_id(j, b->id) == 1);
- sd_journal_close(j);
+ for (size_t i = 0; i < n_boots; i++) {
+ sd_id128_t id;
+
+ /* positive offset */
+ assert_se(journal_find_boot(j, SD_ID128_NULL, (int) (i + 1), &id) == 1);
+ assert_se(sd_id128_equal(id, boots[i].id));
+
+ /* negative offset */
+ assert_se(journal_find_boot(j, SD_ID128_NULL, (int) (i + 1) - (int) n_boots, &id) == 1);
+ assert_se(sd_id128_equal(id, boots[i].id));
+
+ for (size_t k = 0; k < n_boots; k++) {
+ int offset = (int) k - (int) i;
+
+ /* relative offset */
+ assert_se(journal_find_boot(j, boots[i].id, offset, &id) == 1);
+ assert_se(sd_id128_equal(id, boots[k].id));
+ }
}
- for (int i = - (int) n_boots + 1; i <= (int) n_boots; i++) {
- sd_id128_t id;
+ for (size_t i = 0; i <= n_boots_expected + 1; i++) {
+ _cleanup_free_ BootId *boots_limited = NULL;
+ size_t n_boots_limited;
+
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ false, /* max_ids = */ i,
+ &boots_limited, &n_boots_limited) >= 0);
+ assert_se(boots_limited || i == 0);
+ assert_se(n_boots_limited == MIN(i, n_boots_expected));
+ assert_se(memcmp_safe(boots, boots_limited, n_boots_limited * sizeof(BootId)) == 0);
+ }
- assert_ret(sd_journal_open_directory(&j, t, 0));
- assert_se(journal_find_boot_by_offset(j, i, &id) == 1);
- if (i <= 0)
- assert_se(sd_id128_equal(id, boots[n_boots + i - 1].id));
- else
- assert_se(sd_id128_equal(id, boots[i - 1].id));
- sd_journal_close(j);
+ for (size_t i = 0; i <= n_boots_expected + 1; i++) {
+ _cleanup_free_ BootId *boots_limited = NULL;
+ size_t n_boots_limited;
+
+ assert_se(journal_get_boots(
+ j,
+ /* advance_older = */ true, /* max_ids = */ i,
+ &boots_limited, &n_boots_limited) >= 0);
+ assert_se(boots_limited || i == 0);
+ assert_se(n_boots_limited == MIN(i, n_boots_expected));
+ for (size_t k = 0; k < n_boots_limited; k++)
+ assert_se(memcmp(&boots[n_boots - k - 1], &boots_limited[k], sizeof(BootId)) == 0);
}
test_done(t);
@@ -453,7 +542,7 @@ static void test_sequence_numbers_one(void) {
mkdtemp_chdir_chattr(t);
- assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
+ assert_se(journal_file_open(-EBADF, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
UINT64_MAX, NULL, m, NULL, &one) == 0);
append_number(one, 1, NULL, &seqnum, NULL);
@@ -470,7 +559,7 @@ static void test_sequence_numbers_one(void) {
memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t));
- assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
+ assert_se(journal_file_open(-EBADF, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
UINT64_MAX, NULL, m, one, &two) == 0);
assert_se(two->header->state == STATE_ONLINE);
@@ -502,12 +591,12 @@ static void test_sequence_numbers_one(void) {
test_close(one);
/* If the machine-id is not initialized, the header file verification
- * (which happens when re-opening a journal file) will fail. */
+ * (which happens when reopening a journal file) will fail. */
if (sd_id128_get_machine(NULL) >= 0) {
/* restart server */
seqnum = 0;
- assert_se(journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
+ assert_se(journal_file_open(-EBADF, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0,
UINT64_MAX, NULL, m, NULL, &two) == 0);
assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id));
@@ -567,7 +656,41 @@ static int expected_result(uint64_t needle, const uint64_t *candidates, const ui
}
}
-static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offset, size_t n) {
+static int expected_result_next(uint64_t needle, const uint64_t *candidates, const uint64_t *offset, size_t n, direction_t direction, uint64_t *ret) {
+ switch (direction) {
+ case DIRECTION_DOWN:
+ for (size_t i = 0; i < n; i++)
+ if (needle < offset[i]) {
+ *ret = candidates[i];
+ return candidates[i] > 0;
+ }
+ *ret = 0;
+ return 0;
+
+ case DIRECTION_UP:
+ for (size_t i = 0; i < n; i++)
+ if (needle <= offset[i]) {
+ n = i;
+ break;
+ }
+
+ for (; n > 0 && candidates[n - 1] == 0; n--)
+ ;
+
+ if (n == 0) {
+ *ret = 0;
+ return 0;
+ }
+
+ *ret = candidates[n - 1];
+ return candidates[n - 1] > 0;
+
+ default:
+ assert_not_reached();
+ }
+}
+
+static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offset_candidates, const uint64_t *offset, size_t n) {
uint64_t p, q;
int r, e;
@@ -664,12 +787,81 @@ static void verify(JournalFile *f, const uint64_t *seqnum, const uint64_t *offse
assert_se(r == e);
assert_se(p == q);
}
+
+ /* by journal_file_next_entry() */
+ for (size_t i = 0; i < n; i++) {
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] - 2, DIRECTION_DOWN, NULL, &p);
+ e = expected_result_next(offset[i] - 2, offset_candidates, offset, n, DIRECTION_DOWN, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_DOWN, NULL, &p);
+ e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_DOWN, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i], DIRECTION_DOWN, NULL, &p);
+ e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_DOWN, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_DOWN, NULL, &p);
+ e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_DOWN, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] - 1, DIRECTION_UP, NULL, &p);
+ e = expected_result_next(offset[i] - 1, offset_candidates, offset, n, DIRECTION_UP, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i], DIRECTION_UP, NULL, &p);
+ e = expected_result_next(offset[i], offset_candidates, offset, n, DIRECTION_UP, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] + 1, DIRECTION_UP, NULL, &p);
+ e = expected_result_next(offset[i] + 1, offset_candidates, offset, n, DIRECTION_UP, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+
+ p = 0;
+ r = journal_file_next_entry(f, offset[i] + 2, DIRECTION_UP, NULL, &p);
+ e = expected_result_next(offset[i] + 2, offset_candidates, offset, n, DIRECTION_UP, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+ }
+ for (size_t trial = 0; trial < 3 * n; trial++) {
+ uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
+
+ p = 0;
+ r = journal_file_next_entry(f, i, DIRECTION_DOWN, NULL, &p);
+ e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_DOWN, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+ }
+ for (size_t trial = 0; trial < 3 * n; trial++) {
+ uint64_t i = offset[0] - 1 + random_u64_range(offset[n-1] - offset[0] + 2);
+
+ p = 0;
+ r = journal_file_next_entry(f, i, DIRECTION_UP, NULL, &p);
+ e = expected_result_next(i, offset_candidates, offset, n, DIRECTION_UP, &q);
+ assert_se(e == 0 ? r <= 0 : r > 0);
+ assert_se(p == q);
+ }
}
static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) {
_cleanup_(mmap_cache_unrefp) MMapCache *m = NULL;
char t[] = "/var/tmp/journal-seq-XXXXXX";
- _cleanup_free_ uint64_t *seqnum = NULL, *offset = NULL;
+ _cleanup_free_ uint64_t *seqnum = NULL, *offset = NULL, *offset_candidates = NULL;
JournalFile *f;
log_info("/* %s(%zu, %zu) */", __func__, n, num_corrupted);
@@ -678,7 +870,7 @@ static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) {
mkdtemp_chdir_chattr(t);
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
+ assert_se(journal_file_open(-EBADF, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644,
UINT64_MAX, NULL, m, NULL, &f) == 0);
assert_se(seqnum = new0(uint64_t, n));
@@ -695,7 +887,9 @@ static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) {
}
}
- verify(f, seqnum, offset, n);
+ assert_se(offset_candidates = newdup(uint64_t, offset, n));
+
+ verify(f, seqnum, offset_candidates, offset, n);
/* Reset chain cache. */
assert_se(journal_file_move_to_entry_by_offset(f, offset[0], DIRECTION_DOWN, NULL, NULL) > 0);
@@ -708,9 +902,10 @@ static void test_generic_array_bisect_one(size_t n, size_t num_corrupted) {
assert_se(o);
o->entry.seqnum = 0;
seqnum[i] = 0;
+ offset_candidates[i] = 0;
}
- verify(f, seqnum, offset, n);
+ verify(f, seqnum, offset_candidates, offset, n);
test_close(f);
test_done(t);
diff --git a/src/libsystemd/sd-journal/test-journal-match.c b/src/libsystemd/sd-journal/test-journal-match.c
index 571a88c..c2c345f 100644
--- a/src/libsystemd/sd-journal/test-journal-match.c
+++ b/src/libsystemd/sd-journal/test-journal-match.c
@@ -16,40 +16,40 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
- assert_se(sd_journal_open(&j, 0) >= 0);
+ assert_se(sd_journal_open(&j, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "foobar", 0) < 0);
- assert_se(sd_journal_add_match(j, "foobar=waldo", 0) < 0);
- assert_se(sd_journal_add_match(j, "", 0) < 0);
- assert_se(sd_journal_add_match(j, "=", 0) < 0);
- assert_se(sd_journal_add_match(j, "=xxxxx", 0) < 0);
+ assert_se(sd_journal_add_match(j, "foobar", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "foobar=waldo", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "=", SIZE_MAX) < 0);
+ assert_se(sd_journal_add_match(j, "=xxxxx", SIZE_MAX) < 0);
assert_se(sd_journal_add_match(j, (uint8_t[4]){'A', '=', '\1', '\2'}, 4) >= 0);
assert_se(sd_journal_add_match(j, (uint8_t[5]){'B', '=', 'C', '\0', 'D'}, 5) >= 0);
- assert_se(sd_journal_add_match(j, "HALLO=WALDO", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=mmmm", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
- assert_se(sd_journal_add_match(j, "HALLO=", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=xxxxx", 0) >= 0);
- assert_se(sd_journal_add_match(j, "QUUX=yyyyy", 0) >= 0);
- assert_se(sd_journal_add_match(j, "PIFF=paff", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "HALLO=WALDO", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=mmmm", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "HALLO=", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=xxxxx", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "QUUX=yyyyy", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "PIFF=paff", SIZE_MAX) >= 0);
assert_se(sd_journal_add_disjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "ONE=one", 0) >= 0);
- assert_se(sd_journal_add_match(j, "ONE=two", 0) >= 0);
- assert_se(sd_journal_add_match(j, "TWO=two", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "ONE=one", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "ONE=two", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "TWO=two", SIZE_MAX) >= 0);
assert_se(sd_journal_add_conjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "L4_1=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_1=ok", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_2=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L4_2=ok", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_1=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_1=ok", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L4_2=ok", SIZE_MAX) >= 0);
assert_se(sd_journal_add_disjunction(j) >= 0);
- assert_se(sd_journal_add_match(j, "L3=yes", 0) >= 0);
- assert_se(sd_journal_add_match(j, "L3=ok", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "L3=yes", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "L3=ok", SIZE_MAX) >= 0);
assert_se(t = journal_make_match_string(j));
diff --git a/src/libsystemd/sd-journal/test-journal-stream.c b/src/libsystemd/sd-journal/test-journal-stream.c
index 3a370ef..00c0435 100644
--- a/src/libsystemd/sd-journal/test-journal-stream.c
+++ b/src/libsystemd/sd-journal/test-journal-stream.c
@@ -76,9 +76,9 @@ static void run_test(void) {
assert_se(chdir(t) >= 0);
(void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
- assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &one) == 0);
- assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &two) == 0);
- assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &three) == 0);
+ assert_se(journal_file_open(-EBADF, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &one) == 0);
+ assert_se(journal_file_open(-EBADF, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &two) == 0);
+ assert_se(journal_file_open(-EBADF, "three.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &three) == 0);
for (i = 0; i < N_ENTRIES; i++) {
char *p, *q;
@@ -119,9 +119,9 @@ static void run_test(void) {
(void) journal_file_offline_close(two);
(void) journal_file_offline_close(three);
- assert_se(sd_journal_open_directory(&j, t, 0) >= 0);
+ assert_se(sd_journal_open_directory(&j, t, SD_JOURNAL_ASSUME_IMMUTABLE) >= 0);
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
SD_JOURNAL_FOREACH_BACKWARDS(j) {
_cleanup_free_ char *c;
@@ -147,7 +147,7 @@ static void run_test(void) {
verify_contents(j, 1);
printf("NEXT TEST\n");
- assert_se(sd_journal_add_match(j, "MAGIC=quux", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=quux", SIZE_MAX) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
@@ -157,10 +157,10 @@ static void run_test(void) {
printf("NEXT TEST\n");
sd_journal_flush_matches(j);
- assert_se(sd_journal_add_match(j, "MAGIC=waldo", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=10", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=11", 0) >= 0);
- assert_se(sd_journal_add_match(j, "NUMBER=12", 0) >= 0);
+ assert_se(sd_journal_add_match(j, "MAGIC=waldo", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=10", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=11", SIZE_MAX) >= 0);
+ assert_se(sd_journal_add_match(j, "NUMBER=12", SIZE_MAX) >= 0);
assert_se(z = journal_make_match_string(j));
printf("resulting match expression is: %s\n", z);
diff --git a/src/libsystemd/sd-journal/test-journal-verify.c b/src/libsystemd/sd-journal/test-journal-verify.c
index edce440..396ebe1 100644
--- a/src/libsystemd/sd-journal/test-journal-verify.c
+++ b/src/libsystemd/sd-journal/test-journal-verify.c
@@ -47,7 +47,7 @@ static int raw_verify(const char *fn, const char *verification_key) {
assert_se(m != NULL);
r = journal_file_open(
- /* fd= */ -1,
+ /* fd= */ -EBADF,
fn,
O_RDONLY,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
@@ -92,7 +92,7 @@ static int run_test(const char *verification_key, ssize_t max_iterations) {
log_info("Generating a test journal");
assert_se(journal_file_open(
- /* fd= */ -1,
+ /* fd= */ -EBADF,
"test.journal",
O_RDWR|O_CREAT,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
@@ -128,7 +128,7 @@ static int run_test(const char *verification_key, ssize_t max_iterations) {
log_info("Verifying with key: %s", strna(verification_key));
assert_se(journal_file_open(
- /* fd= */ -1,
+ /* fd= */ -EBADF,
"test.journal",
O_RDONLY,
JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0),
diff --git a/src/libsystemd/sd-journal/test-journal.c b/src/libsystemd/sd-journal/test-journal.c
index 96f2b67..19a6d2d 100644
--- a/src/libsystemd/sd-journal/test-journal.c
+++ b/src/libsystemd/sd-journal/test-journal.c
@@ -39,7 +39,7 @@ static void test_non_empty_one(void) {
mkdtemp_chdir_chattr(t);
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f) == 0);
+ assert_se(journal_file_open(-EBADF, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f) == 0);
assert_se(dual_timestamp_now(&ts));
assert_se(sd_id128_randomize(&fake_boot_id) == 0);
@@ -136,10 +136,10 @@ static void test_empty_one(void) {
mkdtemp_chdir_chattr(t);
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0, 0666, UINT64_MAX, NULL, m, NULL, &f1) == 0);
- assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &f2) == 0);
- assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f3) == 0);
- assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f4) == 0);
+ assert_se(journal_file_open(-EBADF, "test.journal", O_RDWR|O_CREAT, 0, 0666, UINT64_MAX, NULL, m, NULL, &f1) == 0);
+ assert_se(journal_file_open(-EBADF, "test-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, &f2) == 0);
+ assert_se(journal_file_open(-EBADF, "test-seal.journal", O_RDWR|O_CREAT, JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f3) == 0);
+ assert_se(journal_file_open(-EBADF, "test-seal-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, &f4) == 0);
journal_file_print_header(f1);
puts("");
@@ -194,7 +194,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
mkdtemp_chdir_chattr(t);
- assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, compress_threshold, NULL, m, NULL, &f) == 0);
+ assert_se(journal_file_open(-EBADF, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, compress_threshold, NULL, m, NULL, &f) == 0);
dual_timestamp_now(&ts);
diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c
index f9e86c6..4d91ba9 100644
--- a/src/libsystemd/sd-login/sd-login.c
+++ b/src/libsystemd/sd-login/sd-login.c
@@ -579,10 +579,7 @@ _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat)
if (isempty(content))
return 0;
- char t[DECIMAL_STR_MAX(uid_t)];
- xsprintf(t, UID_FMT, uid);
-
- return string_contains_word(content, NULL, t);
+ return string_contains_word(content, NULL, FORMAT_UID(uid));
}
static int uid_get_array(uid_t uid, const char *variable, char ***array) {
@@ -1275,7 +1272,7 @@ _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
_public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
if (m)
- (void) close_nointr(MONITOR_TO_FD(m));
+ (void) close(MONITOR_TO_FD(m));
return NULL;
}
diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c
index 819f86f..66e4274 100644
--- a/src/libsystemd/sd-login/test-login.c
+++ b/src/libsystemd/sd-login/test-login.c
@@ -1,6 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <poll.h>
+#if HAVE_PIDFD_OPEN
+#include <sys/pidfd.h>
+#endif
#include "sd-login.h"
@@ -10,6 +13,7 @@
#include "format-util.h"
#include "log.h"
#include "missing_syscall.h"
+#include "mountpoint-util.h"
#include "process-util.h"
#include "string-util.h"
#include "strv.h"
@@ -103,7 +107,7 @@ TEST(login) {
assert_se(IN_SET(r, 0, -ENOMEDIUM));
}
- r = sd_uid_get_display(u2, &display_session);
+ r = ASSERT_RETURN_IS_CRITICAL(uid_is_valid(u2), sd_uid_get_display(u2, &display_session));
log_info("sd_uid_get_display("UID_FMT", …) → %s / \"%s\"", u2, e(r), strnull(display_session));
if (u2 == UID_INVALID)
assert_se(r == -EINVAL);
@@ -115,7 +119,7 @@ TEST(login) {
sd_peer_get_session(pair[1], &qq);
assert_se(streq_ptr(pp, qq));
- r = sd_uid_get_sessions(u2, false, &sessions);
+ r = ASSERT_RETURN_IS_CRITICAL(uid_is_valid(u2), sd_uid_get_sessions(u2, false, &sessions));
assert_se(t = strv_join(sessions, " "));
log_info("sd_uid_get_sessions("UID_FMT", …) → %s \"%s\"", u2, e(r), t);
if (u2 == UID_INVALID)
@@ -127,9 +131,9 @@ TEST(login) {
sessions = strv_free(sessions);
free(t);
- assert_se(r == sd_uid_get_sessions(u2, false, NULL));
+ assert_se(r == ASSERT_RETURN_IS_CRITICAL(uid_is_valid(u2), sd_uid_get_sessions(u2, false, NULL)));
- r = sd_uid_get_seats(u2, false, &seats);
+ r = ASSERT_RETURN_IS_CRITICAL(uid_is_valid(u2), sd_uid_get_seats(u2, false, &seats));
assert_se(t = strv_join(seats, " "));
log_info("sd_uid_get_seats("UID_FMT", …) → %s \"%s\"", u2, e(r), t);
if (u2 == UID_INVALID)
@@ -141,7 +145,7 @@ TEST(login) {
seats = strv_free(seats);
free(t);
- assert_se(r == sd_uid_get_seats(u2, false, NULL));
+ assert_se(r == ASSERT_RETURN_IS_CRITICAL(uid_is_valid(u2), sd_uid_get_seats(u2, false, NULL)));
if (session) {
r = sd_session_is_active(session);
@@ -327,6 +331,9 @@ TEST(monitor) {
}
static int intro(void) {
+ if (IN_SET(cg_unified(), -ENOENT, -ENOMEDIUM))
+ return log_tests_skipped("cgroupfs is not mounted");
+
log_info("/* Information printed is from the live system */");
return EXIT_SUCCESS;
}
diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
index 008e802..fb11c7e 100644
--- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c
@@ -56,6 +56,10 @@ static bool rtnl_message_type_is_mdb(uint16_t type) {
return IN_SET(type, RTM_NEWMDB, RTM_DELMDB, RTM_GETMDB);
}
+static bool rtnl_message_type_is_nsid(uint16_t type) {
+ return IN_SET(type, RTM_NEWNSID, RTM_DELNSID, RTM_GETNSID);
+}
+
int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen) {
struct rtmsg *rtm;
@@ -92,6 +96,20 @@ int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char
return 0;
}
+int sd_rtnl_message_route_set_tos(sd_netlink_message *m, unsigned char tos) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_tos = tos;
+
+ return 0;
+}
+
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope) {
struct rtmsg *rtm;
@@ -336,7 +354,7 @@ int sd_rtnl_message_new_nexthop(sd_netlink *rtnl, sd_netlink_message **ret,
return r;
if (nlmsg_type == RTM_NEWNEXTHOP)
- (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+ (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
nhm = NLMSG_DATA((*ret)->hdr);
@@ -1202,3 +1220,24 @@ int sd_rtnl_message_new_mdb(
return 0;
}
+
+int sd_rtnl_message_new_nsid(
+ sd_netlink *rtnl,
+ sd_netlink_message **ret,
+ uint16_t nlmsg_type) {
+
+ struct rtgenmsg *rt;
+ int r;
+
+ assert_return(rtnl_message_type_is_nsid(nlmsg_type), -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = message_new(rtnl, ret, nlmsg_type);
+ if (r < 0)
+ return r;
+
+ rt = NLMSG_DATA((*ret)->hdr);
+ rt->rtgen_family = AF_UNSPEC;
+
+ return 0;
+}
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index 000a50e..49d000d 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -17,9 +17,6 @@
#define GET_CONTAINER(m, i) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset))
-#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
-#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
-
int message_new_empty(sd_netlink *nl, sd_netlink_message **ret) {
sd_netlink_message *m;
@@ -789,32 +786,6 @@ int sd_netlink_message_read_data(sd_netlink_message *m, uint16_t attr_type, size
if (ret_data) {
void *data;
- data = memdup(attr_data, r);
- if (!data)
- return -ENOMEM;
-
- *ret_data = data;
- }
-
- if (ret_size)
- *ret_size = r;
-
- return r;
-}
-
-int sd_netlink_message_read_data_suffix0(sd_netlink_message *m, uint16_t attr_type, size_t *ret_size, void **ret_data) {
- void *attr_data;
- int r;
-
- assert_return(m, -EINVAL);
-
- r = netlink_message_read_internal(m, attr_type, &attr_data, NULL);
- if (r < 0)
- return r;
-
- if (ret_data) {
- void *data;
-
data = memdup_suffix0(attr_data, r);
if (!data)
return -ENOMEM;
diff --git a/src/libsystemd/sd-netlink/netlink-types-genl.c b/src/libsystemd/sd-netlink/netlink-types-genl.c
index 6fe9adc..226ac86 100644
--- a/src/libsystemd/sd-netlink/netlink-types-genl.c
+++ b/src/libsystemd/sd-netlink/netlink-types-genl.c
@@ -199,6 +199,7 @@ static const NLAPolicy genl_nl80211_policies[] = {
[NL80211_ATTR_SSID] = BUILD_POLICY_WITH_SIZE(BINARY, IEEE80211_MAX_SSID_LEN),
[NL80211_ATTR_STATUS_CODE] = BUILD_POLICY(U16),
[NL80211_ATTR_4ADDR] = BUILD_POLICY(U8),
+ [NL80211_ATTR_NETNS_FD] = BUILD_POLICY(U32),
};
/***************** genl wireguard type systems *****************/
diff --git a/src/libsystemd/sd-netlink/netlink-types-rtnl.c b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
index 0153456..e39a75c 100644
--- a/src/libsystemd/sd-netlink/netlink-types-rtnl.c
+++ b/src/libsystemd/sd-netlink/netlink-types-rtnl.c
@@ -17,6 +17,7 @@
#include <linux/if_tunnel.h>
#include <linux/ip.h>
#include <linux/l2tp.h>
+#include <linux/net_namespace.h>
#include <linux/netlink.h>
#include <linux/nexthop.h>
#include <linux/nl80211.h>
@@ -123,6 +124,7 @@ static const NLAPolicy rtnl_link_info_data_bond_policies[] = {
[IFLA_BOND_AD_ACTOR_SYSTEM] = BUILD_POLICY_WITH_SIZE(ETHER_ADDR, ETH_ALEN),
[IFLA_BOND_TLB_DYNAMIC_LB] = BUILD_POLICY(U8),
[IFLA_BOND_PEER_NOTIF_DELAY] = BUILD_POLICY(U32),
+ [IFLA_BOND_MISSED_MAX] = BUILD_POLICY(U8),
};
static const NLAPolicy rtnl_link_info_data_bridge_policies[] = {
@@ -306,6 +308,7 @@ static const NLAPolicy rtnl_link_info_data_macvlan_policies[] = {
[IFLA_MACVLAN_MACADDR_COUNT] = BUILD_POLICY(U32),
[IFLA_MACVLAN_BC_QUEUE_LEN] = BUILD_POLICY(U32),
[IFLA_MACVLAN_BC_QUEUE_LEN_USED] = BUILD_POLICY(U32),
+ [IFLA_MACVLAN_BC_CUTOFF] = BUILD_POLICY(S32),
};
static const NLAPolicy rtnl_link_info_data_tun_policies[] = {
@@ -1185,6 +1188,13 @@ static const NLAPolicy rtnl_mdb_policies[] = {
DEFINE_POLICY_SET(rtnl_mdb);
+static const NLAPolicy rtnl_nsid_policies[] = {
+ [NETNSA_FD] = BUILD_POLICY(S32),
+ [NETNSA_NSID] = BUILD_POLICY(U32),
+};
+
+DEFINE_POLICY_SET(rtnl_nsid);
+
static const NLAPolicy rtnl_policies[] = {
[RTM_NEWLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
[RTM_DELLINK] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_link, sizeof(struct ifinfomsg)),
@@ -1220,6 +1230,9 @@ static const NLAPolicy rtnl_policies[] = {
[RTM_NEWMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
[RTM_DELMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
[RTM_GETMDB] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_mdb, sizeof(struct br_port_msg)),
+ [RTM_NEWNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+ [RTM_DELNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
+ [RTM_GETNSID] = BUILD_POLICY_NESTED_WITH_SIZE(rtnl_nsid, sizeof(struct rtgenmsg)),
};
DEFINE_POLICY_SET(rtnl);
diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c
index 832159a..9061609 100644
--- a/src/libsystemd/sd-netlink/netlink-util.c
+++ b/src/libsystemd/sd-netlink/netlink-util.c
@@ -11,6 +11,154 @@
#include "process-util.h"
#include "strv.h"
+static int parse_newlink_message(
+ sd_netlink_message *message,
+ char **ret_name,
+ char ***ret_altnames) {
+
+ _cleanup_strv_free_ char **altnames = NULL;
+ int r, ifindex;
+
+ assert(message);
+
+ uint16_t type;
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0)
+ return r;
+ if (type != RTM_NEWLINK)
+ return -EPROTO;
+
+ r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
+ if (r < 0)
+ return r;
+ if (ifindex <= 0)
+ return -EPROTO;
+
+ if (ret_altnames) {
+ r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
+ if (r < 0 && r != -ENODATA)
+ return r;
+ }
+
+ if (ret_name) {
+ r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret_name);
+ if (r < 0)
+ return r;
+ }
+
+ if (ret_altnames)
+ *ret_altnames = TAKE_PTR(altnames);
+
+ return ifindex;
+}
+
+int rtnl_get_ifname_full(sd_netlink **rtnl, int ifindex, char **ret_name, char ***ret_altnames) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+ _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+ int r;
+
+ assert(ifindex > 0);
+
+ /* This is similar to if_indextoname(), but also optionally provides alternative names. */
+
+ if (!rtnl)
+ rtnl = &our_rtnl;
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, message, 0, &reply);
+ if (r < 0)
+ return r;
+
+ return parse_newlink_message(reply, ret_name, ret_altnames);
+}
+
+int rtnl_resolve_ifname_full(
+ sd_netlink **rtnl,
+ ResolveInterfaceNameFlag flags,
+ const char *name,
+ char **ret_name,
+ char ***ret_altnames) {
+
+ _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+ int r;
+
+ assert(name);
+ assert(flags > 0);
+
+ /* This is similar to if_nametoindex(), but also resolves alternative names and decimal formatted
+ * ifindex too. Returns ifindex, and optionally provides the main interface name and alternative
+ * names.*/
+
+ if (!rtnl)
+ rtnl = &our_rtnl;
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ /* First, use IFLA_IFNAME */
+ if (FLAGS_SET(flags, RESOLVE_IFNAME_MAIN) && ifname_valid(name)) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+
+ r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, message, 0, &reply);
+ if (r >= 0)
+ return parse_newlink_message(reply, ret_name, ret_altnames);
+ if (r != -ENODEV)
+ return r;
+ }
+
+ /* Next, try IFLA_ALT_IFNAME */
+ if (FLAGS_SET(flags, RESOLVE_IFNAME_ALTERNATIVE) &&
+ ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE)) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+
+ r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(*rtnl, message, 0, &reply);
+ if (r >= 0)
+ return parse_newlink_message(reply, ret_name, ret_altnames);
+ /* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
+ if (!IN_SET(r, -ENODEV, -EINVAL))
+ return r;
+ }
+
+ /* Finally, assume the string is a decimal formatted ifindex. */
+ if (FLAGS_SET(flags, RESOLVE_IFNAME_NUMERIC)) {
+ int ifindex;
+
+ ifindex = parse_ifindex(name);
+ if (ifindex <= 0)
+ return -ENODEV;
+
+ return rtnl_get_ifname_full(rtnl, ifindex, ret_name, ret_altnames);
+ }
+
+ return -ENODEV;
+}
+
static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
int r;
@@ -20,6 +168,13 @@ static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
assert(name);
/* Assign the requested name. */
+
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
if (r < 0)
return r;
@@ -31,6 +186,37 @@ static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
return sd_netlink_call(*rtnl, message, 0, NULL);
}
+int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+ int r, ifindex;
+
+ assert(orig_name);
+ assert(new_name);
+
+ /* This does not check alternative names. Callers must check the requested name is not used as an
+ * alternative name. */
+
+ if (streq(orig_name, new_name))
+ return 0;
+
+ if (!ifname_valid(new_name))
+ return -EINVAL;
+
+ if (!rtnl)
+ rtnl = &our_rtnl;
+ if (!*rtnl) {
+ r = sd_netlink_open(rtnl);
+ if (r < 0)
+ return r;
+ }
+
+ ifindex = rtnl_resolve_ifname(rtnl, orig_name);
+ if (ifindex < 0)
+ return ifindex;
+
+ return set_link_name(rtnl, ifindex, new_name);
+}
+
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const *alternative_names) {
_cleanup_strv_free_ char **original_altnames = NULL, **new_altnames = NULL;
bool altname_deleted = false;
@@ -204,38 +390,6 @@ int rtnl_set_link_properties(
return 0;
}
-int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
- _cleanup_strv_free_ char **names = NULL;
- int r;
-
- assert(rtnl);
- assert(ifindex > 0);
- assert(ret);
-
- if (!*rtnl) {
- r = sd_netlink_open(rtnl);
- if (r < 0)
- return r;
- }
-
- r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
- if (r < 0)
- return r;
-
- r = sd_netlink_call(*rtnl, message, 0, &reply);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
- if (r < 0 && r != -ENODATA)
- return r;
-
- *ret = TAKE_PTR(names);
-
- return 0;
-}
-
static int rtnl_update_link_alternative_names(
sd_netlink **rtnl,
uint16_t nlmsg_type,
@@ -336,92 +490,6 @@ int rtnl_set_link_alternative_names_by_ifname(
return 0;
}
-int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret) {
- _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
- _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
- int r, ifindex;
-
- assert(name);
-
- /* This returns ifindex and the main interface name. */
-
- if (!ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE))
- return -EINVAL;
-
- if (!rtnl)
- rtnl = &our_rtnl;
- if (!*rtnl) {
- r = sd_netlink_open(rtnl);
- if (r < 0)
- return r;
- }
-
- r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
- if (r < 0)
- return r;
-
- r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
- if (r < 0)
- return r;
-
- r = sd_netlink_call(*rtnl, message, 0, &reply);
- if (r == -EINVAL)
- return -ENODEV; /* The device doesn't exist */
- if (r < 0)
- return r;
-
- r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
- if (r < 0)
- return r;
- assert(ifindex > 0);
-
- if (ret) {
- r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, ret);
- if (r < 0)
- return r;
- }
-
- return ifindex;
-}
-
-int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) {
- int r;
-
- /* Like if_nametoindex, but resolves "alternative names" too. */
-
- assert(name);
-
- r = if_nametoindex(name);
- if (r > 0)
- return r;
-
- return rtnl_resolve_link_alternative_name(rtnl, name, NULL);
-}
-
-int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) {
- int r;
-
- /* Like rtnl_resolve_ifname, but resolves interface numbers too. */
-
- assert(name);
-
- r = parse_ifindex(name);
- if (r > 0)
- return r;
- assert(r < 0);
-
- return rtnl_resolve_ifname(rtnl, name);
-}
-
-int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
- int r;
-
- r = rtnl_resolve_interface(rtnl, name);
- if (r < 0)
- return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
- return r;
-}
-
int rtnl_get_link_info(
sd_netlink **rtnl,
int ifindex,
@@ -441,7 +509,7 @@ int rtnl_get_link_info(
assert(rtnl);
assert(ifindex > 0);
- if (!ret_iftype && !ret_flags)
+ if (!ret_iftype && !ret_flags && !ret_kind && !ret_hw_addr && !ret_permanent_hw_addr)
return 0;
if (!*rtnl) {
@@ -575,121 +643,6 @@ int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void
return 0;
}
-MultipathRoute *multipath_route_free(MultipathRoute *m) {
- if (!m)
- return NULL;
-
- free(m->ifname);
-
- return mfree(m);
-}
-
-int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret) {
- _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
- _cleanup_free_ char *ifname = NULL;
-
- assert(m);
- assert(ret);
-
- if (m->ifname) {
- ifname = strdup(m->ifname);
- if (!ifname)
- return -ENOMEM;
- }
-
- n = new(MultipathRoute, 1);
- if (!n)
- return -ENOMEM;
-
- *n = (MultipathRoute) {
- .gateway = m->gateway,
- .weight = m->weight,
- .ifindex = m->ifindex,
- .ifname = TAKE_PTR(ifname),
- };
-
- *ret = TAKE_PTR(n);
-
- return 0;
-}
-
-int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret) {
- _cleanup_ordered_set_free_free_ OrderedSet *set = NULL;
- int r;
-
- assert(rtnh);
- assert(IN_SET(family, AF_INET, AF_INET6));
-
- if (size < sizeof(struct rtnexthop))
- return -EBADMSG;
-
- for (; size >= sizeof(struct rtnexthop); ) {
- _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
-
- if (NLMSG_ALIGN(rtnh->rtnh_len) > size)
- return -EBADMSG;
-
- if (rtnh->rtnh_len < sizeof(struct rtnexthop))
- return -EBADMSG;
-
- m = new(MultipathRoute, 1);
- if (!m)
- return -ENOMEM;
-
- *m = (MultipathRoute) {
- .ifindex = rtnh->rtnh_ifindex,
- .weight = rtnh->rtnh_hops,
- };
-
- if (rtnh->rtnh_len > sizeof(struct rtnexthop)) {
- size_t len = rtnh->rtnh_len - sizeof(struct rtnexthop);
-
- for (struct rtattr *attr = RTNH_DATA(rtnh); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
- if (attr->rta_type == RTA_GATEWAY) {
- if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(family)))
- return -EBADMSG;
-
- m->gateway.family = family;
- memcpy(&m->gateway.address, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(family));
- break;
- } else if (attr->rta_type == RTA_VIA) {
- uint16_t gw_family;
-
- if (family != AF_INET)
- return -EINVAL;
-
- if (attr->rta_len < RTA_LENGTH(sizeof(uint16_t)))
- return -EBADMSG;
-
- gw_family = *(uint16_t *) RTA_DATA(attr);
-
- if (gw_family != AF_INET6)
- return -EBADMSG;
-
- if (attr->rta_len != RTA_LENGTH(FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family)))
- return -EBADMSG;
-
- memcpy(&m->gateway, RTA_DATA(attr), FAMILY_ADDRESS_SIZE(gw_family) + sizeof(gw_family));
- break;
- }
- }
- }
-
- r = ordered_set_ensure_put(&set, NULL, m);
- if (r < 0)
- return r;
-
- TAKE_PTR(m);
-
- size -= NLMSG_ALIGN(rtnh->rtnh_len);
- rtnh = RTNH_NEXT(rtnh);
- }
-
- if (ret)
- *ret = TAKE_PTR(set);
- return 0;
-}
-
bool netlink_pid_changed(sd_netlink *nl) {
/* We don't support people creating an nl connection and
* keeping it around over a fork(). Let's complain. */
diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h
index 369f5d5..4ba64f0 100644
--- a/src/libsystemd/sd-netlink/netlink-util.h
+++ b/src/libsystemd/sd-netlink/netlink-util.h
@@ -10,24 +10,32 @@
#include "ordered-set.h"
#include "socket-util.h"
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
+#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
+
/* See struct rtvia in rtnetlink.h */
typedef struct RouteVia {
uint16_t family;
union in_addr_union address;
} _packed_ RouteVia;
-typedef struct MultipathRoute {
- RouteVia gateway;
- uint32_t weight;
- int ifindex;
- char *ifname;
-} MultipathRoute;
+int rtnl_get_ifname_full(sd_netlink **rtnl, int ifindex, char **ret_name, char ***ret_altnames);
-MultipathRoute *multipath_route_free(MultipathRoute *m);
-DEFINE_TRIVIAL_CLEANUP_FUNC(MultipathRoute*, multipath_route_free);
+typedef enum ResolveInterfaceNameFlag {
+ RESOLVE_IFNAME_MAIN = 1 << 0, /* resolve main interface name */
+ RESOLVE_IFNAME_ALTERNATIVE = 1 << 1, /* resolve alternative name */
+ RESOLVE_IFNAME_NUMERIC = 1 << 2, /* resolve decimal formatted ifindex */
+ _RESOLVE_IFNAME_ALL = RESOLVE_IFNAME_MAIN | RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC,
+} ResolveInterfaceNameFlag;
-int multipath_route_dup(const MultipathRoute *m, MultipathRoute **ret);
+int rtnl_resolve_ifname_full(
+ sd_netlink **rtnl,
+ ResolveInterfaceNameFlag flags,
+ const char *name,
+ char **ret_name,
+ char ***ret_altnames);
+int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name);
int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names);
static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
return rtnl_set_link_name(rtnl, ifindex, NULL, alternative_names);
@@ -43,14 +51,32 @@ int rtnl_set_link_properties(
uint32_t mtu,
uint32_t gso_max_size,
size_t gso_max_segments);
-int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret);
+static inline int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
+ return rtnl_get_ifname_full(rtnl, ifindex, NULL, ret);
+}
int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names);
int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char* const *alternative_names);
int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names);
-int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret);
-int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name);
-int rtnl_resolve_interface(sd_netlink **rtnl, const char *name);
-int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name);
+static inline int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret) {
+ return rtnl_resolve_ifname_full(rtnl, RESOLVE_IFNAME_ALTERNATIVE, name, ret, NULL);
+}
+static inline int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) {
+ return rtnl_resolve_ifname_full(rtnl, RESOLVE_IFNAME_MAIN | RESOLVE_IFNAME_ALTERNATIVE, name, NULL, NULL);
+}
+static inline int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) {
+ return rtnl_resolve_ifname_full(rtnl, _RESOLVE_IFNAME_ALL, name, NULL, NULL);
+}
+static inline int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
+ int r;
+
+ assert(name);
+
+ r = rtnl_resolve_interface(rtnl, name);
+ if (r < 0)
+ return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
+ return r;
+}
+
int rtnl_get_link_info(
sd_netlink **rtnl,
int ifindex,
@@ -103,8 +129,6 @@ int netlink_message_read_in_addr_union(sd_netlink_message *m, unsigned short typ
void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length);
int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length);
-int rtattr_read_nexthop(const struct rtnexthop *rtnh, size_t size, int family, OrderedSet **ret);
-
void netlink_seal_message(sd_netlink *nl, sd_netlink_message *m);
size_t netlink_get_reply_callback_count(sd_netlink *nl);
diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c
index b6730b7..b347ee6 100644
--- a/src/libsystemd/sd-netlink/sd-netlink.c
+++ b/src/libsystemd/sd-netlink/sd-netlink.c
@@ -176,7 +176,7 @@ static int dispatch_rqueue(sd_netlink *nl, sd_netlink_message **ret) {
assert(nl);
assert(ret);
- if (ordered_set_size(nl->rqueue) <= 0) {
+ if (ordered_set_isempty(nl->rqueue)) {
/* Try to read a new message */
r = socket_read_message(nl);
if (r == -ENOBUFS) /* FIXME: ignore buffer overruns for now */
@@ -443,7 +443,7 @@ int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0)
+ if (!ordered_set_isempty(nl->rqueue))
return 0;
r = netlink_poll(nl, false, timeout_usec);
@@ -623,7 +623,7 @@ int sd_netlink_get_events(sd_netlink *nl) {
assert_return(nl, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- return ordered_set_size(nl->rqueue) == 0 ? POLLIN : 0;
+ return ordered_set_isempty(nl->rqueue) ? POLLIN : 0;
}
int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
@@ -633,7 +633,7 @@ int sd_netlink_get_timeout(sd_netlink *nl, uint64_t *timeout_usec) {
assert_return(timeout_usec, -EINVAL);
assert_return(!netlink_pid_changed(nl), -ECHILD);
- if (ordered_set_size(nl->rqueue) > 0) {
+ if (!ordered_set_isempty(nl->rqueue)) {
*timeout_usec = 0;
return 1;
}
diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c
index 13aedc4..cf19c48 100644
--- a/src/libsystemd/sd-netlink/test-netlink.c
+++ b/src/libsystemd/sd-netlink/test-netlink.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* Make sure the net/if.h header is included before any linux/ one */
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
@@ -681,6 +682,19 @@ TEST(rtnl_set_link_name) {
_cleanup_free_ char *resolved = NULL;
assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
assert_se(streq_ptr(resolved, "test-shortname"));
+ resolved = mfree(resolved);
+
+ assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname") >= 0);
+ assert_se(rtnl_rename_link(&rtnl, "test-shortname", "test-shortname2") >= 0);
+ assert_se(rtnl_rename_link(NULL, "test-shortname2", "test-shortname3") >= 0);
+
+ assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-additional-name", &resolved) == ifindex);
+ assert_se(streq_ptr(resolved, "test-shortname3"));
+ resolved = mfree(resolved);
+
+ assert_se(rtnl_resolve_link_alternative_name(&rtnl, "test-shortname3", &resolved) == ifindex);
+ assert_se(streq_ptr(resolved, "test-shortname3"));
+ resolved = mfree(resolved);
}
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c
index 2059567..25c6e44 100644
--- a/src/libsystemd/sd-network/network-util.c
+++ b/src/libsystemd/sd-network/network-util.c
@@ -90,49 +90,48 @@ static const char *const link_online_state_table[_LINK_ONLINE_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(link_online_state, LinkOnlineState);
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out) {
- LinkOperationalStateRange range = { _LINK_OPERSTATE_INVALID, _LINK_OPERSTATE_INVALID };
- _cleanup_free_ const char *min = NULL;
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret) {
+ LinkOperationalStateRange range = LINK_OPERSTATE_RANGE_INVALID;
+ _cleanup_free_ char *buf = NULL;
const char *p;
- assert(str);
- assert(out);
-
- p = strchr(str, ':');
- if (p) {
- min = strndup(str, p - str);
+ assert(s);
+ assert(ret);
- if (!isempty(p + 1)) {
- range.max = link_operstate_from_string(p + 1);
- if (range.max < 0)
- return -EINVAL;
- }
- } else
- min = strdup(str);
+ /* allowed formats: "min", "min:", "min:max", ":max" */
- if (!min)
- return -ENOMEM;
+ if (isempty(s) || streq(s, ":"))
+ return -EINVAL;
- if (!isempty(min)) {
- range.min = link_operstate_from_string(min);
- if (range.min < 0)
+ p = strchr(s, ':');
+ if (!p || isempty(p + 1))
+ range.max = LINK_OPERSTATE_ROUTABLE;
+ else {
+ range.max = link_operstate_from_string(p + 1);
+ if (range.max < 0)
return -EINVAL;
}
- /* Fail on empty strings. */
- if (range.min == _LINK_OPERSTATE_INVALID && range.max == _LINK_OPERSTATE_INVALID)
- return -EINVAL;
+ if (p) {
+ buf = strndup(s, p - s);
+ if (!buf)
+ return -ENOMEM;
- if (range.min == _LINK_OPERSTATE_INVALID)
+ s = buf;
+ }
+
+ if (isempty(s))
range.min = LINK_OPERSTATE_MISSING;
- if (range.max == _LINK_OPERSTATE_INVALID)
- range.max = LINK_OPERSTATE_ROUTABLE;
+ else {
+ range.min = link_operstate_from_string(s);
+ if (range.min < 0)
+ return -EINVAL;
+ }
- if (range.min > range.max)
+ if (!operational_state_range_is_valid(&range))
return -EINVAL;
- *out = range;
-
+ *ret = range;
return 0;
}
diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h
index c47e271..6fc6015 100644
--- a/src/libsystemd/sd-network/network-util.h
+++ b/src/libsystemd/sd-network/network-util.h
@@ -79,8 +79,30 @@ typedef struct LinkOperationalStateRange {
LinkOperationalState max;
} LinkOperationalStateRange;
-#define LINK_OPERSTATE_RANGE_DEFAULT (LinkOperationalStateRange) { LINK_OPERSTATE_DEGRADED, \
- LINK_OPERSTATE_ROUTABLE }
-
-int parse_operational_state_range(const char *str, LinkOperationalStateRange *out);
+#define LINK_OPERSTATE_RANGE_DEFAULT \
+ (const LinkOperationalStateRange) { \
+ .min = LINK_OPERSTATE_DEGRADED, \
+ .max = LINK_OPERSTATE_ROUTABLE, \
+ }
+
+#define LINK_OPERSTATE_RANGE_INVALID \
+ (const LinkOperationalStateRange) { \
+ .min = _LINK_OPERSTATE_INVALID, \
+ .max = _LINK_OPERSTATE_INVALID, \
+ }
+
+int parse_operational_state_range(const char *s, LinkOperationalStateRange *ret);
int network_link_get_operational_state(int ifindex, LinkOperationalState *ret);
+
+static inline bool operational_state_is_valid(LinkOperationalState s) {
+ return s >= 0 && s < _LINK_OPERSTATE_MAX;
+}
+static inline bool operational_state_range_is_valid(const LinkOperationalStateRange *range) {
+ return range &&
+ operational_state_is_valid(range->min) &&
+ operational_state_is_valid(range->max) &&
+ range->min <= range->max;
+}
+static inline bool operational_state_is_in_range(LinkOperationalState s, const LinkOperationalStateRange *range) {
+ return range && range->min <= s && s <= range->max;
+}
diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c
index cf3c400..9d11dce 100644
--- a/src/libsystemd/sd-network/sd-network.c
+++ b/src/libsystemd/sd-network/sd-network.c
@@ -394,7 +394,7 @@ int sd_network_monitor_new(sd_network_monitor **m, const char *category) {
sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) {
if (m)
- (void) close_nointr(MONITOR_TO_FD(m));
+ (void) close(MONITOR_TO_FD(m));
return NULL;
}
diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c
index 7290d1c..8edbde9 100644
--- a/src/libsystemd/sd-path/sd-path.c
+++ b/src/libsystemd/sd-path/sd-path.c
@@ -443,7 +443,7 @@ _public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
}
static int search_from_environment(
- char ***list,
+ char ***ret,
const char *env_home,
const char *home_suffix,
const char *env_search,
@@ -455,7 +455,7 @@ static int search_from_environment(
char *h = NULL;
int r;
- assert(list);
+ assert(ret);
if (env_search) {
e = secure_getenv(env_search);
@@ -465,7 +465,7 @@ static int search_from_environment(
return -ENOMEM;
if (env_search_sufficient) {
- *list = TAKE_PTR(l);
+ *ret = TAKE_PTR(l);
return 0;
}
}
@@ -506,7 +506,7 @@ static int search_from_environment(
return -ENOMEM;
}
- *list = TAKE_PTR(l);
+ *ret = TAKE_PTR(l);
return 0;
}
@@ -516,15 +516,15 @@ static int search_from_environment(
# define ARRAY_SBIN_BIN(x) x "bin"
#endif
-static int get_search(uint64_t type, char ***list) {
+static int get_search(uint64_t type, char ***ret) {
int r;
- assert(list);
+ assert(ret);
switch (type) {
case SD_PATH_SEARCH_BINARIES:
- return search_from_environment(list,
+ return search_from_environment(ret,
NULL,
".local/bin",
"PATH",
@@ -534,7 +534,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_LIBRARY_PRIVATE:
- return search_from_environment(list,
+ return search_from_environment(ret,
NULL,
".local/lib",
NULL,
@@ -544,7 +544,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_LIBRARY_ARCH:
- return search_from_environment(list,
+ return search_from_environment(ret,
NULL,
".local/lib/" LIB_ARCH_TUPLE,
"LD_LIBRARY_PATH",
@@ -553,7 +553,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_SHARED:
- return search_from_environment(list,
+ return search_from_environment(ret,
"XDG_DATA_HOME",
".local/share",
"XDG_DATA_DIRS",
@@ -563,7 +563,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_CONFIGURATION_FACTORY:
- return search_from_environment(list,
+ return search_from_environment(ret,
NULL,
NULL,
NULL,
@@ -573,7 +573,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_STATE_FACTORY:
- return search_from_environment(list,
+ return search_from_environment(ret,
NULL,
NULL,
NULL,
@@ -583,7 +583,7 @@ static int get_search(uint64_t type, char ***list) {
NULL);
case SD_PATH_SEARCH_CONFIGURATION:
- return search_from_environment(list,
+ return search_from_environment(ret,
"XDG_CONFIG_HOME",
".config",
"XDG_CONFIG_DIRS",
@@ -591,12 +591,18 @@ static int get_search(uint64_t type, char ***list) {
"/etc",
NULL);
- case SD_PATH_SEARCH_BINARIES_DEFAULT:
- return strv_from_nulstr(list, DEFAULT_PATH_NULSTR);
+ case SD_PATH_SEARCH_BINARIES_DEFAULT: {
+ char **t = strv_split(default_PATH(), ":");
+ if (!t)
+ return -ENOMEM;
+
+ *ret = t;
+ return 0;
+ }
case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT:
case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: {
- _cleanup_(lookup_paths_free) LookupPaths lp = {};
+ _cleanup_(lookup_paths_done) LookupPaths lp = {};
RuntimeScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ?
RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER;
@@ -604,7 +610,7 @@ static int get_search(uint64_t type, char ***list) {
if (r < 0)
return r;
- *list = TAKE_PTR(lp.search_path);
+ *ret = TAKE_PTR(lp.search_path);
return 0;
}
@@ -618,7 +624,7 @@ static int get_search(uint64_t type, char ***list) {
if (!t)
return -ENOMEM;
- *list = t;
+ *ret = t;
return 0;
}
@@ -631,12 +637,12 @@ static int get_search(uint64_t type, char ***list) {
if (!t)
return -ENOMEM;
- *list = t;
+ *ret = t;
return 0;
}
case SD_PATH_SYSTEMD_SEARCH_NETWORK:
- return strv_from_nulstr(list, NETWORK_DIRS_NULSTR);
+ return strv_from_nulstr(ret, NETWORK_DIRS_NULSTR);
}