summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/string-util.h11
-rw-r--r--src/core/dbus-manager.c36
-rw-r--r--src/core/main.c20
-rw-r--r--src/id128/id128.c51
-rw-r--r--src/journal/journald-server.c12
-rw-r--r--src/libsystemd-network/dhcp-client-internal.h8
-rw-r--r--src/libsystemd-network/dhcp-internal.h3
-rw-r--r--src/libsystemd-network/dhcp-protocol.c18
-rw-r--r--src/libsystemd-network/dhcp-protocol.h22
-rw-r--r--src/libsystemd-network/dhcp6-client-internal.h10
-rw-r--r--src/libsystemd-network/dhcp6-internal.h3
-rw-r--r--src/libsystemd-network/fuzz-ndisc-rs.c27
-rw-r--r--src/libsystemd-network/icmp6-util-unix.c53
-rw-r--r--src/libsystemd-network/icmp6-util-unix.h9
-rw-r--r--src/libsystemd-network/icmp6-util.c22
-rw-r--r--src/libsystemd-network/icmp6-util.h8
-rw-r--r--src/libsystemd-network/meson.build17
-rw-r--r--src/libsystemd-network/ndisc-protocol.c34
-rw-r--r--src/libsystemd-network/ndisc-protocol.h31
-rw-r--r--src/libsystemd-network/ndisc-router.c114
-rw-r--r--src/libsystemd-network/radv-internal.h26
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c59
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c22
-rw-r--r--src/libsystemd-network/sd-ndisc.c8
-rw-r--r--src/libsystemd-network/sd-radv.c77
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c28
-rw-r--r--src/libsystemd-network/test-ndisc-rs.c39
-rw-r--r--src/libsystemd/libsystemd.sym5
-rw-r--r--src/libsystemd/sd-id128/id128-util.c17
-rw-r--r--src/libsystemd/sd-id128/id128-util.h2
-rw-r--r--src/libsystemd/sd-id128/sd-id128.c22
-rw-r--r--src/libsystemd/sd-journal/journal-file.c25
-rw-r--r--src/login/loginctl.c502
-rw-r--r--src/network/meson.build2
-rw-r--r--src/network/networkd-address-generation.c11
-rw-r--r--src/network/networkd-dhcp-common.c2
-rw-r--r--src/network/networkd-dhcp4-bus.c78
-rw-r--r--src/network/networkd-dhcp4-bus.h10
-rw-r--r--src/network/networkd-dhcp4.c5
-rw-r--r--src/network/networkd-dhcp6-bus.c76
-rw-r--r--src/network/networkd-dhcp6-bus.h10
-rw-r--r--src/network/networkd-dhcp6.c6
-rw-r--r--src/network/networkd-json.c32
-rw-r--r--src/network/networkd-link-bus.c6
-rw-r--r--src/network/networkd-link.h1
-rw-r--r--src/network/networkd-manager-bus.c5
-rw-r--r--src/network/networkd-ndisc.c168
-rw-r--r--src/network/networkd-ndisc.h9
-rw-r--r--src/network/networkd-network-gperf.gperf4
-rw-r--r--src/network/networkd-network.h5
-rw-r--r--src/network/networkd-radv.c56
-rw-r--r--src/network/networkd-radv.h1
-rw-r--r--src/nspawn/nspawn.c9
-rw-r--r--src/oom/oomd-manager.c15
-rw-r--r--src/partition/repart.c10
-rw-r--r--src/shared/conf-parser.c17
-rw-r--r--src/shared/format-table.c4
-rw-r--r--src/shared/id128-print.c12
-rw-r--r--src/shared/switch-root.c44
-rw-r--r--src/shared/switch-root.h3
-rw-r--r--src/shutdown/shutdown.c6
-rw-r--r--src/systemctl/systemctl-switch-root.c13
-rw-r--r--src/systemd/sd-id128.h1
-rw-r--r--src/systemd/sd-ndisc.h8
-rw-r--r--src/systemd/sd-radv.h5
-rw-r--r--src/test/test-id128.c17
-rw-r--r--src/test/test-string-util.c8
67 files changed, 1456 insertions, 544 deletions
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index 73d586d4c5..72f791f6cc 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -196,6 +196,17 @@ int strextendf_with_separator(char **x, const char *separator, const char *forma
char *strrep(const char *s, unsigned n);
+#define strrepa(s, n) \
+ ({ \
+ char *_d_, *_p_; \
+ size_t _len_ = strlen(s) * n; \
+ _p_ = _d_ = newa(char, _len_ + 1); \
+ for (unsigned _i_ = 0; _i_ < n; _i_++) \
+ _p_ = stpcpy(_p_, s); \
+ *_p_ = 0; \
+ _d_; \
+ })
+
int split_pair(const char *s, const char *sep, char **l, char **r);
int free_and_strdup(char **p, const char *s);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 245c5f14f1..3d1a38b21e 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -26,6 +26,7 @@
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "initrd-util.h"
#include "install.h"
#include "log.h"
#include "manager-dump.h"
@@ -1864,26 +1865,37 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (!path_is_valid(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory must be a valid path.");
+
if (!path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root path '%s' is not absolute.", root);
- if (path_equal(root, "/"))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
- "New root directory cannot be the old root directory.");
- }
- /* Safety check */
- if (isempty(init)) {
- r = path_is_os_tree(root);
+ r = path_is_root(root);
if (r < 0)
return sd_bus_error_set_errnof(error, r,
- "Failed to determine whether root path '%s' contains an OS tree: %m",
+ "Failed to check if new root directory '%s' is the same as old root: %m",
root);
- if (r == 0)
+ if (r > 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
- "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.",
- root);
- } else {
+ "New root directory cannot be the old root directory.");
+ }
+
+ /* Safety check */
+ if (!in_initrd())
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Not in initrd, refusing switch-root operation.");
+
+ r = path_is_os_tree(root);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r,
+ "Failed to determine whether root path '%s' contains an OS tree: %m",
+ root);
+ if (r == 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.",
+ root);
+
+ if (!isempty(init)) {
if (!path_is_valid(init))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Path to init binary '%s' is not a valid path.", init);
diff --git a/src/core/main.c b/src/core/main.c
index c54558f23b..279f1b16ce 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -242,20 +242,6 @@ static int console_setup(void) {
return 0;
}
-static int set_machine_id(const char *m) {
- sd_id128_t t;
- assert(m);
-
- if (sd_id128_from_string(m, &t) < 0)
- return -EINVAL;
-
- if (sd_id128_is_null(t))
- return -EINVAL;
-
- arg_machine_id = t;
- return 0;
-}
-
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
@@ -392,7 +378,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- r = set_machine_id(value);
+ r = id128_from_string_nonzero(value, &arg_machine_id);
if (r < 0)
log_warning_errno(r, "MachineID '%s' is not valid, ignoring: %m", value);
@@ -1045,7 +1031,7 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_MACHINE_ID:
- r = set_machine_id(optarg);
+ r = id128_from_string_nonzero(optarg, &arg_machine_id);
if (r < 0)
return log_error_errno(r, "MachineID '%s' is not valid: %m", optarg);
break;
@@ -1891,7 +1877,7 @@ static int do_reexecute(
r = switch_root(/* new_root= */ switch_root_dir,
/* old_root_after= */ NULL,
/* flags= */ (objective == MANAGER_SWITCH_ROOT ? SWITCH_ROOT_DESTROY_OLD_ROOT : 0) |
- (objective == MANAGER_SOFT_REBOOT ? SWITCH_ROOT_SKIP_RECURSIVE_RUN : 0));
+ (objective == MANAGER_SOFT_REBOOT ? 0 : SWITCH_ROOT_RECURSIVE_RUN));
if (r < 0)
log_error_errno(r, "Failed to switch root, trying to continue: %m");
}
diff --git a/src/id128/id128.c b/src/id128/id128.c
index a8c5c9ac6c..9e0a3488dd 100644
--- a/src/id128/id128.c
+++ b/src/id128/id128.c
@@ -16,6 +16,7 @@
static Id128PrettyPrintMode arg_mode = ID128_PRINT_ID128;
static sd_id128_t arg_app = {};
+static bool arg_value = false;
static int verb_new(int argc, char **argv, void *userdata) {
return id128_print_new(arg_mode);
@@ -67,8 +68,16 @@ static int verb_invocation_id(int argc, char **argv, void *userdata) {
}
static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first) {
+ sd_id128_t u;
int r;
+ assert(table);
+
+ if (sd_id128_is_null(arg_app))
+ u = uuid;
+ else
+ assert_se(sd_id128_get_app_specific(uuid, arg_app, &u) == 0);
+
if (arg_mode == ID128_PRINT_PRETTY) {
_cleanup_free_ char *id = NULL;
@@ -78,26 +87,28 @@ static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first
ascii_strupper(id);
- r = id128_pretty_print_sample(id, uuid);
+ r = id128_pretty_print_sample(id, u);
if (r < 0)
return r;
if (!first)
puts("");
return 0;
+ }
- } else {
- if (!*table) {
- *table = table_new("name", "id");
- if (!*table)
- return log_oom();
- table_set_width(*table, 0);
- }
+ if (arg_value)
+ return id128_pretty_print(u, arg_mode);
- return table_add_many(*table,
- TABLE_STRING, name,
- arg_mode == ID128_PRINT_ID128 ? TABLE_ID128 : TABLE_UUID,
- uuid);
+ if (!*table) {
+ *table = table_new("name", "id");
+ if (!*table)
+ return log_oom();
+ table_set_width(*table, 0);
}
+
+ return table_add_many(*table,
+ TABLE_STRING, name,
+ arg_mode == ID128_PRINT_ID128 ? TABLE_ID128 : TABLE_UUID,
+ u);
}
static int verb_show(int argc, char **argv, void *userdata) {
@@ -162,7 +173,7 @@ static int help(void) {
" machine-id Print the ID of current machine\n"
" boot-id Print the ID of current boot\n"
" invocation-id Print the ID of current invocation\n"
- " show [NAME] Print one or more well-known GPT partition type IDs\n"
+ " show [NAME|UUID] Print one or more UUIDs\n"
" help Show this help\n"
"\nOptions:\n"
" -h --help Show this help\n"
@@ -191,6 +202,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "pretty", no_argument, NULL, 'p' },
+ { "value", no_argument, NULL, 'P' },
{ "app-specific", required_argument, NULL, 'a' },
{ "uuid", no_argument, NULL, 'u' },
{},
@@ -201,7 +213,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "hpa:u", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "hpa:uP", options, NULL)) >= 0)
switch (c) {
case 'h':
@@ -212,10 +224,19 @@ static int parse_argv(int argc, char *argv[]) {
case 'p':
arg_mode = ID128_PRINT_PRETTY;
+ arg_value = false;
+ break;
+
+ case 'P':
+ arg_value = true;
+ if (arg_mode == ID128_PRINT_PRETTY)
+ arg_mode = ID128_PRINT_ID128;
break;
case 'a':
- r = sd_id128_from_string(optarg, &arg_app);
+ r = id128_from_string_nonzero(optarg, &arg_app);
+ if (r == -ENXIO)
+ return log_error_errno(r, "Application ID cannot be all zeros.");
if (r < 0)
return log_error_errno(r, "Failed to parse \"%s\" as application-ID: %m", optarg);
break;
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index 2932a190ff..897e2abd3a 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -871,12 +871,12 @@ static bool shall_try_append_again(JournalFile *f, int r) {
log_ratelimit_info_errno(r, JOURNAL_LOG_RATELIMIT, "%s: Realtime clock jumped backwards relative to last journal entry, rotating.", f->path);
return true;
- case -EREMOTE: /* Boot ID different from the one of the last entry */
- log_ratelimit_info_errno(r, JOURNAL_LOG_RATELIMIT, "%s: Boot ID changed since last record, rotating.", f->path);
- return true;
-
- case -ENOTNAM: /* Monotonic time (CLOCK_MONOTONIC) jumped backwards relative to last journal entry */
- log_ratelimit_info_errno(r, JOURNAL_LOG_RATELIMIT, "%s: Monotonic clock jumped backwards relative to last journal entry, rotating.", f->path);
+ case -ENOTNAM: /* Monotonic time (CLOCK_MONOTONIC) jumped backwards relative to last journal entry with the same boot ID */
+ log_ratelimit_info_errno(
+ r,
+ JOURNAL_LOG_RATELIMIT,
+ "%s: Monotonic clock jumped backwards relative to last journal entry with the same boot ID, rotating.",
+ f->path);
return true;
case -EILSEQ: /* seqnum ID last used in the file doesn't match the one we'd passed when writing an entry to it */
diff --git a/src/libsystemd-network/dhcp-client-internal.h b/src/libsystemd-network/dhcp-client-internal.h
index a6f37522d1..6f43975977 100644
--- a/src/libsystemd-network/dhcp-client-internal.h
+++ b/src/libsystemd-network/dhcp-client-internal.h
@@ -1,4 +1,12 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "sd-dhcp-client.h"
+
extern const struct hash_ops dhcp_option_hash_ops;
+
+int dhcp_client_set_state_callback(
+ sd_dhcp_client *client,
+ sd_dhcp_client_callback_t cb,
+ void *userdata);
+int dhcp_client_get_state(sd_dhcp_client *client);
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h
index e020db7bcd..d4e4a026b5 100644
--- a/src/libsystemd-network/dhcp-internal.h
+++ b/src/libsystemd-network/dhcp-internal.h
@@ -11,6 +11,7 @@
#include "sd-dhcp-client.h"
+#include "dhcp-client-internal.h"
#include "dhcp-protocol.h"
#include "ether-addr-util.h"
#include "network-common.h"
@@ -29,8 +30,6 @@ typedef struct DHCPServerData {
size_t size;
} DHCPServerData;
-extern const struct hash_ops dhcp_option_hash_ops;
-
typedef struct sd_dhcp_client sd_dhcp_client;
int dhcp_network_bind_raw_socket(
diff --git a/src/libsystemd-network/dhcp-protocol.c b/src/libsystemd-network/dhcp-protocol.c
new file mode 100644
index 0000000000..955d08720d
--- /dev/null
+++ b/src/libsystemd-network/dhcp-protocol.c
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "dhcp-protocol.h"
+#include "string-table.h"
+
+static const char* const dhcp_state_table[_DHCP_STATE_MAX] = {
+ [DHCP_STATE_STOPPED] = "stopped",
+ [DHCP_STATE_INIT] = "initialization",
+ [DHCP_STATE_SELECTING] = "selecting",
+ [DHCP_STATE_INIT_REBOOT] = "init-reboot",
+ [DHCP_STATE_REBOOTING] = "rebooting",
+ [DHCP_STATE_REQUESTING] = "requesting",
+ [DHCP_STATE_BOUND] = "bound",
+ [DHCP_STATE_RENEWING] = "renewing",
+ [DHCP_STATE_REBINDING] = "rebinding",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp_state, DHCPState);
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 2dc0660cc7..dd330ae839 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -55,15 +55,17 @@ enum {
};
enum DHCPState {
- DHCP_STATE_STOPPED = 0,
- DHCP_STATE_INIT = 1,
- DHCP_STATE_SELECTING = 2,
- DHCP_STATE_INIT_REBOOT = 3,
- DHCP_STATE_REBOOTING = 4,
- DHCP_STATE_REQUESTING = 5,
- DHCP_STATE_BOUND = 6,
- DHCP_STATE_RENEWING = 7,
- DHCP_STATE_REBINDING = 8,
+ DHCP_STATE_STOPPED,
+ DHCP_STATE_INIT,
+ DHCP_STATE_SELECTING,
+ DHCP_STATE_INIT_REBOOT,
+ DHCP_STATE_REBOOTING,
+ DHCP_STATE_REQUESTING,
+ DHCP_STATE_BOUND,
+ DHCP_STATE_RENEWING,
+ DHCP_STATE_REBINDING,
+ _DHCP_STATE_MAX,
+ _DHCP_STATE_INVALID = -EINVAL,
};
typedef enum DHCPState DHCPState;
@@ -107,3 +109,5 @@ enum {
DHCP_FQDN_FLAG_E = (1 << 2),
DHCP_FQDN_FLAG_N = (1 << 3),
};
+
+const char *dhcp_state_to_string(DHCPState s) _const_;
diff --git a/src/libsystemd-network/dhcp6-client-internal.h b/src/libsystemd-network/dhcp6-client-internal.h
new file mode 100644
index 0000000000..6c17f5749b
--- /dev/null
+++ b/src/libsystemd-network/dhcp6-client-internal.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp6-client.h"
+
+int dhcp6_client_set_state_callback(
+ sd_dhcp6_client *client,
+ sd_dhcp6_client_callback_t cb,
+ void *userdata);
+int dhcp6_client_get_state(sd_dhcp6_client *client);
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
index fa43f28eb5..97bc82d521 100644
--- a/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libsystemd-network/dhcp6-internal.h
@@ -12,6 +12,7 @@
#include "sd-dhcp6-client.h"
#include "dhcp-identifier.h"
+#include "dhcp6-client-internal.h"
#include "dhcp6-option.h"
#include "dhcp6-protocol.h"
#include "ether-addr-util.h"
@@ -79,6 +80,8 @@ struct sd_dhcp6_client {
sd_dhcp6_client_callback_t callback;
void *userdata;
+ sd_dhcp6_client_callback_t state_callback;
+ void *state_userdata;
bool send_release;
/* Ignore machine-ID when generating DUID. See dhcp_identifier_set_duid_en(). */
diff --git a/src/libsystemd-network/fuzz-ndisc-rs.c b/src/libsystemd-network/fuzz-ndisc-rs.c
index 7a5c45a2f9..54dbfc1c76 100644
--- a/src/libsystemd-network/fuzz-ndisc-rs.c
+++ b/src/libsystemd-network/fuzz-ndisc-rs.c
@@ -9,35 +9,10 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fuzz.h"
-#include "icmp6-util.h"
+#include "icmp6-util-unix.h"
#include "ndisc-internal.h"
#include "socket-util.h"
-static int test_fd[2] = PIPE_EBADF;
-
-int icmp6_bind_router_solicitation(int index) {
- assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
- return test_fd[0];
-}
-
-int icmp6_bind_router_advertisement(int index) {
- return -ENOSYS;
-}
-
-int icmp6_receive(int fd, void *iov_base, size_t iov_len,
- struct in6_addr *dst, triple_timestamp *timestamp) {
- assert_se(read(fd, iov_base, iov_len) == (ssize_t) iov_len);
-
- if (timestamp)
- triple_timestamp_get(timestamp);
-
- return 0;
-}
-
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- return 0;
-}
-
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
struct ether_addr mac_addr = {
.ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
diff --git a/src/libsystemd-network/icmp6-util-unix.c b/src/libsystemd-network/icmp6-util-unix.c
new file mode 100644
index 0000000000..bbb3bc8cc6
--- /dev/null
+++ b/src/libsystemd-network/icmp6-util-unix.c
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <netinet/ip6.h>
+#include <unistd.h>
+
+#include "fd-util.h"
+#include "icmp6-util-unix.h"
+
+send_ra_t send_ra_function = NULL;
+int test_fd[2] = PIPE_EBADF;
+
+static struct in6_addr dummy_link_local = {
+ .s6_addr = {
+ 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x12, 0x34, 0x56, 0xff, 0xfe, 0x78, 0x9a, 0xbc,
+ },
+};
+
+int icmp6_bind_router_solicitation(int ifindex) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
+ return -errno;
+
+ return test_fd[0];
+}
+
+int icmp6_bind_router_advertisement(int ifindex) {
+ return test_fd[1];
+}
+
+int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
+ if (!send_ra_function)
+ return 0;
+
+ return send_ra_function(0);
+}
+
+int icmp6_receive(
+ int fd,
+ void *iov_base,
+ size_t iov_len,
+ struct in6_addr *ret_sender,
+ triple_timestamp *ret_timestamp) {
+
+ assert_se(read (fd, iov_base, iov_len) == (ssize_t) iov_len);
+
+ if (ret_timestamp)
+ triple_timestamp_get(ret_timestamp);
+
+ if (ret_sender)
+ *ret_sender = dummy_link_local;
+
+ return 0;
+}
diff --git a/src/libsystemd-network/icmp6-util-unix.h b/src/libsystemd-network/icmp6-util-unix.h
new file mode 100644
index 0000000000..a9cb05a96e
--- /dev/null
+++ b/src/libsystemd-network/icmp6-util-unix.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "icmp6-util.h"
+
+typedef int (*send_ra_t)(uint8_t flags);
+
+extern send_ra_t send_ra_function;
+extern int test_fd[2];
diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c
index ecddab61e4..ecd0dd0f31 100644
--- a/src/libsystemd-network/icmp6-util.c
+++ b/src/libsystemd-network/icmp6-util.c
@@ -144,8 +144,12 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
return 0;
}
-int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
- triple_timestamp *ret_timestamp) {
+int icmp6_receive(
+ int fd,
+ void *buffer,
+ size_t size,
+ struct in6_addr *ret_sender,
+ triple_timestamp *ret_timestamp) {
/* This needs to be initialized with zero. See #20741. */
CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
@@ -178,7 +182,7 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
sa.in6.sin6_family == AF_INET6) {
addr = sa.in6.sin6_addr;
- if (!in6_addr_is_link_local(&addr))
+ if (!in6_addr_is_link_local(&addr) && !in6_addr_is_null(&addr))
return -EADDRNOTAVAIL;
} else if (msg.msg_namelen > 0)
@@ -206,10 +210,14 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
}
}
- if (!triple_timestamp_is_set(&t))
- triple_timestamp_get(&t);
+ if (ret_timestamp) {
+ if (triple_timestamp_is_set(&t))
+ *ret_timestamp = t;
+ else
+ triple_timestamp_get(ret_timestamp);
+ }
- *ret_dst = addr;
- *ret_timestamp = t;
+ if (ret_sender)
+ *ret_sender = addr;
return 0;
}
diff --git a/src/libsystemd-network/icmp6-util.h b/src/libsystemd-network/icmp6-util.h
index f7ad26b5e6..0a9ecb4c44 100644
--- a/src/libsystemd-network/icmp6-util.h
+++ b/src/libsystemd-network/icmp6-util.h
@@ -20,5 +20,9 @@
int icmp6_bind_router_solicitation(int ifindex);
int icmp6_bind_router_advertisement(int ifindex);
int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
-int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *ret_dst,
- triple_timestamp *ret_timestamp);
+int icmp6_receive(
+ int fd,
+ void *buffer,
+ size_t size,
+ struct in6_addr *ret_sender,
+ triple_timestamp *ret_timestamp);
diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build
index 043d3bc254..3eba57f991 100644
--- a/src/libsystemd-network/meson.build
+++ b/src/libsystemd-network/meson.build
@@ -6,12 +6,14 @@ sources = files(
'dhcp-network.c',
'dhcp-option.c',
'dhcp-packet.c',
+ 'dhcp-protocol.c',
'dhcp6-network.c',
'dhcp6-option.c',
'dhcp6-protocol.c',
'icmp6-util.c',
'lldp-neighbor.c',
'lldp-network.c',
+ 'ndisc-protocol.c',
'ndisc-router.c',
'network-common.c',
'network-internal.c',
@@ -82,10 +84,16 @@ executables += [
'sources' : files('test-lldp-rx.c'),
},
network_test_template + {
- 'sources' : files('test-ndisc-ra.c'),
+ 'sources' : files(
+ 'test-ndisc-ra.c',
+ 'icmp6-util-unix.c',
+ ),
},
network_test_template + {
- 'sources' : files('test-ndisc-rs.c'),
+ 'sources' : files(
+ 'test-ndisc-rs.c',
+ 'icmp6-util-unix.c',
+ ),
},
network_test_template + {
'sources' : files('test-sd-dhcp-lease.c'),
@@ -106,6 +114,9 @@ executables += [
'sources' : files('fuzz-lldp-rx.c'),
},
network_fuzz_template + {
- 'sources' : files('fuzz-ndisc-rs.c'),
+ 'sources' : files(
+ 'fuzz-ndisc-rs.c',
+ 'icmp6-util-unix.c',
+ ),
},
]
diff --git a/src/libsystemd-network/ndisc-protocol.c b/src/libsystemd-network/ndisc-protocol.c
new file mode 100644
index 0000000000..fae4a583ad
--- /dev/null
+++ b/src/libsystemd-network/ndisc-protocol.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "ndisc-protocol.h"
+
+static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
+ [PREFIX_LENGTH_CODE_96] = 96,
+ [PREFIX_LENGTH_CODE_64] = 64,
+ [PREFIX_LENGTH_CODE_56] = 56,
+ [PREFIX_LENGTH_CODE_48] = 48,
+ [PREFIX_LENGTH_CODE_40] = 40,
+ [PREFIX_LENGTH_CODE_32] = 32,
+};
+
+int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret) {
+ plc &= PREF64_PLC_MASK;
+ if (plc >= _PREFIX_LENGTH_CODE_MAX)
+ return -EINVAL;
+
+ if (ret)
+ *ret = prefix_length_code_to_prefix_length[plc];
+ return 0;
+}
+
+int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret) {
+ assert(ret);
+
+ for (size_t i = 0; i < ELEMENTSOF(prefix_length_code_to_prefix_length); i++)
+ if (prefix_length_code_to_prefix_length[i] == prefixlen) {
+ *ret = i;
+ return 0;
+ }
+
+ return -EINVAL;
+}
diff --git a/src/libsystemd-network/ndisc-protocol.h b/src/libsystemd-network/ndisc-protocol.h
new file mode 100644
index 0000000000..8e403e3425
--- /dev/null
+++ b/src/libsystemd-network/ndisc-protocol.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "time-util.h"
+
+/* RFC 8781: PREF64 or (NAT64 prefix) */
+#define PREF64_SCALED_LIFETIME_MASK 0xfff8
+#define PREF64_PLC_MASK 0x0007
+#define PREF64_MAX_LIFETIME_USEC (65528 * USEC_PER_SEC)
+
+typedef enum PrefixLengthCode {
+ PREFIX_LENGTH_CODE_96,
+ PREFIX_LENGTH_CODE_64,
+ PREFIX_LENGTH_CODE_56,
+ PREFIX_LENGTH_CODE_48,
+ PREFIX_LENGTH_CODE_40,
+ PREFIX_LENGTH_CODE_32,
+ _PREFIX_LENGTH_CODE_MAX,
+ _PREFIX_LENGTH_CODE_INVALID = -EINVAL,
+} PrefixLengthCode;
+
+/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PREFIX_LEN (Prefix Length Code): 3-bit unsigned integer */
+struct nd_opt_prefix64_info {
+ uint8_t type;
+ uint8_t length;
+ uint16_t lifetime_and_plc;
+ uint8_t prefix[12];
+} __attribute__((__packed__));
+
+int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret);
+int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret);
diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c
index 86f8ca0364..fe8d26ebd4 100644
--- a/src/libsystemd-network/ndisc-router.c
+++ b/src/libsystemd-network/ndisc-router.c
@@ -13,6 +13,7 @@
#include "memory-util.h"
#include "missing_network.h"
#include "ndisc-internal.h"
+#include "ndisc-protocol.h"
#include "ndisc-router.h"
#include "strv.h"
@@ -69,6 +70,21 @@ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size)
return 0;
}
+static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t length) {
+ uint16_t lifetime_and_plc;
+
+ assert(p);
+
+ if (length != sizeof(struct nd_opt_prefix64_info))
+ return false;
+
+ lifetime_and_plc = be16toh(p->lifetime_and_plc);
+ if (pref64_plc_to_prefix_length(lifetime_and_plc, NULL) < 0)
+ return false;
+
+ return true;
+}
+
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
struct nd_router_advert *a;
const uint8_t *p;
@@ -205,7 +221,12 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
"DNSSL option has invalid size.");
break;
- }
+ case SD_NDISC_OPTION_PREF64: {
+ if (!pref64_option_verify((struct nd_opt_prefix64_info *) p, length))
+ log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
+ "PREF64 prefix has invalid prefix length.");
+ break;
+ }}
p += length, left -= length;
}
@@ -766,3 +787,94 @@ int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **ret
return 0;
}
+
+static int get_pref64_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix64_info **ret) {
+ struct nd_opt_prefix64_info *ri;
+ size_t length;
+ int r;
+
+ assert(rt);
+ assert(ret);
+
+ r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREF64);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -EMEDIUMTYPE;
+
+ length = NDISC_ROUTER_OPTION_LENGTH(rt);
+ if (length != sizeof(struct nd_opt_prefix64_info))
+ return -EBADMSG;
+
+ ri = (struct nd_opt_prefix64_info *) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
+ if (!pref64_option_verify(ri, length))
+ return -EBADMSG;
+
+ *ret = ri;
+ return 0;
+}
+
+int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
+ struct nd_opt_prefix64_info *pi;
+ struct in6_addr a = {};
+ unsigned prefixlen;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret_addr, -EINVAL);
+
+ r = get_pref64_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefixlen);
+ if (r < 0)
+ return r;
+
+ memcpy(&a, pi->prefix, sizeof(pi->prefix));
+ in6_addr_mask(&a, prefixlen);
+ /* extra safety check for refusing malformed prefix. */
+ if (memcmp(&a, pi->prefix, sizeof(pi->prefix)) != 0)
+ return -EBADMSG;
+
+ *ret_addr = a;
+ return 0;
+}
+
+int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
+ struct nd_opt_prefix64_info *pi;
+ uint16_t lifetime_prefix_len;
+ uint8_t prefix_len;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_pref64_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
+ pref64_plc_to_prefix_length(lifetime_prefix_len, &prefix_len);
+
+ *ret = prefix_len;
+ return 0;
+}
+
+int sd_ndisc_router_prefix64_get_lifetime_sec(sd_ndisc_router *rt, uint16_t *ret) {
+ struct nd_opt_prefix64_info *pi;
+ uint16_t lifetime_prefix_len;
+ int r;
+
+ assert_return(rt, -EINVAL);
+ assert_return(ret, -EINVAL);
+
+ r = get_pref64_prefix_info(rt, &pi);
+ if (r < 0)
+ return r;
+
+ lifetime_prefix_len = be16toh(pi->lifetime_and_plc);
+
+ *ret = lifetime_prefix_len & PREF64_SCALED_LIFETIME_MASK;
+ return 0;
+}
diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h
index 534d795982..af0be7a5b7 100644
--- a/src/libsystemd-network/radv-internal.h
+++ b/src/libsystemd-network/radv-internal.h
@@ -5,9 +5,12 @@
Copyright © 2017 Intel Corporation. All rights reserved.
***/
+#include <netinet/icmp6.h>
+
#include "sd-radv.h"
#include "list.h"
+#include "ndisc-protocol.h"
#include "network-common.h"
#include "sparse-endian.h"
#include "time-util.h"
@@ -65,6 +68,11 @@
/* Pref64 option type (RFC8781, section 4) */
#define RADV_OPT_PREF64 38
+/* rfc6275 7.4 Neighbor Discovery Home Agent Lifetime.
+ * The default value is the same as the Router Lifetime
+ * The maximum value corresponds to 18.2 hours. value of 0 MUST NOT be used.*/
+#define RADV_MAX_HOME_AGENT_LIFETIME_USEC (65535 * USEC_PER_SEC)
+
enum RAdvState {
RADV_STATE_IDLE = 0,
RADV_STATE_ADVERTISING = 1,
@@ -112,6 +120,9 @@ struct sd_radv {
size_t n_rdnss;
struct sd_radv_opt_dns *rdnss;
struct sd_radv_opt_dns *dnssl;
+
+ /* Mobile IPv6 extension: Home Agent Info. */
+ struct nd_opt_home_agent_info home_agent;
};
#define radv_prefix_opt__contents { \
@@ -180,23 +191,10 @@ struct sd_radv_route_prefix {
usec_t valid_until;
};
-/* rfc8781: section 4 - Scaled Lifetime: 13-bit unsigned integer. PLC (Prefix Length Code): 3-bit unsigned integer */
-#define radv_pref64_prefix_opt__contents { \
- uint8_t type; \
- uint8_t length; \
- uint16_t lifetime_and_plc; \
- uint8_t prefix[12]; \
-}
-
-struct radv_pref64_prefix_opt radv_pref64_prefix_opt__contents;
-
-struct radv_pref64_prefix_opt__packed radv_pref64_prefix_opt__contents _packed_;
-assert_cc(sizeof(struct radv_pref64_prefix_opt) == sizeof(struct radv_pref64_prefix_opt__packed));
-
struct sd_radv_pref64_prefix {
unsigned n_ref;
- struct radv_pref64_prefix_opt opt;
+ struct nd_opt_prefix64_info opt;
struct in6_addr in6_addr;
uint8_t prefixlen;
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index dd222edd82..9ec7a2256e 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -118,6 +118,8 @@ struct sd_dhcp_client {
sd_event_source *timeout_expire;
sd_dhcp_client_callback_t callback;
void *userdata;
+ sd_dhcp_client_callback_t state_callback;
+ void *state_userdata;
sd_dhcp_lease *lease;
usec_t start_delay;
int ip_service_type;
@@ -226,6 +228,19 @@ int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
return 0;
}
+int dhcp_client_set_state_callback(
+ sd_dhcp_client *client,
+ sd_dhcp_client_callback_t cb,
+ void *userdata) {
+
+ assert_return(client, -EINVAL);
+
+ client->state_callback = cb;
+ client->state_userdata = userdata;
+
+ return 0;
+}
+
int sd_dhcp_client_set_callback(
sd_dhcp_client *client,
sd_dhcp_client_callback_t cb,
@@ -742,6 +757,27 @@ int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t
return 0;
}
+static void client_set_state(sd_dhcp_client *client, DHCPState state) {
+ assert(client);
+
+ if (client->state == state)
+ return;
+
+ log_dhcp_client(client, "State changed: %s -> %s",
+ dhcp_state_to_string(client->state), dhcp_state_to_string(state));
+
+ client->state = state;
+
+ if (client->state_callback)
+ client->state_callback(client, state, client->state_userdata);
+}
+
+int dhcp_client_get_state(sd_dhcp_client *client) {
+ assert_return(client, -EINVAL);
+
+ return client->state;
+}
+
static int client_notify(sd_dhcp_client *client, int event) {
assert(client);
@@ -765,7 +801,7 @@ static int client_initialize(sd_dhcp_client *client) {
client->attempt = 0;
- client->state = DHCP_STATE_STOPPED;
+ client_set_state(client, DHCP_STATE_STOPPED);
client->xid = 0;
client->lease = sd_dhcp_lease_unref(client->lease);
@@ -1195,6 +1231,7 @@ static int client_send_request(sd_dhcp_client *client) {
case DHCP_STATE_REBOOTING:
case DHCP_STATE_BOUND:
case DHCP_STATE_STOPPED:
+ default:
return -EINVAL;
}
@@ -1319,7 +1356,7 @@ static int client_timeout_resend(
case DHCP_STATE_INIT:
r = client_send_discover(client);
if (r >= 0) {
- client->state = DHCP_STATE_SELECTING;
+ client_set_state(client, DHCP_STATE_SELECTING);
client->attempt = 0;
} else if (client->attempt >= client->max_attempts)
goto error;
@@ -1342,7 +1379,7 @@ static int client_timeout_resend(
goto error;
if (client->state == DHCP_STATE_INIT_REBOOT)
- client->state = DHCP_STATE_REBOOTING;
+ client_set_state(client, DHCP_STATE_REBOOTING);
client->request_sent = time_now;
break;
@@ -1352,6 +1389,7 @@ static int client_timeout_resend(
break;
case DHCP_STATE_STOPPED:
+ default:
r = -EINVAL;
goto error;
}
@@ -1491,7 +1529,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata)
client->receive_message = sd_event_source_disable_unref(client->receive_message);
client->fd = safe_close(client->fd);
- client->state = DHCP_STATE_REBINDING;
+ client_set_state(client, DHCP_STATE_REBINDING);
client->attempt = 0;
r = dhcp_network_bind_raw_socket(client->ifindex, &client->link, client->xid,
@@ -1512,9 +1550,9 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
DHCP_CLIENT_DONT_DESTROY(client);
if (client->lease)
- client->state = DHCP_STATE_RENEWING;
+ client_set_state(client, DHCP_STATE_RENEWING);
else if (client->state != DHCP_STATE_INIT)
- client->state = DHCP_STATE_INIT_REBOOT;
+ client_set_state(client, DHCP_STATE_INIT_REBOOT);
client->attempt = 0;
return client_initialize_time_events(client);
@@ -1794,7 +1832,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
if (r < 0)
goto error;
- client->state = DHCP_STATE_REQUESTING;
+ client_set_state(client, DHCP_STATE_REQUESTING);
client->attempt = 0;
r = event_reset_time(client->event, &client->timeout_resend,
@@ -1843,7 +1881,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i
client->receive_message = sd_event_source_disable_unref(client->receive_message);
client->fd = safe_close(client->fd);
- client->state = DHCP_STATE_BOUND;
+ client_set_state(client, DHCP_STATE_BOUND);
client->attempt = 0;
client->last_addr = client->lease->address;
@@ -2059,7 +2097,7 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
client->start_delay = 0;
client->attempt = 1;
- client->state = DHCP_STATE_RENEWING;
+ client_set_state(client, DHCP_STATE_RENEWING);
return client_initialize_time_events(client);
}
@@ -2095,7 +2133,7 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
the client MAY issue a DHCPREQUEST to try to reclaim the current
address. */
if (client->last_addr && !client->anonymize)
- client->state = DHCP_STATE_INIT_REBOOT;
+ client_set_state(client, DHCP_STATE_INIT_REBOOT);
r = client_start(client);
if (r >= 0)
@@ -2188,7 +2226,6 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) {
DHCP_CLIENT_DONT_DESTROY(client);
client_stop(client, SD_DHCP_CLIENT_EVENT_STOP);
- client->state = DHCP_STATE_STOPPED;
return 0;
}
diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
index 8957e1cf4b..fb209a0c62 100644
--- a/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libsystemd-network/sd-dhcp6-client.c
@@ -47,6 +47,19 @@ int sd_dhcp6_client_set_callback(
return 0;
}
+int dhcp6_client_set_state_callback(
+ sd_dhcp6_client *client,
+ sd_dhcp6_client_callback_t cb,
+ void *userdata) {
+
+ assert_return(client, -EINVAL);
+
+ client->state_callback = cb;
+ client->state_userdata = userdata;
+
+ return 0;
+}
+
int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
assert_return(client, -EINVAL);
assert_return(!sd_dhcp6_client_is_running(client), -EBUSY);
@@ -553,6 +566,15 @@ static void client_set_state(sd_dhcp6_client *client, DHCP6State state) {
dhcp6_state_to_string(client->state), dhcp6_state_to_string(state));
client->state = state;
+
+ if (client->state_callback)
+ client->state_callback(client, state, client->state_userdata);
+}
+
+int dhcp6_client_get_state(sd_dhcp6_client *client) {
+ assert_return(client, -EINVAL);
+
+ return client->state;
}
static void client_notify(sd_dhcp6_client *client, int event) {
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index ca5365b95f..d7d9e7e2d9 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -225,8 +225,7 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
if (r < 0)
switch (r) {
case -EADDRNOTAVAIL:
- log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.",
- IN6_ADDR_TO_STRING(&rt->address));
+ log_ndisc(nd, "Received RA from neither link-local nor null address. Ignoring.");
return 0;
case -EMULTIHOP:
@@ -242,6 +241,11 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
return 0;
}
+ /* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states
+ * that hosts MUST discard messages with the null source address. */
+ if (in6_addr_is_null(&rt->address))
+ log_ndisc(nd, "Received RA from null address. Ignoring.");
+
(void) event_source_disable(nd->timeout_event_source);
(void) ndisc_handle_datagram(nd, rt);
return 0;
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c
index cb7bc07f5a..a35311efb9 100644
--- a/src/libsystemd-network/sd-radv.c
+++ b/src/libsystemd-network/sd-radv.c
@@ -231,6 +231,17 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us
if (ra->dnssl)
iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->dnssl, ra->dnssl->length * 8);
+ if (FLAGS_SET(ra->flags, ND_RA_FLAG_HOME_AGENT)) {
+ ra->home_agent.nd_opt_home_agent_info_type = ND_OPT_HOME_AGENT_INFO;
+ ra->home_agent.nd_opt_home_agent_info_len = 1;
+
+ /* 0 means to place the current Router Lifetime value */
+ if (ra->home_agent.nd_opt_home_agent_info_lifetime == 0)
+ ra->home_agent.nd_opt_home_agent_info_lifetime = adv.nd_ra_router_lifetime;
+
+ iov[msg.msg_iovlen++] = IOVEC_MAKE(&ra->home_agent, sizeof(ra->home_agent));
+ }
+
if (sendmsg(ra->fd, &msg, 0) < 0)
return -errno;
@@ -264,8 +275,7 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
if (r < 0)
switch (r) {
case -EADDRNOTAVAIL:
- log_radv(ra, "Received RS from non-link-local address %s. Ignoring",
- IN6_ADDR_TO_STRING(&src));
+ log_radv(ra, "Received RS from neither link-local nor null address. Ignoring");
return 0;
case -EMULTIHOP:
@@ -286,6 +296,9 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat
return 0;
}
+ /* TODO: if the sender address is null, check that the message does not have the source link-layer
+ * address option. See RFC 4861 Section 6.1.1. */
+
const char *addr = IN6_ADDR_TO_STRING(&src);
r = radv_send(ra, &src, ra->lifetime_usec);
@@ -570,6 +583,39 @@ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
return 0;
}
+int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent) {
+ assert_return(ra, -EINVAL);
+
+ if (ra->state != RADV_STATE_IDLE)
+ return -EBUSY;
+
+ SET_FLAG(ra->flags, ND_RA_FLAG_HOME_AGENT, home_agent);
+
+ return 0;
+}
+
+int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference) {
+ assert_return(ra, -EINVAL);
+
+ if (ra->state != RADV_STATE_IDLE)
+ return -EBUSY;
+
+ ra->home_agent.nd_opt_home_agent_info_preference = htobe16(preference);
+
+ return 0;
+}
+
+int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint16_t lifetime) {
+ assert_return(ra, -EINVAL);
+
+ if (ra->state != RADV_STATE_IDLE)
+ return -EBUSY;
+
+ ra->home_agent.nd_opt_home_agent_info_lifetime = htobe16(lifetime);
+
+ return 0;
+}
+
int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
sd_radv_prefix *found = NULL;
int r;
@@ -1091,33 +1137,14 @@ int sd_radv_pref64_prefix_set_prefix(
uint16_t pref64_lifetime;
uint8_t prefixlen_code;
+ int r;
assert_return(p, -EINVAL);
assert_return(prefix, -EINVAL);
- switch (prefixlen) {
- case 96:
- prefixlen_code = 0;
- break;
- case 64:
- prefixlen_code = 1;
- break;
- case 56:
- prefixlen_code = 2;
- break;
- case 48:
- prefixlen_code = 3;
- break;
- case 40:
- prefixlen_code = 4;
- break;
- case 32:
- prefixlen_code = 5;
- break;
- default:
- log_radv(NULL, "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
- return -EINVAL;
- }
+ r = pref64_prefix_length_to_plc(prefixlen, &prefixlen_code);
+ if (r < 0)
+ return log_radv_errno(NULL, r, "Unsupported PREF64 prefix length %u. Valid lengths are 32, 40, 48, 56, 64 and 96", prefixlen);
if (lifetime_usec == USEC_INFINITY || DIV_ROUND_UP(lifetime_usec, 8 * USEC_PER_SEC) >= UINT64_C(1) << 13)
return -EINVAL;
diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c
index bd8c0fd426..18004f6466 100644
--- a/src/libsystemd-network/test-ndisc-ra.c
+++ b/src/libsystemd-network/test-ndisc-ra.c
@@ -11,7 +11,7 @@
#include "alloc-util.h"
#include "hexdecoct.h"
-#include "icmp6-util.h"
+#include "icmp6-util-unix.h"
#include "socket-util.h"
#include "strv.h"
#include "tests.h"
@@ -52,7 +52,6 @@ static uint8_t advertisement[] = {
};
static bool test_stopped;
-static int test_fd[2];
static struct {
struct in6_addr address;
unsigned char prefixlen;
@@ -208,31 +207,6 @@ TEST(radv) {
assert_se(!ra);
}
-int icmp6_bind_router_solicitation(int ifindex) {
- return -ENOSYS;
-}
-
-int icmp6_bind_router_advertisement(int ifindex) {
- assert_se(ifindex == 42);
-
- return test_fd[1];
-}
-
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
-
- return 0;
-}
-
-int icmp6_receive(int fd, void *iov_base, size_t iov_len,
- struct in6_addr *dst, triple_timestamp *timestamp) {
- assert_se(read (fd, iov_base, iov_len) == (ssize_t)iov_len);
-
- if (timestamp)
- triple_timestamp_get(timestamp);
-
- return 0;
-}
-
static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
sd_radv *ra = userdata;
unsigned char buf[168];
diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c
index e501b64377..09e292a2ec 100644
--- a/src/libsystemd-network/test-ndisc-rs.c
+++ b/src/libsystemd-network/test-ndisc-rs.c
@@ -12,7 +12,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "hexdecoct.h"
-#include "icmp6-util.h"
+#include "icmp6-util-unix.h"
#include "socket-util.h"
#include "strv.h"
#include "ndisc-internal.h"
@@ -23,12 +23,8 @@ static struct ether_addr mac_addr = {
};
static bool verbose = false;
-static int test_fd[2];
static sd_ndisc *test_timeout_nd;
-typedef int (*send_ra_t)(uint8_t flags);
-static send_ra_t send_ra_function;
-
static void router_dump(sd_ndisc_router *rt) {
struct in6_addr addr;
uint8_t hop_limit;
@@ -41,7 +37,8 @@ static void router_dump(sd_ndisc_router *rt) {
assert_se(rt);
log_info("--");
- assert_se(sd_ndisc_router_get_address(rt, &addr) == -ENODATA);
+ assert_se(sd_ndisc_router_get_address(rt, &addr) >= 0);
+ log_info("Sender: %s", IN6_ADDR_TO_STRING(&addr));
assert_se(sd_ndisc_router_get_timestamp(rt, CLOCK_REALTIME, &t) >= 0);
log_info("Timestamp: %s", FORMAT_TIMESTAMP(t));
@@ -163,29 +160,6 @@ static void router_dump(sd_ndisc_router *rt) {
}
}
-int icmp6_bind_router_solicitation(int ifindex) {
- assert_se(ifindex == 42);
-
- if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
- return -errno;
-
- return test_fd[0];
-}
-
-int icmp6_bind_router_advertisement(int ifindex) {
- return -ENOSYS;
-}
-
-int icmp6_receive(int fd, void *iov_base, size_t iov_len,
- struct in6_addr *dst, triple_timestamp *timestamp) {
- assert_se(read (fd, iov_base, iov_len) == (ssize_t)iov_len);
-
- if (timestamp)
- triple_timestamp_get(timestamp);
-
- return 0;
-}
-
static int send_ra(uint8_t flags) {
uint8_t advertisement[] = {
0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
@@ -214,13 +188,6 @@ static int send_ra(uint8_t flags) {
return 0;
}
-int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
- if (!send_ra_function)
- return 0;
-
- return send_ra_function(0);
-}
-
static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router *rt, void *userdata) {
sd_event *e = userdata;
static unsigned idx = 0;
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index ea13e02719..56a8831f0f 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -828,3 +828,8 @@ global:
sd_journal_step_one;
sd_session_get_leader;
} LIBSYSTEMD_253;
+
+LIBSYSTEMD_255 {
+global:
+ sd_id128_get_app_specific;
+} LIBSYSTEMD_254;
diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c
index ee9e91c58d..9d391de166 100644
--- a/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libsystemd/sd-id128/id128-util.c
@@ -13,6 +13,23 @@
#include "string-util.h"
#include "sync-util.h"
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret) {
+ sd_id128_t t;
+ int r;
+
+ assert(ret);
+
+ r = sd_id128_from_string(ASSERT_PTR(s), &t);
+ if (r < 0)
+ return r;
+
+ if (sd_id128_is_null(t))
+ return -ENXIO;
+
+ *ret = t;
+ return 0;
+}
+
bool id128_is_valid(const char *s) {
size_t l;
diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h
index bd3e201361..80c36e5a7b 100644
--- a/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libsystemd/sd-id128/id128-util.h
@@ -21,6 +21,8 @@ typedef enum Id128Flag {
ID128_REFUSE_NULL = 1 << 3, /* Refuse all zero ID with -ENOMEDIUM. */
} Id128Flag;
+int id128_from_string_nonzero(const char *s, sd_id128_t *ret);
+
int id128_read_fd(int fd, Id128Flag f, sd_id128_t *ret);
int id128_read_at(int dir_fd, const char *path, Id128Flag f, sd_id128_t *ret);
static inline int id128_read(const char *path, Id128Flag f, sd_id128_t *ret) {
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index 6a82a7f7b8..9fda79ae26 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -338,18 +338,20 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
return 0;
}
-static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
- uint8_t hmac[SHA256_DIGEST_SIZE];
- sd_id128_t result;
+_public_ int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
+ assert_cc(sizeof(sd_id128_t) < SHA256_DIGEST_SIZE); /* Check that we don't need to pad with zeros. */
+ union {
+ uint8_t hmac[SHA256_DIGEST_SIZE];
+ sd_id128_t result;
+ } buf;
- assert(ret);
+ assert_return(ret, -EINVAL);
+ assert_return(!sd_id128_is_null(app_id), -ENXIO);
- hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
+ hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), buf.hmac);
/* Take only the first half. */
- memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
-
- *ret = id128_make_v4_uuid(result);
+ *ret = id128_make_v4_uuid(buf.result);
return 0;
}
@@ -363,7 +365,7 @@ _public_ int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *re
if (r < 0)
return r;
- return get_app_specific(id, app_id, ret);
+ return sd_id128_get_app_specific(id, app_id, ret);
}
_public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
@@ -376,5 +378,5 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
if (r < 0)
return r;
- return get_app_specific(id, app_id, ret);
+ return sd_id128_get_app_specific(id, app_id, ret);
}
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index 06ffa137d4..95cf25bff0 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -572,6 +572,10 @@ static int journal_file_verify_header(JournalFile *f) {
if (journal_file_writable(f) && header_size != sizeof(Header))
return -EPROTONOSUPPORT;
+ /* Don't write to journal files without the new boot ID update behavior guarantee. */
+ if (journal_file_writable(f) && !JOURNAL_HEADER_TAIL_ENTRY_BOOT_ID(f->header))
+ return -EPROTONOSUPPORT;
+
if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
return -EBADMSG;
@@ -2283,18 +2287,15 @@ static int journal_file_append_entry_internal(
"timestamp %" PRIu64 ", refusing entry.",
ts->realtime, le64toh(f->header->tail_entry_realtime));
- if (!sd_id128_is_null(f->header->tail_entry_boot_id) && boot_id) {
-
- if (!sd_id128_equal(f->header->tail_entry_boot_id, *boot_id))
- return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE),
- "Boot ID to write is different from previous boot id, refusing entry.");
-
- if (ts->monotonic < le64toh(f->header->tail_entry_monotonic))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOTNAM),
- "Monotonic timestamp %" PRIu64 " smaller than previous monotonic "
- "timestamp %" PRIu64 ", refusing entry.",
- ts->monotonic, le64toh(f->header->tail_entry_monotonic));
- }
+ if ((!boot_id || sd_id128_equal(*boot_id, f->header->tail_entry_boot_id)) &&
+ ts->monotonic < le64toh(f->header->tail_entry_monotonic))
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOTNAM),
+ "Monotonic timestamp %" PRIu64
+ " smaller than previous monotonic timestamp %" PRIu64
+ " while having the same boot ID, refusing entry.",
+ ts->monotonic,
+ le64toh(f->header->tail_entry_monotonic));
}
if (seqnum_id) {
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index cc72d6ca8f..8704f44dfd 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -113,32 +113,6 @@ static OutputFlags get_output_flags(void) {
colors_enabled() * OUTPUT_COLOR;
}
-static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *error, char **path) {
- _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
- char *ans;
- int r;
-
- assert(bus);
- assert(session_id);
- assert(error);
- assert(path);
-
- r = bus_call_method(bus, bus_login_mgr, "GetSession", error, &reply, "s", session_id);
- if (r < 0)
- return r;
-
- r = sd_bus_message_read(reply, "o", &ans);
- if (r < 0)
- return r;
-
- ans = strdup(ans);
- if (!ans)
- return -ENOMEM;
-
- *path = ans;
- return 0;
-}
-
static int show_table(Table *table, const char *word) {
int r;
@@ -375,14 +349,20 @@ static int list_seats(int argc, char *argv[], void *userdata) {
return show_table(table, "seats");
}
-static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
- _cleanup_free_ char *cgroup = NULL;
+static int show_unit_cgroup(
+ sd_bus *bus,
+ const char *unit,
+ pid_t leader,
+ const char *prefix) {
+
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_free_ char *cgroup = NULL;
unsigned c;
int r;
assert(bus);
assert(unit);
+ assert(prefix);
r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup);
if (r < 0)
@@ -394,12 +374,9 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
c = columns();
if (c > 18)
c -= 18;
- else
- c = 0;
- r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error);
+ r = unit_show_processes(bus, unit, cgroup, prefix, c, get_output_flags(), &error);
if (r == -EBADR) {
-
if (arg_transport == BUS_TRANSPORT_REMOTE)
return 0;
@@ -408,7 +385,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
return 0;
- show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());
+ show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, prefix, c, &leader, leader > 0, get_output_flags());
} else if (r < 0)
return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r));
@@ -467,7 +444,25 @@ static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_messag
return sd_bus_message_exit_container(m);
}
-static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
+static int mark_session(char **sessions, const char *target_session) {
+ assert(sessions);
+ assert(target_session);
+
+ STRV_FOREACH(i, sessions)
+ if (streq(*i, target_session)) {
+ _cleanup_free_ char *marked = NULL;
+
+ marked = strjoin("*", target_session);
+ if (!marked)
+ return log_oom();
+
+ return free_and_replace(*i, marked);
+ }
+
+ return 0;
+}
+
+static int print_session_status_info(sd_bus *bus, const char *path) {
static const struct bus_properties_map map[] = {
{ "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
@@ -497,6 +492,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+ _cleanup_(table_unrefp) Table *table = NULL;
SessionStatusInfo i = {};
int r;
@@ -504,95 +500,148 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
if (r < 0)
return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
- if (*new_line)
- printf("\n");
-
- *new_line = true;
+ table = table_new_vertical();
+ if (!table)
+ return log_oom();
- printf("%s - ", strna(i.id));
+ if (dual_timestamp_is_set(&i.timestamp)) {
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Since");
+ if (r < 0)
+ return table_log_add_error(r);
- if (i.name)
- printf("%s (" UID_FMT ")\n", i.name, i.uid);
- else
- printf(UID_FMT "\n", i.uid);
+ r = table_add_cell_stringf(table, NULL, "%s; %s",
+ FORMAT_TIMESTAMP(i.timestamp.realtime),
+ FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic));
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (dual_timestamp_is_set(&i.timestamp))
- printf("\t Since: %s; %s\n",
- FORMAT_TIMESTAMP(i.timestamp.realtime),
- FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic));
+ r = table_add_many(table,
+ TABLE_FIELD, "State",
+ TABLE_STRING, i.state);
+ if (r < 0)
+ return table_log_add_error(r);
if (i.leader > 0) {
- _cleanup_free_ char *t = NULL;
+ _cleanup_free_ char *name = NULL;
- printf("\t Leader: " PID_FMT, i.leader);
+ (void) get_process_comm(i.leader, &name);
- (void) get_process_comm(i.leader, &t);
- if (t)
- printf(" (%s)", t);
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Leader");
+ if (r < 0)
+ return table_log_add_error(r);
- printf("\n");
+ r = table_add_cell_stringf(table, NULL, PID_FMT "%s%s%s",
+ i.leader,
+ !isempty(name) ? " (" : "",
+ strempty(name),
+ !isempty(name) ? ")" : "");
+ if (r < 0)
+ return table_log_add_error(r);
}
- if (!isempty(i.seat)) {
- printf("\t Seat: %s", i.seat);
-
- if (i.vtnr > 0)
- printf("; vc%u", i.vtnr);
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Seat");
+ if (r < 0)
+ return table_log_add_error(r);
- printf("\n");
- }
+ if (i.vtnr > 0)
+ r = table_add_cell_stringf(table, NULL, "%s; vc%u", i.seat, i.vtnr);
+ else
+ r = table_add_cell(table, NULL, TABLE_STRING, i.seat);
+ if (r < 0)
+ return table_log_add_error(r);
if (i.tty)
- printf("\t TTY: %s\n", i.tty);
+ r = table_add_many(table,
+ TABLE_FIELD, "TTY",
+ TABLE_STRING, i.tty);
else if (i.display)
- printf("\t Display: %s\n", i.display);
+ r = table_add_many(table,
+ TABLE_FIELD, "Display",
+ TABLE_STRING, i.display);
+ else
+ r = 0;
+ if (r < 0)
+ return table_log_add_error(r);
+
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Remote");
+ if (r < 0)
+ return table_log_add_error(r);
if (i.remote_host && i.remote_user)
- printf("\t Remote: %s@%s\n", i.remote_user, i.remote_host);
+ r = table_add_cell_stringf(table, NULL, "%s@%s", i.remote_user, i.remote_host);
else if (i.remote_host)
- printf("\t Remote: %s\n", i.remote_host);
+ r = table_add_cell(table, NULL, TABLE_STRING, i.remote_host);
else if (i.remote_user)
- printf("\t Remote: user %s\n", i.remote_user);
- else if (i.remote)
- printf("\t Remote: Yes\n");
+ r = table_add_cell_stringf(table, NULL, "user %s", i.remote_user);
+ else
+ r = table_add_cell(table, NULL, TABLE_BOOLEAN, &i.remote);
+ if (r < 0)
+ return table_log_add_error(r);
if (i.service) {
- printf("\t Service: %s", i.service);
+ r = table_add_many(table,
+ TABLE_FIELD, "Service",
+ TABLE_STRING, i.service);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (i.type)
- printf("; type %s", i.type);
+ if (i.type) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Type",
+ TABLE_STRING, i.type);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (i.class)
- printf("; class %s", i.class);
+ if (i.class) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Class",
+ TABLE_STRING, i.class);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- printf("\n");
- } else if (i.type) {
- printf("\t Type: %s", i.type);
+ if (!isempty(i.desktop)) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Desktop",
+ TABLE_STRING, i.desktop);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (i.class)
- printf("; class %s", i.class);
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Idle");
+ if (r < 0)
+ return table_log_add_error(r);
- printf("\n");
- } else if (i.class)
- printf("\t Class: %s\n", i.class);
+ if (i.idle_hint && dual_timestamp_is_set(&i.idle_hint_timestamp))
+ r = table_add_cell_stringf(table, NULL, "%s since %s (%s)",
+ yes_no(i.idle_hint),
+ FORMAT_TIMESTAMP(i.idle_hint_timestamp.realtime),
+ FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.idle_hint_timestamp.monotonic));
+ else
+ r = table_add_cell(table, NULL, TABLE_BOOLEAN, &i.idle_hint);
+ if (r < 0)
+ return table_log_add_error(r);
- if (!isempty(i.desktop))
- printf("\t Desktop: %s\n", i.desktop);
+ if (i.scope) {
+ r = table_add_many(table,
+ TABLE_FIELD, "Unit",
+ TABLE_STRING, i.scope);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (i.state)
- printf("\t State: %s\n", i.state);
+ /* We don't use the table to show the header, in order to make the width of the column stable. */
+ printf("%s%s - %s (" UID_FMT ")%s\n", ansi_highlight(), i.id, i.name, i.uid, ansi_normal());
- if (i.idle_hint && dual_timestamp_is_set(&i.idle_hint_timestamp))
- printf("\t Idle: %s since %s (%s)\n",
- yes_no(i.idle_hint),
- FORMAT_TIMESTAMP(i.idle_hint_timestamp.realtime),
- FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.idle_hint_timestamp.monotonic));
- else
- printf("\t Idle: %s\n", yes_no(i.idle_hint));
+ r = table_print(table, NULL);
+ if (r < 0)
+ return table_log_print_error(r);
if (i.scope) {
- printf("\t Unit: %s\n", i.scope);
- show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
+ show_unit_cgroup(bus, i.scope, i.leader, /* prefix = */ strrepa(" ", STRLEN("Display: ")));
if (arg_transport == BUS_TRANSPORT_LOCAL)
show_journal_by_unit(
@@ -613,7 +662,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li
return 0;
}
-static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
+static int print_user_status_info(sd_bus *bus, const char *path) {
static const struct bus_properties_map map[] = {
{ "Name", "s", NULL, offsetof(UserStatusInfo, name) },
@@ -631,66 +680,92 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line)
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(user_status_info_done) UserStatusInfo i = {};
+ _cleanup_(table_unrefp) Table *table = NULL;
int r;
r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, BUS_MAP_BOOLEAN_AS_BOOL, &error, &m, &i);
if (r < 0)
return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
- if (*new_line)
- printf("\n");
-
- *new_line = true;
+ table = table_new_vertical();
+ if (!table)
+ return log_oom();
- if (i.name)
- printf("%s (%"PRIu32")\n", i.name, i.uid);
- else
- printf("%"PRIu32"\n", i.uid);
+ if (dual_timestamp_is_set(&i.timestamp)) {
+ r = table_add_cell(table, NULL, TABLE_FIELD, "Since");
+ if (r < 0)
+ return table_log_add_error(r);
- if (dual_timestamp_is_set(&i.timestamp))
- printf("\t Since: %s; %s\n",
- FORMAT_TIMESTAMP(i.timestamp.realtime),
- FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic));
+ r = table_add_cell_stringf(table, NULL, "%s; %s",
+ FORMAT_TIMESTAMP(i.timestamp.realtime),
+ FORMAT_TIMESTAMP_RELATIVE_MONOTONIC(i.timestamp.monotonic));
+ if (r < 0)
+ return table_log_add_error(r);
+ }
- if (!isempty(i.state))
- printf("\t State: %s\n", i.state);
+ r = table_add_many(table,
+ TABLE_FIELD, "State",
+ TABLE_STRING, i.state);
+ if (r < 0)
+ return table_log_add_error(r);
if (!strv_isempty(i.sessions)) {
- printf("\tSessions:");
+ _cleanup_strv_free_ char **sessions = TAKE_PTR(i.sessions);
- STRV_FOREACH(l, i.sessions)
- printf(" %s%s",
- streq_ptr(*l, i.display) ? "*" : "",
- *l);
+ r = mark_session(sessions, i.display);
+ if (r < 0)
+ return r;
- printf("\n");
+ r = table_add_many(table,
+ TABLE_FIELD, "Sessions",
+ TABLE_STRV_WRAPPED, sessions);
+ if (r < 0)
+ return table_log_add_error(r);
}
- printf("\t Linger: %s\n", yes_no(i.linger));
+ r = table_add_many(table,
+ TABLE_FIELD, "Linger",
+ TABLE_BOOLEAN, i.linger);
+ if (r < 0)
+ return table_log_add_error(r);
if (i.slice) {
- printf("\t Unit: %s\n", i.slice);
- show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
-
- show_journal_by_unit(
- stdout,
- i.slice,
- NULL,
- arg_output,
- 0,
- i.timestamp.monotonic,
- arg_lines,
- 0,
- get_output_flags() | OUTPUT_BEGIN_NEWLINE,
- SD_JOURNAL_LOCAL_ONLY,
- true,
- NULL);
+ r = table_add_many(table,
+ TABLE_FIELD, "Unit",
+ TABLE_STRING, i.slice);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ printf("%s%s (" UID_FMT ")%s\n", ansi_highlight(), i.name, i.uid, ansi_normal());
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return table_log_print_error(r);
+
+ if (i.slice) {
+ show_unit_cgroup(bus, i.slice, /* leader = */ 0, /* prefix = */ strrepa(" ", STRLEN("Sessions: ")));
+
+ if (arg_transport == BUS_TRANSPORT_LOCAL)
+ show_journal_by_unit(
+ stdout,
+ i.slice,
+ NULL,
+ arg_output,
+ 0,
+ i.timestamp.monotonic,
+ arg_lines,
+ 0,
+ get_output_flags() | OUTPUT_BEGIN_NEWLINE,
+ SD_JOURNAL_LOCAL_ONLY,
+ true,
+ NULL);
}
return 0;
}
-static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
+static int print_seat_status_info(sd_bus *bus, const char *path) {
static const struct bus_properties_map map[] = {
{ "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
@@ -702,44 +777,51 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line)
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(seat_status_info_done) SeatStatusInfo i = {};
+ _cleanup_(table_unrefp) Table *table = NULL;
int r;
r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, 0, &error, &m, &i);
if (r < 0)
return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r));
- if (*new_line)
- printf("\n");
-
- *new_line = true;
-
- printf("%s\n", strna(i.id));
+ table = table_new_vertical();
+ if (!table)
+ return log_oom();
if (!strv_isempty(i.sessions)) {
- printf("\tSessions:");
+ _cleanup_strv_free_ char **sessions = TAKE_PTR(i.sessions);
- STRV_FOREACH(l, i.sessions) {
- if (streq_ptr(*l, i.active_session))
- printf(" *%s", *l);
- else
- printf(" %s", *l);
- }
+ r = mark_session(sessions, i.active_session);
+ if (r < 0)
+ return r;
- printf("\n");
+ r = table_add_many(table,
+ TABLE_FIELD, "Sessions",
+ TABLE_STRV_WRAPPED, sessions);
+ if (r < 0)
+ return table_log_add_error(r);
}
if (arg_transport == BUS_TRANSPORT_LOCAL) {
- unsigned c;
+ r = table_add_many(table,
+ TABLE_FIELD, "Devices",
+ TABLE_EMPTY);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ printf("%s%s%s\n", ansi_highlight(), i.id, ansi_normal());
+
+ r = table_print(table, NULL);
+ if (r < 0)
+ return table_log_print_error(r);
- c = columns();
+ if (arg_transport == BUS_TRANSPORT_LOCAL) {
+ unsigned c = columns();
if (c > 21)
c -= 21;
- else
- c = 0;
- printf("\t Devices:\n");
-
- show_sysfs(i.id, "\t\t ", c, get_output_flags());
+ show_sysfs(i.id, strrepa(" ", STRLEN("Sessions:")), c, get_output_flags());
}
return 0;
@@ -825,17 +907,11 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
return 0;
}
-static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
+static int show_properties(sd_bus *bus, const char *path) {
int r;
assert(bus);
assert(path);
- assert(new_line);
-
- if (*new_line)
- printf("\n");
-
- *new_line = true;
r = bus_print_all_properties(
bus,
@@ -851,12 +927,46 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
return 0;
}
+static int get_bus_path_by_id(
+ sd_bus *bus,
+ const char *type,
+ const char *method,
+ const char *id,
+ char **ret) {
+
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ _cleanup_free_ char *p = NULL;
+ const char *path;
+ int r;
+
+ assert(bus);
+ assert(type);
+ assert(STR_IN_SET(type, "session", "seat"));
+ assert(method);
+ assert(id);
+ assert(ret);
+
+ r = bus_call_method(bus, bus_login_mgr, method, &error, &reply, "s", id);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get path for %s '%s': %s", type, id, bus_error_message(&error, r));
+
+ r = sd_bus_message_read(reply, "o", &path);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
+ p = strdup(path);
+ if (!p)
+ return log_oom();
+
+ *ret = TAKE_PTR(p);
+ return 0;
+}
+
static int show_session(int argc, char *argv[], void *userdata) {
- bool properties, new_line = false;
sd_bus *bus = ASSERT_PTR(userdata);
+ bool properties;
int r;
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *path = NULL;
assert(argv);
@@ -867,21 +977,25 @@ static int show_session(int argc, char *argv[], void *userdata) {
if (argc <= 1) {
/* If no argument is specified inspect the manager itself */
if (properties)
- return show_properties(bus, "/org/freedesktop/login1", &new_line);
+ return show_properties(bus, "/org/freedesktop/login1");
- return print_session_status_info(bus, "/org/freedesktop/login1/session/auto", &new_line);
+ return print_session_status_info(bus, "/org/freedesktop/login1/session/auto");
}
- for (int i = 1; i < argc; i++) {
- r = get_session_path(bus, argv[i], &error, &path);
+ for (int i = 1, first = true; i < argc; i++, first = false) {
+ _cleanup_free_ char *path = NULL;
+
+ r = get_bus_path_by_id(bus, "session", "GetSession", argv[i], &path);
if (r < 0)
- return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
+ return r;
+
+ if (!first)
+ putchar('\n');
if (properties)
- r = show_properties(bus, path, &new_line);
+ r = show_properties(bus, path);
else
- r = print_session_status_info(bus, path, &new_line);
-
+ r = print_session_status_info(bus, path);
if (r < 0)
return r;
}
@@ -890,8 +1004,8 @@ static int show_session(int argc, char *argv[], void *userdata) {
}
static int show_user(int argc, char *argv[], void *userdata) {
- bool properties, new_line = false;
sd_bus *bus = ASSERT_PTR(userdata);
+ bool properties;
int r;
assert(argv);
@@ -903,15 +1017,15 @@ static int show_user(int argc, char *argv[], void *userdata) {
if (argc <= 1) {
/* If no argument is specified inspect the manager itself */
if (properties)
- return show_properties(bus, "/org/freedesktop/login1", &new_line);
+ return show_properties(bus, "/org/freedesktop/login1");
- return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
+ return print_user_status_info(bus, "/org/freedesktop/login1/user/self");
}
- for (int i = 1; i < argc; i++) {
+ for (int i = 1, first = true; i < argc; i++, first = false) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
- const char *path = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+ const char *path;
uid_t uid;
r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
@@ -926,11 +1040,13 @@ static int show_user(int argc, char *argv[], void *userdata) {
if (r < 0)
return bus_log_parse_error(r);
+ if (!first)
+ putchar('\n');
+
if (properties)
- r = show_properties(bus, path, &new_line);
+ r = show_properties(bus, path);
else
- r = print_user_status_info(bus, path, &new_line);
-
+ r = print_user_status_info(bus, path);
if (r < 0)
return r;
}
@@ -939,8 +1055,8 @@ static int show_user(int argc, char *argv[], void *userdata) {
}
static int show_seat(int argc, char *argv[], void *userdata) {
- bool properties, new_line = false;
sd_bus *bus = ASSERT_PTR(userdata);
+ bool properties;
int r;
assert(argv);
@@ -952,29 +1068,25 @@ static int show_seat(int argc, char *argv[], void *userdata) {
if (argc <= 1) {
/* If no argument is specified inspect the manager itself */
if (properties)
- return show_properties(bus, "/org/freedesktop/login1", &new_line);
+ return show_properties(bus, "/org/freedesktop/login1");
- return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto", &new_line);
+ return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto");
}
- for (int i = 1; i < argc; i++) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
- const char *path = NULL;
+ for (int i = 1, first = true; i < argc; i++, first = false) {
+ _cleanup_free_ char *path = NULL;
- r = bus_call_method(bus, bus_login_mgr, "GetSeat", &error, &reply, "s", argv[i]);
+ r = get_bus_path_by_id(bus, "seat", "GetSeat", argv[i], &path);
if (r < 0)
- return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r));
+ return r;
- r = sd_bus_message_read(reply, "o", &path);
- if (r < 0)
- return bus_log_parse_error(r);
+ if (!first)
+ putchar('\n');
if (properties)
- r = show_properties(bus, path, &new_line);
+ r = show_properties(bus, path);
else
- r = print_seat_status_info(bus, path, &new_line);
-
+ r = print_seat_status_info(bus, path);
if (r < 0)
return r;
}
diff --git a/src/network/meson.build b/src/network/meson.build
index 7d0e5d6345..2ca9eac714 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -43,7 +43,9 @@ sources = files(
'networkd-dhcp-server-bus.c',
'networkd-dhcp-server-static-lease.c',
'networkd-dhcp-server.c',
+ 'networkd-dhcp4-bus.c',
'networkd-dhcp4.c',
+ 'networkd-dhcp6-bus.c',
'networkd-dhcp6.c',
'networkd-ipv4acd.c',
'networkd-ipv4ll.c',
diff --git a/src/network/networkd-address-generation.c b/src/network/networkd-address-generation.c
index 769cccf748..79fde024a3 100644
--- a/src/network/networkd-address-generation.c
+++ b/src/network/networkd-address-generation.c
@@ -370,16 +370,11 @@ int config_parse_address_generation_type(
}
if (comma) {
- r = sd_id128_from_string(comma + 1, &secret_key);
+ r = id128_from_string_nonzero(comma + 1, &secret_key);
if (r < 0) {
log_syntax(unit, LOG_WARNING, filename, line, r,
- "Failed to parse secret key in %s=, ignoring assignment: %s",
- lvalue, rvalue);
- return 0;
- }
- if (sd_id128_is_null(secret_key)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "Secret key in %s= cannot be null, ignoring assignment: %s",
+ r == -ENXIO ? "Secret key in %s= cannot be null, ignoring assignment: %s"
+ : "Failed to parse secret key in %s=, ignoring assignment: %s",
lvalue, rvalue);
return 0;
}
diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c
index 5b5b251e61..ff0ee717c5 100644
--- a/src/network/networkd-dhcp-common.c
+++ b/src/network/networkd-dhcp-common.c
@@ -6,7 +6,7 @@
#include "bus-error.h"
#include "bus-locator.h"
#include "dhcp-identifier.h"
-#include "dhcp-internal.h"
+#include "dhcp-client-internal.h"
#include "dhcp6-internal.h"
#include "escape.h"
#include "hexdecoct.h"
diff --git a/src/network/networkd-dhcp4-bus.c b/src/network/networkd-dhcp4-bus.c
new file mode 100644
index 0000000000..cb88627d20
--- /dev/null
+++ b/src/network/networkd-dhcp4-bus.c
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "sd-dhcp-client.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-util.h"
+#include "dhcp-client-internal.h"
+#include "dhcp-protocol.h"
+#include "networkd-dhcp4-bus.h"
+#include "networkd-link-bus.h"
+#include "networkd-manager.h"
+#include "string-table.h"
+#include "strv.h"
+
+static int property_get_dhcp_client_state(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = ASSERT_PTR(userdata);
+ sd_dhcp_client *c;
+
+ assert(reply);
+
+ c = l->dhcp_client;
+ if (!c)
+ return sd_bus_message_append(reply, "s", "disabled");
+
+ return sd_bus_message_append(reply, "s", dhcp_state_to_string(dhcp_client_get_state(c)));
+}
+
+static int dhcp_client_emit_changed(Link *link, const char *property, ...) {
+ _cleanup_free_ char *path = NULL;
+ char **l;
+
+ assert(link);
+
+ if (sd_bus_is_ready(link->manager->bus) <= 0)
+ return 0;
+
+ path = link_bus_path(link);
+ if (!path)
+ return log_oom();
+
+ l = strv_from_stdarg_alloca(property);
+
+ return sd_bus_emit_properties_changed_strv(
+ link->manager->bus,
+ path,
+ "org.freedesktop.network1.DHCPv4Client",
+ l);
+}
+
+int dhcp_client_callback_bus(sd_dhcp_client *c, int event, void *userdata) {
+ Link *l = ASSERT_PTR(userdata);
+
+ return dhcp_client_emit_changed(l, "State", NULL);
+}
+
+static const sd_bus_vtable dhcp_client_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("State", "s", property_get_dhcp_client_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+ SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation dhcp_client_object = {
+ "/org/freedesktop/network1/link",
+ "org.freedesktop.network1.DHCPv4Client",
+ .fallback_vtables = BUS_FALLBACK_VTABLES({dhcp_client_vtable, link_object_find}),
+ .node_enumerator = link_node_enumerator,
+};
diff --git a/src/network/networkd-dhcp4-bus.h b/src/network/networkd-dhcp4-bus.h
new file mode 100644
index 0000000000..482e824c0a
--- /dev/null
+++ b/src/network/networkd-dhcp4-bus.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp-client.h"
+
+#include "networkd-link-bus.h"
+
+extern const BusObjectImplementation dhcp_client_object;
+
+int dhcp_client_callback_bus(sd_dhcp_client *client, int event, void *userdata);
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 9dcd37e11c..f952d6dfbc 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -13,6 +13,7 @@
#include "network-internal.h"
#include "networkd-address.h"
#include "networkd-dhcp-prefix-delegation.h"
+#include "networkd-dhcp4-bus.h"
#include "networkd-dhcp4.h"
#include "networkd-ipv4acd.h"
#include "networkd-link.h"
@@ -1482,6 +1483,10 @@ static int dhcp4_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for broadcast: %m");
+ r = dhcp_client_set_state_callback(link->dhcp_client, dhcp_client_callback_bus, link);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set state change callback: %m");
+
if (link->mtu > 0) {
r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu);
if (r < 0)
diff --git a/src/network/networkd-dhcp6-bus.c b/src/network/networkd-dhcp6-bus.c
new file mode 100644
index 0000000000..a225877373
--- /dev/null
+++ b/src/network/networkd-dhcp6-bus.c
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-util.h"
+#include "dhcp6-client-internal.h"
+#include "dhcp6-protocol.h"
+#include "networkd-dhcp6-bus.h"
+#include "networkd-link-bus.h"
+#include "networkd-manager.h"
+#include "string-table.h"
+#include "strv.h"
+
+static int property_get_dhcp6_client_state(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Link *l = ASSERT_PTR(userdata);
+ sd_dhcp6_client *c;
+
+ assert(reply);
+
+ c = l->dhcp6_client;
+ if (!c)
+ return sd_bus_message_append(reply, "s", "disabled");
+
+ return sd_bus_message_append(reply, "s", dhcp6_state_to_string(dhcp6_client_get_state(c)));
+}
+
+static int dhcp6_client_emit_changed(Link *link, const char *property, ...) {
+ _cleanup_free_ char *path = NULL;
+ char **l;
+
+ assert(link);
+
+ if (sd_bus_is_ready(link->manager->bus) <= 0)
+ return 0;
+
+ path = link_bus_path(link);
+ if (!path)
+ return log_oom();
+
+ l = strv_from_stdarg_alloca(property);
+
+ return sd_bus_emit_properties_changed_strv(
+ link->manager->bus,
+ path,
+ "org.freedesktop.network1.DHCPv6Client",
+ l);
+}
+
+void dhcp6_client_callback_bus(sd_dhcp6_client *c, int event, void *userdata) {
+ Link *l = ASSERT_PTR(userdata);
+
+ dhcp6_client_emit_changed(l, "State", NULL);
+}
+
+static const sd_bus_vtable dhcp6_client_vtable[] = {
+ SD_BUS_VTABLE_START(0),
+
+ SD_BUS_PROPERTY("State", "s", property_get_dhcp6_client_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+ SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation dhcp6_client_object = {
+ "/org/freedesktop/network1/link",
+ "org.freedesktop.network1.DHCPv6Client",
+ .fallback_vtables = BUS_FALLBACK_VTABLES({dhcp6_client_vtable, link_object_find}),
+ .node_enumerator = link_node_enumerator,
+};
diff --git a/src/network/networkd-dhcp6-bus.h b/src/network/networkd-dhcp6-bus.h
new file mode 100644
index 0000000000..76a6b727aa
--- /dev/null
+++ b/src/network/networkd-dhcp6-bus.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "sd-dhcp6-client.h"
+
+#include "networkd-link-bus.h"
+
+extern const BusObjectImplementation dhcp6_client_object;
+
+void dhcp6_client_callback_bus(sd_dhcp6_client *client, int event, void *userdata);
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c
index 95b13ca93c..57e1087211 100644
--- a/src/network/networkd-dhcp6.c
+++ b/src/network/networkd-dhcp6.c
@@ -5,11 +5,13 @@
#include "sd-dhcp6-client.h"
+#include "dhcp6-client-internal.h"
#include "hashmap.h"
#include "hostname-setup.h"
#include "hostname-util.h"
#include "networkd-address.h"
#include "networkd-dhcp-prefix-delegation.h"
+#include "networkd-dhcp6-bus.h"
#include "networkd-dhcp6.h"
#include "networkd-link.h"
#include "networkd-manager.h"
@@ -699,6 +701,10 @@ static int dhcp6_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set callback: %m");
+ r = dhcp6_client_set_state_callback(client, dhcp6_client_callback_bus, link);
+ if (r < 0)
+ return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to set state change callback: %m");
+
r = sd_dhcp6_client_set_prefix_delegation(client, link->network->dhcp6_use_pd_prefix);
if (r < 0)
return log_link_debug_errno(link, r, "DHCPv6 CLIENT: Failed to %s requesting prefixes to be delegated: %m",
diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c
index 86488c9816..7c3639d245 100644
--- a/src/network/networkd-json.c
+++ b/src/network/networkd-json.c
@@ -922,6 +922,34 @@ static int captive_portal_append_json(Link *link, JsonVariant **v) {
return json_variant_merge_objectb(v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("CaptivePortal", captive_portal)));
}
+static int pref64_append_json(Link *link, JsonVariant **v) {
+ _cleanup_(json_variant_unrefp) JsonVariant *array = NULL, *w = NULL;
+ NDiscPREF64 *i;
+ int r;
+
+ assert(link);
+ assert(v);
+
+ if (!link->network || !link->network->ipv6_accept_ra_use_pref64)
+ return 0;
+
+ SET_FOREACH(i, link->ndisc_pref64) {
+ r = json_build(&array, JSON_BUILD_OBJECT(
+ JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("Prefix", &i->prefix),
+ JSON_BUILD_PAIR_UNSIGNED("PrefixLength", i->prefix_len),
+ JSON_BUILD_PAIR_FINITE_USEC("LifetimeUSec", i->lifetime_usec),
+ JSON_BUILD_PAIR_IN6_ADDR_NON_NULL("ConfigProvider", &i->router)));
+ if (r < 0)
+ return r;
+ }
+
+ r = json_append_one(&w, "PREF64", array);
+ if (r < 0)
+ return r;
+
+ return json_append_one(v, "NDisc", w);
+}
+
static int dhcp_server_offered_leases_append_json(Link *link, JsonVariant **v) {
_cleanup_(json_variant_unrefp) JsonVariant *array = NULL;
DHCPLease *lease;
@@ -1288,6 +1316,10 @@ int link_build_json(Link *link, JsonVariant **ret) {
if (r < 0)
return r;
+ r = pref64_append_json(link, &v);
+ if (r < 0)
+ return r;
+
r = addresses_append_json(link->addresses, &v);
if (r < 0)
return r;
diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c
index e9c18f0fd0..0674930783 100644
--- a/src/network/networkd-link-bus.c
+++ b/src/network/networkd-link-bus.c
@@ -862,6 +862,12 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
(!link->dhcp_server || sd_dhcp_server_is_in_relay_mode(link->dhcp_server)))
return 0;
+ if (streq(interface, "org.freedesktop.network1.DHCPv4Client") && !link->dhcp_client)
+ return 0;
+
+ if (streq(interface, "org.freedesktop.network1.DHCPv6Client") && !link->dhcp6_client)
+ return 0;
+
*found = link;
return 1;
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index e46e5f0c61..8ced02f3c1 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -157,6 +157,7 @@ typedef struct Link {
Set *ndisc_rdnss;
Set *ndisc_dnssl;
Set *ndisc_captive_portals;
+ Set *ndisc_pref64;
unsigned ndisc_messages;
bool ndisc_configured:1;
diff --git a/src/network/networkd-manager-bus.c b/src/network/networkd-manager-bus.c
index 67f951df69..7813a3173a 100644
--- a/src/network/networkd-manager-bus.c
+++ b/src/network/networkd-manager-bus.c
@@ -9,6 +9,8 @@
#include "bus-message-util.h"
#include "bus-polkit.h"
#include "networkd-dhcp-server-bus.h"
+#include "networkd-dhcp4-bus.h"
+#include "networkd-dhcp6-bus.h"
#include "networkd-json.h"
#include "networkd-link-bus.h"
#include "networkd-link.h"
@@ -413,5 +415,6 @@ const BusObjectImplementation manager_object = {
"/org/freedesktop/network1",
"org.freedesktop.network1.Manager",
.vtables = BUS_VTABLES(manager_vtable),
- .children = BUS_IMPLEMENTATIONS(&dhcp_server_object, &link_object, &network_object),
+ .children = BUS_IMPLEMENTATIONS(&dhcp_server_object, &dhcp_client_object,
+ &dhcp6_client_object, &link_object, &network_object),
};
diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c
index ae267ce5ca..e860650691 100644
--- a/src/network/networkd-ndisc.c
+++ b/src/network/networkd-ndisc.c
@@ -885,6 +885,9 @@ static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt)
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
+ /* RFC 4861 section 4.2. states that the lifetime in the message header should be used only for the
+ * default gateway, but the captive portal option does not have a lifetime field, hence, we use the
+ * main lifetime for the portal. */
r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
@@ -909,7 +912,19 @@ static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt)
if (!in_charset(captive_portal, URI_VALID))
return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBADMSG), "Received invalid captive portal, ignoring.");
- exist = set_get(link->ndisc_captive_portals, &(NDiscCaptivePortal) { .captive_portal = captive_portal });
+ if (lifetime_usec == 0) {
+ /* Drop the portal with zero lifetime. */
+ ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals,
+ &(NDiscCaptivePortal) {
+ .captive_portal = captive_portal,
+ }));
+ return 0;
+ }
+
+ exist = set_get(link->ndisc_captive_portals,
+ &(NDiscCaptivePortal) {
+ .captive_portal = captive_portal,
+ });
if (exist) {
/* update existing entry */
exist->router = router;
@@ -950,6 +965,113 @@ static int ndisc_router_process_captive_portal(Link *link, sd_ndisc_router *rt)
return 1;
}
+static void ndisc_pref64_hash_func(const NDiscPREF64 *x, struct siphash *state) {
+ assert(x);
+
+ siphash24_compress(&x->prefix_len, sizeof(x->prefix_len), state);
+ siphash24_compress(&x->prefix, sizeof(x->prefix), state);
+}
+
+static int ndisc_pref64_compare_func(const NDiscPREF64 *a, const NDiscPREF64 *b) {
+ int r;
+
+ assert(a);
+ assert(b);
+
+ r = CMP(a->prefix_len, b->prefix_len);
+ if (r != 0)
+ return r;
+
+ return memcmp(&a->prefix, &b->prefix, sizeof(a->prefix));
+}
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ ndisc_pref64_hash_ops,
+ NDiscPREF64,
+ ndisc_pref64_hash_func,
+ ndisc_pref64_compare_func,
+ mfree);
+
+static int ndisc_router_process_pref64(Link *link, sd_ndisc_router *rt) {
+ _cleanup_free_ NDiscPREF64 *new_entry = NULL;
+ usec_t lifetime_usec, timestamp_usec;
+ struct in6_addr a, router;
+ uint16_t lifetime_sec;
+ unsigned prefix_len;
+ NDiscPREF64 *exist;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ if (!link->network->ipv6_accept_ra_use_pref64)
+ return 0;
+
+ r = sd_ndisc_router_get_address(rt, &router);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get router address from RA: %m");
+
+ r = sd_ndisc_router_prefix64_get_prefix(rt, &a);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get pref64 prefix: %m");
+
+ r = sd_ndisc_router_prefix64_get_prefixlen(rt, &prefix_len);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get pref64 prefix length: %m");
+
+ r = sd_ndisc_router_prefix64_get_lifetime_sec(rt, &lifetime_sec);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get pref64 prefix lifetime: %m");
+
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, &timestamp_usec);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+
+ lifetime_usec = sec16_to_usec(lifetime_sec, timestamp_usec);
+
+ if (lifetime_usec == 0) {
+ free(set_remove(link->ndisc_pref64,
+ &(NDiscPREF64) {
+ .prefix = a,
+ .prefix_len = prefix_len
+ }));
+ return 0;
+ }
+
+ exist = set_get(link->ndisc_pref64,
+ &(NDiscPREF64) {
+ .prefix = a,
+ .prefix_len = prefix_len
+ });
+ if (exist) {
+ /* update existing entry */
+ exist->router = router;
+ exist->lifetime_usec = lifetime_usec;
+ return 0;
+ }
+
+ new_entry = new(NDiscPREF64, 1);
+ if (!new_entry)
+ return log_oom();
+
+ *new_entry = (NDiscPREF64) {
+ .router = router,
+ .lifetime_usec = lifetime_usec,
+ .prefix = a,
+ .prefix_len = prefix_len,
+ };
+
+ r = set_ensure_put(&link->ndisc_pref64, &ndisc_pref64_hash_ops, new_entry);
+ if (r < 0)
+ return log_oom();
+
+ assert(r > 0);
+ TAKE_PTR(new_entry);
+
+ return 0;
+}
+
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
size_t n_captive_portal = 0;
int r;
@@ -998,13 +1120,16 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
if (r > 0)
n_captive_portal++;
break;
+ case SD_NDISC_OPTION_PREF64:
+ r = ndisc_router_process_pref64(link, rt);
+ break;
}
if (r < 0 && r != -EBADMSG)
return r;
}
}
-static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct in6_addr *router) {
+static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec) {
bool updated = false;
NDiscDNSSL *dnssl;
NDiscRDNSS *rdnss;
@@ -1028,9 +1153,6 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct i
if (route->lifetime_usec >= timestamp_usec)
continue; /* the route is still valid */
- if (router && !in6_addr_equal(&route->provider.in6, router))
- continue;
-
k = route_remove_and_drop(route);
if (k < 0)
r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC route, ignoring: %m");
@@ -1043,9 +1165,6 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct i
if (address->lifetime_valid_usec >= timestamp_usec)
continue; /* the address is still valid */
- if (router && !in6_addr_equal(&address->provider.in6, router))
- continue;
-
k = address_remove_and_drop(address);
if (k < 0)
r = log_link_warning_errno(link, k, "Failed to remove outdated SLAAC address, ignoring: %m");
@@ -1055,9 +1174,6 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct i
if (rdnss->lifetime_usec >= timestamp_usec)
continue; /* the DNS server is still valid */
- if (router && !in6_addr_equal(&rdnss->router, router))
- continue;
-
free(set_remove(link->ndisc_rdnss, rdnss));
updated = true;
}
@@ -1066,9 +1182,6 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct i
if (dnssl->lifetime_usec >= timestamp_usec)
continue; /* the DNS domain is still valid */
- if (router && !in6_addr_equal(&dnssl->router, router))
- continue;
-
free(set_remove(link->ndisc_dnssl, dnssl));
updated = true;
}
@@ -1077,9 +1190,6 @@ static int ndisc_drop_outdated(Link *link, usec_t timestamp_usec, const struct i
if (cp->lifetime_usec >= timestamp_usec)
continue; /* the captive portal is still valid */
- if (router && !in6_addr_equal(&cp->router, router))
- continue;
-
ndisc_captive_portal_free(set_remove(link->ndisc_captive_portals, cp));
updated = true;
}
@@ -1100,7 +1210,7 @@ static int ndisc_expire_handler(sd_event_source *s, uint64_t usec, void *userdat
assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
- (void) ndisc_drop_outdated(link, now_usec, NULL);
+ (void) ndisc_drop_outdated(link, now_usec);
(void) ndisc_setup_expire(link);
return 0;
}
@@ -1201,7 +1311,6 @@ static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
}
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
- uint16_t router_lifetime_sec;
struct in6_addr router;
usec_t timestamp_usec;
int r;
@@ -1234,28 +1343,12 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
log_link_debug(link, "Received RA without timestamp, ignoring.");
return 0;
}
-
- r = ndisc_drop_outdated(link, timestamp_usec, NULL);
if (r < 0)
return r;
- r = sd_ndisc_router_get_lifetime(rt, &router_lifetime_sec);
+ r = ndisc_drop_outdated(link, timestamp_usec);
if (r < 0)
- return log_link_warning_errno(link, r, "Failed to get lifetime of RA message: %m");
-
- /* https://datatracker.ietf.org/doc/html/rfc4861
- * Router Lifetime: A Lifetime of 0 indicates that the router is not a default router
- * and SHOULD NOT appear on the default router list.
- */
- if (router_lifetime_sec == 0) {
- log_link_debug(link, "Received RA with lifetime = 0, dropping configurations.");
-
- r = ndisc_drop_outdated(link, USEC_INFINITY, &router);
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to process RA with zero lifetime, ignoring: %m");
-
- return 0;
- }
+ return r;
r = ndisc_start_dhcp6_client(link, rt);
if (r < 0)
@@ -1430,6 +1523,7 @@ void ndisc_flush(Link *link) {
link->ndisc_rdnss = set_free(link->ndisc_rdnss);
link->ndisc_dnssl = set_free(link->ndisc_dnssl);
link->ndisc_captive_portals = set_free(link->ndisc_captive_portals);
+ link->ndisc_pref64 = set_free(link->ndisc_pref64);
}
static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h
index 267f7d4a02..a463f42b52 100644
--- a/src/network/networkd-ndisc.h
+++ b/src/network/networkd-ndisc.h
@@ -39,6 +39,15 @@ typedef struct NDiscCaptivePortal {
char *captive_portal;
} NDiscCaptivePortal;
+typedef struct NDiscPREF64 {
+ struct in6_addr router;
+ /* This is an absolute point in time, and NOT a timespan/duration.
+ * Must be specified with CLOCK_BOOTTIME. */
+ usec_t lifetime_usec;
+ uint8_t prefix_len;
+ struct in6_addr prefix;
+} NDiscPREF64;
+
static inline char* NDISC_DNSSL_DOMAIN(const NDiscDNSSL *n) {
return ((char*) n) + ALIGN(sizeof(NDiscDNSSL));
}
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 6309baa056..bbb87e00bd 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -285,6 +285,7 @@ IPv6AcceptRA.UseGateway, config_parse_bool,
IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_route_prefix)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
+IPv6AcceptRA.UsePREF64, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_pref64)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
IPv6AcceptRA.UseDomains, config_parse_ipv6_accept_ra_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains)
IPv6AcceptRA.UseMTU, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_mtu)
@@ -384,6 +385,9 @@ IPv6SendRA.EmitDomains, config_parse_bool,
IPv6SendRA.Domains, config_parse_radv_search_domains, 0, 0
IPv6SendRA.DNSLifetimeSec, config_parse_sec, 0, offsetof(Network, router_dns_lifetime_usec)
IPv6SendRA.UplinkInterface, config_parse_uplink, 0, 0
+IPv6SendRA.HomeAgent, config_parse_bool, 0, offsetof(Network, router_home_agent_information)
+IPv6SendRA.HomeAgentLifetimeSec, config_parse_router_home_agent_lifetime, 0, offsetof(Network, home_agent_lifetime_usec)
+IPv6SendRA.HomeAgentPreference, config_parse_uint16, 0, offsetof(Network, router_home_agent_preference)
IPv6Prefix.Prefix, config_parse_prefix, 0, 0
IPv6Prefix.OnLink, config_parse_prefix_boolean, 0, 0
IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_boolean, 0, 0
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 19e4657f3c..3e1110e6d7 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -242,6 +242,10 @@ struct Network {
OrderedSet *router_search_domains;
int router_uplink_index;
char *router_uplink_name;
+ /* Mobile IPv6 Home Agent */
+ bool router_home_agent_information;
+ uint16_t router_home_agent_preference;
+ usec_t home_agent_lifetime_usec;
/* DHCP Prefix Delegation support */
int dhcp_pd;
@@ -329,6 +333,7 @@ struct Network {
bool ipv6_accept_ra_use_icmp6_ratelimit;
bool ipv6_accept_ra_quickack;
bool ipv6_accept_ra_use_captive_portal;
+ bool ipv6_accept_ra_use_pref64;
bool active_slave;
bool primary_slave;
DHCPUseDomains ipv6_accept_ra_use_domains;
diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c
index 57fd68f5a0..b5ee1c322d 100644
--- a/src/network/networkd-radv.c
+++ b/src/network/networkd-radv.c
@@ -600,6 +600,18 @@ static int radv_configure(Link *link) {
if (r < 0)
return log_link_debug_errno(link, r, "Could not set RA Domains: %m");
+ r = sd_radv_set_home_agent_information(link->radv, link->network->router_home_agent_information);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_set_home_agent_preference(link->radv, link->network->router_home_agent_preference);
+ if (r < 0)
+ return r;
+
+ r = sd_radv_set_home_agent_lifetime(link->radv, DIV_ROUND_UP(link->network->home_agent_lifetime_usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+
return 0;
}
@@ -1575,3 +1587,47 @@ int config_parse_router_preference(
return 0;
}
+
+int config_parse_router_home_agent_lifetime(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t usec, *home_agent_lifetime_usec = ASSERT_PTR(data);
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *home_agent_lifetime_usec = 0;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, &usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (usec == USEC_INFINITY || usec == 0 ||
+ DIV_ROUND_UP(usec, USEC_PER_SEC) > RADV_MAX_HOME_AGENT_LIFETIME_USEC) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid %s= must be in the range 1...%lu seconds, ignoring: %s", lvalue,
+ RADV_MAX_HOME_AGENT_LIFETIME_USEC / USEC_PER_SEC, rvalue);
+ return 0;
+ }
+
+ *home_agent_lifetime_usec = usec;
+ return 0;
+}
diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h
index 8ea1a85b26..48677b50de 100644
--- a/src/network/networkd-radv.h
+++ b/src/network/networkd-radv.h
@@ -98,3 +98,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix_lifetime);
CONFIG_PARSER_PROTOTYPE(config_parse_pref64_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_pref64_prefix_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_router_home_agent_lifetime);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index d3ae547746..9e74ead8d5 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -976,13 +976,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_UUID:
- r = sd_id128_from_string(optarg, &arg_uuid);
- if (r < 0)
- return log_error_errno(r, "Invalid UUID: %s", optarg);
-
- if (sd_id128_is_null(arg_uuid))
+ r = id128_from_string_nonzero(optarg, &arg_uuid);
+ if (r == -ENXIO)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Machine UUID may not be all zeroes.");
+ if (r < 0)
+ return log_error_errno(r, "Invalid UUID: %s", optarg);
arg_settings_mask |= SETTING_MACHINE_ID;
break;
diff --git a/src/oom/oomd-manager.c b/src/oom/oomd-manager.c
index 514a4750d2..49896ea7f1 100644
--- a/src/oom/oomd-manager.c
+++ b/src/oom/oomd-manager.c
@@ -127,6 +127,12 @@ static int process_managed_oom_message(Manager *m, uid_t uid, JsonVariant *param
ctx->mem_pressure_limit = limit;
}
+ /* Toggle wake-ups for "ManagedOOMSwap" if entries are present. */
+ r = sd_event_source_set_enabled(m->swap_context_event_source,
+ hashmap_isempty(m->monitored_swap_cgroup_contexts) ? SD_EVENT_OFF : SD_EVENT_ON);
+ if (r < 0)
+ return log_error_errno(r, "Failed to toggle enabled state of swap context source: %m");
+
return 0;
}
@@ -348,6 +354,7 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
int r;
assert(s);
+ assert(!hashmap_isempty(m->monitored_swap_cgroup_contexts));
/* Reset timer */
r = sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &usec_now);
@@ -368,13 +375,9 @@ static int monitor_swap_contexts_handler(sd_event_source *s, uint64_t usec, void
/* We still try to acquire system information for oomctl even if no units want swap monitoring */
r = oomd_system_context_acquire("/proc/meminfo", &m->system_context);
/* If there are no units depending on swap actions, the only error we exit on is ENOMEM. */
- if (r == -ENOMEM || (r < 0 && !hashmap_isempty(m->monitored_swap_cgroup_contexts)))
+ if (r < 0)
return log_error_errno(r, "Failed to acquire system context: %m");
- /* Return early if nothing is requesting swap monitoring */
- if (hashmap_isempty(m->monitored_swap_cgroup_contexts))
- return 0;
-
/* Note that m->monitored_swap_cgroup_contexts does not need to be updated every interval because only the
* system context is used for deciding whether the swap threshold is hit. m->monitored_swap_cgroup_contexts
* is only used to decide which cgroups to kill (and even then only the resource usages of its descendent
@@ -594,7 +597,7 @@ static int monitor_swap_contexts(Manager *m) {
if (r < 0)
return r;
- r = sd_event_source_set_enabled(s, SD_EVENT_ON);
+ r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
if (r < 0)
return r;
diff --git a/src/partition/repart.c b/src/partition/repart.c
index e403012e92..b6fe9ef79e 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -2438,11 +2438,8 @@ static int context_load_partition_table(Context *context) {
if (r < 0)
return log_error_errno(r, "Failed to get current GPT disk label UUID: %m");
- r = sd_id128_from_string(disk_uuid_string, &disk_uuid);
- if (r < 0)
- return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
-
- if (sd_id128_is_null(disk_uuid)) {
+ r = id128_from_string_nonzero(disk_uuid_string, &disk_uuid);
+ if (r == -ENXIO) {
r = derive_uuid(context->seed, "disk-uuid", &disk_uuid);
if (r < 0)
return log_error_errno(r, "Failed to acquire disk GPT uuid: %m");
@@ -2450,7 +2447,8 @@ static int context_load_partition_table(Context *context) {
r = fdisk_set_disklabel_id(c);
if (r < 0)
return log_error_errno(r, "Failed to set GPT disk label: %m");
- }
+ } else if (r < 0)
+ return log_error_errno(r, "Failed to parse current GPT disk label UUID: %m");
r = fdisk_get_partitions(c, &t);
if (r < 0)
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index ec4b53b2e8..68c4b2ca1a 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -19,6 +19,7 @@
#include "fileio.h"
#include "fs-util.h"
#include "hostname-util.h"
+#include "id128-util.h"
#include "in-addr-util.h"
#include "log.h"
#include "macro.h"
@@ -944,25 +945,19 @@ int config_parse_id128(
void *data,
void *userdata) {
- sd_id128_t t, *result = data;
+ sd_id128_t *result = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
- r = sd_id128_from_string(rvalue, &t);
- if (r < 0) {
+ r = id128_from_string_nonzero(rvalue, result);
+ if (r == -ENXIO)
+ log_syntax(unit, LOG_WARNING, filename, line, r, "128-bit ID/UUID is all 0, ignoring: %s", rvalue);
+ else if (r < 0)
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128-bit ID/UUID, ignoring: %s", rvalue);
- return 0;
- }
-
- if (sd_id128_is_null(t)) {
- log_syntax(unit, LOG_WARNING, filename, line, 0, "128-bit ID/UUID is all 0, ignoring: %s", rvalue);
- return 0;
- }
- *result = t;
return 0;
}
diff --git a/src/shared/format-table.c b/src/shared/format-table.c
index 679e835db5..20e1675d0d 100644
--- a/src/shared/format-table.c
+++ b/src/shared/format-table.c
@@ -1586,9 +1586,7 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas
else if (d->type == TABLE_TIMESTAMP_DATE)
ret = format_timestamp_style(p, FORMAT_TIMESTAMP_MAX, d->timestamp, TIMESTAMP_DATE);
else if (d->type == TABLE_TIMESTAMP_RELATIVE_MONOTONIC)
- ret = format_timestamp_relative_full(p, FORMAT_TIMESTAMP_RELATIVE_MAX,
- d->timestamp, CLOCK_MONOTONIC,
- /* implicit_left = */ false);
+ ret = format_timestamp_relative_monotonic(p, FORMAT_TIMESTAMP_RELATIVE_MAX, d->timestamp);
else
ret = format_timestamp_relative_full(p, FORMAT_TIMESTAMP_RELATIVE_MAX,
d->timestamp, CLOCK_REALTIME,
diff --git a/src/shared/id128-print.c b/src/shared/id128-print.c
index f232767adf..c9509b28f0 100644
--- a/src/shared/id128-print.c
+++ b/src/shared/id128-print.c
@@ -11,12 +11,10 @@
#include "terminal-util.h"
int id128_pretty_print_sample(const char *name, sd_id128_t id) {
- _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
- const char *on, *off;
- unsigned i;
+ _cleanup_free_ char *man_link = NULL, *mod_link = NULL;
- on = ansi_highlight();
- off = ansi_normal();
+ const char *on = ansi_highlight(),
+ *off = ansi_normal();
if (terminal_urlify("man:systemd-id128(1)", "systemd-id128(1)", &man_link) < 0)
return log_oom();
@@ -34,8 +32,8 @@ int id128_pretty_print_sample(const char *name, sd_id128_t id) {
on, SD_ID128_FORMAT_VAL(id), off,
man_link,
on, name);
- for (i = 0; i < 16; i++)
- printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
+ for (size_t i = 0; i < 16; i++)
+ printf("%02x%s", id.bytes[i], i < 15 ? "," : "");
printf(")%s\n\n", off);
printf("As Python constant:\n"
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index 9fb9a3e376..b620156c75 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -30,23 +30,22 @@ int switch_root(const char *new_root,
const char *old_root_after, /* path below the new root, where to place the old root after the transition; may be NULL to unmount it */
SwitchRootFlags flags) {
- /* Stuff mounted below /run we don't save on soft reboot, as it might have lost its relevance, i.e.
- * credentials, removable media and such, we rather want that the new boot mounts this fresh.
- * But on the switch from initrd we do use MS_REC, as it is expected that mounts set up in /run
- * are maintained. */
- unsigned long run_mount_flags = MS_BIND|(!FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_RUN) ? MS_REC : 0);
- struct {
+ /* Stuff mounted below /run/ we don't save on soft reboot, as it might have lost its relevance, i.e.
+ * credentials, removable media and such, we rather want that the new boot mounts this fresh. But on
+ * the switch from initrd we do use MS_REC, as it is expected that mounts set up in /run/ are
+ * maintained. */
+ static const struct {
const char *path;
- unsigned long mount_flags;
- bool skip_if_run_is_rec; /* For child mounts of /run, if it's moved recursively no need to handle */
+ unsigned long mount_flags; /* Flags to apply if SWITCH_ROOT_RECURSIVE_RUN is unset */
+ unsigned long mount_flags_recursive_run; /* Flags to apply if SWITCH_ROOT_RECURSIVE_RUN is set (0 if shall be skipped) */
} transfer_table[] = {
- { "/dev", MS_BIND|MS_REC, false }, /* Recursive, because we want to save the original /dev/shm + /dev/pts and similar */
- { "/sys", MS_BIND|MS_REC, false }, /* Similar, we want to retain various API VFS, or the cgroupv1 /sys/fs/cgroup/ tree */
- { "/proc", MS_BIND|MS_REC, false }, /* Similar */
- { "/run", run_mount_flags, false }, /* Recursive except on soft reboot, see above */
- { SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, true }, /* Credentials passed into the system should survive */
- { ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, true }, /* Similar */
- { "/run/host", MS_BIND|MS_REC, true }, /* Host supplied hierarchy should also survive */
+ { "/dev", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Recursive, because we want to save the original /dev/shm/ + /dev/pts/ and similar */
+ { "/sys", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Similar, we want to retain various API VFS, or the cgroupv1 /sys/fs/cgroup/ tree */
+ { "/proc", MS_BIND|MS_REC, MS_BIND|MS_REC }, /* Similar */
+ { "/run", MS_BIND, MS_BIND|MS_REC }, /* Recursive except on soft reboot, see above */
+ { SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, 0 /* skip! */ }, /* Credentials passed into the system should survive */
+ { ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, MS_BIND, 0 /* skip! */ }, /* Similar */
+ { "/run/host", MS_BIND|MS_REC, 0 /* skip! */ }, /* Host supplied hierarchy should also survive */
};
_cleanup_close_ int old_root_fd = -EBADF, new_root_fd = -EBADF;
@@ -129,8 +128,10 @@ int switch_root(const char *new_root,
FOREACH_ARRAY(transfer, transfer_table, ELEMENTSOF(transfer_table)) {
_cleanup_free_ char *chased = NULL;
+ unsigned long mount_flags;
- if (transfer->skip_if_run_is_rec && !FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_RUN))
+ mount_flags = FLAGS_SET(flags, SWITCH_ROOT_RECURSIVE_RUN) ? transfer->mount_flags_recursive_run : transfer->mount_flags;
+ if (mount_flags == 0) /* skip if zero */
continue;
if (access(transfer->path, F_OK) < 0) {
@@ -149,7 +150,7 @@ int switch_root(const char *new_root,
if (r > 0) /* If it is already mounted, then do nothing */
continue;
- r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, transfer->mount_flags, NULL);
+ r = mount_nofollow_verbose(LOG_ERR, transfer->path, chased, NULL, mount_flags, NULL);
if (r < 0)
return r;
}
@@ -172,14 +173,19 @@ int switch_root(const char *new_root,
if (r < 0) {
log_debug_errno(r, "Pivoting root file system failed, moving mounts instead: %m");
+ if (resolved_old_root_after) {
+ r = mount_nofollow_verbose(LOG_ERR, "/", resolved_old_root_after, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+ }
+
/* If we have to use MS_MOVE let's first try to get rid of *all* mounts we can, with the
* exception of the path we want to switch to, plus everything leading to it and within
* it. This is necessary because unlike pivot_root() just moving the mount to the root via
* MS_MOVE won't magically unmount anything below it. Once the chroot() succeeds the mounts
* below would still be around but invisible to us, because not accessible via
* /proc/self/mountinfo. Hence, let's clean everything up first, as long as we still can. */
- if (!FLAGS_SET(flags, SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT))
- (void) umount_recursive_full(NULL, MNT_DETACH, STRV_MAKE(new_root));
+ (void) umount_recursive_full(NULL, MNT_DETACH, STRV_MAKE(new_root));
if (mount(".", "/", NULL, MS_MOVE, NULL) < 0)
return log_error_errno(errno, "Failed to move %s to /: %m", new_root);
diff --git a/src/shared/switch-root.h b/src/shared/switch-root.h
index 20561fcee8..ba0d280eba 100644
--- a/src/shared/switch-root.h
+++ b/src/shared/switch-root.h
@@ -7,8 +7,7 @@ typedef enum SwitchRootFlags {
SWITCH_ROOT_DESTROY_OLD_ROOT = 1 << 0, /* rm -rf old root when switching – under the condition
* that it is backed by non-persistent tmpfs/ramfs/… */
SWITCH_ROOT_DONT_SYNC = 1 << 1, /* don't call sync() immediately before switching root */
- SWITCH_ROOT_SKIP_RECURSIVE_RUN = 1 << 2, /* move /run without MS_REC */
- SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT = 1 << 3, /* do not umount recursively on move */
+ SWITCH_ROOT_RECURSIVE_RUN = 1 << 2, /* move /run/ with MS_REC from old to new root */
} SwitchRootFlags;
int switch_root(const char *new_root, const char *old_root_after, SwitchRootFlags flags);
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
index 263c444ac2..b976b7d8cf 100644
--- a/src/shutdown/shutdown.c
+++ b/src/shutdown/shutdown.c
@@ -169,13 +169,11 @@ static int switch_root_initramfs(void) {
* Disable sync() during switch-root, we after all sync'ed here plenty, and a dumb sync (as opposed
* to the "smart" sync() we did here that looks at progress parameters) would defeat much of our
* efforts here. As the new root will be /run/initramfs/, it is not necessary to mount /run/
- * recursively. Also, do not umount filesystems before MS_MOVE, as that should be done by ourself. */
+ * recursively. */
return switch_root(
/* new_root= */ "/run/initramfs",
/* old_root_after= */ "/oldroot",
- /* flags= */ SWITCH_ROOT_DONT_SYNC |
- SWITCH_ROOT_SKIP_RECURSIVE_RUN |
- SWITCH_ROOT_SKIP_RECURSIVE_UMOUNT);
+ /* flags= */ SWITCH_ROOT_DONT_SYNC);
}
/* Read the following fields from /proc/meminfo:
diff --git a/src/systemctl/systemctl-switch-root.c b/src/systemctl/systemctl-switch-root.c
index b59ea701c9..ae4a1a72c2 100644
--- a/src/systemctl/systemctl-switch-root.c
+++ b/src/systemctl/systemctl-switch-root.c
@@ -4,6 +4,8 @@
#include "bus-error.h"
#include "bus-locator.h"
#include "chase.h"
+#include "fd-util.h"
+#include "initrd-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "proc-cmdline.h"
@@ -47,15 +49,24 @@ int verb_switch_root(int argc, char *argv[], void *userdata) {
if (argc >= 2) {
root = argv[1];
+
if (!path_is_valid(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid root path: %s", root);
+
if (!path_is_absolute(root))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root path is not absolute: %s", root);
- if (path_equal(root, "/"))
+
+ r = path_is_root(root);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if switch-root directory '%s' is current root: %m", root);
+ if (r > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot switch to current root directory: %s", root);
} else
root = "/sysroot";
+ if (!in_initrd())
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not in initrd, refusing switch-root operation.");
+
if (argc >= 3)
init = argv[2];
else {
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index 3303c374ce..a984a9d85e 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -50,6 +50,7 @@ int sd_id128_get_machine(sd_id128_t *ret);
int sd_id128_get_boot(sd_id128_t *ret);
int sd_id128_get_invocation(sd_id128_t *ret);
+int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h
index be702f68a5..89b6413ef9 100644
--- a/src/systemd/sd-ndisc.h
+++ b/src/systemd/sd-ndisc.h
@@ -42,7 +42,8 @@ enum {
SD_NDISC_OPTION_RDNSS = 25,
SD_NDISC_OPTION_FLAGS_EXTENSION = 26,
SD_NDISC_OPTION_DNSSL = 31,
- SD_NDISC_OPTION_CAPTIVE_PORTAL = 37
+ SD_NDISC_OPTION_CAPTIVE_PORTAL = 37,
+ SD_NDISC_OPTION_PREF64 = 38
};
/* Route preference, RFC 4191, Section 2.1 */
@@ -127,6 +128,11 @@ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret);
/* Specific option access: SD_NDISC_OPTION_CAPTIVE_PORTAL */
int sd_ndisc_router_captive_portal_get_uri(sd_ndisc_router *rt, const char **uri, size_t *size);
+/* Specific option access: SD_NDISC_OPTION_PREF64 */
+int sd_ndisc_router_prefix64_get_prefix(sd_ndisc_router *rt, struct in6_addr *ret_addr);
+int sd_ndisc_router_prefix64_get_prefixlen(sd_ndisc_router *rt, unsigned *ret);
+int sd_ndisc_router_prefix64_get_lifetime_sec(sd_ndisc_router *rt, uint16_t *ret);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc, sd_ndisc_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ndisc_router, sd_ndisc_router_unref);
diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h
index 882613c0b3..295b8846f5 100644
--- a/src/systemd/sd-radv.h
+++ b/src/systemd/sd-radv.h
@@ -95,6 +95,11 @@ int sd_radv_pref64_prefix_set_prefix(sd_radv_pref64_prefix *p, const struct in6_
sd_radv_pref64_prefix *sd_radv_pref64_prefix_ref(sd_radv_pref64_prefix *ra);
sd_radv_pref64_prefix *sd_radv_pref64_prefix_unref(sd_radv_pref64_prefix *ra);
+/* Mobile IPv6 extension: Home Agent Info. */
+int sd_radv_set_home_agent_information(sd_radv *ra, int home_agent);
+int sd_radv_set_home_agent_preference(sd_radv *ra, uint16_t preference);
+int sd_radv_set_home_agent_lifetime(sd_radv *ra, uint16_t lifetime);
+
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_route_prefix, sd_radv_route_prefix_unref);
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index 58dc88110e..ae7df27d8f 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -20,6 +20,7 @@
#define ID128_WALDI SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10)
#define STR_WALDI "0102030405060708090a0b0c0d0e0f10"
#define UUID_WALDI "01020304-0506-0708-090a-0b0c0d0e0f10"
+#define STR_NULL "00000000000000000000000000000000"
TEST(id128) {
sd_id128_t id, id2;
@@ -75,6 +76,13 @@ TEST(id128) {
assert_se(sd_id128_from_string("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0);
assert_se(sd_id128_from_string("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0);
+ assert_se(id128_from_string_nonzero(STR_WALDI, &id) == 0);
+ assert_se(id128_from_string_nonzero(STR_NULL, &id) == -ENXIO);
+ assert_se(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f101", &id) < 0);
+ assert_se(id128_from_string_nonzero("01020304-0506-0708-090a-0b0c0d0e0f10-", &id) < 0);
+ assert_se(id128_from_string_nonzero("01020304-0506-0708-090a0b0c0d0e0f10", &id) < 0);
+ assert_se(id128_from_string_nonzero("010203040506-0708-090a-0b0c0d0e0f10", &id) < 0);
+
assert_se(id128_is_valid(STR_WALDI));
assert_se(id128_is_valid(UUID_WALDI));
assert_se(!id128_is_valid(""));
@@ -172,6 +180,11 @@ TEST(id128) {
assert_se(lseek(fd, 0, SEEK_SET) == 0);
assert_se(id128_read_fd(fd, ID128_FORMAT_ANY, NULL) == -EUCLEAN);
+ /* build/systemd-id128 -a f03daaeb1c334b43a732172944bf772e show 51df0b4bc3b04c9780e299b98ca373b8 */
+ assert_se(sd_id128_get_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8),
+ SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
+ assert_se(sd_id128_equal(id, SD_ID128_MAKE(1d,ee,59,54,e7,5c,4d,6f,b9,6c,c6,c0,4c,a1,8a,86)));
+
if (sd_booted() > 0 && sd_id128_get_machine(NULL) >= 0) {
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0);
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0);
@@ -179,6 +192,10 @@ TEST(id128) {
assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0);
assert_se(!sd_id128_equal(id, id2));
}
+
+ /* Check return values */
+ assert_se(sd_id128_get_app_specific(SD_ID128_ALLF, SD_ID128_NULL, &id) == -ENXIO);
+ assert_se(sd_id128_get_app_specific(SD_ID128_NULL, SD_ID128_ALLF, &id) == 0);
}
TEST(sd_id128_get_invocation) {
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
index b5f0008d76..a8fd45df73 100644
--- a/src/test/test-string-util.c
+++ b/src/test/test-string-util.c
@@ -260,6 +260,8 @@ TEST(strextend_with_separator) {
TEST(strrep) {
_cleanup_free_ char *one = NULL, *three = NULL, *zero = NULL;
+ char *onea, *threea;
+
one = strrep("waldo", 1);
three = strrep("waldo", 3);
zero = strrep("waldo", 0);
@@ -267,6 +269,12 @@ TEST(strrep) {
assert_se(streq(one, "waldo"));
assert_se(streq(three, "waldowaldowaldo"));
assert_se(streq(zero, ""));
+
+ onea = strrepa("waldo", 1);
+ threea = strrepa("waldo", 3);
+
+ assert_se(streq(onea, "waldo"));
+ assert_se(streq(threea, "waldowaldowaldo"));
}
TEST(string_has_cc) {