mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-07 00:31:11 +00:00
systemd: update code from upstream (2022-04-01)
This is a direct dump from systemd git. $ git clean -fdx && \ git cat-file -p HEAD | sed '1,/^======$/ d' | bash - && \ git add . ====== SYSTEMD_DIR=../systemd COMMIT=64c843d12dde2a7dc2646a09f38d697caa7faee3 ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/libnm-systemd-core/src/ \ :/src/libnm-systemd-shared/src/ \ :/src/libnm-std-aux/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1" } nm_copy_sd_core() { mkdir -p "./src/libnm-systemd-core/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1" } nm_copy_sd_stdaux() { mkdir -p "./src/libnm-std-aux/" cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/arp-util.c" nm_copy_sd_core "src/libsystemd-network/arp-util.h" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h" nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c" nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h" nm_copy_sd_core "src/libsystemd-network/lldp-network.c" nm_copy_sd_core "src/libsystemd-network/lldp-network.h" nm_copy_sd_core "src/libsystemd-network/lldp-rx-internal.h" nm_copy_sd_core "src/libsystemd-network/network-common.c" nm_copy_sd_core "src/libsystemd-network/network-common.h" nm_copy_sd_core "src/libsystemd-network/network-internal.c" nm_copy_sd_core "src/libsystemd-network/network-internal.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c" nm_copy_sd_core "src/libsystemd-network/sd-lldp-rx.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-dhcp-client.h" nm_copy_sd_core "src/systemd/sd-dhcp-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp-option.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-option.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-ipv4acd.h" nm_copy_sd_core "src/systemd/sd-ipv4ll.h" nm_copy_sd_core "src/systemd/sd-lldp-rx.h" nm_copy_sd_core "src/systemd/sd-lldp.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/async.h" nm_copy_sd_shared "src/basic/cgroup-util.h" nm_copy_sd_shared "src/basic/dns-def.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/errno-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/format-util.c" nm_copy_sd_shared "src/basic/format-util.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/inotify-util.c" nm_copy_sd_shared "src/basic/inotify-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/memory-util.c" nm_copy_sd_shared "src/basic/memory-util.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_fcntl.h" nm_copy_sd_shared "src/basic/missing_random.h" nm_copy_sd_shared "src/basic/missing_socket.h" nm_copy_sd_shared "src/basic/missing_stat.h" nm_copy_sd_shared "src/basic/missing_syscall.h" nm_copy_sd_shared "src/basic/missing_type.h" nm_copy_sd_shared "src/basic/ordered-set.c" nm_copy_sd_shared "src/basic/ordered-set.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/ratelimit.c" nm_copy_sd_shared "src/basic/ratelimit.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/signal-util.c" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sort-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/strxcpyx.c" nm_copy_sd_shared "src/basic/strxcpyx.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/user-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/util.c" nm_copy_sd_shared "src/basic/util.h" nm_copy_sd_shared "src/fundamental/macro-fundamental.h" nm_copy_sd_shared "src/fundamental/string-util-fundamental.c" nm_copy_sd_shared "src/fundamental/string-util-fundamental.h" nm_copy_sd_shared "src/fundamental/types-fundamental.h" nm_copy_sd_shared "src/shared/dns-domain.c" nm_copy_sd_shared "src/shared/dns-domain.h" nm_copy_sd_shared "src/shared/log-link.h" nm_copy_sd_shared "src/shared/web-util.c" nm_copy_sd_shared "src/shared/web-util.h" nm_copy_sd_stdaux "src/basic/unaligned.h"
This commit is contained in:
parent
a4ff07aaad
commit
7b3466fc4c
|
@ -8,20 +8,27 @@
|
|||
#include "sd-id128.h"
|
||||
|
||||
#include "dhcp-identifier.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "netif-util.h"
|
||||
#include "siphash24.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "udev-util.h"
|
||||
#include "virt.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 */
|
||||
|
||||
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
|
||||
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);
|
||||
|
@ -57,112 +64,146 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
|
||||
static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) {
|
||||
uint16_t time_from_2000y;
|
||||
|
||||
assert(duid);
|
||||
assert(len);
|
||||
assert(addr);
|
||||
assert(ret_duid);
|
||||
assert(ret_len);
|
||||
|
||||
if (addr_len == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (arp_type == ARPHRD_ETHER)
|
||||
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
||||
else if (arp_type == ARPHRD_INFINIBAND)
|
||||
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
||||
else
|
||||
return -EINVAL;
|
||||
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(&duid->type, DUID_TYPE_LLT);
|
||||
unaligned_write_be16(&duid->llt.htype, arp_type);
|
||||
unaligned_write_be32(&duid->llt.time, time_from_2000y);
|
||||
memcpy(duid->llt.haddr, addr, addr_len);
|
||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT);
|
||||
unaligned_write_be16(&ret_duid->llt.htype, arp_type);
|
||||
unaligned_write_be32(&ret_duid->llt.time, time_from_2000y);
|
||||
memcpy(ret_duid->llt.haddr, addr, addr_len);
|
||||
|
||||
*len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
|
||||
*ret_len = offsetof(struct duid, llt.haddr) + addr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
|
||||
assert(duid);
|
||||
assert(len);
|
||||
static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) {
|
||||
assert(addr);
|
||||
assert(ret_duid);
|
||||
assert(ret_len);
|
||||
|
||||
if (addr_len == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (arp_type == ARPHRD_ETHER)
|
||||
assert_return(addr_len == ETH_ALEN, -EINVAL);
|
||||
else if (arp_type == ARPHRD_INFINIBAND)
|
||||
assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
|
||||
else
|
||||
return -EINVAL;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
unaligned_write_be16(&duid->type, DUID_TYPE_LL);
|
||||
unaligned_write_be16(&duid->ll.htype, arp_type);
|
||||
memcpy(duid->ll.haddr, addr, addr_len);
|
||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL);
|
||||
unaligned_write_be16(&ret_duid->ll.htype, arp_type);
|
||||
memcpy(ret_duid->ll.haddr, addr, addr_len);
|
||||
|
||||
*len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
|
||||
*ret_len = offsetof(struct duid, ll.haddr) + addr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
|
||||
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(duid);
|
||||
assert(len);
|
||||
assert(ret_duid);
|
||||
assert(ret_len);
|
||||
|
||||
r = sd_id128_get_machine(&machine_id);
|
||||
if (r < 0) {
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
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);
|
||||
#else
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
unaligned_write_be16(&duid->type, DUID_TYPE_EN);
|
||||
unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
|
||||
|
||||
*len = sizeof(duid->type) + sizeof(duid->en);
|
||||
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(duid->en.id, &hash, sizeof(duid->en.id));
|
||||
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;
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
|
||||
static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) {
|
||||
sd_id128_t machine_id;
|
||||
int r;
|
||||
|
||||
assert(duid);
|
||||
assert(len);
|
||||
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(&duid->type, DUID_TYPE_UUID);
|
||||
memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
|
||||
unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID);
|
||||
memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id));
|
||||
|
||||
*len = sizeof(duid->type) + sizeof(machine_id);
|
||||
*ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_duid(
|
||||
DUIDType duid_type,
|
||||
const uint8_t *addr,
|
||||
size_t addr_len,
|
||||
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(addr, addr_len, arp_type, llt_time, ret_duid, ret_len);
|
||||
case DUID_TYPE_EN:
|
||||
return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len);
|
||||
case DUID_TYPE_LL:
|
||||
return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len);
|
||||
case DUID_TYPE_UUID:
|
||||
return dhcp_identifier_set_duid_uuid(ret_duid, ret_len);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int dhcp_identifier_set_iaid(
|
||||
int ifindex,
|
||||
const uint8_t *mac,
|
||||
size_t mac_len,
|
||||
bool legacy_unstable_byteorder,
|
||||
bool use_mac,
|
||||
void *_id) {
|
||||
void *ret) {
|
||||
|
||||
/* name is a pointer to memory in the sd_device struct, so must
|
||||
* have the same scope */
|
||||
|
@ -214,6 +255,6 @@ int dhcp_identifier_set_iaid(
|
|||
* behavior. */
|
||||
id32 = be32toh(id32);
|
||||
|
||||
unaligned_write_ne32(_id, id32);
|
||||
unaligned_write_ne32(ret, id32);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -54,9 +54,23 @@ struct duid {
|
|||
};
|
||||
} _packed_;
|
||||
|
||||
int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict);
|
||||
int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
|
||||
int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len);
|
||||
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
|
||||
int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len);
|
||||
int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id);
|
||||
int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict);
|
||||
int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len);
|
||||
int dhcp_identifier_set_duid(
|
||||
DUIDType duid_type,
|
||||
const uint8_t *addr,
|
||||
size_t addr_len,
|
||||
uint16_t arp_type,
|
||||
usec_t llt_time,
|
||||
bool test_mode,
|
||||
struct duid *ret_duid,
|
||||
size_t *ret_len);
|
||||
int dhcp_identifier_set_iaid(
|
||||
int ifindex,
|
||||
const uint8_t *mac,
|
||||
size_t mac_len,
|
||||
bool legacy_unstable_byteorder,
|
||||
bool use_mac,
|
||||
void *ret);
|
||||
|
||||
const char *duid_type_to_string(DUIDType t) _const_;
|
||||
|
|
|
@ -16,8 +16,6 @@ struct sd_dhcp_route {
|
|||
struct in_addr dst_addr;
|
||||
struct in_addr gw_addr;
|
||||
unsigned char dst_prefixlen;
|
||||
|
||||
uint8_t option;
|
||||
};
|
||||
|
||||
struct sd_dhcp_raw_option {
|
||||
|
@ -52,8 +50,10 @@ struct sd_dhcp_lease {
|
|||
|
||||
DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
|
||||
|
||||
struct sd_dhcp_route *static_route;
|
||||
size_t static_route_size;
|
||||
struct sd_dhcp_route *static_routes;
|
||||
size_t n_static_routes;
|
||||
struct sd_dhcp_route *classless_routes;
|
||||
size_t n_classless_routes;
|
||||
|
||||
uint16_t mtu; /* 0 if unset */
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset,
|
|||
|
||||
case SD_DHCP_OPTION_USER_CLASS: {
|
||||
size_t total = 0;
|
||||
char **s;
|
||||
|
||||
if (strv_isempty((char **) optval))
|
||||
return -EINVAL;
|
||||
|
|
|
@ -73,15 +73,24 @@ enum {
|
|||
};
|
||||
|
||||
enum {
|
||||
DHCP_DISCOVER = 1,
|
||||
DHCP_OFFER = 2,
|
||||
DHCP_REQUEST = 3,
|
||||
DHCP_DECLINE = 4,
|
||||
DHCP_ACK = 5,
|
||||
DHCP_NAK = 6,
|
||||
DHCP_RELEASE = 7,
|
||||
DHCP_INFORM = 8,
|
||||
DHCP_FORCERENEW = 9,
|
||||
DHCP_DISCOVER = 1, /* [RFC2132] */
|
||||
DHCP_OFFER = 2, /* [RFC2132] */
|
||||
DHCP_REQUEST = 3, /* [RFC2132] */
|
||||
DHCP_DECLINE = 4, /* [RFC2132] */
|
||||
DHCP_ACK = 5, /* [RFC2132] */
|
||||
DHCP_NAK = 6, /* [RFC2132] */
|
||||
DHCP_RELEASE = 7, /* [RFC2132] */
|
||||
DHCP_INFORM = 8, /* [RFC2132] */
|
||||
DHCP_FORCERENEW = 9, /* [RFC3203] */
|
||||
DHCPLEASEQUERY = 10, /* [RFC4388] */
|
||||
DHCPLEASEUNASSIGNED = 11, /* [RFC4388] */
|
||||
DHCPLEASEUNKNOWN = 12, /* [RFC4388] */
|
||||
DHCPLEASEACTIVE = 13, /* [RFC4388] */
|
||||
DHCPBULKLEASEQUERY = 14, /* [RFC6926] */
|
||||
DHCPLEASEQUERYDONE = 15, /* [RFC6926] */
|
||||
DHCPACTIVELEASEQUERY = 16, /* [RFC7724] */
|
||||
DHCPLEASEQUERYSTATUS = 17, /* [RFC7724] */
|
||||
DHCPTLS = 18, /* [RFC7724] */
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -11,138 +11,83 @@
|
|||
#include "sd-event.h"
|
||||
#include "sd-dhcp6-client.h"
|
||||
|
||||
#include "dhcp-identifier.h"
|
||||
#include "dhcp6-option.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "network-common.h"
|
||||
#include "ordered-set.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct sd_dhcp6_option {
|
||||
/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
|
||||
typedef enum DHCP6RequestIA {
|
||||
DHCP6_REQUEST_IA_NA = 1 << 0,
|
||||
DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
|
||||
DHCP6_REQUEST_IA_PD = 1 << 2,
|
||||
} DHCP6RequestIA;
|
||||
|
||||
struct sd_dhcp6_client {
|
||||
unsigned n_ref;
|
||||
|
||||
uint32_t enterprise_identifier;
|
||||
uint16_t option;
|
||||
void *data;
|
||||
size_t length;
|
||||
} sd_dhcp6_option;
|
||||
int ifindex;
|
||||
char *ifname;
|
||||
|
||||
extern const struct hash_ops dhcp6_option_hash_ops;
|
||||
struct in6_addr local_address;
|
||||
struct hw_addr_data hw_addr;
|
||||
uint16_t arp_type;
|
||||
|
||||
/* Common option header */
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
sd_event *event;
|
||||
sd_event_source *receive_message;
|
||||
sd_event_source *timeout_resend;
|
||||
sd_event_source *timeout_expire;
|
||||
sd_event_source *timeout_t1;
|
||||
sd_event_source *timeout_t2;
|
||||
int event_priority;
|
||||
int fd;
|
||||
|
||||
/* Address option */
|
||||
struct iaaddr {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
} _packed_;
|
||||
DHCP6State state;
|
||||
bool information_request;
|
||||
usec_t information_request_time_usec;
|
||||
usec_t information_refresh_time_usec;
|
||||
be32_t transaction_id;
|
||||
usec_t transaction_start;
|
||||
usec_t retransmit_time;
|
||||
uint8_t retransmit_count;
|
||||
|
||||
/* Prefix Delegation Prefix option */
|
||||
struct iapdprefix {
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
uint8_t prefixlen;
|
||||
struct in6_addr address;
|
||||
} _packed_;
|
||||
bool iaid_set;
|
||||
DHCP6IA ia_na;
|
||||
DHCP6IA ia_pd;
|
||||
DHCP6RequestIA request_ia;
|
||||
struct duid duid;
|
||||
size_t duid_len;
|
||||
be16_t *req_opts;
|
||||
size_t req_opts_len;
|
||||
char *fqdn;
|
||||
char *mudurl;
|
||||
char **user_class;
|
||||
char **vendor_class;
|
||||
OrderedHashmap *extra_options;
|
||||
OrderedSet *vendor_options;
|
||||
|
||||
typedef struct DHCP6Address DHCP6Address;
|
||||
struct sd_dhcp6_lease *lease;
|
||||
|
||||
struct DHCP6Address {
|
||||
LIST_FIELDS(DHCP6Address, addresses);
|
||||
sd_dhcp6_client_callback_t callback;
|
||||
void *userdata;
|
||||
|
||||
union {
|
||||
struct iaaddr iaaddr;
|
||||
struct iapdprefix iapdprefix;
|
||||
};
|
||||
/* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
|
||||
bool test_mode;
|
||||
};
|
||||
|
||||
/* Non-temporary Address option */
|
||||
struct ia_na {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
|
||||
/* Prefix Delegation option */
|
||||
struct ia_pd {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
|
||||
/* Temporary Address option */
|
||||
struct ia_ta {
|
||||
be32_t id;
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6IA {
|
||||
uint16_t type;
|
||||
union {
|
||||
struct ia_na ia_na;
|
||||
struct ia_pd ia_pd;
|
||||
struct ia_ta ia_ta;
|
||||
};
|
||||
|
||||
LIST_HEAD(DHCP6Address, addresses);
|
||||
} DHCP6IA;
|
||||
|
||||
typedef struct sd_dhcp6_client sd_dhcp6_client;
|
||||
|
||||
bool dhcp6_option_can_request(uint16_t option);
|
||||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
||||
size_t optlen, const void *optval);
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
|
||||
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix);
|
||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
||||
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
|
||||
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
size_t *offset,
|
||||
uint16_t *ret_option_code,
|
||||
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_ia(
|
||||
sd_dhcp6_client *client,
|
||||
be32_t iaid,
|
||||
uint16_t option_code,
|
||||
size_t option_data_len,
|
||||
const uint8_t *option_data,
|
||||
DHCP6IA *ret);
|
||||
int dhcp6_option_parse_addresses(
|
||||
const uint8_t *optval,
|
||||
size_t optlen,
|
||||
struct in6_addr **addrs,
|
||||
size_t *count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
|
||||
|
||||
int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
|
||||
int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
|
||||
const void *packet, size_t len);
|
||||
|
||||
int client_parse_message(
|
||||
sd_dhcp6_client *client,
|
||||
DHCP6Message *message,
|
||||
size_t len,
|
||||
sd_dhcp6_lease *lease);
|
||||
|
||||
const char *dhcp6_message_type_to_string(int s) _const_;
|
||||
int dhcp6_message_type_from_string(const char *s) _pure_;
|
||||
const char *dhcp6_message_status_to_string(int s) _const_;
|
||||
int dhcp6_message_status_from_string(const char *s) _pure_;
|
||||
|
||||
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, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
Copyright © 2014-2015 Intel Corporation. All rights reserved.
|
||||
***/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "sd-dhcp6-lease.h"
|
||||
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-option.h"
|
||||
#include "macro.h"
|
||||
#include "time-util.h"
|
||||
|
||||
struct sd_dhcp6_lease {
|
||||
unsigned n_ref;
|
||||
|
@ -21,10 +23,13 @@ struct sd_dhcp6_lease {
|
|||
uint8_t preference;
|
||||
bool rapid_commit;
|
||||
triple_timestamp timestamp;
|
||||
usec_t lifetime_t1;
|
||||
usec_t lifetime_t2;
|
||||
usec_t lifetime_valid;
|
||||
struct in6_addr server_address;
|
||||
|
||||
DHCP6IA ia;
|
||||
DHCP6IA pd;
|
||||
DHCP6IA *ia_na; /* Identity association non-temporary addresses */
|
||||
DHCP6IA *ia_pd; /* Identity association prefix delegation */
|
||||
|
||||
DHCP6Address *addr_iter;
|
||||
DHCP6Address *prefix_iter;
|
||||
|
@ -40,17 +45,15 @@ struct sd_dhcp6_lease {
|
|||
char *fqdn;
|
||||
};
|
||||
|
||||
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
|
||||
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
|
||||
|
||||
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);
|
||||
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
|
||||
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
|
||||
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
|
||||
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret);
|
||||
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
|
||||
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
|
||||
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret);
|
||||
|
||||
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
|
@ -59,3 +62,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
|
|||
int dhcp6_lease_set_fqdn(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(
|
||||
sd_dhcp6_client *client,
|
||||
const DHCP6Message *message,
|
||||
size_t len,
|
||||
const triple_timestamp *timestamp,
|
||||
const struct in6_addr *server_address,
|
||||
sd_dhcp6_lease **ret);
|
||||
|
|
|
@ -9,14 +9,12 @@
|
|||
#include "sd-dhcp6-client.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp-identifier.h"
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-option.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "dns-domain.h"
|
||||
#include "escape.h"
|
||||
#include "memory-util.h"
|
||||
#include "sparse-endian.h"
|
||||
#include "strv.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
|
@ -74,8 +72,8 @@ bool dhcp6_option_can_request(uint16_t option) {
|
|||
return false;
|
||||
case SD_DHCP6_OPTION_CLIENT_FQDN:
|
||||
case SD_DHCP6_OPTION_PANA_AGENT:
|
||||
case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE:
|
||||
case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE:
|
||||
case SD_DHCP6_OPTION_POSIX_TIMEZONE:
|
||||
case SD_DHCP6_OPTION_TZDB_TIMEZONE:
|
||||
return true;
|
||||
case SD_DHCP6_OPTION_ERO:
|
||||
case SD_DHCP6_OPTION_LQ_QUERY:
|
||||
|
@ -212,19 +210,15 @@ bool dhcp6_option_can_request(uint16_t option) {
|
|||
}
|
||||
|
||||
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
|
||||
DHCP6Option *option;
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
assert_return(*buf, -EINVAL);
|
||||
assert_return(buflen, -EINVAL);
|
||||
|
||||
option = (DHCP6Option*) *buf;
|
||||
|
||||
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
|
||||
return -ENOBUFS;
|
||||
|
||||
option->code = htobe16(optcode);
|
||||
option->len = htobe16(optlen);
|
||||
unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode);
|
||||
unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen);
|
||||
|
||||
*buf += offsetof(DHCP6Option, data);
|
||||
*buflen -= offsetof(DHCP6Option, data);
|
||||
|
@ -242,15 +236,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memcpy_safe(*buf, optval, optlen);
|
||||
|
||||
*buf += optlen;
|
||||
*buf = mempcpy_safe(*buf, optval, optlen);
|
||||
*buflen -= optlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
|
||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) {
|
||||
sd_dhcp6_option *options;
|
||||
int r;
|
||||
|
||||
|
@ -259,7 +251,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
|
|||
assert(buflen);
|
||||
assert(vendor_options);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH(options, vendor_options) {
|
||||
ORDERED_SET_FOREACH(options, vendor_options) {
|
||||
_cleanup_free_ uint8_t *p = NULL;
|
||||
size_t total;
|
||||
|
||||
|
@ -282,14 +274,63 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) {
|
||||
struct iaaddr a;
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
assert(*buf);
|
||||
assert(buflen);
|
||||
assert(address);
|
||||
|
||||
/* Do not append T1 and T2. */
|
||||
a = (struct iaaddr) {
|
||||
.address = address->address,
|
||||
};
|
||||
|
||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*buf = mempcpy(*buf, &a, sizeof(struct iaaddr));
|
||||
*buflen -= sizeof(struct iaaddr);
|
||||
|
||||
return offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
|
||||
}
|
||||
|
||||
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) {
|
||||
struct iapdprefix p;
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
assert(*buf);
|
||||
assert(buflen);
|
||||
assert(prefix);
|
||||
|
||||
if (prefix->prefixlen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do not append T1 and T2. */
|
||||
p = (struct iapdprefix) {
|
||||
.prefixlen = prefix->prefixlen,
|
||||
.address = prefix->address,
|
||||
};
|
||||
|
||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*buf = mempcpy(*buf, &p, sizeof(struct iapdprefix));
|
||||
*buflen -= sizeof(struct iapdprefix);
|
||||
|
||||
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
|
||||
}
|
||||
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
|
||||
size_t ia_buflen, ia_addrlen = 0;
|
||||
struct ia_na ia_na;
|
||||
struct ia_ta ia_ta;
|
||||
DHCP6Address *addr;
|
||||
struct ia_header header;
|
||||
size_t ia_buflen;
|
||||
uint8_t *ia_hdr;
|
||||
uint16_t len;
|
||||
void *p;
|
||||
int r;
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
|
@ -301,23 +342,22 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
|
|||
|
||||
switch (ia->type) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
len = DHCP6_OPTION_IA_NA_LEN;
|
||||
ia_na = (struct ia_na) {
|
||||
.id = ia->ia_na.id,
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
len = sizeof(struct ia_header);
|
||||
header = (struct ia_header) {
|
||||
.id = ia->header.id,
|
||||
};
|
||||
p = &ia_na;
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
len = DHCP6_OPTION_IA_TA_LEN;
|
||||
ia_ta = (struct ia_ta) {
|
||||
.id = ia->ia_ta.id,
|
||||
len = sizeof(header.id); /* IA_TA does not have lifetime. */
|
||||
header = (struct ia_header) {
|
||||
.id = ia->header.id,
|
||||
};
|
||||
p = &ia_ta;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (*buflen < offsetof(DHCP6Option, data) + len)
|
||||
|
@ -326,116 +366,25 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
|
|||
ia_hdr = *buf;
|
||||
ia_buflen = *buflen;
|
||||
|
||||
*buf += offsetof(DHCP6Option, data);
|
||||
*buflen -= offsetof(DHCP6Option, data);
|
||||
|
||||
memcpy(*buf, p, len);
|
||||
|
||||
*buf += len;
|
||||
*buflen -= len;
|
||||
|
||||
LIST_FOREACH(addresses, addr, ia->addresses) {
|
||||
struct iaaddr a = {
|
||||
.address = addr->iaaddr.address,
|
||||
};
|
||||
|
||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memcpy(*buf, &a, sizeof(struct iaaddr));
|
||||
|
||||
*buf += sizeof(struct iaaddr);
|
||||
*buflen -= sizeof(struct iaaddr);
|
||||
|
||||
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
|
||||
}
|
||||
|
||||
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
|
||||
}
|
||||
|
||||
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) {
|
||||
struct iapdprefix p;
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
assert(*buf);
|
||||
assert(buflen);
|
||||
assert(prefix);
|
||||
|
||||
if (prefix->iapdprefix.prefixlen == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do not append T1 and T2. */
|
||||
|
||||
p = (struct iapdprefix) {
|
||||
.prefixlen = prefix->iapdprefix.prefixlen,
|
||||
.address = prefix->iapdprefix.address,
|
||||
};
|
||||
|
||||
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
memcpy(*buf, &p, sizeof(struct iapdprefix));
|
||||
|
||||
*buf += sizeof(struct iapdprefix);
|
||||
*buflen -= sizeof(struct iapdprefix);
|
||||
|
||||
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
|
||||
}
|
||||
|
||||
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) {
|
||||
struct ia_pd ia_pd;
|
||||
size_t len, pd_buflen;
|
||||
uint8_t *pd_hdr;
|
||||
int r;
|
||||
|
||||
assert_return(buf, -EINVAL);
|
||||
assert_return(*buf, -EINVAL);
|
||||
assert_return(buflen, -EINVAL);
|
||||
assert_return(pd, -EINVAL);
|
||||
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
|
||||
|
||||
/* Do not set T1 and T2. */
|
||||
ia_pd = (struct ia_pd) {
|
||||
.id = pd->ia_pd.id,
|
||||
};
|
||||
len = sizeof(struct ia_pd);
|
||||
|
||||
if (*buflen < offsetof(DHCP6Option, data) + len)
|
||||
return -ENOBUFS;
|
||||
|
||||
pd_hdr = *buf;
|
||||
pd_buflen = *buflen;
|
||||
|
||||
/* The header will be written at the end of this function. */
|
||||
*buf += offsetof(DHCP6Option, data);
|
||||
*buflen -= offsetof(DHCP6Option, data);
|
||||
|
||||
memcpy(*buf, &ia_pd, len);
|
||||
*buf = mempcpy(*buf, &header, len);
|
||||
*buflen -= len;
|
||||
|
||||
*buf += sizeof(struct ia_pd);
|
||||
*buflen -= sizeof(struct ia_pd);
|
||||
|
||||
DHCP6Address *prefix;
|
||||
LIST_FOREACH(addresses, prefix, pd->addresses) {
|
||||
r = option_append_pd_prefix(buf, buflen, prefix);
|
||||
LIST_FOREACH(addresses, addr, ia->addresses) {
|
||||
if (ia->type == SD_DHCP6_OPTION_IA_PD)
|
||||
r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix);
|
||||
else
|
||||
r = option_append_ia_address(buf, buflen, &addr->iaaddr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
len += r;
|
||||
}
|
||||
|
||||
if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
|
||||
r = option_append_pd_prefix(buf, buflen, hint_pd_prefix);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
len += r;
|
||||
}
|
||||
|
||||
return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len);
|
||||
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len);
|
||||
}
|
||||
|
||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
||||
|
@ -468,7 +417,6 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
|
|||
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) {
|
||||
_cleanup_free_ uint8_t *p = NULL;
|
||||
size_t total = 0, offset = 0;
|
||||
char * const *s;
|
||||
|
||||
assert(buf);
|
||||
assert(*buf);
|
||||
|
@ -501,7 +449,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const
|
|||
_cleanup_free_ uint8_t *p = NULL;
|
||||
uint32_t enterprise_identifier;
|
||||
size_t total, offset;
|
||||
char * const *s;
|
||||
|
||||
assert(buf);
|
||||
assert(*buf);
|
||||
|
@ -548,7 +495,6 @@ int dhcp6_option_parse(
|
|||
size_t *ret_option_data_len,
|
||||
const uint8_t **ret_option_data) {
|
||||
|
||||
const DHCP6Option *option;
|
||||
size_t len;
|
||||
|
||||
assert(buf);
|
||||
|
@ -563,16 +509,15 @@ int dhcp6_option_parse(
|
|||
if (*offset >= buflen - offsetof(DHCP6Option, data))
|
||||
return -EBADMSG;
|
||||
|
||||
option = (const DHCP6Option*) (buf + *offset);
|
||||
len = be16toh(option->len);
|
||||
len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len));
|
||||
|
||||
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
|
||||
return -EBADMSG;
|
||||
|
||||
*offset += offsetof(DHCP6Option, data) + len;
|
||||
*ret_option_code = be16toh(option->code);
|
||||
*ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code));
|
||||
*ret_option_data_len = len;
|
||||
*ret_option_data = option->data;
|
||||
*ret_option_data = buf + *offset + offsetof(DHCP6Option, data);
|
||||
*offset += offsetof(DHCP6Option, data) + len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -601,9 +546,9 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s
|
|||
static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) {
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
assert(buf || buflen == 0);
|
||||
|
||||
for(size_t offset = 0; offset < buflen;) {
|
||||
for (size_t offset = 0; offset < buflen;) {
|
||||
const uint8_t *data;
|
||||
size_t data_len;
|
||||
uint16_t code;
|
||||
|
@ -612,22 +557,22 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(code) {
|
||||
switch (code) {
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
r = dhcp6_option_parse_status(data, data_len, &msg);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Let's log but ignore the invalid status option. */
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
|
||||
else if (r > 0)
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA address or PD prefix option with non-zero status: %s%s%s",
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
if (r < 0)
|
||||
/* Let's log but ignore the invalid status option. */
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -638,19 +583,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||
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;
|
||||
DHCP6Address *a;
|
||||
int r;
|
||||
|
||||
assert(data);
|
||||
assert(ret);
|
||||
assert(ia);
|
||||
assert(data || len == 0);
|
||||
|
||||
if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA))
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA address sub-option in an invalid option, ignoring.");
|
||||
|
||||
if (len < sizeof(struct iaaddr))
|
||||
return -EBADMSG;
|
||||
|
||||
lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred);
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
||||
|
||||
lt_valid = be32toh(a->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(a->iaaddr.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
|
@ -667,27 +622,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t
|
|||
return r;
|
||||
}
|
||||
|
||||
LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int r;
|
||||
|
||||
assert(ia);
|
||||
assert(data || len == 0);
|
||||
|
||||
if (ia->type != SD_DHCP6_OPTION_IA_PD)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an PD prefix sub-option in an invalid option, ignoring");
|
||||
|
||||
if (len < sizeof(struct iapdprefix))
|
||||
return -EBADMSG;
|
||||
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
||||
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) {
|
||||
uint32_t lt_valid, lt_pref;
|
||||
DHCP6Address *a;
|
||||
int r;
|
||||
|
||||
if (len < sizeof(struct iapdprefix))
|
||||
return -ENOMSG;
|
||||
|
||||
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
|
||||
lt_valid = be32toh(a->iapdprefix.lifetime_valid);
|
||||
lt_pref = be32toh(a->iapdprefix.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
|
@ -704,14 +665,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t
|
|||
return r;
|
||||
}
|
||||
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix));
|
||||
|
||||
*ret = a;
|
||||
LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -721,16 +675,15 @@ int dhcp6_option_parse_ia(
|
|||
uint16_t option_code,
|
||||
size_t option_data_len,
|
||||
const uint8_t *option_data,
|
||||
DHCP6IA *ret) {
|
||||
DHCP6IA **ret) {
|
||||
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX;
|
||||
be32_t received_iaid;
|
||||
size_t offset;
|
||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
||||
uint32_t lt_t1, lt_t2;
|
||||
size_t header_len;
|
||||
int r;
|
||||
|
||||
assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD));
|
||||
assert(option_data);
|
||||
assert(option_data || option_data_len == 0);
|
||||
assert(ret);
|
||||
|
||||
/* This will return the following:
|
||||
|
@ -743,56 +696,51 @@ int dhcp6_option_parse_ia(
|
|||
|
||||
switch (option_code) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
|
||||
if (option_data_len < DHCP6_OPTION_IA_NA_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
offset = DHCP6_OPTION_IA_NA_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_na*) option_data)->id;
|
||||
lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1);
|
||||
lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
|
||||
if (option_data_len < DHCP6_OPTION_IA_PD_LEN)
|
||||
return -EBADMSG;
|
||||
|
||||
offset = DHCP6_OPTION_IA_PD_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_pd*) option_data)->id;
|
||||
lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1);
|
||||
lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2);
|
||||
header_len = sizeof(struct ia_header);
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
if (option_data_len < DHCP6_OPTION_IA_TA_LEN)
|
||||
return -ENOMSG;
|
||||
|
||||
offset = DHCP6_OPTION_IA_TA_LEN;
|
||||
|
||||
received_iaid = ((const struct ia_ta*) option_data)->id;
|
||||
lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */
|
||||
header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
if (option_data_len < header_len)
|
||||
return -EBADMSG;
|
||||
|
||||
ia = new(DHCP6IA, 1);
|
||||
if (!ia)
|
||||
return -ENOMEM;
|
||||
|
||||
*ia = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
};
|
||||
memcpy(&ia->header, option_data, header_len);
|
||||
|
||||
/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
|
||||
* but not necessary to ignore or refuse the whole message. */
|
||||
if (received_iaid != iaid)
|
||||
if (ia->header.id != iaid)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
|
||||
"Received an IA option with a different IAID "
|
||||
"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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
for (; offset < option_data_len;) {
|
||||
for (size_t offset = header_len; offset < option_data_len;) {
|
||||
const uint8_t *subdata;
|
||||
size_t subdata_len;
|
||||
uint16_t subopt;
|
||||
|
@ -803,41 +751,19 @@ int dhcp6_option_parse_ia(
|
|||
|
||||
switch (subopt) {
|
||||
case SD_DHCP6_OPTION_IAADDR: {
|
||||
DHCP6Address *a;
|
||||
|
||||
if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) {
|
||||
log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
|
||||
r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Ignore the sub-option on non-critical errors. */
|
||||
continue;
|
||||
|
||||
lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid));
|
||||
LIST_PREPEND(addresses, ia.addresses, a);
|
||||
/* Ignore non-critical errors in the sub-option. */
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_PD_PREFIX: {
|
||||
DHCP6Address *a;
|
||||
|
||||
if (option_code != SD_DHCP6_OPTION_IA_PD) {
|
||||
log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a);
|
||||
r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
/* Ignore the sub-option on non-critical errors. */
|
||||
continue;
|
||||
|
||||
lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid));
|
||||
LIST_PREPEND(addresses, ia.addresses, a);
|
||||
/* Ignore non-critical errors in the sub-option. */
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
|
@ -846,14 +772,14 @@ int dhcp6_option_parse_ia(
|
|||
r = dhcp6_option_parse_status(subdata, subdata_len, &msg);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA option with an invalid status sub option, ignoring: %m");
|
||||
else if (r > 0)
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received an IA option with non-zero status: %s%s%s",
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r,
|
||||
"Received an IA option with an invalid status sub option, ignoring: %m");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -861,50 +787,11 @@ int dhcp6_option_parse_ia(
|
|||
}
|
||||
}
|
||||
|
||||
if (!ia.addresses)
|
||||
if (!ia->addresses)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA),
|
||||
"Received an IA option without valid IA addresses or PD prefixes, ignoring.");
|
||||
|
||||
if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) &&
|
||||
lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) {
|
||||
lt_t1 = lt_min / 2;
|
||||
lt_t2 = lt_min / 10 * 8;
|
||||
|
||||
log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. "
|
||||
"Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: "
|
||||
"T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2);
|
||||
}
|
||||
|
||||
switch(option_code) {
|
||||
case SD_DHCP6_OPTION_IA_NA:
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_na.id = iaid,
|
||||
.ia_na.lifetime_t1 = htobe32(lt_t1),
|
||||
.ia_na.lifetime_t2 = htobe32(lt_t2),
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
case SD_DHCP6_OPTION_IA_TA:
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_ta.id = iaid,
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
case SD_DHCP6_OPTION_IA_PD:
|
||||
*ret = (DHCP6IA) {
|
||||
.type = option_code,
|
||||
.ia_pd.id = iaid,
|
||||
.ia_pd.lifetime_t1 = htobe32(lt_t1),
|
||||
.ia_pd.lifetime_t2 = htobe32(lt_t2),
|
||||
.addresses = TAKE_PTR(ia.addresses),
|
||||
};
|
||||
break;
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(ia);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
104
src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
Normal file
104
src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-dhcp6-client.h"
|
||||
|
||||
#include "hash-funcs.h"
|
||||
#include "list.h"
|
||||
#include "macro.h"
|
||||
#include "ordered-set.h"
|
||||
#include "sparse-endian.h"
|
||||
|
||||
typedef struct sd_dhcp6_option {
|
||||
unsigned n_ref;
|
||||
|
||||
uint32_t enterprise_identifier;
|
||||
uint16_t option;
|
||||
void *data;
|
||||
size_t length;
|
||||
} sd_dhcp6_option;
|
||||
|
||||
extern const struct hash_ops dhcp6_option_hash_ops;
|
||||
|
||||
/* Common option header */
|
||||
typedef struct DHCP6Option {
|
||||
be16_t code;
|
||||
be16_t len;
|
||||
uint8_t data[];
|
||||
} _packed_ DHCP6Option;
|
||||
|
||||
/* Address option */
|
||||
struct iaaddr {
|
||||
struct in6_addr address;
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
} _packed_;
|
||||
|
||||
/* Prefix Delegation Prefix option */
|
||||
struct iapdprefix {
|
||||
be32_t lifetime_preferred;
|
||||
be32_t lifetime_valid;
|
||||
uint8_t prefixlen;
|
||||
struct in6_addr address;
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6Address DHCP6Address;
|
||||
|
||||
struct DHCP6Address {
|
||||
LIST_FIELDS(DHCP6Address, addresses);
|
||||
|
||||
union {
|
||||
struct iaaddr iaaddr;
|
||||
struct iapdprefix iapdprefix;
|
||||
};
|
||||
};
|
||||
|
||||
struct ia_header {
|
||||
be32_t id;
|
||||
be32_t lifetime_t1;
|
||||
be32_t lifetime_t2;
|
||||
} _packed_;
|
||||
|
||||
typedef struct DHCP6IA {
|
||||
uint16_t type;
|
||||
struct ia_header header;
|
||||
|
||||
LIST_HEAD(DHCP6Address, addresses);
|
||||
} DHCP6IA;
|
||||
|
||||
void dhcp6_ia_clear_addresses(DHCP6IA *ia);
|
||||
DHCP6IA *dhcp6_ia_free(DHCP6IA *ia);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free);
|
||||
|
||||
bool dhcp6_option_can_request(uint16_t option);
|
||||
|
||||
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
|
||||
size_t optlen, const void *optval);
|
||||
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
|
||||
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
|
||||
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);
|
||||
int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options);
|
||||
|
||||
int dhcp6_option_parse(
|
||||
const uint8_t *buf,
|
||||
size_t buflen,
|
||||
size_t *offset,
|
||||
uint16_t *ret_option_code,
|
||||
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_ia(
|
||||
sd_dhcp6_client *client,
|
||||
be32_t iaid,
|
||||
uint16_t option_code,
|
||||
size_t option_data_len,
|
||||
const uint8_t *option_data,
|
||||
DHCP6IA **ret);
|
||||
int dhcp6_option_parse_addresses(
|
||||
const uint8_t *optval,
|
||||
size_t optlen,
|
||||
struct in6_addr **addrs,
|
||||
size_t *count);
|
||||
int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret);
|
||||
int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret);
|
|
@ -0,0 +1,84 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "string-table.h"
|
||||
|
||||
static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = {
|
||||
[DHCP6_STATE_STOPPED] = "stopped",
|
||||
[DHCP6_STATE_INFORMATION_REQUEST] = "information-request",
|
||||
[DHCP6_STATE_SOLICITATION] = "solicitation",
|
||||
[DHCP6_STATE_REQUEST] = "request",
|
||||
[DHCP6_STATE_BOUND] = "bound",
|
||||
[DHCP6_STATE_RENEW] = "renew",
|
||||
[DHCP6_STATE_REBIND] = "rebind",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State);
|
||||
|
||||
static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
|
||||
[DHCP6_MESSAGE_SOLICIT] = "Solicit",
|
||||
[DHCP6_MESSAGE_ADVERTISE] = "Advertise",
|
||||
[DHCP6_MESSAGE_REQUEST] = "Request",
|
||||
[DHCP6_MESSAGE_CONFIRM] = "Confirm",
|
||||
[DHCP6_MESSAGE_RENEW] = "Renew",
|
||||
[DHCP6_MESSAGE_REBIND] = "Rebind",
|
||||
[DHCP6_MESSAGE_REPLY] = "Reply",
|
||||
[DHCP6_MESSAGE_RELEASE] = "Release",
|
||||
[DHCP6_MESSAGE_DECLINE] = "Decline",
|
||||
[DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure",
|
||||
[DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
|
||||
[DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward",
|
||||
[DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply",
|
||||
[DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query",
|
||||
[DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply",
|
||||
[DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done",
|
||||
[DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data",
|
||||
[DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
|
||||
[DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply",
|
||||
[DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query",
|
||||
[DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response",
|
||||
[DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query",
|
||||
[DHCP6_MESSAGE_START_TLS] = "Start TLS",
|
||||
[DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update",
|
||||
[DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply",
|
||||
[DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request",
|
||||
[DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response",
|
||||
[DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request",
|
||||
[DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All",
|
||||
[DHCP6_MESSAGE_UPDATE_DONE] = "Update Done",
|
||||
[DHCP6_MESSAGE_CONNECT] = "Connect",
|
||||
[DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply",
|
||||
[DHCP6_MESSAGE_DISCONNECT] = "Disconnect",
|
||||
[DHCP6_MESSAGE_STATE] = "State",
|
||||
[DHCP6_MESSAGE_CONTACT] = "Contact",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType);
|
||||
|
||||
static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
|
||||
[DHCP6_STATUS_SUCCESS] = "Success",
|
||||
[DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
|
||||
[DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
|
||||
[DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
|
||||
[DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
|
||||
[DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
|
||||
[DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available",
|
||||
[DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type",
|
||||
[DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query",
|
||||
[DHCP6_STATUS_NOT_CONFIGURED] = "Not configured",
|
||||
[DHCP6_STATUS_NOT_ALLOWED] = "Not allowed",
|
||||
[DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated",
|
||||
[DHCP6_STATUS_DATA_MISSING] = "Data missing",
|
||||
[DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete",
|
||||
[DHCP6_STATUS_NOT_SUPPORTED] = "Not supported",
|
||||
[DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused",
|
||||
[DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use",
|
||||
[DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict",
|
||||
[DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information",
|
||||
[DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
|
||||
[DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down",
|
||||
[DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported",
|
||||
[DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status);
|
|
@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message;
|
|||
#define DHCP6_MIN_OPTIONS_SIZE \
|
||||
1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
|
||||
|
||||
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
|
||||
{ { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
|
||||
|
||||
enum {
|
||||
|
@ -100,13 +100,15 @@ typedef enum DHCP6MessageType {
|
|||
DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */
|
||||
DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */
|
||||
_DHCP6_MESSAGE_TYPE_MAX,
|
||||
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
|
||||
_DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
|
||||
} DHCP6MessageType;
|
||||
|
||||
typedef enum DHCP6NTPSubOption {
|
||||
DHCP6_NTP_SUBOPTION_SRV_ADDR = 1,
|
||||
DHCP6_NTP_SUBOPTION_MC_ADDR = 2,
|
||||
DHCP6_NTP_SUBOPTION_SRV_FQDN = 3,
|
||||
_DHCP6_NTP_SUBOPTION_MAX,
|
||||
_DHCP6_NTP_SUBOPTION_INVALID = -EINVAL,
|
||||
} DHCP6NTPSubOption;
|
||||
|
||||
/*
|
||||
|
@ -138,7 +140,7 @@ typedef enum DHCP6Status {
|
|||
DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21,
|
||||
DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22,
|
||||
_DHCP6_STATUS_MAX,
|
||||
_DHCP6_STATUS_INVALID = -EINVAL,
|
||||
_DHCP6_STATUS_INVALID = -EINVAL,
|
||||
} DHCP6Status;
|
||||
|
||||
typedef enum DHCP6FQDNFlag {
|
||||
|
@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag {
|
|||
DHCP6_FQDN_FLAG_O = 1 << 1,
|
||||
DHCP6_FQDN_FLAG_N = 1 << 2,
|
||||
} DHCP6FQDNFlag;
|
||||
|
||||
const char *dhcp6_state_to_string(DHCP6State s) _const_;
|
||||
const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
|
||||
DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
|
||||
const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
|
||||
DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
|
||||
|
|
|
@ -333,9 +333,9 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
|
|||
usec_t base;
|
||||
|
||||
/* Use the packet's timestamp if there is one known */
|
||||
base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
|
||||
if (base <= 0 || base == USEC_INFINITY)
|
||||
base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
|
||||
base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME);
|
||||
if (!timestamp_is_set(base))
|
||||
base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */
|
||||
|
||||
n->until = usec_add(base, n->ttl * USEC_PER_SEC);
|
||||
} else
|
||||
|
|
|
@ -21,7 +21,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
|
|||
struct sd_dhcp_route;
|
||||
struct sd_dhcp_lease;
|
||||
|
||||
void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
|
||||
void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route **routes, size_t size);
|
||||
int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string);
|
||||
|
||||
/* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
|
||||
|
|
|
@ -145,11 +145,11 @@ static const uint8_t default_req_opts_anonymize[] = {
|
|||
SD_DHCP_OPTION_ROUTER, /* 3 */
|
||||
SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
|
||||
SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
|
||||
SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
|
||||
SD_DHCP_OPTION_ROUTER_DISCOVERY, /* 31 */
|
||||
SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
|
||||
SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
|
||||
SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
|
||||
SD_DHCP_OPTION_NETBIOS_NAME_SERVER, /* 44 */
|
||||
SD_DHCP_OPTION_NETBIOS_NODE_TYPE, /* 46 */
|
||||
SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
|
||||
|
@ -247,7 +247,7 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) {
|
|||
assert_return(client, -EINVAL);
|
||||
assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY);
|
||||
|
||||
switch(option) {
|
||||
switch (option) {
|
||||
|
||||
case SD_DHCP_OPTION_PAD:
|
||||
case SD_DHCP_OPTION_OVERLOAD:
|
||||
|
@ -450,7 +450,7 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||
bool iaid_append,
|
||||
bool iaid_set,
|
||||
uint32_t iaid,
|
||||
uint16_t duid_type,
|
||||
DUIDType duid_type,
|
||||
const void *duid,
|
||||
size_t duid_len,
|
||||
usec_t llt_time) {
|
||||
|
@ -489,37 +489,20 @@ static int dhcp_client_set_iaid_duid_internal(
|
|||
client->client_id.ns.duid.type = htobe16(duid_type);
|
||||
memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len);
|
||||
len = sizeof(client->client_id.ns.duid.type) + duid_len;
|
||||
} else
|
||||
switch (duid_type) {
|
||||
case DUID_TYPE_LLT:
|
||||
if (client->mac_addr_len == 0)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set.");
|
||||
|
||||
r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m");
|
||||
break;
|
||||
case DUID_TYPE_EN:
|
||||
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m");
|
||||
break;
|
||||
case DUID_TYPE_LL:
|
||||
if (client->mac_addr_len == 0)
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set.");
|
||||
|
||||
r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m");
|
||||
break;
|
||||
case DUID_TYPE_UUID:
|
||||
r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len);
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m");
|
||||
break;
|
||||
default:
|
||||
return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type");
|
||||
}
|
||||
} else {
|
||||
r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len,
|
||||
client->arp_type, llt_time, client->test_mode,
|
||||
&client->client_id.ns.duid, &len);
|
||||
if (r == -EOPNOTSUPP)
|
||||
return log_dhcp_client_errno(client, r,
|
||||
"Failed to set %s. MAC address is not set or "
|
||||
"interface type is not supported.",
|
||||
duid_type_to_string(duid_type));
|
||||
if (r < 0)
|
||||
return log_dhcp_client_errno(client, r, "Failed to set %s: %m",
|
||||
duid_type_to_string(duid_type));
|
||||
}
|
||||
|
||||
client->client_id_len = sizeof(client->client_id.type) + len +
|
||||
(iaid_append ? sizeof(client->client_id.ns.iaid) : 0);
|
||||
|
@ -612,7 +595,6 @@ int sd_dhcp_client_set_user_class(
|
|||
sd_dhcp_client *client,
|
||||
char * const *user_class) {
|
||||
|
||||
char * const *p;
|
||||
char **s = NULL;
|
||||
|
||||
assert_return(client, -EINVAL);
|
||||
|
@ -839,7 +821,7 @@ static int client_message_init(
|
|||
|
||||
/* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
|
||||
refuse to issue an DHCP lease if 'secs' is set to zero */
|
||||
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
||||
r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(time_now >= client->start_time);
|
||||
|
@ -876,7 +858,7 @@ static int client_message_init(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len);
|
||||
r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -1264,7 +1246,7 @@ static int client_timeout_resend(
|
|||
assert(client);
|
||||
assert(client->event);
|
||||
|
||||
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
||||
r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -1312,7 +1294,7 @@ static int client_timeout_resend(
|
|||
}
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
next_timeout, 10 * USEC_PER_MSEC,
|
||||
client_timeout_resend, client,
|
||||
client->event_priority, "dhcp4-resend-timer", true);
|
||||
|
@ -1412,12 +1394,12 @@ static int client_initialize_time_events(sd_dhcp_client *client) {
|
|||
assert(client->event);
|
||||
|
||||
if (client->start_delay > 0) {
|
||||
assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0);
|
||||
assert_se(sd_event_now(client->event, CLOCK_BOOTTIME, &usec) >= 0);
|
||||
usec += client->start_delay;
|
||||
}
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
usec, 0,
|
||||
client_timeout_resend, client,
|
||||
client->event_priority, "dhcp4-resend-timer", true);
|
||||
|
@ -1458,7 +1440,7 @@ static int client_start_delayed(sd_dhcp_client *client) {
|
|||
client->fd = r;
|
||||
|
||||
if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT))
|
||||
client->start_time = now(clock_boottime_or_monotonic());
|
||||
client->start_time = now(CLOCK_BOOTTIME);
|
||||
|
||||
return client_initialize_events(client, client_receive_message_raw);
|
||||
}
|
||||
|
@ -1702,7 +1684,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now);
|
||||
r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now);
|
||||
if (r < 0)
|
||||
return r;
|
||||
assert(client->request_sent <= time_now);
|
||||
|
@ -1735,7 +1717,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
|
||||
/* arm lifetime timeout */
|
||||
r = event_reset_time(client->event, &client->timeout_expire,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
client->expire_time, 10 * USEC_PER_MSEC,
|
||||
client_timeout_expire, client,
|
||||
client->event_priority, "dhcp4-lifetime", true);
|
||||
|
@ -1751,7 +1733,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
|
||||
/* arm T2 timeout */
|
||||
r = event_reset_time(client->event, &client->timeout_t2,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
client->t2_time, 10 * USEC_PER_MSEC,
|
||||
client_timeout_t2, client,
|
||||
client->event_priority, "dhcp4-t2-timeout", true);
|
||||
|
@ -1767,7 +1749,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) {
|
|||
|
||||
/* arm T1 timeout */
|
||||
r = event_reset_time(client->event, &client->timeout_t1,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
client->t1_time, 10 * USEC_PER_MSEC,
|
||||
client_timeout_t1, client,
|
||||
client->event_priority, "dhcp4-t1-timer", true);
|
||||
|
@ -1802,7 +1784,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
|
|||
client->attempt = 0;
|
||||
|
||||
r = event_reset_time(client->event, &client->timeout_resend,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
0, 0,
|
||||
client_timeout_resend, client,
|
||||
client->event_priority, "dhcp4-resend-timer", true);
|
||||
|
|
|
@ -215,25 +215,38 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
|
|||
* The returned routes array must be freed by the caller.
|
||||
* Route objects have the same lifetime of the lease and must not be freed.
|
||||
*/
|
||||
int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) {
|
||||
sd_dhcp_route **ret;
|
||||
unsigned i;
|
||||
static int dhcp_lease_get_routes(sd_dhcp_route *routes, size_t n_routes, sd_dhcp_route ***ret) {
|
||||
assert(routes || n_routes == 0);
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(routes, -EINVAL);
|
||||
|
||||
if (lease->static_route_size <= 0)
|
||||
if (n_routes <= 0)
|
||||
return -ENODATA;
|
||||
|
||||
ret = new(sd_dhcp_route *, lease->static_route_size);
|
||||
if (!ret)
|
||||
return -ENOMEM;
|
||||
if (ret) {
|
||||
sd_dhcp_route **buf;
|
||||
|
||||
for (i = 0; i < lease->static_route_size; i++)
|
||||
ret[i] = &lease->static_route[i];
|
||||
buf = new(sd_dhcp_route*, n_routes);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
*routes = ret;
|
||||
return (int) lease->static_route_size;
|
||||
for (size_t i = 0; i < n_routes; i++)
|
||||
buf[i] = &routes[i];
|
||||
|
||||
*ret = buf;
|
||||
}
|
||||
|
||||
return (int) n_routes;
|
||||
}
|
||||
|
||||
int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
|
||||
return dhcp_lease_get_routes(lease->static_routes, lease->n_static_routes, ret);
|
||||
}
|
||||
|
||||
int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
|
||||
return dhcp_lease_get_routes(lease->classless_routes, lease->n_classless_routes, ret);
|
||||
}
|
||||
|
||||
int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
|
||||
|
@ -312,7 +325,8 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
|
|||
for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
|
||||
free(lease->servers[i].addr);
|
||||
|
||||
free(lease->static_route);
|
||||
free(lease->static_routes);
|
||||
free(lease->classless_routes);
|
||||
free(lease->client_id);
|
||||
free(lease->vendor_specific);
|
||||
strv_free(lease->search_domains);
|
||||
|
@ -467,96 +481,88 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a
|
|||
return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
|
||||
}
|
||||
|
||||
static int lease_parse_routes(
|
||||
const uint8_t *option, size_t len,
|
||||
struct sd_dhcp_route **routes, size_t *routes_size) {
|
||||
|
||||
struct in_addr addr;
|
||||
static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
|
||||
int r;
|
||||
|
||||
assert(lease);
|
||||
assert(option || len <= 0);
|
||||
assert(routes);
|
||||
assert(routes_size);
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
if (len % 8 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!GREEDY_REALLOC(*routes, *routes_size + (len / 8)))
|
||||
return -ENOMEM;
|
||||
|
||||
while (len >= 8) {
|
||||
struct sd_dhcp_route *route = *routes + *routes_size;
|
||||
int r;
|
||||
struct in_addr dst, gw;
|
||||
uint8_t prefixlen;
|
||||
|
||||
route->option = SD_DHCP_OPTION_STATIC_ROUTE;
|
||||
r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen);
|
||||
if (r < 0) {
|
||||
log_debug("Failed to determine destination prefix length from class based IP, ignoring");
|
||||
continue;
|
||||
}
|
||||
|
||||
assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0);
|
||||
route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
|
||||
assert_se(lease_parse_be32(option, 4, &dst.s_addr) >= 0);
|
||||
option += 4;
|
||||
|
||||
assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
|
||||
assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
|
||||
option += 4;
|
||||
|
||||
len -= 8;
|
||||
(*routes_size)++;
|
||||
|
||||
r = in4_addr_default_prefixlen(&dst, &prefixlen);
|
||||
if (r < 0) {
|
||||
log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
(void) in4_addr_mask(&dst, prefixlen);
|
||||
|
||||
if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) {
|
||||
.dst_addr = dst,
|
||||
.gw_addr = gw,
|
||||
.dst_prefixlen = prefixlen,
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parses RFC3442 Classless Static Route Option */
|
||||
static int lease_parse_classless_routes(
|
||||
const uint8_t *option, size_t len,
|
||||
struct sd_dhcp_route **routes, size_t *routes_size) {
|
||||
|
||||
static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
|
||||
assert(lease);
|
||||
assert(option || len <= 0);
|
||||
assert(routes);
|
||||
assert(routes_size);
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
|
||||
/* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */
|
||||
/* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */
|
||||
|
||||
while (len > 0) {
|
||||
uint8_t dst_octets;
|
||||
struct sd_dhcp_route *route;
|
||||
uint8_t prefixlen, dst_octets;
|
||||
struct in_addr dst = {}, gw;
|
||||
|
||||
if (!GREEDY_REALLOC(*routes, *routes_size + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
route = *routes + *routes_size;
|
||||
route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE;
|
||||
|
||||
dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
|
||||
route->dst_prefixlen = *option;
|
||||
prefixlen = *option;
|
||||
option++;
|
||||
len--;
|
||||
|
||||
dst_octets = DIV_ROUND_UP(prefixlen, 8);
|
||||
|
||||
/* can't have more than 4 octets in IPv4 */
|
||||
if (dst_octets > 4 || len < dst_octets)
|
||||
return -EINVAL;
|
||||
|
||||
route->dst_addr.s_addr = 0;
|
||||
memcpy(&route->dst_addr.s_addr, option, dst_octets);
|
||||
memcpy(&dst, option, dst_octets);
|
||||
option += dst_octets;
|
||||
len -= dst_octets;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0);
|
||||
assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0);
|
||||
option += 4;
|
||||
len -= 4;
|
||||
|
||||
(*routes_size)++;
|
||||
if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) {
|
||||
.dst_addr = dst,
|
||||
.gw_addr = gw,
|
||||
.dst_prefixlen = prefixlen,
|
||||
};
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -623,7 +629,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
|
||||
assert(lease);
|
||||
|
||||
switch(code) {
|
||||
switch (code) {
|
||||
|
||||
case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
|
||||
r = lease_parse_u32(option, len, &lease->lifetime, 1);
|
||||
|
@ -698,12 +704,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
break;
|
||||
|
||||
case SD_DHCP_OPTION_STATIC_ROUTE:
|
||||
r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size);
|
||||
r = lease_parse_static_routes(lease, option, len);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse static routes, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_INTERFACE_MTU:
|
||||
case SD_DHCP_OPTION_MTU_INTERFACE:
|
||||
r = lease_parse_u16(option, len, &lease->mtu, 68);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse MTU, ignoring: %m");
|
||||
|
@ -723,7 +729,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST:
|
||||
case SD_DHCP_OPTION_DOMAIN_SEARCH:
|
||||
r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m");
|
||||
|
@ -744,28 +750,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
|
|||
log_debug_errno(r, "Failed to parse root path, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_RENEWAL_T1_TIME:
|
||||
case SD_DHCP_OPTION_RENEWAL_TIME:
|
||||
r = lease_parse_u32(option, len, &lease->t1, 1);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse T1 time, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_REBINDING_T2_TIME:
|
||||
case SD_DHCP_OPTION_REBINDING_TIME:
|
||||
r = lease_parse_u32(option, len, &lease->t2, 1);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse T2 time, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
|
||||
r = lease_parse_classless_routes(
|
||||
option, len,
|
||||
&lease->static_route,
|
||||
&lease->static_route_size);
|
||||
r = lease_parse_classless_routes(lease, option, len);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse classless routes, ignoring: %m");
|
||||
break;
|
||||
|
||||
case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: {
|
||||
case SD_DHCP_OPTION_TZDB_TIMEZONE: {
|
||||
_cleanup_free_ char *tz = NULL;
|
||||
|
||||
r = lease_parse_string(option, len, &tz);
|
||||
|
@ -907,19 +910,21 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d
|
|||
pos = next_chunk;
|
||||
}
|
||||
|
||||
*domains = TAKE_PTR(names);
|
||||
strv_free_and_replace(*domains, names);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) {
|
||||
struct sd_dhcp_raw_option *cur, *option;
|
||||
struct sd_dhcp_raw_option *option, *before = NULL;
|
||||
|
||||
assert(lease);
|
||||
|
||||
LIST_FOREACH(options, cur, lease->private_options) {
|
||||
if (tag < cur->tag)
|
||||
if (tag < cur->tag) {
|
||||
before = cur;
|
||||
break;
|
||||
}
|
||||
if (tag == cur->tag) {
|
||||
log_debug("Ignoring duplicate option, tagged %i.", tag);
|
||||
return 0;
|
||||
|
@ -938,7 +943,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
LIST_INSERT_BEFORE(options, lease->private_options, cur, option);
|
||||
LIST_INSERT_BEFORE(options, lease->private_options, before, option);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -958,7 +963,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) {
|
|||
int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
||||
_cleanup_(unlink_and_freep) char *temp_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
struct sd_dhcp_raw_option *option;
|
||||
struct in_addr address;
|
||||
const struct in_addr *addresses;
|
||||
const void *client_id, *data;
|
||||
|
@ -967,7 +971,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
|||
const char *string;
|
||||
uint16_t mtu;
|
||||
_cleanup_free_ sd_dhcp_route **routes = NULL;
|
||||
char **search_domains = NULL;
|
||||
char **search_domains;
|
||||
uint32_t t1, t2, lifetime;
|
||||
int r;
|
||||
|
||||
|
@ -1066,9 +1070,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
|||
if (r >= 0)
|
||||
fprintf(f, "ROOT_PATH=%s\n", string);
|
||||
|
||||
r = sd_dhcp_lease_get_routes(lease, &routes);
|
||||
r = sd_dhcp_lease_get_static_routes(lease, &routes);
|
||||
if (r > 0)
|
||||
serialize_dhcp_routes(f, "ROUTES", routes, r);
|
||||
serialize_dhcp_routes(f, "STATIC_ROUTES", routes, r);
|
||||
|
||||
routes = mfree(routes);
|
||||
r = sd_dhcp_lease_get_classless_routes(lease, &routes);
|
||||
if (r > 0)
|
||||
serialize_dhcp_routes(f, "CLASSLESS_ROUTES", routes, r);
|
||||
|
||||
r = sd_dhcp_lease_get_timezone(lease, &string);
|
||||
if (r >= 0)
|
||||
|
@ -1116,6 +1125,18 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static char **private_options_free(char **options) {
|
||||
if (!options)
|
||||
return NULL;
|
||||
|
||||
for (unsigned i = 0; i < SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1; i++)
|
||||
free(options[i]);
|
||||
|
||||
return mfree(options);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, private_options_free);
|
||||
|
||||
int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
||||
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
|
||||
_cleanup_free_ char
|
||||
|
@ -1132,14 +1153,15 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
|||
*smtp = NULL,
|
||||
*lpr = NULL,
|
||||
*mtu = NULL,
|
||||
*routes = NULL,
|
||||
*static_routes = NULL,
|
||||
*classless_routes = NULL,
|
||||
*domains = NULL,
|
||||
*client_id_hex = NULL,
|
||||
*vendor_specific_hex = NULL,
|
||||
*lifetime = NULL,
|
||||
*t1 = NULL,
|
||||
*t2 = NULL,
|
||||
*options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {};
|
||||
*t2 = NULL;
|
||||
_cleanup_(private_options_freep) char **options = NULL;
|
||||
|
||||
int r, i;
|
||||
|
||||
|
@ -1150,6 +1172,10 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
options = new0(char*, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
|
||||
r = parse_env_file(NULL, lease_file,
|
||||
"ADDRESS", &address,
|
||||
"ROUTER", &router,
|
||||
|
@ -1168,7 +1194,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
|||
"HOSTNAME", &lease->hostname,
|
||||
"DOMAIN_SEARCH_LIST", &domains,
|
||||
"ROOT_PATH", &lease->root_path,
|
||||
"ROUTES", &routes,
|
||||
"STATIC_ROUTES", &static_routes,
|
||||
"CLASSLESS_ROUTES", &classless_routes,
|
||||
"CLIENTID", &client_id_hex,
|
||||
"TIMEZONE", &lease->timezone,
|
||||
"VENDOR_SPECIFIC", &vendor_specific_hex,
|
||||
|
@ -1315,13 +1342,22 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
|
|||
lease->search_domains = TAKE_PTR(a);
|
||||
}
|
||||
|
||||
if (routes) {
|
||||
if (static_routes) {
|
||||
r = deserialize_dhcp_routes(
|
||||
&lease->static_route,
|
||||
&lease->static_route_size,
|
||||
routes);
|
||||
&lease->static_routes,
|
||||
&lease->n_static_routes,
|
||||
static_routes);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes);
|
||||
log_debug_errno(r, "Failed to parse DHCP static routes %s, ignoring: %m", static_routes);
|
||||
}
|
||||
|
||||
if (classless_routes) {
|
||||
r = deserialize_dhcp_routes(
|
||||
&lease->classless_routes,
|
||||
&lease->n_classless_routes,
|
||||
classless_routes);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to parse DHCP classless routes %s, ignoring: %m", classless_routes);
|
||||
}
|
||||
|
||||
if (lifetime) {
|
||||
|
@ -1468,9 +1504,3 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) {
|
|||
*gateway = route->gw_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_route_get_option(sd_dhcp_route *route) {
|
||||
assert_return(route, -EINVAL);
|
||||
|
||||
return route->option;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,10 +6,21 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "dhcp6-internal.h"
|
||||
#include "dhcp6-lease-internal.h"
|
||||
#include "dhcp6-protocol.h"
|
||||
#include "strv.h"
|
||||
#include "util.h"
|
||||
|
||||
#define IRT_DEFAULT (1 * USEC_PER_DAY)
|
||||
#define IRT_MINIMUM (600 * USEC_PER_SEC)
|
||||
|
||||
static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
|
||||
assert(lease);
|
||||
|
||||
if (timestamp && triple_timestamp_is_set(timestamp))
|
||||
lease->timestamp = *timestamp;
|
||||
else
|
||||
triple_timestamp_get(&lease->timestamp);
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
|
@ -24,6 +35,68 @@ 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;
|
||||
|
||||
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));
|
||||
|
||||
LIST_FOREACH(addresses, a, lease->ia_na->addresses)
|
||||
min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid));
|
||||
}
|
||||
|
||||
if (lease->ia_pd) {
|
||||
t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1));
|
||||
t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2));
|
||||
|
||||
LIST_FOREACH(addresses, a, lease->ia_pd->addresses)
|
||||
min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid));
|
||||
}
|
||||
|
||||
if (t2 == 0 || t2 > min_valid_lt) {
|
||||
/* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes,
|
||||
* then adjust lifetime with it. */
|
||||
t1 = min_valid_lt / 2;
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
|
||||
assert(lease);
|
||||
|
||||
if (server_address)
|
||||
lease->server_address = *server_address;
|
||||
else
|
||||
lease->server_address = (struct in6_addr) {};
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
@ -32,55 +105,35 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re
|
|||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
|
||||
DHCP6Address *addr;
|
||||
uint32_t valid = 0, t;
|
||||
void dhcp6_ia_clear_addresses(DHCP6IA *ia) {
|
||||
assert(ia);
|
||||
|
||||
assert_return(ia, -EINVAL);
|
||||
assert_return(expire, -EINVAL);
|
||||
LIST_FOREACH(addresses, a, ia->addresses)
|
||||
free(a);
|
||||
|
||||
LIST_FOREACH(addresses, addr, ia->addresses) {
|
||||
t = be32toh(addr->iaaddr.lifetime_valid);
|
||||
if (valid < t)
|
||||
valid = t;
|
||||
}
|
||||
|
||||
t = be32toh(ia->ia_na.lifetime_t2);
|
||||
if (t > valid)
|
||||
return -EINVAL;
|
||||
|
||||
*expire = valid - t;
|
||||
|
||||
return 0;
|
||||
ia->addresses = NULL;
|
||||
}
|
||||
|
||||
DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
|
||||
DHCP6Address *address;
|
||||
|
||||
DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) {
|
||||
if (!ia)
|
||||
return NULL;
|
||||
|
||||
while (ia->addresses) {
|
||||
address = ia->addresses;
|
||||
dhcp6_ia_clear_addresses(ia);
|
||||
|
||||
LIST_REMOVE(addresses, ia->addresses, address);
|
||||
|
||||
free(address);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return mfree(ia);
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
||||
uint8_t *clientid;
|
||||
uint8_t *clientid = NULL;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(id, -EINVAL);
|
||||
assert_return(len > 0, -EINVAL);
|
||||
assert(lease);
|
||||
assert(id || len == 0);
|
||||
|
||||
clientid = memdup(id, len);
|
||||
if (!clientid)
|
||||
return -ENOMEM;
|
||||
if (len > 0) {
|
||||
clientid = memdup(id, len);
|
||||
if (!clientid)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free_and_replace(lease->clientid, clientid);
|
||||
lease->clientid_len = len;
|
||||
|
@ -89,7 +142,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le
|
|||
}
|
||||
|
||||
int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert(lease);
|
||||
|
||||
if (!lease->clientid)
|
||||
return -ENODATA;
|
||||
|
@ -103,15 +156,16 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re
|
|||
}
|
||||
|
||||
int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
|
||||
uint8_t *serverid;
|
||||
uint8_t *serverid = NULL;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(id, -EINVAL);
|
||||
assert_return(len > 0, -EINVAL);
|
||||
assert(lease);
|
||||
assert(id || len == 0);
|
||||
|
||||
serverid = memdup(id, len);
|
||||
if (!serverid)
|
||||
return -ENOMEM;
|
||||
if (len > 0) {
|
||||
serverid = memdup(id, len);
|
||||
if (!serverid)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free_and_replace(lease->serverid, serverid);
|
||||
lease->serverid_len = len;
|
||||
|
@ -120,7 +174,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le
|
|||
}
|
||||
|
||||
int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert(lease);
|
||||
|
||||
if (!lease->serverid)
|
||||
return -ENODATA;
|
||||
|
@ -129,107 +183,99 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re
|
|||
*ret_id = lease->serverid;
|
||||
if (ret_len)
|
||||
*ret_len = lease->serverid_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert(lease);
|
||||
|
||||
lease->preference = preference;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) {
|
||||
assert_return(preference, -EINVAL);
|
||||
|
||||
if (!lease)
|
||||
return -EINVAL;
|
||||
|
||||
*preference = lease->preference;
|
||||
int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) {
|
||||
assert(lease);
|
||||
assert(ret);
|
||||
|
||||
*ret = lease->preference;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert(lease);
|
||||
|
||||
lease->rapid_commit = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(rapid_commit, -EINVAL);
|
||||
|
||||
*rapid_commit = lease->rapid_commit;
|
||||
int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) {
|
||||
assert(lease);
|
||||
assert(ret);
|
||||
|
||||
*ret = lease->rapid_commit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
|
||||
uint32_t *lifetime_preferred,
|
||||
uint32_t *lifetime_valid) {
|
||||
int sd_dhcp6_lease_get_address(
|
||||
sd_dhcp6_lease *lease,
|
||||
struct in6_addr *ret_addr,
|
||||
uint32_t *ret_lifetime_preferred,
|
||||
uint32_t *ret_lifetime_valid) {
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(lifetime_preferred, -EINVAL);
|
||||
assert_return(lifetime_valid, -EINVAL);
|
||||
|
||||
if (!lease->addr_iter)
|
||||
return -ENOMSG;
|
||||
return -ENODATA;
|
||||
|
||||
memcpy(addr, &lease->addr_iter->iaaddr.address,
|
||||
sizeof(struct in6_addr));
|
||||
*lifetime_preferred =
|
||||
be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
|
||||
*lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
|
||||
if (ret_addr)
|
||||
*ret_addr = lease->addr_iter->iaaddr.address;
|
||||
if (ret_lifetime_preferred)
|
||||
*ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred);
|
||||
if (ret_lifetime_valid)
|
||||
*ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid);
|
||||
|
||||
lease->addr_iter = lease->addr_iter->addresses_next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) {
|
||||
if (lease)
|
||||
lease->addr_iter = lease->ia.addresses;
|
||||
lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL;
|
||||
}
|
||||
|
||||
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_get_pd(
|
||||
sd_dhcp6_lease *lease,
|
||||
struct in6_addr *ret_prefix,
|
||||
uint8_t *ret_prefix_len,
|
||||
uint32_t *ret_lifetime_preferred,
|
||||
uint32_t *ret_lifetime_valid) {
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(prefix, -EINVAL);
|
||||
assert_return(prefix_len, -EINVAL);
|
||||
assert_return(lifetime_preferred, -EINVAL);
|
||||
assert_return(lifetime_valid, -EINVAL);
|
||||
|
||||
if (!lease->prefix_iter)
|
||||
return -ENOMSG;
|
||||
return -ENODATA;
|
||||
|
||||
memcpy(prefix, &lease->prefix_iter->iapdprefix.address,
|
||||
sizeof(struct in6_addr));
|
||||
*prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
||||
*lifetime_preferred =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
||||
*lifetime_valid =
|
||||
be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
||||
if (ret_prefix)
|
||||
*ret_prefix = lease->prefix_iter->iapdprefix.address;
|
||||
if (ret_prefix_len)
|
||||
*ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen;
|
||||
if (ret_lifetime_preferred)
|
||||
*ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred);
|
||||
if (ret_lifetime_valid)
|
||||
*ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid);
|
||||
|
||||
lease->prefix_iter = lease->prefix_iter->addresses_next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) {
|
||||
if (lease)
|
||||
lease->prefix_iter = lease->pd.addresses;
|
||||
lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL;
|
||||
}
|
||||
|
||||
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
assert(lease);
|
||||
assert(optval || optlen == 0);
|
||||
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
@ -241,7 +287,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
|
|||
assert_return(lease, -EINVAL);
|
||||
|
||||
if (!lease->dns)
|
||||
return -ENOENT;
|
||||
return -ENODATA;
|
||||
|
||||
if (ret)
|
||||
*ret = lease->dns;
|
||||
|
@ -253,8 +299,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
|
|||
_cleanup_strv_free_ char **domains = NULL;
|
||||
int r;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
assert(lease);
|
||||
assert(optval || optlen == 0);
|
||||
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
@ -271,7 +317,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
|||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!lease->domains)
|
||||
return -ENOENT;
|
||||
return -ENODATA;
|
||||
|
||||
*ret = lease->domains;
|
||||
return strv_length(lease->domains);
|
||||
|
@ -280,8 +326,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
|
|||
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
assert(lease);
|
||||
assert(optval || optlen == 0);
|
||||
|
||||
for (size_t offset = 0; offset < optlen;) {
|
||||
const uint8_t *subval;
|
||||
|
@ -292,11 +338,11 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(subopt) {
|
||||
switch (subopt) {
|
||||
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
|
||||
case DHCP6_NTP_SUBOPTION_MC_ADDR:
|
||||
if (sublen != 16)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
||||
if (r < 0)
|
||||
|
@ -326,8 +372,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
|
|||
}
|
||||
|
||||
int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
assert(lease);
|
||||
assert(optval || optlen == 0);
|
||||
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
@ -352,14 +398,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
|
|||
return lease->sntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
|
||||
if (!lease->ntp_fqdn)
|
||||
return -ENOENT;
|
||||
return -ENODATA;
|
||||
|
||||
if (ret)
|
||||
*ret = lease->ntp_fqdn;
|
||||
|
@ -367,11 +413,14 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
|
|||
}
|
||||
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
char *fqdn;
|
||||
int r;
|
||||
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(optval, -EINVAL);
|
||||
assert(lease);
|
||||
assert(optval || optlen == 0);
|
||||
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
if (optlen < 2)
|
||||
return -ENODATA;
|
||||
|
@ -390,20 +439,222 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
|
|||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!lease->fqdn)
|
||||
return -ENOENT;
|
||||
return -ENODATA;
|
||||
|
||||
*ret = lease->fqdn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dhcp6_lease_parse_message(
|
||||
sd_dhcp6_client *client,
|
||||
sd_dhcp6_lease *lease,
|
||||
const DHCP6Message *message,
|
||||
size_t len) {
|
||||
|
||||
usec_t irt = IRT_DEFAULT;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(lease);
|
||||
assert(message);
|
||||
assert(len >= sizeof(DHCP6Message));
|
||||
|
||||
len -= sizeof(DHCP6Message);
|
||||
for (size_t offset = 0; offset < len;) {
|
||||
uint16_t optcode;
|
||||
size_t optlen;
|
||||
const uint8_t *optval;
|
||||
|
||||
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
r = dhcp6_lease_set_clientid(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_SERVERID:
|
||||
if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
r = dhcp6_lease_set_serverid(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_PREFERENCE:
|
||||
if (optlen != 1)
|
||||
return -EINVAL;
|
||||
|
||||
r = dhcp6_lease_set_preference(lease, optval[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE: {
|
||||
_cleanup_free_ char *msg = NULL;
|
||||
|
||||
r = dhcp6_option_parse_status(optval, optlen, &msg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (r > 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"Received %s message with non-zero status: %s%s%s",
|
||||
dhcp6_message_type_to_string(message->type),
|
||||
strempty(msg), isempty(msg) ? "" : ": ",
|
||||
dhcp6_message_status_to_string(r));
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_NA: {
|
||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
||||
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lease->ia_na) {
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
dhcp6_ia_free(lease->ia_na);
|
||||
lease->ia_na = TAKE_PTR(ia);
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_IA_PD: {
|
||||
_cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
|
||||
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
|
||||
break;
|
||||
}
|
||||
|
||||
r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
if (lease->ia_pd) {
|
||||
log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
dhcp6_ia_free(lease->ia_pd);
|
||||
lease->ia_pd = TAKE_PTR(ia);
|
||||
break;
|
||||
}
|
||||
case SD_DHCP6_OPTION_RAPID_COMMIT:
|
||||
r = dhcp6_lease_set_rapid_commit(lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_DNS_SERVERS:
|
||||
r = dhcp6_lease_add_dns(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_DOMAIN_LIST:
|
||||
r = dhcp6_lease_add_domains(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_NTP_SERVER:
|
||||
r = dhcp6_lease_add_ntp(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_SNTP_SERVERS:
|
||||
r = dhcp6_lease_add_sntp(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_CLIENT_FQDN:
|
||||
r = dhcp6_lease_set_fqdn(lease, optval, optlen);
|
||||
if (r < 0)
|
||||
log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
|
||||
if (optlen != 4)
|
||||
return -EINVAL;
|
||||
|
||||
irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t *clientid;
|
||||
size_t clientid_len;
|
||||
if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"%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)
|
||||
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));
|
||||
|
||||
if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
|
||||
client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
|
||||
log_dhcp6_client(client, "New information request will be refused in %s.",
|
||||
FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC));
|
||||
|
||||
} else {
|
||||
r = dhcp6_lease_get_serverid(lease, NULL, NULL);
|
||||
if (r < 0)
|
||||
return log_dhcp6_client_errno(client, r, "%s has no server id",
|
||||
dhcp6_message_type_to_string(message->type));
|
||||
|
||||
if (!lease->ia_na && !lease->ia_pd)
|
||||
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
|
||||
"No IA_PD prefix or IA_NA address received. Ignoring.");
|
||||
|
||||
dhcp6_lease_set_lifetime(lease);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
|
||||
if (!lease)
|
||||
return NULL;
|
||||
|
||||
free(lease->clientid);
|
||||
free(lease->serverid);
|
||||
dhcp6_lease_free_ia(&lease->ia);
|
||||
dhcp6_lease_free_ia(&lease->pd);
|
||||
dhcp6_ia_free(lease->ia_na);
|
||||
dhcp6_ia_free(lease->ia_pd);
|
||||
free(lease->dns);
|
||||
free(lease->fqdn);
|
||||
strv_free(lease->domains);
|
||||
|
@ -419,14 +670,47 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free);
|
|||
int dhcp6_lease_new(sd_dhcp6_lease **ret) {
|
||||
sd_dhcp6_lease *lease;
|
||||
|
||||
lease = new0(sd_dhcp6_lease, 1);
|
||||
assert(ret);
|
||||
|
||||
lease = new(sd_dhcp6_lease, 1);
|
||||
if (!lease)
|
||||
return -ENOMEM;
|
||||
|
||||
lease->n_ref = 1;
|
||||
|
||||
LIST_HEAD_INIT(lease->ia.addresses);
|
||||
*lease = (sd_dhcp6_lease) {
|
||||
.n_ref = 1,
|
||||
};
|
||||
|
||||
*ret = lease;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_new_from_message(
|
||||
sd_dhcp6_client *client,
|
||||
const DHCP6Message *message,
|
||||
size_t len,
|
||||
const triple_timestamp *timestamp,
|
||||
const struct in6_addr *server_address,
|
||||
sd_dhcp6_lease **ret) {
|
||||
|
||||
_cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
assert(message);
|
||||
assert(len >= sizeof(DHCP6Message));
|
||||
assert(ret);
|
||||
|
||||
r = dhcp6_lease_new(&lease);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
dhcp6_lease_set_timestamp(lease, timestamp);
|
||||
dhcp6_lease_set_server_address(lease, server_address);
|
||||
|
||||
r = dhcp6_lease_parse_message(client, lease, message, len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(lease);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,614 +0,0 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
/***
|
||||
Copyright © 2014 Axis Communications AB. All rights reserved.
|
||||
***/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "sd-ipv4acd.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "arp-util.h"
|
||||
#include "ether-addr-util.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "memory-util.h"
|
||||
#include "network-common.h"
|
||||
#include "random-util.h"
|
||||
#include "siphash24.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "time-util.h"
|
||||
|
||||
/* Constants from the RFC */
|
||||
#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
|
||||
#define PROBE_NUM 3U
|
||||
#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
|
||||
#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
|
||||
#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
|
||||
#define ANNOUNCE_NUM 2U
|
||||
#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
|
||||
#define MAX_CONFLICTS 10U
|
||||
#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
|
||||
#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
|
||||
|
||||
typedef enum IPv4ACDState {
|
||||
IPV4ACD_STATE_INIT,
|
||||
IPV4ACD_STATE_STARTED,
|
||||
IPV4ACD_STATE_WAITING_PROBE,
|
||||
IPV4ACD_STATE_PROBING,
|
||||
IPV4ACD_STATE_WAITING_ANNOUNCE,
|
||||
IPV4ACD_STATE_ANNOUNCING,
|
||||
IPV4ACD_STATE_RUNNING,
|
||||
_IPV4ACD_STATE_MAX,
|
||||
_IPV4ACD_STATE_INVALID = -EINVAL,
|
||||
} IPv4ACDState;
|
||||
|
||||
struct sd_ipv4acd {
|
||||
unsigned n_ref;
|
||||
|
||||
IPv4ACDState state;
|
||||
int ifindex;
|
||||
int fd;
|
||||
|
||||
char *ifname;
|
||||
unsigned n_iteration;
|
||||
unsigned n_conflict;
|
||||
|
||||
sd_event_source *receive_message_event_source;
|
||||
sd_event_source *timer_event_source;
|
||||
|
||||
usec_t defend_window;
|
||||
struct in_addr address;
|
||||
|
||||
/* External */
|
||||
struct ether_addr mac_addr;
|
||||
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
sd_ipv4acd_callback_t callback;
|
||||
void *userdata;
|
||||
sd_ipv4acd_check_mac_callback_t check_mac_callback;
|
||||
void *check_mac_userdata;
|
||||
};
|
||||
|
||||
#define log_ipv4acd_errno(acd, error, fmt, ...) \
|
||||
log_interface_prefix_full_errno( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd, acd, \
|
||||
error, fmt, ##__VA_ARGS__)
|
||||
#define log_ipv4acd(acd, fmt, ...) \
|
||||
log_interface_prefix_full_errno_zerook( \
|
||||
"IPv4ACD: ", \
|
||||
sd_ipv4acd, acd, \
|
||||
0, fmt, ##__VA_ARGS__)
|
||||
|
||||
static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
|
||||
[IPV4ACD_STATE_INIT] = "init",
|
||||
[IPV4ACD_STATE_STARTED] = "started",
|
||||
[IPV4ACD_STATE_WAITING_PROBE] = "waiting-probe",
|
||||
[IPV4ACD_STATE_PROBING] = "probing",
|
||||
[IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce",
|
||||
[IPV4ACD_STATE_ANNOUNCING] = "announcing",
|
||||
[IPV4ACD_STATE_RUNNING] = "running",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
|
||||
|
||||
static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
|
||||
assert(acd);
|
||||
assert(st < _IPV4ACD_STATE_MAX);
|
||||
|
||||
if (st != acd->state)
|
||||
log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
|
||||
|
||||
if (st == acd->state && !reset_counter)
|
||||
acd->n_iteration++;
|
||||
else {
|
||||
acd->state = st;
|
||||
acd->n_iteration = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ipv4acd_reset(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
(void) event_source_disable(acd->timer_event_source);
|
||||
acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
|
||||
|
||||
acd->fd = safe_close(acd->fd);
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
|
||||
}
|
||||
|
||||
static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
sd_event_source_unref(acd->timer_event_source);
|
||||
sd_ipv4acd_detach_event(acd);
|
||||
free(acd->ifname);
|
||||
return mfree(acd);
|
||||
}
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
|
||||
|
||||
int sd_ipv4acd_new(sd_ipv4acd **ret) {
|
||||
_cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
|
||||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
acd = new(sd_ipv4acd, 1);
|
||||
if (!acd)
|
||||
return -ENOMEM;
|
||||
|
||||
*acd = (sd_ipv4acd) {
|
||||
.n_ref = 1,
|
||||
.state = IPV4ACD_STATE_INIT,
|
||||
.ifindex = -1,
|
||||
.fd = -1,
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(acd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
|
||||
assert(acd);
|
||||
|
||||
if (!acd->callback)
|
||||
return;
|
||||
|
||||
acd->callback(acd, event, acd->userdata);
|
||||
}
|
||||
|
||||
int sd_ipv4acd_stop(sd_ipv4acd *acd) {
|
||||
IPv4ACDState old_state;
|
||||
|
||||
if (!acd)
|
||||
return 0;
|
||||
|
||||
old_state = acd->state;
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
|
||||
if (old_state == IPV4ACD_STATE_INIT)
|
||||
return 0;
|
||||
|
||||
log_ipv4acd(acd, "STOPPED");
|
||||
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
|
||||
|
||||
static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
|
||||
usec_t next_timeout, time_now;
|
||||
|
||||
assert(acd);
|
||||
|
||||
next_timeout = usec;
|
||||
|
||||
if (random_usec > 0)
|
||||
next_timeout += (usec_t) random_u64() % random_usec;
|
||||
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
|
||||
|
||||
return event_reset_time(acd->event, &acd->timer_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
time_now + next_timeout, 0,
|
||||
ipv4acd_on_timeout, acd,
|
||||
acd->event_priority, "ipv4acd-timer", true);
|
||||
}
|
||||
|
||||
static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
|
||||
sd_ipv4acd *acd = userdata;
|
||||
int r = 0;
|
||||
|
||||
assert(acd);
|
||||
|
||||
switch (acd->state) {
|
||||
|
||||
case IPV4ACD_STATE_STARTED:
|
||||
acd->defend_window = 0;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
|
||||
|
||||
if (acd->n_conflict >= MAX_CONFLICTS) {
|
||||
log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
|
||||
FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
|
||||
r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
|
||||
} else
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
/* Send a probe */
|
||||
r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
if (acd->n_iteration < PROBE_NUM - 2) {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
} else {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
|
||||
break;
|
||||
}
|
||||
|
||||
_fallthrough_;
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
/* Send announcement packet */
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
if (acd->n_iteration == 0) {
|
||||
acd->n_conflict = 0;
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
sd_ipv4acd_stop(acd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
|
||||
assert(acd);
|
||||
assert(arp);
|
||||
|
||||
/* RFC 5227 section 2.1.1.
|
||||
* "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
|
||||
* being performed, where the packet's 'sender IP address' is the address being probed for,
|
||||
* then the host MUST treat this address as being in use by some other host" */
|
||||
if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
|
||||
return true;
|
||||
|
||||
if (announced)
|
||||
/* the TPA matched instead of SPA, this is not a conflict */
|
||||
return false;
|
||||
|
||||
/* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
|
||||
* the packet's 'sender hardware address' is not the hardware address of any of the host's
|
||||
* interfaces, then the host SHOULD similarly treat this as an address conflict" */
|
||||
if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
|
||||
return false; /* not ARP Request, ignoring. */
|
||||
if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
|
||||
return false; /* not ARP Probe, ignoring. */
|
||||
if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
|
||||
return false; /* target IP address does not match, BPF code is broken? */
|
||||
|
||||
if (acd->check_mac_callback &&
|
||||
acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
|
||||
/* sender hardware is one of the host's interfaces, ignoring. */
|
||||
return true;
|
||||
|
||||
return true; /* conflict! */
|
||||
}
|
||||
|
||||
static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
|
||||
assert(acd);
|
||||
|
||||
acd->n_conflict++;
|
||||
|
||||
log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
|
||||
|
||||
ipv4acd_reset(acd);
|
||||
ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
|
||||
}
|
||||
|
||||
static int ipv4acd_on_packet(
|
||||
sd_event_source *s,
|
||||
int fd,
|
||||
uint32_t revents,
|
||||
void *userdata) {
|
||||
|
||||
sd_ipv4acd *acd = userdata;
|
||||
struct ether_arp packet;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(acd);
|
||||
assert(fd >= 0);
|
||||
|
||||
n = recv(fd, &packet, sizeof(struct ether_arp), 0);
|
||||
if (n < 0) {
|
||||
if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
|
||||
return 0;
|
||||
|
||||
log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
|
||||
goto fail;
|
||||
}
|
||||
if ((size_t) n != sizeof(struct ether_arp)) {
|
||||
log_ipv4acd(acd, "Ignoring too short ARP packet.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (acd->state) {
|
||||
|
||||
case IPV4ACD_STATE_ANNOUNCING:
|
||||
case IPV4ACD_STATE_RUNNING:
|
||||
|
||||
if (ipv4acd_arp_conflict(acd, &packet, true)) {
|
||||
usec_t ts;
|
||||
|
||||
assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
|
||||
|
||||
/* Defend address */
|
||||
if (ts > acd->defend_window) {
|
||||
acd->defend_window = ts + DEFEND_INTERVAL_USEC;
|
||||
r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
|
||||
|
||||
} else
|
||||
ipv4acd_on_conflict(acd);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPV4ACD_STATE_WAITING_PROBE:
|
||||
case IPV4ACD_STATE_PROBING:
|
||||
case IPV4ACD_STATE_WAITING_ANNOUNCE:
|
||||
if (ipv4acd_arp_conflict(acd, &packet, false))
|
||||
ipv4acd_on_conflict(acd);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
sd_ipv4acd_stop(acd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(ifindex > 0, -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
acd->ifindex = ifindex;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
|
||||
if (!acd)
|
||||
return -EINVAL;
|
||||
|
||||
return acd->ifindex;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(ifname, -EINVAL);
|
||||
|
||||
if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
|
||||
return -EINVAL;
|
||||
|
||||
return free_and_strdup(&acd->ifname, ifname);
|
||||
}
|
||||
|
||||
int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
r = get_ifname(acd->ifindex, &acd->ifname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = acd->ifname;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(addr, -EINVAL);
|
||||
assert_return(!ether_addr_is_null(addr), -EINVAL);
|
||||
|
||||
acd->mac_addr = *addr;
|
||||
|
||||
if (!sd_ipv4acd_is_running(acd))
|
||||
return 0;
|
||||
|
||||
assert(acd->fd >= 0);
|
||||
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
|
||||
if (r < 0) {
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
acd->event = sd_event_unref(acd->event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(!acd->event, -EBUSY);
|
||||
|
||||
if (event)
|
||||
acd->event = sd_event_ref(event);
|
||||
else {
|
||||
r = sd_event_default(&acd->event);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
acd->event_priority = priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
acd->callback = cb;
|
||||
acd->userdata = userdata;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
|
||||
assert_return(acd, -EINVAL);
|
||||
|
||||
acd->check_mac_callback = cb;
|
||||
acd->check_mac_userdata = userdata;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
assert_return(in4_addr_is_set(address), -EINVAL);
|
||||
|
||||
if (in4_addr_equal(&acd->address, address))
|
||||
return 0;
|
||||
|
||||
acd->address = *address;
|
||||
|
||||
if (!sd_ipv4acd_is_running(acd))
|
||||
return 0;
|
||||
|
||||
assert(acd->fd >= 0);
|
||||
r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(address, -EINVAL);
|
||||
|
||||
*address = acd->address;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
|
||||
assert_return(acd, false);
|
||||
|
||||
return acd->state != IPV4ACD_STATE_INIT;
|
||||
}
|
||||
|
||||
int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
|
||||
int r;
|
||||
|
||||
assert_return(acd, -EINVAL);
|
||||
assert_return(acd->event, -EINVAL);
|
||||
assert_return(acd->ifindex > 0, -EINVAL);
|
||||
assert_return(in4_addr_is_set(&acd->address), -EINVAL);
|
||||
assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
|
||||
assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
|
||||
|
||||
r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
CLOSE_AND_REPLACE(acd->fd, r);
|
||||
|
||||
if (reset_conflicts)
|
||||
acd->n_conflict = 0;
|
||||
|
||||
r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
(void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
|
||||
|
||||
r = ipv4acd_set_next_wakeup(acd, 0, 0);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
ipv4acd_reset(acd);
|
||||
return r;
|
||||
}
|
|
@ -69,7 +69,7 @@ static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) {
|
|||
goto remove_one;
|
||||
|
||||
if (t == USEC_INFINITY)
|
||||
t = now(clock_boottime_or_monotonic());
|
||||
t = now(CLOCK_BOOTTIME);
|
||||
|
||||
if (n->until > t)
|
||||
break;
|
||||
|
@ -448,7 +448,7 @@ static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor)
|
|||
return event_source_disable(lldp_rx->timer_event_source);
|
||||
|
||||
return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source,
|
||||
clock_boottime_or_monotonic(),
|
||||
CLOCK_BOOTTIME,
|
||||
n->until, 0,
|
||||
on_timer_event, lldp_rx,
|
||||
lldp_rx->event_priority, "lldp-rx-timer", true);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "event-source.h"
|
||||
#include "event-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "string-util.h"
|
||||
|
||||
|
@ -121,3 +122,41 @@ int event_source_is_enabled(sd_event_source *s) {
|
|||
|
||||
return sd_event_source_get_enabled(s, NULL);
|
||||
}
|
||||
|
||||
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) {
|
||||
_cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
int r;
|
||||
|
||||
assert(e);
|
||||
|
||||
/* Allocates an IO event source that gets woken up whenever the clock changes. Needs to be recreated on each event */
|
||||
|
||||
fd = time_change_fd();
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
r = sd_event_add_io(e, &s, fd, EPOLLIN, callback, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_event_source_set_io_fd_own(s, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
TAKE_FD(fd);
|
||||
|
||||
r = sd_event_source_set_description(s, "time-change");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(s);
|
||||
else {
|
||||
r = sd_event_source_set_floating(s, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,3 +29,5 @@ int event_reset_time_relative(
|
|||
bool force_reset);
|
||||
int event_source_disable(sd_event_source *s);
|
||||
int event_source_is_enabled(sd_event_source *s);
|
||||
|
||||
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
|
||||
|
|
|
@ -1920,7 +1920,6 @@ static int event_make_inode_data(
|
|||
static uint32_t inode_data_determine_mask(struct inode_data *d) {
|
||||
bool excl_unlink = true;
|
||||
uint32_t combined = 0;
|
||||
sd_event_source *s;
|
||||
|
||||
assert(d);
|
||||
|
||||
|
@ -3458,9 +3457,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
|
|||
/* The queue overran, let's pass this event to all event sources connected to this inotify
|
||||
* object */
|
||||
|
||||
HASHMAP_FOREACH(inode_data, d->inodes) {
|
||||
sd_event_source *s;
|
||||
|
||||
HASHMAP_FOREACH(inode_data, d->inodes)
|
||||
LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) {
|
||||
|
||||
if (event_source_is_offline(s))
|
||||
|
@ -3470,10 +3467,8 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
struct inode_data *inode_data;
|
||||
sd_event_source *s;
|
||||
|
||||
/* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from
|
||||
* our watch descriptor table. */
|
||||
|
@ -3521,7 +3516,6 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) {
|
|||
}
|
||||
|
||||
static int process_inotify(sd_event *e) {
|
||||
struct inotify_data *d;
|
||||
int r, done = 0;
|
||||
|
||||
assert(e);
|
||||
|
@ -3906,6 +3900,7 @@ static int epoll_wait_usec(
|
|||
int msec;
|
||||
#if 0
|
||||
static bool epoll_pwait2_absent = false;
|
||||
int r;
|
||||
|
||||
/* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not.
|
||||
*
|
||||
|
@ -3914,12 +3909,10 @@ static int epoll_wait_usec(
|
|||
* https://github.com/systemd/systemd/issues/19052. */
|
||||
|
||||
if (!epoll_pwait2_absent && timeout != USEC_INFINITY) {
|
||||
struct timespec ts;
|
||||
|
||||
r = epoll_pwait2(fd,
|
||||
events,
|
||||
maxevents,
|
||||
timespec_store(&ts, timeout),
|
||||
TIMESPEC_STORE(timeout),
|
||||
NULL);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
|
@ -4320,12 +4313,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
|
|||
if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here,
|
||||
* for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for
|
||||
* the purpose of getting the time this doesn't matter. */
|
||||
if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!triple_timestamp_is_set(&e->timestamp)) {
|
||||
/* Implicitly fall back to now() if we never ran before and thus have no cached time. */
|
||||
*usec = now(clock);
|
||||
|
|
|
@ -12,29 +12,6 @@
|
|||
#include "string-util.h"
|
||||
#include "sync-util.h"
|
||||
|
||||
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) {
|
||||
unsigned n, k = 0;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
|
||||
|
||||
for (n = 0; n < 16; n++) {
|
||||
|
||||
if (IN_SET(n, 4, 6, 8, 10))
|
||||
s[k++] = '-';
|
||||
|
||||
s[k++] = hexchar(id.bytes[n] >> 4);
|
||||
s[k++] = hexchar(id.bytes[n] & 0xF);
|
||||
}
|
||||
|
||||
assert(k == 36);
|
||||
|
||||
s[k] = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool id128_is_valid(const char *s) {
|
||||
size_t i, l;
|
||||
|
||||
|
@ -153,13 +130,13 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) {
|
|||
assert(f < _ID128_FORMAT_MAX);
|
||||
|
||||
if (f != ID128_UUID) {
|
||||
sd_id128_to_string(id, buffer);
|
||||
buffer[32] = '\n';
|
||||
sz = 33;
|
||||
assert_se(sd_id128_to_string(id, buffer));
|
||||
buffer[SD_ID128_STRING_MAX - 1] = '\n';
|
||||
sz = SD_ID128_STRING_MAX;
|
||||
} else {
|
||||
id128_to_uuid_string(id, buffer);
|
||||
buffer[36] = '\n';
|
||||
sz = 37;
|
||||
assert_se(sd_id128_to_uuid_string(id, buffer));
|
||||
buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n';
|
||||
sz = SD_ID128_UUID_STRING_MAX;
|
||||
}
|
||||
|
||||
r = loop_write(fd, buffer, sz, false);
|
||||
|
@ -229,3 +206,19 @@ int id128_get_product(sd_id128_t *ret) {
|
|||
*ret = uuid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int id128_equal_string(const char *s, sd_id128_t id) {
|
||||
sd_id128_t parsed;
|
||||
int r;
|
||||
|
||||
if (!s)
|
||||
return false;
|
||||
|
||||
/* Checks if the specified string matches a valid string representation of the specified 128 bit ID/uuid */
|
||||
|
||||
r = sd_id128_from_string(s, &parsed);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_id128_equal(parsed, id);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,6 @@
|
|||
#include "hash-funcs.h"
|
||||
#include "macro.h"
|
||||
|
||||
#define ID128_UUID_STRING_MAX 37
|
||||
|
||||
char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]);
|
||||
|
||||
#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {})
|
||||
|
||||
bool id128_is_valid(const char *s) _pure_;
|
||||
|
||||
typedef enum Id128Format {
|
||||
|
@ -40,3 +34,5 @@ extern const struct hash_ops id128_hash_ops;
|
|||
sd_id128_t id128_make_v4_uuid(sd_id128_t id);
|
||||
|
||||
int id128_get_product(sd_id128_t *ret);
|
||||
|
||||
int id128_equal_string(const char *s, sd_id128_t id);
|
||||
|
|
|
@ -19,16 +19,36 @@
|
|||
#include "util.h"
|
||||
|
||||
_public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) {
|
||||
unsigned n;
|
||||
|
||||
assert_return(s, NULL);
|
||||
|
||||
for (n = 0; n < 16; n++) {
|
||||
for (size_t n = 0; n < 16; n++) {
|
||||
s[n*2] = hexchar(id.bytes[n] >> 4);
|
||||
s[n*2+1] = hexchar(id.bytes[n] & 0xF);
|
||||
}
|
||||
|
||||
s[32] = 0;
|
||||
s[SD_ID128_STRING_MAX-1] = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) {
|
||||
size_t k = 0;
|
||||
|
||||
assert_return(s, NULL);
|
||||
|
||||
/* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */
|
||||
|
||||
for (size_t n = 0; n < 16; n++) {
|
||||
|
||||
if (IN_SET(n, 4, 6, 8, 10))
|
||||
s[k++] = '-';
|
||||
|
||||
s[k++] = hexchar(id.bytes[n] >> 4);
|
||||
s[k++] = hexchar(id.bytes[n] & 0xF);
|
||||
}
|
||||
|
||||
assert(k == SD_ID128_UUID_STRING_MAX - 1);
|
||||
s[k] = 0;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -256,9 +276,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
|
|||
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
/* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be
|
||||
* fine for UUIDS. */
|
||||
r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND);
|
||||
r = genuine_random_bytes(&t, sizeof(t), 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
|
|
@ -44,67 +44,175 @@ enum {
|
|||
* The client may want to start acquiring link-local addresses. */
|
||||
};
|
||||
|
||||
/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */
|
||||
enum {
|
||||
SD_DHCP_OPTION_PAD = 0,
|
||||
SD_DHCP_OPTION_SUBNET_MASK = 1,
|
||||
SD_DHCP_OPTION_TIME_OFFSET = 2,
|
||||
SD_DHCP_OPTION_ROUTER = 3,
|
||||
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6,
|
||||
SD_DHCP_OPTION_LPR_SERVER = 9,
|
||||
SD_DHCP_OPTION_HOST_NAME = 12,
|
||||
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13,
|
||||
SD_DHCP_OPTION_DOMAIN_NAME = 15,
|
||||
SD_DHCP_OPTION_ROOT_PATH = 17,
|
||||
SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19,
|
||||
SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20,
|
||||
SD_DHCP_OPTION_POLICY_FILTER = 21,
|
||||
SD_DHCP_OPTION_INTERFACE_MDR = 22,
|
||||
SD_DHCP_OPTION_INTERFACE_TTL = 23,
|
||||
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
|
||||
SD_DHCP_OPTION_INTERFACE_MTU = 26,
|
||||
SD_DHCP_OPTION_BROADCAST = 28,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
|
||||
SD_DHCP_OPTION_STATIC_ROUTE = 33,
|
||||
SD_DHCP_OPTION_NTP_SERVER = 42,
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
|
||||
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
|
||||
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
|
||||
SD_DHCP_OPTION_OVERLOAD = 52,
|
||||
SD_DHCP_OPTION_MESSAGE_TYPE = 53,
|
||||
SD_DHCP_OPTION_SERVER_IDENTIFIER = 54,
|
||||
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
|
||||
SD_DHCP_OPTION_ERROR_MESSAGE = 56,
|
||||
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57,
|
||||
SD_DHCP_OPTION_RENEWAL_T1_TIME = 58,
|
||||
SD_DHCP_OPTION_REBINDING_T2_TIME = 59,
|
||||
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
|
||||
SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61,
|
||||
SD_DHCP_OPTION_SMTP_SERVER = 69,
|
||||
SD_DHCP_OPTION_POP3_SERVER = 70,
|
||||
SD_DHCP_OPTION_USER_CLASS = 77,
|
||||
SD_DHCP_OPTION_FQDN = 81,
|
||||
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82,
|
||||
SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
|
||||
SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
|
||||
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
|
||||
SD_DHCP_OPTION_SIP_SERVER = 120,
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
|
||||
SD_DHCP_OPTION_MUD_URL = 161,
|
||||
SD_DHCP_OPTION_6RD = 212,
|
||||
SD_DHCP_OPTION_PAD = 0, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_SUBNET_MASK = 1, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_TIME_OFFSET = 2, /* [RFC2132], deprecated by 100 and 101 */
|
||||
SD_DHCP_OPTION_ROUTER = 3, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_TIME_SERVER = 4, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NAME_SERVER = 5, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_LOG_SERVER = 7, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_QUOTES_SERVER = 8, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_LPR_SERVER = 9, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_IMPRESS_SERVER = 10, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_RLP_SERVER = 11, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_HOST_NAME = 12, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MERIT_DUMP_FILE = 14, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_DOMAIN_NAME = 15, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_SWAP_SERVER = 16, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ROOT_PATH = 17, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_EXTENSION_FILE = 18, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_FORWARD = 19, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_SOURCE_ROUTE = 20, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_POLICY_FILTER = 21, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MAX_DATAGRAM_ASSEMBLY = 22, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_DEFAULT_IP_TTL = 23, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MTU_TIMEOUT = 24, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MTU_PLATEAU = 25, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MTU_INTERFACE = 26, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MTU_SUBNET = 27, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_BROADCAST = 28, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MASK_DISCOVERY = 29, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MASK_SUPPLIER = 30, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ROUTER_DISCOVERY = 31, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ROUTER_REQUEST = 32, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_STATIC_ROUTE = 33, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_TRAILERS = 34, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ARP_TIMEOUT = 35, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ETHERNET = 36, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_DEFAULT_TCP_TTL = 37, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_KEEPALIVE_TIME = 38, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_KEEPALIVE_DATA = 39, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NIS_DOMAIN = 40, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NIS_SERVER = 41, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NTP_SERVER = 42, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NETBIOS_NAME_SERVER = 44, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NETBIOS_DIST_SERVER = 45, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NETBIOS_NODE_TYPE = 46, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NETBIOS_SCOPE = 47, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_X_WINDOW_FONT = 48, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_X_WINDOW_MANAGER = 49, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_OVERLOAD = 52, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MESSAGE_TYPE = 53, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_ERROR_MESSAGE = 56, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_RENEWAL_TIME = 58, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_REBINDING_TIME = 59, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NETWARE_IP_DOMAIN = 62, /* [RFC2242] */
|
||||
SD_DHCP_OPTION_NETWARE_IP_OPTION = 63, /* [RFC2242] */
|
||||
SD_DHCP_OPTION_NIS_DOMAIN_NAME = 64, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NIS_SERVER_ADDR = 65, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_BOOT_SERVER_NAME = 66, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_BOOT_FILENAME = 67, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_HOME_AGENT_ADDRESSES = 68, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_SMTP_SERVER = 69, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_POP3_SERVER = 70, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_NNTP_SERVER = 71, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_WWW_SERVER = 72, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_FINGER_SERVER = 73, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_IRC_SERVER = 74, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_STREETTALK_SERVER = 75, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_STDA_SERVER = 76, /* [RFC2132] */
|
||||
SD_DHCP_OPTION_USER_CLASS = 77, /* [RFC3004] */
|
||||
SD_DHCP_OPTION_DIRECTORY_AGENT = 78, /* [RFC2610] */
|
||||
SD_DHCP_OPTION_SERVICE_SCOPE = 79, /* [RFC2610] */
|
||||
SD_DHCP_OPTION_RAPID_COMMIT = 80, /* [RFC4039] */
|
||||
SD_DHCP_OPTION_FQDN = 81, /* [RFC4702] */
|
||||
SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, /* [RFC3046] */
|
||||
SD_DHCP_OPTION_ISNS = 83, /* [RFC4174] */
|
||||
/* option code 84 is unassigned [RFC3679] */
|
||||
SD_DHCP_OPTION_NDS_SERVER = 85, /* [RFC2241] */
|
||||
SD_DHCP_OPTION_NDS_TREE_NAME = 86, /* [RFC2241] */
|
||||
SD_DHCP_OPTION_NDS_CONTEXT = 87, /* [RFC2241] */
|
||||
SD_DHCP_OPTION_BCMCS_CONTROLLER_DOMAIN_NAM = 88, /* [RFC4280] */
|
||||
SD_DHCP_OPTION_BCMCS_CONTROLLER_ADDRESS = 89, /* [RFC4280] */
|
||||
SD_DHCP_OPTION_AUTHENTICATION = 90, /* [RFC3118] */
|
||||
SD_DHCP_OPTION_CLIENT_LAST_TRANSACTION_TIME = 91, /* [RFC4388] */
|
||||
SD_DHCP_OPTION_ASSOCIATED_IP = 92, /* [RFC4388] */
|
||||
SD_DHCP_OPTION_CLIENT_SYSTEM = 93, /* [RFC4578] */
|
||||
SD_DHCP_OPTION_CLIENT_NDI = 94, /* [RFC4578] */
|
||||
SD_DHCP_OPTION_LDAP = 95, /* [RFC3679] */
|
||||
/* option code 96 is unassigned [RFC3679] */
|
||||
SD_DHCP_OPTION_UUID = 97, /* [RFC4578] */
|
||||
SD_DHCP_OPTION_USER_AUTHENTICATION = 98, /* [RFC2485] */
|
||||
SD_DHCP_OPTION_GEOCONF_CIVIC = 99, /* [RFC4776] */
|
||||
SD_DHCP_OPTION_POSIX_TIMEZONE = 100, /* [RFC4833] */
|
||||
SD_DHCP_OPTION_TZDB_TIMEZONE = 101, /* [RFC4833] */
|
||||
/* option codes 102-107 are unassigned [RFC3679] */
|
||||
SD_DHCP_OPTION_IPV6_ONLY_PREFERRED = 108, /* [RFC8925] */
|
||||
SD_DHCP_OPTION_DHCP4O6_SOURCE_ADDRESS = 109, /* [RFC8539] */
|
||||
/* option codes 110-111 are unassigned [RFC3679] */
|
||||
SD_DHCP_OPTION_NETINFO_ADDRESS = 112, /* [RFC3679] */
|
||||
SD_DHCP_OPTION_NETINFO_TAG = 113, /* [RFC3679] */
|
||||
SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL = 114, /* [RFC8910] */
|
||||
/* option code 115 is unassigned [RFC3679] */
|
||||
SD_DHCP_OPTION_AUTO_CONFIG = 116, /* [RFC2563] */
|
||||
SD_DHCP_OPTION_NAME_SERVICE_SEARCH = 117, /* [RFC2937] */
|
||||
SD_DHCP_OPTION_SUBNET_SELECTION = 118, /* [RFC3011] */
|
||||
SD_DHCP_OPTION_DOMAIN_SEARCH = 119, /* [RFC3397] */
|
||||
SD_DHCP_OPTION_SIP_SERVER = 120, /* [RFC3361] */
|
||||
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, /* [RFC3442] */
|
||||
SD_DHCP_OPTION_CABLELABS_CLIENT_CONFIGURATION = 122, /* [RFC3495] */
|
||||
SD_DHCP_OPTION_GEOCONF = 123, /* [RFC6225] */
|
||||
SD_DHCP_OPTION_VENDOR_CLASS = 124, /* [RFC3925] */
|
||||
SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION = 125, /* [RFC3925] */
|
||||
/* option codes 126-127 are unassigned [RFC3679] */
|
||||
/* option codes 128-135 are assigned to use by PXE, but they are vendor specific [RFC4578] */
|
||||
SD_DHCP_OPTION_PANA_AGENT = 136, /* [RFC5192] */
|
||||
SD_DHCP_OPTION_LOST_SERVER_FQDN = 137, /* [RFC5223] */
|
||||
SD_DHCP_OPTION_CAPWAP_AC_ADDRESS = 138, /* [RFC5417] */
|
||||
SD_DHCP_OPTION_MOS_ADDRESS = 139, /* [RFC5678] */
|
||||
SD_DHCP_OPTION_MOS_FQDN = 140, /* [RFC5678] */
|
||||
SD_DHCP_OPTION_SIP_SERVICE_DOMAINS = 141, /* [RFC6011] */
|
||||
SD_DHCP_OPTION_ANDSF_ADDRESS = 142, /* [RFC6153] */
|
||||
SD_DHCP_OPTION_SZTP_REDIRECT = 143, /* [RFC8572] */
|
||||
SD_DHCP_OPTION_GEOLOC = 144, /* [RFC6225] */
|
||||
SD_DHCP_OPTION_FORCERENEW_NONCE_CAPABLE = 145, /* [RFC6704] */
|
||||
SD_DHCP_OPTION_RDNSS_SELECTION = 146, /* [RFC6731] */
|
||||
SD_DHCP_OPTION_DOTS_RI = 147, /* [RFC8973] */
|
||||
SD_DHCP_OPTION_DOTS_ADDRESS = 148, /* [RFC8973] */
|
||||
/* option code 149 is unassigned [RFC3942] */
|
||||
SD_DHCP_OPTION_TFTP_SERVER_ADDRESS = 150, /* [RFC5859] */
|
||||
SD_DHCP_OPTION_STATUS_CODE = 151, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_BASE_TIME = 152, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_START_TIME_OF_STATE = 153, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_QUERY_START_TIME = 154, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_QUERY_END_TIME = 155, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_DHCP_STATE = 156, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_DATA_SOURCE = 157, /* [RFC6926] */
|
||||
SD_DHCP_OPTION_PCP_SERVER = 158, /* [RFC7291] */
|
||||
SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */
|
||||
/* option code 160 is unassigned [RFC7710][RFC8910] */
|
||||
SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */
|
||||
/* option codes 162-174 are unassigned [RFC3942] */
|
||||
/* option codes 175-177 are temporary assigned. */
|
||||
/* option codes 178-207 are unassigned [RFC3942] */
|
||||
SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */
|
||||
SD_DHCP_OPTION_CONFIGURATION_FILE = 209, /* [RFC5071] */
|
||||
SD_DHCP_OPTION_PATH_PREFIX = 210, /* [RFC5071] */
|
||||
SD_DHCP_OPTION_REBOOT_TIME = 211, /* [RFC5071] */
|
||||
SD_DHCP_OPTION_6RD = 212, /* [RFC5969] */
|
||||
SD_DHCP_OPTION_ACCESS_DOMAIN = 213, /* [RFC5986] */
|
||||
/* option codes 214-219 are unassigned */
|
||||
SD_DHCP_OPTION_SUBNET_ALLOCATION = 220, /* [RFC6656] */
|
||||
SD_DHCP_OPTION_VIRTUAL_SUBNET_SELECTION = 221, /* [RFC6607] */
|
||||
/* option codes 222-223 are unassigned [RFC3942] */
|
||||
/* option codes 224-254 are reserved for private use */
|
||||
SD_DHCP_OPTION_PRIVATE_BASE = 224,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
|
||||
/* Windows 10 option to send when Anonymize=true */
|
||||
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
|
||||
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, /* [RFC7844] */
|
||||
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, /* [RFC7844] */
|
||||
SD_DHCP_OPTION_PRIVATE_LAST = 254,
|
||||
SD_DHCP_OPTION_END = 255,
|
||||
SD_DHCP_OPTION_END = 255, /* [RFC2132] */
|
||||
};
|
||||
|
||||
/* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */
|
||||
|
|
|
@ -67,7 +67,8 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
|
|||
int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
|
||||
int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
|
||||
int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
|
||||
int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
|
||||
int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
||||
int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret);
|
||||
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
|
||||
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
|
||||
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
|
||||
|
@ -82,7 +83,6 @@ int sd_dhcp_lease_get_6rd(
|
|||
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
|
||||
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
|
||||
int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway);
|
||||
int sd_dhcp_route_get_option(sd_dhcp_route *route);
|
||||
|
||||
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref);
|
||||
|
||||
|
|
|
@ -81,8 +81,8 @@ enum {
|
|||
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_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */
|
||||
SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */
|
||||
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 */
|
||||
|
@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class(
|
|||
int sd_dhcp6_client_set_prefix_delegation_hint(
|
||||
sd_dhcp6_client *client,
|
||||
uint8_t prefixlen,
|
||||
const struct in6_addr *pd_address);
|
||||
const struct in6_addr *pd_prefix);
|
||||
int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client,
|
||||
int *delegation);
|
||||
int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client,
|
||||
|
@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
|
|||
int *request);
|
||||
int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
|
||||
int request);
|
||||
int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
|
||||
uint32_t transaction_id);
|
||||
int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
|
||||
sd_dhcp6_option *v);
|
||||
|
||||
|
|
|
@ -35,11 +35,14 @@ union sd_id128 {
|
|||
};
|
||||
|
||||
#define SD_ID128_STRING_MAX 33U
|
||||
#define SD_ID128_UUID_STRING_MAX 37U
|
||||
|
||||
char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
|
||||
char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]);
|
||||
int sd_id128_from_string(const char *s, sd_id128_t *ret);
|
||||
|
||||
#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
|
||||
#define SD_ID128_TO_UUID_STRING(id) sd_id128_to_uuid_string((id), (char[SD_ID128_UUID_STRING_MAX]) {})
|
||||
|
||||
int sd_id128_randomize(sd_id128_t *ret);
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#endif
|
||||
|
||||
typedef void (*free_func_t)(void *p);
|
||||
typedef void* (*mfree_func_t)(void *p);
|
||||
|
||||
/* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than
|
||||
* proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */
|
||||
|
@ -173,13 +174,23 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
|
|||
* is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the
|
||||
* object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of
|
||||
* malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by
|
||||
* both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the
|
||||
* size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory,
|
||||
* too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
|
||||
* both the compiler's and libc's standards. Note that _FORTIFY_SOURCES=3 handles also dynamically allocated
|
||||
* objects and thus it's safer using __builtin_dynamic_object_size if _FORTIFY_SOURCES=3 is used (#22801).
|
||||
* Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and
|
||||
* __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner
|
||||
* case. */
|
||||
|
||||
#if defined __has_builtin
|
||||
# if __has_builtin(__builtin_dynamic_object_size)
|
||||
# define MALLOC_SIZEOF_SAFE(x) \
|
||||
MIN(malloc_usable_size(x), __builtin_dynamic_object_size(x, 0))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef MALLOC_SIZEOF_SAFE
|
||||
#define MALLOC_SIZEOF_SAFE(x) \
|
||||
MIN(malloc_usable_size(x), __builtin_object_size(x, 0))
|
||||
#endif
|
||||
|
||||
/* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items
|
||||
* that fit into the specified memory block */
|
||||
|
|
|
@ -205,6 +205,8 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
|
|||
|
||||
int cg_rmdir(const char *controller, const char *path);
|
||||
|
||||
int cg_is_threaded(const char *controller, const char *path);
|
||||
|
||||
typedef enum {
|
||||
CG_KEY_MODE_GRACEFUL = 1 << 0,
|
||||
} CGroupKeyMode;
|
||||
|
|
|
@ -16,14 +16,12 @@ static int parse_env_file_internal(
|
|||
FILE *f,
|
||||
const char *fname,
|
||||
int (*push) (const char *filename, unsigned line,
|
||||
const char *key, char *value, void *userdata, int *n_pushed),
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
const char *key, char *value, void *userdata),
|
||||
void *userdata) {
|
||||
|
||||
size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX;
|
||||
_cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
|
||||
unsigned line = 1;
|
||||
char *p;
|
||||
int r;
|
||||
|
||||
enum {
|
||||
|
@ -46,7 +44,7 @@ static int parse_env_file_internal(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (p = contents; *p; p++) {
|
||||
for (char *p = contents; *p; p++) {
|
||||
char c = *p;
|
||||
|
||||
switch (state) {
|
||||
|
@ -100,7 +98,7 @@ static int parse_env_file_internal(
|
|||
if (last_key_whitespace != SIZE_MAX)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
r = push(fname, line, key, value, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -143,7 +141,7 @@ static int parse_env_file_internal(
|
|||
if (last_key_whitespace != SIZE_MAX)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
r = push(fname, line, key, value, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -262,7 +260,7 @@ static int parse_env_file_internal(
|
|||
if (last_key_whitespace != SIZE_MAX)
|
||||
key[last_key_whitespace] = 0;
|
||||
|
||||
r = push(fname, line, key, value, userdata, n_pushed);
|
||||
r = push(fname, line, key, value, userdata);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -300,8 +298,7 @@ static int check_utf8ness_and_warn(
|
|||
static int parse_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
void *userdata) {
|
||||
|
||||
const char *k;
|
||||
va_list aq, *ap = userdata;
|
||||
|
@ -323,9 +320,6 @@ static int parse_env_file_push(
|
|||
free(*v);
|
||||
*v = value;
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -341,16 +335,13 @@ int parse_env_filev(
|
|||
const char *fname,
|
||||
va_list ap) {
|
||||
|
||||
int r, n_pushed = 0;
|
||||
int r;
|
||||
va_list aq;
|
||||
|
||||
va_copy(aq, ap);
|
||||
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
|
||||
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq);
|
||||
va_end(aq);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return n_pushed;
|
||||
return r;
|
||||
}
|
||||
|
||||
int parse_env_file_sentinel(
|
||||
|
@ -371,8 +362,7 @@ int parse_env_file_sentinel(
|
|||
static int load_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
void *userdata) {
|
||||
char ***m = userdata;
|
||||
char *p;
|
||||
int r;
|
||||
|
@ -389,78 +379,69 @@ static int load_env_file_push(
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
free(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_env_file(FILE *f, const char *fname, char ***rl) {
|
||||
char **m = NULL;
|
||||
_cleanup_strv_free_ char **m = NULL;
|
||||
int r;
|
||||
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
|
||||
if (r < 0) {
|
||||
strv_free(m);
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*rl = m;
|
||||
*rl = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_env_file_push_pairs(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
char ***m = userdata;
|
||||
void *userdata) {
|
||||
|
||||
char ***m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
r = check_utf8ness_and_warn(filename, line, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Check if the key is present */
|
||||
for (char **t = *m; t && *t; t += 2)
|
||||
if (streq(t[0], key)) {
|
||||
if (value)
|
||||
return free_and_replace(t[1], value);
|
||||
else
|
||||
return free_and_strdup(t+1, "");
|
||||
}
|
||||
|
||||
r = strv_extend(m, key);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
return r;
|
||||
|
||||
if (!value) {
|
||||
r = strv_extend(m, "");
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
r = strv_push(m, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (n_pushed)
|
||||
(*n_pushed)++;
|
||||
|
||||
return 0;
|
||||
if (value)
|
||||
return strv_push(m, value);
|
||||
else
|
||||
return strv_extend(m, "");
|
||||
}
|
||||
|
||||
int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
|
||||
char **m = NULL;
|
||||
_cleanup_strv_free_ char **m = NULL;
|
||||
int r;
|
||||
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
|
||||
if (r < 0) {
|
||||
strv_free(m);
|
||||
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*rl = m;
|
||||
*rl = TAKE_PTR(m);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int merge_env_file_push(
|
||||
const char *filename, unsigned line,
|
||||
const char *key, char *value,
|
||||
void *userdata,
|
||||
int *n_pushed) {
|
||||
void *userdata) {
|
||||
|
||||
char ***env = userdata;
|
||||
char *expanded_value;
|
||||
|
@ -489,7 +470,7 @@ static int merge_env_file_push(
|
|||
|
||||
log_debug("%s:%u: setting %s=%s", filename, line, key, value);
|
||||
|
||||
return load_env_file_push(filename, line, key, value, env, n_pushed);
|
||||
return load_env_file_push(filename, line, key, value, env);
|
||||
}
|
||||
|
||||
int merge_env_file(
|
||||
|
@ -501,7 +482,7 @@ int merge_env_file(
|
|||
* plus "extended" substitutions, unlike other exported parsing functions.
|
||||
*/
|
||||
|
||||
return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
|
||||
return parse_env_file_internal(f, fname, merge_env_file_push, env);
|
||||
}
|
||||
|
||||
static void write_env_var(FILE *f, const char *v) {
|
||||
|
@ -538,7 +519,6 @@ static void write_env_var(FILE *f, const char *v) {
|
|||
int write_env_file(const char *fname, char **l) {
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
char **i;
|
||||
int r;
|
||||
|
||||
assert(fname);
|
||||
|
|
|
@ -96,8 +96,6 @@ bool env_assignment_is_valid(const char *e) {
|
|||
}
|
||||
|
||||
bool strv_env_is_valid(char **e) {
|
||||
char **p, **q;
|
||||
|
||||
STRV_FOREACH(p, e) {
|
||||
size_t k;
|
||||
|
||||
|
@ -115,8 +113,6 @@ bool strv_env_is_valid(char **e) {
|
|||
}
|
||||
|
||||
bool strv_env_name_is_valid(char **l) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
if (!env_name_is_valid(*p))
|
||||
return false;
|
||||
|
@ -129,8 +125,6 @@ bool strv_env_name_is_valid(char **l) {
|
|||
}
|
||||
|
||||
bool strv_env_name_or_assignment_is_valid(char **l) {
|
||||
char **p;
|
||||
|
||||
STRV_FOREACH(p, l) {
|
||||
if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
|
||||
return false;
|
||||
|
@ -272,7 +266,7 @@ static bool env_entry_has_name(const char *entry, const char *name) {
|
|||
|
||||
char **strv_env_delete(char **x, size_t n_lists, ...) {
|
||||
size_t n, i = 0;
|
||||
char **k, **r;
|
||||
char **r;
|
||||
va_list ap;
|
||||
|
||||
/* Deletes every entry from x that is mentioned in the other
|
||||
|
@ -287,7 +281,7 @@ char **strv_env_delete(char **x, size_t n_lists, ...) {
|
|||
STRV_FOREACH(k, x) {
|
||||
va_start(ap, n_lists);
|
||||
for (size_t v = 0; v < n_lists; v++) {
|
||||
char **l, **j;
|
||||
char **l;
|
||||
|
||||
l = va_arg(ap, char**);
|
||||
STRV_FOREACH(j, l)
|
||||
|
@ -379,7 +373,6 @@ char **strv_env_unset_many(char **l, ...) {
|
|||
|
||||
int strv_env_replace_consume(char ***l, char *p) {
|
||||
const char *t, *name;
|
||||
char **f;
|
||||
int r;
|
||||
|
||||
assert(p);
|
||||
|
@ -467,8 +460,6 @@ int strv_env_assign(char ***l, const char *key, const char *value) {
|
|||
}
|
||||
|
||||
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
|
||||
char **i;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (k <= 0)
|
||||
|
@ -496,7 +487,7 @@ char *strv_env_get(char **l, const char *name) {
|
|||
}
|
||||
|
||||
char *strv_env_pairs_get(char **l, const char *name) {
|
||||
char **key, **value, *result = NULL;
|
||||
char *result = NULL;
|
||||
|
||||
assert(name);
|
||||
|
||||
|
@ -508,7 +499,6 @@ char *strv_env_pairs_get(char **l, const char *name) {
|
|||
}
|
||||
|
||||
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
|
||||
char **p, **q;
|
||||
int k = 0;
|
||||
|
||||
STRV_FOREACH(p, e) {
|
||||
|
@ -702,7 +692,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
|
|||
}
|
||||
|
||||
char **replace_env_argv(char **argv, char **env) {
|
||||
char **ret, **i;
|
||||
char **ret;
|
||||
size_t k = 0, l = 0;
|
||||
|
||||
l = strv_length(argv);
|
||||
|
@ -832,7 +822,6 @@ int setenv_systemd_exec_pid(bool update_only) {
|
|||
int getenv_path_list(const char *name, char ***ret_paths) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
const char *e;
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
@ -868,19 +857,36 @@ int getenv_path_list(const char *name, char ***ret_paths) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int unsetenv_erase(const char *name) {
|
||||
char *p;
|
||||
int getenv_steal_erase(const char *name, char **ret) {
|
||||
_cleanup_(erase_and_freep) char *a = NULL;
|
||||
char *e;
|
||||
|
||||
assert(name);
|
||||
|
||||
p = getenv(name);
|
||||
if (!p)
|
||||
return 0;
|
||||
/* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes
|
||||
* it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for
|
||||
* testing, and given that people are likely going to misuse this, be thorough) */
|
||||
|
||||
string_erase(p);
|
||||
e = getenv(name);
|
||||
if (!e) {
|
||||
if (ret)
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
a = strdup(e);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
string_erase(e);
|
||||
|
||||
if (unsetenv(name) < 0)
|
||||
return -errno;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(a);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -69,4 +69,4 @@ int setenv_systemd_exec_pid(bool update_only);
|
|||
* PATH-like colon-separated absolute paths */
|
||||
int getenv_path_list(const char *name, char ***ret_paths);
|
||||
|
||||
int unsetenv_erase(const char *name);
|
||||
int getenv_steal_erase(const char *name, char **ret);
|
||||
|
|
|
@ -138,10 +138,18 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) {
|
|||
EPERM);
|
||||
}
|
||||
|
||||
/* Three difference errors for "not enough disk space" */
|
||||
/* Three different errors for "not enough disk space" */
|
||||
static inline bool ERRNO_IS_DISK_SPACE(int r) {
|
||||
return IN_SET(abs(r),
|
||||
ENOSPC,
|
||||
EDQUOT,
|
||||
EFBIG);
|
||||
}
|
||||
|
||||
/* Three different errors for "this device does not quite exist" */
|
||||
static inline bool ERRNO_IS_DEVICE_ABSENT(int r) {
|
||||
return IN_SET(abs(r),
|
||||
ENODEV,
|
||||
ENXIO,
|
||||
ENOENT);
|
||||
}
|
||||
|
|
|
@ -549,7 +549,6 @@ char* quote_command_line(char **argv, ShellEscapeFlags flags) {
|
|||
|
||||
assert(argv);
|
||||
|
||||
char **a;
|
||||
STRV_FOREACH(a, argv) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/btrfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/resource.h>
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include "io-util.h"
|
||||
#include "macro.h"
|
||||
#include "missing_fcntl.h"
|
||||
#include "missing_fs.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
|
@ -417,14 +419,11 @@ int same_fd(int a, int b) {
|
|||
assert(a >= 0);
|
||||
assert(b >= 0);
|
||||
|
||||
/* Compares two file descriptors. Note that semantics are
|
||||
* quite different depending on whether we have kcmp() or we
|
||||
* don't. If we have kcmp() this will only return true for
|
||||
* dup()ed file descriptors, but not otherwise. If we don't
|
||||
* have kcmp() this will also return true for two fds of the same
|
||||
* file, created by separate open() calls. Since we use this
|
||||
* call mostly for filtering out duplicates in the fd store
|
||||
* this difference hopefully doesn't matter too much. */
|
||||
/* Compares two file descriptors. Note that semantics are quite different depending on whether we
|
||||
* have kcmp() or we don't. If we have kcmp() this will only return true for dup()ed file
|
||||
* descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of
|
||||
* the same file, created by separate open() calls. Since we use this call mostly for filtering out
|
||||
* duplicates in the fd store this difference hopefully doesn't matter too much. */
|
||||
|
||||
if (a == b)
|
||||
return true;
|
||||
|
@ -436,7 +435,7 @@ int same_fd(int a, int b) {
|
|||
return true;
|
||||
if (r > 0)
|
||||
return false;
|
||||
if (!IN_SET(errno, ENOSYS, EACCES, EPERM))
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
|
||||
return -errno;
|
||||
|
||||
/* We don't have kcmp(), use fstat() instead. */
|
||||
|
@ -446,23 +445,17 @@ int same_fd(int a, int b) {
|
|||
if (fstat(b, &stb) < 0)
|
||||
return -errno;
|
||||
|
||||
if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
|
||||
if (!stat_inode_same(&sta, &stb))
|
||||
return false;
|
||||
|
||||
/* We consider all device fds different, since two device fds
|
||||
* might refer to quite different device contexts even though
|
||||
* they share the same inode and backing dev_t. */
|
||||
/* We consider all device fds different, since two device fds might refer to quite different device
|
||||
* contexts even though they share the same inode and backing dev_t. */
|
||||
|
||||
if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
|
||||
return false;
|
||||
|
||||
if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
|
||||
return false;
|
||||
|
||||
/* The fds refer to the same inode on disk, let's also check
|
||||
* if they have the same fd flags. This is useful to
|
||||
* distinguish the read and write side of a pipe created with
|
||||
* pipe(). */
|
||||
/* The fds refer to the same inode on disk, let's also check if they have the same fd flags. This is
|
||||
* useful to distinguish the read and write side of a pipe created with pipe(). */
|
||||
fa = fcntl(a, F_GETFL);
|
||||
if (fa < 0)
|
||||
return -errno;
|
||||
|
@ -797,3 +790,24 @@ int btrfs_defrag_fd(int fd) {
|
|||
|
||||
return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL));
|
||||
}
|
||||
|
||||
int fd_get_diskseq(int fd, uint64_t *ret) {
|
||||
uint64_t diskseq;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) {
|
||||
/* Note that the kernel is weird: non-existing ioctls currently return EINVAL
|
||||
* rather than ENOTTY on loopback block devices. They should fix that in the kernel,
|
||||
* but in the meantime we accept both here. */
|
||||
if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL)
|
||||
return -errno;
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
*ret = diskseq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ static inline int make_null_stdio(void) {
|
|||
int fd_reopen(int fd, int flags);
|
||||
int read_nr_open(void);
|
||||
int btrfs_defrag_fd(int fd);
|
||||
int fd_get_diskseq(int fd, uint64_t *ret);
|
||||
|
||||
/* The maximum length a buffer for a /proc/self/fd/<fd> path needs */
|
||||
#define PROC_FD_PATH_MAX \
|
||||
|
|
|
@ -1042,8 +1042,6 @@ static int search_and_fopen_internal(
|
|||
FILE **ret,
|
||||
char **ret_path) {
|
||||
|
||||
char **i;
|
||||
|
||||
assert(path);
|
||||
assert(mode);
|
||||
assert(ret);
|
||||
|
@ -1195,7 +1193,7 @@ int write_timestamp_file_atomic(const char *fn, usec_t n) {
|
|||
/* Creates a "timestamp" file, that contains nothing but a
|
||||
* usec_t timestamp, formatted in ASCII. */
|
||||
|
||||
if (n <= 0 || n >= USEC_INFINITY)
|
||||
if (!timestamp_is_set(n))
|
||||
return -ERANGE;
|
||||
|
||||
xsprintf(ln, USEC_FMT "\n", n);
|
||||
|
@ -1216,7 +1214,7 @@ int read_timestamp_file(const char *fn, usec_t *ret) {
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
|
||||
if (!timestamp_is_set(t))
|
||||
return -ERANGE;
|
||||
|
||||
*ret = (usec_t) t;
|
||||
|
|
|
@ -110,6 +110,12 @@ typedef enum ReadLineFlags {
|
|||
|
||||
int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret);
|
||||
|
||||
static inline bool file_offset_beyond_memory_size(off_t x) {
|
||||
if (x < 0) /* off_t is signed, filter that out */
|
||||
return false;
|
||||
return (uint64_t) x > (uint64_t) SIZE_MAX;
|
||||
}
|
||||
|
||||
static inline int read_line(FILE *f, size_t limit, char **ret) {
|
||||
return read_line_full(f, limit, 0, ret);
|
||||
}
|
||||
|
|
|
@ -119,7 +119,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
|
|||
size_t l = PATH_MAX;
|
||||
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *c = NULL;
|
||||
|
@ -135,7 +134,10 @@ int readlinkat_malloc(int fd, const char *p, char **ret) {
|
|||
|
||||
if ((size_t) n < l) {
|
||||
c[n] = 0;
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -570,7 +572,6 @@ int get_files_in_directory(const char *path, char ***list) {
|
|||
}
|
||||
|
||||
static int getenv_tmp_dir(const char **ret_path) {
|
||||
const char *n;
|
||||
int r, ret = 0;
|
||||
|
||||
assert(ret_path);
|
||||
|
@ -856,8 +857,7 @@ int conservative_renameat(
|
|||
if (fstat(new_fd, &new_stat) < 0)
|
||||
goto do_rename;
|
||||
|
||||
if (new_stat.st_ino == old_stat.st_ino &&
|
||||
new_stat.st_dev == old_stat.st_dev)
|
||||
if (stat_inode_same(&new_stat, &old_stat))
|
||||
goto is_same;
|
||||
|
||||
if (old_stat.st_mode != new_stat.st_mode ||
|
||||
|
@ -1084,3 +1084,50 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) {
|
|||
|
||||
return TAKE_FD(fd);
|
||||
}
|
||||
|
||||
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) {
|
||||
unsigned attempts = 7;
|
||||
int fd;
|
||||
|
||||
/* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if
|
||||
* it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will
|
||||
* shortcut to openat() otherwise */
|
||||
|
||||
if (!ret_newly_created)
|
||||
return RET_NERRNO(openat(dirfd, pathname, flags, mode));
|
||||
|
||||
if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) {
|
||||
fd = openat(dirfd, pathname, flags, mode);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
*ret_newly_created = FLAGS_SET(flags, O_CREAT);
|
||||
return fd;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
/* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */
|
||||
fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode);
|
||||
if (fd >= 0) {
|
||||
*ret_newly_created = false;
|
||||
return fd;
|
||||
}
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */
|
||||
fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode);
|
||||
if (fd >= 0) {
|
||||
*ret_newly_created = true;
|
||||
return fd;
|
||||
}
|
||||
if (errno != EEXIST)
|
||||
return -errno;
|
||||
|
||||
/* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again
|
||||
* without the two flags. But let's not spin forever, hence put a limit on things */
|
||||
|
||||
if (--attempts == 0) /* Give up eventually, somebody is playing with us */
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,3 +110,5 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size);
|
|||
int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path);
|
||||
|
||||
int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode);
|
||||
|
||||
int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created);
|
||||
|
|
|
@ -162,7 +162,7 @@ struct _packed_ indirect_storage {
|
|||
unsigned n_buckets; /* number of buckets */
|
||||
|
||||
unsigned idx_lowest_entry; /* Index below which all buckets are free.
|
||||
Makes "while(hashmap_steal_first())" loops
|
||||
Makes "while (hashmap_steal_first())" loops
|
||||
O(n) instead of O(n^2) for unordered hashmaps. */
|
||||
uint8_t _pad[3]; /* padding for the whole HashmapBase */
|
||||
/* The bitfields in HashmapBase complete the alignment of the whole thing. */
|
||||
|
@ -1864,7 +1864,6 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p
|
|||
|
||||
int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) {
|
||||
int n = 0, r;
|
||||
char **i;
|
||||
|
||||
assert(s);
|
||||
|
||||
|
|
|
@ -683,8 +683,7 @@ static int base64_append_width(
|
|||
s += indent;
|
||||
}
|
||||
|
||||
memcpy(s, x + width * line, act);
|
||||
s += act;
|
||||
s = mempcpy(s, x + width * line, act);
|
||||
*(s++) = line < lines - 1 ? '\n' : '\0';
|
||||
avail -= act;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "env-file.h"
|
||||
#include "hostname-util.h"
|
||||
#include "os-util.h"
|
||||
#include "string-util.h"
|
||||
|
@ -191,3 +192,20 @@ bool is_localhost(const char *hostname) {
|
|||
endswith_no_case(hostname, ".localhost.localdomain") ||
|
||||
endswith_no_case(hostname, ".localhost.localdomain.");
|
||||
}
|
||||
|
||||
int get_pretty_hostname(char **ret) {
|
||||
_cleanup_free_ char *n = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (isempty(n))
|
||||
return -ENXIO;
|
||||
|
||||
*ret = TAKE_PTR(n);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "env-file.h"
|
||||
#include "macro.h"
|
||||
#include "strv.h"
|
||||
|
||||
|
@ -61,6 +60,4 @@ static inline bool is_outbound_hostname(const char *hostname) {
|
|||
return STRCASE_IN_SET(hostname, "_outbound", "_outbound.");
|
||||
}
|
||||
|
||||
static inline int get_pretty_hostname(char **ret) {
|
||||
return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret);
|
||||
}
|
||||
int get_pretty_hostname(char **ret);
|
||||
|
|
|
@ -121,6 +121,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) {
|
|||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
int in_addr_is_localhost_one(int family, const union in_addr_union *u) {
|
||||
assert(u);
|
||||
|
||||
if (family == AF_INET)
|
||||
/* 127.0.0.1 */
|
||||
return be32toh(u->in.s_addr) == UINT32_C(0x7F000001);
|
||||
|
||||
if (family == AF_INET6)
|
||||
return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */
|
||||
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) {
|
||||
return a->s6_addr32[0] == 0 &&
|
||||
a->s6_addr32[1] == 0 &&
|
||||
|
|
|
@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a);
|
|||
|
||||
bool in4_addr_is_localhost(const struct in_addr *a);
|
||||
int in_addr_is_localhost(int family, const union in_addr_union *u);
|
||||
int in_addr_is_localhost_one(int family, const union in_addr_union *u);
|
||||
|
||||
bool in4_addr_is_local_multicast(const struct in_addr *a);
|
||||
bool in4_addr_is_non_local(const struct in_addr *a);
|
||||
|
|
|
@ -6,12 +6,28 @@
|
|||
#include <stddef.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1)
|
||||
|
||||
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
|
||||
for ((e) = &buffer.ev; \
|
||||
(uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \
|
||||
(e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len))
|
||||
#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \
|
||||
for (struct inotify_event \
|
||||
*start = &((buffer).ev), \
|
||||
*end = (struct inotify_event*) ((uint8_t*) start + (sz)), \
|
||||
*e = start; \
|
||||
(size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \
|
||||
((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \
|
||||
(log_full(log_level, "Received invalid inotify event, ignoring."), false)); \
|
||||
e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len))
|
||||
|
||||
#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \
|
||||
_FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ))
|
||||
|
||||
#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \
|
||||
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG)
|
||||
|
||||
#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \
|
||||
_FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING)
|
||||
|
||||
union inotify_event_buffer {
|
||||
struct inotify_event ev;
|
||||
|
|
|
@ -159,7 +159,6 @@ int pipe_eof(int fd) {
|
|||
}
|
||||
|
||||
int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
||||
struct timespec ts;
|
||||
int r;
|
||||
|
||||
assert(fds || nfds == 0);
|
||||
|
@ -167,7 +166,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) {
|
|||
if (nfds == 0)
|
||||
return 0;
|
||||
|
||||
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL);
|
||||
r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
if (r == 0)
|
||||
|
|
|
@ -136,33 +136,39 @@
|
|||
#define LIST_JUST_US(name,item) \
|
||||
(!(item)->name##_prev && !(item)->name##_next)
|
||||
|
||||
/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the
|
||||
* loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through
|
||||
* hashmap_get(). In that case, you need to explicitly cast the result. */
|
||||
#define LIST_FOREACH_WITH_NEXT(name,i,n,head) \
|
||||
for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n)
|
||||
|
||||
#define LIST_FOREACH(name,i,head) \
|
||||
for ((i) = (head); (i); (i) = (i)->name##_next)
|
||||
LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head)
|
||||
|
||||
#define LIST_FOREACH_SAFE(name,i,n,head) \
|
||||
for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n))
|
||||
#define _LIST_FOREACH_WITH_PREV(name,i,p,start) \
|
||||
for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p)
|
||||
|
||||
#define LIST_FOREACH_BACKWARDS(name,i,p) \
|
||||
for ((i) = (p); (i); (i) = (i)->name##_prev)
|
||||
#define LIST_FOREACH_BACKWARDS(name,i,start) \
|
||||
_LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start)
|
||||
|
||||
/* Iterate through all the members of the list p is included in, but skip over p */
|
||||
#define LIST_FOREACH_OTHERS(name,i,p) \
|
||||
for (({ \
|
||||
(i) = (p); \
|
||||
while ((i) && (i)->name##_prev) \
|
||||
(i) = (i)->name##_prev; \
|
||||
if ((i) == (p)) \
|
||||
(i) = (p)->name##_next; \
|
||||
}); \
|
||||
(i); \
|
||||
(i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next)
|
||||
for (typeof(*(p)) *_p = (p), *i = ({ \
|
||||
typeof(*_p) *_j = _p; \
|
||||
while (_j && _j->name##_prev) \
|
||||
_j = _j->name##_prev; \
|
||||
if (_j == _p) \
|
||||
_j = _p->name##_next; \
|
||||
_j; \
|
||||
}); \
|
||||
i; \
|
||||
i = i->name##_next == _p ? _p->name##_next : i->name##_next)
|
||||
|
||||
/* Loop starting from p->next until p->prev.
|
||||
p can be adjusted meanwhile. */
|
||||
/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */
|
||||
#define LIST_LOOP_BUT_ONE(name,i,head,p) \
|
||||
for ((i) = (p)->name##_next ? (p)->name##_next : (head); \
|
||||
(i) != (p); \
|
||||
(i) = (i)->name##_next ? (i)->name##_next : (head))
|
||||
for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \
|
||||
i != (p); \
|
||||
i = i->name##_next ? i->name##_next : (head))
|
||||
|
||||
#define LIST_IS_EMPTY(head) \
|
||||
(!(head))
|
||||
|
|
|
@ -273,9 +273,11 @@ int log_emergency_level(void);
|
|||
})
|
||||
|
||||
#if LOG_TRACE
|
||||
# define log_trace(...) log_debug(__VA_ARGS__)
|
||||
# define log_trace(...) log_debug(__VA_ARGS__)
|
||||
# define log_trace_errno(...) log_debug_errno(__VA_ARGS__)
|
||||
#else
|
||||
# define log_trace(...) do {} while (0)
|
||||
# define log_trace(...) do {} while (0)
|
||||
# define log_trace_errno(e, ...) (-ERRNO_VALUE(e))
|
||||
#endif
|
||||
|
||||
/* Structured logging */
|
||||
|
|
|
@ -309,23 +309,32 @@ static inline int __coverity_check_and_return__(int condition) {
|
|||
|
||||
#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
|
||||
|
||||
/* Returns the number of chars needed to format variables of the
|
||||
* specified type as a decimal string. Adds in extra space for a
|
||||
* negative '-' prefix (hence works correctly on signed
|
||||
* types). Includes space for the trailing NUL. */
|
||||
/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in
|
||||
* extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */
|
||||
#define DECIMAL_STR_MAX(type) \
|
||||
(2U+(sizeof(type) <= 1 ? 3U : \
|
||||
((size_t) IS_SIGNED_INTEGER_TYPE(type) + 1U + \
|
||||
(sizeof(type) <= 1 ? 3U : \
|
||||
sizeof(type) <= 2 ? 5U : \
|
||||
sizeof(type) <= 4 ? 10U : \
|
||||
sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
sizeof(type) <= 8 ? (IS_SIGNED_INTEGER_TYPE(type) ? 19U : 20U) : sizeof(int[-2*(sizeof(type) > 8)])))
|
||||
|
||||
#define DECIMAL_STR_WIDTH(x) \
|
||||
({ \
|
||||
typeof(x) _x_ = (x); \
|
||||
size_t ans = 1; \
|
||||
while ((_x_ /= 10) != 0) \
|
||||
ans++; \
|
||||
ans; \
|
||||
/* Returns the number of chars needed to format the specified integer value. It's hence more specific than
|
||||
* DECIMAL_STR_MAX() which answers the same question for all possible values of the specified type. Does
|
||||
* *not* include space for a trailing NUL. (If you wonder why we special case _x_ == 0 here: it's to trick
|
||||
* out gcc's -Wtype-limits, which would complain on comparing an unsigned type with < 0, otherwise. By
|
||||
* special-casing == 0 here first, we can use <= 0 instead of < 0 to trick out gcc.) */
|
||||
#define DECIMAL_STR_WIDTH(x) \
|
||||
({ \
|
||||
typeof(x) _x_ = (x); \
|
||||
size_t ans; \
|
||||
if (_x_ == 0) \
|
||||
ans = 1; \
|
||||
else { \
|
||||
ans = _x_ <= 0 ? 2 : 1; \
|
||||
while ((_x_ /= 10) != 0) \
|
||||
ans++; \
|
||||
} \
|
||||
ans; \
|
||||
})
|
||||
|
||||
#define SWAP_TWO(x, y) do { \
|
||||
|
@ -454,4 +463,20 @@ typedef struct {
|
|||
|
||||
assert_cc(sizeof(dummy_t) == 0);
|
||||
|
||||
/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for for
|
||||
* loops that count down from a high pointer until some base. A naive loop would implement this like this:
|
||||
*
|
||||
* for (p = end-1; p >= base; p--) …
|
||||
*
|
||||
* But this is not safe because p before the base is UB in C. With this macro the loop becomes this instead:
|
||||
*
|
||||
* for (p = PTR_SUB1(end, base); p; p = PTR_SUB1(p, base)) …
|
||||
*
|
||||
* And is free from UB! */
|
||||
#define PTR_SUB1(p, base) \
|
||||
({ \
|
||||
typeof(p) _q = (p); \
|
||||
_q && _q > (base) ? &_q[-1] : NULL; \
|
||||
})
|
||||
|
||||
#include "log.h"
|
||||
|
|
|
@ -15,7 +15,7 @@ size_t page_size(void) _pure_;
|
|||
#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
|
||||
#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
|
||||
|
||||
/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
|
||||
/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */
|
||||
static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
|
||||
if (n == 0)
|
||||
return dst;
|
||||
|
@ -23,7 +23,15 @@ static inline void *memcpy_safe(void *dst, const void *src, size_t n) {
|
|||
return memcpy(dst, src, n);
|
||||
}
|
||||
|
||||
/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */
|
||||
/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */
|
||||
static inline void *mempcpy_safe(void *dst, const void *src, size_t n) {
|
||||
if (n == 0)
|
||||
return dst;
|
||||
assert(src);
|
||||
return mempcpy(dst, src, n);
|
||||
}
|
||||
|
||||
/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */
|
||||
static inline int memcmp_safe(const void *s1, const void *s2, size_t n) {
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
|
|
@ -569,6 +569,10 @@ static inline int missing_open_tree(
|
|||
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
|
||||
#endif
|
||||
|
||||
#ifndef MOVE_MOUNT_T_EMPTY_PATH
|
||||
#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
|
||||
#endif
|
||||
|
||||
static inline int missing_move_mount(
|
||||
int from_dfd,
|
||||
const char *from_pathname,
|
||||
|
|
|
@ -58,7 +58,6 @@ int _ordered_set_put_strdup(OrderedSet **s, const char *p HASHMAP_DEBUG_PARAMS)
|
|||
|
||||
int _ordered_set_put_strdupv(OrderedSet **s, char **l HASHMAP_DEBUG_PARAMS) {
|
||||
int n = 0, r;
|
||||
char **i;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
r = _ordered_set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS);
|
||||
|
|
|
@ -18,6 +18,14 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA
|
|||
int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS);
|
||||
#define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS)
|
||||
|
||||
static inline void ordered_set_clear(OrderedSet *s) {
|
||||
return ordered_hashmap_clear((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline void ordered_set_clear_free(OrderedSet *s) {
|
||||
return ordered_hashmap_clear_free((OrderedHashmap*) s);
|
||||
}
|
||||
|
||||
static inline OrderedSet* ordered_set_free(OrderedSet *s) {
|
||||
return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s);
|
||||
}
|
||||
|
|
|
@ -415,7 +415,7 @@ int safe_atoi(const char *s, int *ret_i) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) {
|
||||
int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) {
|
||||
char *x = NULL;
|
||||
unsigned long long l;
|
||||
|
||||
|
|
|
@ -65,9 +65,9 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) {
|
|||
return safe_atoi(s, (int*) ret_i);
|
||||
}
|
||||
|
||||
int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu);
|
||||
int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu);
|
||||
|
||||
static inline int safe_atollu(const char *s, long long unsigned *ret_llu) {
|
||||
static inline int safe_atollu(const char *s, unsigned long long *ret_llu) {
|
||||
return safe_atollu_full(s, 0, ret_llu);
|
||||
}
|
||||
|
||||
|
@ -82,12 +82,12 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) {
|
|||
}
|
||||
|
||||
static inline int safe_atoux64(const char *s, uint64_t *ret) {
|
||||
assert_cc(sizeof(int64_t) == sizeof(long long unsigned));
|
||||
return safe_atollu_full(s, 16, (long long unsigned*) ret);
|
||||
assert_cc(sizeof(int64_t) == sizeof(unsigned long long));
|
||||
return safe_atollu_full(s, 16, (unsigned long long*) ret);
|
||||
}
|
||||
|
||||
#if LONG_MAX == INT_MAX
|
||||
static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) {
|
||||
static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) {
|
||||
assert_cc(sizeof(unsigned long) == sizeof(unsigned));
|
||||
return safe_atou_full(s, base, (unsigned*) ret_u);
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ static inline int safe_atozu(const char *s, size_t *ret_u) {
|
|||
}
|
||||
#else
|
||||
static inline int safe_atozu(const char *s, size_t *ret_u) {
|
||||
assert_cc(sizeof(size_t) == sizeof(long unsigned));
|
||||
assert_cc(sizeof(size_t) == sizeof(unsigned long));
|
||||
return safe_atolu(s, ret_u);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -63,7 +63,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
|
|||
}
|
||||
|
||||
int safe_getcwd(char **ret) {
|
||||
char *cwd;
|
||||
_cleanup_free_ char *cwd = NULL;
|
||||
|
||||
cwd = get_current_dir_name();
|
||||
if (!cwd)
|
||||
|
@ -71,12 +71,12 @@ int safe_getcwd(char **ret) {
|
|||
|
||||
/* Let's make sure the directory is really absolute, to protect us from the logic behind
|
||||
* CVE-2018-1000001 */
|
||||
if (cwd[0] != '/') {
|
||||
free(cwd);
|
||||
if (cwd[0] != '/')
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
*ret = cwd;
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(cwd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -202,9 +202,9 @@ int path_make_relative(const char *from, const char *to, char **ret) {
|
|||
}
|
||||
|
||||
char* path_startswith_strv(const char *p, char **set) {
|
||||
char **s, *t;
|
||||
|
||||
STRV_FOREACH(s, set) {
|
||||
char *t;
|
||||
|
||||
t = path_startswith(p, *s);
|
||||
if (t)
|
||||
return t;
|
||||
|
@ -214,7 +214,6 @@ char* path_startswith_strv(const char *p, char **set) {
|
|||
}
|
||||
|
||||
int path_strv_make_absolute_cwd(char **l) {
|
||||
char **s;
|
||||
int r;
|
||||
|
||||
/* Goes through every item in the string list and makes it
|
||||
|
@ -236,7 +235,6 @@ int path_strv_make_absolute_cwd(char **l) {
|
|||
}
|
||||
|
||||
char **path_strv_resolve(char **l, const char *root) {
|
||||
char **s;
|
||||
unsigned k = 0;
|
||||
bool enomem = false;
|
||||
int r;
|
||||
|
@ -700,12 +698,12 @@ int find_executable_full(const char *name, const char *root, char **exec_search_
|
|||
p = DEFAULT_PATH;
|
||||
|
||||
if (exec_search_path) {
|
||||
char **element;
|
||||
|
||||
STRV_FOREACH(element, exec_search_path) {
|
||||
_cleanup_free_ char *full_path = NULL;
|
||||
|
||||
if (!path_is_absolute(*element))
|
||||
continue;
|
||||
|
||||
full_path = path_join(*element, name);
|
||||
if (!full_path)
|
||||
return -ENOMEM;
|
||||
|
@ -754,7 +752,6 @@ int find_executable_full(const char *name, const char *root, char **exec_search_
|
|||
|
||||
bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) {
|
||||
bool changed = false, originally_unset;
|
||||
const char* const* i;
|
||||
|
||||
assert(timestamp);
|
||||
|
||||
|
@ -932,8 +929,9 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char **
|
|||
|
||||
static const char *skip_slash_or_dot_backward(const char *path, const char *q) {
|
||||
assert(path);
|
||||
assert(!q || q >= path);
|
||||
|
||||
for (; q >= path; q--) {
|
||||
for (; q; q = PTR_SUB1(q, path)) {
|
||||
if (*q == '/')
|
||||
continue;
|
||||
if (q > path && strneq(q - 1, "/.", 2))
|
||||
|
@ -998,7 +996,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
|
|||
q = path + strlen(path) - 1;
|
||||
|
||||
q = skip_slash_or_dot_backward(path, q);
|
||||
if ((q < path) || /* the root directory */
|
||||
if (!q || /* the root directory */
|
||||
(q == path && *q == '.')) { /* path is "." or "./" */
|
||||
if (next)
|
||||
*next = path;
|
||||
|
@ -1009,10 +1007,10 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
|
|||
|
||||
last_end = q + 1;
|
||||
|
||||
while (q >= path && *q != '/')
|
||||
q--;
|
||||
while (q && *q != '/')
|
||||
q = PTR_SUB1(q, path);
|
||||
|
||||
last_begin = q + 1;
|
||||
last_begin = q ? q + 1 : path;
|
||||
len = last_end - last_begin;
|
||||
|
||||
if (len > NAME_MAX)
|
||||
|
@ -1022,10 +1020,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char *
|
|||
|
||||
if (next) {
|
||||
q = skip_slash_or_dot_backward(path, q);
|
||||
if (q < path)
|
||||
*next = path;
|
||||
else
|
||||
*next = q + 1;
|
||||
*next = q ? q + 1 : path;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
|
@ -1240,9 +1235,10 @@ bool hidden_or_backup_file(const char *filename) {
|
|||
assert(filename);
|
||||
|
||||
if (filename[0] == '.' ||
|
||||
streq(filename, "lost+found") ||
|
||||
streq(filename, "aquota.user") ||
|
||||
streq(filename, "aquota.group") ||
|
||||
STR_IN_SET(filename,
|
||||
"lost+found",
|
||||
"aquota.user",
|
||||
"aquota.group") ||
|
||||
endswith(filename, "~"))
|
||||
return true;
|
||||
|
||||
|
@ -1339,7 +1335,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
|
|||
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
char *c, **name;
|
||||
char *c;
|
||||
|
||||
path = path_join(root, pattern);
|
||||
if (!path)
|
||||
|
@ -1411,8 +1407,6 @@ bool empty_or_root(const char *path) {
|
|||
}
|
||||
|
||||
bool path_strv_contains(char **l, const char *path) {
|
||||
char **i;
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (path_equal(*i, path))
|
||||
return true;
|
||||
|
@ -1421,10 +1415,9 @@ bool path_strv_contains(char **l, const char *path) {
|
|||
}
|
||||
|
||||
bool prefixed_path_strv_contains(char **l, const char *path) {
|
||||
char **i, *j;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
j = *i;
|
||||
const char *j = *i;
|
||||
|
||||
if (*j == '-')
|
||||
j++;
|
||||
if (*j == '+')
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <linux/oom.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -214,7 +215,6 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
|
|||
assert(!(flags & PROCESS_CMDLINE_USE_LOCALE));
|
||||
|
||||
_cleanup_strv_free_ char **args = NULL;
|
||||
char **p;
|
||||
|
||||
args = strv_parse_nulstr(t, k);
|
||||
if (!args)
|
||||
|
@ -472,37 +472,33 @@ int get_process_capeff(pid_t pid, char **ret) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static int get_process_link_contents(const char *proc_file, char **ret) {
|
||||
static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) {
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
assert(proc_file);
|
||||
assert(ret);
|
||||
|
||||
r = readlink_malloc(proc_file, ret);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
p = procfs_file_alloca(pid, proc_file);
|
||||
|
||||
return 0;
|
||||
r = readlink_malloc(p, ret);
|
||||
return r == -ENOENT ? -ESRCH : r;
|
||||
}
|
||||
|
||||
int get_process_exe(pid_t pid, char **ret) {
|
||||
const char *p;
|
||||
char *d;
|
||||
int r;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "exe");
|
||||
r = get_process_link_contents(p, ret);
|
||||
r = get_process_link_contents(pid, "exe", ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
d = endswith(*ret, " (deleted)");
|
||||
if (d)
|
||||
*d = '\0';
|
||||
if (ret) {
|
||||
d = endswith(*ret, " (deleted)");
|
||||
if (d)
|
||||
*d = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -572,28 +568,17 @@ int get_process_gid(pid_t pid, gid_t *ret) {
|
|||
}
|
||||
|
||||
int get_process_cwd(pid_t pid, char **ret) {
|
||||
const char *p;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
if (pid == 0 || pid == getpid_cached())
|
||||
return safe_getcwd(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "cwd");
|
||||
|
||||
return get_process_link_contents(p, ret);
|
||||
return get_process_link_contents(pid, "cwd", ret);
|
||||
}
|
||||
|
||||
int get_process_root(pid_t pid, char **ret) {
|
||||
const char *p;
|
||||
|
||||
assert(pid >= 0);
|
||||
assert(ret);
|
||||
|
||||
p = procfs_file_alloca(pid, "root");
|
||||
|
||||
return get_process_link_contents(p, ret);
|
||||
return get_process_link_contents(pid, "root", ret);
|
||||
}
|
||||
|
||||
#define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U)
|
||||
|
@ -645,7 +630,7 @@ int get_process_environ(pid_t pid, char **ret) {
|
|||
|
||||
int get_process_ppid(pid_t pid, pid_t *ret) {
|
||||
_cleanup_free_ char *line = NULL;
|
||||
long unsigned ppid;
|
||||
unsigned long ppid;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
|
@ -688,7 +673,7 @@ int get_process_ppid(pid_t pid, pid_t *ret) {
|
|||
if (ppid == 0)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid)
|
||||
if ((pid_t) ppid < 0 || (unsigned long) (pid_t) ppid != ppid)
|
||||
return -ERANGE;
|
||||
|
||||
if (ret)
|
||||
|
@ -819,13 +804,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) {
|
|||
for (;;) {
|
||||
usec_t n;
|
||||
siginfo_t status = {};
|
||||
struct timespec ts;
|
||||
|
||||
n = now(CLOCK_MONOTONIC);
|
||||
if (n >= until)
|
||||
break;
|
||||
|
||||
r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)));
|
||||
r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n)));
|
||||
/* Assuming we woke due to the child exiting. */
|
||||
if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) {
|
||||
if (status.si_pid == pid) {
|
||||
|
@ -1161,12 +1145,6 @@ void reset_cached_pid(void) {
|
|||
cached_pid = CACHED_PID_UNSET;
|
||||
}
|
||||
|
||||
/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc
|
||||
* headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against
|
||||
* libpthread, as it is part of glibc anyway. */
|
||||
extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle);
|
||||
extern void* __dso_handle _weak_;
|
||||
|
||||
pid_t getpid_cached(void) {
|
||||
static bool installed = false;
|
||||
pid_t current_value;
|
||||
|
@ -1194,7 +1172,7 @@ pid_t getpid_cached(void) {
|
|||
* only half-documented (glibc doesn't document it but LSB does — though only superficially)
|
||||
* we'll check for errors only in the most generic fashion possible. */
|
||||
|
||||
if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) {
|
||||
if (pthread_atfork(NULL, NULL, reset_cached_pid) != 0) {
|
||||
/* OOM? Let's try again later */
|
||||
cached_pid = CACHED_PID_UNSET;
|
||||
return new_pid;
|
||||
|
@ -1622,6 +1600,30 @@ _noreturn_ void freeze(void) {
|
|||
pause();
|
||||
}
|
||||
|
||||
bool argv_looks_like_help(int argc, char **argv) {
|
||||
char **l;
|
||||
|
||||
/* Scans the command line for indications the user asks for help. This is supposed to be called by
|
||||
* tools that do not implement getopt() style command line parsing because they are not primarily
|
||||
* user-facing. Detects four ways of asking for help:
|
||||
*
|
||||
* 1. Passing zero arguments
|
||||
* 2. Passing "help" as first argument
|
||||
* 3. Passing --help as any argument
|
||||
* 4. Passing -h as any argument
|
||||
*/
|
||||
|
||||
if (argc <= 1)
|
||||
return true;
|
||||
|
||||
if (streq_ptr(argv[1], "help"))
|
||||
return true;
|
||||
|
||||
l = strv_skip(argv, 1);
|
||||
|
||||
return strv_contains(l, "--help") ||
|
||||
strv_contains(l, "-h");
|
||||
}
|
||||
|
||||
static const char *const sigchld_code_table[] = {
|
||||
[CLD_EXITED] = "exited",
|
||||
|
|
|
@ -191,3 +191,5 @@ int setpriority_closest(int priority);
|
|||
bool invoked_as(char *argv[], const char *token);
|
||||
|
||||
_noreturn_ void freeze(void);
|
||||
|
||||
bool argv_looks_like_help(int argc, char **argv);
|
||||
|
|
|
@ -35,218 +35,34 @@
|
|||
|
||||
static bool srand_called = false;
|
||||
|
||||
int rdrand(unsigned long *ret) {
|
||||
|
||||
/* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here,
|
||||
* instead of sticking to /dev/urandom or getrandom()?
|
||||
*
|
||||
* Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and
|
||||
* getrandom() is generally not initialized yet. It is very common that initialization of the random
|
||||
* pool takes a longer time (up to many minutes), in particular on embedded devices that have no
|
||||
* explicit hardware random generator, as well as in virtualized environments such as major cloud
|
||||
* installations that do not provide virtio-rng or a similar mechanism.
|
||||
*
|
||||
* In such an environment using getrandom() synchronously means we'd block the entire system boot-up
|
||||
* until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK)
|
||||
* would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean
|
||||
* generating many kmsg log messages about our use of it before the random pool is properly
|
||||
* initialized. Neither of these outcomes is desirable.
|
||||
*
|
||||
* Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND
|
||||
* provides us quickly and relatively reliably with random values, without having to delay boot,
|
||||
* without triggering warning messages in kmsg.
|
||||
*
|
||||
* Note that we use RDRAND only under very specific circumstances, when the requirements on the
|
||||
* quality of the returned entropy permit it. Specifically, here are some cases where we *do* use
|
||||
* RDRAND:
|
||||
*
|
||||
* • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic
|
||||
* key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be
|
||||
* generated in a way that is reliably unique, but they do not require ultimate trust into
|
||||
* the entropy generator. systemd generates a number of UUIDs during early boot, including
|
||||
* 'invocation IDs' for every unit spawned that identify the specific invocation of the
|
||||
* service globally, and a number of others. Other alternatives for generating these UUIDs
|
||||
* have been considered, but don't really work: for example, hashing uuids from a local
|
||||
* system identifier combined with a counter falls flat because during early boot disk
|
||||
* storage is not yet available (think: initrd) and thus a system-specific ID cannot be
|
||||
* stored or retrieved yet.
|
||||
*
|
||||
* • Hash table seed generation: systemd uses many hash tables internally. Hash tables are
|
||||
* generally assumed to have O(1) access complexity, but can deteriorate to prohibitive
|
||||
* O(n) access complexity if an attacker manages to trigger a large number of hash
|
||||
* collisions. Thus, systemd (as any software employing hash tables should) uses seeded
|
||||
* hash functions for its hash tables, with a seed generated randomly. The hash tables
|
||||
* systemd employs watch the fill level closely and reseed if necessary. This allows use of
|
||||
* a low quality RNG initially, as long as it improves should a hash table be under attack:
|
||||
* the attacker after all needs to trigger many collisions to exploit it for the purpose
|
||||
* of DoS, but if doing so improves the seed the attack surface is reduced as the attack
|
||||
* takes place.
|
||||
*
|
||||
* Some cases where we do NOT use RDRAND are:
|
||||
*
|
||||
* • Generation of cryptographic key material 🔑
|
||||
*
|
||||
* • Generation of cryptographic salt values 🧂
|
||||
*
|
||||
* This function returns:
|
||||
*
|
||||
* -EOPNOTSUPP → RDRAND is not available on this system 😔
|
||||
* -EAGAIN → The operation failed this time, but is likely to work if you try again a few
|
||||
* times ♻
|
||||
* -EUCLEAN → We got some random value, but it looked strange, so we refused using it.
|
||||
* This failure might or might not be temporary. 😕
|
||||
*/
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static int have_rdrand = -1;
|
||||
unsigned long v;
|
||||
uint8_t success;
|
||||
|
||||
if (have_rdrand < 0) {
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
|
||||
/* Check if RDRAND is supported by the CPU */
|
||||
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) {
|
||||
have_rdrand = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Compat with old gcc where bit_RDRND didn't exist yet */
|
||||
#ifndef bit_RDRND
|
||||
#define bit_RDRND (1U << 30)
|
||||
#endif
|
||||
|
||||
have_rdrand = !!(ecx & bit_RDRND);
|
||||
|
||||
if (have_rdrand > 0) {
|
||||
/* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0
|
||||
If it is unset getenv_bool_secure will return a negative value. */
|
||||
if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) {
|
||||
have_rdrand = false;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (have_rdrand == 0)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
asm volatile("rdrand %0;"
|
||||
"setc %1"
|
||||
: "=r" (v),
|
||||
"=qm" (success));
|
||||
msan_unpoison(&success, sizeof(success));
|
||||
if (!success)
|
||||
return -EAGAIN;
|
||||
|
||||
/* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success
|
||||
* via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be
|
||||
* a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking
|
||||
* for this special value (and also 0, just to be sure) and filtering it out. This is a work-around
|
||||
* only however and something AMD really should fix properly. The Linux kernel should probably work
|
||||
* around this issue by turning off RDRAND altogether on those CPUs. See:
|
||||
* https://github.com/systemd/systemd/issues/11810 */
|
||||
if (v == 0 || v == ULONG_MAX)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
"RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v);
|
||||
|
||||
*ret = v;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
|
||||
static int have_syscall = -1;
|
||||
_cleanup_close_ int fd = -1;
|
||||
bool got_some = false;
|
||||
|
||||
/* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from
|
||||
* the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK
|
||||
* flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not
|
||||
* initialized. Otherwise it will always return some data from the kernel, regardless of whether the
|
||||
* random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not
|
||||
* enough better quality randomness could be acquired, the rest is filled up with low quality
|
||||
* randomness.
|
||||
*
|
||||
* Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND
|
||||
* or even RANDOM_EXTEND_WITH_PSEUDO.
|
||||
*
|
||||
* When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use
|
||||
* RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via
|
||||
* an "all bets are off" wrapper, such as random_bytes(), see below. */
|
||||
/* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK
|
||||
* flag is set. If it doesn't block, it will still always return some data from the kernel, regardless
|
||||
* of whether the random pool is fully initialized or not. When creating cryptographic key material you
|
||||
* should always use RANDOM_BLOCK. */
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
|
||||
if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND))
|
||||
/* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is
|
||||
* not required, as we don't trust it (who does?). Note that we only do a single iteration of
|
||||
* RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10
|
||||
* invocations or so. That's because we don't really care about the quality here. We
|
||||
* generally prefer using RDRAND if the caller allows us to, since this way we won't upset
|
||||
* the kernel's random subsystem by accessing it before the pool is initialized (after all it
|
||||
* will kmsg log about every attempt to do so). */
|
||||
for (;;) {
|
||||
unsigned long u;
|
||||
size_t m;
|
||||
|
||||
if (rdrand(&u) < 0) {
|
||||
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
|
||||
/* Fill in the remaining bytes using pseudo-random values */
|
||||
pseudo_random_bytes(p, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */
|
||||
break;
|
||||
}
|
||||
|
||||
m = MIN(sizeof(u), n);
|
||||
memcpy(p, &u, m);
|
||||
|
||||
p = (uint8_t*) p + m;
|
||||
n -= m;
|
||||
|
||||
if (n == 0)
|
||||
return 0; /* Yay, success! */
|
||||
|
||||
got_some = true;
|
||||
}
|
||||
|
||||
/* Use the getrandom() syscall unless we know we don't have it. */
|
||||
if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
|
||||
|
||||
for (;;) {
|
||||
ssize_t l;
|
||||
l = getrandom(p, n,
|
||||
(FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
|
||||
(FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
|
||||
ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE);
|
||||
|
||||
if (l > 0) {
|
||||
have_syscall = true;
|
||||
|
||||
if ((size_t) l == n)
|
||||
return 0; /* Yay, success! */
|
||||
|
||||
/* We didn't get enough data, so try again */
|
||||
assert((size_t) l < n);
|
||||
p = (uint8_t*) p + l;
|
||||
n -= l;
|
||||
|
||||
if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
|
||||
/* Fill in the remaining bytes using pseudo-random values */
|
||||
pseudo_random_bytes(p, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
got_some = true;
|
||||
|
||||
/* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */
|
||||
if (FLAGS_SET(flags, RANDOM_BLOCK))
|
||||
continue;
|
||||
|
||||
/* Fill in the rest with /dev/urandom */
|
||||
break;
|
||||
continue;
|
||||
|
||||
} else if (l == 0) {
|
||||
have_syscall = true;
|
||||
|
@ -257,37 +73,12 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
|
|||
have_syscall = false;
|
||||
break;
|
||||
|
||||
} else if (errno == EAGAIN) {
|
||||
/* The kernel has no entropy whatsoever. Let's remember to use the syscall
|
||||
* the next time again though.
|
||||
*
|
||||
* If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can
|
||||
* produce some pseudo-random bytes instead. Otherwise, fall back to
|
||||
* /dev/urandom, which we know is empty, but the kernel will produce some
|
||||
* bytes for us on a best-effort basis. */
|
||||
have_syscall = true;
|
||||
|
||||
if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) {
|
||||
/* Fill in the remaining bytes using pseudorandom values */
|
||||
pseudo_random_bytes(p, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FLAGS_SET(flags, RANDOM_MAY_FAIL))
|
||||
return -ENODATA;
|
||||
|
||||
/* Use /dev/urandom instead */
|
||||
break;
|
||||
|
||||
} else if (errno == EINVAL) {
|
||||
|
||||
/* Most likely: unknown flag. We know that GRND_INSECURE might cause this,
|
||||
* hence try without. */
|
||||
|
||||
if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) {
|
||||
flags = flags &~ RANDOM_ALLOW_INSECURE;
|
||||
continue;
|
||||
}
|
||||
/* If we previously passed GRND_INSECURE, and this flag isn't known, then
|
||||
* we're likely running an old kernel which has getrandom() but not
|
||||
* GRND_INSECURE. In this case, fall back to /dev/urandom. */
|
||||
if (!FLAGS_SET(flags, RANDOM_BLOCK))
|
||||
break;
|
||||
|
||||
return -errno;
|
||||
} else
|
||||
|
@ -312,8 +103,6 @@ void initialize_srand(void) {
|
|||
#if HAVE_SYS_AUXV_H
|
||||
const void *auxv;
|
||||
#endif
|
||||
unsigned long k;
|
||||
|
||||
if (srand_called)
|
||||
return;
|
||||
|
||||
|
@ -338,9 +127,6 @@ void initialize_srand(void) {
|
|||
x ^= (unsigned) now(CLOCK_REALTIME);
|
||||
x ^= (unsigned) gettid();
|
||||
|
||||
if (rdrand(&k) >= 0)
|
||||
x ^= (unsigned) k;
|
||||
|
||||
srand(x);
|
||||
srand_called = true;
|
||||
|
||||
|
@ -394,11 +180,8 @@ void random_bytes(void *p, size_t n) {
|
|||
*
|
||||
* What this function will do:
|
||||
*
|
||||
* • This function will preferably use the CPU's RDRAND operation, if it is available, in
|
||||
* order to return "mid-quality" random values cheaply.
|
||||
*
|
||||
* • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are
|
||||
* cheaply available.
|
||||
* • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if
|
||||
* they are cheaply available, or less high-quality random values if they are not.
|
||||
*
|
||||
* • This function will return pseudo-random data, generated via libc rand() if nothing
|
||||
* better is available.
|
||||
|
@ -421,7 +204,7 @@ void random_bytes(void *p, size_t n) {
|
|||
* This function is hence not useful for generating UUIDs or cryptographic key material.
|
||||
*/
|
||||
|
||||
if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0)
|
||||
if (genuine_random_bytes(p, n, 0) >= 0)
|
||||
return;
|
||||
|
||||
/* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
|
||||
|
|
|
@ -6,11 +6,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
typedef enum RandomFlags {
|
||||
RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */
|
||||
RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
|
||||
RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */
|
||||
RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */
|
||||
RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */
|
||||
RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */
|
||||
} RandomFlags;
|
||||
|
||||
int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */
|
||||
|
@ -31,10 +27,8 @@ static inline uint32_t random_u32(void) {
|
|||
return u;
|
||||
}
|
||||
|
||||
int rdrand(unsigned long *ret);
|
||||
|
||||
/* Some limits on the pool sizes when we deal with the kernel random pool */
|
||||
#define RANDOM_POOL_SIZE_MIN 512U
|
||||
#define RANDOM_POOL_SIZE_MIN 32U
|
||||
#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
|
||||
|
||||
size_t random_pool_size(void);
|
||||
|
|
|
@ -228,7 +228,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng
|
|||
({ \
|
||||
const union sockaddr_union *__sa = &(sa); \
|
||||
size_t _len; \
|
||||
switch(__sa->sa.sa_family) { \
|
||||
switch (__sa->sa.sa_family) { \
|
||||
case AF_INET: \
|
||||
_len = sizeof(struct sockaddr_in); \
|
||||
break; \
|
||||
|
|
|
@ -16,8 +16,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
|||
|
||||
#define typesafe_bsearch_r(k, b, n, func, userdata) \
|
||||
({ \
|
||||
const typeof(b[0]) *_k = k; \
|
||||
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \
|
||||
const typeof((b)[0]) *_k = k; \
|
||||
int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \
|
||||
xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \
|
||||
})
|
||||
|
||||
|
@ -36,8 +36,8 @@ static inline void* bsearch_safe(const void *key, const void *base,
|
|||
|
||||
#define typesafe_bsearch(k, b, n, func) \
|
||||
({ \
|
||||
const typeof(b[0]) *_k = k; \
|
||||
int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \
|
||||
const typeof((b)[0]) *_k = k; \
|
||||
int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \
|
||||
bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \
|
||||
})
|
||||
|
||||
|
@ -57,7 +57,7 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison
|
|||
* is the prototype for the comparison function */
|
||||
#define typesafe_qsort(p, n, func) \
|
||||
({ \
|
||||
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
|
||||
int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*) = func; \
|
||||
_qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \
|
||||
})
|
||||
|
||||
|
@ -71,7 +71,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso
|
|||
|
||||
#define typesafe_qsort_r(p, n, func, userdata) \
|
||||
({ \
|
||||
int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \
|
||||
int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \
|
||||
qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \
|
||||
})
|
||||
|
||||
|
|
|
@ -127,17 +127,22 @@ bool null_or_empty(struct stat *st) {
|
|||
return false;
|
||||
}
|
||||
|
||||
int null_or_empty_path(const char *fn) {
|
||||
int null_or_empty_path_with_root(const char *fn, const char *root) {
|
||||
struct stat st;
|
||||
int r;
|
||||
|
||||
assert(fn);
|
||||
|
||||
/* If we have the path, let's do an easy text comparison first. */
|
||||
if (path_equal(fn, "/dev/null"))
|
||||
/* A symlink to /dev/null or an empty file?
|
||||
* When looking under root_dir, we can't expect /dev/ to be mounted,
|
||||
* so let's see if the path is a (possibly dangling) symlink to /dev/null. */
|
||||
|
||||
if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
|
||||
return true;
|
||||
|
||||
if (stat(fn, &st) < 0)
|
||||
return -errno;
|
||||
r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return null_or_empty(&st);
|
||||
}
|
||||
|
@ -185,8 +190,7 @@ int files_same(const char *filea, const char *fileb, int flags) {
|
|||
if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
|
||||
return -errno;
|
||||
|
||||
return a.st_dev == b.st_dev &&
|
||||
a.st_ino == b.st_ino;
|
||||
return stat_inode_same(&a, &b);
|
||||
}
|
||||
|
||||
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
|
||||
|
@ -249,6 +253,15 @@ int path_is_temporary_fs(const char *path) {
|
|||
return is_temporary_fs(&s);
|
||||
}
|
||||
|
||||
int path_is_network_fs(const char *path) {
|
||||
struct statfs s;
|
||||
|
||||
if (statfs(path, &s) < 0)
|
||||
return -errno;
|
||||
|
||||
return is_network_fs(&s);
|
||||
}
|
||||
|
||||
int stat_verify_regular(const struct stat *st) {
|
||||
assert(st);
|
||||
|
||||
|
@ -408,6 +421,18 @@ int proc_mounted(void) {
|
|||
return r;
|
||||
}
|
||||
|
||||
bool stat_inode_same(const struct stat *a, const struct stat *b) {
|
||||
|
||||
/* Returns if the specified stat structure references the same (though possibly modified) inode. Does
|
||||
* a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
|
||||
|
||||
return a && b &&
|
||||
(a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
|
||||
((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
|
||||
a->st_dev == b->st_dev &&
|
||||
a->st_ino == b->st_ino;
|
||||
}
|
||||
|
||||
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
|
||||
|
||||
/* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
|
||||
|
@ -419,14 +444,10 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
|
|||
* about contents of the file. The purpose here is to detect file contents changes, and nothing
|
||||
* else. */
|
||||
|
||||
return a && b &&
|
||||
(a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
|
||||
((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
|
||||
return stat_inode_same(a, b) &&
|
||||
a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
|
||||
a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
|
||||
(!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
|
||||
a->st_dev == b->st_dev &&
|
||||
a->st_ino == b->st_ino &&
|
||||
(!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) {
|
|||
}
|
||||
|
||||
bool null_or_empty(struct stat *st) _pure_;
|
||||
int null_or_empty_path(const char *fn);
|
||||
int null_or_empty_path_with_root(const char *fn, const char *root);
|
||||
int null_or_empty_fd(int fd);
|
||||
|
||||
static inline int null_or_empty_path(const char *fn) {
|
||||
return null_or_empty_path_with_root(fn, NULL);
|
||||
}
|
||||
|
||||
int path_is_read_only_fs(const char *path);
|
||||
|
||||
int files_same(const char *filea, const char *fileb, int flags);
|
||||
|
@ -53,6 +57,7 @@ int fd_is_temporary_fs(int fd);
|
|||
int fd_is_network_fs(int fd);
|
||||
|
||||
int path_is_temporary_fs(const char *path);
|
||||
int path_is_network_fs(const char *path);
|
||||
|
||||
/* Because statfs.t_type can be int on some architectures, we have to cast
|
||||
* the const magic to the type, otherwise the compiler warns about
|
||||
|
@ -91,6 +96,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
|
|||
|
||||
int proc_mounted(void);
|
||||
|
||||
bool stat_inode_same(const struct stat *a, const struct stat *b);
|
||||
bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
|
||||
|
||||
int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx);
|
||||
|
@ -113,3 +119,9 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s
|
|||
struct new_statx nsx; \
|
||||
} var
|
||||
#endif
|
||||
|
||||
static inline bool devid_set_and_equal(dev_t a, dev_t b) {
|
||||
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
|
||||
* know" and we'll return false */
|
||||
return a == b && a != 0;
|
||||
}
|
||||
|
|
|
@ -113,4 +113,4 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
|
|||
fputc_unlocked('\n', stdout); \
|
||||
} \
|
||||
funlockfile(stdout); \
|
||||
} while(false)
|
||||
} while (false)
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
#include "strv.h"
|
||||
|
||||
char* strv_find(char * const *l, const char *name) {
|
||||
char * const *i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
|
@ -29,8 +27,6 @@ char* strv_find(char * const *l, const char *name) {
|
|||
}
|
||||
|
||||
char* strv_find_case(char * const *l, const char *name) {
|
||||
char * const *i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
|
@ -41,8 +37,6 @@ char* strv_find_case(char * const *l, const char *name) {
|
|||
}
|
||||
|
||||
char* strv_find_prefix(char * const *l, const char *name) {
|
||||
char * const *i;
|
||||
|
||||
assert(name);
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
|
@ -53,14 +47,14 @@ char* strv_find_prefix(char * const *l, const char *name) {
|
|||
}
|
||||
|
||||
char* strv_find_startswith(char * const *l, const char *name) {
|
||||
char * const *i, *e;
|
||||
|
||||
assert(name);
|
||||
|
||||
/* Like strv_find_prefix, but actually returns only the
|
||||
* suffix, not the whole item */
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
char *e;
|
||||
|
||||
e = startswith(*i, name);
|
||||
if (e)
|
||||
return e;
|
||||
|
@ -70,18 +64,13 @@ char* strv_find_startswith(char * const *l, const char *name) {
|
|||
}
|
||||
|
||||
char** strv_free(char **l) {
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
for (char **k = l; *k; k++)
|
||||
STRV_FOREACH(k, l)
|
||||
free(*k);
|
||||
|
||||
return mfree(l);
|
||||
}
|
||||
|
||||
char** strv_free_erase(char **l) {
|
||||
char **i;
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
erase_and_freep(i);
|
||||
|
||||
|
@ -89,32 +78,29 @@ char** strv_free_erase(char **l) {
|
|||
}
|
||||
|
||||
char** strv_copy(char * const *l) {
|
||||
char **r, **k;
|
||||
_cleanup_strv_free_ char **result = NULL;
|
||||
char **k;
|
||||
|
||||
k = r = new(char*, strv_length(l) + 1);
|
||||
if (!r)
|
||||
result = new(char*, strv_length(l) + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
if (l)
|
||||
for (; *l; k++, l++) {
|
||||
*k = strdup(*l);
|
||||
if (!*k) {
|
||||
strv_free(r);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
k = result;
|
||||
STRV_FOREACH(i, l) {
|
||||
*k = strdup(*i);
|
||||
if (!*k)
|
||||
return NULL;
|
||||
k++;
|
||||
}
|
||||
|
||||
*k = NULL;
|
||||
return r;
|
||||
return TAKE_PTR(result);
|
||||
}
|
||||
|
||||
size_t strv_length(char * const *l) {
|
||||
size_t n = 0;
|
||||
|
||||
if (!l)
|
||||
return 0;
|
||||
|
||||
for (; *l; l++)
|
||||
STRV_FOREACH(i, l)
|
||||
n++;
|
||||
|
||||
return n;
|
||||
|
@ -171,8 +157,8 @@ char** strv_new_internal(const char *x, ...) {
|
|||
}
|
||||
|
||||
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) {
|
||||
char * const *s, **t;
|
||||
size_t p, q, i = 0;
|
||||
char **t;
|
||||
|
||||
assert(a);
|
||||
|
||||
|
@ -217,7 +203,6 @@ rollback:
|
|||
}
|
||||
|
||||
int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) {
|
||||
char * const *s;
|
||||
int r;
|
||||
|
||||
STRV_FOREACH(s, b) {
|
||||
|
@ -367,7 +352,6 @@ int strv_split_colon_pairs(char ***t, const char *s) {
|
|||
}
|
||||
|
||||
char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) {
|
||||
char * const *s;
|
||||
char *r, *e;
|
||||
size_t n, k, m;
|
||||
|
||||
|
@ -595,8 +579,6 @@ int strv_extend_front(char ***l, const char *value) {
|
|||
}
|
||||
|
||||
char** strv_uniq(char **l) {
|
||||
char **i;
|
||||
|
||||
/* Drops duplicate entries. The first identical string will be
|
||||
* kept, the others dropped */
|
||||
|
||||
|
@ -607,10 +589,8 @@ char** strv_uniq(char **l) {
|
|||
}
|
||||
|
||||
bool strv_is_uniq(char * const *l) {
|
||||
char * const *i;
|
||||
|
||||
STRV_FOREACH(i, l)
|
||||
if (strv_find(i+1, *i))
|
||||
if (strv_contains(i+1, *i))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -717,7 +697,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
|
|||
*/
|
||||
|
||||
_cleanup_free_ char *m = NULL;
|
||||
char * const *i;
|
||||
size_t n = 0;
|
||||
|
||||
assert(ret);
|
||||
|
@ -754,8 +733,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) {
|
|||
}
|
||||
|
||||
bool strv_overlap(char * const *a, char * const *b) {
|
||||
char * const *i;
|
||||
|
||||
STRV_FOREACH(i, a)
|
||||
if (strv_contains(b, *i))
|
||||
return true;
|
||||
|
@ -795,8 +772,6 @@ int strv_compare(char * const *a, char * const *b) {
|
|||
}
|
||||
|
||||
void strv_print(char * const *l) {
|
||||
char * const *s;
|
||||
|
||||
STRV_FOREACH(s, l)
|
||||
puts(*s);
|
||||
}
|
||||
|
@ -830,8 +805,6 @@ char** strv_reverse(char **l) {
|
|||
}
|
||||
|
||||
char** strv_shell_escape(char **l, const char *bad) {
|
||||
char **s;
|
||||
|
||||
/* Escapes every character in every string in l that is in bad,
|
||||
* edits in-place, does not roll-back on error. */
|
||||
|
||||
|
@ -914,7 +887,6 @@ rollback:
|
|||
|
||||
int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) {
|
||||
bool b = false;
|
||||
char * const *s;
|
||||
int r;
|
||||
|
||||
/* Like fputs(), but for strv, and with a less stupid argument order */
|
||||
|
|
|
@ -122,19 +122,30 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) {
|
|||
|
||||
bool strv_overlap(char * const *a, char * const *b) _pure_;
|
||||
|
||||
#define _STRV_FOREACH(s, l, i) \
|
||||
for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++)
|
||||
|
||||
#define STRV_FOREACH(s, l) \
|
||||
for ((s) = (l); (s) && *(s); (s)++)
|
||||
_STRV_FOREACH(s, l, UNIQ_T(i, UNIQ))
|
||||
|
||||
#define STRV_FOREACH_BACKWARDS(s, l) \
|
||||
for (s = ({ \
|
||||
typeof(l) _l = l; \
|
||||
_l ? _l + strv_length(_l) - 1U : NULL; \
|
||||
}); \
|
||||
(l) && ((s) >= (l)); \
|
||||
(s)--)
|
||||
#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \
|
||||
for (typeof(*(l)) *s, *h = (l), *i = ({ \
|
||||
size_t _len = strv_length(h); \
|
||||
_len > 0 ? h + _len - 1 : NULL; \
|
||||
}); \
|
||||
(s = i); \
|
||||
i = PTR_SUB1(i, h))
|
||||
|
||||
#define STRV_FOREACH_PAIR(x, y, l) \
|
||||
for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
|
||||
#define STRV_FOREACH_BACKWARDS(s, l) \
|
||||
_STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ))
|
||||
|
||||
#define _STRV_FOREACH_PAIR(x, y, l, i) \
|
||||
for (typeof(*l) *x, *y, *i = (l); \
|
||||
i && *(x = i) && *(y = i + 1); \
|
||||
i += 2)
|
||||
|
||||
#define STRV_FOREACH_PAIR(x, y, l) \
|
||||
_STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ))
|
||||
|
||||
char** strv_sort(char **l);
|
||||
void strv_print(char * const *l);
|
||||
|
@ -185,7 +196,7 @@ void strv_print(char * const *l);
|
|||
#define STARTSWITH_SET(p, ...) \
|
||||
({ \
|
||||
const char *_p = (p); \
|
||||
char *_found = NULL, **_i; \
|
||||
char *_found = NULL; \
|
||||
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
|
||||
_found = startswith(_p, *_i); \
|
||||
if (_found) \
|
||||
|
@ -197,7 +208,7 @@ void strv_print(char * const *l);
|
|||
#define ENDSWITH_SET(p, ...) \
|
||||
({ \
|
||||
const char *_p = (p); \
|
||||
char *_found = NULL, **_i; \
|
||||
char *_found = NULL; \
|
||||
STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \
|
||||
_found = endswith(_p, *_i); \
|
||||
if (_found) \
|
||||
|
@ -207,7 +218,7 @@ void strv_print(char * const *l);
|
|||
})
|
||||
|
||||
#define _FOREACH_STRING(uniq, x, y, ...) \
|
||||
for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \
|
||||
for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \
|
||||
x; \
|
||||
x = *(++UNIQ_T(l, uniq)))
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
|
|||
|
||||
ts->realtime = now(CLOCK_REALTIME);
|
||||
ts->monotonic = now(CLOCK_MONOTONIC);
|
||||
ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
|
||||
ts->boottime = now(CLOCK_BOOTTIME);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
|
|||
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY || u == 0) {
|
||||
if (!timestamp_is_set(u)) {
|
||||
ts->realtime = ts->monotonic = u;
|
||||
return ts;
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
|
|||
|
||||
assert(ts);
|
||||
|
||||
if (u == USEC_INFINITY || u == 0) {
|
||||
if (!timestamp_is_set(u)) {
|
||||
ts->realtime = ts->monotonic = ts->boottime = u;
|
||||
return ts;
|
||||
}
|
||||
|
@ -150,9 +150,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
|
|||
|
||||
ts->realtime = u;
|
||||
ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
|
||||
ts->boottime = clock_boottime_supported() ?
|
||||
map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) :
|
||||
USEC_INFINITY;
|
||||
ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME));
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
@ -170,8 +168,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
|
|||
return ts;
|
||||
}
|
||||
|
||||
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
|
||||
clockid_t cid;
|
||||
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
|
||||
usec_t nowm;
|
||||
|
||||
if (u == USEC_INFINITY) {
|
||||
|
@ -179,14 +176,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us
|
|||
return ts;
|
||||
}
|
||||
|
||||
cid = clock_boottime_or_monotonic();
|
||||
nowm = now(cid);
|
||||
|
||||
if (cid == CLOCK_MONOTONIC)
|
||||
ts->monotonic = u;
|
||||
else
|
||||
ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
|
||||
|
||||
nowm = now(CLOCK_BOOTTIME);
|
||||
ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
|
||||
ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME));
|
||||
return ts;
|
||||
}
|
||||
|
@ -320,11 +311,13 @@ char *format_timestamp_style(
|
|||
time_t sec;
|
||||
size_t n;
|
||||
bool utc = false, us = false;
|
||||
int r;
|
||||
|
||||
assert(buf);
|
||||
|
||||
switch (style) {
|
||||
case TIMESTAMP_PRETTY:
|
||||
case TIMESTAMP_UNIX:
|
||||
break;
|
||||
case TIMESTAMP_US:
|
||||
us = true;
|
||||
|
@ -347,9 +340,17 @@ char *format_timestamp_style(
|
|||
1 + 1 + /* space and shortest possible zone */
|
||||
1))
|
||||
return NULL; /* Not enough space even for the shortest form. */
|
||||
if (t <= 0 || t == USEC_INFINITY)
|
||||
if (!timestamp_is_set(t))
|
||||
return NULL; /* Timestamp is unset */
|
||||
|
||||
if (style == TIMESTAMP_UNIX) {
|
||||
r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */
|
||||
if (r < 0 || (size_t) r >= l)
|
||||
return NULL; /* Doesn't fit */
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Let's not format times with years > 9999 */
|
||||
if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
|
||||
assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
|
||||
|
@ -417,7 +418,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
|
|||
const char *s;
|
||||
usec_t n, d;
|
||||
|
||||
if (t <= 0 || t == USEC_INFINITY)
|
||||
if (!timestamp_is_set(t))
|
||||
return NULL;
|
||||
|
||||
n = now(CLOCK_REALTIME);
|
||||
|
@ -788,6 +789,16 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
|
|||
goto from_tm;
|
||||
}
|
||||
|
||||
/* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
|
||||
tm = copy;
|
||||
k = strptime(t, "%b %d %H:%M:%S", &tm);
|
||||
if (k) {
|
||||
if (*k == '.')
|
||||
goto parse_usec;
|
||||
else if (*k == 0)
|
||||
goto from_tm;
|
||||
}
|
||||
|
||||
tm = copy;
|
||||
k = strptime(t, "%y-%m-%d %H:%M", &tm);
|
||||
if (k && *k == 0) {
|
||||
|
@ -1441,33 +1452,6 @@ int verify_timezone(const char *name, int log_level) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool clock_boottime_supported(void) {
|
||||
static int supported = -1;
|
||||
|
||||
/* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
|
||||
|
||||
if (supported < 0) {
|
||||
int fd;
|
||||
|
||||
fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
|
||||
if (fd < 0)
|
||||
supported = false;
|
||||
else {
|
||||
safe_close(fd);
|
||||
supported = true;
|
||||
}
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
clockid_t clock_boottime_or_monotonic(void) {
|
||||
if (clock_boottime_supported())
|
||||
return CLOCK_BOOTTIME;
|
||||
else
|
||||
return CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
bool clock_supported(clockid_t clock) {
|
||||
struct timespec ts;
|
||||
|
||||
|
@ -1475,16 +1459,10 @@ bool clock_supported(clockid_t clock) {
|
|||
|
||||
case CLOCK_MONOTONIC:
|
||||
case CLOCK_REALTIME:
|
||||
case CLOCK_BOOTTIME:
|
||||
/* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
|
||||
return true;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
return clock_boottime_supported();
|
||||
|
||||
case CLOCK_BOOTTIME_ALARM:
|
||||
if (!clock_boottime_supported())
|
||||
return false;
|
||||
|
||||
_fallthrough_;
|
||||
default:
|
||||
/* For everything else, check properly */
|
||||
return clock_gettime(clock, &ts) >= 0;
|
||||
|
@ -1632,6 +1610,7 @@ static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
|
|||
[TIMESTAMP_US] = "us",
|
||||
[TIMESTAMP_UTC] = "utc",
|
||||
[TIMESTAMP_US_UTC] = "us+utc",
|
||||
[TIMESTAMP_UNIX] = "unix",
|
||||
};
|
||||
|
||||
/* Use the macro for enum → string to allow for aliases */
|
||||
|
|
|
@ -34,6 +34,7 @@ typedef enum TimestampStyle {
|
|||
TIMESTAMP_US,
|
||||
TIMESTAMP_UTC,
|
||||
TIMESTAMP_US_UTC,
|
||||
TIMESTAMP_UNIX,
|
||||
_TIMESTAMP_STYLE_MAX,
|
||||
_TIMESTAMP_STYLE_INVALID = -EINVAL,
|
||||
} TimestampStyle;
|
||||
|
@ -81,7 +82,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
|
|||
dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
|
||||
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u);
|
||||
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u);
|
||||
|
||||
triple_timestamp* triple_timestamp_get(triple_timestamp *ts);
|
||||
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u);
|
||||
|
@ -114,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
|
|||
struct timespec* timespec_store(struct timespec *ts, usec_t u);
|
||||
struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n);
|
||||
|
||||
#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u))
|
||||
|
||||
usec_t timeval_load(const struct timeval *tv) _pure_;
|
||||
struct timeval* timeval_store(struct timeval *tv, usec_t u);
|
||||
|
||||
#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u))
|
||||
|
||||
char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_;
|
||||
char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_;
|
||||
char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_;
|
||||
|
@ -150,9 +155,7 @@ static inline bool timezone_is_valid(const char *name, int log_level) {
|
|||
return verify_timezone(name, log_level) >= 0;
|
||||
}
|
||||
|
||||
bool clock_boottime_supported(void);
|
||||
bool clock_supported(clockid_t clock);
|
||||
clockid_t clock_boottime_or_monotonic(void);
|
||||
|
||||
usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to);
|
||||
|
||||
|
|
|
@ -275,6 +275,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
|
|||
return fd;
|
||||
}
|
||||
|
||||
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(target);
|
||||
assert(ret_file);
|
||||
assert(ret_path);
|
||||
|
||||
fd = open_tmpfile_linkable(target, flags, &path);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
f = take_fdopen(&fd, "w");
|
||||
if (!f)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_path = TAKE_PTR(path);
|
||||
*ret_file = TAKE_PTR(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target) {
|
||||
assert(fd >= 0);
|
||||
assert(target);
|
||||
|
@ -292,6 +314,23 @@ int link_tmpfile(int fd, const char *path, const char *target) {
|
|||
return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW));
|
||||
}
|
||||
|
||||
int flink_tmpfile(FILE *f, const char *path, const char *target) {
|
||||
int fd, r;
|
||||
|
||||
assert(f);
|
||||
assert(target);
|
||||
|
||||
fd = fileno(f);
|
||||
if (fd < 0) /* Not all FILE* objects encapsulate fds */
|
||||
return -EBADF;
|
||||
|
||||
r = fflush_sync_and_check(f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return link_tmpfile(fd, path, target);
|
||||
}
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
|
|
@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret);
|
|||
|
||||
int open_tmpfile_unlinkable(const char *directory, int flags);
|
||||
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
|
||||
int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file);
|
||||
|
||||
int link_tmpfile(int fd, const char *path, const char *target);
|
||||
int flink_tmpfile(FILE *f, const char *path, const char *target);
|
||||
|
||||
int mkdtemp_malloc(const char *template, char **ret);
|
||||
|
|
|
@ -12,6 +12,14 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */
|
||||
#define HOME_UID_MIN 60001
|
||||
#define HOME_UID_MAX 60513
|
||||
|
||||
/* Users mapped from host into a container */
|
||||
#define MAP_UID_MIN 60514
|
||||
#define MAP_UID_MAX 60577
|
||||
|
||||
bool uid_is_valid(uid_t uid);
|
||||
|
||||
static inline bool gid_is_valid(gid_t gid) {
|
||||
|
@ -59,6 +67,19 @@ int take_etc_passwd_lock(const char *root);
|
|||
#define UID_NOBODY ((uid_t) 65534U)
|
||||
#define GID_NOBODY ((gid_t) 65534U)
|
||||
|
||||
/* If REMOUNT_IDMAP_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host root
|
||||
* user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in the
|
||||
* *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we
|
||||
* definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range
|
||||
* we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use
|
||||
* them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is
|
||||
* icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following
|
||||
* our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID
|
||||
* value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read
|
||||
* this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */
|
||||
#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1))
|
||||
#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1))
|
||||
|
||||
#define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock"
|
||||
|
||||
/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
|
||||
|
|
|
@ -9,6 +9,12 @@ extern int saved_argc;
|
|||
extern char **saved_argv;
|
||||
|
||||
static inline void save_argc_argv(int argc, char **argv) {
|
||||
|
||||
/* Protect against CVE-2021-4034 style attacks */
|
||||
assert_se(argc > 0);
|
||||
assert_se(argv);
|
||||
assert_se(argv[0]);
|
||||
|
||||
saved_argc = argc;
|
||||
saved_argv = argv;
|
||||
}
|
||||
|
|
|
@ -53,14 +53,16 @@
|
|||
#define CONCATENATE(x, y) XCONCATENATE(x, y)
|
||||
|
||||
#ifdef SD_BOOT
|
||||
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define assert(expr)
|
||||
#define assert_not_reached() __builtin_unreachable()
|
||||
#else
|
||||
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_;
|
||||
#define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
|
||||
#define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
#endif
|
||||
#define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
|
||||
|
||||
#define memcpy(a, b, c) CopyMem((a), (b), (c))
|
||||
#define free(a) FreePool(a)
|
||||
|
@ -74,6 +76,13 @@
|
|||
_expr_; \
|
||||
})
|
||||
|
||||
#define ASSERT_SE_PTR(expr) \
|
||||
({ \
|
||||
typeof(expr) _expr_ = (expr); \
|
||||
assert_se(_expr_); \
|
||||
_expr_; \
|
||||
})
|
||||
|
||||
#if defined(static_assert)
|
||||
#define assert_cc(expr) \
|
||||
static_assert(expr, #expr)
|
||||
|
@ -243,23 +252,23 @@
|
|||
CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
|
||||
(CASE_F,__VA_ARGS__)
|
||||
|
||||
#define IN_SET(x, ...) \
|
||||
({ \
|
||||
sd_bool _found = sd_false; \
|
||||
#define IN_SET(x, ...) \
|
||||
({ \
|
||||
sd_bool _found = sd_false; \
|
||||
/* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \
|
||||
* type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \
|
||||
* the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \
|
||||
* doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \
|
||||
static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \
|
||||
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
|
||||
switch(x) { \
|
||||
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
|
||||
_found = sd_true; \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
_found; \
|
||||
assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \
|
||||
switch (x) { \
|
||||
FOR_EACH_MAKE_CASE(__VA_ARGS__) \
|
||||
_found = sd_true; \
|
||||
break; \
|
||||
default: \
|
||||
break; \
|
||||
} \
|
||||
_found; \
|
||||
})
|
||||
|
||||
/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time
|
||||
|
|
|
@ -168,24 +168,18 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
|||
|
||||
/* Skip current terminal character (and accept domain names ending it ".") */
|
||||
if (*terminal == 0)
|
||||
terminal--;
|
||||
terminal = PTR_SUB1(terminal, name);
|
||||
if (terminal >= name && *terminal == '.')
|
||||
terminal--;
|
||||
terminal = PTR_SUB1(terminal, name);
|
||||
|
||||
/* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */
|
||||
for (;;) {
|
||||
if (terminal < name) {
|
||||
/* Reached the first label, so indicate that there are no more */
|
||||
terminal = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
while (terminal) {
|
||||
/* Find the start of the last label */
|
||||
if (*terminal == '.') {
|
||||
const char *y;
|
||||
unsigned slashes = 0;
|
||||
|
||||
for (y = terminal - 1; y >= name && *y == '\\'; y--)
|
||||
for (y = PTR_SUB1(terminal, name); y && *y == '\\'; y = PTR_SUB1(y, name))
|
||||
slashes++;
|
||||
|
||||
if (slashes % 2 == 0) {
|
||||
|
@ -198,7 +192,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha
|
|||
}
|
||||
}
|
||||
|
||||
terminal--;
|
||||
terminal = PTR_SUB1(terminal, name);
|
||||
}
|
||||
|
||||
r = dns_label_unescape(&name, dest, sz, 0);
|
||||
|
@ -1415,3 +1409,18 @@ int dns_name_dot_suffixed(const char *name) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool dns_name_dont_resolve(const char *name) {
|
||||
|
||||
/* Never respond to some of the domains listed in RFC6303 */
|
||||
if (dns_name_endswith(name, "0.in-addr.arpa") > 0 ||
|
||||
dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 ||
|
||||
dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0)
|
||||
return true;
|
||||
|
||||
/* Never respond to some of the domains listed in RFC6761 */
|
||||
if (dns_name_endswith(name, "invalid") > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -103,3 +103,5 @@ int dns_name_apply_idna(const char *name, char **ret);
|
|||
int dns_name_is_valid_or_address(const char *name);
|
||||
|
||||
int dns_name_dot_suffixed(const char *name);
|
||||
|
||||
bool dns_name_dont_resolve(const char *name);
|
||||
|
|
Loading…
Reference in a new issue