diff options
Diffstat (limited to 'src/libnm-systemd-core')
39 files changed, 5625 insertions, 831 deletions
diff --git a/src/libnm-systemd-core/README.md b/src/libnm-systemd-core/README.md new file mode 100644 index 00000000..e47eb8ed --- /dev/null +++ b/src/libnm-systemd-core/README.md @@ -0,0 +1,23 @@ +libnm-systemd-core +================== + +This is a fork of systemd source files that are compiled +as a static library with network helpers. + +We use systemd's DHCPv6 and LLDP library, by forking their code. + +We also still use their DHCPv4 library, but that is about to be replaced +by nettools' n-dhcp4 and not used unless you configure the undocumented +`[main].dhcp=systemd` plugin. + +This approach of code-reuse is very cumbersome, and we should replace +systemd code by a proper library (like [nettools](https://github.com/nettools/)). + +We should not use systemd directly from our sources, beyond what +we really need. + + +Reimport Upstream Code +---------------------- + +Read [here](../libnm-systemd-shared/README.md#reimport-upstream-code). diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index 6175e42c..15e1c8a6 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -3,18 +3,21 @@ libnm_systemd_core = static_library( 'nm-systemd-core', sources: files( - 'src/libsystemd-network/dhcp-identifier.c', 'src/libsystemd-network/dhcp6-network.c', 'src/libsystemd-network/dhcp6-option.c', 'src/libsystemd-network/dhcp6-protocol.c', 'src/libsystemd-network/network-common.c', + 'src/libsystemd-network/sd-dhcp-duid.c', 'src/libsystemd-network/sd-dhcp6-client.c', 'src/libsystemd-network/sd-dhcp6-lease.c', + 'src/libsystemd/sd-device/device-private.c', + 'src/libsystemd/sd-device/sd-device.c', 'src/libsystemd/sd-event/event-util.c', 'src/libsystemd/sd-event/sd-event.c', 'src/libsystemd/sd-id128/id128-util.c', 'src/libsystemd/sd-id128/sd-id128.c', 'nm-sd.c', + 'sd-adapt-core/netif-util.c', 'sd-adapt-core/nm-sd-adapt-core.c', ), include_directories: [ @@ -28,6 +31,7 @@ libnm_systemd_core = static_library( top_inc, src_inc, ], + c_args: libnm_systemd_common_cflags, dependencies: [ libnm_systemd_shared_dep_inc, glib_dep, diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.c b/src/libnm-systemd-core/sd-adapt-core/netif-util.c new file mode 100644 index 00000000..cfb361a3 --- /dev/null +++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.c @@ -0,0 +1,221 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#include <linux/if.h> +#ifdef __GLIBC__ +#include <linux/if_arp.h> +#endif + +#include "arphrd-util.h" +#include "device-util.h" +#include "log-link.h" +#include "memory-util.h" +#include "netif-util.h" +#include "siphash24.h" +#include "sparse-endian.h" +#include "strv.h" + +#if 0 /* NM_IGNORED */ +bool netif_has_carrier(uint8_t operstate, unsigned flags) { + /* see Documentation/networking/operstates.txt in the kernel sources */ + + if (operstate == IF_OPER_UP) + return true; + + if (operstate != IF_OPER_UNKNOWN) + return false; + + /* operstate may not be implemented, so fall back to flags */ + return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) && + !FLAGS_SET(flags, IFF_DORMANT); +} + +int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) { + const char *t; + char *p; + + if (device && + sd_device_get_devtype(device, &t) >= 0 && + !isempty(t)) { + p = strdup(t); + if (!p) + return -ENOMEM; + + *ret = p; + return 0; + } + + t = arphrd_to_name(iftype); + if (!t) + return -ENOENT; + + p = strdup(t); + if (!p) + return -ENOMEM; + + *ret = ascii_strlower(p); + return 0; +} +#endif /* NM_IGNORED */ + +const char * +net_get_persistent_name(sd_device *device) +{ + assert(device); + + /* fetch some persistent data unique (on this machine) to this device */ + FOREACH_STRING(field, + "ID_NET_NAME_ONBOARD", + "ID_NET_NAME_SLOT", + "ID_NET_NAME_PATH", + "ID_NET_NAME_MAC") + { + const char *name; + + if (sd_device_get_property_value(device, field, &name) >= 0) + return name; + } + + return NULL; +} + +#if 0 /* NM_IGNORED */ +/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */ +#define HASH_KEY SD_ID128_MAKE(d3, 1e, 48, fa, 90, fe, 4b, 4c, 9d, af, d5, d7, a1, b1, 2e, 8a) + +int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) { + const char *name; + + assert(device); + assert(ret); + + /* net_get_persistent_name() will return one of the device names based on stable information about + * the device. If this is not available, we fall back to using the actual device name. */ + name = net_get_persistent_name(device); + if (!name && use_sysname) + (void) sd_device_get_sysname(device, &name); + if (!name) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), + "No stable identifying information found"); + + log_device_debug(device, "Using \"%s\" as stable identifying information", name); + + return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret); +} + +int net_get_unique_predictable_data_from_name( + const char *name, + const sd_id128_t *key, + uint64_t *ret) { + + size_t l, sz; + uint8_t *v; + int r; + + assert(name); + assert(key); + assert(ret); + + l = strlen(name); + sz = sizeof(sd_id128_t) + l; + v = newa(uint8_t, sz); + + /* Fetch some persistent data unique to this machine */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + + memcpy(v + sizeof(sd_id128_t), name, l); + + /* Let's hash the machine ID plus the device name. We use + * a fixed, but originally randomly created hash key here. */ + *ret = htole64(siphash24(v, sz, key->bytes)); + return 0; +} + +typedef struct Link { + const char *ifname; +} Link; + +int net_verify_hardware_address( + const char *ifname, + bool is_static, + uint16_t iftype, + const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */ + struct hw_addr_data *new_hw_addr) { + + Link link = { .ifname = ifname }; + + assert(new_hw_addr); + + if (new_hw_addr->length == 0) + return 0; + + if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) { + if (is_static) + log_link_warning(&link, + "Specified MAC address with invalid length (%zu, expected %zu), refusing.", + new_hw_addr->length, arphrd_to_hw_addr_len(iftype)); + return -EINVAL; + } + + switch (iftype) { + case ARPHRD_ETHER: + /* see eth_random_addr() in the kernel */ + + if (ether_addr_is_null(&new_hw_addr->ether)) { + if (is_static) + log_link_warning(&link, "Specified MAC address is null, refusing."); + return -EINVAL; + } + + if (ether_addr_is_broadcast(&new_hw_addr->ether)) { + if (is_static) + log_link_warning(&link, "Specified MAC address is broadcast, refusing."); + return -EINVAL; + } + + if (ether_addr_is_multicast(&new_hw_addr->ether)) { + if (is_static) + log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit."); + + new_hw_addr->bytes[0] &= 0xfe; + } + + if (!is_static && !ether_addr_is_local(&new_hw_addr->ether)) + /* Adjust local assignment bit when the MAC address is generated randomly. */ + new_hw_addr->bytes[0] |= 0x02; + + break; + + case ARPHRD_INFINIBAND: + /* see ipoib_check_lladdr() in the kernel */ + + assert(ib_hw_addr); + assert(ib_hw_addr->length == INFINIBAND_ALEN); + + if (is_static && + (!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) || + memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0)) + log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes."); + + if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) { + if (is_static) + log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing."); + return -EINVAL; + } + + memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8); + break; + + default: + if (is_static) + log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.", + strna(arphrd_to_name(iftype)), iftype); + return -EINVAL; + } + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/sd-adapt-core/netif-util.h b/src/libnm-systemd-core/sd-adapt-core/netif-util.h new file mode 100644 index 00000000..59a80866 --- /dev/null +++ b/src/libnm-systemd-core/sd-adapt-core/netif-util.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <inttypes.h> +#include <stdbool.h> + +#include "sd-device.h" +#include "sd-id128.h" + +#include "ether-addr-util.h" + +bool netif_has_carrier(uint8_t operstate, unsigned flags); +int net_get_type_string(sd_device *device, uint16_t iftype, char **ret); +const char *net_get_persistent_name(sd_device *device); +int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret); +int +net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret); +int net_verify_hardware_address(const char *ifname, + bool is_static, + uint16_t iftype, + const struct hw_addr_data *ib_hw_addr, + struct hw_addr_data *new_hw_addr); diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c index c5ef63eb..13effd12 100644 --- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c +++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.c @@ -12,15 +12,6 @@ /*****************************************************************************/ -int -asynchronous_close(int fd) -{ - safe_close(fd); - return -1; -} - -/*****************************************************************************/ - sd_device * sd_device_ref(sd_device *self) { diff --git a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h index 9c317801..9cb5574d 100644 --- a/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h +++ b/src/libnm-systemd-core/sd-adapt-core/nm-sd-adapt-core.h @@ -86,7 +86,6 @@ sd_notify(int unset_environment, const char *state) #include "sd-id128.h" #include "sparse-endian.h" -#include "async.h" #endif /* (NETWORKMANAGER_COMPILATION) & NM_NETWORKMANAGER_COMPILATION_WITH_SYSTEMD */ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h new file mode 100644 index 00000000..f8bc15c4 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#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" + +#define SYSTEMD_PEN 43793 + +typedef enum DUIDType { + 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, +} 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 */ +struct duid { + be16_t type; + union { + struct { + /* DUID_TYPE_LLT */ + be16_t htype; + be32_t time; + uint8_t haddr[]; + } _packed_ llt; + struct { + /* DUID_TYPE_EN */ + be32_t pen; + uint8_t id[]; + } _packed_ en; + struct { + /* DUID_TYPE_LL */ + be16_t htype; + uint8_t haddr[]; + } _packed_ ll; + struct { + /* DUID_TYPE_UUID */ + sd_id128_t uuid; + } _packed_ uuid; + uint8_t data[MAX_DUID_DATA_LEN]; + }; +} _packed_; + +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); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c deleted file mode 100644 index 05d0585a..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c +++ /dev/null @@ -1,252 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include "nm-sd-adapt-core.h" - -#include <linux/if_infiniband.h> -#include <net/ethernet.h> -#include <net/if_arp.h> - -#include "dhcp-identifier.h" -#include "netif-util.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_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) { - struct duid d; - - assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); - if (duid_len > MAX_DUID_LEN) - return -EINVAL; - - if (!strict) - /* Strict validation is not requested. We only ensure that the - * DUID is not too long. */ - return 0; - - switch (duid_type) { - case DUID_TYPE_LLT: - if (duid_len <= sizeof(d.llt)) - return -EINVAL; - break; - case DUID_TYPE_EN: - if (duid_len != sizeof(d.en)) - return -EINVAL; - break; - case DUID_TYPE_LL: - if (duid_len <= sizeof(d.ll)) - return -EINVAL; - break; - case DUID_TYPE_UUID: - if (duid_len != sizeof(d.uuid)) - return -EINVAL; - break; - default: - /* accept unknown type in order to be forward compatible */ - break; - } - return 0; -} - -#if 0 /* NM_IGNORED */ -static int dhcp_identifier_set_duid_llt( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - usec_t t, - struct duid *ret_duid, - size_t *ret_len) { - - uint16_t time_from_2000y; - - assert(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; -} - -static int dhcp_identifier_set_duid_ll( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - struct duid *ret_duid, - size_t *ret_len) { - - assert(hw_addr); - assert(ret_duid); - assert(ret_len); - - if (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; -} -#endif /* NM_IGNORED */ - -int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) { - sd_id128_t machine_id; - uint64_t hash; - int r; - - assert(ret_duid); - assert(ret_len); - - 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(ret_duid->en.id)); - - *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id); - - 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; -} - -#if 0 /* NM_IGNORED */ -static 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( - DUIDType duid_type, - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - usec_t llt_time, - bool test_mode, - struct duid *ret_duid, - size_t *ret_len) { - - switch (duid_type) { - case DUID_TYPE_LLT: - return dhcp_identifier_set_duid_llt(hw_addr, arp_type, llt_time, ret_duid, ret_len); - case DUID_TYPE_EN: - return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len); - case DUID_TYPE_LL: - return dhcp_identifier_set_duid_ll(hw_addr, arp_type, ret_duid, ret_len); - case DUID_TYPE_UUID: - return dhcp_identifier_set_duid_uuid(ret_duid, ret_len); - default: - return -EINVAL; - } -} -#endif /* NM_IGNORED */ - -int dhcp_identifier_set_iaid( - sd_device *dev, - const struct hw_addr_data *hw_addr, - bool legacy_unstable_byteorder, - void *ret) { -#if 0 /* NM_IGNORED */ - - 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; -#else /* NM_IGNORED */ - /* for NetworkManager, we don't use this function and we should never call here. - * This got replaced by nm_utils_create_dhcp_iaid(). */ - g_return_val_if_reached (-EINVAL); -#endif /* NM_IGNORED */ -} diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h deleted file mode 100644 index 523dfc4a..00000000 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "sd-device.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_MAX, - _DUID_TYPE_INVALID = -EINVAL, -} DUIDType; - -/* RFC 3315 section 9.1: - * A DUID can be no more than 128 octets long (not including the type code). - */ -#define MAX_DUID_LEN 128 - -/* https://tools.ietf.org/html/rfc3315#section-9.1 */ -struct duid { - be16_t type; - union { - struct { - /* DUID_TYPE_LLT */ - be16_t htype; - be32_t time; - uint8_t haddr[0]; - } _packed_ llt; - struct { - /* DUID_TYPE_EN */ - be32_t pen; - uint8_t id[8]; - } _packed_ en; - struct { - /* DUID_TYPE_LL */ - be16_t htype; - uint8_t haddr[0]; - } _packed_ ll; - struct { - /* DUID_TYPE_UUID */ - sd_id128_t uuid; - } _packed_ uuid; - struct { - uint8_t data[MAX_DUID_LEN]; - } _packed_ raw; - }; -} _packed_; - -int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len); -int dhcp_identifier_set_duid( - DUIDType duid_type, - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - usec_t llt_time, - bool test_mode, - struct duid *ret_duid, - size_t *ret_len); -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/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h new file mode 100644 index 00000000..6c17f574 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-client-internal.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp6-client.h" + +int dhcp6_client_set_state_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata); +int dhcp6_client_get_state(sd_dhcp6_client *client); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index fa43f28e..3fbfc028 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -11,7 +11,8 @@ #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" #include "ether-addr-util.h" @@ -63,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; @@ -79,10 +79,9 @@ struct sd_dhcp6_client { sd_dhcp6_client_callback_t callback; void *userdata; + sd_dhcp6_client_callback_t state_callback; + void *state_userdata; bool send_release; - - /* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */ - bool test_mode; }; int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); @@ -90,7 +89,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); int dhcp6_client_send_message(sd_dhcp6_client *client); -void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); #define log_dhcp6_client_errno(client, error, fmt, ...) \ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h index 1f10dccb..e76a108f 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h @@ -10,7 +10,9 @@ #include "sd-dhcp6-lease.h" #include "dhcp6-option.h" +#include "dhcp6-protocol.h" #include "macro.h" +#include "set.h" #include "time-util.h" struct sd_dhcp6_lease { @@ -43,9 +45,11 @@ struct sd_dhcp6_lease { struct in6_addr *sntp; size_t sntp_count; char *fqdn; + char *captive_portal; + struct sd_dhcp6_option **sorted_vendor_options; + Set *vendor_options; }; -int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); @@ -60,6 +64,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); int dhcp6_lease_new_from_message( @@ -69,3 +74,17 @@ int dhcp6_lease_new_from_message( const triple_timestamp *timestamp, const struct in6_addr *server_address, sd_dhcp6_lease **ret); + +#define _FOREACH_DHCP6_ADDRESS(lease, it) \ + for (int it = sd_dhcp6_lease_address_iterator_reset(lease); \ + it > 0; \ + it = sd_dhcp6_lease_address_iterator_next(lease)) +#define FOREACH_DHCP6_ADDRESS(lease) \ + _FOREACH_DHCP6_ADDRESS(lease, UNIQ_T(i, UNIQ)) + +#define _FOREACH_DHCP6_PD_PREFIX(lease, it) \ + for (int it = sd_dhcp6_lease_pd_iterator_reset(lease); \ + it > 0; \ + it = sd_dhcp6_lease_pd_iterator_next(lease)) +#define FOREACH_DHCP6_PD_PREFIX(lease) \ + _FOREACH_DHCP6_PD_PREFIX(lease, UNIQ_T(i, UNIQ)) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 23cf8a89..5fa9b265 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -17,6 +17,7 @@ #include "dns-domain.h" #include "escape.h" #include "memory-util.h" +#include "network-common.h" #include "strv.h" #include "unaligned.h" @@ -526,6 +527,26 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s return status; } +/* parse a string from dhcp option field. *ret must be initialized */ +int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret) { + _cleanup_free_ char *string = NULL; + int r; + + assert(data || data_len == 0); + assert(ret); + + if (data_len <= 0) { + *ret = mfree(*ret); + return 0; + } + + r = make_cstring((const char *) data, data_len, MAKE_CSTRING_REFUSE_TRAILING_NUL, &string); + if (r < 0) + return r; + + return free_and_replace(*ret, string); +} + static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { int r; @@ -567,7 +588,7 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { _cleanup_free_ DHCP6Address *a = NULL; - uint32_t lt_valid, lt_pref; + usec_t lt_valid, lt_pref; int r; assert(ia); @@ -586,17 +607,18 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); - lt_valid = be32toh(a->iaaddr.lifetime_valid); - lt_pref = be32toh(a->iaaddr.lifetime_preferred); + lt_valid = be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true); + lt_pref = be32_sec_to_usec(a->iaaddr.lifetime_preferred, /* max_as_infinity = */ true); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA address with zero valid lifetime, ignoring."); if (lt_pref > lt_valid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA address with preferred lifetime %"PRIu32 - " larger than valid lifetime %"PRIu32", ignoring.", - lt_pref, lt_valid); + "Received an IA address with preferred lifetime %s " + "larger than valid lifetime %s, ignoring.", + FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC), + FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC)); if (len > sizeof(struct iaaddr)) { r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr)); @@ -610,7 +632,7 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, c static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { _cleanup_free_ DHCP6Address *a = NULL; - uint32_t lt_valid, lt_pref; + usec_t lt_valid, lt_pref; int r; assert(ia); @@ -629,17 +651,18 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - lt_valid = be32toh(a->iapdprefix.lifetime_valid); - lt_pref = be32toh(a->iapdprefix.lifetime_preferred); + lt_valid = be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true); + lt_pref = be32_sec_to_usec(a->iapdprefix.lifetime_preferred, /* max_as_infinity = */ true); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received a PD prefix with zero valid lifetime, ignoring."); if (lt_pref > lt_valid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received a PD prefix with preferred lifetime %"PRIu32 - " larger than valid lifetime %"PRIu32", ignoring.", - lt_pref, lt_valid); + "Received a PD prefix with preferred lifetime %s " + "larger than valid lifetime %s, ignoring.", + FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC), + FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC)); if (len > sizeof(struct iapdprefix)) { r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix)); @@ -660,7 +683,7 @@ int dhcp6_option_parse_ia( DHCP6IA **ret) { _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; - uint32_t lt_t1, lt_t2; + usec_t lt_t1, lt_t2; size_t header_len; int r; @@ -710,17 +733,18 @@ int dhcp6_option_parse_ia( "from the one chosen by the client, ignoring."); /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ - lt_t1 = be32toh(ia->header.lifetime_t1); - lt_t2 = be32toh(ia->header.lifetime_t2); + lt_t1 = be32_sec_to_usec(ia->header.lifetime_t1, /* max_as_infinity = */ true); + lt_t2 = be32_sec_to_usec(ia->header.lifetime_t2, /* max_as_infinity = */ true); if (lt_t1 > lt_t2) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", - lt_t1, lt_t2); + "Received an IA option with T1 %s > T2 %s, ignoring.", + FORMAT_TIMESPAN(lt_t1, USEC_PER_SEC), + FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC)); if (lt_t1 == 0 && lt_t2 > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.", - lt_t2); + "Received an IA option with zero T1 and non-zero T2 (%s), ignoring.", + FORMAT_TIMESPAN(lt_t2, USEC_PER_SEC)); for (size_t offset = header_len; offset < option_data_len;) { const uint8_t *subdata; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h index 36841dd2..614b4f8a 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h @@ -88,6 +88,7 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data); int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); +int dhcp6_option_parse_string(const uint8_t *data, size_t data_len, char **ret); int dhcp6_option_parse_ia( sd_dhcp6_client *client, be32_t iaid, diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.c b/src/libnm-systemd-core/src/libsystemd-network/network-common.c index bbc28941..8d62e6ff 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-common.c +++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.c @@ -2,8 +2,11 @@ #include "nm-sd-adapt-core.h" +#include "env-util.h" #include "format-util.h" #include "network-common.h" +#include "socket-util.h" +#include "unaligned.h" int get_ifname(int ifindex, char **ifname) { assert(ifname); @@ -15,3 +18,111 @@ int get_ifname(int ifindex, char **ifname) { return format_ifname_alloc(ifindex, ifname); } + +usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity) { + uint32_t s = unaligned_read_be32(ASSERT_PTR(p)); + + if (s == UINT32_MAX && max_as_infinity) + return USEC_INFINITY; + + return s * USEC_PER_SEC; +} + +usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity) { + uint32_t s = be32toh(t); + + if (s == UINT32_MAX && max_as_infinity) + return USEC_INFINITY; + + return s * USEC_PER_SEC; +} + +usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity) { + uint32_t s = be32toh(t); + + if (s == UINT32_MAX && max_as_infinity) + return USEC_INFINITY; + + return s * USEC_PER_MSEC; +} + +usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity) { + uint16_t s = be16toh(t); + + if (s == UINT16_MAX && max_as_infinity) + return USEC_INFINITY; + + return s * USEC_PER_SEC; +} + +be32_t usec_to_be32_sec(usec_t t) { + if (t == USEC_INFINITY) + /* Some settings, e.g. a lifetime of an address, UINT32_MAX is handled as infinity. so let's + * map USEC_INFINITY to UINT32_MAX. */ + return htobe32(UINT32_MAX); + + if (t >= (UINT32_MAX - 1) * USEC_PER_SEC) + /* Finite but too large. Let's use the largest (or off-by-one from the largest) finite value. */ + return htobe32(UINT32_MAX - 1); + + return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_SEC)); +} + +be32_t usec_to_be32_msec(usec_t t) { + if (t == USEC_INFINITY) + return htobe32(UINT32_MAX); + + if (t >= (UINT32_MAX - 1) * USEC_PER_MSEC) + return htobe32(UINT32_MAX - 1); + + return htobe32((uint32_t) DIV_ROUND_UP(t, USEC_PER_MSEC)); +} + +be16_t usec_to_be16_sec(usec_t t) { + if (t == USEC_INFINITY) + return htobe16(UINT16_MAX); + + if (t >= (UINT16_MAX - 1) * USEC_PER_SEC) + return htobe16(UINT16_MAX - 1); + + return htobe16((uint16_t) DIV_ROUND_UP(t, USEC_PER_SEC)); +} + +usec_t time_span_to_stamp(usec_t span, usec_t base) { + /* Typically, 0 lifetime (timespan) indicates the corresponding configuration (address or so) must be + * dropped. So, when the timespan is zero, here we return 0 rather than 'base'. This makes the caller + * easily understand that the configuration needs to be dropped immediately. */ + if (span == 0) + return 0; + + return usec_add(base, span); +} + +bool network_test_mode_enabled(void) { + static int test_mode = -1; + int r; + + if (test_mode < 0) { + r = getenv_bool("SYSTEMD_NETWORK_TEST_MODE"); + if (r < 0) { + if (r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_NETWORK_TEST_MODE environment variable, ignoring: %m"); + + test_mode = false; + } else + test_mode = r; + } + + return test_mode; +} + +triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh) { + assert(t); + assert(mh); + + struct timeval *tv = CMSG_FIND_AND_COPY_DATA(mh, SOL_SOCKET, SCM_TIMESTAMP, struct timeval); + if (tv) + return triple_timestamp_from_realtime(t, timeval_load(tv)); + + return triple_timestamp_now(t); +} diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.h b/src/libnm-systemd-core/src/libsystemd-network/network-common.h index 2b0e3b56..1750f181 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-common.h +++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.h @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <sys/socket.h> + #include "log-link.h" +#include "sparse-endian.h" +#include "time-util.h" #define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \ ({ \ @@ -28,3 +32,18 @@ }) int get_ifname(int ifindex, char **ifname); + +usec_t unaligned_be32_sec_to_usec(const void *p, bool max_as_infinity); +usec_t be32_sec_to_usec(be32_t t, bool max_as_infinity); +usec_t be32_msec_to_usec(be32_t t, bool max_as_infinity); +usec_t be16_sec_to_usec(be16_t t, bool max_as_infinity); +be32_t usec_to_be32_sec(usec_t t); +be32_t usec_to_be32_msec(usec_t t); +be16_t usec_to_be16_sec(usec_t t); +usec_t time_span_to_stamp(usec_t span, usec_t base); + +bool network_test_mode_enabled(void); + +triple_timestamp* triple_timestamp_from_cmsg(triple_timestamp *t, struct msghdr *mh); +#define TRIPLE_TIMESTAMP_FROM_CMSG(mh) \ + triple_timestamp_from_cmsg(&(triple_timestamp) {}, mh) diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c new file mode 100644 index 00000000..e664a4a7 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-duid.c @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#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/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index 0e6f21f4..7c201164 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -7,27 +7,24 @@ #include <errno.h> #include <sys/ioctl.h> -#if 0 /* NM_IGNORED */ +#ifdef __GLIBC__ #include <linux/if_arp.h> -#else /* NM_IGNORED */ -#include <net/if_arp.h> -#endif /* NM_IGNORED */ +#endif #include <linux/if_infiniband.h> #include "sd-dhcp6-client.h" #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 "io-util.h" +#include "iovec-util.h" #include "random-util.h" #include "socket-util.h" #include "sort-util.h" @@ -52,6 +49,19 @@ int sd_dhcp6_client_set_callback( return 0; } +int dhcp6_client_set_state_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata) { + + assert_return(client, -EINVAL); + + client->state_callback = cb; + client->state_userdata = userdata; + + return 0; +} + int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); @@ -184,10 +194,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->test_mode, &client->duid, &client->duid_len); + return sd_dhcp6_client_set_duid_en(client); } /** @@ -195,101 +205,102 @@ static int client_ensure_duid(sd_dhcp6_client *client) { * without further modification. Otherwise, if duid_type is supported, DUID * is set based on that type. Otherwise, an error is returned. */ -static int dhcp6_client_set_duid_internal( - sd_dhcp6_client *client, - DUIDType duid_type, - const void *duid, - size_t duid_len, - usec_t llt_time) { +int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) { int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - assert_return(duid_len == 0 || duid, -EINVAL); - if (duid) { - r = dhcp_validate_duid_len(duid_type, duid_len, true); - if (r < 0) { - r = dhcp_validate_duid_len(duid_type, duid_len, false); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m"); + 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"); - log_dhcp6_client(client, "Using DUID of type %i of incorrect length, proceeding.", duid_type); - } + return 0; +} - client->duid.type = htobe16(duid_type); - memcpy(&client->duid.raw.data, duid, duid_len); - client->duid_len = sizeof(client->duid.type) + duid_len; +int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) { + int r; - } else { -#if 0 /* NM_IGNORED */ - r = dhcp_identifier_set_duid(duid_type, &client->hw_addr, client->arp_type, llt_time, - client->test_mode, &client->duid, &client->duid_len); - if (r == -EOPNOTSUPP) - return log_dhcp6_client_errno(client, r, - "Failed to set %s. MAC address is not set or " - "interface type is not supported.", - duid_type_to_string(duid_type)); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set %s: %m", - duid_type_to_string(duid_type)); -#else /* NM_IGNORED */ - g_return_val_if_reached (-EINVAL); -#endif /* NM_IGNORED */ - } + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + 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"); return 0; } -int sd_dhcp6_client_set_duid( - sd_dhcp6_client *client, - uint16_t duid_type, - const void *duid, - size_t duid_len) { - return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0); +int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) { + int r; + + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + r = sd_dhcp_duid_set_en(&client->duid); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); + + return 0; } -int sd_dhcp6_client_set_duid_llt( - sd_dhcp6_client *client, - usec_t llt_time) { - return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); +int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) { + int r; + + assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + + r = sd_dhcp_duid_set_uuid(&client->duid); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %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 sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len) { int r; assert_return(client, -EINVAL); - assert_return(client->duid_len > 0, -ENODATA); - assert_return(duid, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(duid || duid_len == 0, -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; - } + 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"); - t = hexmem(&client->duid.raw.data, client->duid_len); - if (!t) - return -ENOMEM; + return 0; +} - p = strjoin(s, ":", t); - if (!p) - return -ENOMEM; +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); - *duid = TAKE_PTR(p); + 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(ret, -EINVAL); + + if (!sd_dhcp_duid_is_set(&client->duid)) + return -ENODATA; + + *ret = &client->duid; return 0; } +int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret) { + assert_return(client, -EINVAL); + assert_return(ret, -EINVAL); + + if (!sd_dhcp_duid_is_set(&client->duid)) + return -ENODATA; + + return sd_dhcp_duid_to_string(&client->duid, ret); +} + int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); @@ -335,12 +346,6 @@ int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { return 0; } -void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { - assert(client); - - client->test_mode = test_mode; -} - int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { @@ -491,7 +496,7 @@ int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { assert(client); - assert(client->test_mode); + assert_se(network_test_mode_enabled()); /* This is for tests or fuzzers. */ @@ -510,7 +515,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; @@ -552,6 +556,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) { dhcp6_state_to_string(client->state), dhcp6_state_to_string(state)); client->state = state; + + if (client->state_callback) + client->state_callback(client, state, client->state_userdata); +} + +int dhcp6_client_get_state(sd_dhcp6_client *client) { + assert_return(client, -EINVAL); + + return client->state; } static void client_notify(sd_dhcp6_client *client, int event) { @@ -809,9 +822,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; @@ -824,7 +837,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { /* RFC 8415 Section 21.9. * A client MUST include an Elapsed Time option in messages to indicate how long the client has * been trying to complete a DHCP message exchange. */ - elapsed_usec = NM_MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX); + elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX); elapsed_time = htobe16(elapsed_usec); r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time); if (r < 0) @@ -1047,12 +1060,20 @@ static int client_enter_bound_state(sd_dhcp6_client *client) { (void) event_source_disable(client->receive_message); (void) event_source_disable(client->timeout_resend); - r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); + r = sd_dhcp6_lease_get_t1(client->lease, &lifetime_t1); + if (r < 0) + goto error; + + r = sd_dhcp6_lease_get_t2(client->lease, &lifetime_t2); + if (r < 0) + goto error; + + r = sd_dhcp6_lease_get_valid_lifetime(client->lease, &lifetime_valid); if (r < 0) goto error; lifetime_t2 = client_timeout_compute_random(lifetime_t2); - lifetime_t1 = client_timeout_compute_random(NM_MIN(lifetime_t1, lifetime_t2)); + lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2)); if (lifetime_t1 == USEC_INFINITY) { log_dhcp6_client(client, "Infinite T1"); @@ -1286,16 +1307,15 @@ static int client_receive_message( .msg_control = &control, .msg_controllen = sizeof(control), }; - triple_timestamp t = {}; + triple_timestamp t; _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; ssize_t buflen, len; buflen = next_datagram_size_fd(fd); + if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen)) + return 0; if (buflen < 0) { - if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen)) - return 0; - log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m"); return 0; } @@ -1307,10 +1327,9 @@ static int client_receive_message( iov = IOVEC_MAKE(message, buflen); len = recvmsg_safe(fd, &msg, MSG_DONTWAIT); + if (ERRNO_IS_NEG_TRANSIENT(len) || ERRNO_IS_NEG_DISCONNECT(len)) + return 0; if (len < 0) { - if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len)) - return 0; - log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m"); return 0; } @@ -1329,9 +1348,7 @@ static int client_receive_message( server_address = &sa.in6.sin6_addr; } - struct timeval *tv = CMSG_FIND_AND_COPY_DATA(&msg, SOL_SOCKET, SCM_TIMESTAMP, struct timeval); - if (tv) - triple_timestamp_from_realtime(&t, timeval_load(tv)); + triple_timestamp_from_cmsg(&t, &msg); if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index 3e25b4e8..a42df243 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -10,7 +10,9 @@ #include "alloc-util.h" #include "dhcp6-internal.h" #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) @@ -21,7 +23,7 @@ static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timest if (timestamp && triple_timestamp_is_set(timestamp)) lease->timestamp = *timestamp; else - triple_timestamp_get(&lease->timestamp); + triple_timestamp_now(&lease->timestamp); } int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { @@ -37,30 +39,26 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } -static usec_t sec2usec(uint32_t sec) { - return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC; -} - static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { - uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; + usec_t t1 = USEC_INFINITY, t2 = USEC_INFINITY, min_valid_lt = USEC_INFINITY; assert(lease); assert(lease->ia_na || lease->ia_pd); if (lease->ia_na) { - t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1)); - t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2)); + t1 = MIN(t1, be32_sec_to_usec(lease->ia_na->header.lifetime_t1, /* max_as_infinity = */ true)); + t2 = MIN(t2, be32_sec_to_usec(lease->ia_na->header.lifetime_t2, /* max_as_infinity = */ true)); LIST_FOREACH(addresses, a, lease->ia_na->addresses) - min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid)); + min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iaaddr.lifetime_valid, /* max_as_infinity = */ true)); } if (lease->ia_pd) { - t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1)); - t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2)); + t1 = MIN(t1, be32_sec_to_usec(lease->ia_pd->header.lifetime_t1, /* max_as_infinity = */ true)); + t2 = MIN(t2, be32_sec_to_usec(lease->ia_pd->header.lifetime_t2, /* max_as_infinity = */ true)); LIST_FOREACH(addresses, a, lease->ia_pd->addresses) - min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid)); + min_valid_lt = MIN(min_valid_lt, be32_sec_to_usec(a->iapdprefix.lifetime_valid, /* max_as_infinity = */ true)); } if (t2 == 0 || t2 > min_valid_lt) { @@ -70,25 +68,52 @@ static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { t2 = min_valid_lt / 10 * 8; } - lease->lifetime_valid = sec2usec(min_valid_lt); - lease->lifetime_t1 = sec2usec(t1); - lease->lifetime_t2 = sec2usec(t2); -} - -int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) { - assert(lease); + lease->lifetime_valid = min_valid_lt; + lease->lifetime_t1 = t1; + lease->lifetime_t2 = t2; +} + +#define DEFINE_GET_TIME_FUNCTIONS(name, val) \ + int sd_dhcp6_lease_get_##name( \ + sd_dhcp6_lease *lease, \ + uint64_t *ret) { \ + \ + assert_return(lease, -EINVAL); \ + \ + if (!lease->ia_na && !lease->ia_pd) \ + return -ENODATA; \ + \ + if (ret) \ + *ret = lease->val; \ + return 0; \ + } \ + \ + int sd_dhcp6_lease_get_##name##_timestamp( \ + sd_dhcp6_lease *lease, \ + clockid_t clock, \ + uint64_t *ret) { \ + \ + usec_t s, t; \ + int r; \ + \ + assert_return(lease, -EINVAL); \ + \ + r = sd_dhcp6_lease_get_##name(lease, &s); \ + if (r < 0) \ + return r; \ + \ + r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \ + if (r < 0) \ + return r; \ + \ + if (ret) \ + *ret = time_span_to_stamp(s, t); \ + return 0; \ + } - if (!lease->ia_na && !lease->ia_pd) - return -ENODATA; - - if (ret_t1) - *ret_t1 = lease->lifetime_t1; - if (ret_t2) - *ret_t2 = lease->lifetime_t2; - if (ret_valid) - *ret_valid = lease->lifetime_valid; - return 0; -} +DEFINE_GET_TIME_FUNCTIONS(t1, lifetime_t1); +DEFINE_GET_TIME_FUNCTIONS(t2, lifetime_t1); +DEFINE_GET_TIME_FUNCTIONS(valid_lifetime, lifetime_valid); static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) { assert(lease); @@ -218,61 +243,151 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) { return 0; } -int sd_dhcp6_lease_get_address( +int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { + assert_return(lease, -EINVAL); + + if (!lease->addr_iter) + return -ENODATA; + + if (ret) + *ret = lease->addr_iter->iaaddr.address; + return 0; +} + +int sd_dhcp6_lease_get_address_lifetime( sd_dhcp6_lease *lease, - struct in6_addr *ret_addr, - uint32_t *ret_lifetime_preferred, - uint32_t *ret_lifetime_valid) { + usec_t *ret_lifetime_preferred, + usec_t *ret_lifetime_valid) { + + const struct iaaddr *a; assert_return(lease, -EINVAL); if (!lease->addr_iter) return -ENODATA; - if (ret_addr) - *ret_addr = lease->addr_iter->iaaddr.address; + a = &lease->addr_iter->iaaddr; + if (ret_lifetime_preferred) - *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true); if (ret_lifetime_valid) - *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true); + return 0; +} + +int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease) { + if (!lease) + return false; + + lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; + return !!lease->addr_iter; +} + +int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease) { + if (!lease || !lease->addr_iter) + return false; lease->addr_iter = lease->addr_iter->addresses_next; - return 0; + return !!lease->addr_iter; } -void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { - if (lease) - lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; +int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease) { + return lease && lease->ia_na; } -int sd_dhcp6_lease_get_pd( +int sd_dhcp6_lease_get_pd_prefix( sd_dhcp6_lease *lease, struct in6_addr *ret_prefix, - uint8_t *ret_prefix_len, - uint32_t *ret_lifetime_preferred, - uint32_t *ret_lifetime_valid) { + uint8_t *ret_prefix_len) { + + const struct iapdprefix *a; assert_return(lease, -EINVAL); if (!lease->prefix_iter) return -ENODATA; + a = &lease->prefix_iter->iapdprefix; + if (ret_prefix) - *ret_prefix = lease->prefix_iter->iapdprefix.address; + *ret_prefix = a->address; if (ret_prefix_len) - *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + *ret_prefix_len = a->prefixlen; + return 0; +} + +int sd_dhcp6_lease_get_pd_lifetime( + sd_dhcp6_lease *lease, + uint64_t *ret_lifetime_preferred, + uint64_t *ret_lifetime_valid) { + + const struct iapdprefix *a; + + assert_return(lease, -EINVAL); + + if (!lease->prefix_iter) + return -ENODATA; + + a = &lease->prefix_iter->iapdprefix; + if (ret_lifetime_preferred) - *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + *ret_lifetime_preferred = be32_sec_to_usec(a->lifetime_preferred, /* max_as_infinity = */ true); if (ret_lifetime_valid) - *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); - - lease->prefix_iter = lease->prefix_iter->addresses_next; + *ret_lifetime_valid = be32_sec_to_usec(a->lifetime_valid, /* max_as_infinity = */ true); return 0; } -void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { - if (lease) - lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; +int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease) { + if (!lease) + return false; + + lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; + return !!lease->prefix_iter; +} + +int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease) { + if (!lease || !lease->prefix_iter) + return false; + + lease->prefix_iter = lease->prefix_iter->addresses_next; + return !!lease->prefix_iter; +} + +#define DEFINE_GET_TIMESTAMP2(name) \ + int sd_dhcp6_lease_get_##name##_lifetime_timestamp( \ + sd_dhcp6_lease *lease, \ + clockid_t clock, \ + uint64_t *ret_lifetime_preferred, \ + uint64_t *ret_lifetime_valid) { \ + \ + usec_t t, p, v; \ + int r; \ + \ + assert_return(lease, -EINVAL); \ + \ + r = sd_dhcp6_lease_get_##name##_lifetime( \ + lease, \ + ret_lifetime_preferred ? &p : NULL, \ + ret_lifetime_valid ? &v : NULL); \ + if (r < 0) \ + return r; \ + \ + r = sd_dhcp6_lease_get_timestamp(lease, clock, &t); \ + if (r < 0) \ + return r; \ + \ + if (ret_lifetime_preferred) \ + *ret_lifetime_preferred = time_span_to_stamp(p, t); \ + if (ret_lifetime_valid) \ + *ret_lifetime_valid = time_span_to_stamp(v, t); \ + return 0; \ + } + +DEFINE_GET_TIMESTAMP2(address); +DEFINE_GET_TIMESTAMP2(pd); + +int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) { + return lease && lease->ia_pd; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -447,6 +562,111 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { return 0; } +int dhcp6_lease_set_captive_portal(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { + _cleanup_free_ char *uri = NULL; + int r; + + assert(lease); + assert(optval || optlen == 0); + + r = dhcp6_option_parse_string(optval, optlen, &uri); + if (r < 0) + return r; + + if (uri && !in_charset(uri, URI_VALID)) + return -EINVAL; + + return free_and_replace(lease->captive_portal, uri); +} + +int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret) { + assert_return(lease, -EINVAL); + assert_return(ret, -EINVAL); + + if (!lease->captive_portal) + return -ENODATA; + + *ret = lease->captive_portal; + return 0; +} + +int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret) { + int r; + + assert_return(lease, -EINVAL); + + if (set_isempty(lease->vendor_options)) + return -ENODATA; + + if (ret) { + if (!lease->sorted_vendor_options) { + r = set_dump_sorted(lease->vendor_options, (void***) &lease->sorted_vendor_options, NULL); + if (r < 0) + return r; + } + + *ret = lease->sorted_vendor_options; + } + + return set_size(lease->vendor_options); +} + +static int dhcp6_lease_insert_vendor_option( + sd_dhcp6_lease *lease, + uint16_t option_code, + const void *data, + size_t len, + uint32_t enterprise_id) { + + _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *option = NULL; + + assert(lease); + + option = new(sd_dhcp6_option, 1); + if (!option) + return -ENOMEM; + + *option = (sd_dhcp6_option) { + .n_ref = 1, + .enterprise_identifier = enterprise_id, + .option = option_code, + .length = len, + }; + option->data = memdup_suffix0(data, len); + if (!option->data) + return -ENOMEM; + + return set_ensure_consume(&lease->vendor_options, &dhcp6_option_hash_ops, TAKE_PTR(option)); +} + +static int dhcp6_lease_add_vendor_option(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { + int r; + uint32_t enterprise_id; + + assert(lease); + assert(optval || optlen == 0); + + if (optlen < sizeof(be32_t)) + return -EBADMSG; + + enterprise_id = unaligned_read_be32(optval); + + for (size_t offset = 4; offset < optlen;) { + const uint8_t *subval; + size_t sublen; + uint16_t subopt; + + r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval); + if (r < 0) + return r; + + r = dhcp6_lease_insert_vendor_option(lease, subopt, subval, sublen, enterprise_id); + if (r < 0) + return r; + } + return 0; +} + static int dhcp6_lease_parse_message( sd_dhcp6_client *client, sd_dhcp6_lease *lease, @@ -467,6 +687,11 @@ static int dhcp6_lease_parse_message( size_t optlen; const uint8_t *optval; + if (len - offset < offsetof(DHCP6Option, data)) { + log_dhcp6_client(client, "Ignoring %zu invalid byte(s) at the end of the packet", len - offset); + break; + } + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); if (r < 0) return log_dhcp6_client_errno(client, r, @@ -607,6 +832,12 @@ static int dhcp6_lease_parse_message( break; + case SD_DHCP6_OPTION_CAPTIVE_PORTAL: + r = dhcp6_lease_set_captive_portal(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse captive portal option, ignoring: %m"); + break; + case SD_DHCP6_OPTION_CLIENT_FQDN: r = dhcp6_lease_set_fqdn(lease, optval, optlen); if (r < 0) @@ -619,7 +850,14 @@ static int dhcp6_lease_parse_message( return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received information refresh time option with an invalid length (%zu).", optlen); - irt = unaligned_read_be32(optval) * USEC_PER_SEC; + irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false); + break; + + case SD_DHCP6_OPTION_VENDOR_OPTS: + r = dhcp6_lease_add_vendor_option(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse vendor option, ignoring: %m"); + break; } } @@ -631,7 +869,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)); @@ -661,12 +899,15 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { if (!lease) return NULL; + set_free(lease->vendor_options); + free(lease->sorted_vendor_options); free(lease->clientid); free(lease->serverid); dhcp6_ia_free(lease->ia_na); dhcp6_ia_free(lease->ia_pd); free(lease->dns); free(lease->fqdn); + free(lease->captive_portal); strv_free(lease->domains); free(lease->ntp); strv_free(lease->ntp_fqdn); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h new file mode 100644 index 00000000..a465eb25 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-device.h" + +#include "device-private.h" +#include "hashmap.h" +#include "set.h" +#include "time-util.h" + +#define LATEST_UDEV_DATABASE_VERSION 1 + +struct sd_device { + unsigned n_ref; + + /* The database version indicates the supported features by the udev database. + * This is saved and parsed in V field. + * + * 0: None of the following features are supported (systemd version <= 246). + * 1: The current tags (Q) and the database version (V) features are implemented (>= 247). + */ + unsigned database_version; + + sd_device *parent; + + OrderedHashmap *properties; + Iterator properties_iterator; + uint64_t properties_generation; /* changes whenever the properties are changed */ + uint64_t properties_iterator_generation; /* generation when iteration was started */ + + /* the subset of the properties that should be written to the db */ + OrderedHashmap *properties_db; + + Hashmap *sysattr_values; /* cached sysattr values */ + + Set *sysattrs; /* names of sysattrs */ + Iterator sysattrs_iterator; + + Set *all_tags, *current_tags; + Iterator all_tags_iterator, current_tags_iterator; + uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */ + uint64_t tags_generation; /* changes whenever the tags are changed */ + + Set *devlinks; + Iterator devlinks_iterator; + uint64_t devlinks_generation; /* changes whenever the devlinks are changed */ + uint64_t devlinks_iterator_generation; /* generation when iteration was started */ + int devlink_priority; + + Hashmap *children; + Iterator children_iterator; + bool children_enumerated; + + int ifindex; + char *devtype; + char *devname; + dev_t devnum; + + char **properties_strv; /* the properties hashmap as a strv */ + char *properties_nulstr; /* the same as a nulstr */ + size_t properties_nulstr_len; + + char *syspath; + const char *devpath; + const char *sysnum; + char *sysname; + + char *subsystem; + char *driver_subsystem; /* only set for the 'drivers' subsystem */ + char *driver; + + char *device_id; + + usec_t usec_initialized; + + mode_t devmode; + uid_t devuid; + gid_t devgid; + + uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */ + + /* only set when device is passed through netlink */ + sd_device_action_t action; + uint64_t seqnum; + + bool parent_set:1; /* no need to try to reload parent */ + bool sysattrs_read:1; /* don't try to re-read sysattrs once read */ + bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */ + bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */ + bool properties_buf_outdated:1; /* need to reread hashmap */ + bool subsystem_set:1; /* don't reread subsystem */ + bool driver_set:1; /* don't reread driver */ + bool uevent_loaded:1; /* don't reread uevent */ + bool db_loaded; /* don't reread db */ + + bool is_initialized:1; + bool sealed:1; /* don't read more information from uevent/db */ + bool db_persist:1; /* don't clean up the db when switching from initrd to real root */ +}; + +int device_new_aux(sd_device **ret); +int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); +static inline int device_add_property_internal(sd_device *device, const char *key, const char *value) { + return device_add_property_aux(device, key, value, false); +} + +int device_set_syspath(sd_device *device, const char *_syspath, bool verify); +int device_set_ifindex(sd_device *device, const char *ifindex); +int device_set_devmode(sd_device *device, const char *devmode); +int device_set_devname(sd_device *device, const char *devname); +int device_set_devtype(sd_device *device, const char *devtype); +int device_set_devnum(sd_device *device, const char *major, const char *minor); +int device_set_subsystem(sd_device *device, const char *subsystem); +int device_set_diskseq(sd_device *device, const char *str); +int device_set_drivers_subsystem(sd_device *device); +int device_set_driver(sd_device *device, const char *driver); +int device_set_usec_initialized(sd_device *device, usec_t when); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c new file mode 100644 index 00000000..eef3d618 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c @@ -0,0 +1,962 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#include <ctype.h> +#include <net/if.h> +#include <sys/types.h> + +#include "sd-device.h" + +#include "alloc-util.h" +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "hashmap.h" +#include "macro.h" +#include "mkdir.h" +#include "nulstr-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "set.h" +#include "stdio-util.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "strxcpyx.h" +#include "tmpfile-util.h" +#include "user-util.h" + +#if 0 /* NM_IGNORED */ +int device_add_property(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + if (key[0] != '.') { + r = device_add_property_aux(device, key, value, true); + if (r < 0) + return r; + } + + return 0; +} + +int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) { + _cleanup_free_ char *value = NULL; + va_list ap; + int r; + + assert(device); + assert(key); + + if (!format) + return device_add_property(device, key, NULL); + + va_start(ap, format); + r = vasprintf(&value, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return device_add_property(device, key, value); +} + +void device_set_devlink_priority(sd_device *device, int priority) { + assert(device); + + device->devlink_priority = priority; +} + +void device_set_is_initialized(sd_device *device) { + assert(device); + + device->is_initialized = true; +} + +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) { + usec_t when; + + assert(device); + + if (device_old && device_old->usec_initialized > 0) + when = device_old->usec_initialized; + else + when = now(CLOCK_MONOTONIC); + + return device_set_usec_initialized(device, when); +} + +uint64_t device_get_properties_generation(sd_device *device) { + assert(device); + + return device->properties_generation; +} + +uint64_t device_get_tags_generation(sd_device *device) { + assert(device); + + return device->tags_generation; +} + +uint64_t device_get_devlinks_generation(sd_device *device) { + assert(device); + + return device->devlinks_generation; +} + +int device_get_devnode_mode(sd_device *device, mode_t *ret) { + int r; + + assert(device); + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->devmode == MODE_INVALID) + return -ENOENT; + + if (ret) + *ret = device->devmode; + + return 0; +} + +int device_get_devnode_uid(sd_device *device, uid_t *ret) { + int r; + + assert(device); + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->devuid == UID_INVALID) + return -ENOENT; + + if (ret) + *ret = device->devuid; + + return 0; +} + +static int device_set_devuid(sd_device *device, const char *uid) { + uid_t u; + int r; + + assert(device); + assert(uid); + + r = parse_uid(uid, &u); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVUID", uid); + if (r < 0) + return r; + + device->devuid = u; + + return 0; +} + +int device_get_devnode_gid(sd_device *device, gid_t *ret) { + int r; + + assert(device); + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->devgid == GID_INVALID) + return -ENOENT; + + if (ret) + *ret = device->devgid; + + return 0; +} + +static int device_set_devgid(sd_device *device, const char *gid) { + gid_t g; + int r; + + assert(device); + assert(gid); + + r = parse_gid(gid, &g); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVGID", gid); + if (r < 0) + return r; + + device->devgid = g; + + return 0; +} + +int device_set_action(sd_device *device, sd_device_action_t a) { + int r; + + assert(device); + assert(a >= 0 && a < _SD_DEVICE_ACTION_MAX); + + r = device_add_property_internal(device, "ACTION", device_action_to_string(a)); + if (r < 0) + return r; + + device->action = a; + + return 0; +} + +static int device_set_action_from_string(sd_device *device, const char *action) { + sd_device_action_t a; + + assert(device); + assert(action); + + a = device_action_from_string(action); + if (a < 0) + return a; + + return device_set_action(device, a); +} + +static int device_set_seqnum(sd_device *device, const char *str) { + uint64_t seqnum; + int r; + + assert(device); + assert(str); + + r = safe_atou64(str, &seqnum); + if (r < 0) + return r; + if (seqnum == 0) + return -EINVAL; + + r = device_add_property_internal(device, "SEQNUM", str); + if (r < 0) + return r; + + device->seqnum = seqnum; + + return 0; +} + +static int device_amend(sd_device *device, const char *key, const char *value) { + int r; + + assert(device); + assert(key); + assert(value); + + if (streq(key, "DEVPATH")) { + char *path; + + path = strjoina("/sys", value); + + /* the caller must verify or trust this data (e.g., if it comes from the kernel) */ + r = device_set_syspath(device, path, false); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path); + } else if (streq(key, "SUBSYSTEM")) { + r = device_set_subsystem(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value); + } else if (streq(key, "DEVTYPE")) { + r = device_set_devtype(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value); + } else if (streq(key, "DEVNAME")) { + r = device_set_devname(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value); + } else if (streq(key, "USEC_INITIALIZED")) { + usec_t t; + + r = safe_atou64(value, &t); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value); + + r = device_set_usec_initialized(device, t); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value); + } else if (streq(key, "DRIVER")) { + r = device_set_driver(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value); + } else if (streq(key, "IFINDEX")) { + r = device_set_ifindex(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value); + } else if (streq(key, "DEVMODE")) { + r = device_set_devmode(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value); + } else if (streq(key, "DEVUID")) { + r = device_set_devuid(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value); + } else if (streq(key, "DEVGID")) { + r = device_set_devgid(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value); + } else if (streq(key, "ACTION")) { + r = device_set_action_from_string(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set action to '%s': %m", value); + } else if (streq(key, "SEQNUM")) { + r = device_set_seqnum(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value); + } else if (streq(key, "DISKSEQ")) { + r = device_set_diskseq(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set DISKSEQ to '%s': %m", value); + } else if (streq(key, "DEVLINKS")) { + for (const char *p = value;;) { + _cleanup_free_ char *word = NULL; + + /* udev rules may set escaped strings, and sd-device does not modify the input + * strings. So, it is also necessary to keep the strings received through + * sd-device-monitor. */ + r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return r; + if (r == 0) + break; + + r = device_add_devlink(device, word); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word); + } + } else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) { + for (const char *p = value;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; + if (isempty(word)) + continue; + + r = device_add_tag(device, word, streq(key, "CURRENT_TAGS")); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word); + } + } else if (streq(key, "UDEV_DATABASE_VERSION")) { + r = safe_atou(value, &device->database_version); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to parse udev database version '%s': %m", value); + } else { + r = device_add_property_internal(device, key, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value); + } + + return 0; +} + +static int device_append( + sd_device *device, + char *key, + const char **_major, + const char **_minor) { + + const char *major = NULL, *minor = NULL; + char *value; + int r; + + assert(device); + assert(key); + assert(_major); + assert(_minor); + + value = strchr(key, '='); + if (!value) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Not a key-value pair: '%s'", key); + + *value = '\0'; + + value++; + + if (streq(key, "MAJOR")) + major = value; + else if (streq(key, "MINOR")) + minor = value; + else { + r = device_amend(device, key, value); + if (r < 0) + return r; + } + + if (major) + *_major = major; + + if (minor) + *_minor = minor; + + return 0; +} + +void device_seal(sd_device *device) { + assert(device); + + device->sealed = true; +} + +static int device_verify(sd_device *device) { + int r; + + assert(device); + + if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); + + if (streq(device->subsystem, "drivers")) { + r = device_set_drivers_subsystem(device); + if (r < 0) + return r; + } + + device->sealed = true; + + return 0; +} + +int device_new_from_strv(sd_device **ret, char **strv) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *major = NULL, *minor = NULL; + int r; + + assert(ret); + assert(strv); + + r = device_new_aux(&device); + if (r < 0) + return r; + + STRV_FOREACH(key, strv) { + r = device_append(device, *key, &major, &minor); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device); + if (r < 0) + return r; + + *ret = TAKE_PTR(device); + + return 0; +} + +int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + const char *major = NULL, *minor = NULL; + int r; + + assert(ret); + assert(nulstr); + assert(len); + + r = device_new_aux(&device); + if (r < 0) + return r; + + for (size_t i = 0; i < len; ) { + char *key; + const char *end; + + key = nulstr + i; + end = memchr(key, '\0', len - i); + if (!end) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), + "sd-device: Failed to parse nulstr"); + + i += end - key + 1; + + /* netlink messages for some devices contain an unwanted newline at the end of value. + * Let's drop the newline and remaining characters after the newline. */ + truncate_nl(key); + + r = device_append(device, key, &major, &minor); + if (r < 0) + return r; + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); + } + + r = device_verify(device); + if (r < 0) + return r; + + *ret = TAKE_PTR(device); + + return 0; +} + +static int device_update_properties_bufs(sd_device *device) { + _cleanup_free_ char **buf_strv = NULL, *buf_nulstr = NULL; + size_t nulstr_len = 0, num = 0; + + assert(device); + + if (!device->properties_buf_outdated) + return 0; + + /* append udev database version */ + buf_nulstr = newdup(char, "UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\0", + STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 2); + if (!buf_nulstr) + return -ENOMEM; + + nulstr_len += STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 1; + num++; + + FOREACH_DEVICE_PROPERTY(device, prop, val) { + size_t len = 0; + + len = strlen(prop) + 1 + strlen(val); + + buf_nulstr = GREEDY_REALLOC0(buf_nulstr, nulstr_len + len + 2); + if (!buf_nulstr) + return -ENOMEM; + + strscpyl(buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL); + nulstr_len += len + 1; + num++; + } + + /* build buf_strv from buf_nulstr */ + buf_strv = new0(char*, num + 1); + if (!buf_strv) + return -ENOMEM; + + size_t i = 0; + NULSTR_FOREACH(p, buf_nulstr) + buf_strv[i++] = p; + assert(i == num); + + free_and_replace(device->properties_nulstr, buf_nulstr); + device->properties_nulstr_len = nulstr_len; + free_and_replace(device->properties_strv, buf_strv); + + device->properties_buf_outdated = false; + return 0; +} + +int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len) { + int r; + + assert(device); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + if (ret_nulstr) + *ret_nulstr = device->properties_nulstr; + if (ret_len) + *ret_len = device->properties_nulstr_len; + + return 0; +} + +int device_get_properties_strv(sd_device *device, char ***ret) { + int r; + + assert(device); + + r = device_update_properties_bufs(device); + if (r < 0) + return r; + + if (ret) + *ret = device->properties_strv; + + return 0; +} + +int device_get_devlink_priority(sd_device *device, int *ret) { + int r; + + assert(device); + + r = device_read_db(device); + if (r < 0) + return r; + + if (ret) + *ret = device->devlink_priority; + + return 0; +} + +int device_clone_with_db(sd_device *device, sd_device **ret) { + _cleanup_(sd_device_unrefp) sd_device *dest = NULL; + const char *key, *val; + int r; + + assert(device); + assert(ret); + + /* The device may be already removed. Let's copy minimal set of information that was obtained through + * netlink socket. */ + + r = device_new_aux(&dest); + if (r < 0) + return r; + + /* Seal device to prevent reading the uevent file, as the device may have been already removed. */ + dest->sealed = true; + + /* Copy syspath, then also devname, sysname or sysnum can be obtained. */ + r = device_set_syspath(dest, device->syspath, false); + if (r < 0) + return r; + + /* Copy other information stored in database. Here, do not use FOREACH_DEVICE_PROPERTY() and + * sd_device_get_property_value(), as they calls device_properties_prepare() -> + * device_read_uevent_file(), but as commented in the above, the device may be already removed and + * reading uevent file may fail. */ + ORDERED_HASHMAP_FOREACH_KEY(val, key, device->properties) { + if (streq(key, "MINOR")) + continue; + + if (streq(key, "MAJOR")) { + const char *minor = NULL; + + minor = ordered_hashmap_get(device->properties, "MINOR"); + r = device_set_devnum(dest, val, minor); + } else + r = device_amend(dest, key, val); + if (r < 0) + return r; + + if (streq(key, "SUBSYSTEM") && streq(val, "drivers")) { + r = free_and_strdup(&dest->driver_subsystem, device->driver_subsystem); + if (r < 0) + return r; + } + } + + /* Finally, read the udev database. */ + r = device_read_db_internal(dest, /* force = */ true); + if (r < 0) + return r; + + *ret = TAKE_PTR(dest); + return 0; +} + +void device_cleanup_tags(sd_device *device) { + assert(device); + + device->all_tags = set_free_free(device->all_tags); + device->current_tags = set_free_free(device->current_tags); + device->property_tags_outdated = true; + device->tags_generation++; +} + +void device_cleanup_devlinks(sd_device *device) { + assert(device); + + set_free_free(device->devlinks); + device->devlinks = NULL; + device->property_devlinks_outdated = true; + device->devlinks_generation++; +} + +void device_remove_tag(sd_device *device, const char *tag) { + assert(device); + assert(tag); + + free(set_remove(device->current_tags, tag)); + device->property_tags_outdated = true; + device->tags_generation++; +} + +static int device_tag(sd_device *device, const char *tag, bool add) { + const char *id; + char *path; + int r; + + assert(device); + assert(tag); + + r = device_get_device_id(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/tags/", tag, "/", id); + + if (add) + return touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444); + + if (unlink(path) < 0 && errno != ENOENT) + return -errno; + + return 0; +} + +int device_tag_index(sd_device *device, sd_device *device_old, bool add) { + int r = 0, k; + + if (add && device_old) + /* delete possible left-over tags */ + FOREACH_DEVICE_TAG(device_old, tag) + if (!sd_device_has_tag(device, tag)) { + k = device_tag(device_old, tag, false); + if (r >= 0 && k < 0) + r = k; + } + + FOREACH_DEVICE_TAG(device, tag) { + k = device_tag(device, tag, add); + if (r >= 0 && k < 0) + r = k; + } + + return r; +} + +static bool device_has_info(sd_device *device) { + assert(device); + + if (!set_isempty(device->devlinks)) + return true; + + if (device->devlink_priority != 0) + return true; + + if (!ordered_hashmap_isempty(device->properties_db)) + return true; + + if (!set_isempty(device->all_tags)) + return true; + + if (!set_isempty(device->current_tags)) + return true; + + 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; +} +#endif /* NM_IGNORED */ + +static int device_get_db_path(sd_device *device, char **ret) { + const char *id; + char *path; + int r; + + assert(device); + assert(ret); + + r = device_get_device_id(device, &id); + if (r < 0) + return r; + + path = path_join("/run/udev/data/", id); + if (!path) + return -ENOMEM; + + *ret = path; + return 0; +} + +#if 0 /* NM_IGNORED */ +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 (!device_should_have_db(device)) + return device_delete_db(device); + + 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 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 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) + return log_device_debug_errno(device, errno, + "sd-device: Failed to chmod temporary database file '%s': %m", + path_tmp); + + if (device_has_info(device)) { + const char *property, *value, *ct; + + if (major(device->devnum) > 0) { + FOREACH_DEVICE_DEVLINK(device, devlink) + fprintf(f, "S:%s\n", devlink + STRLEN("/dev/")); + + if (device->devlink_priority != 0) + fprintf(f, "L:%i\n", device->devlink_priority); + } + + if (device->usec_initialized > 0) + fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized); + + ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db) + fprintf(f, "E:%s=%s\n", property, value); + + FOREACH_DEVICE_TAG(device, tag) + fprintf(f, "G:%s\n", tag); /* Any tag */ + + SET_FOREACH(ct, device->current_tags) + fprintf(f, "Q:%s\n", ct); /* Current tag */ + + /* Always write the latest database version here, instead of the value stored in + * device->database_version, as which may be 0. */ + fputs("V:" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\n", f); + } + + r = fflush_and_check(f); + if (r < 0) + 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) + return log_device_debug_errno(device, errno, + "sd-device: Failed to rename temporary database file '%s' to '%s': %m", + path_tmp, path); + + log_device_debug(device, "sd-device: Created database file '%s' for '%s'.", path, device->devpath); + + path_tmp = mfree(path_tmp); + path = mfree(path); + + return 0; +} + +int device_delete_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; + + if (unlink(path) < 0 && errno != ENOENT) + return -errno; + + return 0; +} +#endif /* NM_IGNORED */ + +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); +} + +#if 0 /* NM_IGNORED */ +static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = { + [SD_DEVICE_ADD] = "add", + [SD_DEVICE_REMOVE] = "remove", + [SD_DEVICE_CHANGE] = "change", + [SD_DEVICE_MOVE] = "move", + [SD_DEVICE_ONLINE] = "online", + [SD_DEVICE_OFFLINE] = "offline", + [SD_DEVICE_BIND] = "bind", + [SD_DEVICE_UNBIND] = "unbind", +}; + +DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t); + +void dump_device_action_table(void) { + DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX); +} +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h new file mode 100644 index 00000000..e1f3b6e8 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <dirent.h> +#include <inttypes.h> +#include <stdbool.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "sd-device.h" + +#include "macro.h" + +int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum); +int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len); +int device_new_from_strv(sd_device **ret, char **strv); + +int device_opendir(sd_device *device, const char *subdir, DIR **ret); + +int device_get_property_bool(sd_device *device, const char *key); +int device_get_property_int(sd_device *device, const char *key, int *ret); +int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value); +int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *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); +int device_get_devnode_mode(sd_device *device, mode_t *ret); +int device_get_devnode_uid(sd_device *device, uid_t *ret); +int device_get_devnode_gid(sd_device *device, gid_t *ret); + +void device_clear_sysattr_cache(sd_device *device); +int device_cache_sysattr_value(sd_device *device, const char *key, char *value); +int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value); + +void device_seal(sd_device *device); +void device_set_is_initialized(sd_device *device); +void device_set_db_persist(sd_device *device); +void device_set_devlink_priority(sd_device *device, int priority); +int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); +int device_add_devlink(sd_device *device, const char *devlink); +int device_remove_devlink(sd_device *device, const char *devlink); +bool device_has_devlink(sd_device *device, const char *devlink); +int device_add_property(sd_device *device, const char *property, const char *value); +int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4); +int device_add_tag(sd_device *device, const char *tag, bool both); +void device_remove_tag(sd_device *device, const char *tag); +void device_cleanup_tags(sd_device *device); +void device_cleanup_devlinks(sd_device *device); + +uint64_t device_get_properties_generation(sd_device *device); +uint64_t device_get_tags_generation(sd_device *device); +uint64_t device_get_devlinks_generation(sd_device *device); + +int device_properties_prepare(sd_device *device); +int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len); +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 */ +int device_read_db_internal(sd_device *device, bool force); +static inline int device_read_db(sd_device *device) { + return device_read_db_internal(device, false); +} + +int device_read_uevent_file(sd_device *device); + +int device_set_action(sd_device *device, sd_device_action_t a); +sd_device_action_t device_action_from_string(const char *s) _pure_; +const char *device_action_to_string(sd_device_action_t a) _const_; +void dump_device_action_table(void); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h index a1b5e91e..534a2967 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h @@ -7,39 +7,41 @@ #include "sd-device.h" +#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) -#define FOREACH_DEVICE_PROPERTY(device, key, value) \ - for (key = sd_device_get_property_first(device, &(value)); \ - key; \ - key = sd_device_get_property_next(device, &(value))) +#define FOREACH_DEVICE_PROPERTY(device, key, value) \ + for (const char *value, *key = sd_device_get_property_first(device, &value); \ + key; \ + key = sd_device_get_property_next(device, &value)) -#define FOREACH_DEVICE_TAG(device, tag) \ - for (tag = sd_device_get_tag_first(device); \ - tag; \ +#define FOREACH_DEVICE_TAG(device, tag) \ + for (const char *tag = sd_device_get_tag_first(device); \ + tag; \ tag = sd_device_get_tag_next(device)) -#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \ - for (tag = sd_device_get_current_tag_first(device); \ - tag; \ +#define FOREACH_DEVICE_CURRENT_TAG(device, tag) \ + for (const char *tag = sd_device_get_current_tag_first(device); \ + tag; \ tag = sd_device_get_current_tag_next(device)) -#define FOREACH_DEVICE_SYSATTR(device, attr) \ - for (attr = sd_device_get_sysattr_first(device); \ - attr; \ +#define FOREACH_DEVICE_SYSATTR(device, attr) \ + for (const char *attr = sd_device_get_sysattr_first(device); \ + attr; \ attr = sd_device_get_sysattr_next(device)) -#define FOREACH_DEVICE_DEVLINK(device, devlink) \ - for (devlink = sd_device_get_devlink_first(device); \ - devlink; \ +#define FOREACH_DEVICE_DEVLINK(device, devlink) \ + for (const char *devlink = sd_device_get_devlink_first(device); \ + devlink; \ devlink = sd_device_get_devlink_next(device)) #define _FOREACH_DEVICE_CHILD(device, child, suffix_ptr) \ - for (child = sd_device_get_child_first(device, suffix_ptr); \ + for (sd_device *child = sd_device_get_child_first(device, suffix_ptr); \ child; \ child = sd_device_get_child_next(device, suffix_ptr)) @@ -49,14 +51,14 @@ #define FOREACH_DEVICE_CHILD_WITH_SUFFIX(device, child, suffix) \ _FOREACH_DEVICE_CHILD(device, child, &suffix) -#define FOREACH_DEVICE(enumerator, device) \ - for (device = sd_device_enumerator_get_device_first(enumerator); \ - device; \ +#define FOREACH_DEVICE(enumerator, device) \ + for (sd_device *device = sd_device_enumerator_get_device_first(enumerator); \ + device; \ device = sd_device_enumerator_get_device_next(enumerator)) -#define FOREACH_SUBSYSTEM(enumerator, device) \ - for (device = sd_device_enumerator_get_subsystem_first(enumerator); \ - device; \ +#define FOREACH_SUBSYSTEM(enumerator, device) \ + for (sd_device *device = sd_device_enumerator_get_subsystem_first(enumerator); \ + device; \ device = sd_device_enumerator_get_subsystem_next(enumerator)) #define log_device_full_errno_zerook(device, level, error, ...) \ @@ -81,17 +83,17 @@ #define log_device_full(device, level, ...) (void) log_device_full_errno_zerook(device, level, 0, __VA_ARGS__) -#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__) -#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__) -#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__) +#define log_device_debug(device, ...) log_device_full(device, LOG_DEBUG, __VA_ARGS__) +#define log_device_info(device, ...) log_device_full(device, LOG_INFO, __VA_ARGS__) +#define log_device_notice(device, ...) log_device_full(device, LOG_NOTICE, __VA_ARGS__) #define log_device_warning(device, ...) log_device_full(device, LOG_WARNING, __VA_ARGS__) -#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__) +#define log_device_error(device, ...) log_device_full(device, LOG_ERR, __VA_ARGS__) -#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__) -#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__) -#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__) +#define log_device_debug_errno(device, error, ...) log_device_full_errno(device, LOG_DEBUG, error, __VA_ARGS__) +#define log_device_info_errno(device, error, ...) log_device_full_errno(device, LOG_INFO, error, __VA_ARGS__) +#define log_device_notice_errno(device, error, ...) log_device_full_errno(device, LOG_NOTICE, error, __VA_ARGS__) #define log_device_warning_errno(device, error, ...) log_device_full_errno(device, LOG_WARNING, error, __VA_ARGS__) -#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__) +#define log_device_error_errno(device, error, ...) log_device_full_errno(device, LOG_ERR, error, __VA_ARGS__) int devname_from_devnum(mode_t mode, dev_t devnum, char **ret); static inline int devname_from_stat_rdev(const struct stat *st, char **ret) { @@ -101,3 +103,13 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) { int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret); 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/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c new file mode 100644 index 00000000..0ca87146 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c @@ -0,0 +1,2724 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#include <ctype.h> +#include <net/if.h> +#include <sys/ioctl.h> +#include <sys/types.h> + +#include "sd-device.h" + +#include "alloc-util.h" +#include "chase.h" +#include "device-internal.h" +#include "device-private.h" +#include "device-util.h" +#include "devnum-util.h" +#include "dirent-util.h" +#include "env-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "format-util.h" +#include "fs-util.h" +#include "hashmap.h" +#if 0 /* NM_IGNORED */ +#include "id128-util.h" +#endif /* NM_IGNORED */ +#include "macro.h" +#include "missing_magic.h" +#if 0 /* NM_IGNORED */ +#include "netlink-util.h" +#endif /* NM_IGNORED */ +#include "parse-util.h" +#include "path-util.h" +#include "set.h" +#include "socket-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "strxcpyx.h" +#include "user-util.h" + +#if 0 /* NM_IGNORED */ +int device_new_aux(sd_device **ret) { + sd_device *device; + + assert(ret); + + device = new(sd_device, 1); + if (!device) + return -ENOMEM; + + *device = (sd_device) { + .n_ref = 1, + .devmode = MODE_INVALID, + .devuid = UID_INVALID, + .devgid = GID_INVALID, + .action = _SD_DEVICE_ACTION_INVALID, + }; + + *ret = device; + return 0; +} + +static sd_device *device_free(sd_device *device) { + assert(device); + + sd_device_unref(device->parent); + free(device->syspath); + free(device->sysname); + free(device->devtype); + free(device->devname); + free(device->subsystem); + free(device->driver_subsystem); + free(device->driver); + free(device->device_id); + free(device->properties_strv); + free(device->properties_nulstr); + + ordered_hashmap_free(device->properties); + ordered_hashmap_free(device->properties_db); + hashmap_free(device->sysattr_values); + set_free(device->sysattrs); + set_free(device->all_tags); + set_free(device->current_tags); + set_free(device->devlinks); + hashmap_free(device->children); + + return mfree(device); +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device, sd_device, device_free); +#endif /* NM_IGNORED */ + +int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db) { + OrderedHashmap **properties; + + assert(device); + assert(key); + + if (db) + properties = &device->properties_db; + else + properties = &device->properties; + + if (value) { + _unused_ _cleanup_free_ char *old_value = NULL; + _cleanup_free_ char *new_key = NULL, *new_value = NULL, *old_key = NULL; + int r; + + r = ordered_hashmap_ensure_allocated(properties, &string_hash_ops_free_free); + if (r < 0) + return r; + + new_key = strdup(key); + if (!new_key) + return -ENOMEM; + + new_value = strdup(value); + if (!new_value) + return -ENOMEM; + + old_value = ordered_hashmap_get2(*properties, key, (void**) &old_key); + + /* ordered_hashmap_replace() does not fail when the hashmap already has the entry. */ + r = ordered_hashmap_replace(*properties, new_key, new_value); + if (r < 0) + return r; + + TAKE_PTR(new_key); + TAKE_PTR(new_value); + } else { + _unused_ _cleanup_free_ char *old_value = NULL; + _cleanup_free_ char *old_key = NULL; + + old_value = ordered_hashmap_remove2(*properties, key, (void**) &old_key); + } + + if (!db) { + device->properties_generation++; + device->properties_buf_outdated = true; + } + + return 0; +} + +#if 0 /* NM_IGNORED */ +int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { + _cleanup_free_ char *syspath = NULL; + const char *devpath; + int r; + + assert(device); + assert(_syspath); + + if (verify) { + _cleanup_close_ int fd = -EBADF; + + /* The input path maybe a symlink located outside of /sys. Let's try to chase the symlink at first. + * The primary use case is that e.g. /proc/device-tree is a symlink to /sys/firmware/devicetree/base. + * By chasing symlinks in the path at first, we can call sd_device_new_from_path() with such path. */ + r = chase(_syspath, NULL, 0, &syspath, &fd); + if (r == -ENOENT) + /* the device does not exist (any more?) */ + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: Failed to chase symlinks in \"%s\".", _syspath); + if (r < 0) + return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath); + + if (!path_startswith(syspath, "/sys")) { + _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL; + char *p; + + /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */ + r = chase("/sys", NULL, 0, &real_sys, NULL); + if (r < 0) + return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m"); + + p = path_startswith(syspath, real_sys); + if (!p) + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'", + syspath, real_sys); + + new_syspath = path_join("/sys", p); + if (!new_syspath) + return log_oom_debug(); + + free_and_replace(syspath, new_syspath); + path_simplify(syspath); + } + + if (path_startswith(syspath, "/sys/devices/")) { + /* For proper devices, stricter rules apply: they must have a 'uevent' file, + * otherwise we won't allow them */ + + if (faccessat(fd, "uevent", F_OK, 0) < 0) { + if (errno == ENOENT) + /* This is not a valid device. Note, this condition is quite often + * satisfied when enumerating devices or finding a parent device. + * Hence, use log_trace_errno() here. */ + return log_trace_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the uevent file \"%s/uevent\" does not exist.", syspath); + if (errno == ENOTDIR) + /* Not actually a directory. */ + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the syspath \"%s\" is not a directory.", syspath); + + return log_debug_errno(errno, "sd-device: cannot find uevent file for %s: %m", syspath); + } + } else { + struct stat st; + + /* For everything else lax rules apply: they just need to be a directory */ + + if (fstat(fd, &st) < 0) + return log_debug_errno(errno, "sd-device: failed to check if syspath \"%s\" is a directory: %m", syspath); + if (!S_ISDIR(st.st_mode)) + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the syspath \"%s\" is not a directory.", syspath); + } + + /* 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 = 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) { + r = fd_is_fs_type(fd, SYSFS_MAGIC); + if (r < 0) + return log_debug_errno(r, "sd-device: failed to check if syspath \"%s\" is backed by sysfs.", syspath); + if (r == 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the syspath \"%s\" is outside of sysfs, refusing.", syspath); + } + } else { + /* must be a subdirectory of /sys */ + if (!path_startswith(_syspath, "/sys/")) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "sd-device: Syspath '%s' is not a subdirectory of /sys", + _syspath); + + r = path_simplify_alloc(_syspath, &syspath); + if (r < 0) + return r; + } + + assert_se(devpath = startswith(syspath, "/sys")); + if (devpath[0] != '/') + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path."); + + r = device_add_property_internal(device, "DEVPATH", devpath); + if (r < 0) + return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath); + + free_and_replace(device->syspath, syspath); + device->devpath = devpath; + + /* Unset sysname and sysnum, they will be assigned when requested. */ + device->sysnum = NULL; + device->sysname = mfree(device->sysname); + return 0; +} + +static int device_new_from_syspath(sd_device **ret, const char *syspath, bool strict) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(syspath, -EINVAL); + + if (strict && !path_startswith(syspath, "/sys/")) + return -EINVAL; + + r = device_new_aux(&device); + if (r < 0) + return r; + + r = device_set_syspath(device, syspath, /* verify= */ true); + if (r < 0) + return r; + + *ret = TAKE_PTR(device); + return 0; +} + +_public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) { + return device_new_from_syspath(ret, syspath, /* strict = */ true); +} + +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; + dev_t n; + int r; + + assert(ret); + + if (S_ISCHR(mode)) + t = "char"; + else if (S_ISBLK(mode)) + t = "block"; + else + return -ENOTTY; + + if (major(devnum) == 0) + return -ENODEV; + + if (asprintf(&syspath, "/sys/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0) + return -ENOMEM; + + r = sd_device_new_from_syspath(&dev, syspath); + if (r < 0) + return r; + + r = sd_device_get_devnum(dev, &n); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (n != devnum) + return -ENXIO; + + if (device_in_subsystem(dev, "block") != !!S_ISBLK(mode)) + return -ENXIO; + + *ret = TAKE_PTR(dev); + return 0; +} + +_public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) { + assert_return(ret, -EINVAL); + assert_return(IN_SET(type, 'b', 'c'), -EINVAL); + + return device_new_from_mode_and_devnum(ret, type == 'b' ? S_IFBLK : S_IFCHR, devnum); +} + +static int device_new_from_main_ifname(sd_device **ret, const char *ifname) { + const char *syspath; + + assert(ret); + assert(ifname); + + syspath = strjoina("/sys/class/net/", ifname); + return sd_device_new_from_syspath(ret, syspath); +} + +_public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) { + _cleanup_free_ char *main_name = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(ifname, -EINVAL); + + r = device_new_from_main_ifname(ret, ifname); + if (r >= 0) + return r; + + r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL); + if (r < 0) + return r; + + return device_new_from_main_ifname(ret, main_name); +} + +_public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + _cleanup_free_ char *ifname = NULL; + int r, i; + + assert_return(ret, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + + 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) + return r; + + r = sd_device_get_ifindex(dev, &i); + if (r == -ENOENT) + return -ENXIO; + if (r < 0) + return r; + if (i != ifindex) + return -ENXIO; + + *ret = TAKE_PTR(dev); + return 0; +} + +static int device_strjoin_new( + const char *a, + const char *b, + const char *c, + const char *d, + sd_device **ret) { + + const char *p; + int r; + + p = strjoina(a, b, c, d); + if (access(p, F_OK) < 0) + return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */ + + r = sd_device_new_from_syspath(ret, p); + if (r < 0) + return r; + + return 1; +} + +_public_ int sd_device_new_from_subsystem_sysname( + sd_device **ret, + const char *subsystem, + const char *sysname) { + + char *name; + int r; + + assert_return(ret, -EINVAL); + assert_return(subsystem, -EINVAL); + assert_return(sysname, -EINVAL); + + if (!path_is_normalized(subsystem)) + return -EINVAL; + if (!path_is_normalized(sysname)) + return -EINVAL; + + /* translate sysname back to sysfs filename */ + name = strdupa_safe(sysname); + string_replace_char(name, '/', '!'); + + if (streq(subsystem, "subsystem")) { + FOREACH_STRING(s, "/sys/bus/", "/sys/class/") { + r = device_strjoin_new(s, name, NULL, NULL, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + } + + } else if (streq(subsystem, "module")) { + r = device_strjoin_new("/sys/module/", name, NULL, NULL, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + + } else if (streq(subsystem, "drivers")) { + const char *sep; + + sep = strchr(name, ':'); + if (sep && sep[1] != '\0') { /* Require ":" and something non-empty after that. */ + + const char *subsys = memdupa_suffix0(name, sep - name); + sep++; + + if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */ + r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret); + else + r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + } + } + + r = device_strjoin_new("/sys/bus/", subsystem, "/devices/", name, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = device_strjoin_new("/sys/class/", subsystem, "/", name, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = device_strjoin_new("/sys/firmware/", subsystem, "/", name, ret); + if (r < 0) + return r; + if (r > 0) + return 0; + + return -ENODEV; +} + +_public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) { + assert_return(ret, -EINVAL); + assert_return(st, -EINVAL); + + return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev); +} + +_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) { + struct stat st; + dev_t devnum; + mode_t mode; + + assert_return(ret, -EINVAL); + assert_return(devname, -EINVAL); + + /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device + * nodes below /dev/. */ + + /* Also ignore when the specified path is "/dev". */ + if (isempty(path_startswith(devname, "/dev"))) + return -EINVAL; + + if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0) + /* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified. + * In that case, we can directly convert the path to syspath, hence it is not necessary + * that the specified path exists. So, this works fine without udevd being running. */ + return device_new_from_mode_and_devnum(ret, mode, devnum); + + if (stat(devname, &st) < 0) + return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno; + + return sd_device_new_from_stat_rdev(ret, &st); +} + +_public_ int sd_device_new_from_path(sd_device **ret, const char *path) { + assert_return(ret, -EINVAL); + assert_return(path, -EINVAL); + + if (path_startswith(path, "/dev")) + return sd_device_new_from_devname(ret, path); + + return device_new_from_syspath(ret, path, /* strict = */ false); +} +#endif /* NM_IGNORED */ + +int device_set_devtype(sd_device *device, const char *devtype) { + _cleanup_free_ char *t = NULL; + int r; + + assert(device); + assert(devtype); + + t = strdup(devtype); + if (!t) + return -ENOMEM; + + r = device_add_property_internal(device, "DEVTYPE", t); + if (r < 0) + return r; + + return free_and_replace(device->devtype, t); +} + +int device_set_ifindex(sd_device *device, const char *name) { + int r, ifindex; + + assert(device); + assert(name); + + ifindex = parse_ifindex(name); + if (ifindex < 0) + return ifindex; + + r = device_add_property_internal(device, "IFINDEX", name); + if (r < 0) + return r; + + device->ifindex = ifindex; + + return 0; +} + +static int mangle_devname(const char *p, char **ret) { + char *q; + + assert(p); + assert(ret); + + if (!path_is_safe(p)) + return -EINVAL; + + /* When the path is absolute, it must start with "/dev/", but ignore "/dev/" itself. */ + if (path_is_absolute(p)) { + if (isempty(path_startswith(p, "/dev/"))) + return -EINVAL; + + q = strdup(p); + } else + q = path_join("/dev/", p); + if (!q) + return -ENOMEM; + + path_simplify(q); + + *ret = q; + return 0; +} + +int device_set_devname(sd_device *device, const char *devname) { + _cleanup_free_ char *t = NULL; + int r; + + assert(device); + assert(devname); + + r = mangle_devname(devname, &t); + if (r < 0) + return r; + + r = device_add_property_internal(device, "DEVNAME", t); + if (r < 0) + return r; + + return free_and_replace(device->devname, t); +} + +int device_set_devmode(sd_device *device, const char *_devmode) { + unsigned devmode; + int r; + + assert(device); + assert(_devmode); + + r = safe_atou(_devmode, &devmode); + if (r < 0) + return r; + + if (devmode > 07777) + return -EINVAL; + + r = device_add_property_internal(device, "DEVMODE", _devmode); + if (r < 0) + return r; + + device->devmode = devmode; + + return 0; +} + +int device_set_devnum(sd_device *device, const char *major, const char *minor) { + unsigned maj, min = 0; + int r; + + assert(device); + assert(major); + + r = safe_atou(major, &maj); + if (r < 0) + return r; + if (maj == 0) + return 0; + if (!DEVICE_MAJOR_VALID(maj)) + return -EINVAL; + + if (minor) { + r = safe_atou(minor, &min); + if (r < 0) + return r; + if (!DEVICE_MINOR_VALID(min)) + return -EINVAL; + } + + r = device_add_property_internal(device, "MAJOR", major); + if (r < 0) + return r; + + if (minor) { + r = device_add_property_internal(device, "MINOR", minor); + if (r < 0) + return r; + } + + device->devnum = makedev(maj, min); + + return 0; +} + +int device_set_diskseq(sd_device *device, const char *str) { + uint64_t diskseq; + int r; + + assert(device); + assert(str); + + r = safe_atou64(str, &diskseq); + if (r < 0) + return r; + if (diskseq == 0) + return -EINVAL; + + r = device_add_property_internal(device, "DISKSEQ", str); + if (r < 0) + return r; + + device->diskseq = diskseq; + + return 0; +} + +static int handle_uevent_line( + sd_device *device, + const char *key, + const char *value, + const char **major, + const char **minor) { + + assert(device); + assert(key); + assert(value); + assert(major); + assert(minor); + + if (streq(key, "DEVTYPE")) + return device_set_devtype(device, value); + if (streq(key, "IFINDEX")) + return device_set_ifindex(device, value); + if (streq(key, "DEVNAME")) + return device_set_devname(device, value); + if (streq(key, "DEVMODE")) + return device_set_devmode(device, value); + if (streq(key, "DISKSEQ")) + return device_set_diskseq(device, value); + if (streq(key, "MAJOR")) + *major = value; + else if (streq(key, "MINOR")) + *minor = value; + else + return device_add_property_internal(device, key, value); + + return 0; +} + +int device_read_uevent_file(sd_device *device) { + _cleanup_free_ char *uevent = NULL; + const char *syspath, *key = NULL, *value = NULL, *major = NULL, *minor = NULL; + char *path; + size_t uevent_len = 0; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + + if (device->uevent_loaded || device->sealed) + return 0; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + device->uevent_loaded = true; + + path = strjoina(syspath, "/uevent"); + + r = read_full_virtual_file(path, &uevent, &uevent_len); + if (r == -EACCES || ERRNO_IS_NEG_DEVICE_ABSENT(r)) + /* The uevent files may be write-only, the device may be already removed, or the device + * may not have the uevent file. */ + return 0; + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path); + + for (size_t i = 0; i < uevent_len; i++) + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, uevent[i])) { + key = &uevent[i]; + + state = KEY; + } + + break; + case KEY: + if (uevent[i] == '=') { + uevent[i] = '\0'; + + state = PRE_VALUE; + } else if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key); + + state = PRE_KEY; + } + + break; + case PRE_VALUE: + value = &uevent[i]; + state = VALUE; + + _fallthrough_; /* to handle empty property */ + case VALUE: + if (strchr(NEWLINE, uevent[i])) { + uevent[i] = '\0'; + + r = handle_uevent_line(device, key, value, &major, &minor); + if (r < 0) + log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value); + + state = PRE_KEY; + } + + break; + default: + assert_not_reached(); + } + + if (major) { + r = device_set_devnum(device, major, minor); + if (r < 0) + log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, strna(minor), path); + } + + return 0; +} + +_public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (device->ifindex <= 0) + return -ENOENT; + + if (ifindex) + *ifindex = device->ifindex; + + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { + int r; + + assert_return(ret, -EINVAL); + assert_return(id, -EINVAL); + + switch (id[0]) { + case 'b': + case 'c': { + dev_t devt; + + if (isempty(id)) + return -EINVAL; + + r = parse_devnum(id + 1, &devt); + if (r < 0) + return r; + + return sd_device_new_from_devnum(ret, id[0], devt); + } + + case 'n': { + int ifindex; + + ifindex = parse_ifindex(id + 1); + if (ifindex < 0) + return ifindex; + + return sd_device_new_from_ifindex(ret, ifindex); + } + + case '+': { + const char *subsys, *sep; + + sep = strchr(id + 1, ':'); + if (!sep || sep - id - 1 > NAME_MAX) + return -EINVAL; + + subsys = memdupa_suffix0(id + 1, sep - id - 1); + + return sd_device_new_from_subsystem_sysname(ret, subsys, sep + 1); + } + + default: + return -EINVAL; + } +} +#endif /* NM_IGNORED */ + +_public_ int sd_device_get_syspath(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + + assert(path_startswith(device->syspath, "/sys/")); + + if (ret) + *ret = device->syspath; + + return 0; +} + +#if 0 /* NM_IGNORED */ +DEFINE_PRIVATE_HASH_OPS_FULL( + device_by_path_hash_ops, + char, path_hash_func, path_compare, free, + sd_device, sd_device_unref); + +static int device_enumerate_children_internal(sd_device *device, const char *subdir, Set **stack, Hashmap **children) { + _cleanup_closedir_ DIR *dir = NULL; + int r; + + assert(device); + assert(stack); + assert(children); + + r = device_opendir(device, subdir, &dir); + if (r < 0) + return r; + + FOREACH_DIRENT_ALL(de, dir, return -errno) { + _cleanup_(sd_device_unrefp) sd_device *child = NULL; + _cleanup_free_ char *p = NULL; + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_DIR)) + continue; + + if (subdir) + p = path_join(subdir, de->d_name); + else + p = strdup(de->d_name); + if (!p) + return -ENOMEM; + + /* Try to create child device. */ + r = sd_device_new_child(&child, device, p); + if (r >= 0) { + /* OK, this is a child device, saving it. */ + r = hashmap_ensure_put(children, &device_by_path_hash_ops, p, child); + if (r < 0) + return r; + + TAKE_PTR(p); + TAKE_PTR(child); + } else if (r == -ENODEV) { + /* This is not a child device. Push the sub-directory into stack, and read it later. */ + + if (de->d_type == DT_LNK) + /* Do not follow symlinks, otherwise, we will enter an infinite loop, e.g., + * /sys/class/block/nvme0n1/subsystem/nvme0n1/subsystem/nvme0n1/subsystem/… */ + continue; + + r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p)); + if (r < 0) + return r; + } else + return r; + } + + return 0; +} + +static int device_enumerate_children(sd_device *device) { + _cleanup_hashmap_free_ Hashmap *children = NULL; + _cleanup_set_free_ Set *stack = NULL; + int r; + + assert(device); + + if (device->children_enumerated) + return 0; /* Already enumerated. */ + + r = device_enumerate_children_internal(device, NULL, &stack, &children); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *subdir = NULL; + + subdir = set_steal_first(stack); + if (!subdir) + break; + + r = device_enumerate_children_internal(device, subdir, &stack, &children); + if (r < 0) + return r; + } + + device->children_enumerated = true; + device->children = TAKE_PTR(children); + return 1; /* Enumerated. */ +} + +_public_ sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix) { + int r; + + assert(device); + + r = device_enumerate_children(device); + if (r < 0) { + log_device_debug_errno(device, r, "sd-device: failed to enumerate child devices: %m"); + if (ret_suffix) + *ret_suffix = NULL; + return NULL; + } + + device->children_iterator = ITERATOR_FIRST; + + return sd_device_get_child_next(device, ret_suffix); +} + +_public_ sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix) { + sd_device *child; + + assert(device); + + (void) hashmap_iterate(device->children, &device->children_iterator, (void**) &child, (const void**) ret_suffix); + return child; +} + +_public_ int sd_device_new_child(sd_device **ret, sd_device *device, const char *suffix) { + _cleanup_free_ char *path = NULL; + sd_device *child; + const char *s; + int r; + + assert_return(ret, -EINVAL); + assert_return(device, -EINVAL); + assert_return(suffix, -EINVAL); + + if (!path_is_safe(suffix)) + return -EINVAL; + + /* If we have already enumerated children, try to find the child from the cache. */ + child = hashmap_get(device->children, suffix); + if (child) { + *ret = sd_device_ref(child); + return 0; + } + + r = sd_device_get_syspath(device, &s); + if (r < 0) + return r; + + path = path_join(s, suffix); + if (!path) + return -ENOMEM; + + return sd_device_new_from_syspath(ret, path); +} + +static int device_new_from_child(sd_device **ret, sd_device *child) { + _cleanup_free_ char *path = NULL; + const char *syspath; + int r; + + assert(ret); + assert(child); + + r = sd_device_get_syspath(child, &syspath); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *p = NULL; + + r = path_extract_directory(path ?: syspath, &p); + if (r < 0) + return r; + + if (path_equal(p, "/sys")) + return -ENODEV; + + r = sd_device_new_from_syspath(ret, p); + if (r != -ENODEV) + return r; + + free_and_replace(path, p); + } +} + +_public_ int sd_device_get_parent(sd_device *child, sd_device **ret) { + int r; + + assert_return(child, -EINVAL); + + if (!child->parent_set) { + r = device_new_from_child(&child->parent, child); + if (r < 0 && r != -ENODEV) + return r; + + child->parent_set = true; + } + + if (!child->parent) + return -ENOENT; + + if (ret) + *ret = child->parent; + return 0; +} +#endif /* NM_IGNORED */ + +int device_set_subsystem(sd_device *device, const char *subsystem) { + _cleanup_free_ char *s = NULL; + int r; + + assert(device); + + if (subsystem) { + s = strdup(subsystem); + if (!s) + return -ENOMEM; + } + + r = device_add_property_internal(device, "SUBSYSTEM", s); + if (r < 0) + return r; + + device->subsystem_set = true; + return free_and_replace(device->subsystem, s); +} + +int device_set_drivers_subsystem(sd_device *device) { + _cleanup_free_ char *subsystem = NULL; + const char *devpath, *drivers, *p; + int r; + + assert(device); + + r = sd_device_get_devpath(device, &devpath); + if (r < 0) + return r; + + drivers = strstr(devpath, "/drivers/"); + if (!drivers) + drivers = endswith(devpath, "/drivers"); + if (!drivers) + return -EINVAL; + + /* Find the path component immediately before the "/drivers/" string */ + r = path_find_last_component(devpath, /* accept_dot_dot= */ false, &drivers, &p); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + subsystem = strndup(p, r); + if (!subsystem) + return -ENOMEM; + + r = device_set_subsystem(device, "drivers"); + if (r < 0) + return r; + + return free_and_replace(device->driver_subsystem, subsystem); +} + +_public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + + if (!device->subsystem_set) { + _cleanup_free_ char *subsystem = NULL; + const char *syspath; + char *path; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + /* read 'subsystem' link */ + path = strjoina(syspath, "/subsystem"); + r = readlink_value(path, &subsystem); + if (r < 0 && r != -ENOENT) + return log_device_debug_errno(device, r, + "sd-device: Failed to read subsystem for %s: %m", + device->devpath); + + if (subsystem) + r = device_set_subsystem(device, subsystem); + /* use implicit names */ + else if (!isempty(path_startswith(device->devpath, "/module/"))) + r = device_set_subsystem(device, "module"); + else if (strstr(syspath, "/drivers/") || endswith(syspath, "/drivers")) + r = device_set_drivers_subsystem(device); + else if (!isempty(PATH_STARTSWITH_SET(device->devpath, "/class/", "/bus/"))) + r = device_set_subsystem(device, "subsystem"); + else { + device->subsystem_set = true; + r = 0; + } + if (r < 0) + return log_device_debug_errno(device, r, + "sd-device: Failed to set subsystem for %s: %m", + device->devpath); + } + + if (!device->subsystem) + return -ENOENT; + + if (ret) + *ret = device->subsystem; + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_get_devtype(sd_device *device, const char **devtype) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (!device->devtype) + return -ENOENT; + + if (devtype) + *devtype = device->devtype; + + return !!device->devtype; +} + +_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(device, -EINVAL); + assert_return(subsystem, -EINVAL); + + for (;;) { + r = sd_device_get_parent(device, &device); + if (r < 0) + return r; + + if (!device_in_subsystem(device, subsystem)) + continue; + + if (devtype && !device_is_devtype(device, devtype)) + continue; + + if (ret) + *ret = device; + return 0; + } +} +#endif /* NM_IGNORED */ + +_public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (major(device->devnum) <= 0) + return -ENOENT; + + if (devnum) + *devnum = device->devnum; + + return 0; +} + +int device_set_driver(sd_device *device, const char *driver) { + _cleanup_free_ char *d = NULL; + int r; + + assert(device); + + if (driver) { + d = strdup(driver); + if (!d) + return -ENOMEM; + } + + r = device_add_property_internal(device, "DRIVER", d); + if (r < 0) + return r; + + device->driver_set = true; + return free_and_replace(device->driver, d); +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_get_driver(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + + if (!device->driver_set) { + _cleanup_free_ char *driver = NULL; + const char *syspath; + char *path; + int r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = strjoina(syspath, "/driver"); + r = readlink_value(path, &driver); + if (r < 0 && r != -ENOENT) + return log_device_debug_errno(device, r, + "sd-device: readlink(\"%s\") failed: %m", path); + + r = device_set_driver(device, driver); + if (r < 0) + return log_device_debug_errno(device, r, + "sd-device: Failed to set driver \"%s\": %m", driver); + } + + if (!device->driver) + return -ENOENT; + + if (ret) + *ret = device->driver; + return 0; +} +#endif /* NM_IGNORED */ + +_public_ int sd_device_get_devpath(sd_device *device, const char **ret) { + assert_return(device, -EINVAL); + + assert(device->devpath); + assert(device->devpath[0] == '/'); + + if (ret) + *ret = device->devpath; + + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_get_devname(sd_device *device, const char **devname) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (!device->devname) + return -ENOENT; + + assert(!isempty(path_startswith(device->devname, "/dev/"))); + + if (devname) + *devname = device->devname; + return 0; +} +#endif /* NM_IGNORED */ + +static int device_set_sysname_and_sysnum(sd_device *device) { + _cleanup_free_ char *sysname = NULL; + size_t len, n; + int r; + + assert(device); + + r = path_extract_filename(device->devpath, &sysname); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return -EINVAL; + + /* some devices have '!' in their name, change that to '/' */ + string_replace_char(sysname, '!', '/'); + + n = strspn_from_end(sysname, DIGITS); + len = strlen(sysname); + assert(n <= len); + if (n == len) + n = 0; /* Do not set sysnum for number only sysname. */ + + device->sysnum = n > 0 ? sysname + len - n : NULL; + return free_and_replace(device->sysname, sysname); +} + +_public_ int sd_device_get_sysname(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + + if (!device->sysname) { + r = device_set_sysname_and_sysnum(device); + if (r < 0) + return r; + } + + if (ret) + *ret = device->sysname; + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_get_sysnum(sd_device *device, const char **ret) { + int r; + + assert_return(device, -EINVAL); + + if (!device->sysname) { + r = device_set_sysname_and_sysnum(device); + if (r < 0) + return r; + } + + if (!device->sysnum) + return -ENOENT; + + if (ret) + *ret = device->sysnum; + return 0; +} + +_public_ int sd_device_get_action(sd_device *device, sd_device_action_t *ret) { + assert_return(device, -EINVAL); + + if (device->action < 0) + return -ENOENT; + + if (ret) + *ret = device->action; + + return 0; +} + +_public_ int sd_device_get_seqnum(sd_device *device, uint64_t *ret) { + assert_return(device, -EINVAL); + + if (device->seqnum == 0) + return -ENOENT; + + if (ret) + *ret = device->seqnum; + + return 0; +} + +_public_ int sd_device_get_diskseq(sd_device *device, uint64_t *ret) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + if (device->diskseq == 0) + return -ENOENT; + + if (ret) + *ret = device->diskseq; + + return 0; +} +#endif /* NM_IGNORED */ + +static bool is_valid_tag(const char *tag) { + assert(tag); + + return in_charset(tag, ALPHANUMERICAL "-_") && filename_is_valid(tag); +} + +int device_add_tag(sd_device *device, const char *tag, bool both) { + int r, added; + + assert(device); + assert(tag); + + if (!is_valid_tag(tag)) + return -EINVAL; + + /* Definitely add to the "all" list of tags (i.e. the sticky list) */ + added = set_put_strdup(&device->all_tags, tag); + if (added < 0) + return added; + + /* And optionally, also add it to the current list of tags */ + if (both) { + r = set_put_strdup(&device->current_tags, tag); + if (r < 0) { + if (added > 0) + (void) set_remove(device->all_tags, tag); + + return r; + } + } + + device->tags_generation++; + device->property_tags_outdated = true; + + return 0; +} + +int device_add_devlink(sd_device *device, const char *devlink) { + char *p; + int r; + + assert(device); + assert(devlink); + + r = mangle_devname(devlink, &p); + if (r < 0) + return r; + + r = set_ensure_consume(&device->devlinks, &path_hash_ops_free, p); + if (r < 0) + return r; + + device->devlinks_generation++; + device->property_devlinks_outdated = true; + + return r; /* return 1 when newly added, 0 when already exists */ +} + +#if 0 /* NM_IGNORED */ +int device_remove_devlink(sd_device *device, const char *devlink) { + _cleanup_free_ char *p = NULL, *s = NULL; + int r; + + assert(device); + assert(devlink); + + r = mangle_devname(devlink, &p); + if (r < 0) + return r; + + s = set_remove(device->devlinks, p); + if (!s) + return 0; /* does not exist */ + + device->devlinks_generation++; + device->property_devlinks_outdated = true; + return 1; /* removed */ +} + +bool device_has_devlink(sd_device *device, const char *devlink) { + assert(device); + assert(devlink); + + return set_contains(device->devlinks, devlink); +} +#endif /* NM_IGNORED */ + +static int device_add_property_internal_from_string(sd_device *device, const char *str) { + _cleanup_free_ char *key = NULL; + char *value; + int r; + + assert(device); + assert(str); + + key = strdup(str); + if (!key) + return -ENOMEM; + + value = strchr(key, '='); + if (!value) + return -EINVAL; + + *value = '\0'; + + if (isempty(++value)) + value = NULL; + + /* Add the property to both sd_device::properties and sd_device::properties_db, + * as this is called by only handle_db_line(). */ + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + return device_add_property_aux(device, key, value, true); +} + +int device_set_usec_initialized(sd_device *device, usec_t when) { + char s[DECIMAL_STR_MAX(usec_t)]; + int r; + + assert(device); + + xsprintf(s, USEC_FMT, when); + + r = device_add_property_internal(device, "USEC_INITIALIZED", s); + if (r < 0) + return r; + + device->usec_initialized = when; + return 0; +} + +static int handle_db_line(sd_device *device, char key, const char *value) { + int r; + + assert(device); + assert(value); + + switch (key) { + case 'G': /* Any tag */ + case 'Q': /* Current tag */ + return device_add_tag(device, value, key == 'Q'); + + case 'S': { + const char *path; + + path = strjoina("/dev/", value); + return device_add_devlink(device, path); + } + case 'E': + return device_add_property_internal_from_string(device, value); + + case 'I': { + usec_t t; + + r = safe_atou64(value, &t); + if (r < 0) + return r; + + return device_set_usec_initialized(device, t); + } + case 'L': + return safe_atoi(value, &device->devlink_priority); + + case 'W': + /* Deprecated. Previously, watch handle is both saved in database and /run/udev/watch. + * However, the handle saved in database may not be updated when the handle is updated + * or removed. Moreover, it is not necessary to store the handle within the database, + * as its value becomes meaningless when udevd is restarted. */ + return 0; + + case 'V': + return safe_atou(value, &device->database_version); + + default: + log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key); + return 0; + } +} + +int device_get_device_id(sd_device *device, const char **ret) { + assert(device); + assert(ret); + + if (!device->device_id) { + _cleanup_free_ char *id = NULL; + const char *subsystem; + dev_t devnum; + int ifindex, r; + + r = sd_device_get_subsystem(device, &subsystem); + if (r < 0) + return r; + + if (sd_device_get_devnum(device, &devnum) >= 0) { + /* use dev_t — b259:131072, c254:0 */ + if (asprintf(&id, "%c" DEVNUM_FORMAT_STR, + streq(subsystem, "block") ? 'b' : 'c', + DEVNUM_FORMAT_VAL(devnum)) < 0) + return -ENOMEM; + } else if (sd_device_get_ifindex(device, &ifindex) >= 0) { + /* use netdev ifindex — n3 */ + if (asprintf(&id, "n%u", (unsigned) ifindex) < 0) + return -ENOMEM; + } else { + _cleanup_free_ char *sysname = NULL; + + /* use $subsys:$sysname — pci:0000:00:1f.2 + * sd_device_get_sysname() has '!' translated, get it from devpath */ + r = path_extract_filename(device->devpath, &sysname); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return -EINVAL; + + if (streq(subsystem, "drivers")) { + /* the 'drivers' pseudo-subsystem is special, and needs the real + * subsystem encoded as well */ + assert(device->driver_subsystem); + id = strjoin("+drivers:", device->driver_subsystem, ":", sysname); + } else + id = strjoin("+", subsystem, ":", sysname); + if (!id) + return -ENOMEM; + } + + if (!filename_is_valid(id)) + return -EINVAL; + + device->device_id = TAKE_PTR(id); + } + + *ret = device->device_id; + return 0; +} + +int device_read_db_internal_filename(sd_device *device, const char *filename) { + _cleanup_free_ char *db = NULL; + const char *value = NULL; + size_t db_len = 0; + char key = '\0'; /* Unnecessary initialization to appease gcc-12.0.0-0.4.fc36 */ + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + INVALID_LINE, + } state = PRE_KEY; + + assert(device); + assert(filename); + + r = read_full_file(filename, &db, &db_len); + if (r < 0) { + if (r == -ENOENT) + return 0; + + return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", filename); + } + + /* devices with a database entry are initialized */ + device->is_initialized = true; + + device->db_loaded = true; + + for (size_t i = 0; i < db_len; i++) + switch (state) { + case PRE_KEY: + if (!strchr(NEWLINE, db[i])) { + key = db[i]; + + state = KEY; + } + + break; + case KEY: + if (db[i] != ':') { + log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key); + + state = INVALID_LINE; + } else { + db[i] = '\0'; + + state = PRE_VALUE; + } + + break; + case PRE_VALUE: + value = &db[i]; + + state = VALUE; + + break; + case INVALID_LINE: + if (strchr(NEWLINE, db[i])) + state = PRE_KEY; + + break; + case VALUE: + if (strchr(NEWLINE, db[i])) { + db[i] = '\0'; + r = handle_db_line(device, key, value); + if (r < 0) + log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", + key, value); + + state = PRE_KEY; + } + + break; + default: + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax."); + } + + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ int sd_device_get_is_initialized(sd_device *device) { + int r; + + assert_return(device, -EINVAL); + + r = device_read_db(device); + if (r == -ENOENT) + /* The device may be already removed or renamed. */ + return false; + if (r < 0) + return r; + + return device->is_initialized; +} + +_public_ int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret) { + int r; + + assert_return(device, -EINVAL); + + r = sd_device_get_is_initialized(device); + if (r < 0) + return r; + if (r == 0) + return -EBUSY; + + if (device->usec_initialized == 0) + return -ENODATA; + + if (ret) + *ret = device->usec_initialized; + + return 0; +} + +_public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret) { + usec_t now_ts, ts; + int r; + + assert_return(device, -EINVAL); + + r = sd_device_get_usec_initialized(device, &ts); + if (r < 0) + return r; + + now_ts = now(CLOCK_MONOTONIC); + + if (now_ts < ts) + return -EIO; + + if (ret) + *ret = usec_sub_unsigned(now_ts, ts); + + return 0; +} + +_public_ const char *sd_device_get_tag_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->all_tags_iterator_generation = device->tags_generation; + device->all_tags_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_tag_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->all_tags_iterator_generation != device->tags_generation) + return NULL; + + (void) set_iterate(device->all_tags, &device->all_tags_iterator, &v); + return v; +} + +static bool device_database_supports_current_tags(sd_device *device) { + assert(device); + + (void) device_read_db(device); + + /* The current tags (saved in Q field) feature is implemented in database version 1. + * If the database version is 0, then the tags (NOT current tags, saved in G field) are not + * sticky. Thus, we can safely bypass the operations for the current tags (Q) to tags (G). */ + + return device->database_version >= 1; +} + +_public_ const char *sd_device_get_current_tag_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + if (!device_database_supports_current_tags(device)) + return sd_device_get_tag_first(device); + + (void) device_read_db(device); + + device->current_tags_iterator_generation = device->tags_generation; + device->current_tags_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_current_tag_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + if (!device_database_supports_current_tags(device)) + return sd_device_get_tag_next(device); + + (void) device_read_db(device); + + if (device->current_tags_iterator_generation != device->tags_generation) + return NULL; + + (void) set_iterate(device->current_tags, &device->current_tags_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_first(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + device->devlinks_iterator_generation = device->devlinks_generation; + device->devlinks_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_devlink_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + (void) device_read_db(device); + + if (device->devlinks_iterator_generation != device->devlinks_generation) + return NULL; + + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); + return v; +} +#endif /* NM_IGNORED */ + +int device_properties_prepare(sd_device *device) { + int r; + + assert(device); + + r = device_read_uevent_file(device); + if (r < 0) + return r; + + r = device_read_db(device); + if (r < 0) + return r; + + if (device->property_devlinks_outdated) { + _cleanup_free_ char *devlinks = NULL; + + r = set_strjoin(device->devlinks, " ", false, &devlinks); + if (r < 0) + return r; + + if (!isempty(devlinks)) { + r = device_add_property_internal(device, "DEVLINKS", devlinks); + if (r < 0) + return r; + } + + device->property_devlinks_outdated = false; + } + + if (device->property_tags_outdated) { + _cleanup_free_ char *tags = NULL; + + r = set_strjoin(device->all_tags, ":", true, &tags); + if (r < 0) + return r; + + if (!isempty(tags)) { + r = device_add_property_internal(device, "TAGS", tags); + if (r < 0) + return r; + } + + tags = mfree(tags); + r = set_strjoin(device->current_tags, ":", true, &tags); + if (r < 0) + return r; + + if (!isempty(tags)) { + r = device_add_property_internal(device, "CURRENT_TAGS", tags); + if (r < 0) + return r; + } + + device->property_tags_outdated = false; + } + + return 0; +} + +#if 0 /* NM_IGNORED */ +_public_ const char *sd_device_get_property_first(sd_device *device, const char **_value) { + const char *key; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + device->properties_iterator_generation = device->properties_generation; + device->properties_iterator = ITERATOR_FIRST; + + (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key); + return key; +} + +_public_ const char *sd_device_get_property_next(sd_device *device, const char **_value) { + const char *key; + int r; + + assert_return(device, NULL); + + r = device_properties_prepare(device); + if (r < 0) + return NULL; + + if (device->properties_iterator_generation != device->properties_generation) + return NULL; + + (void) ordered_hashmap_iterate(device->properties, &device->properties_iterator, (void**)_value, (const void**)&key); + return key; +} + +static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir, Set **stack) { + _cleanup_closedir_ DIR *dir = NULL; + int r; + + assert(device); + assert(stack); + + r = device_opendir(device, subdir, &dir); + if (r == -ENOENT && subdir) + return 0; /* Maybe, this is a child device, and is already removed. */ + if (r < 0) + return r; + + if (subdir) { + if (faccessat(dirfd(dir), "uevent", F_OK, 0) >= 0) + return 0; /* this is a child device, skipping */ + if (errno != ENOENT) { + log_device_debug_errno(device, errno, + "sd-device: Failed to access %s/uevent, ignoring sub-directory %s: %m", + subdir, subdir); + return 0; + } + } + + FOREACH_DIRENT_ALL(de, dir, return -errno) { + _cleanup_free_ char *p = NULL; + struct stat statbuf; + + if (dot_or_dot_dot(de->d_name)) + continue; + + /* only handle symlinks, regular files, and directories */ + if (!IN_SET(de->d_type, DT_LNK, DT_REG, DT_DIR)) + continue; + + if (subdir) { + p = path_join(subdir, de->d_name); + if (!p) + return -ENOMEM; + } + + if (de->d_type == DT_DIR) { + /* push the sub-directory into the stack, and read it later. */ + if (p) + r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p)); + else + r = set_put_strdup_full(stack, &path_hash_ops_free, de->d_name); + if (r < 0) + return r; + + continue; + } + + if (fstatat(dirfd(dir), de->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) < 0) + continue; + + if ((statbuf.st_mode & (S_IRUSR | S_IWUSR)) == 0) + continue; + + if (p) + r = set_ensure_consume(&device->sysattrs, &path_hash_ops_free, TAKE_PTR(p)); + else + r = set_put_strdup_full(&device->sysattrs, &path_hash_ops_free, de->d_name); + if (r < 0) + return r; + } + + return 0; +} + +static int device_sysattrs_read_all(sd_device *device) { + _cleanup_set_free_ Set *stack = NULL; + int r; + + assert(device); + + if (device->sysattrs_read) + return 0; + + r = device_sysattrs_read_all_internal(device, NULL, &stack); + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *subdir = NULL; + + subdir = set_steal_first(stack); + if (!subdir) + break; + + r = device_sysattrs_read_all_internal(device, subdir, &stack); + if (r < 0) + return r; + } + + device->sysattrs_read = true; + + return 0; +} + +_public_ const char *sd_device_get_sysattr_first(sd_device *device) { + void *v; + int r; + + assert_return(device, NULL); + + if (!device->sysattrs_read) { + r = device_sysattrs_read_all(device); + if (r < 0) { + errno = -r; + return NULL; + } + } + + device->sysattrs_iterator = ITERATOR_FIRST; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ const char *sd_device_get_sysattr_next(sd_device *device) { + void *v; + + assert_return(device, NULL); + + if (!device->sysattrs_read) + return NULL; + + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + return v; +} + +_public_ int sd_device_has_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + (void) device_read_db(device); + + return set_contains(device->all_tags, tag); +} + +_public_ int sd_device_has_current_tag(sd_device *device, const char *tag) { + assert_return(device, -EINVAL); + assert_return(tag, -EINVAL); + + if (!device_database_supports_current_tags(device)) + return sd_device_has_tag(device, tag); + + (void) device_read_db(device); + + return set_contains(device->current_tags, tag); +} +#endif /* NM_IGNORED */ + +_public_ int sd_device_get_property_value(sd_device *device, const char *key, const char **ret_value) { + const char *value; + int r; + + assert_return(device, -EINVAL); + assert_return(key, -EINVAL); + + r = device_properties_prepare(device); + if (r < 0) + return r; + + value = ordered_hashmap_get(device->properties, key); + if (!value) + return -ENOENT; + + if (ret_value) + *ret_value = value; + return 0; +} + +#if 0 /* NM_IGNORED */ +int device_get_property_bool(sd_device *device, const char *key) { + const char *value; + int r; + + assert(device); + assert(key); + + r = sd_device_get_property_value(device, key, &value); + if (r < 0) + return r; + + return parse_boolean(value); +} + +int device_get_property_int(sd_device *device, const char *key, int *ret) { + const char *value; + int r, v; + + assert(device); + assert(key); + + r = sd_device_get_property_value(device, key, &value); + if (r < 0) + return r; + + r = safe_atoi(value, &v); + if (r < 0) + return r; + + if (ret) + *ret = v; + return 0; +} + +_public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) { + const char *s; + sd_id128_t id; + int r; + + assert_return(device, -EINVAL); + + /* Retrieves the UUID attached to a uevent when triggering it from userspace via + * sd_device_trigger_with_uuid() or an equivalent interface. Returns -ENOENT if the record is not + * caused by a synthetic event and -ENODATA if it was but no UUID was specified */ + + r = sd_device_get_property_value(device, "SYNTH_UUID", &s); + if (r < 0) + return r; + + if (streq(s, "0")) /* SYNTH_UUID=0 is set whenever a device is triggered by userspace without specifying a UUID */ + return -ENODATA; + + r = sd_id128_from_string(s, &id); + if (r < 0) + return r; + + if (ret) + *ret = id; + + return 0; +} + +void device_clear_sysattr_cache(sd_device *device) { + device->sysattr_values = hashmap_free(device->sysattr_values); +} + +int device_cache_sysattr_value(sd_device *device, const char *key, char *value) { + _unused_ _cleanup_free_ char *old_value = NULL; + _cleanup_free_ char *new_key = NULL; + int r; + + assert(device); + assert(key); + + /* This takes the reference of the input value. The input value may be NULL. + * This replaces the value if it already exists. */ + + /* First, remove the old cache entry. So, we do not need to clear cache on error. */ + old_value = hashmap_remove2(device->sysattr_values, key, (void **) &new_key); + if (!new_key) { + new_key = strdup(key); + if (!new_key) + return -ENOMEM; + } + + r = hashmap_ensure_put(&device->sysattr_values, &path_hash_ops_free_free, new_key, value); + if (r < 0) + return r; + + TAKE_PTR(new_key); + + return 0; +} + +int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) { + const char *k = NULL, *value; + + assert(device); + assert(key); + + value = hashmap_get2(device->sysattr_values, key, (void **) &k); + if (!k) + return -ESTALE; /* We have not read the attribute. */ + if (!value) + return -ENOENT; /* We have looked up the attribute before and it did not exist. */ + if (ret_value) + *ret_value = value; + return 0; +} + +/* We cache all sysattr lookups. If an attribute does not exist, it is stored + * with a NULL value in the cache, otherwise the returned string is stored */ +_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) { + _cleanup_free_ char *value = NULL, *path = NULL; + const char *syspath; + struct stat statbuf; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + /* look for possibly already cached result */ + r = device_get_cached_sysattr_value(device, sysattr, ret_value); + if (r != -ESTALE) + return r; + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = path_join(syspath, sysattr); + if (!path) + return -ENOMEM; + + if (lstat(path, &statbuf) < 0) { + int k; + + r = -errno; + + /* remember that we could not access the sysattr */ + k = device_cache_sysattr_value(device, sysattr, NULL); + if (k < 0) + log_device_debug_errno(device, k, + "sd-device: failed to cache attribute '%s' with NULL, ignoring: %m", + sysattr); + + return r; + } else if (S_ISLNK(statbuf.st_mode)) { + /* Some core links return only the last element of the target path, + * these are just values, the paths should not be exposed. */ + if (STR_IN_SET(sysattr, "driver", "subsystem", "module")) { + r = readlink_value(path, &value); + if (r < 0) + return r; + } else + return -EINVAL; + } else if (S_ISDIR(statbuf.st_mode)) + /* skip directories */ + return -EISDIR; + else if (!(statbuf.st_mode & S_IRUSR)) + /* skip non-readable files */ + return -EPERM; + else { + size_t size; + + /* Read attribute value, Some attributes contain embedded '\0'. So, it is necessary to + * also get the size of the result. See issue #20025. */ + r = read_full_virtual_file(path, &value, &size); + if (r < 0) + return r; + + /* drop trailing newlines */ + while (size > 0 && strchr(NEWLINE, value[--size])) + value[size] = '\0'; + } + + /* Unfortunately, we need to return 'const char*' instead of 'char*'. Hence, failure in caching + * sysattr value is critical unlike the other places. */ + r = device_cache_sysattr_value(device, sysattr, value); + if (r < 0) { + log_device_debug_errno(device, r, + "sd-device: failed to cache attribute '%s' with '%s'%s: %m", + sysattr, value, ret_value ? "" : ", ignoring"); + if (ret_value) + return r; + + return 0; + } + + if (ret_value) + *ret_value = value; + + TAKE_PTR(value); + return 0; +} + +int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value) { + const char *value; + int r; + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return r; + + int v; + r = safe_atoi(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_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) { + const char *value; + int r; + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return r; + + unsigned v; + r = safe_atou(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_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; + + assert(device); + assert(sysattr); + + r = sd_device_get_sysattr_value(device, sysattr, &value); + if (r < 0) + return r; + + return parse_boolean(value); +} + +static void device_remove_cached_sysattr_value(sd_device *device, const char *_key) { + _cleanup_free_ char *key = NULL; + + assert(device); + assert(_key); + + free(hashmap_remove2(device->sysattr_values, _key, (void **) &key)); +} + +_public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) { + _cleanup_free_ char *value = NULL, *path = NULL; + const char *syspath; + size_t len; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + /* Set the attribute and save it in the cache. */ + + if (!_value) { + /* If input value is NULL, then clear cache and not write anything. */ + device_remove_cached_sysattr_value(device, sysattr); + return 0; + } + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + path = path_join(syspath, sysattr); + if (!path) + return -ENOMEM; + + len = strlen(_value); + + /* drop trailing newlines */ + while (len > 0 && strchr(NEWLINE, _value[len - 1])) + len--; + + /* value length is limited to 4k */ + if (len > 4096) + return -EINVAL; + + value = strndup(_value, len); + if (!value) + return -ENOMEM; + + r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW); + if (r < 0) { + /* On failure, clear cache entry, as we do not know how it fails. */ + device_remove_cached_sysattr_value(device, sysattr); + return r; + } + + /* Do not cache action string written into uevent file. */ + if (streq(sysattr, "uevent")) + return 0; + + r = device_cache_sysattr_value(device, sysattr, value); + if (r < 0) + log_device_debug_errno(device, r, + "sd-device: failed to cache attribute '%s' with '%s', ignoring: %m", + sysattr, value); + else + TAKE_PTR(value); + + return 0; +} + +_public_ int sd_device_set_sysattr_valuef(sd_device *device, const char *sysattr, const char *format, ...) { + _cleanup_free_ char *value = NULL; + va_list ap; + int r; + + assert_return(device, -EINVAL); + assert_return(sysattr, -EINVAL); + + if (!format) { + device_remove_cached_sysattr_value(device, sysattr); + return 0; + } + + va_start(ap, format); + r = vasprintf(&value, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return sd_device_set_sysattr_value(device, sysattr, value); +} + +_public_ int sd_device_trigger(sd_device *device, sd_device_action_t action) { + const char *s; + + assert_return(device, -EINVAL); + + s = device_action_to_string(action); + if (!s) + return -EINVAL; + + /* This uses the simple no-UUID interface of kernel < 4.13 */ + return sd_device_set_sysattr_value(device, "uevent", s); +} + +_public_ int sd_device_trigger_with_uuid( + sd_device *device, + sd_device_action_t action, + sd_id128_t *ret_uuid) { + + const char *s, *j; + sd_id128_t u; + int r; + + assert_return(device, -EINVAL); + + /* If no one wants to know the UUID, use the simple interface from pre-4.13 times */ + if (!ret_uuid) + return sd_device_trigger(device, action); + + s = device_action_to_string(action); + if (!s) + return -EINVAL; + + r = sd_id128_randomize(&u); + if (r < 0) + return r; + + j = strjoina(s, " ", SD_ID128_TO_UUID_STRING(u)); + + r = sd_device_set_sysattr_value(device, "uevent", j); + if (r < 0) + return r; + + *ret_uuid = u; + return 0; +} + +_public_ int sd_device_open(sd_device *device, int flags) { + _cleanup_close_ int fd = -EBADF, fd2 = -EBADF; + const char *devname; + uint64_t q, diskseq = 0; + struct stat st; + dev_t devnum; + int r; + + assert_return(device, -EINVAL); + assert_return(FLAGS_SET(flags, O_PATH) || !FLAGS_SET(flags, O_NOFOLLOW), -EINVAL); + + r = sd_device_get_devname(device, &devname); + if (r == -ENOENT) + return -ENOEXEC; + if (r < 0) + return r; + + r = sd_device_get_devnum(device, &devnum); + if (r == -ENOENT) + return -ENOEXEC; + if (r < 0) + return r; + + fd = open(devname, FLAGS_SET(flags, O_PATH) ? flags : O_CLOEXEC|O_NOFOLLOW|O_PATH); + if (fd < 0) + return -errno; + + if (fstat(fd, &st) < 0) + return -errno; + + if (st.st_rdev != devnum) + return -ENXIO; + + 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. */ + if (FLAGS_SET(flags, O_PATH)) + return TAKE_FD(fd); + + /* If the device is not initialized, then we cannot determine if we should check diskseq through + * ID_IGNORE_DISKSEQ property. Let's skip to check diskseq in that case. */ + r = sd_device_get_is_initialized(device); + if (r < 0) + return r; + if (r > 0) { + r = device_get_property_bool(device, "ID_IGNORE_DISKSEQ"); + if (r < 0 && r != -ENOENT) + return r; + if (r <= 0) { + r = sd_device_get_diskseq(device, &diskseq); + if (r < 0 && r != -ENOENT) + return r; + } + } + + fd2 = fd_reopen(fd, flags); + if (fd2 < 0) + return fd2; + + if (diskseq == 0) + return TAKE_FD(fd2); + + r = fd_get_diskseq(fd2, &q); + if (r < 0) + return r; + + if (q != diskseq) + return -ENXIO; + + return TAKE_FD(fd2); +} + +int device_opendir(sd_device *device, const char *subdir, DIR **ret) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *path = NULL; + const char *syspath; + int r; + + assert(device); + assert(ret); + + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; + + if (subdir) { + if (!path_is_safe(subdir)) + return -EINVAL; + + path = path_join(syspath, subdir); + if (!path) + return -ENOMEM; + } + + d = opendir(path ?: syspath); + if (!d) + return -errno; + + *ret = TAKE_PTR(d); + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c index 663296f4..ef0c2d2a 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c @@ -99,16 +99,21 @@ int event_reset_time_relative( const char *description, bool force_reset) { - usec_t usec_now; int r; assert(e); - r = sd_event_now(e, clock, &usec_now); - if (r < 0) - return log_debug_errno(r, "sd-event: Failed to get the current time: %m"); + if (usec > 0) { + usec_t usec_now; + + r = sd_event_now(e, clock, &usec_now); + if (r < 0) + return log_debug_errno(r, "sd-event: Failed to get the current time: %m"); - return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset); + usec = usec_add(usec_now, usec); + } + + return event_reset_time(e, s, clock, usec, accuracy, callback, userdata, priority, description, force_reset); } #if 0 /* NM_IGNORED */ @@ -149,4 +154,21 @@ 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); +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h index c1855844..ad0f2e78 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h +++ b/src/libnm-systemd-core/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); + +#if 0 /* NM_IGNORED */ +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); +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index 5c7ba182..6449b85c 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -1169,10 +1169,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--; } } @@ -1579,7 +1579,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( @@ -1983,7 +1983,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; } @@ -2239,8 +2239,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); @@ -4008,7 +4008,7 @@ static int process_inotify(sd_event *e) { if (r < 0) return r; if (r > 0) - done ++; + done++; } return done; @@ -4620,7 +4620,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t /* Set timestamp only when this is called first time. */ if (threshold == INT64_MAX) - triple_timestamp_get(&e->timestamp); + triple_timestamp_now(&e->timestamp); for (size_t i = 0; i < m; i++) { @@ -4923,13 +4923,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; } @@ -5047,7 +5047,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/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index c20f7325..58173055 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -11,9 +11,29 @@ #include "hexdecoct.h" #include "id128-util.h" #include "io-util.h" +#include "sha256.h" #include "stdio-util.h" #include "string-util.h" +#include "strv.h" #include "sync-util.h" +#include "virt.h" + +int id128_from_string_nonzero(const char *s, sd_id128_t *ret) { + sd_id128_t t; + int r; + + assert(ret); + + r = sd_id128_from_string(ASSERT_PTR(s), &t); + if (r < 0) + return r; + + if (sd_id128_is_null(t)) + return -ENXIO; + + *ret = t; + return 0; +} #if 0 /* NM_IGNORED */ bool id128_is_valid(const char *s) { @@ -24,7 +44,7 @@ bool id128_is_valid(const char *s) { l = strlen(s); if (l == SD_ID128_STRING_MAX - 1) - /* Plain formatted 128bit hex string */ + /* Plain formatted 128-bit hex string */ return in_charset(s, HEXDIGITS); if (l == SD_ID128_UUID_STRING_MAX - 1) { @@ -53,7 +73,7 @@ int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret) { assert(fd >= 0); - /* Reads an 128bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both + /* Reads an 128-bit ID from a file, which may either be in plain format (32 hex digits), or in UUID format, both * optionally followed by a newline and nothing else. ID files should really be newline terminated, but if they * aren't that's OK too, following the rule of "Be conservative in what you send, be liberal in what you * accept". @@ -123,7 +143,7 @@ int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret) { assert(dir_fd >= 0 || dir_fd == AT_FDCWD); assert(path); - fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY, /* xopen_flags = */ 0, /* mode = */ 0); + fd = xopenat(dir_fd, path, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return fd; @@ -151,7 +171,7 @@ int id128_write_fd(int fd, Id128Flag f, sd_id128_t id) { } buffer[sz - 1] = '\n'; - r = loop_write(fd, buffer, sz, false); + r = loop_write(fd, buffer, sz); if (r < 0) return r; @@ -170,7 +190,7 @@ int id128_write_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t id) { assert(dir_fd >= 0 || dir_fd == AT_FDCWD); assert(path); - fd = xopenat(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444); + fd = xopenat_full(dir_fd, path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_TRUNC, /* xopen_flags = */ 0, 0444); if (fd < 0) return fd; @@ -178,11 +198,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) { @@ -210,9 +230,22 @@ int id128_get_product(sd_id128_t *ret) { /* Reads the systems product UUID from DMI or devicetree (where it is located on POWER). This is * particularly relevant in VM environments, where VM managers typically place a VM uuid there. */ - 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); + r = detect_container(); + if (r < 0) + return r; + if (r > 0) /* Refuse returning this in containers, as this is not a property of our system then, but + * of the host */ + return -ENOENT; + + 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; @@ -222,4 +255,22 @@ int id128_get_product(sd_id128_t *ret) { *ret = uuid; return 0; } + +sd_id128_t id128_digest(const void *data, size_t size) { + assert(data || size == 0); + + /* Hashes a UUID from some arbitrary data */ + + if (size == SIZE_MAX) + size = strlen(data); + + uint8_t h[SHA256_DIGEST_SIZE]; + sd_id128_t id; + + /* Take the first half of the SHA256 result */ + assert_cc(sizeof(h) >= sizeof(id.bytes)); + memcpy(id.bytes, sha256_direct(data, size, h), sizeof(id.bytes)); + + return id128_make_v4_uuid(id); +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index 7bcbd8e5..53ba50a8 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -6,6 +6,7 @@ #include "sd-id128.h" +#include "errno-util.h" #include "hash-funcs.h" #include "macro.h" @@ -20,6 +21,8 @@ typedef enum Id128Flag { ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */ } Id128Flag; +int id128_from_string_nonzero(const char *s, sd_id128_t *ret); + int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret); int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret); static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) { @@ -44,9 +47,12 @@ sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); +sd_id128_t id128_digest(const void *data, size_t size); + /* A helper to check for the three relevant cases of "machine ID not initialized" */ -#define ERRNO_IS_MACHINE_ID_UNSET(r) \ - IN_SET(abs(r), \ - ENOENT, \ - ENOMEDIUM, \ - ENOPKG) +#define ERRNO_IS_NEG_MACHINE_ID_UNSET(r) \ + IN_SET(r, \ + -ENOENT, \ + -ENOMEDIUM, \ + -ENOPKG) +_DEFINE_ABS_WRAPPER(MACHINE_ID_UNSET); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c index c63e1a97..ff0db776 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c @@ -345,18 +345,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { return 0; } -static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { - uint8_t hmac[SHA256_DIGEST_SIZE]; - sd_id128_t result; +_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) { + assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */ + union { + uint8_t hmac[SHA256_DIGEST_SIZE]; + sd_id128_t result; + } buf; - assert(ret); + assert_return(ret, -EINVAL); + assert_return(!sd_id128_is_null(app_id), -ENXIO); - hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac); + hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac); /* Take only the first half. */ - memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result))); - - *ret = id128_make_v4_uuid(result); + *ret = id128_make_v4_uuid(buf.result); return 0; } @@ -370,7 +372,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re if (r < 0) return r; - return get_app_specific(id, app_id, ret); + return sd_id128_get_app_specific(id, app_id, ret); } _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) { @@ -383,6 +385,6 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) if (r < 0) return r; - return get_app_specific(id, app_id, ret); + return sd_id128_get_app_specific(id, app_id, ret); } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h index 6f657c22..d4381d90 100644 --- a/src/libnm-systemd-core/src/systemd/_sd-common.h +++ b/src/libnm-systemd-core/src/systemd/_sd-common.h @@ -99,7 +99,7 @@ typedef void (*_sd_destroy_t)(void *userdata); } \ struct _sd_useless_struct_to_allow_trailing_semicolon_ -/* The following macro should be used in all public enums, to force 64bit wideness on them, so that we can +/* The following macro should be used in all public enums, to force 64-bit wideness on them, so that we can * freely extend them later on, without breaking compatibility. */ #define _SD_ENUM_FORCE_S64(id) \ _SD_##id##_INT64_MIN = INT64_MIN, \ diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h index e3d647f7..b67ec0f3 100644 --- a/src/libnm-systemd-core/src/systemd/sd-device.h +++ b/src/libnm-systemd-core/src/systemd/sd-device.h @@ -129,6 +129,7 @@ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator *enumera int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match); int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match); int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value); +int sd_device_enumerator_add_match_property_required(sd_device_enumerator *enumerator, const char *property, const char *value); int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname); int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, const char *sysname); int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h new file mode 100644 index 00000000..555b40e9 --- /dev/null +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-duid.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddhcpduidhfoo +#define foosddhcpduidhfoo + +/*** + systemd 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. + + systemd 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 systemd; If not, see <https://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <sys/types.h> + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DUID_TYPE_LLT = 1, + SD_DUID_TYPE_EN = 2, + SD_DUID_TYPE_LL = 3, + SD_DUID_TYPE_UUID = 4 +}; + +typedef struct sd_dhcp_duid sd_dhcp_duid; + +int sd_dhcp_duid_clear(sd_dhcp_duid *duid); + +int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid); + +int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size); +int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size); + +int sd_dhcp_duid_set( + sd_dhcp_duid *duid, + uint16_t duid_type, + const void *data, + size_t data_size); +int sd_dhcp_duid_set_raw( + sd_dhcp_duid *duid, + const void *data, + size_t data_size); +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); +int sd_dhcp_duid_set_ll( + sd_dhcp_duid *duid, + const void *hw_addr, + size_t hw_addr_size, + uint16_t arp_type); +int sd_dhcp_duid_set_en(sd_dhcp_duid *duid); +int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid); + +int sd_dhcp_duid_to_string(const sd_dhcp_duid *duid, char **ret); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h index a9fa7856..d551b4dd 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -24,6 +24,7 @@ #include <sys/types.h> #include "sd-device.h" +#include "sd-dhcp-duid.h" #include "sd-dhcp6-lease.h" #include "sd-dhcp6-option.h" #include "sd-event.h" @@ -40,154 +41,6 @@ enum { SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST = 13 }; -/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */ -enum { - SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */ - SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */ - SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */ - SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */ - SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */ - SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */ - SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */ - SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */ - SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */ - /* option code 10 is unassigned */ - SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */ - SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */ - SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */ - SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */ - SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */ - SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */ - SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */ - SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */ - SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */ - SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */ - SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */ - SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */ - SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */ - SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */ - SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */ - SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */ - SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */ - SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */ - SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */ - SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */ - SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */ - SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */ - SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */ - SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */ - /* option code 35 is unassigned */ - SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */ - SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */ - SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */ - SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */ - SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */ - SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */ - SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */ - SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */ - SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */ - SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */ - SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */ - SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */ - SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */ - SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */ - SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */ - SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */ - SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */ - SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */ - SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */ - SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */ - SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ - SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */ - SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */ - SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */ - SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */ - SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */ - SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */ - SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */ - SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */ - SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */ - SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */ - SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */ - SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */ - SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */ - SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */ - SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */ - SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */ - SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */ - SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */ - SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */ - SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */ - SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */ - SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */ - SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */ - SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */ - SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */ - SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */ - SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */ - SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */ - SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */ - SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */ - SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */ - SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */ - SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */ - SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */ - SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */ - SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */ - SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */ - SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */ - SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */ - SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */ - SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */ - SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */ - SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */ - SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */ - SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */ - SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */ - SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */ - SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */ - SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */ - SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */ - SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */ - SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */ - SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */ - SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */ - SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */ - SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */ - SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */ - SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */ - SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */ - SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */ - SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */ - SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */ - SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */ - SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */ - SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */ - SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */ - SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */ - SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */ - SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */ - SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */ - SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */ - SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */ - SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */ - SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */ - SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */ - SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */ - SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */ - SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */ - SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */ - SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */ - SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */ - SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */ - /* option codes 144-65535 are unassigned */ -}; - typedef struct sd_dhcp6_client sd_dhcp6_client; typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata); @@ -211,23 +64,20 @@ int sd_dhcp6_client_set_mac( const uint8_t *addr, size_t addr_len, uint16_t arp_type); -int sd_dhcp6_client_set_duid( - sd_dhcp6_client *client, - uint16_t duid_type, - const void *duid, - size_t duid_len); -int sd_dhcp6_client_set_duid_llt( - sd_dhcp6_client *client, - uint64_t llt_time); +int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time); +int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client); +int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client); +int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client); +int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, const uint8_t *duid, size_t duid_len); +int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, const sd_dhcp_duid *duid); +int sd_dhcp6_client_get_duid(sd_dhcp6_client *client, const sd_dhcp_duid **ret); +int sd_dhcp6_client_get_duid_as_string(sd_dhcp6_client *client, char **ret); int sd_dhcp6_client_set_iaid( sd_dhcp6_client *client, uint32_t iaid); int sd_dhcp6_client_get_iaid( sd_dhcp6_client *client, uint32_t *iaid); -int sd_dhcp6_client_duid_as_string( - sd_dhcp6_client *client, - char **duid); int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h index 716f6fc1..e18d5781 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h @@ -23,6 +23,8 @@ #include <netinet/in.h> #include <sys/types.h> +#include "sd-dhcp6-option.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; @@ -30,24 +32,54 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp6_lease sd_dhcp6_lease; int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); +int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret); +int sd_dhcp6_lease_get_t1_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); +int sd_dhcp6_lease_get_t2(sd_dhcp6_lease *lease, uint64_t *ret); +int sd_dhcp6_lease_get_t2_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); +int sd_dhcp6_lease_get_valid_lifetime(sd_dhcp6_lease *lease, uint64_t *ret); +int sd_dhcp6_lease_get_valid_lifetime_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret); -void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease); -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, - struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid); -void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease); -int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, - uint8_t *prefix_len, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid); +int sd_dhcp6_lease_address_iterator_reset(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_address_iterator_next(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_address( + sd_dhcp6_lease *lease, + struct in6_addr *ret); +int sd_dhcp6_lease_get_address_lifetime( + sd_dhcp6_lease *lease, + uint64_t *ret_lifetime_preferred, + uint64_t *ret_lifetime_valid); +int sd_dhcp6_lease_get_address_lifetime_timestamp( + sd_dhcp6_lease *lease, + clockid_t clock, + uint64_t *ret_lifetime_preferred, + uint64_t *ret_lifetime_valid); +int sd_dhcp6_lease_has_address(sd_dhcp6_lease *lease); + +int sd_dhcp6_lease_pd_iterator_reset(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_pd_iterator_next(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_pd_prefix( + sd_dhcp6_lease *lease, + struct in6_addr *ret_prefix, + uint8_t *ret_prefix_length); +int sd_dhcp6_lease_get_pd_lifetime( + sd_dhcp6_lease *lease, + uint64_t *ret_lifetime_preferred, + uint64_t *ret_lifetime_valid); +int sd_dhcp6_lease_get_pd_lifetime_timestamp( + sd_dhcp6_lease *lease, + clockid_t clock, + uint64_t *ret_lifetime_preferred, + uint64_t *ret_lifetime_valid); +int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret); int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret); int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret); +int sd_dhcp6_lease_get_captive_portal(sd_dhcp6_lease *lease, const char **ret); +int sd_dhcp6_lease_get_vendor_options(sd_dhcp6_lease *lease, sd_dhcp6_option ***ret); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h index b4b4671e..32012426 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-option.h @@ -20,6 +20,8 @@ #include <inttypes.h> #include <sys/types.h> +#include "sd-dhcp6-protocol.h" + #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h new file mode 100644 index 00000000..78c80f7c --- /dev/null +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddhcp6protocolhfoo +#define foosddhcp6protocolhfoo + +/*** + systemd 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. + + systemd 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 systemd; If not, see <https://www.gnu.org/licenses/>. +***/ + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */ +enum { + SD_DHCP6_OPTION_CLIENTID = 1, /* RFC 8415 */ + SD_DHCP6_OPTION_SERVERID = 2, /* RFC 8415 */ + SD_DHCP6_OPTION_IA_NA = 3, /* RFC 8415 */ + SD_DHCP6_OPTION_IA_TA = 4, /* RFC 8415 */ + SD_DHCP6_OPTION_IAADDR = 5, /* RFC 8415 */ + SD_DHCP6_OPTION_ORO = 6, /* RFC 8415 */ + SD_DHCP6_OPTION_PREFERENCE = 7, /* RFC 8415 */ + SD_DHCP6_OPTION_ELAPSED_TIME = 8, /* RFC 8415 */ + SD_DHCP6_OPTION_RELAY_MSG = 9, /* RFC 8415 */ + /* option code 10 is unassigned */ + SD_DHCP6_OPTION_AUTH = 11, /* RFC 8415 */ + SD_DHCP6_OPTION_UNICAST = 12, /* RFC 8415 */ + SD_DHCP6_OPTION_STATUS_CODE = 13, /* RFC 8415 */ + SD_DHCP6_OPTION_RAPID_COMMIT = 14, /* RFC 8415 */ + SD_DHCP6_OPTION_USER_CLASS = 15, /* RFC 8415 */ + SD_DHCP6_OPTION_VENDOR_CLASS = 16, /* RFC 8415 */ + SD_DHCP6_OPTION_VENDOR_OPTS = 17, /* RFC 8415 */ + SD_DHCP6_OPTION_INTERFACE_ID = 18, /* RFC 8415 */ + SD_DHCP6_OPTION_RECONF_MSG = 19, /* RFC 8415 */ + SD_DHCP6_OPTION_RECONF_ACCEPT = 20, /* RFC 8415 */ + SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME = 21, /* RFC 3319 */ + SD_DHCP6_OPTION_SIP_SERVER_ADDRESS = 22, /* RFC 3319 */ + SD_DHCP6_OPTION_DNS_SERVER = 23, /* RFC 3646 */ + SD_DHCP6_OPTION_DOMAIN = 24, /* RFC 3646 */ + SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, RFC 8415 */ + SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, RFC 8415 */ + SD_DHCP6_OPTION_NIS_SERVER = 27, /* RFC 3898 */ + SD_DHCP6_OPTION_NISP_SERVER = 28, /* RFC 3898 */ + SD_DHCP6_OPTION_NIS_DOMAIN_NAME = 29, /* RFC 3898 */ + SD_DHCP6_OPTION_NISP_DOMAIN_NAME = 30, /* RFC 3898 */ + SD_DHCP6_OPTION_SNTP_SERVER = 31, /* RFC 4075, deprecated */ + SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME = 32, /* RFC 4242, 8415, sec. 21.23 */ + SD_DHCP6_OPTION_BCMCS_SERVER_D = 33, /* RFC 4280 */ + SD_DHCP6_OPTION_BCMCS_SERVER_A = 34, /* RFC 4280 */ + /* option code 35 is unassigned */ + SD_DHCP6_OPTION_GEOCONF_CIVIC = 36, /* RFC 4776 */ + SD_DHCP6_OPTION_REMOTE_ID = 37, /* RFC 4649 */ + SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */ + SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */ + SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */ + SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */ + SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */ + SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */ + SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */ + SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */ + SD_DHCP6_OPTION_CLT_TIME = 46, /* RFC 5007 */ + SD_DHCP6_OPTION_LQ_RELAY_DATA = 47, /* RFC 5007 */ + SD_DHCP6_OPTION_LQ_CLIENT_LINK = 48, /* RFC 5007 */ + SD_DHCP6_OPTION_MIP6_HNIDF = 49, /* RFC 6610 */ + SD_DHCP6_OPTION_MIP6_VDINF = 50, /* RFC 6610 */ + SD_DHCP6_OPTION_V6_LOST = 51, /* RFC 5223 */ + SD_DHCP6_OPTION_CAPWAP_AC_V6 = 52, /* RFC 5417 */ + SD_DHCP6_OPTION_RELAY_ID = 53, /* RFC 5460 */ + SD_DHCP6_OPTION_IPV6_ADDRESS_MOS = 54, /* RFC 5678 */ + SD_DHCP6_OPTION_IPV6_FQDN_MOS = 55, /* RFC 5678 */ + SD_DHCP6_OPTION_NTP_SERVER = 56, /* RFC 5908 */ + SD_DHCP6_OPTION_V6_ACCESS_DOMAIN = 57, /* RFC 5986 */ + SD_DHCP6_OPTION_SIP_UA_CS_LIST = 58, /* RFC 6011 */ + SD_DHCP6_OPTION_BOOTFILE_URL = 59, /* RFC 5970 */ + SD_DHCP6_OPTION_BOOTFILE_PARAM = 60, /* RFC 5970 */ + SD_DHCP6_OPTION_CLIENT_ARCH_TYPE = 61, /* RFC 5970 */ + SD_DHCP6_OPTION_NII = 62, /* RFC 5970 */ + SD_DHCP6_OPTION_GEOLOCATION = 63, /* RFC 6225 */ + SD_DHCP6_OPTION_AFTR_NAME = 64, /* RFC 6334 */ + SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME = 65, /* RFC 6440 */ + SD_DHCP6_OPTION_RSOO = 66, /* RFC 6422 */ + SD_DHCP6_OPTION_PD_EXCLUDE = 67, /* RFC 6603 */ + SD_DHCP6_OPTION_VSS = 68, /* RFC 6607 */ + SD_DHCP6_OPTION_MIP6_IDINF = 69, /* RFC 6610 */ + SD_DHCP6_OPTION_MIP6_UDINF = 70, /* RFC 6610 */ + SD_DHCP6_OPTION_MIP6_HNP = 71, /* RFC 6610 */ + SD_DHCP6_OPTION_MIP6_HAA = 72, /* RFC 6610 */ + SD_DHCP6_OPTION_MIP6_HAF = 73, /* RFC 6610 */ + SD_DHCP6_OPTION_RDNSS_SELECTION = 74, /* RFC 6731 */ + SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME = 75, /* RFC 6784 */ + SD_DHCP6_OPTION_KRB_REALM_NAME = 76, /* RFC 6784 */ + SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME = 77, /* RFC 6784 */ + SD_DHCP6_OPTION_KRB_KDC = 78, /* RFC 6784 */ + SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR = 79, /* RFC 6939 */ + SD_DHCP6_OPTION_LINK_ADDRESS = 80, /* RFC 6977 */ + SD_DHCP6_OPTION_RADIUS = 81, /* RFC 7037 */ + SD_DHCP6_OPTION_SOL_MAX_RT = 82, /* RFC 7083, RFC 8415 */ + SD_DHCP6_OPTION_INF_MAX_RT = 83, /* RFC 7083, RFC 8415 */ + SD_DHCP6_OPTION_ADDRSEL = 84, /* RFC 7078 */ + SD_DHCP6_OPTION_ADDRSEL_TABLE = 85, /* RFC 7078 */ + SD_DHCP6_OPTION_V6_PCP_SERVER = 86, /* RFC 7291 */ + SD_DHCP6_OPTION_DHCPV4_MSG = 87, /* RFC 7341 */ + SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER = 88, /* RFC 7341 */ + SD_DHCP6_OPTION_S46_RULE = 89, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_BR = 90, /* RFC 7598, RFC 8539 */ + SD_DHCP6_OPTION_S46_DMR = 91, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_V4V6BIND = 92, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_PORTPARAMS = 93, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_CONT_MAPE = 94, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_CONT_MAPT = 95, /* RFC 7598 */ + SD_DHCP6_OPTION_S46_CONT_LW = 96, /* RFC 7598 */ + SD_DHCP6_OPTION_4RD = 97, /* RFC 7600 */ + SD_DHCP6_OPTION_4RD_MAP_RULE = 98, /* RFC 7600 */ + SD_DHCP6_OPTION_4RD_NON_MAP_RULE = 99, /* RFC 7600 */ + SD_DHCP6_OPTION_LQ_BASE_TIME = 100, /* RFC 7653 */ + SD_DHCP6_OPTION_LQ_START_TIME = 101, /* RFC 7653 */ + SD_DHCP6_OPTION_LQ_END_TIME = 102, /* RFC 7653 */ + SD_DHCP6_OPTION_CAPTIVE_PORTAL = 103, /* RFC 8910 */ + SD_DHCP6_OPTION_MPL_PARAMETERS = 104, /* RFC 7774 */ + SD_DHCP6_OPTION_ANI_ATT = 105, /* RFC 7839 */ + SD_DHCP6_OPTION_ANI_NETWORK_NAME = 106, /* RFC 7839 */ + SD_DHCP6_OPTION_ANI_AP_NAME = 107, /* RFC 7839 */ + SD_DHCP6_OPTION_ANI_AP_BSSID = 108, /* RFC 7839 */ + SD_DHCP6_OPTION_ANI_OPERATOR_ID = 109, /* RFC 7839 */ + SD_DHCP6_OPTION_ANI_OPERATOR_REALM = 110, /* RFC 7839 */ + SD_DHCP6_OPTION_S46_PRIORITY = 111, /* RFC 8026 */ + SD_DHCP6_OPTION_MUD_URL_V6 = 112, /* RFC 8520 */ + SD_DHCP6_OPTION_V6_PREFIX64 = 113, /* RFC 8115 */ + SD_DHCP6_OPTION_F_BINDING_STATUS = 114, /* RFC 8156 */ + SD_DHCP6_OPTION_F_CONNECT_FLAGS = 115, /* RFC 8156 */ + SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO = 116, /* RFC 8156 */ + SD_DHCP6_OPTION_F_DNS_HOST_NAME = 117, /* RFC 8156 */ + SD_DHCP6_OPTION_F_DNS_ZONE_NAME = 118, /* RFC 8156 */ + SD_DHCP6_OPTION_F_DNS_FLAGS = 119, /* RFC 8156 */ + SD_DHCP6_OPTION_F_EXPIRATION_TIME = 120, /* RFC 8156 */ + SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD = 121, /* RFC 8156 */ + SD_DHCP6_OPTION_F_MCLT = 122, /* RFC 8156 */ + SD_DHCP6_OPTION_F_PARTNER_LIFETIME = 123, /* RFC 8156 */ + SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT = 124, /* RFC 8156 */ + SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME = 125, /* RFC 8156 */ + SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME = 126, /* RFC 8156 */ + SD_DHCP6_OPTION_F_PROTOCOL_VERSION = 127, /* RFC 8156 */ + SD_DHCP6_OPTION_F_KEEPALIVE_TIME = 128, /* RFC 8156 */ + SD_DHCP6_OPTION_F_RECONFIGURE_DATA = 129, /* RFC 8156 */ + SD_DHCP6_OPTION_F_RELATIONSHIP_NAME = 130, /* RFC 8156 */ + SD_DHCP6_OPTION_F_SERVER_FLAGS = 131, /* RFC 8156 */ + SD_DHCP6_OPTION_F_SERVER_STATE = 132, /* RFC 8156 */ + SD_DHCP6_OPTION_F_START_TIME_OF_STATE = 133, /* RFC 8156 */ + SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME = 134, /* RFC 8156 */ + SD_DHCP6_OPTION_RELAY_PORT = 135, /* RFC 8357 */ + SD_DHCP6_OPTION_V6_SZTP_REDIRECT = 136, /* RFC 8572 */ + SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX = 137, /* RFC 8539 */ + SD_DHCP6_OPTION_IA_LL = 138, /* RFC 8947 */ + SD_DHCP6_OPTION_LLADDR = 139, /* RFC 8947 */ + SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */ + SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */ + SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */ + SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */ + /* option codes 144-65535 are unassigned */ +}; + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h index 3303c374..a984a9d8 100644 --- a/src/libnm-systemd-core/src/systemd/sd-id128.h +++ b/src/libnm-systemd-core/src/systemd/sd-id128.h @@ -50,6 +50,7 @@ int sd_id128_get_machine(sd_id128_t *ret); int sd_id128_get_boot(sd_id128_t *ret); int sd_id128_get_invocation(sd_id128_t *ret); +int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret); int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret); diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h index ee309a42..5f4f6caf 100644 --- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h +++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h @@ -26,34 +26,14 @@ #include <sys/types.h> #include "sd-event.h" +#include "sd-ndisc-protocol.h" +#include "sd-ndisc-router.h" #include "_sd-common.h" _SD_BEGIN_DECLARATIONS; -/* Neighbor Discovery Options, RFC 4861, Section 4.6 and - * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */ -enum { - SD_NDISC_OPTION_SOURCE_LL_ADDRESS = 1, - SD_NDISC_OPTION_TARGET_LL_ADDRESS = 2, - SD_NDISC_OPTION_PREFIX_INFORMATION = 3, - SD_NDISC_OPTION_MTU = 5, - SD_NDISC_OPTION_ROUTE_INFORMATION = 24, - SD_NDISC_OPTION_RDNSS = 25, - SD_NDISC_OPTION_FLAGS_EXTENSION = 26, - SD_NDISC_OPTION_DNSSL = 31, - SD_NDISC_OPTION_CAPTIVE_PORTAL = 37 -}; - -/* Route preference, RFC 4191, Section 2.1 */ -enum { - SD_NDISC_PREFERENCE_LOW = 3U, - SD_NDISC_PREFERENCE_MEDIUM = 0U, - SD_NDISC_PREFERENCE_HIGH = 1U -}; - typedef struct sd_ndisc sd_ndisc; -typedef struct sd_ndisc_router sd_ndisc_router; __extension__ typedef enum sd_ndisc_event_t { SD_NDISC_EVENT_TIMEOUT, @@ -63,14 +43,16 @@ __extension__ typedef enum sd_ndisc_event_t { _SD_ENUM_FORCE_S64(NDISC_EVENT) } sd_ndisc_event_t; -typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata); +typedef void (*sd_ndisc_callback_t)(sd_ndisc *nd, sd_ndisc_event_t event, void *message, void *userdata); int sd_ndisc_new(sd_ndisc **ret); sd_ndisc *sd_ndisc_ref(sd_ndisc *nd); sd_ndisc *sd_ndisc_unref(sd_ndisc *nd); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); int sd_ndisc_start(sd_ndisc *nd); int sd_ndisc_stop(sd_ndisc *nd); +int sd_ndisc_is_running(sd_ndisc *nd); int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority); int sd_ndisc_detach_event(sd_ndisc *nd); @@ -80,52 +62,9 @@ int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata); int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index); int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name); int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret); +int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr); int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); -sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt); -sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt); - -int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret); -int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); - -int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags); -int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret); -int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime); -int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret); - -/* Generic option access */ -int sd_ndisc_router_option_rewind(sd_ndisc_router *rt); -int sd_ndisc_router_option_next(sd_ndisc_router *rt); -int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type); -int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size); - -/* Specific option access: SD_NDISC_OPTION_PREFIX_INFORMATION */ -int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret); -int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); - -/* Specific option access: SD_NDISC_OPTION_ROUTE_INFORMATION */ -int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); -int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr); -int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *prefixlen); -int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret); - -/* Specific option access: SD_NDISC_OPTION_RDNSS */ -int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret); -int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); - -/* Specific option access: SD_NDISC_OPTION_DNSSL */ -int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret); -int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref); - _SD_END_DECLARATIONS; #endif |