From 51f93e00a23fbd09f5ad96da6290bf4ca737d46a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 29 Jul 2021 12:46:06 +0200 Subject: [PATCH] systemd: update code from upstream (2021-10-06) This is a direct dump from systemd git. ====== SYSTEMD_DIR=../systemd COMMIT=e5f093bf99ed742b74b81ac97e0e5e01cde02d95 ( cd "$SYSTEMD_DIR" git checkout "$COMMIT" git reset --hard git clean -fdx ) git ls-files -z :/src/libnm-systemd-core/src/ \ :/src/libnm-systemd-shared/src/ \ :/src/libnm-std-aux/unaligned.h | \ xargs -0 rm -f nm_copy_sd_shared() { mkdir -p "./src/libnm-systemd-shared/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-shared/$1" } nm_copy_sd_core() { mkdir -p "./src/libnm-systemd-core/$(dirname "$1")" cp "$SYSTEMD_DIR/$1" "./src/libnm-systemd-core/$1" } nm_copy_sd_stdaux() { mkdir -p "./src/libnm-std-aux/" cp "$SYSTEMD_DIR/$1" "./src/libnm-std-aux/${1##*/}" } nm_copy_sd_core "src/libsystemd-network/arp-util.c" nm_copy_sd_core "src/libsystemd-network/arp-util.h" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.c" nm_copy_sd_core "src/libsystemd-network/dhcp-identifier.h" nm_copy_sd_core "src/libsystemd-network/dhcp-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp-packet.c" nm_copy_sd_core "src/libsystemd-network/dhcp-protocol.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-lease-internal.h" nm_copy_sd_core "src/libsystemd-network/dhcp6-network.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-option.c" nm_copy_sd_core "src/libsystemd-network/dhcp6-protocol.h" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.c" nm_copy_sd_core "src/libsystemd-network/lldp-neighbor.h" nm_copy_sd_core "src/libsystemd-network/lldp-network.c" nm_copy_sd_core "src/libsystemd-network/lldp-network.h" nm_copy_sd_core "src/libsystemd-network/lldp-rx-internal.h" nm_copy_sd_core "src/libsystemd-network/network-common.c" nm_copy_sd_core "src/libsystemd-network/network-common.h" nm_copy_sd_core "src/libsystemd-network/network-internal.c" nm_copy_sd_core "src/libsystemd-network/network-internal.h" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-client.c" nm_copy_sd_core "src/libsystemd-network/sd-dhcp6-lease.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4acd.c" nm_copy_sd_core "src/libsystemd-network/sd-ipv4ll.c" nm_copy_sd_core "src/libsystemd-network/sd-lldp-rx.c" nm_copy_sd_core "src/libsystemd/sd-event/event-source.h" nm_copy_sd_core "src/libsystemd/sd-event/event-util.c" nm_copy_sd_core "src/libsystemd/sd-event/event-util.h" nm_copy_sd_core "src/libsystemd/sd-event/sd-event.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.c" nm_copy_sd_core "src/libsystemd/sd-id128/id128-util.h" nm_copy_sd_core "src/libsystemd/sd-id128/sd-id128.c" nm_copy_sd_core "src/systemd/_sd-common.h" nm_copy_sd_core "src/systemd/sd-dhcp-client.h" nm_copy_sd_core "src/systemd/sd-dhcp-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp-option.h" nm_copy_sd_core "src/systemd/sd-dhcp6-client.h" nm_copy_sd_core "src/systemd/sd-dhcp6-lease.h" nm_copy_sd_core "src/systemd/sd-dhcp6-option.h" nm_copy_sd_core "src/systemd/sd-event.h" nm_copy_sd_core "src/systemd/sd-id128.h" nm_copy_sd_core "src/systemd/sd-ipv4acd.h" nm_copy_sd_core "src/systemd/sd-ipv4ll.h" nm_copy_sd_core "src/systemd/sd-lldp-rx.h" nm_copy_sd_core "src/systemd/sd-lldp.h" nm_copy_sd_core "src/systemd/sd-ndisc.h" nm_copy_sd_shared "src/basic/alloc-util.c" nm_copy_sd_shared "src/basic/alloc-util.h" nm_copy_sd_shared "src/basic/async.h" nm_copy_sd_shared "src/basic/cgroup-util.h" nm_copy_sd_shared "src/basic/dns-def.h" nm_copy_sd_shared "src/basic/env-file.c" nm_copy_sd_shared "src/basic/env-file.h" nm_copy_sd_shared "src/basic/env-util.c" nm_copy_sd_shared "src/basic/env-util.h" nm_copy_sd_shared "src/basic/errno-util.h" nm_copy_sd_shared "src/basic/escape.c" nm_copy_sd_shared "src/basic/escape.h" nm_copy_sd_shared "src/basic/ether-addr-util.c" nm_copy_sd_shared "src/basic/ether-addr-util.h" nm_copy_sd_shared "src/basic/extract-word.c" nm_copy_sd_shared "src/basic/extract-word.h" nm_copy_sd_shared "src/basic/fd-util.c" nm_copy_sd_shared "src/basic/fd-util.h" nm_copy_sd_shared "src/basic/fileio.c" nm_copy_sd_shared "src/basic/fileio.h" nm_copy_sd_shared "src/basic/format-util.c" nm_copy_sd_shared "src/basic/format-util.h" nm_copy_sd_shared "src/basic/fs-util.c" nm_copy_sd_shared "src/basic/fs-util.h" nm_copy_sd_shared "src/basic/hash-funcs.c" nm_copy_sd_shared "src/basic/hash-funcs.h" nm_copy_sd_shared "src/basic/hashmap.c" nm_copy_sd_shared "src/basic/hashmap.h" nm_copy_sd_shared "src/basic/hexdecoct.c" nm_copy_sd_shared "src/basic/hexdecoct.h" nm_copy_sd_shared "src/basic/hostname-util.c" nm_copy_sd_shared "src/basic/hostname-util.h" nm_copy_sd_shared "src/basic/in-addr-util.c" nm_copy_sd_shared "src/basic/in-addr-util.h" nm_copy_sd_shared "src/basic/inotify-util.c" nm_copy_sd_shared "src/basic/inotify-util.h" nm_copy_sd_shared "src/basic/io-util.c" nm_copy_sd_shared "src/basic/io-util.h" nm_copy_sd_shared "src/basic/list.h" nm_copy_sd_shared "src/basic/log.h" nm_copy_sd_shared "src/basic/macro.h" nm_copy_sd_shared "src/basic/memory-util.c" nm_copy_sd_shared "src/basic/memory-util.h" nm_copy_sd_shared "src/basic/mempool.c" nm_copy_sd_shared "src/basic/mempool.h" nm_copy_sd_shared "src/basic/missing_fcntl.h" nm_copy_sd_shared "src/basic/missing_random.h" nm_copy_sd_shared "src/basic/missing_socket.h" nm_copy_sd_shared "src/basic/missing_stat.h" nm_copy_sd_shared "src/basic/missing_syscall.h" nm_copy_sd_shared "src/basic/missing_type.h" nm_copy_sd_shared "src/basic/ordered-set.c" nm_copy_sd_shared "src/basic/ordered-set.h" nm_copy_sd_shared "src/basic/parse-util.c" nm_copy_sd_shared "src/basic/parse-util.h" nm_copy_sd_shared "src/basic/path-util.c" nm_copy_sd_shared "src/basic/path-util.h" nm_copy_sd_shared "src/basic/prioq.c" nm_copy_sd_shared "src/basic/prioq.h" nm_copy_sd_shared "src/basic/process-util.c" nm_copy_sd_shared "src/basic/process-util.h" nm_copy_sd_shared "src/basic/random-util.c" nm_copy_sd_shared "src/basic/random-util.h" nm_copy_sd_shared "src/basic/ratelimit.c" nm_copy_sd_shared "src/basic/ratelimit.h" nm_copy_sd_shared "src/basic/set.h" nm_copy_sd_shared "src/basic/signal-util.c" nm_copy_sd_shared "src/basic/signal-util.h" nm_copy_sd_shared "src/basic/siphash24.h" nm_copy_sd_shared "src/basic/socket-util.c" nm_copy_sd_shared "src/basic/socket-util.h" nm_copy_sd_shared "src/basic/sort-util.h" nm_copy_sd_shared "src/basic/sparse-endian.h" nm_copy_sd_shared "src/basic/stat-util.c" nm_copy_sd_shared "src/basic/stat-util.h" nm_copy_sd_shared "src/basic/stdio-util.h" nm_copy_sd_shared "src/basic/string-table.c" nm_copy_sd_shared "src/basic/string-table.h" nm_copy_sd_shared "src/basic/string-util.c" nm_copy_sd_shared "src/basic/string-util.h" nm_copy_sd_shared "src/basic/strv.c" nm_copy_sd_shared "src/basic/strv.h" nm_copy_sd_shared "src/basic/strxcpyx.c" nm_copy_sd_shared "src/basic/strxcpyx.h" nm_copy_sd_shared "src/basic/time-util.c" nm_copy_sd_shared "src/basic/time-util.h" nm_copy_sd_shared "src/basic/tmpfile-util.c" nm_copy_sd_shared "src/basic/tmpfile-util.h" nm_copy_sd_shared "src/basic/umask-util.h" nm_copy_sd_shared "src/basic/user-util.h" nm_copy_sd_shared "src/basic/utf8.c" nm_copy_sd_shared "src/basic/utf8.h" nm_copy_sd_shared "src/basic/util.c" nm_copy_sd_shared "src/basic/util.h" nm_copy_sd_shared "src/fundamental/macro-fundamental.h" nm_copy_sd_shared "src/fundamental/string-util-fundamental.c" nm_copy_sd_shared "src/fundamental/string-util-fundamental.h" nm_copy_sd_shared "src/fundamental/type.h" nm_copy_sd_shared "src/shared/dns-domain.c" nm_copy_sd_shared "src/shared/dns-domain.h" nm_copy_sd_shared "src/shared/log-link.h" nm_copy_sd_shared "src/shared/web-util.c" nm_copy_sd_shared "src/shared/web-util.h" nm_copy_sd_stdaux "src/basic/unaligned.h" --- .../src/libsystemd-network/dhcp-internal.h | 6 +- .../src/libsystemd-network/dhcp6-internal.h | 38 +- .../libsystemd-network/dhcp6-lease-internal.h | 14 +- .../src/libsystemd-network/dhcp6-option.c | 649 ++++++++------- .../src/libsystemd-network/lldp-neighbor.c | 119 +-- .../src/libsystemd-network/lldp-neighbor.h | 8 +- .../src/libsystemd-network/lldp-network.c | 3 + .../{lldp-internal.h => lldp-rx-internal.h} | 24 +- .../src/libsystemd-network/network-common.c | 15 +- .../src/libsystemd-network/network-common.h | 26 +- .../src/libsystemd-network/sd-dhcp-client.c | 34 +- .../src/libsystemd-network/sd-dhcp6-client.c | 241 +++--- .../src/libsystemd-network/sd-dhcp6-lease.c | 195 ++--- .../src/libsystemd-network/sd-ipv4acd.c | 30 +- .../src/libsystemd-network/sd-ipv4ll.c | 15 +- .../src/libsystemd-network/sd-lldp-rx.c | 523 ++++++++++++ .../src/libsystemd-network/sd-lldp.c | 522 ------------ .../src/libsystemd/sd-event/event-source.h | 2 +- .../src/libsystemd/sd-event/sd-event.c | 14 +- .../src/libsystemd/sd-id128/id128-util.c | 7 +- .../src/libsystemd/sd-id128/id128-util.h | 2 + .../src/systemd/sd-dhcp-client.h | 2 +- .../src/systemd/sd-dhcp6-client.h | 2 +- .../src/systemd/sd-dhcp6-lease.h | 10 +- src/libnm-systemd-core/src/systemd/sd-id128.h | 4 +- .../src/systemd/sd-ipv4acd.h | 2 +- .../src/systemd/sd-ipv4ll.h | 2 +- .../src/systemd/sd-lldp-rx.h | 109 +++ src/libnm-systemd-core/src/systemd/sd-lldp.h | 105 +-- src/libnm-systemd-core/src/systemd/sd-ndisc.h | 2 +- .../src/basic/alloc-util.h | 17 +- .../src/basic/cgroup-util.h | 6 +- src/libnm-systemd-shared/src/basic/env-util.c | 84 +- src/libnm-systemd-shared/src/basic/env-util.h | 6 +- src/libnm-systemd-shared/src/basic/fd-util.c | 8 +- src/libnm-systemd-shared/src/basic/fd-util.h | 16 +- src/libnm-systemd-shared/src/basic/fileio.c | 147 ++-- src/libnm-systemd-shared/src/basic/fileio.h | 36 +- .../src/basic/format-util.c | 62 +- .../src/basic/format-util.h | 19 +- src/libnm-systemd-shared/src/basic/fs-util.c | 780 +----------------- src/libnm-systemd-shared/src/basic/fs-util.h | 47 -- src/libnm-systemd-shared/src/basic/hashmap.c | 6 +- .../src/basic/hostname-util.c | 65 +- .../src/basic/hostname-util.h | 38 +- .../src/basic/in-addr-util.c | 31 +- .../src/basic/in-addr-util.h | 8 - .../src/basic/inotify-util.c | 29 + .../src/basic/inotify-util.h | 22 + src/libnm-systemd-shared/src/basic/list.h | 7 +- src/libnm-systemd-shared/src/basic/log.h | 10 +- src/libnm-systemd-shared/src/basic/macro.h | 52 +- .../src/basic/memory-util.c | 13 +- .../src/basic/memory-util.h | 14 +- .../src/basic/missing_syscall.h | 20 + .../src/basic/parse-util.c | 8 +- .../src/basic/parse-util.h | 9 +- .../src/basic/path-util.c | 100 ++- .../src/basic/path-util.h | 4 +- src/libnm-systemd-shared/src/basic/prioq.c | 10 + src/libnm-systemd-shared/src/basic/prioq.h | 1 + .../src/basic/process-util.c | 21 +- .../src/basic/process-util.h | 3 +- src/libnm-systemd-shared/src/basic/set.h | 2 +- .../src/basic/signal-util.c | 2 +- .../src/basic/socket-util.c | 27 +- .../src/basic/socket-util.h | 33 +- .../src/basic/stat-util.c | 3 +- .../src/basic/stdio-util.h | 11 +- .../src/basic/string-util.c | 16 + .../src/basic/string-util.h | 18 +- src/libnm-systemd-shared/src/basic/strv.c | 18 + src/libnm-systemd-shared/src/basic/strv.h | 11 +- .../src/basic/time-util.c | 76 +- .../src/basic/tmpfile-util.c | 6 +- .../src/fundamental/macro-fundamental.h | 54 +- .../src/fundamental/string-util-fundamental.h | 17 + .../src/shared/log-link.h | 18 - .../src/shared/web-util.c | 17 +- .../src/shared/web-util.h | 1 + 80 files changed, 2208 insertions(+), 2546 deletions(-) rename src/libnm-systemd-core/src/libsystemd-network/{lldp-internal.h => lldp-rx-internal.h} (56%) create mode 100644 src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c delete mode 100644 src/libnm-systemd-core/src/libsystemd-network/sd-lldp.c create mode 100644 src/libnm-systemd-core/src/systemd/sd-lldp-rx.h create mode 100644 src/libnm-systemd-shared/src/basic/inotify-util.c create mode 100644 src/libnm-systemd-shared/src/basic/inotify-util.h diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h index 16999971d2..6538f05bdb 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h @@ -12,7 +12,7 @@ #include "sd-dhcp-client.h" #include "dhcp-protocol.h" -#include "log-link.h" +#include "network-common.h" #include "socket-util.h" typedef struct sd_dhcp_option { @@ -75,10 +75,10 @@ void dhcp_client_set_test_mode(sd_dhcp_client *client, bool test_mode); #define log_dhcp_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ "DHCPv4 client: ", \ - sd_dhcp_client_get_ifname(client), \ + sd_dhcp_client, client, \ error, fmt, ##__VA_ARGS__) #define log_dhcp_client(client, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ "DHCPv4 client: ", \ - sd_dhcp_client_get_ifname(client), \ + sd_dhcp_client, client, \ 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h index f0f814957f..4eb56aa8c9 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -13,8 +13,8 @@ #include "hashmap.h" #include "list.h" -#include "log-link.h" #include "macro.h" +#include "network-common.h" #include "sparse-endian.h" typedef struct sd_dhcp6_option { @@ -101,15 +101,29 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); -int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, - size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_status(DHCP6Option *option, size_t len); -int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code); -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count); -int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, - char ***str_arr); -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); + +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data); +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA *ret); +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, @@ -125,10 +139,10 @@ void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); #define log_dhcp6_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ "DHCPv6 client: ", \ - sd_dhcp6_client_get_ifname(client), \ + sd_dhcp6_client, client, \ error, fmt, ##__VA_ARGS__) #define log_dhcp6_client(client, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ "DHCPv6 client: ", \ - sd_dhcp6_client_get_ifname(client), \ + sd_dhcp6_client, client, \ 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h index 391b4f1fa9..8801497b72 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h @@ -28,11 +28,11 @@ struct sd_dhcp6_lease { struct in6_addr *dns; size_t dns_count; char **domains; - size_t domains_count; struct in6_addr *ntp; size_t ntp_count; char **ntp_fqdn; - size_t ntp_fqdn_count; + struct in6_addr *sntp; + size_t sntp_count; char *fqdn; }; @@ -50,12 +50,10 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen); -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) ; +int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c index 97ef03a2d2..0276ce693c 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -14,29 +14,12 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "escape.h" #include "memory-util.h" #include "sparse-endian.h" #include "strv.h" #include "unaligned.h" -typedef struct DHCP6StatusOption { - struct DHCP6Option option; - be16_t status; - char msg[]; -} _packed_ DHCP6StatusOption; - -typedef struct DHCP6AddressOption { - struct DHCP6Option option; - struct iaaddr iaaddr; - uint8_t options[]; -} _packed_ DHCP6AddressOption; - -typedef struct DHCP6PDPrefixOption { - struct DHCP6Option option; - struct iapdprefix iapdprefix; - uint8_t options[]; -} _packed_ DHCP6PDPrefixOption; - #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) @@ -370,337 +353,410 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); } -static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { - DHCP6Option *option = (DHCP6Option*) *buf; - uint16_t len; +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data) { - assert_return(buf, -EINVAL); - assert_return(optcode, -EINVAL); - assert_return(optlen, -EINVAL); + const DHCP6Option *option; + size_t len; - if (*buflen < offsetof(DHCP6Option, data)) - return -ENOMSG; + assert(buf); + assert(offset); + assert(ret_option_code); + assert(ret_option_data_len); + assert(ret_option_data); + if (buflen < offsetof(DHCP6Option, data)) + return -EBADMSG; + + if (*offset >= buflen - offsetof(DHCP6Option, data)) + return -EBADMSG; + + option = (const DHCP6Option*) (buf + *offset); len = be16toh(option->len); - if (len > *buflen) + if (len > buflen - offsetof(DHCP6Option, data) - *offset) + return -EBADMSG; + + *offset += offsetof(DHCP6Option, data) + len; + *ret_option_code = be16toh(option->code); + *ret_option_data_len = len; + *ret_option_data = option->data; + + return 0; +} + +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) { + assert(data); + + if (data_len < sizeof(uint16_t)) + return -EBADMSG; + + if (ret_status_message) { + char *msg; + + /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415. + * Let's escape unsafe characters for safety. */ + msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t)); + if (!msg) + return -ENOMEM; + + *ret_status_message = msg; + } + + return unaligned_read_be16(data); +} + +static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { + int r; + + assert(buf); + + for(size_t offset = 0; offset < buflen;) { + const uint8_t *data; + size_t data_len; + uint16_t code; + + r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data); + if (r < 0) + return r; + + switch(code) { + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(data, data_len, &msg); + if (r == -ENOMEM) + return r; + if (r < 0) + /* Let's log but ignore the invalid status option. */ + log_dhcp6_client_errno(client, r, + "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); + else if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address or PD prefix option with non-zero status: %s%s%s", + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + default: + log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code); + } + } + + return 0; +} + +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { + uint32_t lt_valid, lt_pref; + DHCP6Address *a; + int r; + + assert(data); + assert(ret); + + if (len < sizeof(struct iaaddr)) + return -EBADMSG; + + lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid); + lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred); + + if (lt_valid == 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address with preferred lifetime %"PRIu32 + " larger than valid lifetime %"PRIu32", ignoring.", + lt_pref, lt_valid); + + if (len > sizeof(struct iaaddr)) { + r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr)); + if (r < 0) + return r; + } + + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + LIST_INIT(addresses, a); + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + + *ret = a; + return 0; +} + +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { + uint32_t lt_valid, lt_pref; + DHCP6Address *a; + int r; + + if (len < sizeof(struct iapdprefix)) return -ENOMSG; - *optcode = be16toh(option->code); - *optlen = len; + lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); + lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); - *buf += 4; - *buflen -= 4; - - return 0; -} - -int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, - size_t *optlen, uint8_t **optvalue) { - int r; - - assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL); - - r = option_parse_hdr(buf, buflen, optcode, optlen); - if (r < 0) - return r; - - if (*optlen > *buflen) - return -ENOBUFS; - - *optvalue = *buf; - *buflen -= *optlen; - *buf += *optlen; - - return 0; -} - -int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { - DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; - - if (len < sizeof(DHCP6StatusOption) || - be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption)) - return -ENOBUFS; - - return be16toh(statusopt->status); -} - -static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { - DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; - DHCP6Address *addr; - uint32_t lt_valid, lt_pref; - int r; - - if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option)) - return -ENOBUFS; - - lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); - lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred); - - if (lt_valid == 0 || lt_pref > lt_valid) + if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Valid lifetime of an IA address is zero or " - "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, + "Received a PD prefix with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received a PD prefix with preferred lifetime %"PRIu32 + " larger than valid lifetime %"PRIu32", ignoring.", lt_pref, lt_valid); - if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); + if (len > sizeof(struct iapdprefix)) { + r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix)); if (r < 0) return r; - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Non-zero status code '%s' for address is received", - dhcp6_message_status_to_string(r)); } - addr = new0(DHCP6Address, 1); - if (!addr) + a = new(DHCP6Address, 1); + if (!a) return -ENOMEM; - LIST_INIT(addresses, addr); - memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); - - LIST_PREPEND(addresses, ia->addresses, addr); - - *ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + LIST_INIT(addresses, a); + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); + *ret = a; return 0; } -static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { - DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; - DHCP6Address *prefix; - uint32_t lt_valid, lt_pref; +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA *ret) { + + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; + be32_t received_iaid; + size_t offset; int r; - if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option)) - return -ENOBUFS; + assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); + assert(option_data); + assert(ret); - lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); - lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred); + /* This will return the following: + * -ENOMEM: memory allocation error, + * -ENOANO: unmatching IAID, + * -EINVAL: non-zero status code, or invalid lifetime, + * -EBADMSG: invalid message format, + * -ENODATA: no valid address or PD prefix, + * 0: success. */ - if (lt_valid == 0 || lt_pref > lt_valid) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Valid lifetieme of a PD prefix is zero or " - "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, - lt_pref, lt_valid); - - if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); - if (r < 0) - return r; - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Non-zero status code '%s' for PD prefix is received", - dhcp6_message_status_to_string(r)); - } - - prefix = new0(DHCP6Address, 1); - if (!prefix) - return -ENOMEM; - - LIST_INIT(addresses, prefix); - memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix)); - - LIST_PREPEND(addresses, ia->addresses, prefix); - - *ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); - - return 0; -} - -int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) { - uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; - uint16_t iatype, optlen; - size_t iaaddr_offset; - int r = 0, status; - size_t i, len; - uint16_t opt; - - assert_return(ia, -EINVAL); - assert_return(!ia->addresses, -EINVAL); - - iatype = be16toh(iaoption->code); - len = be16toh(iaoption->len); - - switch (iatype) { + switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - if (len < DHCP6_OPTION_IA_NA_LEN) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_NA_LEN) + return -EBADMSG; - iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); - - lt_t1 = be32toh(ia->ia_na.lifetime_t1); - lt_t2 = be32toh(ia->ia_na.lifetime_t2); - - if (lt_t1 > lt_t2) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec", - lt_t1, lt_t2); + offset = DHCP6_OPTION_IA_NA_LEN; + received_iaid = ((const struct ia_na*) option_data)->id; + lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1); + lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2); break; case SD_DHCP6_OPTION_IA_PD: - if (len < sizeof(ia->ia_pd)) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_PD_LEN) + return -EBADMSG; - iaaddr_offset = sizeof(ia->ia_pd); - memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); - - lt_t1 = be32toh(ia->ia_pd.lifetime_t1); - lt_t2 = be32toh(ia->ia_pd.lifetime_t2); - - if (lt_t1 > lt_t2) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec", - lt_t1, lt_t2); + offset = DHCP6_OPTION_IA_PD_LEN; + received_iaid = ((const struct ia_pd*) option_data)->id; + lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1); + lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2); break; case SD_DHCP6_OPTION_IA_TA: - if (len < DHCP6_OPTION_IA_TA_LEN) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_TA_LEN) + return -ENOMSG; - iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); + offset = DHCP6_OPTION_IA_TA_LEN; + received_iaid = ((const struct ia_ta*) option_data)->id; + lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */ break; default: - return -ENOMSG; + assert_not_reached(); } - ia->type = iatype; - i = iaaddr_offset; + /* According to RFC8415, IAs which do not match the client's IAID should be ignored, + * but not necessary to ignore or refuse the whole message. */ + if (received_iaid != iaid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), + "Received an IA option with a different IAID " + "from the one chosen by the client, ignoring."); - while (i < len) { - DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; + if (lt_t1 > lt_t2) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", + lt_t1, lt_t2); - if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) - return -ENOBUFS; + for (; offset < option_data_len;) { + const uint8_t *subdata; + size_t subdata_len; + uint16_t subopt; - opt = be16toh(option->code); - optlen = be16toh(option->len); + r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata); + if (r < 0) + return r; - switch (opt) { - case SD_DHCP6_OPTION_IAADDR: + switch (subopt) { + case SD_DHCP6_OPTION_IAADDR: { + DHCP6Address *a; - if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA Address option not in IA NA or TA option"); - - r = dhcp6_option_parse_address(client, option, ia, <_valid); - if (r < 0 && r != -EINVAL) - return r; - if (r >= 0 && lt_valid < lt_min) - lt_min = lt_valid; - - break; - - case SD_DHCP6_OPTION_IA_PD_PREFIX: - - if (ia->type != SD_DHCP6_OPTION_IA_PD) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA PD Prefix option not in IA PD option"); - - r = dhcp6_option_parse_pdprefix(client, option, ia, <_valid); - if (r < 0 && r != -EINVAL) - return r; - if (r >= 0 && lt_valid < lt_min) - lt_min = lt_valid; - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: - - status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); - if (status < 0) - return status; - - if (status > 0) { - if (ret_status_code) - *ret_status_code = status; - - log_dhcp6_client(client, "IA status %s", - dhcp6_message_status_to_string(status)); - - return 0; + if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { + log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring."); + continue; } - break; + r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + if (r == -ENOMEM) + return r; + if (r < 0) + /* Ignore the sub-option on non-critical errors. */ + continue; + lt_min = MIN(lt_min, a->iaaddr.lifetime_valid); + LIST_PREPEND(addresses, ia.addresses, a); + break; + } + case SD_DHCP6_OPTION_IA_PD_PREFIX: { + DHCP6Address *a; + + if (option_code != SD_DHCP6_OPTION_IA_PD) { + log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring"); + continue; + } + + r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a); + if (r == -ENOMEM) + return r; + if (r < 0) + /* Ignore the sub-option on non-critical errors. */ + continue; + + lt_min = MIN(lt_min, a->iapdprefix.lifetime_valid); + LIST_PREPEND(addresses, ia.addresses, a); + break; + } + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(subdata, subdata_len, &msg); + if (r == -ENOMEM) + return r; + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); + else if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with non-zero status: %s%s%s", + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } default: - log_dhcp6_client(client, "Unknown IA option %d", opt); - break; + log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt); } - - i += sizeof(*option) + optlen; } - switch(iatype) { + if (!ia.addresses) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), + "Received an IA option without valid IA addresses or PD prefixes, ignoring."); + + if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) && + lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + + log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " + "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " + "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); + } + + switch(option_code) { case SD_DHCP6_OPTION_IA_NA: - if (ia->ia_na.lifetime_t1 == 0 && ia->ia_na.lifetime_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->ia_na.lifetime_t1 = htobe32(lt_t1); - ia->ia_na.lifetime_t2 = htobe32(lt_t2); - - log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", - lt_t1, lt_t2); - } - + *ret = (DHCP6IA) { + .type = option_code, + .ia_na.id = iaid, + .ia_na.lifetime_t1 = htobe32(lt_t1), + .ia_na.lifetime_t2 = htobe32(lt_t2), + .addresses = TAKE_PTR(ia.addresses), + }; + break; + case SD_DHCP6_OPTION_IA_TA: + *ret = (DHCP6IA) { + .type = option_code, + .ia_ta.id = iaid, + .addresses = TAKE_PTR(ia.addresses), + }; break; - case SD_DHCP6_OPTION_IA_PD: - if (ia->ia_pd.lifetime_t1 == 0 && ia->ia_pd.lifetime_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->ia_pd.lifetime_t1 = htobe32(lt_t1); - ia->ia_pd.lifetime_t2 = htobe32(lt_t2); - - log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", - lt_t1, lt_t2); - } - + *ret = (DHCP6IA) { + .type = option_code, + .ia_pd.id = iaid, + .ia_pd.lifetime_t1 = htobe32(lt_t1), + .ia_pd.lifetime_t2 = htobe32(lt_t2), + .addresses = TAKE_PTR(ia.addresses), + }; break; - default: - break; + assert_not_reached(); } - if (ret_status_code) - *ret_status_code = 0; - - return 1; + return 0; } -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count) { +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count) { + + assert(optval); + assert(addrs); + assert(count); if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) - return -EINVAL; + return -EBADMSG; - if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen)) + if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr))) return -ENOMEM; - memcpy(*addrs + count, optval, optlen); + memcpy(*addrs + *count, optval, optlen); + *count += optlen / sizeof(struct in6_addr); - count += optlen / sizeof(struct in6_addr); - - return count; + return 0; } -static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { - _cleanup_free_ char *ret = NULL; - const uint8_t *optval = *data; - uint16_t optlen = *len; - bool first = true; - size_t n = 0; +static int parse_domain(const uint8_t **data, size_t *len, char **ret) { + _cleanup_free_ char *domain = NULL; + const uint8_t *optval; + size_t optlen, n = 0; int r; + assert(data); + assert(*data); + assert(len); + assert(ret); + + optval = *data; + optlen = *len; + if (optlen <= 1) return -ENODATA; @@ -724,42 +780,44 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) return -EMSGSIZE; /* Literal label */ - label = (const char *)optval; + label = (const char*) optval; optval += c; optlen -= c; - if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; - if (first) - first = false; - else - ret[n++] = '.'; + if (n != 0) + domain[n++] = '.'; - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX); if (r < 0) return r; n += r; } - if (n) { - if (!GREEDY_REALLOC(ret, n + 1)) + if (n > 0) { + if (!GREEDY_REALLOC(domain, n + 1)) return -ENOMEM; - ret[n] = 0; + + domain[n] = '\0'; } - *out_domain = TAKE_PTR(ret); + *ret = TAKE_PTR(domain); *data = optval; *len = optlen; return n; } -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) { +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) { _cleanup_free_ char *domain = NULL; int r; + assert(optval); + assert(ret); + r = parse_domain(&optval, &optlen, &domain); if (r < 0) return r; @@ -768,39 +826,38 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (optlen != 0) return -EINVAL; - *str = TAKE_PTR(domain); + *ret = TAKE_PTR(domain); return 0; } -int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) { - size_t idx = 0; +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) { _cleanup_strv_free_ char **names = NULL; int r; + assert(optval); + assert(ret); + if (optlen <= 1) return -ENODATA; if (optval[optlen - 1] != '\0') return -EINVAL; while (optlen > 0) { - _cleanup_free_ char *ret = NULL; + _cleanup_free_ char *name = NULL; - r = parse_domain(&optval, &optlen, &ret); + r = parse_domain(&optval, &optlen, &name); if (r < 0) return r; if (r == 0) continue; - r = strv_extend(&names, ret); + r = strv_consume(&names, TAKE_PTR(name)); if (r < 0) return r; - - idx++; } - *str_arr = TAKE_PTR(names); - - return idx; + *ret = TAKE_PTR(names); + return 0; } static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c index 3bd775158e..48c2142b1e 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c @@ -5,13 +5,15 @@ #include "ether-addr-util.h" #include "hexdecoct.h" #include "in-addr-util.h" -#include "lldp-internal.h" #include "lldp-neighbor.h" #include "memory-util.h" #include "missing_network.h" #include "unaligned.h" static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { + assert(id); + assert(state); + siphash24_compress(id->chassis_id, id->chassis_id_size, state); siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); siphash24_compress(id->port_id, id->port_id_size, state); @@ -19,16 +21,27 @@ static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash } int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) { + assert(x); + assert(y); + return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size) ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size); } -DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops, LLDPNeighborID, lldp_neighbor_id_hash_func, lldp_neighbor_id_compare_func, - sd_lldp_neighbor, lldp_neighbor_unlink); +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + lldp_neighbor_hash_ops, + LLDPNeighborID, + lldp_neighbor_id_hash_func, + lldp_neighbor_id_compare_func, + sd_lldp_neighbor, + lldp_neighbor_unlink); int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { const sd_lldp_neighbor *x = a, *y = b; + assert(x); + assert(y); + return CMP(x->until, y->until); } @@ -36,14 +49,15 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { if (!n) return NULL; - assert(n->n_ref > 0 || n->lldp); + assert(n->n_ref > 0 || n->lldp_rx); n->n_ref++; return n; } -static void lldp_neighbor_free(sd_lldp_neighbor *n) { - assert(n); +static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) { + if (!n) + return NULL; free(n->id.port_id); free(n->id.chassis_id); @@ -53,7 +67,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) { free(n->mud_url); free(n->chassis_id_as_string); free(n->port_id_as_string); - free(n); + return mfree(n); } _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { @@ -67,7 +81,7 @@ _public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { assert(n->n_ref > 0); n->n_ref--; - if (n->n_ref <= 0 && !n->lldp) + if (n->n_ref <= 0 && !n->lldp_rx) lldp_neighbor_free(n); return NULL; @@ -80,18 +94,18 @@ sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { if (!n) return NULL; - if (!n->lldp) + if (!n->lldp_rx) return NULL; /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register * ourselves from the hashtable and sometimes are called after we already are de-registered. */ - (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n); + (void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n); - assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); + assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0); - n->lldp = NULL; + n->lldp_rx = NULL; if (n->n_ref <= 0) lldp_neighbor_free(n); @@ -112,7 +126,7 @@ sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { return n; } -static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { +static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) { const char *p = q; char *k; @@ -120,7 +134,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { assert(p || n == 0); if (*s) { - log_lldp(lldp, "Found duplicate string, ignoring field."); + log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field."); return 0; } @@ -133,7 +147,7 @@ static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) { /* Look for inner NULs */ if (memchr(p, 0, n)) { - log_lldp(lldp, "Found inner NUL in string, ignoring field."); + log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field."); return 0; } @@ -157,14 +171,14 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { assert(n); if (n->raw_size < sizeof(struct ether_header)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received truncated packet, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received truncated packet, ignoring."); memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); if (h.ether_type != htobe16(ETHERTYPE_LLDP)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong type, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received packet with wrong type, ignoring."); if (h.ether_dhost[0] != 0x01 || h.ether_dhost[1] != 0x80 || @@ -172,8 +186,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { h.ether_dhost[3] != 0x00 || h.ether_dhost[4] != 0x00 || !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Received packet with wrong destination address, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Received packet with wrong destination address, ignoring."); memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); @@ -186,7 +200,7 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { uint16_t length; if (left < 2) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), "TLV lacks header, ignoring."); type = p[0] >> 1; @@ -194,15 +208,15 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { p += 2, left -= 2; if (left < length) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "TLV truncated, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "TLV truncated, ignoring datagram."); switch (type) { case SD_LLDP_TYPE_END: if (length != 0) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "End marker TLV not zero-sized, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "End marker TLV not zero-sized, ignoring datagram."); /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ @@ -212,12 +226,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_CHASSIS_ID: if (length < 2 || length > 256) /* includes the chassis subtype, hence one extra byte */ - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Chassis ID field size out of range, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Chassis ID field size out of range, ignoring datagram."); if (n->id.chassis_id) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate chassis ID field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate chassis ID field, ignoring datagram."); n->id.chassis_id = memdup(p, length); if (!n->id.chassis_id) @@ -229,12 +243,12 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_PORT_ID: if (length < 2 || length > 256) /* includes the port subtype, hence one extra byte */ - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Port ID field size out of range, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Port ID field size out of range, ignoring datagram."); if (n->id.port_id) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate port ID field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate port ID field, ignoring datagram."); n->id.port_id = memdup(p, length); if (!n->id.port_id) @@ -245,39 +259,39 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_TTL: if (length != 2) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "TTL field has wrong size, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "TTL field has wrong size, ignoring datagram."); if (n->has_ttl) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Duplicate TTL field, ignoring datagram."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Duplicate TTL field, ignoring datagram."); n->ttl = unaligned_read_be16(p); n->has_ttl = true; break; case SD_LLDP_TYPE_PORT_DESCRIPTION: - r = parse_string(n->lldp, &n->port_description, p, length); + r = parse_string(n->lldp_rx, &n->port_description, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_NAME: - r = parse_string(n->lldp, &n->system_name, p, length); + r = parse_string(n->lldp_rx, &n->system_name, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: - r = parse_string(n->lldp, &n->system_description, p, length); + r = parse_string(n->lldp_rx, &n->system_description, p, length); if (r < 0) return r; break; case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: if (length != 4) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "System capabilities field has wrong size."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "System capabilities field has wrong size."); n->system_capabilities = unaligned_read_be16(p); n->enabled_capabilities = unaligned_read_be16(p + 2); @@ -286,14 +300,13 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { case SD_LLDP_TYPE_PRIVATE: if (length < 4) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "Found private TLV that is too short, ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "Found private TLV that is too short, ignoring."); /* RFC 8520: MUD URL */ - if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 && - p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) { - r = parse_string(n->lldp, &n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1, - length - 1 - sizeof(SD_LLDP_OUI_MUD)); + if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) { + r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD), + length - sizeof(SD_LLDP_OUI_IANA_MUD)); if (r < 0) return r; } @@ -305,8 +318,8 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { end_marker: if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) - return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG), - "One or more mandatory TLV missing in datagram. Ignoring."); + return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG), + "One or more mandatory TLV missing in datagram. Ignoring."); n->rindex = sizeof(struct ether_header); @@ -328,8 +341,8 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { } else n->until = 0; - if (n->lldp) - prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); + if (n->lldp_rx) + prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx); } bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h index a5718c8c31..016286b17d 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h @@ -5,10 +5,10 @@ #include #include -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "hash-funcs.h" -#include "lldp-internal.h" +#include "lldp-rx-internal.h" #include "time-util.h" typedef struct LLDPNeighborID { @@ -21,8 +21,8 @@ typedef struct LLDPNeighborID { } LLDPNeighborID; struct sd_lldp_neighbor { - /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ - sd_lldp *lldp; + /* Neighbor objects stay around as long as they are linked into an "sd_lldp_rx" object or n_ref > 0. */ + sd_lldp_rx *lldp_rx; unsigned n_ref; triple_timestamp timestamp; diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c index 43141b2d79..d11af3ebbb 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c @@ -56,15 +56,18 @@ int lldp_network_bind_raw_socket(int ifindex) { if (r < 0) return -errno; + /* customer bridge */ r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (r < 0) return -errno; + /* non TPMR bridge */ mreq.mr_address[ETH_ALEN - 1] = 0x03; r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (r < 0) return -errno; + /* nearest bridge */ mreq.mr_address[ETH_ALEN - 1] = 0x0E; r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); if (r < 0) diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h similarity index 56% rename from src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h rename to src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h index cf0578c5c2..83d0bc460d 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-rx-internal.h @@ -2,13 +2,13 @@ #pragma once #include "sd-event.h" -#include "sd-lldp.h" +#include "sd-lldp-rx.h" #include "hashmap.h" -#include "log-link.h" +#include "network-common.h" #include "prioq.h" -struct sd_lldp { +struct sd_lldp_rx { unsigned n_ref; int ifindex; @@ -25,7 +25,7 @@ struct sd_lldp { uint64_t neighbors_max; - sd_lldp_callback_t callback; + sd_lldp_rx_callback_t callback; void *userdata; uint16_t capability_mask; @@ -33,16 +33,16 @@ struct sd_lldp { struct ether_addr filter_address; }; -const char* lldp_event_to_string(sd_lldp_event_t e) _const_; -sd_lldp_event_t lldp_event_from_string(const char *s) _pure_; +const char* lldp_rx_event_to_string(sd_lldp_rx_event_t e) _const_; +sd_lldp_rx_event_t lldp_rx_event_from_string(const char *s) _pure_; -#define log_lldp_errno(lldp, error, fmt, ...) \ +#define log_lldp_rx_errno(lldp_rx, error, fmt, ...) \ log_interface_prefix_full_errno( \ - "LLDP: ", \ - sd_lldp_get_ifname(lldp), \ + "LLDP Rx: ", \ + sd_lldp_rx, lldp_rx, \ error, fmt, ##__VA_ARGS__) -#define log_lldp(lldp, fmt, ...) \ +#define log_lldp_rx(lldp_rx, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ - "LLDP: ", \ - sd_lldp_get_ifname(lldp), \ + "LLDP Rx: ", \ + sd_lldp_rx, lldp_rx, \ 0, fmt, ##__VA_ARGS__) diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.c b/src/libnm-systemd-core/src/libsystemd-network/network-common.c index 9bc0da96cd..b8b4ecdaae 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-common.c +++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.c @@ -2,23 +2,14 @@ #include "format-util.h" #include "network-common.h" -#include "string-util.h" - -const char *get_ifname(int ifindex, char **ifname) { - char buf[IF_NAMESIZE + 1]; +int get_ifname(int ifindex, char **ifname) { assert(ifname); /* This sets ifname only when it is not set yet. */ if (*ifname) - return *ifname; + return 0; - if (ifindex <= 0) - return NULL; - - if (!format_ifname(ifindex, buf)) - return NULL; - - return *ifname = strdup(buf); + return format_ifname_alloc(ifindex, ifname); } diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.h b/src/libnm-systemd-core/src/libsystemd-network/network-common.h index 76a6c4a989..d43b76d4b8 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-common.h +++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.h @@ -1,4 +1,28 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -const char *get_ifname(int ifindex, char **ifname); +#include "log-link.h" + +#define log_interface_prefix_full_errno_zerook(prefix, type, val, error, fmt, ...) \ + ({ \ + int _e = (error); \ + if (DEBUG_LOGGING) { \ + const char *_n = NULL; \ + \ + (void) type##_get_ifname(val, &_n); \ + log_interface_full_errno_zerook( \ + _n, LOG_DEBUG, _e, prefix fmt, \ + ##__VA_ARGS__); \ + } \ + -ERRNO_VALUE(_e); \ + }) + +#define log_interface_prefix_full_errno(prefix, type, val, error, fmt, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_interface_prefix_full_errno_zerook( \ + prefix, type, val, _error, fmt, ##__VA_ARGS__); \ + }) + +int get_ifname(int ifindex, char **ifname); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c index 7fe660466f..45c2351c53 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c @@ -192,7 +192,7 @@ int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) { if (len != sizeof_field(sd_dhcp_client_id, eth)) return -EINVAL; - r = asprintf(&t, "%x:%x:%x:%x:%x:%x", + r = asprintf(&t, "%02x:%02x:%02x:%02x:%02x:%02x", client_id->eth.haddr[0], client_id->eth.haddr[1], client_id->eth.haddr[2], @@ -297,11 +297,19 @@ int sd_dhcp_client_set_ifname(sd_dhcp_client *client, const char *ifname) { return free_and_strdup(&client->ifname, ifname); } -const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client) { - if (!client) - return NULL; +int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret) { + int r; - return get_ifname(client->ifindex, &client->ifname); + assert_return(client, -EINVAL); + + r = get_ifname(client->ifindex, &client->ifname); + if (r < 0) + return r; + + if (ret) + *ret = client->ifname; + + return 0; } int sd_dhcp_client_set_mac( @@ -726,7 +734,7 @@ static int client_notify(sd_dhcp_client *client, int event) { static int client_initialize(sd_dhcp_client *client) { assert_return(client, -EINVAL); - client->receive_message = sd_event_source_unref(client->receive_message); + client->receive_message = sd_event_source_disable_unref(client->receive_message); client->fd = safe_close(client->fd); @@ -1308,7 +1316,7 @@ static int client_timeout_resend( goto error; default: - assert_not_reached("Unhandled choice"); + assert_not_reached(); } r = event_reset_time(client->event, &client->timeout_resend, @@ -1492,7 +1500,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) assert(client); - client->receive_message = sd_event_source_unref(client->receive_message); + client->receive_message = sd_event_source_disable_unref(client->receive_message); client->fd = safe_close(client->fd); client->state = DHCP_STATE_REBINDING; @@ -1844,7 +1852,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->start_delay = 0; (void) event_source_disable(client->timeout_resend); - client->receive_message = sd_event_source_unref(client->receive_message); + client->receive_message = sd_event_source_disable_unref(client->receive_message); client->fd = safe_close(client->fd); client->state = DHCP_STATE_BOUND; @@ -1896,7 +1904,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i r = -EINVAL; goto error; default: - assert_not_reached("invalid state"); + assert_not_reached(); } error: @@ -2226,17 +2234,15 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) { log_dhcp_client(client, "FREE"); + client_initialize(client); + client->timeout_resend = sd_event_source_unref(client->timeout_resend); client->timeout_t1 = sd_event_source_unref(client->timeout_t1); client->timeout_t2 = sd_event_source_unref(client->timeout_t2); client->timeout_expire = sd_event_source_unref(client->timeout_expire); - client_initialize(client); - sd_dhcp_client_detach_event(client); - sd_dhcp_lease_unref(client->lease); - set_free(client->req_opts); free(client->hostname); free(client->vendor_class_identifier); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c index 079f154acd..87dd8c84d1 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c @@ -179,11 +179,19 @@ int sd_dhcp6_client_set_ifname(sd_dhcp6_client *client, const char *ifname) { return free_and_strdup(&client->ifname, ifname); } -const char *sd_dhcp6_client_get_ifname(sd_dhcp6_client *client) { - if (!client) - return NULL; +int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret) { + int r; - return get_ifname(client->ifindex, &client->ifname); + assert_return(client, -EINVAL); + + r = get_ifname(client->ifindex, &client->ifname); + if (r < 0) + return r; + + if (ret) + *ret = client->ifname; + + return 0; } int sd_dhcp6_client_set_local_address( @@ -1101,11 +1109,9 @@ static int client_parse_message( size_t len, sd_dhcp6_lease *lease) { - uint16_t ia_na_status = 0, ia_pd_status = 0; - uint32_t lt_t1 = ~0, lt_t2 = ~0; + uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; usec_t irt = IRT_DEFAULT; bool clientid = false; - size_t pos = 0; int r; assert(client); @@ -1114,50 +1120,35 @@ static int client_parse_message( assert(lease); len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; - while (pos < len) { - DHCP6Option *option = (DHCP6Option *) &message->options[pos]; - uint16_t optcode, optlen; - be32_t iaid_lease; - int status; - uint8_t *optval; - - if (len < pos + offsetof(DHCP6Option, data)) - return -ENOBUFS; - - optcode = be16toh(option->code); - optlen = be16toh(option->len); - optval = option->data; - - if (len < pos + offsetof(DHCP6Option, data) + optlen) - return -ENOBUFS; + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: - if (clientid) { - log_dhcp6_client(client, "%s contains multiple clientids", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } + if (clientid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids", + dhcp6_message_type_to_string(message->type)); if (optlen != client->duid_len || - memcmp(&client->duid, optval, optlen) != 0) { - log_dhcp6_client(client, "%s DUID does not match", - dhcp6_message_type_to_string(message->type)); + memcmp(&client->duid, optval, optlen) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match", + dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } clientid = true; break; case SD_DHCP6_OPTION_SERVERID: r = dhcp6_lease_get_serverid(lease, NULL, NULL); - if (r >= 0) { - log_dhcp6_client(client, "%s contains multiple serverids", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } + if (r >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids", + dhcp6_message_type_to_string(message->type)); r = dhcp6_lease_set_serverid(lease, optval, optlen); if (r < 0) @@ -1175,87 +1166,75 @@ static int client_parse_message( break; - case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); - if (status < 0) - return status; + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; - if (status > 0) { - log_dhcp6_client(client, "%s Status %s", - dhcp6_message_type_to_string(message->type), - dhcp6_message_status_to_string(status)); - - return -EINVAL; - } - - break; - - case SD_DHCP6_OPTION_IA_NA: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Information request ignoring IA NA option"); - - break; - } - - r = dhcp6_option_parse_ia(client, option, &lease->ia, &ia_na_status); - if (r < 0 && r != -ENOMSG) - return r; - - if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; - continue; - } - - r = dhcp6_lease_get_iaid(lease, &iaid_lease); + r = dhcp6_option_parse_status(optval, optlen, &msg); if (r < 0) return r; - if (client->ia_na.ia_na.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID for IA NA", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; + if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received %s message with non-zero status: %s%s%s", + dhcp6_message_type_to_string(message->type), + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + case SD_DHCP6_OPTION_IA_NA: { + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); + break; } + r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + if (lease->ia.addresses) { - lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1)); - } - - break; - - case SD_DHCP6_OPTION_IA_PD: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Information request ignoring IA PD option"); - - break; - } - - r = dhcp6_option_parse_ia(client, option, &lease->pd, &ia_pd_status); - if (r < 0 && r != -ENOMSG) - return r; - - if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); continue; } - r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease); - if (r < 0) - return r; + lease->ia = ia; + ia = (DHCP6IA) {}; - if (client->ia_pd.ia_pd.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID for IA PD", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } - - if (lease->pd.addresses) { - lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); - } + lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->pd.addresses) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; + } + + lease->pd = ia; + ia = (DHCP6IA) {}; + + lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); + + break; + } case SD_DHCP6_OPTION_RAPID_COMMIT: r = dhcp6_lease_set_rapid_commit(lease); if (r < 0) @@ -1264,28 +1243,28 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_set_dns(lease, optval, optlen); + r = dhcp6_lease_add_dns(lease, optval, optlen); if (r < 0) return r; break; case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_set_domains(lease, optval, optlen); + r = dhcp6_lease_add_domains(lease, optval, optlen); if (r < 0) return r; break; case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_set_ntp(lease, optval, optlen); + r = dhcp6_lease_add_ntp(lease, optval, optlen); if (r < 0) return r; break; case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_set_sntp(lease, optval, optlen); + r = dhcp6_lease_add_sntp(lease, optval, optlen); if (r < 0) return r; @@ -1305,30 +1284,21 @@ static int client_parse_message( irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; break; } - - pos += offsetof(DHCP6Option, data) + optlen; } - if (ia_na_status > 0 && ia_pd_status > 0) { - log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring."); - return -EINVAL; - } - - if (!clientid) { - log_dhcp6_client(client, "%s has incomplete options", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } + if (!clientid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options", + dhcp6_message_type_to_string(message->type)); if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { r = dhcp6_lease_get_serverid(lease, NULL, NULL); - if (r < 0) { - log_dhcp6_client(client, "%s has no server id", - dhcp6_message_type_to_string(message->type)); - return -EINVAL; - } + if (r < 0) + return log_dhcp6_client_errno(client, r, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + + if (!lease->ia.addresses && !lease->pd.addresses) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - } else { if (lease->ia.addresses) { lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); @@ -1479,15 +1449,16 @@ static int client_receive_message( return 0; } - if (client->transaction_id != (message->transaction_id & - htobe32(0x00ffffff))) + if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: r = client_receive_reply(client, message, len); - if (r < 0) + if (r < 0) { + log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); return 0; + } client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); @@ -1497,10 +1468,13 @@ static int client_receive_message( case DHCP6_STATE_SOLICITATION: r = client_receive_advertise(client, message, len); + if (r < 0) { + log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); + return 0; + } if (r == DHCP6_STATE_REQUEST) { client_start(client, r); - break; } @@ -1510,11 +1484,12 @@ static int client_receive_message( case DHCP6_STATE_REBIND: r = client_receive_reply(client, message, len); - if (r < 0) + if (r < 0) { + log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); return 0; + } if (r == DHCP6_STATE_BOUND) { - r = client_start(client, DHCP6_STATE_BOUND); if (r < 0) { client_stop(client, r); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c index 9082185bca..c55b06d2f7 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c @@ -193,86 +193,69 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { lease->prefix_iter = lease->pd.addresses; } -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - +int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, - lease->dns_count); - if (r < 0) - return r; - - lease->dns_count = r; - - return 0; + return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count); } -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->dns_count) { - *addrs = lease->dns; - return lease->dns_count; - } + if (!lease->dns) + return -ENOENT; - return -ENOENT; + *ret = lease->dns; + return lease->dns_count; } -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) { +int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { + _cleanup_strv_free_ char **domains = NULL; int r; - char **domains; assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; r = dhcp6_option_parse_domainname_list(optval, optlen, &domains); if (r < 0) - return 0; + return r; - strv_free_and_replace(lease->domains, domains); - lease->domains_count = r; - - return r; + return strv_extend_strv(&lease->domains, domains, true); } -int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); - assert_return(domains, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->domains_count) { - *domains = lease->domains; - return lease->domains_count; - } + if (!lease->domains) + return -ENOENT; - return -ENOENT; + *ret = lease->domains; + return strv_length(lease->domains); } -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { +int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; - uint16_t subopt; - size_t sublen; - uint8_t *subval; assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - lease->ntp = mfree(lease->ntp); - lease->ntp_count = 0; + for (size_t offset = 0; offset < optlen;) { + const uint8_t *subval; + size_t sublen; + uint16_t subopt; - while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, - &subval)) >= 0) { - int s; - char **servers; + r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval); + if (r < 0) + return r; switch(subopt) { case DHCP6_NTP_SUBOPTION_SRV_ADDR: @@ -280,86 +263,74 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { if (sublen != 16) return 0; - s = dhcp6_option_parse_ip6addrs(subval, sublen, - &lease->ntp, - lease->ntp_count); - if (s < 0) - return s; - - lease->ntp_count = s; - - break; - - case DHCP6_NTP_SUBOPTION_SRV_FQDN: - r = dhcp6_option_parse_domainname_list(subval, sublen, - &servers); + r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) - return 0; - - strv_free_and_replace(lease->ntp_fqdn, servers); - lease->ntp_fqdn_count = r; + return r; break; - } - } - if (r != -ENOMSG) - return r; + case DHCP6_NTP_SUBOPTION_SRV_FQDN: { + _cleanup_free_ char *server = NULL; + + r = dhcp6_option_parse_domainname(subval, sublen, &server); + if (r < 0) + return r; + + if (strv_contains(lease->ntp_fqdn, server)) + continue; + + r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server)); + if (r < 0) + return r; + + break; + }} + } return 0; } -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - +int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; - if (lease->ntp || lease->ntp_fqdn) - return -EEXIST; - - /* Using deprecated SNTP information */ - - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, - lease->ntp_count); - if (r < 0) - return r; - - lease->ntp_count = r; - - return 0; + /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */ + return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count); } -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - const struct in6_addr **addrs) { +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->ntp_count) { - *addrs = lease->ntp; + if (lease->ntp) { + *ret = lease->ntp; return lease->ntp_count; } - return -ENOENT; -} - -int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { - assert_return(lease, -EINVAL); - assert_return(ntp_fqdn, -EINVAL); - - if (lease->ntp_fqdn_count) { - *ntp_fqdn = lease->ntp_fqdn; - return lease->ntp_fqdn_count; + if (lease->sntp && !lease->ntp_fqdn) { + /* Fallback to the deprecated SNTP option. */ + *ret = lease->sntp; + return lease->sntp_count; } return -ENOENT; } -int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, - size_t optlen) { +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { + assert_return(lease, -EINVAL); + assert_return(ret, -EINVAL); + + if (!lease->ntp_fqdn) + return -ENOENT; + + *ret = lease->ntp_fqdn; + return strv_length(lease->ntp_fqdn); +} + +int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; char *fqdn; @@ -378,33 +349,31 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, return free_and_replace(lease->fqdn, fqdn); } -int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) { +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(lease, -EINVAL); - assert_return(fqdn, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->fqdn) { - *fqdn = lease->fqdn; - return 0; - } + if (!lease->fqdn) + return -ENOENT; - return -ENOENT; + *ret = lease->fqdn; + return 0; } static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { - assert(lease); + if (!lease) + return NULL; free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); dhcp6_lease_free_ia(&lease->pd); - free(lease->dns); free(lease->fqdn); - - lease->domains = strv_free(lease->domains); - + strv_free(lease->domains); free(lease->ntp); + strv_free(lease->ntp_fqdn); + free(lease->sntp); - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); return mfree(lease); } diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c index c6aa9aa11a..0a975fbbad 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c @@ -17,7 +17,6 @@ #include "event-util.h" #include "fd-util.h" #include "in-addr-util.h" -#include "log-link.h" #include "memory-util.h" #include "network-common.h" #include "random-util.h" @@ -81,12 +80,12 @@ struct sd_ipv4acd { #define log_ipv4acd_errno(acd, error, fmt, ...) \ log_interface_prefix_full_errno( \ "IPv4ACD: ", \ - sd_ipv4acd_get_ifname(acd), \ + sd_ipv4acd, acd, \ error, fmt, ##__VA_ARGS__) #define log_ipv4acd(acd, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ "IPv4ACD: ", \ - sd_ipv4acd_get_ifname(acd), \ + sd_ipv4acd, acd, \ 0, fmt, ##__VA_ARGS__) static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = { @@ -120,7 +119,7 @@ static void ipv4acd_reset(sd_ipv4acd *acd) { assert(acd); (void) event_source_disable(acd->timer_event_source); - acd->receive_message_event_source = sd_event_source_unref(acd->receive_message_event_source); + acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source); acd->fd = safe_close(acd->fd); @@ -130,9 +129,8 @@ static void ipv4acd_reset(sd_ipv4acd *acd) { static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) { assert(acd); - acd->timer_event_source = sd_event_source_unref(acd->timer_event_source); - ipv4acd_reset(acd); + sd_event_source_unref(acd->timer_event_source); sd_ipv4acd_detach_event(acd); free(acd->ifname); return mfree(acd); @@ -293,7 +291,7 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) break; default: - assert_not_reached("Invalid state."); + assert_not_reached(); } return 0; @@ -409,7 +407,7 @@ static int ipv4acd_on_packet( break; default: - assert_not_reached("Invalid state."); + assert_not_reached(); } return 0; @@ -446,11 +444,19 @@ int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) { return free_and_strdup(&acd->ifname, ifname); } -const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd) { - if (!acd) - return NULL; +int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) { + int r; - return get_ifname(acd->ifindex, &acd->ifname); + assert_return(acd, -EINVAL); + + r = get_ifname(acd->ifindex, &acd->ifname); + if (r < 0) + return r; + + if (ret) + *ret = acd->ifname; + + return 0; } int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) { diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c index 7b9d63ca3c..ff065fd1b6 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c @@ -15,7 +15,7 @@ #include "alloc-util.h" #include "ether-addr-util.h" #include "in-addr-util.h" -#include "log-link.h" +#include "network-common.h" #include "random-util.h" #include "siphash24.h" #include "sparse-endian.h" @@ -55,12 +55,12 @@ struct sd_ipv4ll { #define log_ipv4ll_errno(ll, error, fmt, ...) \ log_interface_prefix_full_errno( \ "IPv4LL: ", \ - sd_ipv4ll_get_ifname(ll), \ + sd_ipv4ll, ll, \ error, fmt, ##__VA_ARGS__) #define log_ipv4ll(ll, fmt, ...) \ log_interface_prefix_full_errno_zerook( \ "IPv4LL: ", \ - sd_ipv4ll_get_ifname(ll), \ + sd_ipv4ll, ll, \ 0, fmt, ##__VA_ARGS__) static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata); @@ -133,11 +133,10 @@ int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) { return sd_ipv4acd_set_ifname(ll->acd, ifname); } -const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll) { - if (!ll) - return NULL; +int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret) { + assert_return(ll, -EINVAL); - return sd_ipv4acd_get_ifname(ll->acd); + return sd_ipv4acd_get_ifname(ll->acd, ret); } int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) { @@ -360,7 +359,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { break; default: - assert_not_reached("Invalid IPv4ACD event."); + assert_not_reached(); } return; diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c new file mode 100644 index 0000000000..7224d3a345 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c @@ -0,0 +1,523 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "sd-lldp-rx.h" + +#include "alloc-util.h" +#include "ether-addr-util.h" +#include "event-util.h" +#include "fd-util.h" +#include "lldp-neighbor.h" +#include "lldp-network.h" +#include "lldp-rx-internal.h" +#include "memory-util.h" +#include "network-common.h" +#include "socket-util.h" +#include "sort-util.h" +#include "string-table.h" + +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U + +static const char * const lldp_rx_event_table[_SD_LLDP_RX_EVENT_MAX] = { + [SD_LLDP_RX_EVENT_ADDED] = "added", + [SD_LLDP_RX_EVENT_REMOVED] = "removed", + [SD_LLDP_RX_EVENT_UPDATED] = "updated", + [SD_LLDP_RX_EVENT_REFRESHED] = "refreshed", +}; + +DEFINE_STRING_TABLE_LOOKUP(lldp_rx_event, sd_lldp_rx_event_t); + +static void lldp_rx_flush_neighbors(sd_lldp_rx *lldp_rx) { + assert(lldp_rx); + + hashmap_clear(lldp_rx->neighbor_by_id); +} + +static void lldp_rx_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n) { + assert(lldp_rx); + assert(event >= 0 && event < _SD_LLDP_RX_EVENT_MAX); + + if (!lldp_rx->callback) + return (void) log_lldp_rx(lldp_rx, "Received '%s' event.", lldp_rx_event_to_string(event)); + + log_lldp_rx(lldp_rx, "Invoking callback for '%s' event.", lldp_rx_event_to_string(event)); + lldp_rx->callback(lldp_rx, event, n, lldp_rx->userdata); +} + +static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { + usec_t t = USEC_INFINITY; + bool changed = false; + + assert(lldp_rx); + + /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries + * are free. */ + + for (;;) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + + n = prioq_peek(lldp_rx->neighbor_by_expiry); + if (!n) + break; + + sd_lldp_neighbor_ref(n); + + if (hashmap_size(lldp_rx->neighbor_by_id) > LESS_BY(lldp_rx->neighbors_max, extra)) + goto remove_one; + + if (t == USEC_INFINITY) + t = now(clock_boottime_or_monotonic()); + + if (n->until > t) + break; + + remove_one: + lldp_neighbor_unlink(n); + lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, n); + changed = true; + } + + return changed; +} + +static bool lldp_rx_keep_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + assert(lldp_rx); + assert(n); + + /* Don't keep data with a zero TTL */ + if (n->ttl <= 0) + return false; + + /* Filter out data from the filter address */ + if (!ether_addr_is_null(&lldp_rx->filter_address) && + ether_addr_equal(&lldp_rx->filter_address, &n->source_address)) + return false; + + /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with + * no caps field set. */ + if (n->has_capabilities && + (n->enabled_capabilities & lldp_rx->capability_mask) == 0) + return false; + + /* Keep everything else */ + return true; +} + +static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor); + +static int lldp_rx_add_neighbor(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; + bool keep; + int r; + + assert(lldp_rx); + assert(n); + assert(!n->lldp_rx); + + keep = lldp_rx_keep_neighbor(lldp_rx, n); + + /* First retrieve the old entry for this MSAP */ + old = hashmap_get(lldp_rx->neighbor_by_id, &n->id); + if (old) { + sd_lldp_neighbor_ref(old); + + if (!keep) { + lldp_neighbor_unlink(old); + lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old); + return 0; + } + + if (lldp_neighbor_equal(n, old)) { + /* Is this equal, then restart the TTL counter, but don't do anything else. */ + old->timestamp = n->timestamp; + lldp_rx_start_timer(lldp_rx, old); + lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REFRESHED, old); + return 0; + } + + /* Data changed, remove the old entry, and add a new one */ + lldp_neighbor_unlink(old); + + } else if (!keep) + return 0; + + /* Then, make room for at least one new neighbor */ + lldp_rx_make_space(lldp_rx, 1); + + r = hashmap_ensure_put(&lldp_rx->neighbor_by_id, &lldp_neighbor_hash_ops, &n->id, n); + if (r < 0) + goto finish; + + r = prioq_ensure_put(&lldp_rx->neighbor_by_expiry, lldp_neighbor_prioq_compare_func, n, &n->prioq_idx); + if (r < 0) { + assert_se(hashmap_remove(lldp_rx->neighbor_by_id, &n->id) == n); + goto finish; + } + + n->lldp_rx = lldp_rx; + + lldp_rx_start_timer(lldp_rx, n); + lldp_rx_callback(lldp_rx, old ? SD_LLDP_RX_EVENT_UPDATED : SD_LLDP_RX_EVENT_ADDED, n); + + return 1; + +finish: + if (old) + lldp_rx_callback(lldp_rx, SD_LLDP_RX_EVENT_REMOVED, old); + + return r; +} + +static int lldp_rx_handle_datagram(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *n) { + int r; + + assert(lldp_rx); + assert(n); + + r = lldp_neighbor_parse(n); + if (r < 0) + return r; + + r = lldp_rx_add_neighbor(lldp_rx, n); + if (r < 0) + return log_lldp_rx_errno(lldp_rx, r, "Failed to add datagram. Ignoring."); + + log_lldp_rx(lldp_rx, "Successfully processed LLDP datagram."); + return 0; +} + +static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + ssize_t space, length; + sd_lldp_rx *lldp_rx = userdata; + struct timespec ts; + + assert(fd >= 0); + assert(lldp_rx); + + space = next_datagram_size_fd(fd); + if (space < 0) { + log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m"); + return 0; + } + + n = lldp_neighbor_new(space); + if (!n) { + log_oom_debug(); + return 0; + } + + length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); + if (length < 0) { + if (IN_SET(errno, EAGAIN, EINTR)) + return 0; + + log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m"); + return 0; + } + + if ((size_t) length != n->raw_size) { + log_lldp_rx(lldp_rx, "Packet size mismatch, ignoring"); + return 0; + } + + /* Try to get the timestamp of this packet if it is known */ + if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) + triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); + else + triple_timestamp_get(&n->timestamp); + + (void) lldp_rx_handle_datagram(lldp_rx, n); + return 0; +} + +static void lldp_rx_reset(sd_lldp_rx *lldp_rx) { + assert(lldp_rx); + + (void) event_source_disable(lldp_rx->timer_event_source); + lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source); + lldp_rx->fd = safe_close(lldp_rx->fd); +} + +int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) { + if (!lldp_rx) + return false; + + return lldp_rx->fd >= 0; +} + +_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) { + int r; + + assert_return(lldp_rx, -EINVAL); + assert_return(lldp_rx->event, -EINVAL); + assert_return(lldp_rx->ifindex > 0, -EINVAL); + + if (sd_lldp_rx_is_running(lldp_rx)) + return 0; + + assert(!lldp_rx->io_event_source); + + lldp_rx->fd = lldp_network_bind_raw_socket(lldp_rx->ifindex); + if (lldp_rx->fd < 0) + return lldp_rx->fd; + + r = sd_event_add_io(lldp_rx->event, &lldp_rx->io_event_source, lldp_rx->fd, EPOLLIN, lldp_rx_receive_datagram, lldp_rx); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(lldp_rx->io_event_source, lldp_rx->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(lldp_rx->io_event_source, "lldp-rx-io"); + + log_lldp_rx(lldp_rx, "Started LLDP client"); + return 1; + +fail: + lldp_rx_reset(lldp_rx); + return r; +} + +_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) { + if (!sd_lldp_rx_is_running(lldp_rx)) + return 0; + + log_lldp_rx(lldp_rx, "Stopping LLDP client"); + + lldp_rx_reset(lldp_rx); + lldp_rx_flush_neighbors(lldp_rx); + + return 1; +} + +_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) { + int r; + + assert_return(lldp_rx, -EINVAL); + assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); + assert_return(!lldp_rx->event, -EBUSY); + + if (event) + lldp_rx->event = sd_event_ref(event); + else { + r = sd_event_default(&lldp_rx->event); + if (r < 0) + return r; + } + + lldp_rx->event_priority = priority; + + return 0; +} + +_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) { + assert_return(lldp_rx, -EINVAL); + assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); + + lldp_rx->io_event_source = sd_event_source_disable_unref(lldp_rx->io_event_source); + lldp_rx->timer_event_source = sd_event_source_disable_unref(lldp_rx->timer_event_source); + lldp_rx->event = sd_event_unref(lldp_rx->event); + return 0; +} + +_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) { + assert_return(lldp_rx, NULL); + + return lldp_rx->event; +} + +_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) { + assert_return(lldp_rx, -EINVAL); + + lldp_rx->callback = cb; + lldp_rx->userdata = userdata; + + return 0; +} + +_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) { + assert_return(lldp_rx, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY); + + lldp_rx->ifindex = ifindex; + return 0; +} + +int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname) { + assert_return(lldp_rx, -EINVAL); + assert_return(ifname, -EINVAL); + + if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) + return -EINVAL; + + return free_and_strdup(&lldp_rx->ifname, ifname); +} + +int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret) { + int r; + + assert_return(lldp_rx, -EINVAL); + + r = get_ifname(lldp_rx->ifindex, &lldp_rx->ifname); + if (r < 0) + return r; + + if (ret) + *ret = lldp_rx->ifname; + + return 0; +} + +static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) { + if (!lldp_rx) + return NULL; + + lldp_rx_reset(lldp_rx); + + sd_lldp_rx_detach_event(lldp_rx); + + lldp_rx_flush_neighbors(lldp_rx); + + hashmap_free(lldp_rx->neighbor_by_id); + prioq_free(lldp_rx->neighbor_by_expiry); + free(lldp_rx->ifname); + return mfree(lldp_rx); +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free); + +_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) { + _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL; + + assert_return(ret, -EINVAL); + + lldp_rx = new(sd_lldp_rx, 1); + if (!lldp_rx) + return -ENOMEM; + + *lldp_rx = (sd_lldp_rx) { + .n_ref = 1, + .fd = -1, + .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, + .capability_mask = UINT16_MAX, + }; + + *ret = TAKE_PTR(lldp_rx); + return 0; +} + +static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { + sd_lldp_rx *lldp_rx = userdata; + int r; + + r = lldp_rx_make_space(lldp_rx, 0); + if (r < 0) { + log_lldp_rx_errno(lldp_rx, r, "Failed to make space, ignoring: %m"); + return 0; + } + + r = lldp_rx_start_timer(lldp_rx, NULL); + if (r < 0) { + log_lldp_rx_errno(lldp_rx, r, "Failed to restart timer, ignoring: %m"); + return 0; + } + + return 0; +} + +static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) { + sd_lldp_neighbor *n; + + assert(lldp_rx); + assert(lldp_rx->event); + + if (neighbor) + lldp_neighbor_start_ttl(neighbor); + + n = prioq_peek(lldp_rx->neighbor_by_expiry); + if (!n) + return event_source_disable(lldp_rx->timer_event_source); + + return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source, + clock_boottime_or_monotonic(), + n->until, 0, + on_timer_event, lldp_rx, + lldp_rx->event_priority, "lldp-rx-timer", true); +} + +static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { + assert(a); + assert(b); + assert(*a); + assert(*b); + + return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); +} + +_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) { + _cleanup_free_ sd_lldp_neighbor **l = NULL; + sd_lldp_neighbor *n; + int k = 0; + + assert_return(lldp_rx, -EINVAL); + assert_return(ret, -EINVAL); + + if (hashmap_isempty(lldp_rx->neighbor_by_id)) { /* Special shortcut */ + *ret = NULL; + return 0; + } + + l = new0(sd_lldp_neighbor*, hashmap_size(lldp_rx->neighbor_by_id)); + if (!l) + return -ENOMEM; + + HASHMAP_FOREACH(n, lldp_rx->neighbor_by_id) + l[k++] = sd_lldp_neighbor_ref(n); + + assert((size_t) k == hashmap_size(lldp_rx->neighbor_by_id)); + + /* Return things in a stable order */ + typesafe_qsort(l, k, neighbor_compare_func); + *ret = TAKE_PTR(l); + + return k; +} + +_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) { + assert_return(lldp_rx, -EINVAL); + assert_return(m > 0, -EINVAL); + + lldp_rx->neighbors_max = m; + lldp_rx_make_space(lldp_rx, 0); + + return 0; +} + +_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) { + assert_return(lldp_rx, -EINVAL); + assert_return(mask != 0, -EINVAL); + + lldp_rx->capability_mask = mask; + + return 0; +} + +_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) { + assert_return(lldp_rx, -EINVAL); + + /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so + * that our own can be filtered out here. */ + + if (addr) + lldp_rx->filter_address = *addr; + else + zero(lldp_rx->filter_address); + + return 0; +} diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp.c b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp.c deleted file mode 100644 index 49aa876a53..0000000000 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp.c +++ /dev/null @@ -1,522 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include -#include -#include - -#include "sd-lldp.h" - -#include "alloc-util.h" -#include "ether-addr-util.h" -#include "event-util.h" -#include "fd-util.h" -#include "lldp-internal.h" -#include "lldp-neighbor.h" -#include "lldp-network.h" -#include "memory-util.h" -#include "network-common.h" -#include "socket-util.h" -#include "sort-util.h" -#include "string-table.h" - -#define LLDP_DEFAULT_NEIGHBORS_MAX 128U - -static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = { - [SD_LLDP_EVENT_ADDED] = "added", - [SD_LLDP_EVENT_REMOVED] = "removed", - [SD_LLDP_EVENT_UPDATED] = "updated", - [SD_LLDP_EVENT_REFRESHED] = "refreshed", -}; - -DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event_t); - -static void lldp_flush_neighbors(sd_lldp *lldp) { - assert(lldp); - - hashmap_clear(lldp->neighbor_by_id); -} - -static void lldp_callback(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n) { - assert(lldp); - assert(event >= 0 && event < _SD_LLDP_EVENT_MAX); - - if (!lldp->callback) - return (void) log_lldp(lldp, "Received '%s' event.", lldp_event_to_string(event)); - - log_lldp(lldp, "Invoking callback for '%s' event.", lldp_event_to_string(event)); - lldp->callback(lldp, event, n, lldp->userdata); -} - -static int lldp_make_space(sd_lldp *lldp, size_t extra) { - usec_t t = USEC_INFINITY; - bool changed = false; - - assert(lldp); - - /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries - * are free. */ - - for (;;) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - - n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) - break; - - sd_lldp_neighbor_ref(n); - - if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) - goto remove_one; - - if (t == USEC_INFINITY) - t = now(clock_boottime_or_monotonic()); - - if (n->until > t) - break; - - remove_one: - lldp_neighbor_unlink(n); - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); - changed = true; - } - - return changed; -} - -static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { - assert(lldp); - assert(n); - - /* Don't keep data with a zero TTL */ - if (n->ttl <= 0) - return false; - - /* Filter out data from the filter address */ - if (!ether_addr_is_null(&lldp->filter_address) && - ether_addr_equal(&lldp->filter_address, &n->source_address)) - return false; - - /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with - * no caps field set. */ - if (n->has_capabilities && - (n->enabled_capabilities & lldp->capability_mask) == 0) - return false; - - /* Keep everything else */ - return true; -} - -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); - -static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; - bool keep; - int r; - - assert(lldp); - assert(n); - assert(!n->lldp); - - keep = lldp_keep_neighbor(lldp, n); - - /* First retrieve the old entry for this MSAP */ - old = hashmap_get(lldp->neighbor_by_id, &n->id); - if (old) { - sd_lldp_neighbor_ref(old); - - if (!keep) { - lldp_neighbor_unlink(old); - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); - return 0; - } - - if (lldp_neighbor_equal(n, old)) { - /* Is this equal, then restart the TTL counter, but don't do anything else. */ - old->timestamp = n->timestamp; - lldp_start_timer(lldp, old); - lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); - return 0; - } - - /* Data changed, remove the old entry, and add a new one */ - lldp_neighbor_unlink(old); - - } else if (!keep) - return 0; - - /* Then, make room for at least one new neighbor */ - lldp_make_space(lldp, 1); - - r = hashmap_put(lldp->neighbor_by_id, &n->id, n); - if (r < 0) - goto finish; - - r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); - if (r < 0) { - assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); - goto finish; - } - - n->lldp = lldp; - - lldp_start_timer(lldp, n); - lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); - - return 1; - -finish: - if (old) - lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); - - return r; -} - -static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { - int r; - - assert(lldp); - assert(n); - - r = lldp_neighbor_parse(n); - if (r == -EBADMSG) /* Ignore bad messages */ - return 0; - if (r < 0) - return r; - - r = lldp_add_neighbor(lldp, n); - if (r < 0) { - log_lldp_errno(lldp, r, "Failed to add datagram. Ignoring."); - return 0; - } - - log_lldp(lldp, "Successfully processed LLDP datagram."); - return 0; -} - -static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - ssize_t space, length; - sd_lldp *lldp = userdata; - struct timespec ts; - - assert(fd >= 0); - assert(lldp); - - space = next_datagram_size_fd(fd); - if (space < 0) { - log_lldp_errno(lldp, space, "Failed to determine datagram size to read, ignoring: %m"); - return 0; - } - - n = lldp_neighbor_new(space); - if (!n) - return -ENOMEM; - - length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); - if (length < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) - return 0; - - log_lldp_errno(lldp, errno, "Failed to read LLDP datagram, ignoring: %m"); - return 0; - } - - if ((size_t) length != n->raw_size) { - log_lldp(lldp, "Packet size mismatch, ignoring"); - return 0; - } - - /* Try to get the timestamp of this packet if it is known */ - if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0) - triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts)); - else - triple_timestamp_get(&n->timestamp); - - return lldp_handle_datagram(lldp, n); -} - -static void lldp_reset(sd_lldp *lldp) { - assert(lldp); - - (void) event_source_disable(lldp->timer_event_source); - lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); - lldp->fd = safe_close(lldp->fd); -} - -_public_ int sd_lldp_start(sd_lldp *lldp) { - int r; - - assert_return(lldp, -EINVAL); - assert_return(lldp->event, -EINVAL); - assert_return(lldp->ifindex > 0, -EINVAL); - - if (lldp->fd >= 0) - return 0; - - assert(!lldp->io_event_source); - - lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); - if (lldp->fd < 0) - return lldp->fd; - - r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); - if (r < 0) - goto fail; - - r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); - if (r < 0) - goto fail; - - (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); - - log_lldp(lldp, "Started LLDP client"); - return 1; - -fail: - lldp_reset(lldp); - return r; -} - -_public_ int sd_lldp_stop(sd_lldp *lldp) { - if (!lldp) - return 0; - - if (lldp->fd < 0) - return 0; - - log_lldp(lldp, "Stopping LLDP client"); - - lldp_reset(lldp); - lldp_flush_neighbors(lldp); - - return 1; -} - -_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { - int r; - - assert_return(lldp, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - assert_return(!lldp->event, -EBUSY); - - if (event) - lldp->event = sd_event_ref(event); - else { - r = sd_event_default(&lldp->event); - if (r < 0) - return r; - } - - lldp->event_priority = priority; - - return 0; -} - -_public_ int sd_lldp_detach_event(sd_lldp *lldp) { - - assert_return(lldp, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - - lldp->event = sd_event_unref(lldp->event); - return 0; -} - -_public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) { - assert_return(lldp, NULL); - - return lldp->event; -} - -_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { - assert_return(lldp, -EINVAL); - - lldp->callback = cb; - lldp->userdata = userdata; - - return 0; -} - -_public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) { - assert_return(lldp, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(lldp->fd < 0, -EBUSY); - - lldp->ifindex = ifindex; - return 0; -} - -int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname) { - assert_return(lldp, -EINVAL); - assert_return(ifname, -EINVAL); - - if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE)) - return -EINVAL; - - return free_and_strdup(&lldp->ifname, ifname); -} - -const char *sd_lldp_get_ifname(sd_lldp *lldp) { - if (!lldp) - return NULL; - - return get_ifname(lldp->ifindex, &lldp->ifname); -} - -static sd_lldp* lldp_free(sd_lldp *lldp) { - assert(lldp); - - lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); - - lldp_reset(lldp); - sd_lldp_detach_event(lldp); - lldp_flush_neighbors(lldp); - - hashmap_free(lldp->neighbor_by_id); - prioq_free(lldp->neighbor_by_expiry); - free(lldp->ifname); - return mfree(lldp); -} - -DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free); - -_public_ int sd_lldp_new(sd_lldp **ret) { - _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; - int r; - - assert_return(ret, -EINVAL); - - lldp = new(sd_lldp, 1); - if (!lldp) - return -ENOMEM; - - *lldp = (sd_lldp) { - .n_ref = 1, - .fd = -1, - .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX, - .capability_mask = UINT16_MAX, - }; - - lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops); - if (!lldp->neighbor_by_id) - return -ENOMEM; - - r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); - if (r < 0) - return r; - - *ret = TAKE_PTR(lldp); - - return 0; -} - -static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) { - return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id); -} - -static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { - sd_lldp *lldp = userdata; - int r; - - r = lldp_make_space(lldp, 0); - if (r < 0) { - log_lldp_errno(lldp, r, "Failed to make space, ignoring: %m"); - return 0; - } - - r = lldp_start_timer(lldp, NULL); - if (r < 0) { - log_lldp_errno(lldp, r, "Failed to restart timer, ignoring: %m"); - return 0; - } - - return 0; -} - -static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { - sd_lldp_neighbor *n; - - assert(lldp); - - if (neighbor) - lldp_neighbor_start_ttl(neighbor); - - n = prioq_peek(lldp->neighbor_by_expiry); - if (!n) - return event_source_disable(lldp->timer_event_source); - - if (!lldp->event) - return 0; - - return event_reset_time(lldp->event, &lldp->timer_event_source, - clock_boottime_or_monotonic(), - n->until, 0, - on_timer_event, lldp, - lldp->event_priority, "lldp-timer", true); -} - -_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { - sd_lldp_neighbor **l = NULL, *n; - int k = 0, r; - - assert_return(lldp, -EINVAL); - assert_return(ret, -EINVAL); - - if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ - *ret = NULL; - return 0; - } - - l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); - if (!l) - return -ENOMEM; - - r = lldp_start_timer(lldp, NULL); - if (r < 0) { - free(l); - return r; - } - - HASHMAP_FOREACH(n, lldp->neighbor_by_id) - l[k++] = sd_lldp_neighbor_ref(n); - - assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); - - /* Return things in a stable order */ - typesafe_qsort(l, k, neighbor_compare_func); - *ret = l; - - return k; -} - -_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { - assert_return(lldp, -EINVAL); - assert_return(m > 0, -EINVAL); - - lldp->neighbors_max = m; - lldp_make_space(lldp, 0); - - return 0; -} - -_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { - assert_return(lldp, -EINVAL); - assert_return(mask != 0, -EINVAL); - - lldp->capability_mask = mask; - - return 0; -} - -_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { - assert_return(lldp, -EINVAL); - - /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so - * that our own can be filtered out here. */ - - if (addr) - lldp->filter_address = *addr; - else - zero(lldp->filter_address); - - return 0; -} diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h index d2dc21470e..08b409fef1 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h @@ -7,8 +7,8 @@ #include "sd-event.h" -#include "fs-util.h" #include "hashmap.h" +#include "inotify-util.h" #include "list.h" #include "prioq.h" #include "ratelimit.h" diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c index e9199deb41..b06e00fae2 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c @@ -915,7 +915,7 @@ static void source_disconnect(sd_event_source *s) { } default: - assert_not_reached("Wut? I shouldn't exist."); + assert_not_reached(); } if (s->pending) @@ -2407,7 +2407,7 @@ static int event_source_offline( break; default: - assert_not_reached("Wut? I shouldn't exist."); + assert_not_reached(); } /* Always reshuffle time prioq, as the ratelimited flag may be changed. */ @@ -2495,7 +2495,7 @@ static int event_source_online( break; default: - assert_not_reached("Wut? I shouldn't exist."); + assert_not_reached(); } s->enabled = enabled; @@ -3569,7 +3569,7 @@ static int source_dispatch(sd_event_source *s) { case SOURCE_WATCHDOG: case _SOURCE_EVENT_SOURCE_TYPE_MAX: case _SOURCE_EVENT_SOURCE_TYPE_INVALID: - assert_not_reached("Wut? I shouldn't exist."); + assert_not_reached(); } s->dispatching = false; @@ -3929,7 +3929,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t break; default: - assert_not_reached("Unexpected event source type"); + assert_not_reached(); } break; @@ -3953,7 +3953,7 @@ static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t break; default: - assert_not_reached("Invalid wake-up pointer"); + assert_not_reached(); } } if (r < 0) @@ -4154,7 +4154,7 @@ _public_ int sd_event_loop(sd_event *e) { assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); - _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL; + _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e); while (e->state != SD_EVENT_FINISHED) { r = sd_event_run(e, UINT64_MAX); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c index 2074771a41..19937110b0 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c @@ -5,12 +5,12 @@ #include #include "fd-util.h" -#include "fs-util.h" #include "hexdecoct.h" #include "id128-util.h" #include "io-util.h" #include "stdio-util.h" #include "string-util.h" +#include "sync-util.h" char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) { unsigned n, k = 0; @@ -167,10 +167,7 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { return r; if (do_sync) { - if (fsync(fd) < 0) - return -errno; - - r = fsync_directory_of_file(fd); + r = fsync_full(fd); if (r < 0) return r; } diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h index 053ef0a6a8..b7327a1f07 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h @@ -12,6 +12,8 @@ char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]); +#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {}) + bool id128_is_valid(const char *s) _pure_; typedef enum Id128Format { diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h index 5300234098..23979b1232 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h @@ -135,7 +135,7 @@ int sd_dhcp_client_set_ifindex( int sd_dhcp_client_set_ifname( sd_dhcp_client *client, const char *interface_name); -const char *sd_dhcp_client_get_ifname(sd_dhcp_client *client); +int sd_dhcp_client_get_ifname(sd_dhcp_client *client, const char **ret); int sd_dhcp_client_set_mac( sd_dhcp_client *client, const uint8_t *addr, diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h index e02d67632a..f3889782bc 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -94,7 +94,7 @@ int sd_dhcp6_client_set_ifindex( int sd_dhcp6_client_set_ifname( sd_dhcp6_client *client, const char *interface_name); -const char * sd_dhcp6_client_get_ifname(sd_dhcp6_client *client); +int sd_dhcp6_client_get_ifname(sd_dhcp6_client *client, const char **ret); int sd_dhcp6_client_set_local_address( sd_dhcp6_client *client, const struct in6_addr *local_address); diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h index f77b31acf9..a10901e5cb 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h @@ -39,11 +39,11 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs); -int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs); -int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); -int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret); +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret); +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h index f2dd7357f8..e64f3200a3 100644 --- a/src/libnm-systemd-core/src/systemd/sd-id128.h +++ b/src/libnm-systemd-core/src/systemd/sd-id128.h @@ -34,11 +34,13 @@ union sd_id128 { uint64_t qwords[2]; }; -#define SD_ID128_STRING_MAX 33 +#define SD_ID128_STRING_MAX 33U char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); int sd_id128_from_string(const char *s, sd_id128_t *ret); +#define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {}) + int sd_id128_randomize(sd_id128_t *ret); int sd_id128_get_machine(sd_id128_t *ret); diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h index 3213db553c..90d3f0a0d1 100644 --- a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h +++ b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h @@ -47,7 +47,7 @@ int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr); int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index); int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd); int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name); -const char *sd_ipv4acd_get_ifname(sd_ipv4acd *acd); +int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret); int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address); int sd_ipv4acd_is_running(sd_ipv4acd *acd); int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts); diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h index 27263bc837..ed014b53f2 100644 --- a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h +++ b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h @@ -47,7 +47,7 @@ int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index); int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll); int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name); -const char *sd_ipv4ll_get_ifname(sd_ipv4ll *ll); +int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret); int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed); int sd_ipv4ll_is_running(sd_ipv4ll *ll); diff --git a/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h b/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h new file mode 100644 index 0000000000..bfeac14ce3 --- /dev/null +++ b/src/libnm-systemd-core/src/systemd/sd-lldp-rx.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosdlldprxhfoo +#define foosdlldprxhfoo + +/*** + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include + +#include "sd-event.h" +#include "sd-lldp.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_lldp_rx sd_lldp_rx; +typedef struct sd_lldp_neighbor sd_lldp_neighbor; + +typedef enum sd_lldp_rx_event_t { + SD_LLDP_RX_EVENT_ADDED, + SD_LLDP_RX_EVENT_REMOVED, + SD_LLDP_RX_EVENT_UPDATED, + SD_LLDP_RX_EVENT_REFRESHED, + _SD_LLDP_RX_EVENT_MAX, + _SD_LLDP_RX_EVENT_INVALID = -EINVAL, + _SD_ENUM_FORCE_S64(LLDP_RX_EVENT), +} sd_lldp_rx_event_t; + +typedef void (*sd_lldp_rx_callback_t)(sd_lldp_rx *lldp_rx, sd_lldp_rx_event_t event, sd_lldp_neighbor *n, void *userdata); + +int sd_lldp_rx_new(sd_lldp_rx **ret); +sd_lldp_rx *sd_lldp_rx_ref(sd_lldp_rx *lldp_rx); +sd_lldp_rx *sd_lldp_rx_unref(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_start(sd_lldp_rx *lldp_rx); +int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx); +int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority); +int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx); +sd_event *sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx); + +int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata); +int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex); +int sd_lldp_rx_set_ifname(sd_lldp_rx *lldp_rx, const char *ifname); +int sd_lldp_rx_get_ifname(sd_lldp_rx *lldp_rx, const char **ret); + +/* Controls how much and what to store in the neighbors database */ +int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t n); +int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask); +int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *address); + +int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***neighbors); + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); +sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); +sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); + +/* Access to LLDP frame metadata */ +int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); +int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); +int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); +int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_rx, sd_lldp_rx_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/libnm-systemd-core/src/systemd/sd-lldp.h b/src/libnm-systemd-core/src/systemd/sd-lldp.h index 64047ee817..c32d789476 100644 --- a/src/libnm-systemd-core/src/systemd/sd-lldp.h +++ b/src/libnm-systemd-core/src/systemd/sd-lldp.h @@ -17,12 +17,7 @@ along with systemd; If not, see . ***/ -#include #include -#include -#include - -#include "sd-event.h" #include "_sd-common.h" @@ -85,20 +80,24 @@ enum { #define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ ((uint16_t) \ - (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ - SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ - SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ - SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ - SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ - SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ - SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ + (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER | \ + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE | \ + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP | \ + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER | \ + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS | \ + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN | \ + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN | \ SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) -#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } -#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } +#define SD_LLDP_OUI_802_1 (const uint8_t[]) { 0x00, 0x80, 0xc2 } +#define SD_LLDP_OUI_802_3 (const uint8_t[]) { 0x00, 0x12, 0x0f } -#define SD_LLDP_OUI_MUD (uint8_t[]) { 0x00, 0x00, 0x5E } -#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION 0x01 +#define _SD_LLDP_OUI_IANA 0x00, 0x00, 0x5E +#define SD_LLDP_OUI_IANA (const uint8_t[]) { _SD_LLDP_OUI_IANA } + +#define SD_LLDP_OUI_IANA_SUBTYPE_MUD 0x01 +#define SD_LLDP_OUI_IANA_MUD \ + (const uint8_t[]) { _SD_LLDP_OUI_IANA, SD_LLDP_OUI_IANA_SUBTYPE_MUD } /* IEEE 802.1AB-2009 Annex E */ enum { @@ -119,80 +118,6 @@ enum { SD_LLDP_OUI_802_3_SUBTYPE_MAXIMUM_FRAME_SIZE = 4, }; -typedef struct sd_lldp sd_lldp; -typedef struct sd_lldp_neighbor sd_lldp_neighbor; - -typedef enum sd_lldp_event_t { - SD_LLDP_EVENT_ADDED, - SD_LLDP_EVENT_REMOVED, - SD_LLDP_EVENT_UPDATED, - SD_LLDP_EVENT_REFRESHED, - _SD_LLDP_EVENT_MAX, - _SD_LLDP_EVENT_INVALID = -EINVAL, - _SD_ENUM_FORCE_S64(LLDP_EVENT), -} sd_lldp_event_t; - -typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event_t event, sd_lldp_neighbor *n, void *userdata); - -int sd_lldp_new(sd_lldp **ret); -sd_lldp* sd_lldp_ref(sd_lldp *lldp); -sd_lldp* sd_lldp_unref(sd_lldp *lldp); - -int sd_lldp_start(sd_lldp *lldp); -int sd_lldp_stop(sd_lldp *lldp); - -int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); -int sd_lldp_detach_event(sd_lldp *lldp); -sd_event *sd_lldp_get_event(sd_lldp *lldp); - -int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); -int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex); -int sd_lldp_set_ifname(sd_lldp *lldp, const char *ifname); -const char *sd_lldp_get_ifname(sd_lldp *lldp); - -/* Controls how much and what to store in the neighbors database */ -int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); -int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); -int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); - -int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); - -int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); -sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); -sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); - -/* Access to LLDP frame metadata */ -int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); -int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret); -int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ -int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); -int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec); -int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret); -int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); -int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); - -/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs - * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ -int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); -int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); -int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); -int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype); -int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype); -int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); - -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); - _SD_END_DECLARATIONS; #endif diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h index 6088def1b6..f45b2ad65e 100644 --- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h +++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h @@ -79,7 +79,7 @@ sd_event *sd_ndisc_get_event(sd_ndisc *nd); int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata); int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index); int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name); -const char *sd_ndisc_get_ifname(sd_ndisc *nd); +int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret); int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/alloc-util.h b/src/libnm-systemd-shared/src/basic/alloc-util.h index 3c33308f48..e587fe79e7 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -44,16 +44,19 @@ typedef void (*free_func_t)(void *p); #define malloc0(n) (calloc(1, (n) ?: 1)) -static inline void *mfree(void *memory) { - free(memory); - return NULL; -} +#define mfree(memory) \ + ({ \ + free(memory); \ + (typeof(memory)) NULL; \ + }) #define free_and_replace(a, b) \ ({ \ - free(a); \ - (a) = (b); \ - (b) = NULL; \ + typeof(a)* _a = &(a); \ + typeof(b)* _b = &(b); \ + free(*_a); \ + (*_a) = (*_b); \ + (*_b) = NULL; \ 0; \ }) diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index ce2f4c6589..90ccd2c032 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -32,6 +32,7 @@ typedef enum CGroupController { CGROUP_CONTROLLER_BPF_DEVICES, CGROUP_CONTROLLER_BPF_FOREIGN, CGROUP_CONTROLLER_BPF_SOCKET_BIND, + CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES, _CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_INVALID = -EINVAL, @@ -53,6 +54,7 @@ typedef enum CGroupMask { CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES), CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN), CGROUP_MASK_BPF_SOCKET_BIND = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_SOCKET_BIND), + CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES), /* All real cgroup v1 controllers */ CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS, @@ -61,7 +63,7 @@ typedef enum CGroupMask { CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, /* All cgroup v2 BPF pseudo-controllers */ - CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND, + CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN|CGROUP_MASK_BPF_SOCKET_BIND|CGROUP_MASK_BPF_RESTRICT_NETWORK_INTERFACES, _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 } CGroupMask; @@ -172,6 +174,7 @@ typedef enum CGroupFlags { typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); +int cg_kill_kernel_sigkill(const char *controller, const char *path); int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); int cg_split_spec(const char *spec, char **ret_controller, char **ret_path); @@ -272,6 +275,7 @@ int cg_kernel_controllers(Set **controllers); bool cg_ns_supported(void); bool cg_freezer_supported(void); +bool cg_kill_supported(void); int cg_all_unified(void); int cg_hybrid_unified(void); diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index 81b1e3f10e..bc979915b5 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) { return 0; } -char **strv_env_merge(size_t n_lists, ...) { - _cleanup_strv_free_ char **ret = NULL; - size_t n = 0; - char **l, **k; +char** _strv_env_merge(char **first, ...) { + _cleanup_strv_free_ char **merged = NULL; + char **k; va_list ap; /* Merges an arbitrary number of environment sets */ - va_start(ap, n_lists); - for (size_t i = 0; i < n_lists; i++) { + size_t n = strv_length(first); + + va_start(ap, first); + for (;;) { + char **l; + l = va_arg(ap, char**); + if (l == POINTER_MAX) + break; + n += strv_length(l); } va_end(ap); - ret = new(char*, n+1); - if (!ret) + k = merged = new(char*, n + 1); + if (!merged) + return NULL; + merged[0] = NULL; + + if (env_append(merged, &k, first) < 0) return NULL; - *ret = NULL; - k = ret; + va_start(ap, first); + for (;;) { + char **l; - va_start(ap, n_lists); - for (size_t i = 0; i < n_lists; i++) { l = va_arg(ap, char**); - if (env_append(ret, &k, l) < 0) { + if (l == POINTER_MAX) + break; + + if (env_append(merged, &k, l) < 0) { va_end(ap); return NULL; } } va_end(ap); - return TAKE_PTR(ret); + return TAKE_PTR(merged); } static bool env_match(const char *t, const char *pattern) { @@ -409,6 +421,32 @@ int strv_env_replace_strdup(char ***l, const char *assignment) { return strv_env_replace_consume(l, p); } +int strv_env_replace_strdup_passthrough(char ***l, const char *assignment) { + /* Like strv_env_replace_strdup(), but pulls the variable from the environment of + * the calling program, if a variable name without value is specified. + */ + char *p; + + if (strchr(assignment, '=')) { + if (!env_assignment_is_valid(assignment)) + return -EINVAL; + + p = strdup(assignment); + } else { + if (!env_name_is_valid(assignment)) + return -EINVAL; + + /* If we can't find the variable in our environment, we will use + * the empty string. This way "passthrough" is equivalent to passing + * --setenv=FOO=$FOO in the shell. */ + p = strjoin(assignment, "=", secure_getenv(assignment)); + } + if (!p) + return -ENOMEM; + + return strv_env_replace_consume(l, p); +} + int strv_env_assign(char ***l, const char *key, const char *value) { if (!env_name_is_valid(key)) return -EINVAL; @@ -577,6 +615,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { word = e+1; state = WORD; + nest--; } else if (*e == ':') { if (flags & REPLACE_ENV_ALLOW_EXTENDED) { len = e - word - 2; @@ -832,3 +871,20 @@ int getenv_path_list(const char *name, char ***ret_paths) { *ret_paths = TAKE_PTR(l); return 1; } + +int unsetenv_erase(const char *name) { + char *p; + + assert(name); + + p = getenv(name); + if (!p) + return 0; + + string_erase(p); + + if (unsetenv(name) < 0) + return -errno; + + return 1; +} diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 1fbe7e4270..38bfc8a3f2 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -39,13 +39,15 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha bool strv_env_name_is_valid(char **l); bool strv_env_name_or_assignment_is_valid(char **l); -char **strv_env_merge(size_t n_lists, ...); +char** _strv_env_merge(char **first, ...); +#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX) char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ char **strv_env_unset(char **l, const char *p); /* In place ... */ char **strv_env_unset_many(char **l, ...) _sentinel_; int strv_env_replace_consume(char ***l, char *p); /* In place ... */ int strv_env_replace_strdup(char ***l, const char *assignment); +int strv_env_replace_strdup_passthrough(char ***l, const char *assignment); int strv_env_assign(char ***l, const char *key, const char *value); char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; @@ -66,3 +68,5 @@ int setenv_systemd_exec_pid(bool update_only); /* Parses and does sanity checks on an environment variable containing * PATH-like colon-separated absolute paths */ int getenv_path_list(const char *name, char ***ret_paths); + +int unsetenv_erase(const char *name); diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index 6b6457dbc2..d7cf85e0ee 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -433,11 +433,9 @@ bool fdname_is_valid(const char *s) { } int fd_get_path(int fd, char **ret) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; int r; - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - r = readlink_malloc(procfs_path, ret); + r = readlink_malloc(FORMAT_PROC_FD_PATH(fd), ret); if (r == -ENOENT) { /* ENOENT can mean two things: that the fd does not exist or that /proc is not mounted. Let's make * things debuggable and distinguish the two. */ @@ -647,7 +645,6 @@ finish: } int fd_reopen(int fd, int flags) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; int new_fd; /* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to @@ -657,8 +654,7 @@ int fd_reopen(int fd, int flags) { * * This implicitly resets the file read index to 0. */ - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - new_fd = open(procfs_path, flags); + new_fd = open(FORMAT_PROC_FD_PATH(fd), flags); if (new_fd < 0) { if (errno != ENOENT) return -errno; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index 61b6684cb3..2382d52d40 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -7,6 +7,7 @@ #include #include "macro.h" +#include "stdio-util.h" /* maximum length of fdname */ #define FDNAME_MAX 255 @@ -104,7 +105,20 @@ static inline int make_null_stdio(void) { 0; \ }) - int fd_reopen(int fd, int flags); int read_nr_open(void); int btrfs_defrag_fd(int fd); + +/* The maximum length a buffer for a /proc/self/fd/ path needs */ +#define PROC_FD_PATH_MAX \ + (STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)) + +static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int fd) { + assert(buf); + assert(fd >= 0); + assert_se(snprintf_ok(buf, PROC_FD_PATH_MAX, "/proc/self/fd/%i", fd)); + return buf; +} + +#define FORMAT_PROC_FD_PATH(fd) \ + format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd)) diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index 19fe6e214c..cced1dd564 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -13,6 +13,7 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -25,19 +26,22 @@ #include "socket-util.h" #include "stdio-util.h" #include "string-util.h" +#include "sync-util.h" #include "tmpfile-util.h" /* The maximum size of the file we'll read in one go in read_full_file() (64M). */ #define READ_FULL_BYTES_MAX (64U*1024U*1024U - 1U) -/* The maximum size of virtual files we'll read in one go in read_virtual_file() (4M). Note that this limit - * is different (and much lower) than the READ_FULL_BYTES_MAX limit. This reflects the fact that we use - * different strategies for reading virtual and regular files: virtual files are generally size constrained: - * there we allocate the full buffer size in advance. Regular files OTOH can be much larger, and here we grow - * the allocations exponentially in a loop. In glibc large allocations are immediately backed by mmap() - * making them relatively slow (measurably so). Thus, when allocating the full buffer in advance the large - * limit is a problem. When allocating piecemeal it's not. Hence pick two distinct limits. */ -#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 1U) +/* The maximum size of virtual files (i.e. procfs, sysfs, and other virtual "API" files) we'll read in one go + * in read_virtual_file(). Note that this limit is different (and much lower) than the READ_FULL_BYTES_MAX + * limit. This reflects the fact that we use different strategies for reading virtual and regular files: + * virtual files we generally have to read in a single read() syscall since the kernel doesn't support + * continuation read()s for them. Thankfully they are somewhat size constrained. Thus we can allocate the + * full potential buffer in advance. Regular files OTOH can be much larger, and there we grow the allocations + * exponentially in a loop. We use a size limit of 4M-2 because 4M-1 is the maximum buffer that /proc/sys/ + * allows us to read() (larger reads will fail with ENOMEM), and we want to read one extra byte so that we + * can detect EOFs. */ +#define READ_VIRTUAL_BYTES_MAX (4U*1024U*1024U - 2U) int fopen_unlocked(const char *path, const char *options, FILE **ret) { assert(ret); @@ -146,6 +150,30 @@ int write_string_stream_ts( return -EBADF; } + if (flags & WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) { + _cleanup_free_ char *t = NULL; + + /* If value to be written is same as that of the existing value, then suppress the write. */ + + if (fd < 0) { + fd = fileno(f); + if (fd < 0) + return -EBADF; + } + + /* Read an additional byte to detect cases where the prefix matches but the rest + * doesn't. Also, 0 returned by read_virtual_file_fd() means the read was truncated and + * it won't be equal to the new value. */ + if (read_virtual_file_fd(fd, strlen(line)+1, &t, NULL) > 0 && + streq_skip_trailing_chars(line, t, NEWLINE)) { + log_debug("No change in value '%s', supressing write", line); + return 0; + } + + if (lseek(fd, 0, SEEK_SET) < 0) + return -errno; + } + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { @@ -261,10 +289,11 @@ int write_string_file_ts( assert(!ts); /* We manually build our own version of fopen(..., "we") that works without O_CREAT and with O_NOFOLLOW if needed. */ - fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY | + fd = open(fn, O_CLOEXEC|O_NOCTTY | (FLAGS_SET(flags, WRITE_STRING_FILE_NOFOLLOW) ? O_NOFOLLOW : 0) | (FLAGS_SET(flags, WRITE_STRING_FILE_CREATE) ? O_CREAT : 0) | - (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0), + (FLAGS_SET(flags, WRITE_STRING_FILE_TRUNCATE) ? O_TRUNC : 0) | + (FLAGS_SET(flags, WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL) ? O_RDWR : O_WRONLY), (FLAGS_SET(flags, WRITE_STRING_FILE_MODE_0600) ? 0600 : 0666)); if (fd < 0) { r = -errno; @@ -373,9 +402,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { return 1; } -int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { +int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; - _cleanup_close_ int fd = -1; size_t n, size; int n_retries; bool truncated = false; @@ -393,10 +421,7 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents * contents* may be returned. (Though the read is still done using one syscall.) Returns 0 on * partial success, 1 if untruncated contents were read. */ - fd = open(filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - + assert(fd >= 0); assert(max_size <= READ_VIRTUAL_BYTES_MAX || max_size == SIZE_MAX); /* Limit the number of attempts to read the number of bytes returned by fstat(). */ @@ -431,6 +456,11 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents } n_retries--; + } else if (n_retries > 1) { + /* Files in /proc are generally smaller than the page size so let's start with + * a page size buffer from malloc and only use the max buffer on the final try. */ + size = MIN3(page_size() - 1, READ_VIRTUAL_BYTES_MAX, max_size); + n_retries = 1; } else { size = MIN(READ_VIRTUAL_BYTES_MAX, max_size); n_retries = 0; @@ -463,9 +493,14 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents if (n <= size) break; - /* If a maximum size is specified and we already read as much, no need to try again */ - if (max_size != SIZE_MAX && n >= max_size) { - n = max_size; + /* If a maximum size is specified and we already read more we know the file is larger, and + * can handle this as truncation case. Note that if the size of what we read equals the + * maximum size then this doesn't mean truncation, the file might or might not end on that + * byte. We need to rerun the loop in that case, with a larger buffer size, so that we read + * at least one more byte to be able to distinguish EOF from truncation. */ + if (max_size != SIZE_MAX && n > max_size) { + n = size; /* Make sure we never use more than what we sized the buffer for (so that + * we have one free byte in it for the trailing NUL we add below).*/ truncated = true; break; } @@ -512,6 +547,18 @@ int read_virtual_file(const char *filename, size_t max_size, char **ret_contents return !truncated; } +int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) { + _cleanup_close_ int fd = -1; + + assert(filename); + + fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC); + if (fd < 0) + return -errno; + + return read_virtual_file_fd(fd, max_size, ret_contents, ret_size); +} + int read_full_stream_full( FILE *f, const char *filename, @@ -721,8 +768,6 @@ int read_full_file_full( if (dir_fd == AT_FDCWD) r = sockaddr_un_set_path(&sa.un, filename); else { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - /* If we shall operate relative to some directory, then let's use O_PATH first to * open the socket inode, and then connect to it via /proc/self/fd/. We have to do * this since there's not connectat() that takes a directory fd as first arg. */ @@ -731,8 +776,7 @@ int read_full_file_full( if (dfd < 0) return -errno; - xsprintf(procfs_path, "/proc/self/fd/%i", dfd); - r = sockaddr_un_set_path(&sa.un, procfs_path); + r = sockaddr_un_set_path(&sa.un, FORMAT_PROC_FD_PATH(dfd)); } if (r < 0) return r; @@ -897,10 +941,12 @@ DIR *xopendirat(int fd, const char *name, int flags) { return d; } -static int mode_to_flags(const char *mode) { +int fopen_mode_to_flags(const char *mode) { const char *p; int flags; + assert(mode); + if ((p = startswith(mode, "r+"))) flags = O_RDWR; else if ((p = startswith(mode, "r"))) @@ -953,7 +999,7 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r } else { int fd, mode_flags; - mode_flags = mode_to_flags(mode); + mode_flags = fopen_mode_to_flags(mode); if (mode_flags < 0) return mode_flags; @@ -1093,39 +1139,6 @@ int search_and_fopen_nulstr( return search_and_fopen_internal(filename, mode, root, s, ret, ret_path); } -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path) { - - _cleanup_close_ int fd = -1; - _cleanup_free_ char *final_path = NULL; - int mode_flags, r; - - assert(path); - assert(open_flags); - assert(ret_file); - - mode_flags = mode_to_flags(open_flags); - if (mode_flags < 0) - return mode_flags; - - fd = chase_symlinks_and_open(path, root, chase_flags, mode_flags, ret_path ? &final_path : NULL); - if (fd < 0) - return fd; - - r = take_fdopen_unlocked(&fd, open_flags, ret_file); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(final_path); - return 0; -} - int fflush_and_check(FILE *f) { assert(f); @@ -1153,10 +1166,7 @@ int fflush_sync_and_check(FILE *f) { if (fd < 0) return 0; - if (fsync(fd) < 0) - return -errno; - - r = fsync_directory_of_file(fd); + r = fsync_full(fd); if (r < 0) return r; @@ -1425,16 +1435,3 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c filename, st->st_mode & 07777); return 0; } - -int rename_and_apply_smack_floor_label(const char *from, const char *to) { - int r = 0; - if (rename(from, to) < 0) - return -errno; - -#if HAVE_SMACK_RUN_LABEL - r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL); - if (r < 0) - return r; -#endif - return r; -} diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index af797cfafd..899def946b 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -2,11 +2,11 @@ #pragma once #include +#include #include #include #include #include -#include #include #include "macro.h" @@ -15,17 +15,18 @@ #define LONG_LINE_MAX (1U*1024U*1024U) typedef enum { - WRITE_STRING_FILE_CREATE = 1 << 0, - WRITE_STRING_FILE_TRUNCATE = 1 << 1, - WRITE_STRING_FILE_ATOMIC = 1 << 2, - WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, - WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, - WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5, - WRITE_STRING_FILE_SYNC = 1 << 6, - WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7, - WRITE_STRING_FILE_NOFOLLOW = 1 << 8, - WRITE_STRING_FILE_MKDIR_0755 = 1 << 9, - WRITE_STRING_FILE_MODE_0600 = 1 << 10, + WRITE_STRING_FILE_CREATE = 1 << 0, + WRITE_STRING_FILE_TRUNCATE = 1 << 1, + WRITE_STRING_FILE_ATOMIC = 1 << 2, + WRITE_STRING_FILE_AVOID_NEWLINE = 1 << 3, + WRITE_STRING_FILE_VERIFY_ON_FAILURE = 1 << 4, + WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE = 1 << 5, + WRITE_STRING_FILE_SYNC = 1 << 6, + WRITE_STRING_FILE_DISABLE_BUFFER = 1 << 7, + WRITE_STRING_FILE_NOFOLLOW = 1 << 8, + WRITE_STRING_FILE_MKDIR_0755 = 1 << 9, + WRITE_STRING_FILE_MODE_0600 = 1 << 10, + WRITE_STRING_FILE_SUPPRESS_REDUNDANT_VIRTUAL = 1 << 11, /* And before you wonder, why write_string_file_atomic_label_ts() is a separate function instead of just one more flag here: it's about linking: we don't want to pull -lselinux into all users of write_string_file() @@ -67,6 +68,7 @@ static inline int read_full_file(const char *filename, char **ret_contents, size return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); } +int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size); int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size); static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) { return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size); @@ -89,14 +91,6 @@ int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **r int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **ret, char **ret_path); int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **ret, char **ret_path); -int chase_symlinks_and_fopen_unlocked( - const char *path, - const char *root, - unsigned chase_flags, - const char *open_flags, - FILE **ret_file, - char **ret_path); - int fflush_and_check(FILE *f); int fflush_sync_and_check(FILE *f); @@ -125,4 +119,4 @@ int safe_fgetc(FILE *f, char *ret); int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); -int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path); +int fopen_mode_to_flags(const char *mode); diff --git a/src/libnm-systemd-shared/src/basic/format-util.c b/src/libnm-systemd-shared/src/basic/format-util.c index 9920604f3a..94501853a1 100644 --- a/src/libnm-systemd-shared/src/basic/format-util.c +++ b/src/libnm-systemd-shared/src/basic/format-util.c @@ -3,23 +3,43 @@ #include "format-util.h" #include "memory-util.h" #include "stdio-util.h" +#include "strxcpyx.h" + +assert_cc(STRLEN("%") + DECIMAL_STR_MAX(int) <= IF_NAMESIZE); +int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) { + if (ifindex <= 0) + return -EINVAL; -assert_cc(DECIMAL_STR_MAX(int) + 1 <= IF_NAMESIZE + 1); -char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag) { - /* Buffer is always cleared */ - memzero(buf, IF_NAMESIZE + 1); if (if_indextoname(ifindex, buf)) - return buf; + return 0; if (!FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX)) - return NULL; + return -errno; if (FLAGS_SET(flag, FORMAT_IFNAME_IFINDEX_WITH_PERCENT)) - snprintf(buf, IF_NAMESIZE + 1, "%%%d", ifindex); + assert(snprintf_ok(buf, IF_NAMESIZE, "%%%d", ifindex)); else - snprintf(buf, IF_NAMESIZE + 1, "%d", ifindex); + assert(snprintf_ok(buf, IF_NAMESIZE, "%d", ifindex)); - return buf; + return 0; +} + +int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret) { + char buf[IF_NAMESIZE], *copy; + int r; + + assert(ret); + + r = format_ifname_full(ifindex, flag, buf); + if (r < 0) + return r; + + copy = strdup(buf); + if (!copy) + return -ENOMEM; + + *ret = copy; + return 0; } char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { @@ -56,23 +76,23 @@ char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) { for (size_t i = 0; i < n; i++) if (t >= table[i].factor) { if (flag & FORMAT_BYTES_BELOW_POINT) { - snprintf(buf, l, - "%" PRIu64 ".%" PRIu64 "%s", - t / table[i].factor, - i != n - 1 ? - (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10): - (t * UINT64_C(10) / table[i].factor) % UINT64_C(10), - table[i].suffix); + (void) snprintf(buf, l, + "%" PRIu64 ".%" PRIu64 "%s", + t / table[i].factor, + i != n - 1 ? + (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10): + (t * UINT64_C(10) / table[i].factor) % UINT64_C(10), + table[i].suffix); } else - snprintf(buf, l, - "%" PRIu64 "%s", - t / table[i].factor, - table[i].suffix); + (void) snprintf(buf, l, + "%" PRIu64 "%s", + t / table[i].factor, + table[i].suffix); goto finish; } - snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : ""); + (void) snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : ""); finish: buf[l-1] = 0; diff --git a/src/libnm-systemd-shared/src/basic/format-util.h b/src/libnm-systemd-shared/src/basic/format-util.h index 579f231897..7dd422b987 100644 --- a/src/libnm-systemd-shared/src/basic/format-util.h +++ b/src/libnm-systemd-shared/src/basic/format-util.h @@ -61,10 +61,23 @@ typedef enum { FORMAT_IFNAME_IFINDEX_WITH_PERCENT = (1 << 1) | FORMAT_IFNAME_IFINDEX, } FormatIfnameFlag; -char *format_ifname_full(int ifindex, char buf[static IF_NAMESIZE + 1], FormatIfnameFlag flag); -static inline char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) { - return format_ifname_full(ifindex, buf, 0); +int format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]); +int format_ifname_full_alloc(int ifindex, FormatIfnameFlag flag, char **ret); + +static inline int format_ifname(int ifindex, char buf[static IF_NAMESIZE]) { + return format_ifname_full(ifindex, 0, buf); } +static inline int format_ifname_alloc(int ifindex, char **ret) { + return format_ifname_full_alloc(ifindex, 0, ret); +} + +static inline char *_format_ifname_full(int ifindex, FormatIfnameFlag flag, char buf[static IF_NAMESIZE]) { + (void) format_ifname_full(ifindex, flag, buf); + return buf; +} + +#define FORMAT_IFNAME_FULL(index, flag) _format_ifname_full(index, flag, (char[IF_NAMESIZE]){}) +#define FORMAT_IFNAME(index) _format_ifname_full(index, 0, (char[IF_NAMESIZE]){}) typedef enum { FORMAT_BYTES_USE_IEC = 1 << 0, diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 28c7247e05..847ebd1841 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -12,7 +12,6 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "locale-util.h" #include "log.h" #include "macro.h" #include "missing_fcntl.h" @@ -45,50 +44,39 @@ int unlink_noerrno(const char *path) { } int rmdir_parents(const char *path, const char *stop) { - size_t l; - int r = 0; + char *p; + int r; assert(path); assert(stop); - l = strlen(path); + if (!path_is_safe(path)) + return -EINVAL; - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; + if (!path_is_safe(stop)) + return -EINVAL; - while (l > 0) { - char *t; + p = strdupa(path); - /* Skip last component */ - while (l > 0 && path[l-1] != '/') - l--; + for (;;) { + char *slash = NULL; - /* Skip trailing slashes */ - while (l > 0 && path[l-1] == '/') - l--; - - if (l <= 0) - break; - - t = strndup(path, l); - if (!t) - return -ENOMEM; - - if (path_startswith(stop, t)) { - free(t); + /* skip the last component. */ + r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL); + if (r <= 0) + return r; + if (slash == p) return 0; - } - r = rmdir(t); - free(t); + assert(*slash == '/'); + *slash = '\0'; - if (r < 0) - if (errno != ENOENT) - return -errno; + if (path_startswith_full(stop, p, /* accept_dot_dot= */ false)) + return 0; + + if (rmdir(p) < 0 && errno != ENOENT) + return -errno; } - - return 0; } int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { @@ -308,14 +296,11 @@ int fchmod_umask(int fd, mode_t m) { } int fchmod_opath(int fd, mode_t m) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - /* This function operates also on fd that might have been opened with * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like * fchownat() does. */ - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (chmod(procfs_path, m) < 0) { + if (chmod(FORMAT_PROC_FD_PATH(fd), m) < 0) { if (errno != ENOENT) return -errno; @@ -329,12 +314,9 @@ int fchmod_opath(int fd, mode_t m) { } int futimens_opath(int fd, const struct timespec ts[2]) { - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - /* Similar to fchmod_path() but for futimens() */ - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - if (utimensat(AT_FDCWD, procfs_path, ts, 0) < 0) { + if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, 0) < 0) { if (errno != ENOENT) return -errno; @@ -380,9 +362,8 @@ int fd_warn_permissions(const char *path, int fd) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int fd = -1; - int r, ret = 0; + int r, ret; assert(path); @@ -412,8 +393,6 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is * something fchown(), fchmod(), futimensat() don't allow. */ - xsprintf(fdpath, "/proc/self/fd/%i", fd); - ret = fchmod_and_chown(fd, mode, uid, gid); if (stamp != USEC_INFINITY) { @@ -421,11 +400,11 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi timespec_store(&ts[0], stamp); ts[1] = ts[0]; - r = utimensat(AT_FDCWD, fdpath, ts, 0); + r = futimens_opath(fd, ts); } else - r = utimensat(AT_FDCWD, fdpath, NULL, 0); + r = futimens_opath(fd, NULL); if (r < 0 && ret >= 0) - return -errno; + return r; return ret; } @@ -702,547 +681,10 @@ int unlink_or_warn(const char *filename) { return 0; } -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int wd; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - wd = inotify_add_watch(fd, path, mask); - if (wd < 0) - return -errno; - - return wd; -} - -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { - int wd; - - wd = inotify_add_watch(fd, pathname, mask); - if (wd < 0) { - if (errno == ENOSPC) - return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); - - return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); - } - - return wd; -} - -bool unsafe_transition(const struct stat *a, const struct stat *b) { - /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to - * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files - * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ - - if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ - return false; - - return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */ -} - -static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL; - struct stat st; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -ENOLINK; - - (void) fd_get_path(a, &n1); - (void) fd_get_path(b, &n2); - - if (fstat(a, &st) == 0) - user_a = uid_to_name(st.st_uid); - if (fstat(b, &st) == 0) - user_b = uid_to_name(st.st_uid); - - return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), - "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.", - strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path); -} - -static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL; - - if (!FLAGS_SET(flags, CHASE_WARN)) - return -EREMOTE; - - (void) fd_get_path(fd, &n1); - - return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE), - "Detected autofs mount point %s during canonicalization of %s.", - strna(n1), path); -} - -int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret_path, int *ret_fd) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */ - bool exists = true, append_trail_slash = false; - struct stat previous_stat; - const char *todo; - int r; - - assert(path); - - /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ - if ((flags & CHASE_NONEXISTENT) && ret_fd) - return -EINVAL; - - if ((flags & CHASE_STEP) && ret_fd) - return -EINVAL; - - if (isempty(path)) - return -EINVAL; - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following - * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is - * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first - * prefixed accordingly. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * to a minimum. - * - * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got - * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this - * function what to do when encountering a symlink with an absolute path as directory: prefix it by the - * specified path. - * - * There are five ways to invoke this function: - * - * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is - * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 - * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is - * returned if the destination was found, -ENOENT if it wasn't. - * - * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file - * descriptor is returned as return value. This is useful to open files relative to some root - * directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using - * fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be combined with - * CHASE_NONEXISTENT. - * - * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first - * symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if - * a caller wants to trace the path through the file system verbosely. Returns < 0 on error, > 0 if the - * path is fully normalized, and == 0 for each normalization step. This may be combined with - * CHASE_NONEXISTENT, in which case 1 is returned when a component is not found. - * - * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from - * unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If - * CHASE_WARN is also set, a warning describing the unsafe transition is emitted. - * - * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization - * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of - * the mount point is emitted. - */ - - /* A root directory of "/" or "" is identical to none */ - if (empty_or_root(original_root)) - original_root = NULL; - - if (!original_root && !ret_path && !(flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_STEP)) && ret_fd) { - /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root set - * and doesn't care about any of the other special features we provide either. */ - r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0)); - if (r < 0) - return -errno; - - *ret_fd = r; - return 0; - } - - if (original_root) { - r = path_make_absolute_cwd(original_root, &root); - if (r < 0) - return r; - - /* Simplify the root directory, so that it has no duplicate slashes and nothing at the - * end. While we won't resolve the root path we still simplify it. Note that dropping the - * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY - * anyway. Moreover at the end of this function after processing everything we'll always turn - * the empty string back to "/". */ - delete_trailing_chars(root, "/"); - path_simplify(root); - - if (flags & CHASE_PREFIX_ROOT) { - /* We don't support relative paths in combination with a root directory */ - if (!path_is_absolute(path)) - return -EINVAL; - - path = prefix_roota(root, path); - } - } - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) - if (fstat(fd, &previous_stat) < 0) - return -errno; - - if (flags & CHASE_TRAIL_SLASH) - append_trail_slash = endswith(buffer, "/") || endswith(buffer, "/."); - - if (root) { - /* If we are operating on a root directory, let's take the root directory as it is. */ - - todo = path_startswith(buffer, root); - if (!todo) - return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, - SYNTHETIC_ERRNO(ECHRNG), - "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", - path, root); - - done = strdup(root); - } else { - todo = buffer; - done = strdup("/"); - } - - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - const char *e; - - r = path_find_first_component(&todo, true, &e); - if (r < 0) - return r; - if (r == 0) { /* We reached the end. */ - if (append_trail_slash) - if (!strextend(&done, "/")) - return -ENOMEM; - break; - } - - first = strndup(e, r); - if (!first) - return -ENOMEM; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "..")) { - _cleanup_free_ char *parent = NULL; - _cleanup_close_ int fd_parent = -1; - - /* If we already are at the top, then going up will not change anything. This is in-line with - * how the kernel handles this. */ - if (empty_or_root(done)) - continue; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir. */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - continue; - - free_and_replace(done, parent); - - if (flags & CHASE_STEP) - goto chased_one; - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd_parent, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, fd_parent, path, flags); - - previous_stat = st; - } - - safe_close(fd); - fd = TAKE_FD(fd_parent); - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) { - if (errno == ENOENT && - (flags & CHASE_NONEXISTENT) && - (isempty(todo) || path_is_safe(todo))) { - /* If CHASE_NONEXISTENT is set, and the path does not exist, then - * that's OK, return what we got so far. But don't allow this if the - * remaining path contains "../" or something else weird. */ - - if (!path_extend(&done, first, todo)) - return -ENOMEM; - - exists = false; - break; - } - - return -errno; - } - - if (fstat(child, &st) < 0) - return -errno; - if ((flags & CHASE_SAFE) && - unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(fd, child, path, flags); - - previous_stat = st; - - if ((flags & CHASE_NO_AUTOFS) && - fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) - return log_autofs_mount_point(child, path, flags); - - if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) { - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we - * don't follow symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); - if (fd < 0) - return -errno; - - if (flags & CHASE_SAFE) { - if (fstat(fd, &st) < 0) - return -errno; - - if (unsafe_transition(&previous_stat, &st)) - return log_unsafe_transition(child, fd, path, flags); - - previous_stat = st; - } - - /* Note that we do not revalidate the root, we take it as is. */ - r = free_and_strdup(&done, empty_to_root(root)); - if (r < 0) - return r; - } - - /* Prefix what's left to do with what we just read, and start the loop again, but - * remain in the current directory. */ - if (!path_extend(&destination, todo)) - return -ENOMEM; - - free_and_replace(buffer, destination); - todo = buffer; - - if (flags & CHASE_STEP) - goto chased_one; - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!path_extend(&done, first)) - return -ENOMEM; - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = TAKE_FD(child); - } - - if (ret_path) - *ret_path = TAKE_PTR(done); - - if (ret_fd) { - /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a - * proper fd by opening /proc/self/fd/xyz. */ - - assert(fd >= 0); - *ret_fd = TAKE_FD(fd); - } - - if (flags & CHASE_STEP) - return 1; - - return exists; - -chased_one: - if (ret_path) { - const char *e; - - /* todo may contain slashes at the beginning. */ - r = path_find_first_component(&todo, true, &e); - if (r < 0) - return r; - if (r == 0) - *ret_path = TAKE_PTR(done); - else { - char *c; - - c = path_join(done, e); - if (!c) - return -ENOMEM; - - *ret_path = c; - } - } - - return 0; -} - -int chase_symlinks_and_open( - const char *path, - const char *root, - unsigned chase_flags, - int open_flags, - char **ret_path) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - r = open(path, open_flags); - if (r < 0) - return -errno; - - return r; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - r = fd_reopen(path_fd, open_flags); - if (r < 0) - return r; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - return r; -} - -int chase_symlinks_and_opendir( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - DIR **ret_dir) { - - char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - DIR *d; - int r; - - if (!ret_dir) - return -EINVAL; - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - d = opendir(path); - if (!d) - return -errno; - - *ret_dir = d; - return 0; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - xsprintf(procfs_path, "/proc/self/fd/%i", path_fd); - d = opendir(procfs_path); - if (!d) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - - *ret_dir = d; - return 0; -} - -int chase_symlinks_and_stat( - const char *path, - const char *root, - unsigned chase_flags, - char **ret_path, - struct stat *ret_stat, - int *ret_fd) { - - _cleanup_close_ int path_fd = -1; - _cleanup_free_ char *p = NULL; - int r; - - assert(path); - assert(ret_stat); - - if (chase_flags & CHASE_NONEXISTENT) - return -EINVAL; - - if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) { - /* Shortcut this call if none of the special features of this call are requested */ - if (stat(path, ret_stat) < 0) - return -errno; - - return 1; - } - - r = chase_symlinks(path, root, chase_flags, ret_path ? &p : NULL, &path_fd); - if (r < 0) - return r; - assert(path_fd >= 0); - - if (fstat(path_fd, ret_stat) < 0) - return -errno; - - if (ret_path) - *ret_path = TAKE_PTR(p); - if (ret_fd) - *ret_fd = TAKE_FD(path_fd); - - return 1; -} - int access_fd(int fd, int mode) { - char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - /* Like access() but operates on an already open fd */ - xsprintf(p, "/proc/self/fd/%i", fd); - if (access(p, mode) < 0) { + if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) { if (errno != ENOENT) return -errno; @@ -1377,172 +819,6 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { return 0; } -int fsync_directory_of_file(int fd) { - _cleanup_close_ int dfd = -1; - struct stat st; - int r; - - assert(fd >= 0); - - /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check - * for the inode type first */ - if (fstat(fd, &st) < 0) - return -errno; - - if (S_ISDIR(st.st_mode)) { - dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); - if (dfd < 0) - return -errno; - - } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other - * types check O_PATH flag */ - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags < 0) - return -errno; - - if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case - * we can sensibly do what is requested. Otherwise this refers - * to a socket, fifo or device node, where the concept of a - * containing directory doesn't make too much sense. */ - return -ENOTTY; - } - - if (dfd < 0) { - _cleanup_free_ char *path = NULL; - - r = fd_get_path(fd, &path); - if (r < 0) { - log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", - fd, - r == -ENOSYS ? ", ignoring" : ""); - - if (r == -ENOSYS) - /* If /proc is not available, we're most likely running in some - * chroot environment, and syncing the directory is not very - * important in that case. Let's just silently do nothing. */ - return 0; - - return r; - } - - if (!path_is_absolute(path)) - return -EINVAL; - - dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); - if (dfd < 0) - return dfd; - } - - if (fsync(dfd) < 0) - return -errno; - - return 0; -} - -int fsync_full(int fd) { - int r, q; - - /* Sync both the file and the directory */ - - r = fsync(fd) < 0 ? -errno : 0; - - q = fsync_directory_of_file(fd); - if (r < 0) /* Return earlier error */ - return r; - if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a - * parent dir */ - return 0; - return q; -} - -int fsync_path_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - int fd; - - if (isempty(path)) { - if (at_fd == AT_FDCWD) { - opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } else - fd = at_fd; - } else { - opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); - if (opened_fd < 0) - return -errno; - - fd = opened_fd; - } - - if (fsync(fd) < 0) - return -errno; - - return 0; -} - -int fsync_parent_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - - if (isempty(path)) { - if (at_fd != AT_FDCWD) - return fsync_directory_of_file(at_fd); - - opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - if (fsync(opened_fd) < 0) - return -errno; - - return 0; - } - - opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW); - if (opened_fd < 0) - return -errno; - - return fsync_directory_of_file(opened_fd); -} - -int fsync_path_and_parent_at(int at_fd, const char *path) { - _cleanup_close_ int opened_fd = -1; - - if (isempty(path)) { - if (at_fd != AT_FDCWD) - return fsync_full(at_fd); - - opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - } else - opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); - if (opened_fd < 0) - return -errno; - - return fsync_full(opened_fd); -} - -int syncfs_path(int atfd, const char *path) { - _cleanup_close_ int fd = -1; - - if (isempty(path)) { - if (atfd != AT_FDCWD) - return syncfs(atfd) < 0 ? -errno : 0; - - fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); - } else - fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - if (syncfs(fd) < 0) - return -errno; - - return 0; -} - int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd, r; diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index d612cbe404..f8a7657a07 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -67,44 +66,6 @@ int var_tmp_dir(const char **ret); int unlink_or_warn(const char *filename); -#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) - -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) - -union inotify_event_buffer { - struct inotify_event ev; - uint8_t raw[INOTIFY_EVENT_MAX]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); -int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask); - -enum { - CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */ - CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */ - CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */ - CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */ - CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's - * right-most component refers to symlink, return O_PATH fd of the symlink. */ - CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered */ -}; - -bool unsafe_transition(const struct stat *a, const struct stat *b); - -/* How many iterations to execute before returning -ELOOP */ -#define CHASE_SYMLINKS_MAX 32 - -int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret_path, int *ret_fd); - -int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path); -int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir); -int chase_symlinks_and_stat(const char *path, const char *root, unsigned chase_flags, char **ret_path, struct stat *ret_stat, int *ret_fd); - /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ static inline char *rmdir_and_free(char *p) { PROTECT_ERRNO; @@ -137,14 +98,6 @@ typedef enum UnlinkDeallocateFlags { int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags); -int fsync_directory_of_file(int fd); -int fsync_full(int fd); -int fsync_path_at(int at_fd, const char *path); -int fsync_parent_at(int at_fd, const char *path); -int fsync_path_and_parent_at(int at_fd, const char *path); - -int syncfs_path(int atfd, const char *path); - int open_parent(const char *path, int flags, mode_t mode); int conservative_renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath); diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index 0decbb04e1..b51d70bc87 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -401,7 +401,7 @@ static struct hashmap_base_entry* bucket_at_virtual(HashmapBase *h, struct swap_ if (idx < _IDX_SWAP_END) return &bucket_at_swap(swap, idx)->p.b; - assert_not_reached("Invalid index"); + assert_not_reached(); } static dib_raw_t* dib_raw_ptr(HashmapBase *h) { @@ -513,7 +513,7 @@ static void* entry_value(HashmapBase *h, struct hashmap_base_entry *e) { return (void*) e->key; default: - assert_not_reached("Unknown hashmap type"); + assert_not_reached(); } } @@ -1747,7 +1747,7 @@ HashmapBase* _hashmap_copy(HashmapBase *h HASHMAP_DEBUG_PARAMS) { r = set_merge((Set*)copy, (Set*)h); break; default: - assert_not_reached("Unknown hashmap type"); + assert_not_reached(); } if (r < 0) diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index 29d69910d2..1d0640e075 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -36,66 +36,39 @@ char* get_default_hostname(void) { return strdup(FALLBACK_HOSTNAME); } -char* gethostname_malloc(void) { +int gethostname_full(GetHostnameFlags flags, char **ret) { + _cleanup_free_ char *buf = NULL, *fallback = NULL; struct utsname u; const char *s; - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ + assert(ret); assert_se(uname(&u) >= 0); s = u.nodename; - if (isempty(s) || streq(s, "(none)")) - return get_default_hostname(); + if (isempty(s) || + (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_NONE) && streq(s, "(none)")) || + (!FLAGS_SET(flags, GET_HOSTNAME_ALLOW_LOCALHOST) && is_localhost(s)) || + (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.')) { + if (!FLAGS_SET(flags, GET_HOSTNAME_FALLBACK_DEFAULT)) + return -ENXIO; - return strdup(s); -} - -char* gethostname_short_malloc(void) { - struct utsname u; - const char *s; - _cleanup_free_ char *f = NULL; - - /* Like above, but kills the FQDN part if present. */ - - assert_se(uname(&u) >= 0); - - s = u.nodename; - if (isempty(s) || streq(s, "(none)") || s[0] == '.') { - s = f = get_default_hostname(); + s = fallback = get_default_hostname(); if (!s) - return NULL; + return -ENOMEM; - assert(s[0] != '.'); + if (FLAGS_SET(flags, GET_HOSTNAME_SHORT) && s[0] == '.') + return -ENXIO; } - return strndup(s, strcspn(s, ".")); -} - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) + if (FLAGS_SET(flags, GET_HOSTNAME_SHORT)) + buf = strndup(s, strcspn(s, ".")); + else + buf = strdup(s); + if (!buf) return -ENOMEM; - *ret = k; + *ret = TAKE_PTR(buf); return 0; } diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.h b/src/libnm-systemd-shared/src/basic/hostname-util.h index c3fc6752cb..0d1574db9e 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -4,13 +4,41 @@ #include #include +#include "env-file.h" #include "macro.h" #include "strv.h" +typedef enum GetHostnameFlags { + GET_HOSTNAME_ALLOW_NONE = 1 << 0, /* accepts "(none)". */ + GET_HOSTNAME_ALLOW_LOCALHOST = 1 << 1, /* accepts "localhost" or friends. */ + GET_HOSTNAME_FALLBACK_DEFAULT = 1 << 2, /* use default hostname if no hostname is set. */ + GET_HOSTNAME_SHORT = 1 << 3, /* kills the FQDN part if present. */ +} GetHostnameFlags; + +int gethostname_full(GetHostnameFlags flags, char **ret); +static inline int gethostname_strict(char **ret) { + return gethostname_full(0, ret); +} + +static inline char* gethostname_malloc(void) { + char *s; + + if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT, &s) < 0) + return NULL; + + return s; +} + +static inline char* gethostname_short_malloc(void) { + char *s; + + if (gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST | GET_HOSTNAME_FALLBACK_DEFAULT | GET_HOSTNAME_SHORT, &s) < 0) + return NULL; + + return s; +} + char* get_default_hostname(void); -char* gethostname_malloc(void); -char* gethostname_short_malloc(void); -int gethostname_strict(char **ret); bool valid_ldh_char(char c) _const_; @@ -33,3 +61,7 @@ static inline bool is_outbound_hostname(const char *hostname) { /* This tries to identify the valid syntaxes for the our synthetic "outbound" host. */ return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } + +static inline int get_pretty_hostname(char **ret) { + return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret); +} diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.c b/src/libnm-systemd-shared/src/basic/in-addr-util.c index 9cc92a1c76..3d392afc08 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -794,7 +794,7 @@ int in_addr_prefix_from_string_auto_internal( k = 0; break; default: - assert_not_reached("Invalid prefixlen mode"); + assert_not_reached(); } if (ret_family) @@ -831,35 +831,6 @@ static int in_addr_data_compare_func(const struct in_addr_data *x, const struct DEFINE_HASH_OPS(in_addr_data_hash_ops, struct in_addr_data, in_addr_data_hash_func, in_addr_data_compare_func); -static void in_addr_prefix_hash_func(const struct in_addr_prefix *a, struct siphash *state) { - assert(a); - assert(state); - - siphash24_compress(&a->family, sizeof(a->family), state); - siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); - siphash24_compress(&a->address, FAMILY_ADDRESS_SIZE(a->family), state); -} - -static int in_addr_prefix_compare_func(const struct in_addr_prefix *x, const struct in_addr_prefix *y) { - int r; - - assert(x); - assert(y); - - r = CMP(x->family, y->family); - if (r != 0) - return r; - - r = CMP(x->prefixlen, y->prefixlen); - if (r != 0) - return r; - - return memcmp(&x->address, &y->address, FAMILY_ADDRESS_SIZE(x->family)); -} - -DEFINE_HASH_OPS(in_addr_prefix_hash_ops, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func); -DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(in_addr_prefix_hash_ops_free, struct in_addr_prefix, in_addr_prefix_hash_func, in_addr_prefix_compare_func, free); - void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state) { assert(addr); assert(state); diff --git a/src/libnm-systemd-shared/src/basic/in-addr-util.h b/src/libnm-systemd-shared/src/basic/in-addr-util.h index f3ead91962..c74b0d512b 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -20,12 +20,6 @@ struct in_addr_data { union in_addr_union address; }; -struct in_addr_prefix { - int family; - uint8_t prefixlen; - union in_addr_union address; -}; - bool in4_addr_is_null(const struct in_addr *a); static inline bool in4_addr_is_set(const struct in_addr *a) { return !in4_addr_is_null(a); @@ -124,8 +118,6 @@ void in6_addr_hash_func(const struct in6_addr *addr, struct siphash *state); int in6_addr_compare_func(const struct in6_addr *a, const struct in6_addr *b); extern const struct hash_ops in_addr_data_hash_ops; -extern const struct hash_ops in_addr_prefix_hash_ops; -extern const struct hash_ops in_addr_prefix_hash_ops_free; extern const struct hash_ops in6_addr_hash_ops; #define IPV4_ADDRESS_FMT_STR "%u.%u.%u.%u" diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.c b/src/libnm-systemd-shared/src/basic/inotify-util.c new file mode 100644 index 0000000000..848f8590fa --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/inotify-util.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "inotify-util.h" + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + int wd; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + wd = inotify_add_watch(fd, FORMAT_PROC_FD_PATH(what), mask); + if (wd < 0) + return -errno; + + return wd; +} + +int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask) { + int wd; + + wd = inotify_add_watch(fd, pathname, mask); + if (wd < 0) { + if (errno == ENOSPC) + return log_error_errno(errno, "Failed to add a watch for %s: inotify watch limit reached", pathname); + + return log_error_errno(errno, "Failed to add a watch for %s: %m", pathname); + } + + return wd; +} diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h new file mode 100644 index 0000000000..88af08688f --- /dev/null +++ b/src/libnm-systemd-shared/src/basic/inotify-util.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include +#include + +#define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + for ((e) = &buffer.ev; \ + (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ + (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) + +union inotify_event_buffer { + struct inotify_event ev; + uint8_t raw[INOTIFY_EVENT_MAX]; +}; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); +int inotify_add_watch_and_warn(int fd, const char *pathname, uint32_t mask); diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index 256b7187c2..e488fff9f0 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -142,11 +142,8 @@ #define LIST_FOREACH_SAFE(name,i,n,head) \ for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#define LIST_FOREACH_AFTER(name,i,p) \ - for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) +#define LIST_FOREACH_BACKWARDS(name,i,p) \ + for ((i) = (p); (i); (i) = (i)->name##_prev) /* Iterate through all the members of the list p is included in, but skip over p */ #define LIST_FOREACH_OTHERS(name,i,p) \ diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 738c181070..b34bdffd1b 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -32,6 +32,15 @@ typedef enum LogTarget{ #define IS_SYNTHETIC_ERRNO(val) ((val) >> 30 & 1) #define ERRNO_VALUE(val) (abs(val) & 255) +/* The callback function to be invoked when syntax warnings are seen + * in the unit files. */ +typedef void (*log_syntax_callback_t)(const char *unit, int level, void *userdata); +void set_log_syntax_callback(log_syntax_callback_t cb, void *userdata); + +static inline void clear_log_syntax_callback(dummy_t *dummy) { + set_log_syntax_callback(/* cb= */ NULL, /* userdata= */ NULL); +} + const char *log_target_to_string(LogTarget target) _const_; LogTarget log_target_from_string(const char *s) _pure_; void log_set_target(LogTarget target); @@ -174,7 +183,6 @@ _noreturn_ void log_assert_failed( const char *func); _noreturn_ void log_assert_failed_unreachable( - const char *text, const char *file, int line, const char *func); diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index a8476184c2..6977a1ddd9 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -20,31 +20,14 @@ #define _sentinel_ __attribute__((__sentinel__)) #define _destructor_ __attribute__((__destructor__)) #define _deprecated_ __attribute__((__deprecated__)) -#define _packed_ __attribute__((__packed__)) #define _malloc_ __attribute__((__malloc__)) #define _weak_ __attribute__((__weak__)) -#define _likely_(x) (__builtin_expect(!!(x), 1)) -#define _unlikely_(x) (__builtin_expect(!!(x), 0)) #define _public_ __attribute__((__visibility__("default"))) #define _hidden_ __attribute__((__visibility__("hidden"))) #define _weakref_(x) __attribute__((__weakref__(#x))) #define _alignas_(x) __attribute__((__aligned__(__alignof(x)))) #define _alignptr_ __attribute__((__aligned__(sizeof(void*)))) #define _warn_unused_result_ __attribute__((__warn_unused_result__)) -#if __GNUC__ >= 7 -#define _fallthrough_ __attribute__((__fallthrough__)) -#else -#define _fallthrough_ -#endif -/* Define C11 noreturn without and even on older gcc - * compiler versions */ -#ifndef _noreturn_ -#if __STDC_VERSION__ >= 201112L -#define _noreturn_ _Noreturn -#else -#define _noreturn_ __attribute__((__noreturn__)) -#endif -#endif #if !defined(HAS_FEATURE_MEMORY_SANITIZER) # if defined(__has_feature) @@ -163,6 +146,20 @@ #define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) static inline size_t ALIGN_TO(size_t l, size_t ali) { + /* Check that alignment is exponent of 2 */ +#if SIZE_MAX == UINT_MAX + assert(__builtin_popcount(ali) == 1); +#elif SIZE_MAX == ULONG_MAX + assert(__builtin_popcountl(ali) == 1); +#elif SIZE_MAX == ULONGLONG_MAX + assert(__builtin_popcountll(ali) == 1); +#else +#error "Unexpected size_t" +#endif + + if (l > SIZE_MAX - (ali - 1)) + return SIZE_MAX; /* indicate overflow */ + return ((l + ali - 1) & ~(ali - 1)); } @@ -209,13 +206,6 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) { return m; } -/* - * STRLEN - return the length of a string literal, minus the trailing NUL byte. - * Contrary to strlen(), this is a constant expression. - * @x: a string literal. - */ -#define STRLEN(x) ((unsigned) sizeof(""x"") - 1) - /* * container_of - cast a member of a structure out to the containing structure * @ptr: the pointer to the member. @@ -282,8 +272,8 @@ static inline int __coverity_check_and_return__(int condition) { #define assert(expr) assert_message_se(expr, #expr) #endif -#define assert_not_reached(t) \ - log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__) +#define assert_not_reached() \ + log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__) #define assert_return(expr, r) \ do { \ @@ -345,12 +335,12 @@ static inline int __coverity_check_and_return__(int condition) { (2U+(sizeof(type) <= 1 ? 3U : \ sizeof(type) <= 2 ? 5U : \ sizeof(type) <= 4 ? 10U : \ - sizeof(type) <= 8 ? 20U : (unsigned) sizeof(int[-2*(sizeof(type) > 8)]))) + sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)]))) #define DECIMAL_STR_WIDTH(x) \ ({ \ typeof(x) _x_ = (x); \ - unsigned ans = 1; \ + size_t ans = 1; \ while ((_x_ /= 10) != 0) \ ans++; \ ans; \ @@ -483,4 +473,10 @@ static inline size_t size_add(size_t x, size_t y) { return y >= SIZE_MAX - x ? SIZE_MAX : x + y; } +typedef struct { + int _empty[0]; +} dummy_t; + +assert_cc(sizeof(dummy_t) == 0); + #include "log.h" diff --git a/src/libnm-systemd-shared/src/basic/memory-util.c b/src/libnm-systemd-shared/src/basic/memory-util.c index 3338e355f7..2983762117 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.c +++ b/src/libnm-systemd-shared/src/basic/memory-util.c @@ -18,26 +18,25 @@ size_t page_size(void) { return pgsz; } -bool memeqzero(const void *data, size_t length) { - /* Does the buffer consist entirely of NULs? +bool memeqbyte(uint8_t byte, const void *data, size_t length) { + /* Does the buffer consist entirely of the same specific byte value? * Copied from https://github.com/systemd/casync/, copied in turn from * https://github.com/rustyrussell/ccan/blob/master/ccan/mem/mem.c#L92, * which is licensed CC-0. */ const uint8_t *p = data; - size_t i; /* Check first 16 bytes manually */ - for (i = 0; i < 16; i++, length--) { + for (size_t i = 0; i < 16; i++, length--) { if (length == 0) return true; - if (p[i]) + if (p[i] != byte) return false; } - /* Now we know first 16 bytes are NUL, memcmp with self. */ - return memcmp(data, p + i, length) == 0; + /* Now we know first 16 bytes match, memcmp() with self. */ + return memcmp(data, p + 16, length) == 0; } #if !HAVE_EXPLICIT_BZERO diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h index 0b04278ab4..9f37431fc1 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.h +++ b/src/libnm-systemd-shared/src/basic/memory-util.h @@ -47,7 +47,9 @@ static inline int memcmp_nn(const void *s1, size_t n1, const void *s2, size_t n2 #define zero(x) (memzero(&(x), sizeof(x))) -bool memeqzero(const void *data, size_t length); +bool memeqbyte(uint8_t byte, const void *data, size_t length); + +#define memeqzero(data, length) memeqbyte(0x00, data, length) #define eqzero(x) memeqzero(x, sizeof(x)) @@ -71,6 +73,16 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } +static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { + const uint8_t *p; + + p = memmem_safe(haystack, haystacklen, needle, needlelen); + if (!p) + return NULL; + + return (uint8_t*) p + needlelen; +} + #if HAVE_EXPLICIT_BZERO static inline void* explicit_bzero_safe(void *p, size_t l) { if (l > 0) diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index 9e3a165857..57dae77b53 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -41,6 +41,26 @@ static inline int missing_pivot_root(const char *new_root, const char *put_old) /* ======================================================================= */ +#if !HAVE_IOPRIO_GET +static inline int missing_ioprio_get(int which, int who) { + return syscall(__NR_ioprio_get, which, who); +} + +# define ioprio_get missing_ioprio_get +#endif + +/* ======================================================================= */ + +#if !HAVE_IOPRIO_SET +static inline int missing_ioprio_set(int which, int who, int ioprio) { + return syscall(__NR_ioprio_set, which, who, ioprio); +} + +# define ioprio_set missing_ioprio_set +#endif + +/* ======================================================================= */ + #if !HAVE_MEMFD_CREATE static inline int missing_memfd_create(const char *name, unsigned int flags) { # ifdef __NR_memfd_create diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 70062eba2a..27b1b84aff 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -740,13 +740,13 @@ int parse_oom_score_adjust(const char *s, int *ret) { int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) { assert(ret); - if (i >= (~0UL << FSHIFT)) + if (i >= (~0UL << LOADAVG_PRECISION_BITS)) return -ERANGE; - i = i << FSHIFT; - f = DIV_ROUND_UP((f << FSHIFT), 100); + i = i << LOADAVG_PRECISION_BITS; + f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100); - if (f >= FIXED_1) + if (f >= LOADAVG_FIXED_POINT_1_0) return -ERANGE; *ret = i | f; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 908202dafd..3dc5e140c9 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -136,6 +135,14 @@ int parse_ip_prefix_length(const char *s, int *ret); int parse_oom_score_adjust(const char *s, int *ret); +/* Implement floating point using fixed integers, to improve performance when + * calculating load averages. These macros can be used to extract the integer + * and decimal parts of a value. */ +#define LOADAVG_PRECISION_BITS 11 +#define LOADAVG_FIXED_POINT_1_0 (1 << LOADAVG_PRECISION_BITS) +#define LOADAVG_INT_SIDE(x) ((x) >> LOADAVG_PRECISION_BITS) +#define LOADAVG_DECIMAL_SIDE(x) LOADAVG_INT_SIDE(((x) & (LOADAVG_FIXED_POINT_1_0 - 1)) * 100) + /* Given a Linux load average (e.g. decimal number 34.89 where 34 is passed as i and 89 is passed as f), convert it * to a loadavg_t. */ int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret); diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index 4aebb6541e..4c952d863c 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -13,6 +13,7 @@ #undef basename #include "alloc-util.h" +#include "chase-symlinks.h" #include "extract-word.h" #include "fd-util.h" #include "fs-util.h" @@ -630,7 +631,11 @@ static int check_x_access(const char *path, int *ret_fd) { return r; r = access_fd(fd, X_OK); - if (r < 0) + if (r == -ENOSYS) { + /* /proc is not mounted. Fallback to access(). */ + if (access(path, X_OK) < 0) + return -errno; + } else if (r < 0) return r; if (ret_fd) @@ -639,30 +644,53 @@ static int check_x_access(const char *path, int *ret_fd) { return 0; } -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) { - int last_error, r; +static int find_executable_impl(const char *name, const char *root, char **ret_filename, int *ret_fd) { + _cleanup_close_ int fd = -1; + _cleanup_free_ char *path_name = NULL; + int r; + + assert(name); + + /* Function chase_symlinks() is invoked only when root is not NULL, as using it regardless of + * root value would alter the behavior of existing callers for example: /bin/sleep would become + * /usr/bin/sleep when find_executables is called. Hence, this function should be invoked when + * needed to avoid unforeseen regression or other complicated changes. */ + if (root) { + r = chase_symlinks(name, + root, + CHASE_PREFIX_ROOT, + &path_name, + /* ret_fd= */ NULL); /* prefix root to name in case full paths are not specified */ + if (r < 0) + return r; + + name = path_name; + } + + r = check_x_access(name, ret_fd ? &fd : NULL); + if (r < 0) + return r; + + if (ret_filename) { + r = path_make_absolute_cwd(name, ret_filename); + if (r < 0) + return r; + } + + if (ret_fd) + *ret_fd = TAKE_FD(fd); + + return 0; +} + +int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) { + int last_error = -ENOENT, r = 0; const char *p = NULL; assert(name); - if (is_path(name)) { - _cleanup_close_ int fd = -1; - - r = check_x_access(name, ret_fd ? &fd : NULL); - if (r < 0) - return r; - - if (ret_filename) { - r = path_make_absolute_cwd(name, ret_filename); - if (r < 0) - return r; - } - - if (ret_fd) - *ret_fd = TAKE_FD(fd); - - return 0; - } + if (is_path(name)) + return find_executable_impl(name, root, ret_filename, ret_fd); if (use_path_envvar) /* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the @@ -671,12 +699,31 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file if (!p) p = DEFAULT_PATH; - last_error = -ENOENT; + if (exec_search_path) { + char **element; + + STRV_FOREACH(element, exec_search_path) { + _cleanup_free_ char *full_path = NULL; + if (!path_is_absolute(*element)) + continue; + full_path = path_join(*element, name); + if (!full_path) + return -ENOMEM; + + r = find_executable_impl(full_path, root, ret_filename, ret_fd); + if (r < 0) { + if (r != -EACCES) + last_error = r; + continue; + } + return 0; + } + return last_error; + } /* Resolve a single-component name to a full path */ for (;;) { _cleanup_free_ char *element = NULL; - _cleanup_close_ int fd = -1; r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) @@ -690,7 +737,7 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file if (!path_extend(&element, name)) return -ENOMEM; - r = check_x_access(element, ret_fd ? &fd : NULL); + r = find_executable_impl(element, root, ret_filename, ret_fd); if (r < 0) { /* PATH entries which we don't have access to are ignored, as per tradition. */ if (r != -EACCES) @@ -699,11 +746,6 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file } /* Found it! */ - if (ret_filename) - *ret_filename = path_simplify(TAKE_PTR(element)); - if (ret_fd) - *ret_fd = TAKE_FD(fd); - return 0; } diff --git a/src/libnm-systemd-shared/src/basic/path-util.h b/src/libnm-systemd-shared/src/basic/path-util.h index 26e7362d1f..518f3340bf 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.h +++ b/src/libnm-systemd-shared/src/basic/path-util.h @@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *root); char** path_strv_resolve_uniq(char **l, const char *root); -int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd); +int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd); static inline int find_executable(const char *name, char **ret_filename) { - return find_executable_full(name, true, ret_filename, NULL); + return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL); } bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); diff --git a/src/libnm-systemd-shared/src/basic/prioq.c b/src/libnm-systemd-shared/src/basic/prioq.c index 559e5d124d..c15dcb26af 100644 --- a/src/libnm-systemd-shared/src/basic/prioq.c +++ b/src/libnm-systemd-shared/src/basic/prioq.c @@ -173,6 +173,16 @@ int prioq_put(Prioq *q, void *data, unsigned *idx) { return 0; } +int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx) { + int r; + + r = prioq_ensure_allocated(q, compare_func); + if (r < 0) + return r; + + return prioq_put(*q, data, idx); +} + static void remove_item(Prioq *q, struct prioq_item *i) { struct prioq_item *l; diff --git a/src/libnm-systemd-shared/src/basic/prioq.h b/src/libnm-systemd-shared/src/basic/prioq.h index 7c76647611..508db88026 100644 --- a/src/libnm-systemd-shared/src/basic/prioq.h +++ b/src/libnm-systemd-shared/src/basic/prioq.h @@ -16,6 +16,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Prioq*, prioq_free); int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); int prioq_put(Prioq *q, void *data, unsigned *idx); +int prioq_ensure_put(Prioq **q, compare_func_t compare_func, void *data, unsigned *idx); int prioq_remove(Prioq *q, void *data, unsigned *idx); int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index ecb14306f8..5e7ed06ea5 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -27,7 +27,6 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "ioprio.h" #include "locale-util.h" #include "log.h" #include "macro.h" @@ -1254,7 +1253,7 @@ int safe_fork_full( pid_t original_pid, pid; sigset_t saved_ss, ss; - _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; + _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; bool block_signals = false, block_all = false; int prio, r; @@ -1503,6 +1502,24 @@ int set_oom_score_adjust(int value) { WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); } +int get_oom_score_adjust(int *ret) { + _cleanup_free_ char *t; + int r, a; + + r = read_virtual_file("/proc/self/oom_score_adj", SIZE_MAX, &t, NULL); + if (r < 0) + return r; + + delete_trailing_chars(t, WHITESPACE); + + assert_se(safe_atoi(t, &a) >= 0); + assert_se(oom_score_adjust_is_valid(a)); + + if (ret) + *ret = a; + return 0; +} + int pidfd_get_pid(int fd, pid_t *ret) { char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *fdinfo = NULL; diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index c953649502..7e87f5a17c 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -13,8 +13,8 @@ #include "alloc-util.h" #include "format-util.h" -#include "ioprio.h" #include "macro.h" +#include "missing_ioprio.h" #include "time-util.h" #define procfs_file_alloca(pid, field) \ @@ -175,6 +175,7 @@ static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { int namespace_fork(const char *outer_name, const char *inner_name, int except_fds[], size_t n_except_fds, ForkFlags flags, int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd, pid_t *ret_pid); int set_oom_score_adjust(int value); +int get_oom_score_adjust(int *ret); /* The highest possibly (theoretic) pid_t value on this architecture. */ #define PID_T_MAX ((pid_t) INT32_MAX) diff --git a/src/libnm-systemd-shared/src/basic/set.h b/src/libnm-systemd-shared/src/basic/set.h index 0f8673934f..5cae13160b 100644 --- a/src/libnm-systemd-shared/src/basic/set.h +++ b/src/libnm-systemd-shared/src/basic/set.h @@ -26,7 +26,7 @@ static inline Set* set_free_free(Set *s) { /* no set_free_free_free */ -#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h) HASHMAP_DEBUG_SRC_ARGS)) +#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(s) HASHMAP_DEBUG_SRC_ARGS)) int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) diff --git a/src/libnm-systemd-shared/src/basic/signal-util.c b/src/libnm-systemd-shared/src/basic/signal-util.c index b06b5ce774..34b2b27918 100644 --- a/src/libnm-systemd-shared/src/basic/signal-util.c +++ b/src/libnm-systemd-shared/src/basic/signal-util.c @@ -265,7 +265,7 @@ int pop_pending_signal_internal(int sig, ...) { if (sigemptyset(&ss) < 0) return -errno; - /* Add first signal (if the signal is zero, we'll silently skip it, to make it easiert to build + /* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles * this.) */ if (sig > 0 && sigaddset(&ss, sig) < 0) diff --git a/src/libnm-systemd-shared/src/basic/socket-util.c b/src/libnm-systemd-shared/src/basic/socket-util.c index 1c353e86b0..1e66f8700b 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.c +++ b/src/libnm-systemd-shared/src/basic/socket-util.c @@ -455,23 +455,23 @@ int sockaddr_pretty( if (r < 0) return -ENOMEM; } else { - char a[INET6_ADDRSTRLEN], ifname[IF_NAMESIZE + 1]; + char a[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - if (sa->in6.sin6_scope_id != 0) - format_ifname_full(sa->in6.sin6_scope_id, ifname, FORMAT_IFNAME_IFINDEX); if (include_port) { - r = asprintf(&p, + if (asprintf(&p, "[%s]:%u%s%s", a, be16toh(sa->in6.sin6_port), sa->in6.sin6_scope_id != 0 ? "%" : "", - sa->in6.sin6_scope_id != 0 ? ifname : ""); - if (r < 0) + FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)) < 0) return -ENOMEM; } else { - p = sa->in6.sin6_scope_id != 0 ? strjoin(a, "%", ifname) : strdup(a); + if (sa->in6.sin6_scope_id != 0) + p = strjoin(a, "%", FORMAT_IFNAME_FULL(sa->in6.sin6_scope_id, FORMAT_IFNAME_IFINDEX)); + else + p = strdup(a); if (!p) return -ENOMEM; } @@ -793,7 +793,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { /* Let's refuse "all" and "default" as interface name, to avoid collisions with the special sysctl * directories /proc/sys/net/{ipv4,ipv6}/conf/{all,default} */ - if (STR_IN_SET(p, "all", "default")) + if (!FLAGS_SET(flags, IFNAME_VALID_SPECIAL) && STR_IN_SET(p, "all", "default")) return false; for (const char *t = p; *t; t++) { @@ -921,7 +921,7 @@ int getpeergroups(int fd, gid_t **ret) { ssize_t send_one_fd_iov_sa( int transport_fd, int fd, - struct iovec *iov, size_t iovlen, + const struct iovec *iov, size_t iovlen, const struct sockaddr *sa, socklen_t len, int flags) { @@ -929,7 +929,7 @@ ssize_t send_one_fd_iov_sa( struct msghdr mh = { .msg_name = (struct sockaddr*) sa, .msg_namelen = len, - .msg_iov = iov, + .msg_iov = (struct iovec *)iov, .msg_iovlen = iovlen, }; ssize_t k; @@ -1233,7 +1233,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) { } int socket_bind_to_ifindex(int fd, int ifindex) { - char ifname[IF_NAMESIZE + 1]; + char ifname[IF_NAMESIZE]; int r; assert(fd >= 0); @@ -1251,8 +1251,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) { return r; /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ - if (!format_ifname(ifindex, ifname)) - return -errno; + r = format_ifname(ifindex, ifname); + if (r < 0) + return r; return socket_bind_to_ifname(fd, ifname); } diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index f92e425fd6..cb4a92236f 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -135,9 +135,10 @@ int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); typedef enum { - IFNAME_VALID_ALTERNATIVE = 1 << 0, - IFNAME_VALID_NUMERIC = 1 << 1, - _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, + IFNAME_VALID_ALTERNATIVE = 1 << 0, /* Allow "altnames" too */ + IFNAME_VALID_NUMERIC = 1 << 1, /* Allow decimal formatted ifindexes too */ + IFNAME_VALID_SPECIAL = 1 << 2, /* Allow the special names "all" and "default" */ + _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC | IFNAME_VALID_SPECIAL, } IfnameValidFlags; bool ifname_valid_char(char a); bool ifname_valid_full(const char *p, IfnameValidFlags flags); @@ -153,7 +154,7 @@ int getpeergroups(int fd, gid_t **ret); ssize_t send_one_fd_iov_sa( int transport_fd, int fd, - struct iovec *iov, size_t iovlen, + const struct iovec *iov, size_t iovlen, const struct sockaddr *sa, socklen_t len, int flags); int send_one_fd_sa(int transport_fd, @@ -245,7 +246,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng _len = sizeof(struct sockaddr_vm); \ break; \ default: \ - assert_not_reached("invalid socket family"); \ + assert_not_reached(); \ } \ _len; \ }) @@ -277,6 +278,28 @@ static inline int getsockopt_int(int fd, int level, int optname, int *ret) { int socket_bind_to_ifname(int fd, const char *ifname); int socket_bind_to_ifindex(int fd, int ifindex); +/* Define a 64bit version of timeval/timespec in any case, even on 32bit userspace. */ +struct timeval_large { + uint64_t tvl_sec, tvl_usec; +}; +struct timespec_large { + uint64_t tvl_sec, tvl_nsec; +}; + +/* glibc duplicates timespec/timeval on certain 32bit archs, once in 32bit and once in 64bit. + * See __convert_scm_timestamps() in glibc source code. Hence, we need additional buffer space for them + * to prevent from recvmsg_safe() returning -EXFULL. */ +#define CMSG_SPACE_TIMEVAL \ + ((sizeof(struct timeval) == sizeof(struct timeval_large)) ? \ + CMSG_SPACE(sizeof(struct timeval)) : \ + CMSG_SPACE(sizeof(struct timeval)) + \ + CMSG_SPACE(sizeof(struct timeval_large))) +#define CMSG_SPACE_TIMESPEC \ + ((sizeof(struct timespec) == sizeof(struct timespec_large)) ? \ + CMSG_SPACE(sizeof(struct timespec)) : \ + CMSG_SPACE(sizeof(struct timespec)) + \ + CMSG_SPACE(sizeof(struct timespec_large))) + ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags); int socket_get_family(int fd, int *ret); diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 72a7e4a48b..45864e9e62 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -8,10 +8,11 @@ #include #include "alloc-util.h" +#include "chase-symlinks.h" #include "dirent-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "macro.h" #include "missing_fs.h" #include "missing_magic.h" diff --git a/src/libnm-systemd-shared/src/basic/stdio-util.h b/src/libnm-systemd-shared/src/basic/stdio-util.h index 6dc1e72312..69d7062ec6 100644 --- a/src/libnm-systemd-shared/src/basic/stdio-util.h +++ b/src/libnm-systemd-shared/src/basic/stdio-util.h @@ -9,8 +9,13 @@ #include "macro.h" #include "memory-util.h" -#define snprintf_ok(buf, len, fmt, ...) \ - ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) +#define snprintf_ok(buf, len, fmt, ...) \ + ({ \ + char *_buf = (buf); \ + size_t _len = (len); \ + int _snpf = snprintf(_buf, _len, (fmt), __VA_ARGS__); \ + _snpf >= 0 && (size_t) _snpf < _len ? _buf : NULL; \ + }) #define xsprintf(buf, fmt, ...) \ assert_message_se(snprintf_ok(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__), "xsprintf: " #buf "[] must be big enough") @@ -58,7 +63,7 @@ do { \ (void) va_arg(ap, long double); \ break; \ default: \ - assert_not_reached("Unknown format string argument."); \ + assert_not_reached(); \ } \ } \ } while (false) diff --git a/src/libnm-systemd-shared/src/basic/string-util.c b/src/libnm-systemd-shared/src/basic/string-util.c index a645958d38..6ceaeaf9df 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.c +++ b/src/libnm-systemd-shared/src/basic/string-util.c @@ -1146,3 +1146,19 @@ int string_contains_word_strv(const char *string, const char *separators, char * *ret_word = found; return !!found; } + +bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) { + if (!s1 && !s2) + return true; + if (!s1 || !s2) + return false; + + if (!ok) + ok = WHITESPACE; + + for (; *s1 && *s2; s1++, s2++) + if (*s1 != *s2) + break; + + return in_charset(s1, ok) && in_charset(s2, ok); +} diff --git a/src/libnm-systemd-shared/src/basic/string-util.h b/src/libnm-systemd-shared/src/basic/string-util.h index 9155e50ba8..775d694c67 100644 --- a/src/libnm-systemd-shared/src/basic/string-util.h +++ b/src/libnm-systemd-shared/src/basic/string-util.h @@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) { } REENABLE_WARNING; -/* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - assert(token); - - size_t n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - if (memcmp(p, token, n) != 0) - return NULL; - - return (uint8_t*) p + n; -} - /* Like startswith_no_case(), but operates on arbitrary memory blocks. * It works only for ASCII strings. */ @@ -242,3 +226,5 @@ int string_contains_word_strv(const char *string, const char *separators, char * static inline int string_contains_word(const char *string, const char *separators, const char *word) { return string_contains_word_strv(string, separators, STRV_MAKE(word), NULL); } + +bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok); diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 3adf3c5c7e..005f30a016 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -300,6 +300,24 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla return (int) n; } +int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags) { + _cleanup_strv_free_ char **l = NULL; + int r; + + assert(t); + assert(s); + + r = strv_split_full(&l, s, separators, flags); + if (r < 0) + return r; + + r = strv_extend_strv(t, l, filter_duplicates); + if (r < 0) + return r; + + return (int) strv_length(*t); +} + int strv_split_colon_pairs(char ***t, const char *s) { _cleanup_strv_free_ char **l = NULL; size_t n = 0; diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 911528fab4..a56ef94139 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -83,6 +83,9 @@ static inline char **strv_split(const char *s, const char *separators) { return ret; } +int strv_split_and_extend_full(char ***t, const char *s, const char *separators, bool filter_duplicates, ExtractFlags flags); +#define strv_split_and_extend(t, s, sep, dup) strv_split_and_extend_full(t, s, sep, dup, 0) + int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags); static inline char **strv_split_newlines(const char *s) { char **ret; @@ -233,9 +236,11 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space); #define strv_free_and_replace(a, b) \ ({ \ - strv_free(a); \ - (a) = (b); \ - (b) = NULL; \ + char ***_a = &(a); \ + char ***_b = &(b); \ + strv_free(*_a); \ + (*_a) = (*_b); \ + (*_b) = NULL; \ 0; \ }) diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index c3b175a192..f4022f7c86 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -433,62 +433,62 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) { usec_t years = d / USEC_PER_YEAR; usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH; - snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", - years, - years == 1 ? "year" : "years", - months, - months == 1 ? "month" : "months", - s); + (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", + years, + years == 1 ? "year" : "years", + months, + months == 1 ? "month" : "months", + s); } else if (d >= USEC_PER_MONTH) { usec_t months = d / USEC_PER_MONTH; usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY; - snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", - months, - months == 1 ? "month" : "months", - days, - days == 1 ? "day" : "days", - s); + (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", + months, + months == 1 ? "month" : "months", + days, + days == 1 ? "day" : "days", + s); } else if (d >= USEC_PER_WEEK) { usec_t weeks = d / USEC_PER_WEEK; usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY; - snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", - weeks, - weeks == 1 ? "week" : "weeks", - days, - days == 1 ? "day" : "days", - s); + (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s %s", + weeks, + weeks == 1 ? "week" : "weeks", + days, + days == 1 ? "day" : "days", + s); } else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); + (void) snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); + (void) snprintf(buf, l, "1 day " USEC_FMT "h %s", + (d - USEC_PER_DAY) / USEC_PER_HOUR, s); else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); + (void) snprintf(buf, l, USEC_FMT "h %s", + d / USEC_PER_HOUR, s); else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); + (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", + d / USEC_PER_HOUR, + (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); + (void) snprintf(buf, l, USEC_FMT "min %s", + d / USEC_PER_MINUTE, s); else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); + (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", + d / USEC_PER_MINUTE, + (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); + (void) snprintf(buf, l, USEC_FMT "s %s", + d / USEC_PER_SEC, s); else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); + (void) snprintf(buf, l, USEC_FMT "ms %s", + d / USEC_PER_MSEC, s); else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); + (void) snprintf(buf, l, USEC_FMT"us %s", + d, s); else - snprintf(buf, l, "now"); + (void) snprintf(buf, l, "now"); buf[l-1] = 0; return buf; diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index 91c8ff1725..36930cb34b 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -300,11 +300,7 @@ int link_tmpfile(int fd, const char *path, const char *target) { if (r < 0) return r; } else { - char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + if (linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) return -errno; } diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index 967518600d..20d8dabf29 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -11,9 +11,26 @@ #define _const_ __attribute__((__const__)) #define _pure_ __attribute__((__pure__)) #define _section_(x) __attribute__((__section__(x))) +#define _packed_ __attribute__((__packed__)) #define _used_ __attribute__((__used__)) #define _unused_ __attribute__((__unused__)) #define _cleanup_(x) __attribute__((__cleanup__(x))) +#define _likely_(x) (__builtin_expect(!!(x), 1)) +#define _unlikely_(x) (__builtin_expect(!!(x), 0)) +#if __GNUC__ >= 7 +#define _fallthrough_ __attribute__((__fallthrough__)) +#else +#define _fallthrough_ +#endif +/* Define C11 noreturn without and even on older gcc + * compiler versions */ +#ifndef _noreturn_ +#if __STDC_VERSION__ >= 201112L +#define _noreturn_ _Noreturn +#else +#define _noreturn_ __attribute__((__noreturn__)) +#endif +#endif #define XSTRINGIFY(x) #x #define STRINGIFY(x) XSTRINGIFY(x) @@ -34,7 +51,14 @@ #define CONCATENATE(x, y) XCONCATENATE(x, y) #ifdef SD_BOOT -#define assert(expr) do {} while (false) + #ifdef NDEBUG + #define assert(expr) + #define assert_not_reached() __builtin_unreachable() + #else + void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; + #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) + #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) + #endif #endif #if defined(static_assert) @@ -70,12 +94,29 @@ UNIQ_T(A, aq) > UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \ }) -/* evaluates to (void) if _A or _B are not constant or of different types */ +#define IS_UNSIGNED_INTEGER_TYPE(type) \ + (__builtin_types_compatible_p(typeof(type), unsigned char) || \ + __builtin_types_compatible_p(typeof(type), unsigned short) || \ + __builtin_types_compatible_p(typeof(type), unsigned) || \ + __builtin_types_compatible_p(typeof(type), unsigned long) || \ + __builtin_types_compatible_p(typeof(type), unsigned long long)) + +#define IS_SIGNED_INTEGER_TYPE(type) \ + (__builtin_types_compatible_p(typeof(type), signed char) || \ + __builtin_types_compatible_p(typeof(type), signed short) || \ + __builtin_types_compatible_p(typeof(type), signed) || \ + __builtin_types_compatible_p(typeof(type), signed long) || \ + __builtin_types_compatible_p(typeof(type), signed long long)) + +/* Evaluates to (void) if _A or _B are not constant or of different types (being integers of different sizes + * is also OK as long as the signedness matches) */ #define CONST_MAX(_A, _B) \ (__builtin_choose_expr( \ __builtin_constant_p(_A) && \ __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ + (__builtin_types_compatible_p(typeof(_A), typeof(_B)) || \ + (IS_UNSIGNED_INTEGER_TYPE(_A) && IS_UNSIGNED_INTEGER_TYPE(_B)) || \ + (IS_SIGNED_INTEGER_TYPE(_A) && IS_SIGNED_INTEGER_TYPE(_B))), \ ((_A) > (_B)) ? (_A) : (_B), \ VOID_0)) @@ -216,3 +257,10 @@ (ptr) = NULL; \ _ptr_; \ }) + +/* + * STRLEN - return the length of a string literal, minus the trailing NUL byte. + * Contrary to strlen(), this is a constant expression. + * @x: a string literal. + */ +#define STRLEN(x) (sizeof(""x"") - sizeof(typeof(x[0]))) diff --git a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h index 407cede48d..7455c05492 100644 --- a/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/string-util-fundamental.h @@ -16,6 +16,7 @@ #define strncmp(a, b, n) StrnCmp((a), (b), (n)) #define strcasecmp(a, b) StriCmp((a), (b)) #define STR_C(str) (L ## str) +#define memcmp(a, b, n) CompareMem(a, b, n) #else #define STR_C(str) (str) #endif @@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) { } sd_int strverscmp_improved(const sd_char *a, const sd_char *b); + +/* Like startswith(), but operates on arbitrary memory blocks */ +static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) { + assert(token); + + sd_size_t n = strlen(token) * sizeof(sd_char); + if (sz < n) + return NULL; + + assert(p); + + if (memcmp(p, token, n) != 0) + return NULL; + + return (uint8_t*) p + n; +} diff --git a/src/libnm-systemd-shared/src/shared/log-link.h b/src/libnm-systemd-shared/src/shared/log-link.h index 51eaa0c06e..5f2b176353 100644 --- a/src/libnm-systemd-shared/src/shared/log-link.h +++ b/src/libnm-systemd-shared/src/shared/log-link.h @@ -17,24 +17,6 @@ log_interface_full_errno_zerook(ifname, level, _error, __VA_ARGS__); \ }) -#define log_interface_prefix_full_errno_zerook(prefix, ifname_expr, error, fmt, ...) \ - ({ \ - int _e = (error); \ - if (DEBUG_LOGGING) \ - log_interface_full_errno_zerook( \ - ifname_expr, \ - LOG_DEBUG, _e, prefix fmt, \ - ##__VA_ARGS__); \ - -ERRNO_VALUE(_e); \ - }) - -#define log_interface_prefix_full_errno(prefix, ifname_expr, error, fmt, ...) \ - ({ \ - int _error = (error); \ - ASSERT_NON_ZERO(_error); \ - log_interface_prefix_full_errno_zerook(prefix, ifname_expr, _error, fmt, ##__VA_ARGS__); \ - }) - /* * The following macros append INTERFACE= to the message. * The macros require a struct named 'Link' which contains 'char *ifname': diff --git a/src/libnm-systemd-shared/src/shared/web-util.c b/src/libnm-systemd-shared/src/shared/web-util.c index 82cd5fbd6b..39a300f5c9 100644 --- a/src/libnm-systemd-shared/src/shared/web-util.c +++ b/src/libnm-systemd-shared/src/shared/web-util.c @@ -36,16 +36,29 @@ bool http_url_is_valid(const char *url) { return ascii_is_valid(p); } +bool file_url_is_valid(const char *url) { + const char *p; + + if (isempty(url)) + return false; + + p = startswith(url, "file:/"); + if (isempty(p)) + return false; + + return ascii_is_valid(p); +} + bool documentation_url_is_valid(const char *url) { const char *p; if (isempty(url)) return false; - if (http_url_is_valid(url)) + if (http_url_is_valid(url) || file_url_is_valid(url)) return true; - p = STARTSWITH_SET(url, "file:/", "info:", "man:"); + p = STARTSWITH_SET(url, "info:", "man:"); if (isempty(p)) return false; diff --git a/src/libnm-systemd-shared/src/shared/web-util.h b/src/libnm-systemd-shared/src/shared/web-util.h index ec54669f50..88b4897be0 100644 --- a/src/libnm-systemd-shared/src/shared/web-util.h +++ b/src/libnm-systemd-shared/src/shared/web-util.h @@ -6,6 +6,7 @@ #include "macro.h" bool http_url_is_valid(const char *url) _pure_; +bool file_url_is_valid(const char *url) _pure_; bool documentation_url_is_valid(const char *url) _pure_;