diff options
Diffstat (limited to 'src')
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, ×tamp_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) { |