1
0
mirror of https://github.com/systemd/systemd synced 2026-04-01 04:34:51 +02:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Lennart Poettering
1845ba368e homework: replace homegrown "dir-is-empty" check with dir_is_empty_at() 2021-10-26 08:13:15 +09:00
Yu Watanabe
b1ccd7d737
Merge pull request #21097 from poettering/dir-is-empty-fix
dir_is_empty_at() tweaks
2021-10-26 08:12:50 +09:00
Yu Watanabe
8a088877ab test: enable debug logging of systemd-udevd
Otherwise, it is hard to debug when the test fails.
2021-10-26 08:12:10 +09:00
Yu Watanabe
0f96a82311 network: dhcp6pd: do not assign downstream prefix when RADV is requested but not configured yet 2021-10-26 08:11:44 +09:00
Yu Watanabe
928112a487 network: ndisc: fix behavior when DHCPv6Client=always
The man page says that when 'always' is set, the DHCPv6 client always
starts in managed mode, and that means we need to ignore the RA flags.
2021-10-26 08:11:19 +09:00
Yu Watanabe
e817f5b0f9
Merge pull request #21119 from yuwata/network-dhcp6-pd-cleanups
network: dhcp6pd: several cleanups
2021-10-26 08:10:56 +09:00
Yu Watanabe
a84171ce6f libsystemd-network: do not warn when log_dhcp_client() or friends with NULL 2021-10-26 08:09:40 +09:00
Lennart Poettering
0dbce03c37 tree-wide: explicitly unpoison getdents64() memory
Apparently memory sanitizer doesn't grok getdents64() properly. Let's
address that by explicitly marken memory initialized by getdents64() as
unpoisoned.
2021-10-25 21:51:37 +02:00
Lennart Poettering
bfc569b060 test: add test for dir_is_empty_at() 2021-10-25 21:51:37 +02:00
Lennart Poettering
d96f21eec2 stat-util: make sure dir_is_empty_at() does something useful in all cases 2021-10-25 21:51:37 +02:00
Lennart Poettering
a068aceafb stat-util: optimize dir_is_empty_at() a bit, by using getdents64()
That way we have a single syscall only for it, instead of the multiple
readdir() and friends do. And we can operate entirely on the stack, no
malloc() implicit.
2021-10-25 21:51:37 +02:00
Lennart Poettering
a4e70ef7ba dirent-util: add FOREACH macro for iterating through getdents64() buffers
We already have a similar loop twice, let's make it easier to read via
an iteration macro.

(The new macro is a bit more careful even, as it verifies the full
dirent fits into the remaining buffer when returning it)
2021-10-25 21:51:37 +02:00
Lennart Poettering
ca664db258 dirent-util: move getdents64() related definitions to common header
We want to reuse getdents64() wherever necessary, let's hence move
definitions useful for that into public code.
2021-10-25 21:50:20 +02:00
Lennart Poettering
b9d0652263 stat-util: specify O_DIRECTORY when reopening dir in dir_is_empty_at()
That way we can fail earlier if the specified fd is not actually a
directory.

(Also, it's not exactly according to standards to open things without
either O_RDONLY/O_RDWR...)
2021-10-25 21:39:05 +02:00
Yu Watanabe
df8bf72631 network: dhcp6: rename variables 2021-10-26 00:35:22 +09:00
Yu Watanabe
78e50edfd4 network: dhcp6pd: move logic of acquiring subnet prefix into dhcp6_pd_assign_prefix()
Preparation for later commits.
2021-10-26 00:35:22 +09:00
Yu Watanabe
56bbe0ef02 network: dhcp6pd: introduce a simplified and unified method to calculate subnet prefix 2021-10-26 00:35:22 +09:00
16 changed files with 315 additions and 177 deletions

View File

@ -33,3 +33,32 @@ struct dirent *readdir_no_dot(DIR *dirp);
} \
break; \
} else
/* Maximum space one dirent structure might require at most */
#define DIRENT_SIZE_MAX CONST_MAX(sizeof(struct dirent), offsetof(struct dirent, d_name) + NAME_MAX + 1)
/* Only if 64bit off_t is enabled struct dirent + struct dirent64 are actually the same. We require this, and
* we want them to be interchangeable to make getdents64() work, hence verify that. */
assert_cc(_FILE_OFFSET_BITS == 64);
assert_cc(sizeof(struct dirent) == sizeof(struct dirent64));
assert_cc(offsetof(struct dirent, d_ino) == offsetof(struct dirent64, d_ino));
assert_cc(sizeof_field(struct dirent, d_ino) == sizeof_field(struct dirent64, d_ino));
assert_cc(offsetof(struct dirent, d_off) == offsetof(struct dirent64, d_off));
assert_cc(sizeof_field(struct dirent, d_off) == sizeof_field(struct dirent64, d_off));
assert_cc(offsetof(struct dirent, d_reclen) == offsetof(struct dirent64, d_reclen));
assert_cc(sizeof_field(struct dirent, d_reclen) == sizeof_field(struct dirent64, d_reclen));
assert_cc(offsetof(struct dirent, d_type) == offsetof(struct dirent64, d_type));
assert_cc(sizeof_field(struct dirent, d_type) == sizeof_field(struct dirent64, d_type));
assert_cc(offsetof(struct dirent, d_name) == offsetof(struct dirent64, d_name));
assert_cc(sizeof_field(struct dirent, d_name) == sizeof_field(struct dirent64, d_name));
#define FOREACH_DIRENT_IN_BUFFER(de, buf, sz) \
for (void *_end = (uint8_t*) ({ (de) = (buf); }) + (sz); \
(uint8_t*) (de) < (uint8_t*) _end; \
(de) = (struct dirent*) ((uint8_t*) (de) + (de)->d_reclen))
#define DEFINE_DIRENT_BUFFER(name, sz) \
union { \
struct dirent de; \
uint8_t data[(sz) * DIRENT_SIZE_MAX]; \
} name

View File

@ -25,38 +25,21 @@ static bool ignore_dirent(const struct dirent *de, RecurseDirFlags flags) {
dot_or_dot_dot(de->d_name);
}
/* Maximum space one direent structure might require at most */
#define DIRENT_SIZE_MAX MAX(sizeof(struct dirent), offsetof(struct dirent, d_name) + NAME_MAX + 1)
int readdir_all(int dir_fd,
RecurseDirFlags flags,
DirectoryEntries **ret) {
_cleanup_free_ DirectoryEntries *de = NULL;
struct dirent *entry;
DirectoryEntries *nde;
size_t add, sz, j;
assert(dir_fd >= 0);
/* Returns an array with pointers to "struct dirent" directory entries, optionally sorted. Free the
* array with readdir_all_freep(). */
/* Only if 64bit off_t is enabled struct dirent + struct dirent64 are actually the same. We require
* this, and we want them to be interchangeable, hence verify that. */
assert_cc(_FILE_OFFSET_BITS == 64);
assert_cc(sizeof(struct dirent) == sizeof(struct dirent64));
assert_cc(offsetof(struct dirent, d_ino) == offsetof(struct dirent64, d_ino));
assert_cc(sizeof(((struct dirent*) NULL)->d_ino) == sizeof(((struct dirent64*) NULL)->d_ino));
assert_cc(offsetof(struct dirent, d_off) == offsetof(struct dirent64, d_off));
assert_cc(sizeof(((struct dirent*) NULL)->d_off) == sizeof(((struct dirent64*) NULL)->d_off));
assert_cc(offsetof(struct dirent, d_reclen) == offsetof(struct dirent64, d_reclen));
assert_cc(sizeof(((struct dirent*) NULL)->d_reclen) == sizeof(((struct dirent64*) NULL)->d_reclen));
assert_cc(offsetof(struct dirent, d_type) == offsetof(struct dirent64, d_type));
assert_cc(sizeof(((struct dirent*) NULL)->d_type) == sizeof(((struct dirent64*) NULL)->d_type));
assert_cc(offsetof(struct dirent, d_name) == offsetof(struct dirent64, d_name));
assert_cc(sizeof(((struct dirent*) NULL)->d_name) == sizeof(((struct dirent64*) NULL)->d_name));
/* Start with space for up to 8 directory entries. We expect at least 2 ("." + ".."), hence hopefully
* array with readdir_all_freep().
*
* Start with space for up to 8 directory entries. We expect at least 2 ("." + ".."), hence hopefully
* 8 will cover most cases comprehensively. (Note that most likely a lot more entries will actually
* fit in the buffer, given we calculate maximum file name length here.) */
de = malloc(offsetof(DirectoryEntries, buffer) + DIRENT_SIZE_MAX * 8);
@ -71,12 +54,14 @@ int readdir_all(int dir_fd,
bs = MIN(MALLOC_SIZEOF_SAFE(de) - offsetof(DirectoryEntries, buffer), (size_t) SSIZE_MAX);
assert(bs > de->buffer_size);
n = getdents64(dir_fd, de->buffer + de->buffer_size, bs - de->buffer_size);
n = getdents64(dir_fd, (uint8_t*) de->buffer + de->buffer_size, bs - de->buffer_size);
if (n < 0)
return -errno;
if (n == 0)
break;
msan_unpoison((uint8_t*) de->buffer + de->buffer_size, n);
de->buffer_size += n;
if (de->buffer_size < bs - DIRENT_SIZE_MAX) /* Still room for one more entry, then try to
@ -95,10 +80,7 @@ int readdir_all(int dir_fd,
}
de->n_entries = 0;
for (struct dirent *entry = (struct dirent*) de->buffer;
(uint8_t*) entry < de->buffer + de->buffer_size;
entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
FOREACH_DIRENT_IN_BUFFER(entry, de->buffer, de->buffer_size) {
if (ignore_dirent(entry, flags))
continue;
@ -118,10 +100,7 @@ int readdir_all(int dir_fd,
de->entries = (struct dirent**) ((uint8_t*) de + ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size));
j = 0;
for (struct dirent *entry = (struct dirent*) de->buffer;
(uint8_t*) entry < de->buffer + de->buffer_size;
entry = (struct dirent*) ((uint8_t*) entry + entry->d_reclen)) {
FOREACH_DIRENT_IN_BUFFER(entry, de->buffer, de->buffer_size) {
if (ignore_dirent(entry, flags))
continue;

View File

@ -71,7 +71,7 @@ typedef struct DirectoryEntries {
size_t n_entries;
struct dirent** entries;
size_t buffer_size;
uint8_t buffer[] _alignas_(struct dirent);
struct dirent buffer[];
} DirectoryEntries;
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);

View File

@ -73,26 +73,40 @@ int is_device_node(const char *path) {
int dir_is_empty_at(int dir_fd, const char *path) {
_cleanup_close_ int fd = -1;
_cleanup_closedir_ DIR *d = NULL;
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
DEFINE_DIRENT_BUFFER(buffer, 3);
struct dirent *de;
ssize_t n;
if (path) {
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else if (dir_fd == AT_FDCWD) {
fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return -errno;
} else {
/* Note that DUPing is not enough, as the internal pointer
* would still be shared and moved by FOREACH_DIRENT. */
fd = fd_reopen(dir_fd, O_CLOEXEC);
/* Note that DUPing is not enough, as the internal pointer would still be shared and moved
* getedents64(). */
assert(dir_fd >= 0);
fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
if (fd < 0)
return fd;
}
d = take_fdopendir(&fd);
if (!d)
n = getdents64(fd, &buffer, sizeof(buffer));
if (n < 0)
return -errno;
FOREACH_DIRENT(de, d, return -errno)
msan_unpoison(&buffer, n);
FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
if (!dot_or_dot_dot(de->d_name))
return 0;
return 1;

View File

@ -9,6 +9,7 @@
#include "homework-mount.h"
#include "mount-util.h"
#include "process-util.h"
#include "stat-util.h"
#include "strv.h"
#include "tmpfile-util.h"
@ -143,8 +144,6 @@ int home_activate_cifs(
int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
_cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
_cleanup_(closedirp) DIR *d = NULL;
_cleanup_close_ int copy = -1;
int r;
assert(h);
@ -166,19 +165,11 @@ int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
if (r < 0)
return r;
copy = fcntl(setup->root_fd, F_DUPFD_CLOEXEC, 3);
if (copy < 0)
return -errno;
d = take_fdopendir(&copy);
if (!d)
return -errno;
errno = 0;
if (readdir_no_dot(d))
r = dir_is_empty_at(setup->root_fd, NULL);
if (r < 0)
return log_error_errno(r, "Failed to detect if CIFS directory is empty: %m");
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTEMPTY), "Selected CIFS directory not empty, refusing.");
if (errno != 0)
return log_error_errno(errno, "Failed to detect if CIFS directory is empty: %m");
r = home_populate(h, setup->root_fd);
if (r < 0)

View File

@ -8,8 +8,10 @@
int _e = (error); \
if (DEBUG_LOGGING) { \
const char *_n = NULL; \
type *_v = (val); \
\
(void) type##_get_ifname(val, &_n); \
if (_v) \
(void) type##_get_ifname(_v, &_n); \
log_interface_full_errno_zerook( \
_n, LOG_DEBUG, _e, prefix fmt, \
##__VA_ARGS__); \

View File

@ -421,35 +421,6 @@ static int dhcp6_pd_request_address(
return 0;
}
static int dhcp6_pd_assign_prefix(
Link *link,
const struct in6_addr *prefix,
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
int r;
assert(link);
assert(link->network);
assert(prefix);
if (link->network->dhcp6_pd_announce) {
r = radv_add_prefix(link, prefix, 64, lifetime_preferred_usec, lifetime_valid_usec);
if (r < 0)
return r;
}
r = dhcp6_pd_request_route(link, prefix, lifetime_valid_usec);
if (r < 0)
return r;
r = dhcp6_pd_request_address(link, prefix, lifetime_preferred_usec, lifetime_valid_usec);
if (r < 0)
return r;
return link_add_dhcp6_pd_prefix(link, prefix);
}
static bool link_has_preferred_subnet_id(Link *link) {
if (!link->network)
return false;
@ -457,77 +428,137 @@ static bool link_has_preferred_subnet_id(Link *link) {
return link->network->dhcp6_pd_subnet_id >= 0;
}
static int dhcp6_get_preferred_delegated_prefix(
static int dhcp6_pd_calculate_prefix(
const struct in6_addr *pd_prefix,
uint8_t pd_prefix_len,
uint64_t subnet_id,
struct in6_addr *ret) {
struct in6_addr prefix;
assert(pd_prefix);
assert(pd_prefix_len <= 64);
assert(ret);
if (subnet_id >= UINT64_C(1) << (64 - pd_prefix_len))
return -ERANGE;
prefix = *pd_prefix;
if (pd_prefix_len < 32)
prefix.s6_addr32[0] |= htobe32(subnet_id >> 32);
prefix.s6_addr32[1] |= htobe32(subnet_id & 0xffffffff);
*ret = prefix;
return 0;
}
static int dhcp6_pd_get_preferred_prefix(
Link *link,
const struct in6_addr *pd_prefix,
uint8_t pd_prefix_len,
struct in6_addr *ret) {
/* We start off with the original PD prefix we have been assigned and iterate from there */
union in_addr_union prefix;
uint64_t n_prefixes;
struct in6_addr prefix;
Link *assigned_link;
int r;
assert(link);
assert(link->manager);
assert(pd_prefix);
assert(pd_prefix_len <= 64);
n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
prefix.in6 = *pd_prefix;
if (link_has_preferred_subnet_id(link)) {
uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
/* If the link has a preference for a particular subnet id try to allocate that */
if (subnet_id >= n_prefixes)
return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE),
"subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
subnet_id, n_prefixes);
r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id);
r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, link->network->dhcp6_pd_subnet_id, &prefix);
if (r < 0)
return log_link_warning_errno(link, r,
"subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
subnet_id, n_prefixes);
link->network->dhcp6_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len));
/* Verify that the prefix we did calculate fits in the pd prefix.
* This should not fail as we checked the prefix size beforehand */
assert_se(in6_addr_prefix_covers(pd_prefix, pd_prefix_len, &prefix.in6) > 0);
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) >= 0 &&
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) >= 0 &&
assigned_link != link) {
_cleanup_free_ char *assigned_buf = NULL;
(void) in6_addr_to_string(&prefix.in6, &assigned_buf);
(void) in6_addr_to_string(&prefix, &assigned_buf);
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
"The requested prefix %s is already assigned to another link.",
strna(assigned_buf));
}
*ret = prefix.in6;
*ret = prefix;
return 0;
}
for (uint64_t n = 0; n < n_prefixes; n++) {
for (uint64_t n = 0; ; n++) {
r = dhcp6_pd_calculate_prefix(pd_prefix, pd_prefix_len, n, &prefix);
if (r < 0)
return log_link_warning_errno(link, r,
"Couldn't find a suitable prefix. Ran out of address space.");
/* If we do not have an allocation preference just iterate
* through the address space and return the first free prefix. */
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix.in6, &assigned_link) < 0 ||
if (link_get_by_dhcp6_pd_prefix(link->manager, &prefix, &assigned_link) < 0 ||
assigned_link == link) {
*ret = prefix.in6;
*ret = prefix;
return 0;
}
}
}
r = in_addr_prefix_next(AF_INET6, &prefix, 64);
static int dhcp6_pd_assign_prefix(
Link *link,
const struct in6_addr *pd_prefix,
uint8_t pd_prefix_len,
usec_t lifetime_preferred_usec,
usec_t lifetime_valid_usec) {
_cleanup_free_ char *buf = NULL;
struct in6_addr prefix;
int r;
assert(link);
assert(link->network);
assert(pd_prefix);
if (dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0 &&
dhcp6_pd_get_preferred_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0)
return 0;
(void) in6_addr_prefix_to_string(&prefix, 64, &buf);
if (link->network->dhcp6_pd_announce) {
r = radv_add_prefix(link, &prefix, 64, lifetime_preferred_usec, lifetime_valid_usec);
if (r < 0)
return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m");
return log_link_warning_errno(link, r,
"Failed to assign/update prefix %s to IPv6 Router Advertisement: %m",
strna(buf));
}
return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space.");
r = dhcp6_pd_request_route(link, &prefix, lifetime_valid_usec);
if (r < 0)
return log_link_warning_errno(link, r,
"Failed to assign/update route for prefix %s: %m",
strna(buf));
r = dhcp6_pd_request_address(link, &prefix, lifetime_preferred_usec, lifetime_valid_usec);
if (r < 0)
return log_link_warning_errno(link, r,
"Failed to assign/update address for prefix %s: %m",
strna(buf));
r = link_add_dhcp6_pd_prefix(link, &prefix);
if (r < 0)
return log_link_warning_errno(link, r,
"Failed to save assigned prefix %s: %m",
strna(buf));
log_link_debug(link, "Assigned prefix %s", strna(buf));
return 1;
}
static int dhcp6_pd_prefix_distribute(
static int dhcp6_pd_distribute_prefix(
Link *dhcp6_link,
const struct in6_addr *pd_prefix,
uint8_t pd_prefix_len,
@ -544,8 +575,6 @@ static int dhcp6_pd_prefix_distribute(
assert(pd_prefix_len <= 64);
HASHMAP_FOREACH(link, dhcp6_link->manager->links_by_index) {
_cleanup_free_ char *buf = NULL;
struct in6_addr assigned_prefix;
if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
continue;
@ -553,28 +582,23 @@ static int dhcp6_pd_prefix_distribute(
if (!link_dhcp6_pd_is_enabled(link))
continue;
if (link->network->dhcp6_pd_announce && !link->radv)
continue;
if (link == dhcp6_link && !link->network->dhcp6_pd_assign)
continue;
if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link))
continue;
if (dhcp6_pd_get_assigned_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0 &&
dhcp6_get_preferred_delegated_prefix(link, pd_prefix, pd_prefix_len, &assigned_prefix) < 0)
continue;
(void) in6_addr_prefix_to_string(&assigned_prefix, 64, &buf);
r = dhcp6_pd_assign_prefix(link, &assigned_prefix, lifetime_preferred_usec, lifetime_valid_usec);
r = dhcp6_pd_assign_prefix(link, pd_prefix, pd_prefix_len, lifetime_preferred_usec, lifetime_valid_usec);
if (r < 0) {
log_link_warning_errno(link, r, "Failed to assign/update prefix %s: %m", strna(buf));
if (link == dhcp6_link)
return r;
link_enter_failed(link);
continue;
}
log_link_debug(link, "Assigned prefix %s", strna(buf));
}
return 0;
@ -587,6 +611,9 @@ static int dhcp6_pd_prepare(Link *link) {
if (!link_dhcp6_pd_is_enabled(link))
return 0;
if (link->network->dhcp6_pd_announce && !link->radv)
return 0;
link_mark_addresses(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL);
link_mark_routes(link, NETWORK_CONFIG_SOURCE_DHCP6PD, NULL);
@ -602,6 +629,9 @@ static int dhcp6_pd_finalize(Link *link) {
if (!link_dhcp6_pd_is_enabled(link))
return 0;
if (link->network->dhcp6_pd_announce && !link->radv)
return 0;
if (link->dhcp6_pd_messages == 0) {
link->dhcp6_pd_configured = false;
@ -906,7 +936,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
n_prefixes, strna(buf));
}
r = dhcp6_pd_prefix_distribute(dhcp6_link,
r = dhcp6_pd_distribute_prefix(dhcp6_link,
&pd_prefix,
pd_prefix_len,
lifetime_preferred_usec,
@ -915,7 +945,7 @@ static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
if (r < 0)
return r;
r = dhcp6_pd_prefix_distribute(dhcp6_link,
r = dhcp6_pd_distribute_prefix(dhcp6_link,
&pd_prefix,
pd_prefix_len,
lifetime_preferred_usec,
@ -1535,10 +1565,12 @@ static int dhcp6_configure(Link *link) {
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation: %m");
}
if (link->network->dhcp6_pd_length > 0) {
r = sd_dhcp6_client_set_prefix_delegation_hint(client, link->network->dhcp6_pd_length, &link->network->dhcp6_pd_address);
if (link->network->dhcp6_pd_prefix_length > 0) {
r = sd_dhcp6_client_set_prefix_delegation_hint(client,
link->network->dhcp6_pd_prefix_length,
&link->network->dhcp6_pd_prefix_hint);
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix hint: %m");
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set prefix delegation hint: %m");
}
link->dhcp6_client = TAKE_PTR(client);
@ -1651,7 +1683,7 @@ int link_serialize_dhcp6_client(Link *link, FILE *f) {
return 0;
}
int config_parse_dhcp6_pd_hint(
int config_parse_dhcp6_pd_prefix_hint(
const char* unit,
const char *filename,
unsigned line,
@ -1663,7 +1695,7 @@ int config_parse_dhcp6_pd_hint(
void *data,
void *userdata) {
Network *network = data;
Network *network = userdata;
union in_addr_union u;
unsigned char prefixlen;
int r;
@ -1671,7 +1703,7 @@ int config_parse_dhcp6_pd_hint(
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
assert(userdata);
r = in_addr_prefix_from_string(rvalue, AF_INET6, &u, &prefixlen);
if (r < 0) {
@ -1686,8 +1718,8 @@ int config_parse_dhcp6_pd_hint(
return 0;
}
network->dhcp6_pd_address = u.in6;
network->dhcp6_pd_length = prefixlen;
network->dhcp6_pd_prefix_hint = u.in6;
network->dhcp6_pd_prefix_length = prefixlen;
return 0;
}

View File

@ -29,7 +29,7 @@ int link_request_dhcp6_client(Link *link);
int link_serialize_dhcp6_client(Link *link, FILE *f);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_prefix_hint);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id);

View File

@ -597,16 +597,9 @@ static int link_acquire_dynamic_ipv6_conf(Link *link) {
assert(link);
if (link->radv) {
assert(link->radv);
assert(in6_addr_is_link_local(&link->ipv6ll_address));
log_link_debug(link, "Starting IPv6 Router Advertisements");
r = sd_radv_start(link->radv);
r = radv_start(link);
if (r < 0)
return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
}
return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
r = ndisc_start(link);
if (r < 0)
@ -616,10 +609,6 @@ static int link_acquire_dynamic_ipv6_conf(Link *link) {
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start DHCPv6 client: %m");
r = dhcp6_request_prefix_delegation(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
return 0;
}
@ -662,6 +651,7 @@ static int link_acquire_dynamic_conf(Link *link) {
int r;
assert(link);
assert(link->network);
r = link_acquire_dynamic_ipv4_conf(link);
if (r < 0)
@ -673,6 +663,16 @@ static int link_acquire_dynamic_conf(Link *link) {
return r;
}
if (!link_radv_enabled(link) || !link->network->dhcp6_pd_announce) {
/* DHCPv6PD downstream does not require IPv6LL address. But may require RADV to be
* configured, and RADV may not be configured yet here. Only acquire subnet prefix when
* RADV is disabled, or the announcement of the prefix is disabled. Otherwise, the
* below will be called in radv_start(). */
r = dhcp6_request_prefix_delegation(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
}
if (link->lldp_tx) {
r = sd_lldp_tx_start(link->lldp_tx);
if (r < 0)

View File

@ -883,9 +883,51 @@ static void ndisc_mark(Link *link, const struct in6_addr *router) {
dnssl->marked = true;
}
static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
int r;
assert(link);
assert(link->network);
switch (link->network->ipv6_accept_ra_start_dhcp6_client) {
case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
return 0;
case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
uint64_t flags;
r = sd_ndisc_router_get_flags(rt, &flags);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
return 0;
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
* Note, if both managed and other information bits are set, then ignore other
* information bit. See RFC 4861. */
r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED));
break;
}
case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
/* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode
* even if the router flags have neither M nor O flags. */
r = dhcp6_request_information(link, false);
break;
default:
assert_not_reached();
}
if (r < 0)
return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
return 0;
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
struct in6_addr router;
uint64_t flags;
int r;
assert(link);
@ -912,26 +954,9 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
ndisc_mark(link, &router);
r = sd_ndisc_router_get_flags(rt, &flags);
r = ndisc_start_dhcp6_client(link, rt);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA flags: %m");
if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) &&
link->network->ipv6_accept_ra_start_dhcp6_client != IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO) ||
link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS) {
if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))
/* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED));
else
/* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode
* even if router does not have M or O flag. */
r = dhcp6_request_information(link, false);
if (r < 0 && r != -EBUSY)
return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
else
log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
}
return r;
r = ndisc_router_process_default(link, rt);
if (r < 0)

View File

@ -248,7 +248,7 @@ DHCPv6.UserClass, config_parse_dhcp_user_or_vendor_cl
DHCPv6.VendorClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_vendor_class)
DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options)
DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_hint, 0, 0
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_prefix_hint, 0, 0
DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_without_ra)
DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options)
DHCPv6.IAID, config_parse_iaid, AF_INET6, 0

View File

@ -183,11 +183,11 @@ struct Network {
bool dhcp6_iaid_set;
bool dhcp6_iaid_set_explicitly;
DUID dhcp6_duid;
uint8_t dhcp6_pd_length;
uint8_t dhcp6_pd_prefix_length;
struct in6_addr dhcp6_pd_prefix_hint;
char *dhcp6_mudurl;
char **dhcp6_user_class;
char **dhcp6_vendor_class;
struct in6_addr dhcp6_pd_address;
DHCP6ClientStartMode dhcp6_without_ra;
OrderedHashmap *dhcp6_client_send_options;
OrderedHashmap *dhcp6_client_send_vendor_options;

View File

@ -9,6 +9,7 @@
#include "dns-domain.h"
#include "networkd-address-generation.h"
#include "networkd-address.h"
#include "networkd-dhcp6.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
@ -49,7 +50,7 @@ void network_adjust_radv(Network *network) {
}
}
static bool link_radv_enabled(Link *link) {
bool link_radv_enabled(Link *link) {
assert(link);
if (!link_ipv6ll_enabled(link))
@ -596,7 +597,7 @@ int request_process_radv(Request *req) {
return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Advertisement engine: %m");
if (link_has_carrier(link)) {
r = sd_radv_start(link->radv);
r = radv_start(link);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to start IPv6 Router Advertisement engine: %m");
}
@ -625,6 +626,34 @@ int link_request_radv(Link *link) {
return 0;
}
int radv_start(Link *link) {
int r;
assert(link);
assert(link->network);
if (!link->radv)
return 0;
if (!link_has_carrier(link))
return 0;
if (in6_addr_is_null(&link->ipv6ll_address))
return 0;
if (sd_radv_is_running(link->radv))
return 0;
if (link->network->dhcp6_pd_announce) {
r = dhcp6_request_prefix_delegation(link);
if (r < 0)
return log_link_debug_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
}
log_link_debug(link, "Starting IPv6 Router Advertisements");
return sd_radv_start(link->radv);
}
int radv_add_prefix(
Link *link,
const struct in6_addr *prefix,

View File

@ -62,6 +62,8 @@ void network_adjust_radv(Network *network);
int link_request_radv_addresses(Link *link);
bool link_radv_enabled(Link *link);
int radv_start(Link *link);
int radv_update_mac(Link *link);
int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
usec_t lifetime_preferred_usec, usec_t lifetime_valid_usec);

View File

@ -8,10 +8,12 @@
#include "alloc-util.h"
#include "errno-list.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "mountpoint-util.h"
#include "namespace-util.h"
#include "path-util.h"
#include "rm-rf.h"
#include "stat-util.h"
#include "tests.h"
#include "tmpfile-util.h"
@ -223,6 +225,35 @@ static void test_device_path_make_canonical(void) {
}
}
static void test_dir_is_empty(void) {
_cleanup_(rm_rf_physical_and_freep) char *empty_dir = NULL;
_cleanup_free_ char *j = NULL, *jj = NULL;
log_info("/* %s */", __func__);
assert_se(dir_is_empty_at(AT_FDCWD, "/proc") == 0);
assert_se(dir_is_empty_at(AT_FDCWD, "/icertainlydontexistdoi") == -ENOENT);
assert_se(mkdtemp_malloc("/tmp/emptyXXXXXX", &empty_dir) >= 0);
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0);
j = path_join(empty_dir, "zzz");
assert_se(j);
assert_se(touch(j) >= 0);
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
jj = path_join(empty_dir, "ppp");
assert_se(jj);
assert_se(touch(jj) >= 0);
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
assert_se(unlink(j) >= 0);
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
assert_se(unlink(jj) >= 0);
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0);
}
int main(int argc, char *argv[]) {
log_show_color(true);
test_setup_logging(LOG_INFO);
@ -235,6 +266,7 @@ int main(int argc, char *argv[]) {
test_fd_is_ns();
test_device_major_minor_valid();
test_device_path_make_canonical();
test_dir_is_empty();
return 0;
}

View File

@ -639,6 +639,7 @@ testcase_long_sysfs_path() {
: >/failed
udevadm settle
udevadm control --log-level debug
lsblk -a
echo "Check if all symlinks under /dev/disk/ are valid (pre-test)"
@ -658,6 +659,8 @@ udevadm settle
echo "Check if all symlinks under /dev/disk/ are valid (post-test)"
helper_check_device_symlinks
udevadm control --log-level info
systemctl status systemd-udevd
touch /testok