mirror of
https://github.com/systemd/systemd
synced 2026-04-01 04:34:51 +02:00
Compare commits
17 Commits
6681eb021a
...
1845ba368e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1845ba368e | ||
|
|
b1ccd7d737 | ||
|
|
8a088877ab | ||
|
|
0f96a82311 | ||
|
|
928112a487 | ||
|
|
e817f5b0f9 | ||
|
|
a84171ce6f | ||
|
|
0dbce03c37 | ||
|
|
bfc569b060 | ||
|
|
d96f21eec2 | ||
|
|
a068aceafb | ||
|
|
a4e70ef7ba | ||
|
|
ca664db258 | ||
|
|
b9d0652263 | ||
|
|
df8bf72631 | ||
|
|
78e50edfd4 | ||
|
|
56bbe0ef02 |
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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(©);
|
||||
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)
|
||||
|
||||
@ -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__); \
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user