diff --git a/Makefile.am b/Makefile.am index 784a346ad4..2cf4fcd286 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2359,6 +2359,8 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \ src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h \ src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c \ src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c \ + src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h \ + src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c \ src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h \ src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c \ src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.h \ diff --git a/src/core/dhcp/nm-dhcp-systemd.c b/src/core/dhcp/nm-dhcp-systemd.c index 0884def35d..70f89a211c 100644 --- a/src/core/dhcp/nm-dhcp-systemd.c +++ b/src/core/dhcp/nm-dhcp-systemd.c @@ -81,16 +81,20 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, const struct in_addr *addr_list; char addr_str[NM_UTILS_INET_ADDRSTRLEN]; const char *s; - nm_auto_free_gstring GString *str = NULL; - nm_auto_free sd_dhcp_route **routes = NULL; - const char *const *search_domains = NULL; + nm_auto_free_gstring GString *str = NULL; + nm_auto_free sd_dhcp_route **routes_static = NULL; + nm_auto_free sd_dhcp_route **routes_classless = NULL; + const char *const *search_domains = NULL; + guint32 default_route_metric_offset; guint16 mtu; - int i, num; + int i; + int num; + int is_classless; + int n_routes_static; + int n_routes_classless; const void *data; gsize data_len; gboolean has_router_from_classless = FALSE; - gboolean has_classless_route = FALSE; - gboolean has_static_route = FALSE; const gint32 ts = nm_utils_get_monotonic_timestamp_sec(); gint64 ts_time = time(NULL); struct in_addr a_address; @@ -226,44 +230,27 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s); } - num = sd_dhcp_lease_get_routes(lease, &routes); - if (num > 0) { - nm_auto_free_gstring GString *str_classless = NULL; - nm_auto_free_gstring GString *str_static = NULL; - guint32 default_route_metric_offset = 0; + default_route_metric_offset = 0; + n_routes_static = sd_dhcp_lease_get_static_routes(lease, &routes_static); + n_routes_classless = sd_dhcp_lease_get_classless_routes(lease, &routes_classless); + for (is_classless = 1; is_classless >= 0; is_classless--) { + int n_routes = (is_classless ? n_routes_classless : n_routes_static); + sd_dhcp_route *const *routes = (is_classless ? routes_classless : routes_static); - for (i = 0; i < num; i++) { - switch (sd_dhcp_route_get_option(routes[i])) { - case NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE: - has_classless_route = TRUE; - break; - case NM_DHCP_OPTION_DHCP4_STATIC_ROUTE: - has_static_route = TRUE; - break; - } - } + if (n_routes <= 0) + continue; - if (has_classless_route) - str_classless = g_string_sized_new(30); - if (has_static_route) - str_static = g_string_sized_new(30); + nm_gstring_prepare(&str); - for (i = 0; i < num; i++) { + for (i = 0; i < n_routes; i++) { char network_net_str[NM_UTILS_INET_ADDRSTRLEN]; char gateway_str[NM_UTILS_INET_ADDRSTRLEN]; guint8 r_plen; struct in_addr r_network; struct in_addr r_gateway; in_addr_t network_net; - int option; guint32 m; - option = sd_dhcp_route_get_option(routes[i]); - if (!NM_IN_SET(option, - NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, - NM_DHCP_OPTION_DHCP4_STATIC_ROUTE)) - continue; - if (sd_dhcp_route_get_destination(routes[i], &r_network) < 0) continue; if (sd_dhcp_route_get_destination_prefix_length(routes[i], &r_plen) < 0 || r_plen > 32) @@ -275,31 +262,28 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, _nm_utils_inet4_ntop(network_net, network_net_str); _nm_utils_inet4_ntop(r_gateway.s_addr, gateway_str); - g_string_append_printf( - nm_gstring_add_space_delimiter(option == NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE - ? str_classless - : str_static), - "%s/%d %s", - network_net_str, - (int) r_plen, - gateway_str); + g_string_append_printf(nm_gstring_add_space_delimiter(str), + "%s/%d %s", + network_net_str, + (int) r_plen, + gateway_str); - if (option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE && has_classless_route) { + if (!is_classless && n_routes_classless > 0) { /* RFC 3443: if the DHCP server returns both a Classless Static Routes * option and a Static Routes option, the DHCP client MUST ignore the * Static Routes option. */ continue; } - if (r_plen == 0 && option == NM_DHCP_OPTION_DHCP4_STATIC_ROUTE) { - /* for option 33 (static route), RFC 2132 says: - * - * The default route (0.0.0.0) is an illegal destination for a static - * route. */ - continue; - } - if (r_plen == 0) { + if (!is_classless) { + /* for option 33 (static route), RFC 2132 says: + * + * The default route (0.0.0.0) is an illegal destination for a static + * route. */ + continue; + } + /* if there are multiple default routes, we add them with differing * metrics. */ m = default_route_metric_offset++; @@ -320,21 +304,18 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, })); } - if (str_classless && str_classless->len > 0) + if (str->len > 0) { nm_dhcp_option_add_option(options, AF_INET, - NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE, - str_classless->str); - if (str_static && str_static->len > 0) - nm_dhcp_option_add_option(options, - AF_INET, - NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, - str_static->str); + is_classless ? NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE + : NM_DHCP_OPTION_DHCP4_STATIC_ROUTE, + str->str); + } } num = sd_dhcp_lease_get_router(lease, &a_router); if (num > 0) { - guint32 default_route_metric_offset = 0; + default_route_metric_offset = 0; nm_gstring_prepare(&str); for (i = 0; i < num; i++) { diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build index 1fa9448645..80b95c4224 100644 --- a/src/libnm-systemd-core/meson.build +++ b/src/libnm-systemd-core/meson.build @@ -10,6 +10,7 @@ libnm_systemd_core = static_library( 'src/libsystemd-network/dhcp-packet.c', 'src/libsystemd-network/dhcp6-network.c', 'src/libsystemd-network/dhcp6-option.c', + 'src/libsystemd-network/dhcp6-protocol.c', 'src/libsystemd-network/lldp-neighbor.c', 'src/libsystemd-network/lldp-network.c', 'src/libsystemd-network/network-common.c', diff --git a/src/libnm-systemd-core/nm-sd-utils-dhcp.c b/src/libnm-systemd-core/nm-sd-utils-dhcp.c index 7fe889fda9..13c75055ec 100644 --- a/src/libnm-systemd-core/nm-sd-utils-dhcp.c +++ b/src/libnm-systemd-core/nm-sd-utils-dhcp.c @@ -13,8 +13,7 @@ int nm_sd_dhcp_lease_get_private_options(sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options) { - struct sd_dhcp_raw_option *raw_option; - int cnt = 0; + int cnt = 0; g_return_val_if_fail(lease, -EINVAL); g_return_val_if_fail(out_options, -EINVAL); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c index 74aa19de64..f2d9421417 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c @@ -10,20 +10,27 @@ #include "sd-id128.h" #include "dhcp-identifier.h" -#include "dhcp6-protocol.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "stat-util.h" -#include "stdio-util.h" +#include "string-table.h" #include "udev-util.h" -#include "virt.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { +static const char * const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "DUID-LLT", + [DUID_TYPE_EN] = "DUID-EN/Vendor", + [DUID_TYPE_LL] = "DUID-LL", + [DUID_TYPE_UUID] = "UUID", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); + +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); @@ -60,107 +67,141 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { } #if 0 /* NM_IGNORED */ -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { +static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) { uint16_t time_from_2000y; - assert(duid); - assert(len); assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; if (t < USEC_2000) time_from_2000y = 0; else time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); - unaligned_write_be16(&duid->type, DUID_TYPE_LLT); - unaligned_write_be16(&duid->llt.htype, arp_type); - unaligned_write_be32(&duid->llt.time, time_from_2000y); - memcpy(duid->llt.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&ret_duid->llt.htype, arp_type); + unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); + memcpy(ret_duid->llt.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + *ret_len = offsetof(struct duid, llt.haddr) + addr_len; return 0; } -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { - assert(duid); - assert(len); +static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) { assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; - unaligned_write_be16(&duid->type, DUID_TYPE_LL); - unaligned_write_be16(&duid->ll.htype, arp_type); - memcpy(duid->ll.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); + unaligned_write_be16(&ret_duid->ll.htype, arp_type); + memcpy(ret_duid->ll.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + *ret_len = offsetof(struct duid, ll.haddr) + addr_len; return 0; } #endif /* NM_IGNORED */ -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; uint64_t hash; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); - r = sd_id128_get_machine(&machine_id); - if (r < 0) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (!test_mode) { + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + } else + /* For tests, especially for fuzzers, reproducibility is important. + * Hence, use a static and constant machine ID. + * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); -#else - return r; -#endif - } - unaligned_write_be16(&duid->type, DUID_TYPE_EN); - unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); - - *len = sizeof(duid->type) + sizeof(duid->en); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); + unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); /* a bit of snake-oil perhaps, but no need to expose the machine-id * directly; duid->en.id might not be aligned, so we need to copy */ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); - memcpy(duid->en.id, &hash, sizeof(duid->en.id)); + memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id)); + + *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id); + + if (test_mode) + assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); return 0; } #if 0 /* NM_IGNORED */ -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { +static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); if (r < 0) return r; - unaligned_write_be16(&duid->type, DUID_TYPE_UUID); - memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); + memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id)); - *len = sizeof(duid->type) + sizeof(machine_id); + *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); return 0; } -#endif + +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len) { + + switch (duid_type) { + case DUID_TYPE_LLT: + return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len); + case DUID_TYPE_EN: + return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len); + case DUID_TYPE_LL: + return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len); + case DUID_TYPE_UUID: + return dhcp_identifier_set_duid_uuid(ret_duid, ret_len); + default: + return -EINVAL; + } +} +#endif /* NM_IGNORED */ int dhcp_identifier_set_iaid( int ifindex, @@ -168,7 +209,7 @@ int dhcp_identifier_set_iaid( size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, - void *_id) { + void *ret) { #if 0 /* NM_IGNORED */ /* name is a pointer to memory in the sd_device struct, so must @@ -221,7 +262,7 @@ int dhcp_identifier_set_iaid( * behavior. */ id32 = be32toh(id32); - unaligned_write_ne32(_id, id32); + unaligned_write_ne32(ret, id32); return 0; #else /* NM_IGNORED */ /* for NetworkManager, we don't use this function and we should never call here. diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h index 6c24af0326..697ba3bfbb 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.h @@ -54,9 +54,23 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id); +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len); +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len); +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + bool use_mac, + void *ret); + +const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h index 992ac9f325..c67e9511a1 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h @@ -16,8 +16,6 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; unsigned char dst_prefixlen; - - uint8_t option; }; struct sd_dhcp_raw_option { @@ -52,8 +50,10 @@ struct sd_dhcp_lease { DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; - struct sd_dhcp_route *static_route; - size_t static_route_size; + struct sd_dhcp_route *static_routes; + size_t n_static_routes; + struct sd_dhcp_route *classless_routes; + size_t n_classless_routes; uint16_t mtu; /* 0 if unset */ diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c index 1710f6962b..712f609784 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-option.c @@ -60,7 +60,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, case SD_DHCP_OPTION_USER_CLASS: { size_t total = 0; - char **s; if (strv_isempty((char **) optval)) return -EINVAL; diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h index 11f4201ab2..dd54bcf6ee 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-protocol.h @@ -73,15 +73,24 @@ enum { }; enum { - DHCP_DISCOVER = 1, - DHCP_OFFER = 2, - DHCP_REQUEST = 3, - DHCP_DECLINE = 4, - DHCP_ACK = 5, - DHCP_NAK = 6, - DHCP_RELEASE = 7, - DHCP_INFORM = 8, - DHCP_FORCERENEW = 9, + DHCP_DISCOVER = 1, /* [RFC2132] */ + DHCP_OFFER = 2, /* [RFC2132] */ + DHCP_REQUEST = 3, /* [RFC2132] */ + DHCP_DECLINE = 4, /* [RFC2132] */ + DHCP_ACK = 5, /* [RFC2132] */ + DHCP_NAK = 6, /* [RFC2132] */ + DHCP_RELEASE = 7, /* [RFC2132] */ + DHCP_INFORM = 8, /* [RFC2132] */ + DHCP_FORCERENEW = 9, /* [RFC3203] */ + DHCPLEASEQUERY = 10, /* [RFC4388] */ + DHCPLEASEUNASSIGNED = 11, /* [RFC4388] */ + DHCPLEASEUNKNOWN = 12, /* [RFC4388] */ + DHCPLEASEACTIVE = 13, /* [RFC4388] */ + DHCPBULKLEASEQUERY = 14, /* [RFC6926] */ + DHCPLEASEQUERYDONE = 15, /* [RFC6926] */ + DHCPACTIVELEASEQUERY = 16, /* [RFC7724] */ + DHCPLEASEQUERYSTATUS = 17, /* [RFC7724] */ + DHCPTLS = 18, /* [RFC7724] */ }; enum { 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 f943409856..0d7813f613 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h @@ -11,138 +11,83 @@ #include "sd-event.h" #include "sd-dhcp6-client.h" +#include "dhcp-identifier.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" +#include "ether-addr-util.h" #include "hashmap.h" -#include "list.h" #include "macro.h" #include "network-common.h" +#include "ordered-set.h" #include "sparse-endian.h" +#include "time-util.h" -typedef struct sd_dhcp6_option { +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +typedef enum DHCP6RequestIA { + DHCP6_REQUEST_IA_NA = 1 << 0, + DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ + DHCP6_REQUEST_IA_PD = 1 << 2, +} DHCP6RequestIA; + +struct sd_dhcp6_client { unsigned n_ref; - uint32_t enterprise_identifier; - uint16_t option; - void *data; - size_t length; -} sd_dhcp6_option; + int ifindex; + char *ifname; -extern const struct hash_ops dhcp6_option_hash_ops; + struct in6_addr local_address; + struct hw_addr_data hw_addr; + uint16_t arp_type; -/* Common option header */ -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; + sd_event *event; + sd_event_source *receive_message; + sd_event_source *timeout_resend; + sd_event_source *timeout_expire; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + int event_priority; + int fd; -/* Address option */ -struct iaaddr { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; -} _packed_; + DHCP6State state; + bool information_request; + usec_t information_request_time_usec; + usec_t information_refresh_time_usec; + be32_t transaction_id; + usec_t transaction_start; + usec_t retransmit_time; + uint8_t retransmit_count; -/* Prefix Delegation Prefix option */ -struct iapdprefix { - be32_t lifetime_preferred; - be32_t lifetime_valid; - uint8_t prefixlen; - struct in6_addr address; -} _packed_; + bool iaid_set; + DHCP6IA ia_na; + DHCP6IA ia_pd; + DHCP6RequestIA request_ia; + struct duid duid; + size_t duid_len; + be16_t *req_opts; + size_t req_opts_len; + char *fqdn; + char *mudurl; + char **user_class; + char **vendor_class; + OrderedHashmap *extra_options; + OrderedSet *vendor_options; -typedef struct DHCP6Address DHCP6Address; + struct sd_dhcp6_lease *lease; -struct DHCP6Address { - LIST_FIELDS(DHCP6Address, addresses); + sd_dhcp6_client_callback_t callback; + void *userdata; - union { - struct iaaddr iaaddr; - struct iapdprefix iapdprefix; - }; + /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + bool test_mode; }; -/* Non-temporary Address option */ -struct ia_na { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Prefix Delegation option */ -struct ia_pd { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Temporary Address option */ -struct ia_ta { - be32_t id; -} _packed_; - -typedef struct DHCP6IA { - uint16_t type; - union { - struct ia_na ia_na; - struct ia_pd ia_pd; - struct ia_ta ia_ta; - }; - - LIST_HEAD(DHCP6Address, addresses); -} DHCP6IA; - -typedef struct sd_dhcp6_client sd_dhcp6_client; - -bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix); -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( - 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, const void *packet, size_t len); -int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease); - -const char *dhcp6_message_type_to_string(int s) _const_; -int dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(int s) _const_; -int dhcp6_message_status_from_string(const char *s) _pure_; - +int dhcp6_client_send_message(sd_dhcp6_client *client); void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); #define log_dhcp6_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ 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 8ae2ecd6d9..1f10dccbbb 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 @@ -5,11 +5,13 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include +#include #include "sd-dhcp6-lease.h" -#include "dhcp6-internal.h" +#include "dhcp6-option.h" +#include "macro.h" +#include "time-util.h" struct sd_dhcp6_lease { unsigned n_ref; @@ -21,10 +23,13 @@ struct sd_dhcp6_lease { uint8_t preference; bool rapid_commit; triple_timestamp timestamp; + usec_t lifetime_t1; + usec_t lifetime_t2; + usec_t lifetime_valid; struct in6_addr server_address; - DHCP6IA ia; - DHCP6IA pd; + DHCP6IA *ia_na; /* Identity association non-temporary addresses */ + DHCP6IA *ia_pd; /* Identity association prefix delegation */ DHCP6Address *addr_iter; DHCP6Address *prefix_iter; @@ -40,17 +45,15 @@ struct sd_dhcp6_lease { char *fqdn; }; -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); - +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret); int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret); 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); @@ -59,3 +62,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + 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 237771e613..03d563710d 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c @@ -11,14 +11,12 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" -#include "dhcp-identifier.h" #include "dhcp6-internal.h" -#include "dhcp6-lease-internal.h" +#include "dhcp6-option.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" @@ -76,8 +74,8 @@ bool dhcp6_option_can_request(uint16_t option) { return false; case SD_DHCP6_OPTION_CLIENT_FQDN: case SD_DHCP6_OPTION_PANA_AGENT: - case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE: - case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE: + case SD_DHCP6_OPTION_POSIX_TIMEZONE: + case SD_DHCP6_OPTION_TZDB_TIMEZONE: return true; case SD_DHCP6_OPTION_ERO: case SD_DHCP6_OPTION_LQ_QUERY: @@ -214,19 +212,15 @@ bool dhcp6_option_can_request(uint16_t option) { } static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { - DHCP6Option *option; - assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - option = (DHCP6Option*) *buf; - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; - option->code = htobe16(optcode); - option->len = htobe16(optlen); + unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); @@ -244,15 +238,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, if (r < 0) return r; - memcpy_safe(*buf, optval, optlen); - - *buf += optlen; + *buf = mempcpy_safe(*buf, optval, optlen); *buflen -= optlen; return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; @@ -261,7 +253,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash assert(buflen); assert(vendor_options); - ORDERED_HASHMAP_FOREACH(options, vendor_options) { + ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; size_t total; @@ -284,14 +276,63 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash return 0; } +static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { + struct iaaddr a; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(address); + + /* Do not append T1 and T2. */ + a = (struct iaaddr) { + .address = address->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); + if (r < 0) + return r; + + *buf = mempcpy(*buf, &a, sizeof(struct iaaddr)); + *buflen -= sizeof(struct iaaddr); + + return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); +} + +static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { + struct iapdprefix p; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(prefix); + + if (prefix->prefixlen == 0) + return -EINVAL; + + /* Do not append T1 and T2. */ + p = (struct iapdprefix) { + .prefixlen = prefix->prefixlen, + .address = prefix->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); + if (r < 0) + return r; + + *buf = mempcpy(*buf, &p, sizeof(struct iapdprefix)); + *buflen -= sizeof(struct iapdprefix); + + return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { - size_t ia_buflen, ia_addrlen = 0; - struct ia_na ia_na; - struct ia_ta ia_ta; - DHCP6Address *addr; + struct ia_header header; + size_t ia_buflen; uint8_t *ia_hdr; uint16_t len; - void *p; int r; assert_return(buf, -EINVAL); @@ -303,23 +344,22 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: - len = DHCP6_OPTION_IA_NA_LEN; - ia_na = (struct ia_na) { - .id = ia->ia_na.id, + case SD_DHCP6_OPTION_IA_PD: + len = sizeof(struct ia_header); + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_na; break; case SD_DHCP6_OPTION_IA_TA: - len = DHCP6_OPTION_IA_TA_LEN; - ia_ta = (struct ia_ta) { - .id = ia->ia_ta.id, + len = sizeof(header.id); /* IA_TA does not have lifetime. */ + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_ta; break; default: - return -EINVAL; + assert_not_reached(); } if (*buflen < offsetof(DHCP6Option, data) + len) @@ -328,116 +368,25 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); - - memcpy(*buf, p, len); - - *buf += len; - *buflen -= len; - - LIST_FOREACH(addresses, addr, ia->addresses) { - struct iaaddr a = { - .address = addr->iaaddr.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; - - memcpy(*buf, &a, sizeof(struct iaaddr)); - - *buf += sizeof(struct iaaddr); - *buflen -= sizeof(struct iaaddr); - - ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr); - } - - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); -} - -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) { - struct iapdprefix p; - int r; - - assert(buf); - assert(*buf); - assert(buflen); - assert(prefix); - - if (prefix->iapdprefix.prefixlen == 0) - return -EINVAL; - - /* Do not append T1 and T2. */ - - p = (struct iapdprefix) { - .prefixlen = prefix->iapdprefix.prefixlen, - .address = prefix->iapdprefix.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); - if (r < 0) - return r; - - memcpy(*buf, &p, sizeof(struct iapdprefix)); - - *buf += sizeof(struct iapdprefix); - *buflen -= sizeof(struct iapdprefix); - - return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); -} - -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { - struct ia_pd ia_pd; - size_t len, pd_buflen; - uint8_t *pd_hdr; - int r; - - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(pd, -EINVAL); - assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); - - /* Do not set T1 and T2. */ - ia_pd = (struct ia_pd) { - .id = pd->ia_pd.id, - }; - len = sizeof(struct ia_pd); - - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - pd_hdr = *buf; - pd_buflen = *buflen; - /* The header will be written at the end of this function. */ *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, &ia_pd, len); + *buf = mempcpy(*buf, &header, len); + *buflen -= len; - *buf += sizeof(struct ia_pd); - *buflen -= sizeof(struct ia_pd); - - DHCP6Address *prefix; - LIST_FOREACH(addresses, prefix, pd->addresses) { - r = option_append_pd_prefix(buf, buflen, prefix); + LIST_FOREACH(addresses, addr, ia->addresses) { + if (ia->type == SD_DHCP6_OPTION_IA_PD) + r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + else + r = option_append_ia_address(buf, buflen, &addr->iaaddr); if (r < 0) return r; len += r; } - if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { - r = option_append_pd_prefix(buf, buflen, hint_pd_prefix); - if (r < 0) - return r; - - len += r; - } - - return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); + return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { @@ -470,7 +419,6 @@ 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) { _cleanup_free_ uint8_t *p = NULL; size_t total = 0, offset = 0; - char * const *s; assert(buf); assert(*buf); @@ -503,7 +451,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const _cleanup_free_ uint8_t *p = NULL; uint32_t enterprise_identifier; size_t total, offset; - char * const *s; assert(buf); assert(*buf); @@ -550,7 +497,6 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data) { - const DHCP6Option *option; size_t len; assert(buf); @@ -565,16 +511,15 @@ int dhcp6_option_parse( if (*offset >= buflen - offsetof(DHCP6Option, data)) return -EBADMSG; - option = (const DHCP6Option*) (buf + *offset); - len = be16toh(option->len); + len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len)); if (len > buflen - offsetof(DHCP6Option, data) - *offset) return -EBADMSG; - *offset += offsetof(DHCP6Option, data) + len; - *ret_option_code = be16toh(option->code); + *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code)); *ret_option_data_len = len; - *ret_option_data = option->data; + *ret_option_data = buf + *offset + offsetof(DHCP6Option, data); + *offset += offsetof(DHCP6Option, data) + len; return 0; } @@ -603,9 +548,9 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { int r; - assert(buf); + assert(buf || buflen == 0); - for(size_t offset = 0; offset < buflen;) { + for (size_t offset = 0; offset < buflen;) { const uint8_t *data; size_t data_len; uint16_t code; @@ -614,22 +559,22 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t if (r < 0) return r; - switch(code) { + 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) + 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)); + 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"); break; } default: @@ -640,19 +585,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return 0; } -static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; uint32_t lt_valid, lt_pref; - DHCP6Address *a; int r; - assert(data); - assert(ret); + assert(ia); + assert(data || len == 0); + + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address sub-option in an invalid option, ignoring."); 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); + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + + lt_valid = be32toh(a->iaaddr.lifetime_valid); + lt_pref = be32toh(a->iaaddr.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -669,27 +624,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t return r; } + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); + return 0; +} + +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; + uint32_t lt_valid, lt_pref; + int r; + + assert(ia); + assert(data || len == 0); + + if (ia->type != SD_DHCP6_OPTION_IA_PD) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an PD prefix sub-option in an invalid option, ignoring"); + + if (len < sizeof(struct iapdprefix)) + return -EBADMSG; + a = new(DHCP6Address, 1); if (!a) return -ENOMEM; - LIST_INIT(addresses, a); - memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - *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; - - lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); + lt_valid = be32toh(a->iapdprefix.lifetime_valid); + lt_pref = be32toh(a->iapdprefix.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -706,14 +667,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t return r; } - a = new(DHCP6Address, 1); - if (!a) - return -ENOMEM; - - LIST_INIT(addresses, a); - memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - - *ret = a; + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); return 0; } @@ -723,16 +677,15 @@ int dhcp6_option_parse_ia( uint16_t option_code, size_t option_data_len, const uint8_t *option_data, - DHCP6IA *ret) { + 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; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + uint32_t lt_t1, lt_t2; + size_t header_len; int r; assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); - assert(option_data); + assert(option_data || option_data_len == 0); assert(ret); /* This will return the following: @@ -745,56 +698,51 @@ int dhcp6_option_parse_ia( switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - - if (option_data_len < DHCP6_OPTION_IA_NA_LEN) - return -EBADMSG; - - 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 (option_data_len < DHCP6_OPTION_IA_PD_LEN) - return -EBADMSG; - - 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); + header_len = sizeof(struct ia_header); break; case SD_DHCP6_OPTION_IA_TA: - if (option_data_len < DHCP6_OPTION_IA_TA_LEN) - return -ENOMSG; - - 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. */ + header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */ break; default: assert_not_reached(); } + if (option_data_len < header_len) + return -EBADMSG; + + ia = new(DHCP6IA, 1); + if (!ia) + return -ENOMEM; + + *ia = (DHCP6IA) { + .type = option_code, + }; + memcpy(&ia->header, option_data, header_len); + /* 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) + if (ia->header.id != 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."); + /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ + lt_t1 = be32toh(ia->header.lifetime_t1); + lt_t2 = be32toh(ia->header.lifetime_t2); + 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 (lt_t1 == 0 && lt_t2 > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.", + lt_t2); - for (; offset < option_data_len;) { + for (size_t offset = header_len; offset < option_data_len;) { const uint8_t *subdata; size_t subdata_len; uint16_t subopt; @@ -805,41 +753,19 @@ int dhcp6_option_parse_ia( switch (subopt) { case SD_DHCP6_OPTION_IAADDR: { - DHCP6Address *a; - - 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; - } - - r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ 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); + r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -848,14 +774,14 @@ int dhcp6_option_parse_ia( 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) + 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)); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); break; } default: @@ -863,50 +789,11 @@ int dhcp6_option_parse_ia( } } - if (!ia.addresses) + 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: - *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: - *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: - assert_not_reached(); - } - + *ret = TAKE_PTR(ia); return 0; } diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h new file mode 100644 index 0000000000..80aba7f37f --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp6-client.h" + +#include "hash-funcs.h" +#include "list.h" +#include "macro.h" +#include "ordered-set.h" +#include "sparse-endian.h" + +typedef struct sd_dhcp6_option { + unsigned n_ref; + + uint32_t enterprise_identifier; + uint16_t option; + void *data; + size_t length; +} sd_dhcp6_option; + +extern const struct hash_ops dhcp6_option_hash_ops; + +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + +typedef struct DHCP6Address DHCP6Address; + +struct DHCP6Address { + LIST_FIELDS(DHCP6Address, addresses); + + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; +}; + +struct ia_header { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +typedef struct DHCP6IA { + uint16_t type; + struct ia_header header; + + LIST_HEAD(DHCP6Address, addresses); +} DHCP6IA; + +void dhcp6_ia_clear_addresses(DHCP6IA *ia); +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); + +bool dhcp6_option_can_request(uint16_t option); + +int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, + size_t optlen, const void *optval); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +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, OrderedSet *vendor_options); + +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); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c new file mode 100644 index 0000000000..e19faa51e6 --- /dev/null +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.c @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "nm-sd-adapt-core.h" + +#include "dhcp6-protocol.h" +#include "string-table.h" + +static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { + [DHCP6_STATE_STOPPED] = "stopped", + [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", + [DHCP6_STATE_SOLICITATION] = "solicitation", + [DHCP6_STATE_REQUEST] = "request", + [DHCP6_STATE_BOUND] = "bound", + [DHCP6_STATE_RENEW] = "renew", + [DHCP6_STATE_REBIND] = "rebind", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); + +static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { + [DHCP6_MESSAGE_SOLICIT] = "Solicit", + [DHCP6_MESSAGE_ADVERTISE] = "Advertise", + [DHCP6_MESSAGE_REQUEST] = "Request", + [DHCP6_MESSAGE_CONFIRM] = "Confirm", + [DHCP6_MESSAGE_RENEW] = "Renew", + [DHCP6_MESSAGE_REBIND] = "Rebind", + [DHCP6_MESSAGE_REPLY] = "Reply", + [DHCP6_MESSAGE_RELEASE] = "Release", + [DHCP6_MESSAGE_DECLINE] = "Decline", + [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", + [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", + [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", + [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", + [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", + [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", + [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", + [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", + [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", + [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", + [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", + [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", + [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", + [DHCP6_MESSAGE_START_TLS] = "Start TLS", + [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", + [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", + [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", + [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", + [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", + [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", + [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", + [DHCP6_MESSAGE_CONNECT] = "Connect", + [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", + [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", + [DHCP6_MESSAGE_STATE] = "State", + [DHCP6_MESSAGE_CONTACT] = "Contact", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); + +static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", + [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", + [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", + [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", + [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", + [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", + [DHCP6_STATUS_DATA_MISSING] = "Data missing", + [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", + [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", + [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", + [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", + [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", + [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", + [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", + [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", + [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", + [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h index 5d2af439e2..f4e47857e3 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h @@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message; #define DHCP6_MIN_OPTIONS_SIZE \ 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) -#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ +#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } enum { @@ -100,13 +100,15 @@ typedef enum DHCP6MessageType { DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */ DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */ _DHCP6_MESSAGE_TYPE_MAX, - _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, + _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, } DHCP6MessageType; typedef enum DHCP6NTPSubOption { DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, + _DHCP6_NTP_SUBOPTION_MAX, + _DHCP6_NTP_SUBOPTION_INVALID = -EINVAL, } DHCP6NTPSubOption; /* @@ -138,7 +140,7 @@ typedef enum DHCP6Status { DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, _DHCP6_STATUS_MAX, - _DHCP6_STATUS_INVALID = -EINVAL, + _DHCP6_STATUS_INVALID = -EINVAL, } DHCP6Status; typedef enum DHCP6FQDNFlag { @@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag { DHCP6_FQDN_FLAG_O = 1 << 1, DHCP6_FQDN_FLAG_N = 1 << 2, } DHCP6FQDNFlag; + +const char *dhcp6_state_to_string(DHCP6State s) _const_; +const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_; +DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; +DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; 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 b4d7c327cc..f56929ad65 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c +++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c @@ -335,9 +335,9 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { usec_t base; /* Use the packet's timestamp if there is one known */ - base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); - if (base <= 0 || base == USEC_INFINITY) - base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ + base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME); + if (!timestamp_is_set(base)) + base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */ n->until = usec_add(base, n->ttl * USEC_PER_SEC); } else diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h index 895a00d01b..5aa225e977 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/network-internal.h +++ b/src/libnm-systemd-core/src/libsystemd-network/network-internal.h @@ -21,7 +21,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); struct sd_dhcp_route; struct sd_dhcp_lease; -void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); +void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string); /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ 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 1600f806ed..d56978adcd 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 @@ -147,11 +147,11 @@ static const uint8_t default_req_opts_anonymize[] = { SD_DHCP_OPTION_ROUTER, /* 3 */ SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ - SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_ROUTER_DISCOVERY, /* 31 */ SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ - SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE, /* 46 */ SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ @@ -249,7 +249,7 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); - switch(option) { + switch (option) { case SD_DHCP_OPTION_PAD: case SD_DHCP_OPTION_OVERLOAD: @@ -454,7 +454,7 @@ static int dhcp_client_set_iaid_duid_internal( bool iaid_append, bool iaid_set, uint32_t iaid, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { @@ -493,37 +493,20 @@ static int dhcp_client_set_iaid_duid_internal( client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len, + client->arp_type, llt_time, client->test_mode, + &client->client_id.ns.duid, &len); + if (r == -EOPNOTSUPP) + return log_dhcp_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } client->client_id_len = sizeof(client->client_id.type) + len + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); @@ -617,7 +600,6 @@ int sd_dhcp_client_set_user_class( sd_dhcp_client *client, char * const *user_class) { - char * const *p; char **s = NULL; assert_return(client, -EINVAL); @@ -844,7 +826,7 @@ static int client_message_init( /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(time_now >= client->start_time); @@ -881,7 +863,7 @@ static int client_message_init( if (r < 0) return r; - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len); if (r < 0) return r; @@ -1269,7 +1251,7 @@ static int client_timeout_resend( assert(client); assert(client->event); - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) goto error; @@ -1317,7 +1299,7 @@ static int client_timeout_resend( } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, next_timeout, 10 * USEC_PER_MSEC, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1417,12 +1399,12 @@ static int client_initialize_time_events(sd_dhcp_client *client) { assert(client->event); if (client->start_delay > 0) { - assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(client->event, CLOCK_BOOTTIME, &usec) >= 0); usec += client->start_delay; } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, usec, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1463,7 +1445,7 @@ static int client_start_delayed(sd_dhcp_client *client) { client->fd = r; if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT)) - client->start_time = now(clock_boottime_or_monotonic()); + client->start_time = now(CLOCK_BOOTTIME); return client_initialize_events(client, client_receive_message_raw); } @@ -1707,7 +1689,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; } - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(client->request_sent <= time_now); @@ -1740,7 +1722,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm lifetime timeout */ r = event_reset_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->expire_time, 10 * USEC_PER_MSEC, client_timeout_expire, client, client->event_priority, "dhcp4-lifetime", true); @@ -1756,7 +1738,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T2 timeout */ r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t2_time, 10 * USEC_PER_MSEC, client_timeout_t2, client, client->event_priority, "dhcp4-t2-timeout", true); @@ -1772,7 +1754,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T1 timeout */ r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t1_time, 10 * USEC_PER_MSEC, client_timeout_t1, client, client->event_priority, "dhcp4-t1-timer", true); @@ -1807,7 +1789,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->attempt = 0; r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, 0, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c index 7a2505343d..5f83fa66ae 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c @@ -217,25 +217,38 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { * The returned routes array must be freed by the caller. * Route objects have the same lifetime of the lease and must not be freed. */ -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { - sd_dhcp_route **ret; - unsigned i; +static int dhcp_lease_get_routes(sd_dhcp_route *routes, size_t n_routes, sd_dhcp_route ***ret) { + assert(routes || n_routes == 0); - assert_return(lease, -EINVAL); - assert_return(routes, -EINVAL); - - if (lease->static_route_size <= 0) + if (n_routes <= 0) return -ENODATA; - ret = new(sd_dhcp_route *, lease->static_route_size); - if (!ret) - return -ENOMEM; + if (ret) { + sd_dhcp_route **buf; - for (i = 0; i < lease->static_route_size; i++) - ret[i] = &lease->static_route[i]; + buf = new(sd_dhcp_route*, n_routes); + if (!buf) + return -ENOMEM; - *routes = ret; - return (int) lease->static_route_size; + for (size_t i = 0; i < n_routes; i++) + buf[i] = &routes[i]; + + *ret = buf; + } + + return (int) n_routes; +} + +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); + + return dhcp_lease_get_routes(lease->static_routes, lease->n_static_routes, ret); +} + +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); + + return dhcp_lease_get_routes(lease->classless_routes, lease->n_classless_routes, ret); } int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { @@ -314,7 +327,8 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); - free(lease->static_route); + free(lease->static_routes); + free(lease->classless_routes); free(lease->client_id); free(lease->vendor_specific); strv_free(lease->search_domains); @@ -469,96 +483,88 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } -static int lease_parse_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size) { - - struct in_addr addr; +static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + int r; + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); - - if (len <= 0) - return 0; if (len % 8 != 0) return -EINVAL; - if (!GREEDY_REALLOC(*routes, *routes_size + (len / 8))) - return -ENOMEM; - while (len >= 8) { - struct sd_dhcp_route *route = *routes + *routes_size; - int r; + struct in_addr dst, gw; + uint8_t prefixlen; - route->option = SD_DHCP_OPTION_STATIC_ROUTE; - r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); - if (r < 0) { - log_debug("Failed to determine destination prefix length from class based IP, ignoring"); - continue; - } - - assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0); - route->dst_addr = inet_makeaddr(inet_netof(addr), 0); + assert_se(lease_parse_be32(option, 4, &dst.s_addr) >= 0); option += 4; - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0); option += 4; len -= 8; - (*routes_size)++; + + r = in4_addr_default_prefixlen(&dst, &prefixlen); + if (r < 0) { + log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring."); + continue; + } + + (void) in4_addr_mask(&dst, prefixlen); + + if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1)) + return -ENOMEM; + + lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) { + .dst_addr = dst, + .gw_addr = gw, + .dst_prefixlen = prefixlen, + }; } return 0; } /* parses RFC3442 Classless Static Route Option */ -static int lease_parse_classless_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size) { - +static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); - if (len <= 0) - return 0; - - /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ + /* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */ while (len > 0) { - uint8_t dst_octets; - struct sd_dhcp_route *route; + uint8_t prefixlen, dst_octets; + struct in_addr dst = {}, gw; - if (!GREEDY_REALLOC(*routes, *routes_size + 1)) - return -ENOMEM; - - route = *routes + *routes_size; - route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE; - - dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); - route->dst_prefixlen = *option; + prefixlen = *option; option++; len--; + dst_octets = DIV_ROUND_UP(prefixlen, 8); + /* can't have more than 4 octets in IPv4 */ if (dst_octets > 4 || len < dst_octets) return -EINVAL; - route->dst_addr.s_addr = 0; - memcpy(&route->dst_addr.s_addr, option, dst_octets); + memcpy(&dst, option, dst_octets); option += dst_octets; len -= dst_octets; if (len < 4) return -EINVAL; - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0); option += 4; len -= 4; - (*routes_size)++; + if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1)) + return -ENOMEM; + + lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) { + .dst_addr = dst, + .gw_addr = gw, + .dst_prefixlen = prefixlen, + }; } return 0; @@ -625,7 +631,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void assert(lease); - switch(code) { + switch (code) { case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME: r = lease_parse_u32(option, len, &lease->lifetime, 1); @@ -700,12 +706,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_STATIC_ROUTE: - r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size); + r = lease_parse_static_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; - case SD_DHCP_OPTION_INTERFACE_MTU: + case SD_DHCP_OPTION_MTU_INTERFACE: r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); @@ -725,7 +731,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; - case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST: + case SD_DHCP_OPTION_DOMAIN_SEARCH: r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains); if (r < 0) log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m"); @@ -746,28 +752,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void log_debug_errno(r, "Failed to parse root path, ignoring: %m"); break; - case SD_DHCP_OPTION_RENEWAL_T1_TIME: + case SD_DHCP_OPTION_RENEWAL_TIME: r = lease_parse_u32(option, len, &lease->t1, 1); if (r < 0) log_debug_errno(r, "Failed to parse T1 time, ignoring: %m"); break; - case SD_DHCP_OPTION_REBINDING_T2_TIME: + case SD_DHCP_OPTION_REBINDING_TIME: r = lease_parse_u32(option, len, &lease->t2, 1); if (r < 0) log_debug_errno(r, "Failed to parse T2 time, ignoring: %m"); break; case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - r = lease_parse_classless_routes( - option, len, - &lease->static_route, - &lease->static_route_size); + r = lease_parse_classless_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; - case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { + case SD_DHCP_OPTION_TZDB_TIMEZONE: { _cleanup_free_ char *tz = NULL; r = lease_parse_string(option, len, &tz); @@ -909,19 +912,21 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d pos = next_chunk; } - *domains = TAKE_PTR(names); + strv_free_and_replace(*domains, names); return cnt; } int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { - struct sd_dhcp_raw_option *cur, *option; + struct sd_dhcp_raw_option *option, *before = NULL; assert(lease); LIST_FOREACH(options, cur, lease->private_options) { - if (tag < cur->tag) + if (tag < cur->tag) { + before = cur; break; + } if (tag == cur->tag) { log_debug("Ignoring duplicate option, tagged %i.", tag); return 0; @@ -940,7 +945,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo return -ENOMEM; } - LIST_INSERT_BEFORE(options, lease->private_options, cur, option); + LIST_INSERT_BEFORE(options, lease->private_options, before, option); return 0; } @@ -960,7 +965,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - struct sd_dhcp_raw_option *option; struct in_addr address; const struct in_addr *addresses; const void *client_id, *data; @@ -969,7 +973,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; - char **search_domains = NULL; + char **search_domains; uint32_t t1, t2, lifetime; int r; @@ -1068,9 +1072,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) fprintf(f, "ROOT_PATH=%s\n", string); - r = sd_dhcp_lease_get_routes(lease, &routes); + r = sd_dhcp_lease_get_static_routes(lease, &routes); if (r > 0) - serialize_dhcp_routes(f, "ROUTES", routes, r); + serialize_dhcp_routes(f, "STATIC_ROUTES", routes, r); + + routes = mfree(routes); + r = sd_dhcp_lease_get_classless_routes(lease, &routes); + if (r > 0) + serialize_dhcp_routes(f, "CLASSLESS_ROUTES", routes, r); r = sd_dhcp_lease_get_timezone(lease, &string); if (r >= 0) @@ -1118,6 +1127,18 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { return 0; } +static char **private_options_free(char **options) { + if (!options) + return NULL; + + for (unsigned i = 0; i < SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1; i++) + free(options[i]); + + return mfree(options); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(char**, private_options_free); + int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char @@ -1134,14 +1155,15 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *smtp = NULL, *lpr = NULL, *mtu = NULL, - *routes = NULL, + *static_routes = NULL, + *classless_routes = NULL, *domains = NULL, *client_id_hex = NULL, *vendor_specific_hex = NULL, *lifetime = NULL, *t1 = NULL, - *t2 = NULL, - *options[SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1] = {}; + *t2 = NULL; + _cleanup_(private_options_freep) char **options = NULL; int r, i; @@ -1152,6 +1174,10 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { if (r < 0) return r; + options = new0(char*, SD_DHCP_OPTION_PRIVATE_LAST - SD_DHCP_OPTION_PRIVATE_BASE + 1); + if (!options) + return -ENOMEM; + r = parse_env_file(NULL, lease_file, "ADDRESS", &address, "ROUTER", &router, @@ -1170,7 +1196,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "HOSTNAME", &lease->hostname, "DOMAIN_SEARCH_LIST", &domains, "ROOT_PATH", &lease->root_path, - "ROUTES", &routes, + "STATIC_ROUTES", &static_routes, + "CLASSLESS_ROUTES", &classless_routes, "CLIENTID", &client_id_hex, "TIMEZONE", &lease->timezone, "VENDOR_SPECIFIC", &vendor_specific_hex, @@ -1317,13 +1344,22 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { lease->search_domains = TAKE_PTR(a); } - if (routes) { + if (static_routes) { r = deserialize_dhcp_routes( - &lease->static_route, - &lease->static_route_size, - routes); + &lease->static_routes, + &lease->n_static_routes, + static_routes); if (r < 0) - log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); + log_debug_errno(r, "Failed to parse DHCP static routes %s, ignoring: %m", static_routes); + } + + if (classless_routes) { + r = deserialize_dhcp_routes( + &lease->classless_routes, + &lease->n_classless_routes, + classless_routes); + if (r < 0) + log_debug_errno(r, "Failed to parse DHCP classless routes %s, ignoring: %m", classless_routes); } if (lifetime) { @@ -1470,9 +1506,3 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { *gateway = route->gw_addr; return 0; } - -int sd_dhcp_route_get_option(sd_dhcp_route *route) { - assert_return(route, -EINVAL); - - return route->option; -} 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 0c2206ff71..4394ef4937 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 @@ -20,7 +20,6 @@ #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "dns-domain.h" #include "event-util.h" #include "fd-util.h" @@ -28,74 +27,11 @@ #include "hostname-util.h" #include "in-addr-util.h" #include "io-util.h" -#include "network-common.h" #include "random-util.h" #include "socket-util.h" -#include "string-table.h" #include "strv.h" -#include "util.h" #include "web-util.h" -#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN - -#define IRT_DEFAULT (1 * USEC_PER_DAY) -#define IRT_MINIMUM (600 * USEC_PER_SEC) - -/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ -typedef enum DHCP6RequestIA { - DHCP6_REQUEST_IA_NA = 1 << 0, - DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ - DHCP6_REQUEST_IA_PD = 1 << 2, -} DHCP6RequestIA; - -struct sd_dhcp6_client { - unsigned n_ref; - - DHCP6State state; - sd_event *event; - int event_priority; - int ifindex; - char *ifname; - DHCP6Address hint_pd_prefix; - struct in6_addr local_address; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - DHCP6IA ia_na; - DHCP6IA ia_pd; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - DHCP6RequestIA request_ia; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - bool iaid_set; - be16_t *req_opts; - size_t req_opts_len; - char *fqdn; - char *mudurl; - char **user_class; - char **vendor_class; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; - usec_t information_request_time_usec; - usec_t information_refresh_time_usec; - OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; - - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ - bool test_mode; -}; - static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_DNS_SERVERS, SD_DHCP6_OPTION_DOMAIN_LIST, @@ -103,78 +39,10 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; -const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { - [DHCP6_MESSAGE_SOLICIT] = "Solicit", - [DHCP6_MESSAGE_ADVERTISE] = "Advertise", - [DHCP6_MESSAGE_REQUEST] = "Request", - [DHCP6_MESSAGE_CONFIRM] = "Confirm", - [DHCP6_MESSAGE_RENEW] = "Renew", - [DHCP6_MESSAGE_REBIND] = "Rebind", - [DHCP6_MESSAGE_REPLY] = "Reply", - [DHCP6_MESSAGE_RELEASE] = "Release", - [DHCP6_MESSAGE_DECLINE] = "Decline", - [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", - [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", - [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", - [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", - [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", - [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", - [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", - [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", - [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", - [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", - [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", - [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", - [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", - [DHCP6_MESSAGE_START_TLS] = "Start TLS", - [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", - [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", - [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", - [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", - [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", - [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", - [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", - [DHCP6_MESSAGE_CONNECT] = "Connect", - [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", - [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", - [DHCP6_MESSAGE_STATE] = "State", - [DHCP6_MESSAGE_CONTACT] = "Contact", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); - -const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", - [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", - [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", - [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", - [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", - [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", - [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", - [DHCP6_STATUS_DATA_MISSING] = "Data missing", - [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", - [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", - [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", - [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", - [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", - [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", - [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", - [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", - [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", - [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); - #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) -static int client_start(sd_dhcp6_client *client, DHCP6State state); +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state); int sd_dhcp6_client_set_callback( sd_dhcp6_client *client, @@ -191,8 +59,8 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(ifindex > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ifindex = ifindex; return 0; @@ -228,9 +96,9 @@ int sd_dhcp6_client_set_local_address( const struct in6_addr *local_address) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(local_address, -EINVAL); assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->local_address = *local_address; @@ -239,13 +107,16 @@ int sd_dhcp6_client_set_local_address( int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, - const uint8_t *addr, size_t addr_len, + const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { assert_return(client, -EINVAL); assert_return(addr, -EINVAL); - assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL); + + /* Unlike the other setters, it is OK to set a new MAC address while the client is running, + * as the MAC address is used only when setting DUID or IAID. */ if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); @@ -253,12 +124,12 @@ int sd_dhcp6_client_set_mac( assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else { client->arp_type = ARPHRD_NONE; - client->mac_addr_len = 0; + client->hw_addr.length = 0; return 0; } - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; + memcpy(client->hw_addr.bytes, addr, addr_len); + client->hw_addr.length = addr_len; client->arp_type = arp_type; return 0; @@ -267,25 +138,47 @@ int sd_dhcp6_client_set_mac( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address) { + const struct in6_addr *pd_prefix) { + + _cleanup_free_ DHCP6Address *prefix = NULL; assert_return(client, -EINVAL); - assert_return(pd_address, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - client->hint_pd_prefix.iapdprefix.address = *pd_address; - client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen; + if (!pd_prefix) { + /* clear previous assignments. */ + dhcp6_ia_clear_addresses(&client->ia_pd); + return 0; + } - return 0; + assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL); + + prefix = new(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + *prefix = (DHCP6Address) { + .iapdprefix.address = *pd_prefix, + .iapdprefix.prefixlen = prefixlen, + }; + + LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix)); + return 1; } int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { int r; assert_return(client, -EINVAL); - assert_return(v, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); + if (!v) { + /* Clear the previous assignments. */ + ordered_set_clear(client->vendor_options); + return 0; + } + + r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v); if (r < 0) return r; @@ -295,10 +188,12 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * } static int client_ensure_duid(sd_dhcp6_client *client) { + assert(client); + if (client->duid_len != 0) return 0; - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len); } /** @@ -308,15 +203,15 @@ static int client_ensure_duid(sd_dhcp6_client *client) { */ static int dhcp6_client_set_duid_internal( sd_dhcp6_client *client, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { int r; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(duid_len == 0 || duid, -EINVAL); if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); @@ -331,41 +226,23 @@ static int dhcp6_client_set_duid_internal( client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); client->duid_len = sizeof(client->duid.type) + duid_len; - } else + + } else { #if 0 /* NM_IGNORED */ - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - - r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length, + client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len); + if (r == -EOPNOTSUPP) + return log_dhcp6_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); #else /* NM_IGNORED */ g_return_val_if_reached (-EINVAL); #endif /* NM_IGNORED */ + } return 0; } @@ -384,14 +261,6 @@ int sd_dhcp6_client_set_duid_llt( return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); } -static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "DUID-LLT", - [DUID_TYPE_EN] = "DUID-EN/Vendor", - [DUID_TYPE_LL] = "DUID-LL", - [DUID_TYPE_UUID] = "UUID", -}; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType); - int sd_dhcp6_client_duid_as_string( sd_dhcp6_client *client, char **duid) { @@ -403,7 +272,7 @@ int sd_dhcp6_client_duid_as_string( assert_return(client->duid_len > 0, -ENODATA); assert_return(duid, -EINVAL); - v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); + v = duid_type_to_string(be16toh(client->duid.type)); if (v) { s = strdup(v); if (!s) @@ -429,38 +298,62 @@ int sd_dhcp6_client_duid_as_string( int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - client->ia_na.ia_na.id = htobe32(iaid); - client->ia_pd.ia_pd.id = htobe32(iaid); + client->ia_na.header.id = htobe32(iaid); + client->ia_pd.header.id = htobe32(iaid); client->iaid_set = true; return 0; } +static int client_ensure_iaid(sd_dhcp6_client *client) { + int r; + uint32_t iaid; + + assert(client); + + if (client->iaid_set) + return 0; + + r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length, + /* legacy_unstable_byteorder = */ true, + /* use_mac = */ client->test_mode, + &iaid); + if (r < 0) + return r; + + client->ia_na.header.id = iaid; + client->ia_pd.header.id = iaid; + client->iaid_set = true; + + return 0; +} + +int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { + assert_return(client, -EINVAL); + assert_return(iaid, -EINVAL); + + if (!client->iaid_set) + return -ENODATA; + + *iaid = be32toh(client->ia_na.header.id); + + return 0; +} + void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { assert(client); client->test_mode = test_mode; } -int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { - assert_return(client, -EINVAL); - assert_return(iaid, -EINVAL); - - if (!client->iaid_set) - return -ENODATA; - - *iaid = be32toh(client->ia_na.ia_na.id); - - return 0; -} - int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); /* Make sure FQDN qualifies as DNS and as Linux hostname */ if (fqdn && @@ -472,7 +365,7 @@ int sd_dhcp6_client_set_fqdn( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->information_request = enabled; @@ -492,7 +385,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) size_t t; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (!dhcp6_option_can_request(option)) return -EINVAL; @@ -511,7 +404,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(mudurl, -EINVAL); assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); assert_return(http_url_is_valid(mudurl), -EINVAL); @@ -520,11 +413,10 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud } int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(user_class), -EINVAL); STRV_FOREACH(p, user_class) { @@ -542,11 +434,10 @@ int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const } int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(vendor_class), -EINVAL); STRV_FOREACH(p, vendor_class) { @@ -574,6 +465,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation); @@ -591,16 +483,20 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request); return 0; } -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { - assert_return(client, -EINVAL); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert(client); + assert(client->test_mode); - client->transaction_id = transaction_id; + /* This is for tests or fuzzers. */ + + client->transaction_id = transaction_id & htobe32(0x00ffffff); return 0; } @@ -631,6 +527,18 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { return 0; } +static void client_set_state(sd_dhcp6_client *client, DHCP6State state) { + assert(client); + + if (client->state == state) + return; + + log_dhcp6_client(client, "State changed: %s -> %s", + dhcp6_state_to_string(client->state), dhcp6_state_to_string(state)); + + client->state = state; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -638,30 +546,6 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static int client_reset(sd_dhcp6_client *client) { - assert(client); - - client->lease = sd_dhcp6_lease_unref(client->lease); - - client->receive_message = - sd_event_source_unref(client->receive_message); - - client->transaction_id = 0; - client->transaction_start = 0; - - client->retransmit_time = 0; - client->retransmit_count = 0; - - (void) event_source_disable(client->timeout_resend); - (void) event_source_disable(client->timeout_resend_expire); - (void) event_source_disable(client->timeout_t1); - (void) event_source_disable(client->timeout_t2); - - client->state = DHCP6_STATE_STOPPED; - - return 0; -} - static void client_stop(sd_dhcp6_client *client, int error) { DHCP6_CLIENT_DONT_DESTROY(client); @@ -669,21 +553,115 @@ static void client_stop(sd_dhcp6_client *client, int error) { client_notify(client, error); - client_reset(client); + client->lease = sd_dhcp6_lease_unref(client->lease); + + /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode, + * even though the lease is freed below. */ + client->information_request_time_usec = 0; + client->information_refresh_time_usec = 0; + + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + + client_set_state(client, DHCP6_STATE_STOPPED); } -static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { +static int client_append_common_options_in_managed_mode( + sd_dhcp6_client *client, + uint8_t **opt, + size_t *optlen, + const DHCP6IA *ia_na, + const DHCP6IA *ia_pd) { + + int r; + + assert(client); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + assert(opt); + assert(optlen); + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { + r = dhcp6_option_append_ia(opt, optlen, ia_na); + if (r < 0) + return r; + } + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { + r = dhcp6_option_append_ia(opt, optlen, ia_pd); + if (r < 0) + return r; + } + + if (client->fqdn) { + r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->user_class) { + r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_set_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + if (r < 0) + return r; + } + + return 0; +} + +static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) { + assert(client); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + return DHCP6_MESSAGE_INFORMATION_REQUEST; + case DHCP6_STATE_SOLICITATION: + return DHCP6_MESSAGE_SOLICIT; + case DHCP6_STATE_REQUEST: + return DHCP6_MESSAGE_REQUEST; + case DHCP6_STATE_RENEW: + return DHCP6_MESSAGE_RENEW; + case DHCP6_STATE_REBIND: + return DHCP6_MESSAGE_REBIND; + default: + assert_not_reached(); + } +} + +int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; - int r; - usec_t elapsed_usec; + usec_t elapsed_usec, time_now; be16_t elapsed_time; + int r; assert(client); + assert(client->event); + + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); + if (r < 0) + return r; len = sizeof(DHCP6Message) + optlen; @@ -694,194 +672,56 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { opt = (uint8_t *)(message + 1); message->transaction_id = client->transaction_id; + message->type = client_message_type_from_state(client); - switch(client->state) { + switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - message->type = DHCP6_MESSAGE_INFORMATION_REQUEST; - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - break; case DHCP6_STATE_SOLICITATION: - message->type = DHCP6_MESSAGE_SOLICIT; - - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->ia_na); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, - client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); - if (r < 0) - return r; - } - + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + &client->ia_na, &client->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - if (client->state == DHCP6_STATE_REQUEST) - message->type = DHCP6_MESSAGE_REQUEST; - else - message->type = DHCP6_MESSAGE_RENEW; - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } - - break; - + _fallthrough_; case DHCP6_STATE_REBIND: - message->type = DHCP6_MESSAGE_REBIND; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } + assert(client->lease); + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + client->lease->ia_na, client->lease->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return -EINVAL; default: assert_not_reached(); } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); + if (r < 0) + return r; + } + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, client->req_opts_len * sizeof(be16_t), client->req_opts); @@ -894,6 +734,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + ORDERED_HASHMAP_FOREACH(j, client->extra_options) { + r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + if (r < 0) + return r; + } + /* RFC 8415 Section 21.9. * A client MUST include an Elapsed Time option in messages to indicate how long the client has * been trying to complete a DHCP message exchange. */ @@ -903,12 +749,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); - if (r < 0) - return r; - } - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, len - optlen); if (r < 0) @@ -920,100 +760,43 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return 0; } -static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t2); - - log_dhcp6_client(client, "Timeout T2"); - - client_start(client, DHCP6_STATE_REBIND); - - return 0; -} - -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t1); - - log_dhcp6_client(client, "Timeout T1"); - - client_start(client, DHCP6_STATE_RENEW); - - return 0; -} - -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - DHCP6_CLIENT_DONT_DESTROY(client); - DHCP6State state; - - assert(s); - assert(client); - assert(client->event); - - state = client->state; - - client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - - /* RFC 3315, section 18.1.4., says that "...the client may choose to - use a Solicit message to locate a new DHCP server..." */ - if (state == DHCP6_STATE_REBIND) - client_start(client, DHCP6_STATE_SOLICITATION); - - return 0; -} - static usec_t client_timeout_compute_random(usec_t val) { - return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC; + return usec_sub_unsigned(val, random_u64_range(val / 10)); } static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - int r = 0; - sd_dhcp6_client *client = userdata; - usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - usec_t max_retransmit_duration = 0; - uint8_t max_retransmit_count = 0; + sd_dhcp6_client *client = ASSERT_PTR(userdata); + usec_t init_retransmit_time, max_retransmit_time; + int r; - assert(s); - assert(client); assert(client->event); - (void) event_source_disable(client->timeout_resend); - switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: init_retransmit_time = DHCP6_INF_TIMEOUT; max_retransmit_time = DHCP6_INF_MAX_RT; - break; case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - client_start(client, DHCP6_STATE_REQUEST); + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); return 0; } init_retransmit_time = DHCP6_SOL_TIMEOUT; max_retransmit_time = DHCP6_SOL_MAX_RT; - break; case DHCP6_STATE_REQUEST: + + if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + init_retransmit_time = DHCP6_REQ_TIMEOUT; max_retransmit_time = DHCP6_REQ_MAX_RT; - max_retransmit_count = DHCP6_REQ_MAX_RC; - break; case DHCP6_STATE_RENEW: @@ -1023,422 +806,365 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.3. says max retransmit duration will be the remaining time until T2. Instead of setting MRD, wait for T2 to trigger with the same end result */ - break; case DHCP6_STATE_REBIND: init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { - uint32_t expire = 0; - - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); - if (r < 0) { - client_stop(client, r); - return 0; - } - max_retransmit_duration = expire * USEC_PER_SEC; - } - + /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return 0; default: assert_not_reached(); } - if (max_retransmit_count > 0 && - client->retransmit_count >= max_retransmit_count) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - r = client_send_message(client, time_now); + r = dhcp6_client_send_message(client); if (r >= 0) client->retransmit_count++; if (client->retransmit_time == 0) { - client->retransmit_time = - client_timeout_compute_random(init_retransmit_time); + client->retransmit_time = client_timeout_compute_random(init_retransmit_time); if (client->state == DHCP6_STATE_SOLICITATION) client->retransmit_time += init_retransmit_time / 10; - } else { - assert(max_retransmit_time > 0); - if (client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - } + } else if (client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); log_dhcp6_client(client, "Next retransmission in %s", FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timer", true); - if (r < 0) - goto error; - - if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) { - - log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", - max_retransmit_duration / USEC_PER_SEC); - - r = event_reset_time(client->event, &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, USEC_PER_SEC, - client_timeout_resend_expire, client, - client->event_priority, "dhcp6-resend-expire-timer", true); - if (r < 0) - goto error; - } - -error: + r = event_reset_time_relative(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) client_stop(client, r); return 0; } -static int client_ensure_iaid(sd_dhcp6_client *client) { +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { int r; - uint32_t iaid; assert(client); + assert(client->event); - if (client->iaid_set) - return 0; + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_SOLICITATION: + assert(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_REQUEST: + assert(client->state == DHCP6_STATE_SOLICITATION); + break; + case DHCP6_STATE_RENEW: + assert(client->state == DHCP6_STATE_BOUND); + break; + case DHCP6_STATE_REBIND: + assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); + break; + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, - /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, - &iaid); + client_set_state(client, state); + + client->retransmit_time = 0; + client->retransmit_count = 0; + client->transaction_id = random_u32() & htobe32(0x00ffffff); + + r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start); if (r < 0) - return r; + goto error; - client->ia_na.ia_na.id = iaid; - client->ia_pd.ia_pd.id = iaid; - client->iaid_set = true; + r = event_reset_time(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); + if (r < 0) + goto error; + + r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); + if (r < 0) + goto error; + + return 0; + +error: + client_stop(client, r); + return r; +} + +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + DHCP6_CLIENT_DONT_DESTROY(client); + DHCP6State state; + + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); + + state = client->state; + + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); + + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION); return 0; } -int client_parse_message( +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); + + log_dhcp6_client(client, "Timeout T2"); + + (void) client_start_transaction(client, DHCP6_STATE_REBIND); + + return 0; +} + +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + + (void) event_source_disable(client->timeout_t1); + + log_dhcp6_client(client, "Timeout T1"); + + (void) client_start_transaction(client, DHCP6_STATE_RENEW); + + return 0; +} + +static int client_enter_bound_state(sd_dhcp6_client *client) { + usec_t lifetime_t1, lifetime_t2, lifetime_valid; + int r; + + assert(client); + assert(client->lease); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); + if (r < 0) + goto error; + + lifetime_t2 = client_timeout_compute_random(lifetime_t2); + lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2)); + + if (lifetime_t1 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1"); + event_source_disable(client->timeout_t1); + } else { + log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t1, + CLOCK_BOOTTIME, + lifetime_t1, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); + if (r < 0) + goto error; + } + + if (lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T2"); + event_source_disable(client->timeout_t2); + } else { + log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t2, + CLOCK_BOOTTIME, + lifetime_t2, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); + if (r < 0) + goto error; + } + + if (lifetime_valid == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite valid lifetime"); + event_source_disable(client->timeout_expire); + } else { + log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC)); + + r = event_reset_time_relative(client->event, &client->timeout_expire, + CLOCK_BOOTTIME, + lifetime_valid, USEC_PER_SEC, + client_timeout_expire, client, + client->event_priority, "dhcp6-lease-expire", true); + if (r < 0) + goto error; + } + + client_set_state(client, DHCP6_STATE_BOUND); + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + return 0; + +error: + client_stop(client, r); + return r; +} + +static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { + const char *type_str; + + assert(client); + assert(message); + + type_str = dhcp6_message_type_to_string(message->type); + if (type_str) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unexpected %s message, ignoring.", type_str); + else + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unsupported message type %u, ignoring.", message->type); +} + +static int client_process_information( sd_dhcp6_client *client, DHCP6Message *message, size_t len, - sd_dhcp6_lease *lease) { + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { - uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; - usec_t irt = IRT_DEFAULT; + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; int r; assert(client); assert(message); - assert(len >= sizeof(DHCP6Message)); - assert(lease); - len -= sizeof(DHCP6Message); - for (size_t offset = 0; offset < len;) { - uint16_t optcode; - size_t optlen; - const uint8_t *optval; + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); - if (r < 0) - return r; + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - switch (optcode) { - case SD_DHCP6_OPTION_CLIENTID: - if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", - dhcp6_message_type_to_string(message->type)); + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); - r = dhcp6_lease_set_clientid(lease, optval, optlen); - if (r < 0) - return r; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); - break; - - case SD_DHCP6_OPTION_SERVERID: - if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", - dhcp6_message_type_to_string(message->type)); - - r = dhcp6_lease_set_serverid(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_PREFERENCE: - if (optlen != 1) - return -EINVAL; - - r = dhcp6_lease_set_preference(lease, optval[0]); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: { - _cleanup_free_ char *msg = NULL; - - r = dhcp6_option_parse_status(optval, optlen, &msg); - if (r < 0) - return r; - - 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) { - log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); - continue; - } - - lease->ia = ia; - ia = (DHCP6IA) {}; - - 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) - return r; - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_add_dns(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_add_domains(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_add_ntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_add_sntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_CLIENT_FQDN: - r = dhcp6_lease_set_fqdn(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: - if (optlen != 4) - return -EINVAL; - - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; - break; - } - } - - uint8_t *clientid; - size_t clientid_len; - if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", - dhcp6_message_type_to_string(message->type)); - - if (clientid_len != client->duid_len || - memcmp(clientid, &client->duid, clientid_len) != 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", - 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) - 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."); - - if (lease->ia.addresses) { - lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); - } - - if (lease->pd.addresses) { - lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); - } - } - - client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + /* Do not call client_stop() here, as it frees the acquired lease. */ + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + client_set_state(client, DHCP6_STATE_STOPPED); + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); return 0; } -static int client_receive_reply( +static int client_process_reply( sd_dhcp6_client *client, - DHCP6Message *reply, + DHCP6Message *message, size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool rapid_commit; int r; assert(client); - assert(reply); - assert(t); + assert(message); - if (reply->type != DHCP6_MESSAGE_REPLY) - return 0; + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - r = dhcp6_lease_new(&lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return -ENOMEM; + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); - r = client_parse_message(client, reply, len, lease); + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + return client_enter_bound_state(client); +} + +static int client_process_advertise_or_rapid_commit_reply( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + uint8_t pref_advertise, pref_lease = 0; + int r; + + assert(client); + assert(message); + + if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY)) + return log_invalid_message_type(client, message); + + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m", + dhcp6_message_type_to_string(message->type)); + + if (message->type == DHCP6_MESSAGE_REPLY) { + bool rapid_commit; - if (client->state == DHCP6_STATE_SOLICITATION) { r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); if (r < 0) return r; if (!rapid_commit) - return 0; + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received reply message without rapid commit flag, ignoring."); + + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + return client_enter_bound_state(client); } - sd_dhcp6_lease_unref(client->lease); - client->lease = TAKE_PTR(lease); - - return DHCP6_STATE_BOUND; -} - -static int client_receive_advertise( - sd_dhcp6_client *client, - DHCP6Message *advertise, - size_t len, - const triple_timestamp *t, - const struct in6_addr *server_address) { - - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t pref_advertise = 0, pref_lease = 0; - int r; - - assert(client); - assert(advertise); - assert(t); - - if (advertise->type != DHCP6_MESSAGE_ADVERTISE) - return 0; - - r = dhcp6_lease_new(&lease); - if (r < 0) - return r; - - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; - - r = client_parse_message(client, advertise, len, lease); - if (r < 0) - return r; - r = dhcp6_lease_get_preference(lease, &pref_advertise); if (r < 0) return r; - r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (client->lease) { + r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (r < 0) + return r; + } - if (r < 0 || pref_advertise > pref_lease) { + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + if (!client->lease || pref_advertise > pref_lease) { + /* If this is the first advertise message or has higher preference, then save the lease. */ sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = 0; } if (pref_advertise == 255 || client->retransmit_count > 1) - r = DHCP6_STATE_REQUEST; + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); - return r; + return 0; } static int client_receive_message( @@ -1447,7 +1173,7 @@ static int client_receive_message( revents, void *userdata) { - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); DHCP6_CLIENT_DONT_DESTROY(client); /* This needs to be initialized with zero. See #20741. */ CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {}; @@ -1466,11 +1192,6 @@ static int client_receive_message( _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; ssize_t buflen, len; - int r = 0; - - assert(s); - assert(client); - assert(client->event); buflen = next_datagram_size_fd(fd); if (buflen < 0) { @@ -1517,236 +1238,43 @@ static int client_receive_message( triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); } - if (!triple_timestamp_is_set(&t)) - triple_timestamp_get(&t); - - if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { - const char *type_str = dhcp6_message_type_to_string(message->type); - if (type_str) - log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str); - else - log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type); - return 0; - } - 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, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_information(client, message, len, &t, server_address) < 0) return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - client_start(client, DHCP6_STATE_STOPPED); - break; case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); + if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0) return 0; - } + break; - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - break; - } - - _fallthrough_; /* for Solicitation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: - - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_reply(client, message, len, &t, server_address) < 0) return 0; - } - - if (r == DHCP6_STATE_BOUND) { - r = client_start(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - } - break; case DHCP6_STATE_BOUND: - - break; - case DHCP6_STATE_STOPPED: - return 0; default: assert_not_reached(); } - log_dhcp6_client(client, "Recv %s", - dhcp6_message_type_to_string(message->type)); - return 0; } -static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, - uint32_t *lifetime_t2) { - assert_return(client, -EINVAL); - assert_return(client->lease, -EINVAL); - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); - - return 0; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); - - return 0; - } - - return -ENOMSG; -} - -static int client_start(sd_dhcp6_client *client, DHCP6State state) { - int r; - usec_t timeout, time_now; - uint32_t lifetime_t1, lifetime_t2; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->state != state, -EINVAL); - - (void) event_source_disable(client->timeout_resend_expire); - (void) event_source_disable(client->timeout_resend); - client->retransmit_time = 0; - client->retransmit_count = 0; - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - if (!client->receive_message) { - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; - } - - switch (state) { - case DHCP6_STATE_STOPPED: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - client->state = DHCP6_STATE_STOPPED; - - return 0; - } - - _fallthrough_; - case DHCP6_STATE_SOLICITATION: - client->state = DHCP6_STATE_SOLICITATION; - - break; - - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - client->state = state; - - break; - - case DHCP6_STATE_BOUND: - - r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); - if (r < 0) - goto error; - - if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { - log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - lifetime_t1, lifetime_t2); - - return 0; - } - - timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); - - log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); - - log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); - if (r < 0) - goto error; - - client->state = state; - - return 0; - default: - assert_not_reached(); - } - - client->transaction_id = random_u32() & htobe32(0x00ffffff); - client->transaction_start = time_now; - - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timeout", true); - if (r < 0) - goto error; - - return 0; - - error: - client_reset(client); - return r; -} - int sd_dhcp6_client_stop(sd_dhcp6_client *client) { if (!client) return 0; client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->receive_message = sd_event_source_unref(client->receive_message); client->fd = safe_close(client->fd); return 0; @@ -1766,16 +1294,12 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(client->information_request || client->request_ia != 0, -EINVAL); - if (client->state != DHCP6_STATE_STOPPED) - return -EBUSY; - - if (!client->information_request && client->request_ia == 0) - return -EINVAL; - - r = client_reset(client); - if (r < 0) - return r; + /* Even if the client is in the STOPPED state, the lease acquired in the previous information + * request may be stored. */ + client->lease = sd_dhcp6_lease_unref(client->lease); r = client_ensure_iaid(client); if (r < 0) @@ -1790,7 +1314,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) { _cleanup_free_ char *p = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + (void) in6_addr_to_string(&client->local_address, &p); return log_dhcp6_client_errno(client, r, "Failed to bind to UDP socket at address %s: %m", strna(p)); } @@ -1798,6 +1322,24 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { client->fd = r; } + if (!client->receive_message) { + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL; + + r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s, client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "dhcp6-receive-message"); + if (r < 0) + return r; + + client->receive_message = TAKE_PTR(s); + } + if (client->information_request) { usec_t t = now(CLOCK_MONOTONIC); @@ -1808,10 +1350,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { state = DHCP6_STATE_INFORMATION_REQUEST; } - log_dhcp6_client(client, "Started in %s mode", + log_dhcp6_client(client, "Starting in %s mode", client->information_request ? "Information request" : "Managed"); - return client_start(client, state); + return client_start_transaction(client, state); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { @@ -1819,6 +1361,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 assert_return(client, -EINVAL); assert_return(!client->event, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (event) client->event = sd_event_ref(event); @@ -1835,6 +1378,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->event = sd_event_unref(client->event); @@ -1848,24 +1392,26 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { } static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { - assert(client); + if (!client) + return NULL; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + sd_dhcp6_lease_unref(client->lease); - client_reset(client); + sd_event_source_disable_unref(client->receive_message); + sd_event_source_disable_unref(client->timeout_resend); + sd_event_source_disable_unref(client->timeout_expire); + sd_event_source_disable_unref(client->timeout_t1); + sd_event_source_disable_unref(client->timeout_t2); + sd_event_unref(client->event); client->fd = safe_close(client->fd); - sd_dhcp6_client_detach_event(client); - free(client->req_opts); free(client->fqdn); free(client->mudurl); - + dhcp6_ia_clear_addresses(&client->ia_pd); ordered_hashmap_free(client->extra_options); + ordered_set_free(client->vendor_options); strv_free(client->user_class); strv_free(client->vendor_class); free(client->ifname); @@ -1901,8 +1447,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, .fd = -1, .req_opts_len = ELEMENTSOF(default_req_opts), - .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1, - .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1, .req_opts = TAKE_PTR(req_opts), }; 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 b46a2e3341..fd47cb7351 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 @@ -8,10 +8,21 @@ #include #include "alloc-util.h" +#include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "strv.h" -#include "util.h" + +#define IRT_DEFAULT (1 * USEC_PER_DAY) +#define IRT_MINIMUM (600 * USEC_PER_SEC) + +static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) { + assert(lease); + + if (timestamp && triple_timestamp_is_set(timestamp)) + lease->timestamp = *timestamp; + else + triple_timestamp_get(&lease->timestamp); +} int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { assert_return(lease, -EINVAL); @@ -26,6 +37,68 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } +static usec_t sec2usec(uint32_t sec) { + return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC; +} + +static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { + uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; + + assert(lease); + assert(lease->ia_na || lease->ia_pd); + + if (lease->ia_na) { + t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_na->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid)); + } + + if (lease->ia_pd) { + t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_pd->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid)); + } + + if (t2 == 0 || t2 > min_valid_lt) { + /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes, + * then adjust lifetime with it. */ + t1 = min_valid_lt / 2; + t2 = min_valid_lt / 10 * 8; + } + + lease->lifetime_valid = sec2usec(min_valid_lt); + lease->lifetime_t1 = sec2usec(t1); + lease->lifetime_t2 = sec2usec(t2); +} + +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) { + assert(lease); + + if (!lease->ia_na && !lease->ia_pd) + return -ENODATA; + + if (ret_t1) + *ret_t1 = lease->lifetime_t1; + if (ret_t2) + *ret_t2 = lease->lifetime_t2; + if (ret_valid) + *ret_valid = lease->lifetime_valid; + return 0; +} + +static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) { + assert(lease); + + if (server_address) + lease->server_address = *server_address; + else + lease->server_address = (struct in6_addr) {}; +} + int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { assert_return(lease, -EINVAL); assert_return(ret, -EINVAL); @@ -34,55 +107,35 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re return 0; } -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { - DHCP6Address *addr; - uint32_t valid = 0, t; +void dhcp6_ia_clear_addresses(DHCP6IA *ia) { + assert(ia); - assert_return(ia, -EINVAL); - assert_return(expire, -EINVAL); + LIST_FOREACH(addresses, a, ia->addresses) + free(a); - LIST_FOREACH(addresses, addr, ia->addresses) { - t = be32toh(addr->iaaddr.lifetime_valid); - if (valid < t) - valid = t; - } - - t = be32toh(ia->ia_na.lifetime_t2); - if (t > valid) - return -EINVAL; - - *expire = valid - t; - - return 0; + ia->addresses = NULL; } -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { - DHCP6Address *address; - +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) { if (!ia) return NULL; - while (ia->addresses) { - address = ia->addresses; + dhcp6_ia_clear_addresses(ia); - LIST_REMOVE(addresses, ia->addresses, address); - - free(address); - } - - return NULL; + return mfree(ia); } int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *clientid; + uint8_t *clientid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - clientid = memdup(id, len); - if (!clientid) - return -ENOMEM; + if (len > 0) { + clientid = memdup(id, len); + if (!clientid) + return -ENOMEM; + } free_and_replace(lease->clientid, clientid); lease->clientid_len = len; @@ -91,7 +144,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->clientid) return -ENODATA; @@ -105,15 +158,16 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re } int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *serverid; + uint8_t *serverid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - serverid = memdup(id, len); - if (!serverid) - return -ENOMEM; + if (len > 0) { + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + } free_and_replace(lease->serverid, serverid); lease->serverid_len = len; @@ -122,7 +176,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->serverid) return -ENODATA; @@ -131,107 +185,99 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re *ret_id = lease->serverid; if (ret_len) *ret_len = lease->serverid_len; - return 0; } int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { - assert_return(lease, -EINVAL); + assert(lease); lease->preference = preference; - return 0; } -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(preference, -EINVAL); - - if (!lease) - return -EINVAL; - - *preference = lease->preference; +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) { + assert(lease); + assert(ret); + *ret = lease->preference; return 0; } int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { - assert_return(lease, -EINVAL); + assert(lease); lease->rapid_commit = true; - return 0; } -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { - assert_return(lease, -EINVAL); - assert_return(rapid_commit, -EINVAL); - - *rapid_commit = lease->rapid_commit; +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) { + assert(lease); + assert(ret); + *ret = lease->rapid_commit; return 0; } -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_address( + sd_dhcp6_lease *lease, + struct in6_addr *ret_addr, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->addr_iter) - return -ENOMSG; + return -ENODATA; - memcpy(addr, &lease->addr_iter->iaaddr.address, - sizeof(struct in6_addr)); - *lifetime_preferred = - be32toh(lease->addr_iter->iaaddr.lifetime_preferred); - *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + if (ret_addr) + *ret_addr = lease->addr_iter->iaaddr.address; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); lease->addr_iter = lease->addr_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { if (lease) - lease->addr_iter = lease->ia.addresses; + lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; } -int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, - uint8_t *prefix_len, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_pd( + sd_dhcp6_lease *lease, + struct in6_addr *ret_prefix, + uint8_t *ret_prefix_len, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(prefix, -EINVAL); - assert_return(prefix_len, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->prefix_iter) - return -ENOMSG; + return -ENODATA; - memcpy(prefix, &lease->prefix_iter->iapdprefix.address, - sizeof(struct in6_addr)); - *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; - *lifetime_preferred = - be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); - *lifetime_valid = - be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + if (ret_prefix) + *ret_prefix = lease->prefix_iter->iapdprefix.address; + if (ret_prefix_len) + *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); lease->prefix_iter = lease->prefix_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { if (lease) - lease->prefix_iter = lease->pd.addresses; + lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -243,7 +289,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); if (!lease->dns) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->dns; @@ -255,8 +301,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t _cleanup_strv_free_ char **domains = NULL; int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -273,7 +319,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(ret, -EINVAL); if (!lease->domains) - return -ENOENT; + return -ENODATA; *ret = lease->domains; return strv_length(lease->domains); @@ -282,8 +328,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); for (size_t offset = 0; offset < optlen;) { const uint8_t *subval; @@ -294,11 +340,11 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt if (r < 0) return r; - switch(subopt) { + switch (subopt) { case DHCP6_NTP_SUBOPTION_SRV_ADDR: case DHCP6_NTP_SUBOPTION_MC_ADDR: if (sublen != 16) - return 0; + return -EINVAL; r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) @@ -328,8 +374,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt } int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -354,14 +400,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr ** return lease->sntp_count; } - return -ENOENT; + return -ENODATA; } int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); if (!lease->ntp_fqdn) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->ntp_fqdn; @@ -369,11 +415,14 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { } int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - int r; char *fqdn; + int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); + + if (optlen == 0) + return 0; if (optlen < 2) return -ENODATA; @@ -392,20 +441,222 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(ret, -EINVAL); if (!lease->fqdn) - return -ENOENT; + return -ENODATA; *ret = lease->fqdn; return 0; } +static int dhcp6_lease_parse_message( + sd_dhcp6_client *client, + sd_dhcp6_lease *lease, + const DHCP6Message *message, + size_t len) { + + usec_t irt = IRT_DEFAULT; + int r; + + assert(client); + assert(lease); + assert(message); + assert(len >= sizeof(DHCP6Message)); + + len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; + + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; + + switch (optcode) { + case SD_DHCP6_OPTION_CLIENTID: + if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_clientid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_SERVERID: + if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_serverid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_PREFERENCE: + if (optlen != 1) + return -EINVAL; + + r = dhcp6_lease_set_preference(lease, optval[0]); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(optval, optlen, &msg); + if (r < 0) + return r; + + 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_ia_freep) DHCP6IA *ia = NULL; + + 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_na.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_na) { + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_na); + lease->ia_na = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + 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.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_pd) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_pd); + lease->ia_pd = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_add_dns(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_add_domains(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_add_ntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_add_sntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_CLIENT_FQDN: + r = dhcp6_lease_set_fqdn(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: + if (optlen != 4) + return -EINVAL; + + irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + break; + } + } + + uint8_t *clientid; + size_t clientid_len; + if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "%s message does not contain client ID. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "The client ID in %s message does not match. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + log_dhcp6_client(client, "New information request will be refused in %s.", + FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC)); + + } else { + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + 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_na && !lease->ia_pd) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "No IA_PD prefix or IA_NA address received. Ignoring."); + + dhcp6_lease_set_lifetime(lease); + } + + return 0; +} + static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { if (!lease) return NULL; free(lease->clientid); free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); + dhcp6_ia_free(lease->ia_na); + dhcp6_ia_free(lease->ia_pd); free(lease->dns); free(lease->fqdn); strv_free(lease->domains); @@ -421,14 +672,47 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; - lease = new0(sd_dhcp6_lease, 1); + assert(ret); + + lease = new(sd_dhcp6_lease, 1); if (!lease) return -ENOMEM; - lease->n_ref = 1; - - LIST_HEAD_INIT(lease->ia.addresses); + *lease = (sd_dhcp6_lease) { + .n_ref = 1, + }; *ret = lease; return 0; } + +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; + + assert(client); + assert(message); + assert(len >= sizeof(DHCP6Message)); + assert(ret); + + r = dhcp6_lease_new(&lease); + if (r < 0) + return r; + + dhcp6_lease_set_timestamp(lease, timestamp); + dhcp6_lease_set_server_address(lease, server_address); + + r = dhcp6_lease_parse_message(client, lease, message, len); + if (r < 0) + return r; + + *ret = TAKE_PTR(lease); + return 0; +} 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 index f50e9c8db0..496eb8f611 100644 --- a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c +++ b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c @@ -71,7 +71,7 @@ static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { goto remove_one; if (t == USEC_INFINITY) - t = now(clock_boottime_or_monotonic()); + t = now(CLOCK_BOOTTIME); if (n->until > t) break; @@ -450,7 +450,7 @@ static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) 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(), + CLOCK_BOOTTIME, n->until, 0, on_timer_event, lldp_rx, lldp_rx->event_priority, "lldp-rx-timer", true); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c index 13e16f9b31..6e3c2ce964 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c @@ -6,6 +6,7 @@ #include "event-source.h" #include "event-util.h" +#include "fd-util.h" #include "log.h" #include "string-util.h" @@ -123,3 +124,43 @@ int event_source_is_enabled(sd_event_source *s) { return sd_event_source_get_enabled(s, NULL); } + +#if 0 /* NM_IGNORED */ +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + _cleanup_close_ int fd = -1; + int r; + + assert(e); + + /* Allocates an IO event source that gets woken up whenever the clock changes. Needs to be recreated on each event */ + + fd = time_change_fd(); + if (fd < 0) + return fd; + + r = sd_event_add_io(e, &s, fd, EPOLLIN, callback, userdata); + if (r < 0) + return r; + + r = sd_event_source_set_io_fd_own(s, true); + if (r < 0) + return r; + + TAKE_FD(fd); + + r = sd_event_source_set_description(s, "time-change"); + if (r < 0) + return r; + + if (ret) + *ret = TAKE_PTR(s); + else { + r = sd_event_source_set_floating(s, true); + if (r < 0) + return r; + } + + return 0; +} +#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h index 64a4199244..abd043bdcc 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h +++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h @@ -29,3 +29,5 @@ int event_reset_time_relative( bool force_reset); int event_source_disable(sd_event_source *s); int event_source_is_enabled(sd_event_source *s); + +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata); 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 e87d900c4a..240476e634 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 @@ -1924,7 +1924,6 @@ static int event_make_inode_data( static uint32_t inode_data_determine_mask(struct inode_data *d) { bool excl_unlink = true; uint32_t combined = 0; - sd_event_source *s; assert(d); @@ -3462,9 +3461,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { /* The queue overran, let's pass this event to all event sources connected to this inotify * object */ - HASHMAP_FOREACH(inode_data, d->inodes) { - sd_event_source *s; - + HASHMAP_FOREACH(inode_data, d->inodes) LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { if (event_source_is_offline(s)) @@ -3474,10 +3471,8 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { if (r < 0) return r; } - } } else { struct inode_data *inode_data; - sd_event_source *s; /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from * our watch descriptor table. */ @@ -3525,7 +3520,6 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { } static int process_inotify(sd_event *e) { - struct inotify_data *d; int r, done = 0; assert(e); @@ -3910,6 +3904,7 @@ static int epoll_wait_usec( int msec; #if 0 static bool epoll_pwait2_absent = false; + int r; /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. * @@ -3918,12 +3913,10 @@ static int epoll_wait_usec( * https://github.com/systemd/systemd/issues/19052. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { - struct timespec ts; - r = epoll_pwait2(fd, events, maxevents, - timespec_store(&ts, timeout), + TIMESPEC_STORE(timeout), NULL); if (r >= 0) return r; @@ -4324,12 +4317,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) return -EOPNOTSUPP; - /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here, - * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for - * the purpose of getting the time this doesn't matter. */ - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) - return -EOPNOTSUPP; - if (!triple_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran before and thus have no cached time. */ *usec = now(clock); @@ -4469,6 +4456,7 @@ _public_ int sd_event_source_get_floating(sd_event_source *s) { return s->floating; } +#endif /* NM_IGNORED */ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { assert_return(s, -EINVAL); @@ -4492,6 +4480,7 @@ _public_ int sd_event_source_set_floating(sd_event_source *s, int b) { return 1; } +#if 0 /* NM_IGNORED */ _public_ int sd_event_source_get_exit_on_failure(sd_event_source *s) { assert_return(s, -EINVAL); assert_return(s->type != SOURCE_EXIT, -EDOM); 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 9ec2abc884..f1a3a42ea6 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 @@ -15,29 +15,6 @@ #include "sync-util.h" #if 0 /* NM_IGNORED */ -char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) { - unsigned n, k = 0; - - assert(s); - - /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - - for (n = 0; n < 16; n++) { - - if (IN_SET(n, 4, 6, 8, 10)) - s[k++] = '-'; - - s[k++] = hexchar(id.bytes[n] >> 4); - s[k++] = hexchar(id.bytes[n] & 0xF); - } - - assert(k == 36); - - s[k] = 0; - - return s; -} - bool id128_is_valid(const char *s) { size_t i, l; @@ -158,13 +135,13 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { assert(f < _ID128_FORMAT_MAX); if (f != ID128_UUID) { - sd_id128_to_string(id, buffer); - buffer[32] = '\n'; - sz = 33; + assert_se(sd_id128_to_string(id, buffer)); + buffer[SD_ID128_STRING_MAX - 1] = '\n'; + sz = SD_ID128_STRING_MAX; } else { - id128_to_uuid_string(id, buffer); - buffer[36] = '\n'; - sz = 37; + assert_se(sd_id128_to_uuid_string(id, buffer)); + buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; + sz = SD_ID128_UUID_STRING_MAX; } r = loop_write(fd, buffer, sz, false); @@ -234,4 +211,20 @@ int id128_get_product(sd_id128_t *ret) { *ret = uuid; return 0; } + +int id128_equal_string(const char *s, sd_id128_t id) { + sd_id128_t parsed; + int r; + + if (!s) + return false; + + /* Checks if the specified string matches a valid string representation of the specified 128 bit ID/uuid */ + + r = sd_id128_from_string(s, &parsed); + if (r < 0) + return r; + + return sd_id128_equal(parsed, id); +} #endif /* NM_IGNORED */ 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 b7327a1f07..65a278c8ee 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 @@ -8,12 +8,6 @@ #include "hash-funcs.h" #include "macro.h" -#define ID128_UUID_STRING_MAX 37 - -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 { @@ -40,3 +34,5 @@ extern const struct hash_ops id128_hash_ops; sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); + +int id128_equal_string(const char *s, sd_id128_t id); diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c index c3ed3bbb5b..8e47e26e1e 100644 --- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c @@ -22,16 +22,36 @@ #if 0 /* NM_IGNORED */ _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { - unsigned n; - assert_return(s, NULL); - for (n = 0; n < 16; n++) { + for (size_t n = 0; n < 16; n++) { s[n*2] = hexchar(id.bytes[n] >> 4); s[n*2+1] = hexchar(id.bytes[n] & 0xF); } - s[32] = 0; + s[SD_ID128_STRING_MAX-1] = 0; + + return s; +} + +_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) { + size_t k = 0; + + assert_return(s, NULL); + + /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ + + for (size_t n = 0; n < 16; n++) { + + if (IN_SET(n, 4, 6, 8, 10)) + s[k++] = '-'; + + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); + } + + assert(k == SD_ID128_UUID_STRING_MAX - 1); + s[k] = 0; return s; } @@ -261,9 +281,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be - * fine for UUIDS. */ - r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND); + r = genuine_random_bytes(&t, sizeof(t), 0); if (r < 0) return r; 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 4af4b45f2d..834f80b421 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h @@ -44,67 +44,175 @@ enum { * The client may want to start acquiring link-local addresses. */ }; +/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */ enum { - SD_DHCP_OPTION_PAD = 0, - SD_DHCP_OPTION_SUBNET_MASK = 1, - SD_DHCP_OPTION_TIME_OFFSET = 2, - SD_DHCP_OPTION_ROUTER = 3, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, - SD_DHCP_OPTION_LPR_SERVER = 9, - SD_DHCP_OPTION_HOST_NAME = 12, - SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, - SD_DHCP_OPTION_DOMAIN_NAME = 15, - SD_DHCP_OPTION_ROOT_PATH = 17, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, - SD_DHCP_OPTION_POLICY_FILTER = 21, - SD_DHCP_OPTION_INTERFACE_MDR = 22, - SD_DHCP_OPTION_INTERFACE_TTL = 23, - SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - SD_DHCP_OPTION_INTERFACE_MTU = 26, - SD_DHCP_OPTION_BROADCAST = 28, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_ROUTER_DISCOVER = 31, - SD_DHCP_OPTION_STATIC_ROUTE = 33, - SD_DHCP_OPTION_NTP_SERVER = 42, - SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NODETYPE = 46, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_SCOPE = 47, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, - SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, - SD_DHCP_OPTION_OVERLOAD = 52, - SD_DHCP_OPTION_MESSAGE_TYPE = 53, - SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, - SD_DHCP_OPTION_ERROR_MESSAGE = 56, - SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, - SD_DHCP_OPTION_REBINDING_T2_TIME = 59, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, - SD_DHCP_OPTION_SMTP_SERVER = 69, - SD_DHCP_OPTION_POP3_SERVER = 70, - SD_DHCP_OPTION_USER_CLASS = 77, - SD_DHCP_OPTION_FQDN = 81, - SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, - SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, - SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, - SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, - SD_DHCP_OPTION_SIP_SERVER = 120, - SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, - SD_DHCP_OPTION_MUD_URL = 161, - SD_DHCP_OPTION_6RD = 212, + SD_DHCP_OPTION_PAD = 0, /* [RFC2132] */ + SD_DHCP_OPTION_SUBNET_MASK = 1, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_OFFSET = 2, /* [RFC2132], deprecated by 100 and 101 */ + SD_DHCP_OPTION_ROUTER = 3, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_SERVER = 4, /* [RFC2132] */ + SD_DHCP_OPTION_NAME_SERVER = 5, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, /* [RFC2132] */ + SD_DHCP_OPTION_LOG_SERVER = 7, /* [RFC2132] */ + SD_DHCP_OPTION_QUOTES_SERVER = 8, /* [RFC2132] */ + SD_DHCP_OPTION_LPR_SERVER = 9, /* [RFC2132] */ + SD_DHCP_OPTION_IMPRESS_SERVER = 10, /* [RFC2132] */ + SD_DHCP_OPTION_RLP_SERVER = 11, /* [RFC2132] */ + SD_DHCP_OPTION_HOST_NAME = 12, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, /* [RFC2132] */ + SD_DHCP_OPTION_MERIT_DUMP_FILE = 14, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME = 15, /* [RFC2132] */ + SD_DHCP_OPTION_SWAP_SERVER = 16, /* [RFC2132] */ + SD_DHCP_OPTION_ROOT_PATH = 17, /* [RFC2132] */ + SD_DHCP_OPTION_EXTENSION_FILE = 18, /* [RFC2132] */ + SD_DHCP_OPTION_FORWARD = 19, /* [RFC2132] */ + SD_DHCP_OPTION_SOURCE_ROUTE = 20, /* [RFC2132] */ + SD_DHCP_OPTION_POLICY_FILTER = 21, /* [RFC2132] */ + SD_DHCP_OPTION_MAX_DATAGRAM_ASSEMBLY = 22, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_IP_TTL = 23, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_TIMEOUT = 24, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_PLATEAU = 25, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_INTERFACE = 26, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_SUBNET = 27, /* [RFC2132] */ + SD_DHCP_OPTION_BROADCAST = 28, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_DISCOVERY = 29, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_SUPPLIER = 30, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_DISCOVERY = 31, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_REQUEST = 32, /* [RFC2132] */ + SD_DHCP_OPTION_STATIC_ROUTE = 33, /* [RFC2132] */ + SD_DHCP_OPTION_TRAILERS = 34, /* [RFC2132] */ + SD_DHCP_OPTION_ARP_TIMEOUT = 35, /* [RFC2132] */ + SD_DHCP_OPTION_ETHERNET = 36, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_TCP_TTL = 37, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_TIME = 38, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_DATA = 39, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_DOMAIN = 40, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER = 41, /* [RFC2132] */ + SD_DHCP_OPTION_NTP_SERVER = 42, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER = 44, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_DIST_SERVER = 45, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE = 46, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_SCOPE = 47, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_FONT = 48, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_MANAGER = 49, /* [RFC2132] */ + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, /* [RFC2132] */ + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, /* [RFC2132] */ + SD_DHCP_OPTION_OVERLOAD = 52, /* [RFC2132] */ + SD_DHCP_OPTION_MESSAGE_TYPE = 53, /* [RFC2132] */ + SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, /* [RFC2132] */ + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, /* [RFC2132] */ + SD_DHCP_OPTION_ERROR_MESSAGE = 56, /* [RFC2132] */ + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, /* [RFC2132] */ + SD_DHCP_OPTION_RENEWAL_TIME = 58, /* [RFC2132] */ + SD_DHCP_OPTION_REBINDING_TIME = 59, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, /* [RFC2132] */ + SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, /* [RFC2132] */ + SD_DHCP_OPTION_NETWARE_IP_DOMAIN = 62, /* [RFC2242] */ + SD_DHCP_OPTION_NETWARE_IP_OPTION = 63, /* [RFC2242] */ + SD_DHCP_OPTION_NIS_DOMAIN_NAME = 64, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER_ADDR = 65, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_SERVER_NAME = 66, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILENAME = 67, /* [RFC2132] */ + SD_DHCP_OPTION_HOME_AGENT_ADDRESSES = 68, /* [RFC2132] */ + SD_DHCP_OPTION_SMTP_SERVER = 69, /* [RFC2132] */ + SD_DHCP_OPTION_POP3_SERVER = 70, /* [RFC2132] */ + SD_DHCP_OPTION_NNTP_SERVER = 71, /* [RFC2132] */ + SD_DHCP_OPTION_WWW_SERVER = 72, /* [RFC2132] */ + SD_DHCP_OPTION_FINGER_SERVER = 73, /* [RFC2132] */ + SD_DHCP_OPTION_IRC_SERVER = 74, /* [RFC2132] */ + SD_DHCP_OPTION_STREETTALK_SERVER = 75, /* [RFC2132] */ + SD_DHCP_OPTION_STDA_SERVER = 76, /* [RFC2132] */ + SD_DHCP_OPTION_USER_CLASS = 77, /* [RFC3004] */ + SD_DHCP_OPTION_DIRECTORY_AGENT = 78, /* [RFC2610] */ + SD_DHCP_OPTION_SERVICE_SCOPE = 79, /* [RFC2610] */ + SD_DHCP_OPTION_RAPID_COMMIT = 80, /* [RFC4039] */ + SD_DHCP_OPTION_FQDN = 81, /* [RFC4702] */ + SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, /* [RFC3046] */ + SD_DHCP_OPTION_ISNS = 83, /* [RFC4174] */ + /* option code 84 is unassigned [RFC3679] */ + SD_DHCP_OPTION_NDS_SERVER = 85, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_TREE_NAME = 86, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_CONTEXT = 87, /* [RFC2241] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_DOMAIN_NAM = 88, /* [RFC4280] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_ADDRESS = 89, /* [RFC4280] */ + SD_DHCP_OPTION_AUTHENTICATION = 90, /* [RFC3118] */ + SD_DHCP_OPTION_CLIENT_LAST_TRANSACTION_TIME = 91, /* [RFC4388] */ + SD_DHCP_OPTION_ASSOCIATED_IP = 92, /* [RFC4388] */ + SD_DHCP_OPTION_CLIENT_SYSTEM = 93, /* [RFC4578] */ + SD_DHCP_OPTION_CLIENT_NDI = 94, /* [RFC4578] */ + SD_DHCP_OPTION_LDAP = 95, /* [RFC3679] */ + /* option code 96 is unassigned [RFC3679] */ + SD_DHCP_OPTION_UUID = 97, /* [RFC4578] */ + SD_DHCP_OPTION_USER_AUTHENTICATION = 98, /* [RFC2485] */ + SD_DHCP_OPTION_GEOCONF_CIVIC = 99, /* [RFC4776] */ + SD_DHCP_OPTION_POSIX_TIMEZONE = 100, /* [RFC4833] */ + SD_DHCP_OPTION_TZDB_TIMEZONE = 101, /* [RFC4833] */ + /* option codes 102-107 are unassigned [RFC3679] */ + SD_DHCP_OPTION_IPV6_ONLY_PREFERRED = 108, /* [RFC8925] */ + SD_DHCP_OPTION_DHCP4O6_SOURCE_ADDRESS = 109, /* [RFC8539] */ + /* option codes 110-111 are unassigned [RFC3679] */ + SD_DHCP_OPTION_NETINFO_ADDRESS = 112, /* [RFC3679] */ + SD_DHCP_OPTION_NETINFO_TAG = 113, /* [RFC3679] */ + SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL = 114, /* [RFC8910] */ + /* option code 115 is unassigned [RFC3679] */ + SD_DHCP_OPTION_AUTO_CONFIG = 116, /* [RFC2563] */ + SD_DHCP_OPTION_NAME_SERVICE_SEARCH = 117, /* [RFC2937] */ + SD_DHCP_OPTION_SUBNET_SELECTION = 118, /* [RFC3011] */ + SD_DHCP_OPTION_DOMAIN_SEARCH = 119, /* [RFC3397] */ + SD_DHCP_OPTION_SIP_SERVER = 120, /* [RFC3361] */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, /* [RFC3442] */ + SD_DHCP_OPTION_CABLELABS_CLIENT_CONFIGURATION = 122, /* [RFC3495] */ + SD_DHCP_OPTION_GEOCONF = 123, /* [RFC6225] */ + SD_DHCP_OPTION_VENDOR_CLASS = 124, /* [RFC3925] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION = 125, /* [RFC3925] */ + /* option codes 126-127 are unassigned [RFC3679] */ + /* option codes 128-135 are assigned to use by PXE, but they are vendor specific [RFC4578] */ + SD_DHCP_OPTION_PANA_AGENT = 136, /* [RFC5192] */ + SD_DHCP_OPTION_LOST_SERVER_FQDN = 137, /* [RFC5223] */ + SD_DHCP_OPTION_CAPWAP_AC_ADDRESS = 138, /* [RFC5417] */ + SD_DHCP_OPTION_MOS_ADDRESS = 139, /* [RFC5678] */ + SD_DHCP_OPTION_MOS_FQDN = 140, /* [RFC5678] */ + SD_DHCP_OPTION_SIP_SERVICE_DOMAINS = 141, /* [RFC6011] */ + SD_DHCP_OPTION_ANDSF_ADDRESS = 142, /* [RFC6153] */ + SD_DHCP_OPTION_SZTP_REDIRECT = 143, /* [RFC8572] */ + SD_DHCP_OPTION_GEOLOC = 144, /* [RFC6225] */ + SD_DHCP_OPTION_FORCERENEW_NONCE_CAPABLE = 145, /* [RFC6704] */ + SD_DHCP_OPTION_RDNSS_SELECTION = 146, /* [RFC6731] */ + SD_DHCP_OPTION_DOTS_RI = 147, /* [RFC8973] */ + SD_DHCP_OPTION_DOTS_ADDRESS = 148, /* [RFC8973] */ + /* option code 149 is unassigned [RFC3942] */ + SD_DHCP_OPTION_TFTP_SERVER_ADDRESS = 150, /* [RFC5859] */ + SD_DHCP_OPTION_STATUS_CODE = 151, /* [RFC6926] */ + SD_DHCP_OPTION_BASE_TIME = 152, /* [RFC6926] */ + SD_DHCP_OPTION_START_TIME_OF_STATE = 153, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_START_TIME = 154, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_END_TIME = 155, /* [RFC6926] */ + SD_DHCP_OPTION_DHCP_STATE = 156, /* [RFC6926] */ + SD_DHCP_OPTION_DATA_SOURCE = 157, /* [RFC6926] */ + SD_DHCP_OPTION_PCP_SERVER = 158, /* [RFC7291] */ + SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */ + /* option code 160 is unassigned [RFC7710][RFC8910] */ + SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */ + /* option codes 162-174 are unassigned [RFC3942] */ + /* option codes 175-177 are temporary assigned. */ + /* option codes 178-207 are unassigned [RFC3942] */ + SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */ + SD_DHCP_OPTION_CONFIGURATION_FILE = 209, /* [RFC5071] */ + SD_DHCP_OPTION_PATH_PREFIX = 210, /* [RFC5071] */ + SD_DHCP_OPTION_REBOOT_TIME = 211, /* [RFC5071] */ + SD_DHCP_OPTION_6RD = 212, /* [RFC5969] */ + SD_DHCP_OPTION_ACCESS_DOMAIN = 213, /* [RFC5986] */ + /* option codes 214-219 are unassigned */ + SD_DHCP_OPTION_SUBNET_ALLOCATION = 220, /* [RFC6656] */ + SD_DHCP_OPTION_VIRTUAL_SUBNET_SELECTION = 221, /* [RFC6607] */ + /* option codes 222-223 are unassigned [RFC3942] */ + /* option codes 224-254 are reserved for private use */ SD_DHCP_OPTION_PRIVATE_BASE = 224, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, /* [RFC7844] */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, /* [RFC7844] */ SD_DHCP_OPTION_PRIVATE_LAST = 254, - SD_DHCP_OPTION_END = 255, + SD_DHCP_OPTION_END = 255, /* [RFC2132] */ }; /* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h index 478bbfd7a6..578ac6d4db 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h @@ -67,7 +67,8 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); @@ -82,7 +83,6 @@ int sd_dhcp_lease_get_6rd( int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); -int sd_dhcp_route_get_option(sd_dhcp_route *route); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); 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 0e23c84e64..d89b7d1c83 100644 --- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h +++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h @@ -81,8 +81,8 @@ enum { SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */ SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */ SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */ - SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */ - SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */ + SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */ + SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */ SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */ SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */ SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */ @@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address); + const struct in6_addr *pd_prefix); int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, @@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, - uint32_t transaction_id); int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h index e64f3200a3..f7d3244bb3 100644 --- a/src/libnm-systemd-core/src/systemd/sd-id128.h +++ b/src/libnm-systemd-core/src/systemd/sd-id128.h @@ -35,11 +35,14 @@ union sd_id128 { }; #define SD_ID128_STRING_MAX 33U +#define SD_ID128_UUID_STRING_MAX 37U char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); +char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_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]) {}) +#define SD_ID128_TO_UUID_STRING(id) sd_id128_to_uuid_string((id), (char[SD_ID128_UUID_STRING_MAX]) {}) int sd_id128_randomize(sd_id128_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 65d5175619..9dde770dab 100644 --- a/src/libnm-systemd-shared/src/basic/alloc-util.h +++ b/src/libnm-systemd-shared/src/basic/alloc-util.h @@ -13,6 +13,7 @@ #endif typedef void (*free_func_t)(void *p); +typedef void* (*mfree_func_t)(void *p); /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ @@ -173,13 +174,23 @@ void* greedy_realloc0(void **p, size_t need, size_t size); * is compatible with _FORTIFY_SOURCES. If _FORTIFY_SOURCES is used many memory operations will take the * object size as returned by __builtin_object_size() into account. Hence, let's return the smaller size of * malloc_usable_size() and __builtin_object_size() here, so that we definitely operate in safe territory by - * both the compiler's and libc's standards. Note that __builtin_object_size() evaluates to SIZE_MAX if the - * size cannot be determined, hence the MIN() expression should be safe with dynamically sized memory, - * too. Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and + * both the compiler's and libc's standards. Note that _FORTIFY_SOURCES=3 handles also dynamically allocated + * objects and thus it's safer using __builtin_dynamic_object_size if _FORTIFY_SOURCES=3 is used (#22801). + * Moreover, when NULL is passed malloc_usable_size() is documented to return zero, and * __builtin_object_size() returns SIZE_MAX too, hence we also return a sensible value of 0 in this corner * case. */ + +#if defined __has_builtin +# if __has_builtin(__builtin_dynamic_object_size) +# define MALLOC_SIZEOF_SAFE(x) \ + MIN(malloc_usable_size(x), __builtin_dynamic_object_size(x, 0)) +# endif +#endif + +#ifndef MALLOC_SIZEOF_SAFE #define MALLOC_SIZEOF_SAFE(x) \ MIN(malloc_usable_size(x), __builtin_object_size(x, 0)) +#endif /* Inspired by ELEMENTSOF() but operates on malloc()'ed memory areas: typesafely returns the number of items * that fit into the specified memory block */ diff --git a/src/libnm-systemd-shared/src/basic/cgroup-util.h b/src/libnm-systemd-shared/src/basic/cgroup-util.h index 461c01b3c2..4c413a8d17 100644 --- a/src/libnm-systemd-shared/src/basic/cgroup-util.h +++ b/src/libnm-systemd-shared/src/basic/cgroup-util.h @@ -205,6 +205,8 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +int cg_is_threaded(const char *controller, const char *path); + typedef enum { CG_KEY_MODE_GRACEFUL = 1 << 0, } CGroupKeyMode; diff --git a/src/libnm-systemd-shared/src/basic/env-file.c b/src/libnm-systemd-shared/src/basic/env-file.c index baab9bd284..61a9cd60d7 100644 --- a/src/libnm-systemd-shared/src/basic/env-file.c +++ b/src/libnm-systemd-shared/src/basic/env-file.c @@ -18,14 +18,12 @@ static int parse_env_file_internal( FILE *f, const char *fname, int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { + const char *key, char *value, void *userdata), + void *userdata) { size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; - char *p; int r; enum { @@ -48,7 +46,7 @@ static int parse_env_file_internal( if (r < 0) return r; - for (p = contents; *p; p++) { + for (char *p = contents; *p; p++) { char c = *p; switch (state) { @@ -102,7 +100,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -145,7 +143,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -264,7 +262,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -302,8 +300,7 @@ static int check_utf8ness_and_warn( static int parse_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { const char *k; va_list aq, *ap = userdata; @@ -325,9 +322,6 @@ static int parse_env_file_push( free(*v); *v = value; - if (n_pushed) - (*n_pushed)++; - return 1; } } @@ -343,16 +337,13 @@ int parse_env_filev( const char *fname, va_list ap) { - int r, n_pushed = 0; + int r; va_list aq; va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); va_end(aq); - if (r < 0) - return r; - - return n_pushed; + return r; } int parse_env_file_sentinel( @@ -374,8 +365,7 @@ int parse_env_file_sentinel( static int load_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***m = userdata; char *p; int r; @@ -392,78 +382,69 @@ static int load_env_file_push( if (r < 0) return r; - if (n_pushed) - (*n_pushed)++; - free(value); return 0; } int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int load_env_file_push_pairs( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; + void *userdata) { + + char ***m = ASSERT_PTR(userdata); int r; r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; + /* Check if the key is present */ + for (char **t = *m; t && *t; t += 2) + if (streq(t[0], key)) { + if (value) + return free_and_replace(t[1], value); + else + return free_and_strdup(t+1, ""); + } + r = strv_extend(m, key); if (r < 0) - return -ENOMEM; + return r; - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; + if (value) + return strv_push(m, value); + else + return strv_extend(m, ""); } int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int merge_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***env = userdata; char *expanded_value; @@ -492,7 +473,7 @@ static int merge_env_file_push( log_debug("%s:%u: setting %s=%s", filename, line, key, value); - return load_env_file_push(filename, line, key, value, env, n_pushed); + return load_env_file_push(filename, line, key, value, env); } int merge_env_file( @@ -504,7 +485,7 @@ int merge_env_file( * plus "extended" substitutions, unlike other exported parsing functions. */ - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); + return parse_env_file_internal(f, fname, merge_env_file_push, env); } static void write_env_var(FILE *f, const char *v) { @@ -541,7 +522,6 @@ static void write_env_var(FILE *f, const char *v) { int write_env_file(const char *fname, char **l) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; - char **i; int r; assert(fname); diff --git a/src/libnm-systemd-shared/src/basic/env-util.c b/src/libnm-systemd-shared/src/basic/env-util.c index f47ddefe96..b4ac68116e 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.c +++ b/src/libnm-systemd-shared/src/basic/env-util.c @@ -99,8 +99,6 @@ bool env_assignment_is_valid(const char *e) { } bool strv_env_is_valid(char **e) { - char **p, **q; - STRV_FOREACH(p, e) { size_t k; @@ -118,8 +116,6 @@ bool strv_env_is_valid(char **e) { } bool strv_env_name_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_name_is_valid(*p)) return false; @@ -132,8 +128,6 @@ bool strv_env_name_is_valid(char **l) { } bool strv_env_name_or_assignment_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) return false; @@ -275,7 +269,7 @@ static bool env_entry_has_name(const char *entry, const char *name) { char **strv_env_delete(char **x, size_t n_lists, ...) { size_t n, i = 0; - char **k, **r; + char **r; va_list ap; /* Deletes every entry from x that is mentioned in the other @@ -290,7 +284,7 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { STRV_FOREACH(k, x) { va_start(ap, n_lists); for (size_t v = 0; v < n_lists; v++) { - char **l, **j; + char **l; l = va_arg(ap, char**); STRV_FOREACH(j, l) @@ -382,7 +376,6 @@ char **strv_env_unset_many(char **l, ...) { int strv_env_replace_consume(char ***l, char *p) { const char *t, *name; - char **f; int r; assert(p); @@ -470,8 +463,6 @@ 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) { - char **i; - assert(name); if (k <= 0) @@ -499,7 +490,7 @@ char *strv_env_get(char **l, const char *name) { } char *strv_env_pairs_get(char **l, const char *name) { - char **key, **value, *result = NULL; + char *result = NULL; assert(name); @@ -511,7 +502,6 @@ char *strv_env_pairs_get(char **l, const char *name) { } char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; int k = 0; STRV_FOREACH(p, e) { @@ -705,7 +695,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { } char **replace_env_argv(char **argv, char **env) { - char **ret, **i; + char **ret; size_t k = 0, l = 0; l = strv_length(argv); @@ -837,7 +827,6 @@ int setenv_systemd_exec_pid(bool update_only) { int getenv_path_list(const char *name, char ***ret_paths) { _cleanup_strv_free_ char **l = NULL; const char *e; - char **p; int r; assert(name); @@ -873,20 +862,37 @@ int getenv_path_list(const char *name, char ***ret_paths) { return 1; } -int unsetenv_erase(const char *name) { - char *p; +int getenv_steal_erase(const char *name, char **ret) { + _cleanup_(erase_and_freep) char *a = NULL; + char *e; assert(name); - p = getenv(name); - if (!p) - return 0; + /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes + * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for + * testing, and given that people are likely going to misuse this, be thorough) */ - string_erase(p); + e = getenv(name); + if (!e) { + if (ret) + *ret = NULL; + return 0; + } + + if (ret) { + a = strdup(e); + if (!a) + return -ENOMEM; + } + + string_erase(e); if (unsetenv(name) < 0) return -errno; + if (ret) + *ret = TAKE_PTR(a); + return 1; } #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/env-util.h b/src/libnm-systemd-shared/src/basic/env-util.h index 38bfc8a3f2..2bf0603f21 100644 --- a/src/libnm-systemd-shared/src/basic/env-util.h +++ b/src/libnm-systemd-shared/src/basic/env-util.h @@ -69,4 +69,4 @@ int setenv_systemd_exec_pid(bool update_only); * PATH-like colon-separated absolute paths */ int getenv_path_list(const char *name, char ***ret_paths); -int unsetenv_erase(const char *name); +int getenv_steal_erase(const char *name, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/errno-util.h b/src/libnm-systemd-shared/src/basic/errno-util.h index 09abf0b751..648de50eb4 100644 --- a/src/libnm-systemd-shared/src/basic/errno-util.h +++ b/src/libnm-systemd-shared/src/basic/errno-util.h @@ -138,10 +138,18 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) { EPERM); } -/* Three difference errors for "not enough disk space" */ +/* Three different errors for "not enough disk space" */ static inline bool ERRNO_IS_DISK_SPACE(int r) { return IN_SET(abs(r), ENOSPC, EDQUOT, EFBIG); } + +/* Three different errors for "this device does not quite exist" */ +static inline bool ERRNO_IS_DEVICE_ABSENT(int r) { + return IN_SET(abs(r), + ENODEV, + ENXIO, + ENOENT); +} diff --git a/src/libnm-systemd-shared/src/basic/escape.c b/src/libnm-systemd-shared/src/basic/escape.c index 4dfb9a1637..cc317dfb00 100644 --- a/src/libnm-systemd-shared/src/basic/escape.c +++ b/src/libnm-systemd-shared/src/basic/escape.c @@ -552,7 +552,6 @@ char* quote_command_line(char **argv, ShellEscapeFlags flags) { assert(argv); - char **a; STRV_FOREACH(a, argv) { _cleanup_free_ char *t = NULL; diff --git a/src/libnm-systemd-shared/src/basic/fd-util.c b/src/libnm-systemd-shared/src/basic/fd-util.c index c3df68de58..53efeb401f 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.c +++ b/src/libnm-systemd-shared/src/basic/fd-util.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "io-util.h" #include "macro.h" #include "missing_fcntl.h" +#include "missing_fs.h" #include "missing_syscall.h" #include "parse-util.h" #include "path-util.h" @@ -420,14 +422,11 @@ int same_fd(int a, int b) { assert(a >= 0); assert(b >= 0); - /* Compares two file descriptors. Note that semantics are - * quite different depending on whether we have kcmp() or we - * don't. If we have kcmp() this will only return true for - * dup()ed file descriptors, but not otherwise. If we don't - * have kcmp() this will also return true for two fds of the same - * file, created by separate open() calls. Since we use this - * call mostly for filtering out duplicates in the fd store - * this difference hopefully doesn't matter too much. */ + /* Compares two file descriptors. Note that semantics are quite different depending on whether we + * have kcmp() or we don't. If we have kcmp() this will only return true for dup()ed file + * descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of + * the same file, created by separate open() calls. Since we use this call mostly for filtering out + * duplicates in the fd store this difference hopefully doesn't matter too much. */ if (a == b) return true; @@ -439,7 +438,7 @@ int same_fd(int a, int b) { return true; if (r > 0) return false; - if (!IN_SET(errno, ENOSYS, EACCES, EPERM)) + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) return -errno; /* We don't have kcmp(), use fstat() instead. */ @@ -449,23 +448,17 @@ int same_fd(int a, int b) { if (fstat(b, &stb) < 0) return -errno; - if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) + if (!stat_inode_same(&sta, &stb)) return false; - /* We consider all device fds different, since two device fds - * might refer to quite different device contexts even though - * they share the same inode and backing dev_t. */ + /* We consider all device fds different, since two device fds might refer to quite different device + * contexts even though they share the same inode and backing dev_t. */ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) return false; - if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) - return false; - - /* The fds refer to the same inode on disk, let's also check - * if they have the same fd flags. This is useful to - * distinguish the read and write side of a pipe created with - * pipe(). */ + /* The fds refer to the same inode on disk, let's also check if they have the same fd flags. This is + * useful to distinguish the read and write side of a pipe created with pipe(). */ fa = fcntl(a, F_GETFL); if (fa < 0) return -errno; @@ -804,4 +797,25 @@ int btrfs_defrag_fd(int fd) { return RET_NERRNO(ioctl(fd, BTRFS_IOC_DEFRAG, NULL)); } + +int fd_get_diskseq(int fd, uint64_t *ret) { + uint64_t diskseq; + + assert(fd >= 0); + assert(ret); + + if (ioctl(fd, BLKGETDISKSEQ, &diskseq) < 0) { + /* Note that the kernel is weird: non-existing ioctls currently return EINVAL + * rather than ENOTTY on loopback block devices. They should fix that in the kernel, + * but in the meantime we accept both here. */ + if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EINVAL) + return -errno; + + return -EOPNOTSUPP; + } + + *ret = diskseq; + + return 0; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/fd-util.h b/src/libnm-systemd-shared/src/basic/fd-util.h index f5cfcb4ede..808cac4d5d 100644 --- a/src/libnm-systemd-shared/src/basic/fd-util.h +++ b/src/libnm-systemd-shared/src/basic/fd-util.h @@ -109,6 +109,7 @@ static inline int make_null_stdio(void) { int fd_reopen(int fd, int flags); int read_nr_open(void); int btrfs_defrag_fd(int fd); +int fd_get_diskseq(int fd, uint64_t *ret); /* The maximum length a buffer for a /proc/self/fd/ path needs */ #define PROC_FD_PATH_MAX \ diff --git a/src/libnm-systemd-shared/src/basic/fileio.c b/src/libnm-systemd-shared/src/basic/fileio.c index c66bc67bcb..0ad8ae03b4 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.c +++ b/src/libnm-systemd-shared/src/basic/fileio.c @@ -1049,8 +1049,6 @@ static int search_and_fopen_internal( FILE **ret, char **ret_path) { - char **i; - assert(path); assert(mode); assert(ret); @@ -1204,7 +1202,7 @@ int write_timestamp_file_atomic(const char *fn, usec_t n) { /* Creates a "timestamp" file, that contains nothing but a * usec_t timestamp, formatted in ASCII. */ - if (n <= 0 || n >= USEC_INFINITY) + if (!timestamp_is_set(n)) return -ERANGE; xsprintf(ln, USEC_FMT "\n", n); @@ -1225,7 +1223,7 @@ int read_timestamp_file(const char *fn, usec_t *ret) { if (r < 0) return r; - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) + if (!timestamp_is_set(t)) return -ERANGE; *ret = (usec_t) t; diff --git a/src/libnm-systemd-shared/src/basic/fileio.h b/src/libnm-systemd-shared/src/basic/fileio.h index cea3dd893d..9151d8237a 100644 --- a/src/libnm-systemd-shared/src/basic/fileio.h +++ b/src/libnm-systemd-shared/src/basic/fileio.h @@ -110,6 +110,12 @@ typedef enum ReadLineFlags { int read_line_full(FILE *f, size_t limit, ReadLineFlags flags, char **ret); +static inline bool file_offset_beyond_memory_size(off_t x) { + if (x < 0) /* off_t is signed, filter that out */ + return false; + return (uint64_t) x > (uint64_t) SIZE_MAX; +} + static inline int read_line(FILE *f, size_t limit, char **ret) { return read_line_full(f, limit, 0, ret); } diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index b524a6fd1b..dccfb27b52 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -123,7 +123,6 @@ int readlinkat_malloc(int fd, const char *p, char **ret) { size_t l = PATH_MAX; assert(p); - assert(ret); for (;;) { _cleanup_free_ char *c = NULL; @@ -139,7 +138,10 @@ int readlinkat_malloc(int fd, const char *p, char **ret) { if ((size_t) n < l) { c[n] = 0; - *ret = TAKE_PTR(c); + + if (ret) + *ret = TAKE_PTR(c); + return 0; } @@ -576,7 +578,6 @@ int get_files_in_directory(const char *path, char ***list) { #endif /* NM_IGNORED */ static int getenv_tmp_dir(const char **ret_path) { - const char *n; int r, ret = 0; assert(ret_path); @@ -866,8 +867,7 @@ int conservative_renameat( if (fstat(new_fd, &new_stat) < 0) goto do_rename; - if (new_stat.st_ino == old_stat.st_ino && - new_stat.st_dev == old_stat.st_dev) + if (stat_inode_same(&new_stat, &old_stat)) goto is_same; if (old_stat.st_mode != new_stat.st_mode || @@ -1094,3 +1094,50 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { return TAKE_FD(fd); } + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) { + unsigned attempts = 7; + int fd; + + /* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if + * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will + * shortcut to openat() otherwise */ + + if (!ret_newly_created) + return RET_NERRNO(openat(dirfd, pathname, flags, mode)); + + if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) { + fd = openat(dirfd, pathname, flags, mode); + if (fd < 0) + return -errno; + + *ret_newly_created = FLAGS_SET(flags, O_CREAT); + return fd; + } + + for (;;) { + /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */ + fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode); + if (fd >= 0) { + *ret_newly_created = false; + return fd; + } + if (errno != ENOENT) + return -errno; + + /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */ + fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode); + if (fd >= 0) { + *ret_newly_created = true; + return fd; + } + if (errno != EEXIST) + return -errno; + + /* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again + * without the two flags. But let's not spin forever, hence put a limit on things */ + + if (--attempts == 0) /* Give up eventually, somebody is playing with us */ + return -EEXIST; + } +} diff --git a/src/libnm-systemd-shared/src/basic/fs-util.h b/src/libnm-systemd-shared/src/basic/fs-util.h index 0bbb3f6298..e48cf6800f 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.h +++ b/src/libnm-systemd-shared/src/basic/fs-util.h @@ -110,3 +110,5 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode); + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created); diff --git a/src/libnm-systemd-shared/src/basic/hashmap.c b/src/libnm-systemd-shared/src/basic/hashmap.c index ee2a12e825..e7dedc6d35 100644 --- a/src/libnm-systemd-shared/src/basic/hashmap.c +++ b/src/libnm-systemd-shared/src/basic/hashmap.c @@ -164,7 +164,7 @@ struct _packed_ indirect_storage { unsigned n_buckets; /* number of buckets */ unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops + Makes "while (hashmap_steal_first())" loops O(n) instead of O(n^2) for unordered hashmaps. */ uint8_t _pad[3]; /* padding for the whole HashmapBase */ /* The bitfields in HashmapBase complete the alignment of the whole thing. */ @@ -1868,7 +1868,6 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; assert(s); diff --git a/src/libnm-systemd-shared/src/basic/hexdecoct.c b/src/libnm-systemd-shared/src/basic/hexdecoct.c index 381e79ba5e..570da68823 100644 --- a/src/libnm-systemd-shared/src/basic/hexdecoct.c +++ b/src/libnm-systemd-shared/src/basic/hexdecoct.c @@ -688,8 +688,7 @@ static int base64_append_width( s += indent; } - memcpy(s, x + width * line, act); - s += act; + s = mempcpy(s, x + width * line, act); *(s++) = line < lines - 1 ? '\n' : '\0'; avail -= act; } diff --git a/src/libnm-systemd-shared/src/basic/hostname-util.c b/src/libnm-systemd-shared/src/basic/hostname-util.c index 1d1c2f5142..692733ac53 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.c +++ b/src/libnm-systemd-shared/src/basic/hostname-util.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "env-file.h" #include "hostname-util.h" #include "os-util.h" #include "string-util.h" @@ -197,3 +198,20 @@ bool is_localhost(const char *hostname) { endswith_no_case(hostname, ".localhost.localdomain") || endswith_no_case(hostname, ".localhost.localdomain."); } + +int get_pretty_hostname(char **ret) { + _cleanup_free_ char *n = NULL; + int r; + + assert(ret); + + r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n); + if (r < 0) + return r; + + if (isempty(n)) + return -ENXIO; + + *ret = TAKE_PTR(n); + 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 d435bed50e..a00b852395 100644 --- a/src/libnm-systemd-shared/src/basic/hostname-util.h +++ b/src/libnm-systemd-shared/src/basic/hostname-util.h @@ -4,7 +4,6 @@ #include #include -#include "env-file.h" #include "macro.h" #include "strv.h" @@ -61,6 +60,4 @@ static inline bool is_outbound_hostname(const char *hostname) { 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); -} +int get_pretty_hostname(char **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 1bf0764de1..51a6b165f8 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.c +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.c @@ -123,6 +123,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost_one(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* 127.0.0.1 */ + return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); + + if (family == AF_INET6) + return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + + return -EAFNOSUPPORT; +} + bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) { return a->s6_addr32[0] == 0 && a->s6_addr32[1] == 0 && 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 0178391e5f..5de87a9539 100644 --- a/src/libnm-systemd-shared/src/basic/in-addr-util.h +++ b/src/libnm-systemd-shared/src/basic/in-addr-util.h @@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +int in_addr_is_localhost_one(int family, const union in_addr_union *u); bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); diff --git a/src/libnm-systemd-shared/src/basic/inotify-util.h b/src/libnm-systemd-shared/src/basic/inotify-util.h index 88af08688f..61951ff3e3 100644 --- a/src/libnm-systemd-shared/src/basic/inotify-util.h +++ b/src/libnm-systemd-shared/src/basic/inotify-util.h @@ -6,12 +6,28 @@ #include #include +#include "log.h" + #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)) +#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \ + for (struct inotify_event \ + *start = &((buffer).ev), \ + *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \ + *e = start; \ + (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \ + ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \ + (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \ + e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len)) + +#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \ + _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ)) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG) + +#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING) union inotify_event_buffer { struct inotify_event ev; diff --git a/src/libnm-systemd-shared/src/basic/io-util.c b/src/libnm-systemd-shared/src/basic/io-util.c index d5cbb8999e..8a2b9c2f67 100644 --- a/src/libnm-systemd-shared/src/basic/io-util.c +++ b/src/libnm-systemd-shared/src/basic/io-util.c @@ -165,7 +165,6 @@ int pipe_eof(int fd) { #endif /* NM_IGNORED */ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { - struct timespec ts; int r; assert(fds || nfds == 0); @@ -173,7 +172,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { if (nfds == 0) return 0; - r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL); + r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL); if (r < 0) return -errno; if (r == 0) diff --git a/src/libnm-systemd-shared/src/basic/list.h b/src/libnm-systemd-shared/src/basic/list.h index f827e721eb..58e83a6cb2 100644 --- a/src/libnm-systemd-shared/src/basic/list.h +++ b/src/libnm-systemd-shared/src/basic/list.h @@ -136,33 +136,39 @@ #define LIST_JUST_US(name,item) \ (!(item)->name##_prev && !(item)->name##_next) +/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the + * loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through + * hashmap_get(). In that case, you need to explicitly cast the result. */ +#define LIST_FOREACH_WITH_NEXT(name,i,n,head) \ + for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n) + #define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) + LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head) -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) +#define _LIST_FOREACH_WITH_PREV(name,i,p,start) \ + for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p) -#define LIST_FOREACH_BACKWARDS(name,i,p) \ - for ((i) = (p); (i); (i) = (i)->name##_prev) +#define LIST_FOREACH_BACKWARDS(name,i,start) \ + _LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start) /* Iterate through all the members of the list p is included in, but skip over p */ #define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) + for (typeof(*(p)) *_p = (p), *i = ({ \ + typeof(*_p) *_j = _p; \ + while (_j && _j->name##_prev) \ + _j = _j->name##_prev; \ + if (_j == _p) \ + _j = _p->name##_next; \ + _j; \ + }); \ + i; \ + i = i->name##_next == _p ? _p->name##_next : i->name##_next) -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ +/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */ #define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) + for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \ + i != (p); \ + i = i->name##_next ? i->name##_next : (head)) #define LIST_IS_EMPTY(head) \ (!(head)) diff --git a/src/libnm-systemd-shared/src/basic/log.h b/src/libnm-systemd-shared/src/basic/log.h index 88d278fdce..6361eb0f1c 100644 --- a/src/libnm-systemd-shared/src/basic/log.h +++ b/src/libnm-systemd-shared/src/basic/log.h @@ -409,9 +409,11 @@ int log_emergency_level(void); }) #if LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace_errno(...) log_debug_errno(__VA_ARGS__) #else -# define log_trace(...) do {} while (0) +# define log_trace(...) do {} while (0) +# define log_trace_errno(e, ...) (-ERRNO_VALUE(e)) #endif /* Structured logging */ diff --git a/src/libnm-systemd-shared/src/basic/macro.h b/src/libnm-systemd-shared/src/basic/macro.h index 79437775a5..41d83b3eb6 100644 --- a/src/libnm-systemd-shared/src/basic/macro.h +++ b/src/libnm-systemd-shared/src/basic/macro.h @@ -318,23 +318,32 @@ static inline int __coverity_check_and_return__(int condition) { #define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ +/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in + * extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ - (2U+(sizeof(type) <= 1 ? 3U : \ + ((size_t) IS_SIGNED_INTEGER_TYPE(type) + 1U + \ + (sizeof(type) <= 1 ? 3U : \ sizeof(type) <= 2 ? 5U : \ sizeof(type) <= 4 ? 10U : \ - sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)]))) + sizeof(type) <= 8 ? (IS_SIGNED_INTEGER_TYPE(type) ? 19U : 20U) : sizeof(int[-2*(sizeof(type) > 8)]))) -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - size_t ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ +/* Returns the number of chars needed to format the specified integer value. It's hence more specific than + * DECIMAL_STR_MAX() which answers the same question for all possible values of the specified type. Does + * *not* include space for a trailing NUL. (If you wonder why we special case _x_ == 0 here: it's to trick + * out gcc's -Wtype-limits, which would complain on comparing an unsigned type with < 0, otherwise. By + * special-casing == 0 here first, we can use <= 0 instead of < 0 to trick out gcc.) */ +#define DECIMAL_STR_WIDTH(x) \ + ({ \ + typeof(x) _x_ = (x); \ + size_t ans; \ + if (_x_ == 0) \ + ans = 1; \ + else { \ + ans = _x_ <= 0 ? 2 : 1; \ + while ((_x_ /= 10) != 0) \ + ans++; \ + } \ + ans; \ }) #define SWAP_TWO(x, y) do { \ @@ -463,4 +472,20 @@ typedef struct { assert_cc(sizeof(dummy_t) == 0); +/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for for + * loops that count down from a high pointer until some base. A naive loop would implement this like this: + * + * for (p = end-1; p >= base; p--) … + * + * But this is not safe because p before the base is UB in C. With this macro the loop becomes this instead: + * + * for (p = PTR_SUB1(end, base); p; p = PTR_SUB1(p, base)) … + * + * And is free from UB! */ +#define PTR_SUB1(p, base) \ + ({ \ + typeof(p) _q = (p); \ + _q && _q > (base) ? &_q[-1] : NULL; \ + }) + #include "log.h" diff --git a/src/libnm-systemd-shared/src/basic/memory-util.h b/src/libnm-systemd-shared/src/basic/memory-util.h index 9f37431fc1..6e3280b9df 100644 --- a/src/libnm-systemd-shared/src/basic/memory-util.h +++ b/src/libnm-systemd-shared/src/basic/memory-util.h @@ -15,7 +15,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) #define PAGE_OFFSET(l) ((l) & (page_size() - 1)) -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ +/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) return dst; @@ -23,7 +23,15 @@ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); } -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ +/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */ +static inline void *mempcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return dst; + assert(src); + return mempcpy(dst, src, n); +} + +/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { if (n == 0) return 0; diff --git a/src/libnm-systemd-shared/src/basic/missing_syscall.h b/src/libnm-systemd-shared/src/basic/missing_syscall.h index 41c83ec522..e5bdc0e1cd 100644 --- a/src/libnm-systemd-shared/src/basic/missing_syscall.h +++ b/src/libnm-systemd-shared/src/basic/missing_syscall.h @@ -574,6 +574,10 @@ static inline int missing_open_tree( #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */ #endif +#ifndef MOVE_MOUNT_T_EMPTY_PATH +#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */ +#endif + static inline int missing_move_mount( int from_dfd, const char *from_pathname, diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.c b/src/libnm-systemd-shared/src/basic/ordered-set.c index 62a36b5dc7..f402bb5b0c 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.c +++ b/src/libnm-systemd-shared/src/basic/ordered-set.c @@ -60,7 +60,6 @@ int _ordered_set_put_strdup(OrderedSet **s, const char *p HASHMAP_DEBUG_PARAMS) int _ordered_set_put_strdupv(OrderedSet **s, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; STRV_FOREACH(i, l) { r = _ordered_set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); diff --git a/src/libnm-systemd-shared/src/basic/ordered-set.h b/src/libnm-systemd-shared/src/basic/ordered-set.h index 3ee47350b3..c0650e0158 100644 --- a/src/libnm-systemd-shared/src/basic/ordered-set.h +++ b/src/libnm-systemd-shared/src/basic/ordered-set.h @@ -18,6 +18,14 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); #define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) +static inline void ordered_set_clear(OrderedSet *s) { + return ordered_hashmap_clear((OrderedHashmap*) s); +} + +static inline void ordered_set_clear_free(OrderedSet *s) { + return ordered_hashmap_clear_free((OrderedHashmap*) s); +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); } diff --git a/src/libnm-systemd-shared/src/basic/parse-util.c b/src/libnm-systemd-shared/src/basic/parse-util.c index 4ba2657c2b..0a623c519d 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.c +++ b/src/libnm-systemd-shared/src/basic/parse-util.c @@ -421,7 +421,7 @@ int safe_atoi(const char *s, int *ret_i) { return 0; } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) { char *x = NULL; unsigned long long l; diff --git a/src/libnm-systemd-shared/src/basic/parse-util.h b/src/libnm-systemd-shared/src/basic/parse-util.h index 3dc5e140c9..8273124626 100644 --- a/src/libnm-systemd-shared/src/basic/parse-util.h +++ b/src/libnm-systemd-shared/src/basic/parse-util.h @@ -65,9 +65,9 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { return safe_atoi(s, (int*) ret_i); } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu); -static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { +static inline int safe_atollu(const char *s, unsigned long long *ret_llu) { return safe_atollu_full(s, 0, ret_llu); } @@ -82,12 +82,12 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { } static inline int safe_atoux64(const char *s, uint64_t *ret) { - assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); - return safe_atollu_full(s, 16, (long long unsigned*) ret); + assert_cc(sizeof(int64_t) == sizeof(unsigned long long)); + return safe_atollu_full(s, 16, (unsigned long long*) ret); } #if LONG_MAX == INT_MAX -static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) { +static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); return safe_atou_full(s, base, (unsigned*) ret_u); } @@ -117,7 +117,7 @@ static inline int safe_atozu(const char *s, size_t *ret_u) { } #else static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); + assert_cc(sizeof(size_t) == sizeof(unsigned long)); return safe_atolu(s, ret_u); } #endif diff --git a/src/libnm-systemd-shared/src/basic/path-util.c b/src/libnm-systemd-shared/src/basic/path-util.c index f391848fcb..f608845843 100644 --- a/src/libnm-systemd-shared/src/basic/path-util.c +++ b/src/libnm-systemd-shared/src/basic/path-util.c @@ -66,7 +66,7 @@ char *path_make_absolute(const char *p, const char *prefix) { } int safe_getcwd(char **ret) { - char *cwd; + _cleanup_free_ char *cwd = NULL; cwd = get_current_dir_name(); if (!cwd) @@ -74,12 +74,12 @@ int safe_getcwd(char **ret) { /* Let's make sure the directory is really absolute, to protect us from the logic behind * CVE-2018-1000001 */ - if (cwd[0] != '/') { - free(cwd); + if (cwd[0] != '/') return -ENOMEDIUM; - } - *ret = cwd; + if (ret) + *ret = TAKE_PTR(cwd); + return 0; } @@ -205,9 +205,9 @@ int path_make_relative(const char *from, const char *to, char **ret) { } char* path_startswith_strv(const char *p, char **set) { - char **s, *t; - STRV_FOREACH(s, set) { + char *t; + t = path_startswith(p, *s); if (t) return t; @@ -217,7 +217,6 @@ char* path_startswith_strv(const char *p, char **set) { } int path_strv_make_absolute_cwd(char **l) { - char **s; int r; /* Goes through every item in the string list and makes it @@ -239,7 +238,6 @@ int path_strv_make_absolute_cwd(char **l) { } char **path_strv_resolve(char **l, const char *root) { - char **s; unsigned k = 0; bool enomem = false; int r; @@ -707,12 +705,12 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ p = DEFAULT_PATH; 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; @@ -761,7 +759,6 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { bool changed = false, originally_unset; - const char* const* i; assert(timestamp); @@ -940,8 +937,9 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char ** static const char *skip_slash_or_dot_backward(const char *path, const char *q) { assert(path); + assert(!q || q >= path); - for (; q >= path; q--) { + for (; q; q = PTR_SUB1(q, path)) { if (*q == '/') continue; if (q > path && strneq(q - 1, "/.", 2)) @@ -1006,7 +1004,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * q = path + strlen(path) - 1; q = skip_slash_or_dot_backward(path, q); - if ((q < path) || /* the root directory */ + if (!q || /* the root directory */ (q == path && *q == '.')) { /* path is "." or "./" */ if (next) *next = path; @@ -1017,10 +1015,10 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * last_end = q + 1; - while (q >= path && *q != '/') - q--; + while (q && *q != '/') + q = PTR_SUB1(q, path); - last_begin = q + 1; + last_begin = q ? q + 1 : path; len = last_end - last_begin; if (len > NAME_MAX) @@ -1030,10 +1028,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * if (next) { q = skip_slash_or_dot_backward(path, q); - if (q < path) - *next = path; - else - *next = q + 1; + *next = q ? q + 1 : path; } if (ret) @@ -1249,9 +1244,10 @@ bool hidden_or_backup_file(const char *filename) { assert(filename); if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || + STR_IN_SET(filename, + "lost+found", + "aquota.user", + "aquota.group") || endswith(filename, "~")) return true; @@ -1348,7 +1344,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *path = NULL; - char *c, **name; + char *c; path = path_join(root, pattern); if (!path) @@ -1422,8 +1418,6 @@ bool empty_or_root(const char *path) { } bool path_strv_contains(char **l, const char *path) { - char **i; - STRV_FOREACH(i, l) if (path_equal(*i, path)) return true; @@ -1432,10 +1426,9 @@ bool path_strv_contains(char **l, const char *path) { } bool prefixed_path_strv_contains(char **l, const char *path) { - char **i, *j; - STRV_FOREACH(i, l) { - j = *i; + const char *j = *i; + if (*j == '-') j++; if (*j == '+') diff --git a/src/libnm-systemd-shared/src/basic/process-util.c b/src/libnm-systemd-shared/src/basic/process-util.c index 70564bd398..83e3a71eef 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.c +++ b/src/libnm-systemd-shared/src/basic/process-util.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -220,7 +221,6 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); _cleanup_strv_free_ char **args = NULL; - char **p; args = strv_parse_nulstr(t, k); if (!args) @@ -478,37 +478,33 @@ int get_process_capeff(pid_t pid, char **ret) { return r; } -static int get_process_link_contents(const char *proc_file, char **ret) { +static int get_process_link_contents(pid_t pid, const char *proc_file, char **ret) { + const char *p; int r; assert(proc_file); - assert(ret); - r = readlink_malloc(proc_file, ret); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; + p = procfs_file_alloca(pid, proc_file); - return 0; + r = readlink_malloc(p, ret); + return r == -ENOENT ? -ESRCH : r; } int get_process_exe(pid_t pid, char **ret) { - const char *p; char *d; int r; assert(pid >= 0); - assert(ret); - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, ret); + r = get_process_link_contents(pid, "exe", ret); if (r < 0) return r; - d = endswith(*ret, " (deleted)"); - if (d) - *d = '\0'; + if (ret) { + d = endswith(*ret, " (deleted)"); + if (d) + *d = '\0'; + } return 0; } @@ -578,28 +574,17 @@ int get_process_gid(pid_t pid, gid_t *ret) { } int get_process_cwd(pid_t pid, char **ret) { - const char *p; - assert(pid >= 0); - assert(ret); if (pid == 0 || pid == getpid_cached()) return safe_getcwd(ret); - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, ret); + return get_process_link_contents(pid, "cwd", ret); } int get_process_root(pid_t pid, char **ret) { - const char *p; - assert(pid >= 0); - assert(ret); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, ret); + return get_process_link_contents(pid, "root", ret); } #define ENVIRONMENT_BLOCK_MAX (5U*1024U*1024U) @@ -651,7 +636,7 @@ int get_process_environ(pid_t pid, char **ret) { int get_process_ppid(pid_t pid, pid_t *ret) { _cleanup_free_ char *line = NULL; - long unsigned ppid; + unsigned long ppid; const char *p; int r; @@ -694,7 +679,7 @@ int get_process_ppid(pid_t pid, pid_t *ret) { if (ppid == 0) return -EADDRNOTAVAIL; - if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid) + if ((pid_t) ppid < 0 || (unsigned long) (pid_t) ppid != ppid) return -ERANGE; if (ret) @@ -825,13 +810,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { for (;;) { usec_t n; siginfo_t status = {}; - struct timespec ts; n = now(CLOCK_MONOTONIC); if (n >= until) break; - r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n))); + r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n))); /* Assuming we woke due to the child exiting. */ if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { if (status.si_pid == pid) { @@ -1170,12 +1154,6 @@ void reset_cached_pid(void) { cached_pid = CACHED_PID_UNSET; } -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle _weak_; - pid_t getpid_cached(void) { static bool installed = false; pid_t current_value; @@ -1203,7 +1181,7 @@ pid_t getpid_cached(void) { * only half-documented (glibc doesn't document it but LSB does — though only superficially) * we'll check for errors only in the most generic fashion possible. */ - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + if (pthread_atfork(NULL, NULL, reset_cached_pid) != 0) { /* OOM? Let's try again later */ cached_pid = CACHED_PID_UNSET; return new_pid; @@ -1632,6 +1610,30 @@ _noreturn_ void freeze(void) { pause(); } +bool argv_looks_like_help(int argc, char **argv) { + char **l; + + /* Scans the command line for indications the user asks for help. This is supposed to be called by + * tools that do not implement getopt() style command line parsing because they are not primarily + * user-facing. Detects four ways of asking for help: + * + * 1. Passing zero arguments + * 2. Passing "help" as first argument + * 3. Passing --help as any argument + * 4. Passing -h as any argument + */ + + if (argc <= 1) + return true; + + if (streq_ptr(argv[1], "help")) + return true; + + l = strv_skip(argv, 1); + + return strv_contains(l, "--help") || + strv_contains(l, "-h"); +} static const char *const sigchld_code_table[] = { [CLD_EXITED] = "exited", diff --git a/src/libnm-systemd-shared/src/basic/process-util.h b/src/libnm-systemd-shared/src/basic/process-util.h index a957cab29d..c488c6a833 100644 --- a/src/libnm-systemd-shared/src/basic/process-util.h +++ b/src/libnm-systemd-shared/src/basic/process-util.h @@ -193,3 +193,5 @@ int setpriority_closest(int priority); bool invoked_as(char *argv[], const char *token); _noreturn_ void freeze(void); + +bool argv_looks_like_help(int argc, char **argv); diff --git a/src/libnm-systemd-shared/src/basic/random-util.c b/src/libnm-systemd-shared/src/basic/random-util.c index 9832f8efc9..e3f4ee720e 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.c +++ b/src/libnm-systemd-shared/src/basic/random-util.c @@ -37,225 +37,41 @@ static bool srand_called = false; -int rdrand(unsigned long *ret) { - - /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here, - * instead of sticking to /dev/urandom or getrandom()? - * - * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and - * getrandom() is generally not initialized yet. It is very common that initialization of the random - * pool takes a longer time (up to many minutes), in particular on embedded devices that have no - * explicit hardware random generator, as well as in virtualized environments such as major cloud - * installations that do not provide virtio-rng or a similar mechanism. - * - * In such an environment using getrandom() synchronously means we'd block the entire system boot-up - * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK) - * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean - * generating many kmsg log messages about our use of it before the random pool is properly - * initialized. Neither of these outcomes is desirable. - * - * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND - * provides us quickly and relatively reliably with random values, without having to delay boot, - * without triggering warning messages in kmsg. - * - * Note that we use RDRAND only under very specific circumstances, when the requirements on the - * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use - * RDRAND: - * - * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic - * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be - * generated in a way that is reliably unique, but they do not require ultimate trust into - * the entropy generator. systemd generates a number of UUIDs during early boot, including - * 'invocation IDs' for every unit spawned that identify the specific invocation of the - * service globally, and a number of others. Other alternatives for generating these UUIDs - * have been considered, but don't really work: for example, hashing uuids from a local - * system identifier combined with a counter falls flat because during early boot disk - * storage is not yet available (think: initrd) and thus a system-specific ID cannot be - * stored or retrieved yet. - * - * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are - * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive - * O(n) access complexity if an attacker manages to trigger a large number of hash - * collisions. Thus, systemd (as any software employing hash tables should) uses seeded - * hash functions for its hash tables, with a seed generated randomly. The hash tables - * systemd employs watch the fill level closely and reseed if necessary. This allows use of - * a low quality RNG initially, as long as it improves should a hash table be under attack: - * the attacker after all needs to trigger many collisions to exploit it for the purpose - * of DoS, but if doing so improves the seed the attack surface is reduced as the attack - * takes place. - * - * Some cases where we do NOT use RDRAND are: - * - * • Generation of cryptographic key material 🔑 - * - * • Generation of cryptographic salt values 🧂 - * - * This function returns: - * - * -EOPNOTSUPP → RDRAND is not available on this system 😔 - * -EAGAIN → The operation failed this time, but is likely to work if you try again a few - * times ♻ - * -EUCLEAN → We got some random value, but it looked strange, so we refused using it. - * This failure might or might not be temporary. 😕 - */ - -#if defined(__i386__) || defined(__x86_64__) - static int have_rdrand = -1; - unsigned long v; - uint8_t success; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - -/* Compat with old gcc where bit_RDRND didn't exist yet */ -#ifndef bit_RDRND -#define bit_RDRND (1U << 30) -#endif - - have_rdrand = !!(ecx & bit_RDRND); - - if (have_rdrand > 0) { - /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0 - If it is unset getenv_bool_secure will return a negative value. */ - if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - } - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (v), - "=qm" (success)); - msan_unpoison(&success, sizeof(success)); - if (!success) - return -EAGAIN; - - /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success - * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be - * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking - * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around - * only however and something AMD really should fix properly. The Linux kernel should probably work - * around this issue by turning off RDRAND altogether on those CPUs. See: - * https://github.com/systemd/systemd/issues/11810 */ - if (v == 0 || v == ULONG_MAX) - return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), - "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v); - - *ret = v; - return 0; -#else - return -EOPNOTSUPP; -#endif -} - int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { static int have_syscall = -1; _cleanup_close_ int fd = -1; - bool got_some = false; - /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from - * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK - * flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not - * initialized. Otherwise it will always return some data from the kernel, regardless of whether the - * random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not - * enough better quality randomness could be acquired, the rest is filled up with low quality - * randomness. - * - * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND - * or even RANDOM_EXTEND_WITH_PSEUDO. - * - * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use - * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via - * an "all bets are off" wrapper, such as random_bytes(), see below. */ + /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK + * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless + * of whether the random pool is fully initialized or not. When creating cryptographic key material you + * should always use RANDOM_BLOCK. */ if (n == 0) return 0; - if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND)) - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is - * not required, as we don't trust it (who does?). Note that we only do a single iteration of - * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10 - * invocations or so. That's because we don't really care about the quality here. We - * generally prefer using RDRAND if the caller allows us to, since this way we won't upset - * the kernel's random subsystem by accessing it before the pool is initialized (after all it - * will kmsg log about every attempt to do so). */ - for (;;) { - unsigned long u; - size_t m; - - if (rdrand(&u) < 0) { - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - /* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */ - break; - } - - m = MIN(sizeof(u), n); - memcpy(p, &u, m); - - p = (uint8_t*) p + m; - n -= m; - - if (n == 0) - return 0; /* Yay, success! */ - - got_some = true; - } - /* Use the getrandom() syscall unless we know we don't have it. */ if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - for (;;) { - ssize_t l; -#if !HAVE_GETRANDOM +#if HAVE_GETRANDOM + ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE); +#else /* NetworkManager Note: systemd calls the syscall directly in this case. Don't add that workaround. * If you don't compile against a libc that provides getrandom(), you don't get it. */ - l = -1; + ssize_t l = -1; errno = ENOSYS; -#else - l = getrandom(p, n, - (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | - (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); #endif + if (l > 0) { have_syscall = true; if ((size_t) l == n) return 0; /* Yay, success! */ + /* We didn't get enough data, so try again */ assert((size_t) l < n); p = (uint8_t*) p + l; n -= l; - - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - got_some = true; - - /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */ - if (FLAGS_SET(flags, RANDOM_BLOCK)) - continue; - - /* Fill in the rest with /dev/urandom */ - break; + continue; } else if (l == 0) { have_syscall = true; @@ -266,37 +82,12 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { have_syscall = false; break; - } else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to use the syscall - * the next time again though. - * - * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can - * produce some pseudo-random bytes instead. Otherwise, fall back to - * /dev/urandom, which we know is empty, but the kernel will produce some - * bytes for us on a best-effort basis. */ - have_syscall = true; - - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudo_random_bytes(p, n); - return 0; - } - - if (FLAGS_SET(flags, RANDOM_MAY_FAIL)) - return -ENODATA; - - /* Use /dev/urandom instead */ - break; - } else if (errno == EINVAL) { - - /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, - * hence try without. */ - - if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { - flags = flags &~ RANDOM_ALLOW_INSECURE; - continue; - } + /* If we previously passed GRND_INSECURE, and this flag isn't known, then + * we're likely running an old kernel which has getrandom() but not + * GRND_INSECURE. In this case, fall back to /dev/urandom. */ + if (!FLAGS_SET(flags, RANDOM_BLOCK)) + break; return -errno; } else @@ -321,8 +112,6 @@ void initialize_srand(void) { #if HAVE_SYS_AUXV_H const void *auxv; #endif - unsigned long k; - if (srand_called) return; @@ -347,9 +136,6 @@ void initialize_srand(void) { x ^= (unsigned) now(CLOCK_REALTIME); x ^= (unsigned) gettid(); - if (rdrand(&k) >= 0) - x ^= (unsigned) k; - srand(x); srand_called = true; @@ -403,11 +189,8 @@ void random_bytes(void *p, size_t n) { * * What this function will do: * - * • This function will preferably use the CPU's RDRAND operation, if it is available, in - * order to return "mid-quality" random values cheaply. - * - * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are - * cheaply available. + * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if + * they are cheaply available, or less high-quality random values if they are not. * * • This function will return pseudo-random data, generated via libc rand() if nothing * better is available. @@ -430,7 +213,7 @@ void random_bytes(void *p, size_t n) { * This function is hence not useful for generating UUIDs or cryptographic key material. */ - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) + if (genuine_random_bytes(p, n, 0) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ @@ -504,6 +287,7 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) { return 1; } +#endif /* NM_IGNORED */ uint64_t random_u64_range(uint64_t m) { uint64_t x, remainder; @@ -523,4 +307,3 @@ uint64_t random_u64_range(uint64_t m) { return x % m; } -#endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/random-util.h b/src/libnm-systemd-shared/src/basic/random-util.h index e6528ddc7f..ccee32792f 100644 --- a/src/libnm-systemd-shared/src/basic/random-util.h +++ b/src/libnm-systemd-shared/src/basic/random-util.h @@ -6,11 +6,7 @@ #include typedef enum RandomFlags { - RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ - RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ - RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ - RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ - RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ + RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ @@ -31,10 +27,8 @@ static inline uint32_t random_u32(void) { return u; } -int rdrand(unsigned long *ret); - /* Some limits on the pool sizes when we deal with the kernel random pool */ -#define RANDOM_POOL_SIZE_MIN 512U +#define RANDOM_POOL_SIZE_MIN 32U #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) size_t random_pool_size(void); diff --git a/src/libnm-systemd-shared/src/basic/socket-util.h b/src/libnm-systemd-shared/src/basic/socket-util.h index ddea535008..709da20645 100644 --- a/src/libnm-systemd-shared/src/basic/socket-util.h +++ b/src/libnm-systemd-shared/src/basic/socket-util.h @@ -230,7 +230,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng ({ \ const union sockaddr_union *__sa = &(sa); \ size_t _len; \ - switch(__sa->sa.sa_family) { \ + switch (__sa->sa.sa_family) { \ case AF_INET: \ _len = sizeof(struct sockaddr_in); \ break; \ diff --git a/src/libnm-systemd-shared/src/basic/sort-util.h b/src/libnm-systemd-shared/src/basic/sort-util.h index 28c06c40fc..52d611b820 100644 --- a/src/libnm-systemd-shared/src/basic/sort-util.h +++ b/src/libnm-systemd-shared/src/basic/sort-util.h @@ -16,8 +16,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, #define typesafe_bsearch_r(k, b, n, func, userdata) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) @@ -36,8 +36,8 @@ static inline void* bsearch_safe(const void *key, const void *base, #define typesafe_bsearch(k, b, n, func) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \ }) @@ -57,7 +57,7 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison * is the prototype for the comparison function */ #define typesafe_qsort(p, n, func) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*) = func; \ _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \ }) @@ -72,7 +72,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso #define typesafe_qsort_r(p, n, func, userdata) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \ qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/basic/stat-util.c b/src/libnm-systemd-shared/src/basic/stat-util.c index 8126ce086a..fd32f25d28 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.c +++ b/src/libnm-systemd-shared/src/basic/stat-util.c @@ -132,17 +132,22 @@ bool null_or_empty(struct stat *st) { return false; } -int null_or_empty_path(const char *fn) { +int null_or_empty_path_with_root(const char *fn, const char *root) { struct stat st; + int r; assert(fn); - /* If we have the path, let's do an easy text comparison first. */ - if (path_equal(fn, "/dev/null")) + /* A symlink to /dev/null or an empty file? + * When looking under root_dir, we can't expect /dev/ to be mounted, + * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + + if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) return true; - if (stat(fn, &st) < 0) - return -errno; + r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL); + if (r < 0) + return r; return null_or_empty(&st); } @@ -191,8 +196,7 @@ int files_same(const char *filea, const char *fileb, int flags) { if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) return -errno; - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; + return stat_inode_same(&a, &b); } bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { @@ -211,7 +215,7 @@ int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } -#endif /* NM_IGNORE */ +#endif /* NM_IGNORED */ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { struct statfs s; @@ -222,7 +226,7 @@ int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { return is_fs_type(&s, magic_value); } -#if 0 /* NM_IGNORE */ +#if 0 /* NM_IGNORED */ bool is_temporary_fs(const struct statfs *s) { return fs_in_group(s, FILESYSTEM_SET_TEMPORARY); } @@ -257,6 +261,15 @@ int path_is_temporary_fs(const char *path) { return is_temporary_fs(&s); } + +int path_is_network_fs(const char *path) { + struct statfs s; + + if (statfs(path, &s) < 0) + return -errno; + + return is_network_fs(&s); +} #endif /* NM_IGNORED */ int stat_verify_regular(const struct stat *st) { @@ -420,6 +433,18 @@ int proc_mounted(void) { return r; } +bool stat_inode_same(const struct stat *a, const struct stat *b) { + + /* Returns if the specified stat structure references the same (though possibly modified) inode. Does + * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ + + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino; +} + #if 0 /* NM_IGNORED */ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { @@ -432,14 +457,10 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { * about contents of the file. The purpose here is to detect file contents changes, and nothing * else. */ - return a && b && - (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ - ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + return stat_inode_same(a, b) && a->st_mtim.tv_sec == b->st_mtim.tv_sec && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ - a->st_dev == b->st_dev && - a->st_ino == b->st_ino && (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } diff --git a/src/libnm-systemd-shared/src/basic/stat-util.h b/src/libnm-systemd-shared/src/basic/stat-util.h index a566114f7c..37513a43e7 100644 --- a/src/libnm-systemd-shared/src/basic/stat-util.h +++ b/src/libnm-systemd-shared/src/basic/stat-util.h @@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) { } bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); +int null_or_empty_path_with_root(const char *fn, const char *root); int null_or_empty_fd(int fd); +static inline int null_or_empty_path(const char *fn) { + return null_or_empty_path_with_root(fn, NULL); +} + int path_is_read_only_fs(const char *path); int files_same(const char *filea, const char *fileb, int flags); @@ -53,6 +57,7 @@ int fd_is_temporary_fs(int fd); int fd_is_network_fs(int fd); int path_is_temporary_fs(const char *path); +int path_is_network_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast * the const magic to the type, otherwise the compiler warns about @@ -91,6 +96,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret int proc_mounted(void); +bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); @@ -113,3 +119,9 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s struct new_statx nsx; \ } var #endif + +static inline bool devid_set_and_equal(dev_t a, dev_t b) { + /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't + * know" and we'll return false */ + return a == b && a != 0; +} diff --git a/src/libnm-systemd-shared/src/basic/string-table.h b/src/libnm-systemd-shared/src/basic/string-table.h index 97c1adc07d..e3a26a623c 100644 --- a/src/libnm-systemd-shared/src/basic/string-table.h +++ b/src/libnm-systemd-shared/src/basic/string-table.h @@ -113,4 +113,4 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k fputc_unlocked('\n', stdout); \ } \ funlockfile(stdout); \ - } while(false) + } while (false) diff --git a/src/libnm-systemd-shared/src/basic/strv.c b/src/libnm-systemd-shared/src/basic/strv.c index 3e0faf9590..f2cb80fe2e 100644 --- a/src/libnm-systemd-shared/src/basic/strv.c +++ b/src/libnm-systemd-shared/src/basic/strv.c @@ -19,8 +19,6 @@ #include "strv.h" char* strv_find(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -31,8 +29,6 @@ char* strv_find(char * const *l, const char *name) { } char* strv_find_case(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -43,8 +39,6 @@ char* strv_find_case(char * const *l, const char *name) { } char* strv_find_prefix(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -55,14 +49,14 @@ char* strv_find_prefix(char * const *l, const char *name) { } char* strv_find_startswith(char * const *l, const char *name) { - char * const *i, *e; - assert(name); /* Like strv_find_prefix, but actually returns only the * suffix, not the whole item */ STRV_FOREACH(i, l) { + char *e; + e = startswith(*i, name); if (e) return e; @@ -72,18 +66,13 @@ char* strv_find_startswith(char * const *l, const char *name) { } char** strv_free(char **l) { - if (!l) - return NULL; - - for (char **k = l; *k; k++) + STRV_FOREACH(k, l) free(*k); return mfree(l); } char** strv_free_erase(char **l) { - char **i; - STRV_FOREACH(i, l) erase_and_freep(i); @@ -91,32 +80,29 @@ char** strv_free_erase(char **l) { } char** strv_copy(char * const *l) { - char **r, **k; + _cleanup_strv_free_ char **result = NULL; + char **k; - k = r = new(char*, strv_length(l) + 1); - if (!r) + result = new(char*, strv_length(l) + 1); + if (!result) return NULL; - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } + k = result; + STRV_FOREACH(i, l) { + *k = strdup(*i); + if (!*k) + return NULL; + k++; + } *k = NULL; - return r; + return TAKE_PTR(result); } size_t strv_length(char * const *l) { size_t n = 0; - if (!l) - return 0; - - for (; *l; l++) + STRV_FOREACH(i, l) n++; return n; @@ -173,8 +159,8 @@ char** strv_new_internal(const char *x, ...) { } int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { - char * const *s, **t; size_t p, q, i = 0; + char **t; assert(a); @@ -220,7 +206,6 @@ rollback: #if 0 /* NM_IGNORED */ int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { - char * const *s; int r; STRV_FOREACH(s, b) { @@ -373,7 +358,6 @@ int strv_split_colon_pairs(char ***t, const char *s) { #endif /* NM_IGNORED */ char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { - char * const *s; char *r, *e; size_t n, k, m; @@ -601,8 +585,6 @@ int strv_extend_front(char ***l, const char *value) { } char** strv_uniq(char **l) { - char **i; - /* Drops duplicate entries. The first identical string will be * kept, the others dropped */ @@ -613,10 +595,8 @@ char** strv_uniq(char **l) { } bool strv_is_uniq(char * const *l) { - char * const *i; - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) + if (strv_contains(i+1, *i)) return false; return true; @@ -725,7 +705,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { */ _cleanup_free_ char *m = NULL; - char * const *i; size_t n = 0; assert(ret); @@ -762,8 +741,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { } bool strv_overlap(char * const *a, char * const *b) { - char * const *i; - STRV_FOREACH(i, a) if (strv_contains(b, *i)) return true; @@ -805,8 +782,6 @@ int strv_compare(char * const *a, char * const *b) { } void strv_print(char * const *l) { - char * const *s; - STRV_FOREACH(s, l) puts(*s); } @@ -841,8 +816,6 @@ char** strv_reverse(char **l) { #if 0 /* NM_IGNORED */ char** strv_shell_escape(char **l, const char *bad) { - char **s; - /* Escapes every character in every string in l that is in bad, * edits in-place, does not roll-back on error. */ @@ -926,7 +899,6 @@ rollback: int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; - char * const *s; int r; /* Like fputs(), but for strv, and with a less stupid argument order */ diff --git a/src/libnm-systemd-shared/src/basic/strv.h b/src/libnm-systemd-shared/src/basic/strv.h index 092d40c84b..2a858326c6 100644 --- a/src/libnm-systemd-shared/src/basic/strv.h +++ b/src/libnm-systemd-shared/src/basic/strv.h @@ -122,19 +122,30 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) { bool strv_overlap(char * const *a, char * const *b) _pure_; +#define _STRV_FOREACH(s, l, i) \ + for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) + #define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) + _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - typeof(l) _l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) +#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \ + for (typeof(*(l)) *s, *h = (l), *i = ({ \ + size_t _len = strv_length(h); \ + _len > 0 ? h + _len - 1 : NULL; \ + }); \ + (s = i); \ + i = PTR_SUB1(i, h)) -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) +#define STRV_FOREACH_BACKWARDS(s, l) \ + _STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ)) + +#define _STRV_FOREACH_PAIR(x, y, l, i) \ + for (typeof(*l) *x, *y, *i = (l); \ + i && *(x = i) && *(y = i + 1); \ + i += 2) + +#define STRV_FOREACH_PAIR(x, y, l) \ + _STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ)) char** strv_sort(char **l); void strv_print(char * const *l); @@ -185,7 +196,7 @@ void strv_print(char * const *l); #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = startswith(_p, *_i); \ if (_found) \ @@ -197,7 +208,7 @@ void strv_print(char * const *l); #define ENDSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = endswith(_p, *_i); \ if (_found) \ @@ -207,7 +218,7 @@ void strv_print(char * const *l); }) #define _FOREACH_STRING(uniq, x, y, ...) \ - for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ + for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \ x; \ x = *(++UNIQ_T(l, uniq))) diff --git a/src/libnm-systemd-shared/src/basic/time-util.c b/src/libnm-systemd-shared/src/basic/time-util.c index d7ea2b38d0..8476a6505f 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.c +++ b/src/libnm-systemd-shared/src/basic/time-util.c @@ -79,7 +79,7 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { ts->realtime = now(CLOCK_REALTIME); ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; + ts->boottime = now(CLOCK_BOOTTIME); return ts; } @@ -128,7 +128,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = u; return ts; } @@ -143,7 +143,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = ts->boottime = u; return ts; } @@ -152,9 +152,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) ts->realtime = u; ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); - ts->boottime = clock_boottime_supported() ? - map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) : - USEC_INFINITY; + ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)); return ts; } @@ -172,8 +170,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - clockid_t cid; +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) { usec_t nowm; if (u == USEC_INFINITY) { @@ -181,14 +178,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us return ts; } - cid = clock_boottime_or_monotonic(); - nowm = now(cid); - - if (cid == CLOCK_MONOTONIC) - ts->monotonic = u; - else - ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); - + nowm = now(CLOCK_BOOTTIME); + ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); return ts; } @@ -323,11 +314,13 @@ char *format_timestamp_style( time_t sec; size_t n; bool utc = false, us = false; + int r; assert(buf); switch (style) { case TIMESTAMP_PRETTY: + case TIMESTAMP_UNIX: break; case TIMESTAMP_US: us = true; @@ -350,9 +343,17 @@ char *format_timestamp_style( 1 + 1 + /* space and shortest possible zone */ 1)) return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; /* Timestamp is unset */ + if (style == TIMESTAMP_UNIX) { + r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */ + if (r < 0 || (size_t) r >= l) + return NULL; /* Doesn't fit */ + + return buf; + } + /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); @@ -420,7 +421,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) { const char *s; usec_t n, d; - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; n = now(CLOCK_REALTIME); @@ -793,6 +794,16 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { goto from_tm; } + /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */ + tm = copy; + k = strptime(t, "%b %d %H:%M:%S", &tm); + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } + tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { @@ -1447,33 +1458,6 @@ int verify_timezone(const char *name, int log_level) { return 0; } -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - bool clock_supported(clockid_t clock) { struct timespec ts; @@ -1481,16 +1465,10 @@ bool clock_supported(clockid_t clock) { case CLOCK_MONOTONIC: case CLOCK_REALTIME: + case CLOCK_BOOTTIME: + /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */ return true; - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - _fallthrough_; default: /* For everything else, check properly */ return clock_gettime(clock, &ts) >= 0; @@ -1639,6 +1617,7 @@ static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = { [TIMESTAMP_US] = "us", [TIMESTAMP_UTC] = "utc", [TIMESTAMP_US_UTC] = "us+utc", + [TIMESTAMP_UNIX] = "unix", }; /* Use the macro for enum → string to allow for aliases */ diff --git a/src/libnm-systemd-shared/src/basic/time-util.h b/src/libnm-systemd-shared/src/basic/time-util.h index 895af88299..bf312442b0 100644 --- a/src/libnm-systemd-shared/src/basic/time-util.h +++ b/src/libnm-systemd-shared/src/basic/time-util.h @@ -34,6 +34,7 @@ typedef enum TimestampStyle { TIMESTAMP_US, TIMESTAMP_UTC, TIMESTAMP_US_UTC, + TIMESTAMP_UNIX, _TIMESTAMP_STYLE_MAX, _TIMESTAMP_STYLE_INVALID = -EINVAL, } TimestampStyle; @@ -81,7 +82,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u); triple_timestamp* triple_timestamp_get(triple_timestamp *ts); triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); @@ -114,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; struct timespec* timespec_store(struct timespec *ts, usec_t u); struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n); +#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u)) + usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval* timeval_store(struct timeval *tv, usec_t u); +#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u)) + char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_; char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_; char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_; @@ -150,9 +155,7 @@ static inline bool timezone_is_valid(const char *name, int log_level) { return verify_timezone(name, log_level) >= 0; } -bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.c b/src/libnm-systemd-shared/src/basic/tmpfile-util.c index dc5a49e224..57447a4205 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.c +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.c @@ -280,6 +280,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { return fd; } +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) { + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + + assert(target); + assert(ret_file); + assert(ret_path); + + fd = open_tmpfile_linkable(target, flags, &path); + if (fd < 0) + return fd; + + f = take_fdopen(&fd, "w"); + if (!f) + return -ENOMEM; + + *ret_path = TAKE_PTR(path); + *ret_file = TAKE_PTR(f); + return 0; +} + int link_tmpfile(int fd, const char *path, const char *target) { assert(fd >= 0); assert(target); @@ -297,6 +319,23 @@ int link_tmpfile(int fd, const char *path, const char *target) { return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW)); } +int flink_tmpfile(FILE *f, const char *path, const char *target) { + int fd, r; + + assert(f); + assert(target); + + fd = fileno(f); + if (fd < 0) /* Not all FILE* objects encapsulate fds */ + return -EBADF; + + r = fflush_sync_and_check(f); + if (r < 0) + return r; + + return link_tmpfile(fd, path, target); +} + int mkdtemp_malloc(const char *template, char **ret) { _cleanup_free_ char *p = NULL; int r; diff --git a/src/libnm-systemd-shared/src/basic/tmpfile-util.h b/src/libnm-systemd-shared/src/basic/tmpfile-util.h index 45255fc062..610cbaf87e 100644 --- a/src/libnm-systemd-shared/src/basic/tmpfile-util.h +++ b/src/libnm-systemd-shared/src/basic/tmpfile-util.h @@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret); int open_tmpfile_unlinkable(const char *directory, int flags); int open_tmpfile_linkable(const char *target, int flags, char **ret_path); +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file); int link_tmpfile(int fd, const char *path, const char *target); +int flink_tmpfile(FILE *f, const char *path, const char *target); int mkdtemp_malloc(const char *template, char **ret); diff --git a/src/libnm-systemd-shared/src/basic/user-util.h b/src/libnm-systemd-shared/src/basic/user-util.h index bc76de6b41..e1692c4f66 100644 --- a/src/libnm-systemd-shared/src/basic/user-util.h +++ b/src/libnm-systemd-shared/src/basic/user-util.h @@ -12,6 +12,14 @@ #include #include +/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */ +#define HOME_UID_MIN 60001 +#define HOME_UID_MAX 60513 + +/* Users mapped from host into a container */ +#define MAP_UID_MIN 60514 +#define MAP_UID_MAX 60577 + bool uid_is_valid(uid_t uid); static inline bool gid_is_valid(gid_t gid) { @@ -59,6 +67,19 @@ int take_etc_passwd_lock(const char *root); #define UID_NOBODY ((uid_t) 65534U) #define GID_NOBODY ((gid_t) 65534U) +/* If REMOUNT_IDMAP_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host root + * user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in the + * *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we + * definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range + * we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use + * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is + * icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following + * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID + * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read + * this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */ +#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1)) +#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1)) + #define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer diff --git a/src/libnm-systemd-shared/src/basic/util.h b/src/libnm-systemd-shared/src/basic/util.h index dd1cb4f37e..368e3e4fbb 100644 --- a/src/libnm-systemd-shared/src/basic/util.h +++ b/src/libnm-systemd-shared/src/basic/util.h @@ -9,6 +9,12 @@ extern int saved_argc; extern char **saved_argv; static inline void save_argc_argv(int argc, char **argv) { + + /* Protect against CVE-2021-4034 style attacks */ + assert_se(argc > 0); + assert_se(argv); + assert_se(argv[0]); + saved_argc = argc; saved_argv = argv; } diff --git a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h index d597c743bb..1c198f6ad9 100644 --- a/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h +++ b/src/libnm-systemd-shared/src/fundamental/macro-fundamental.h @@ -53,14 +53,16 @@ #define CONCATENATE(x, y) XCONCATENATE(x, y) #ifdef SD_BOOT + void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; + #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 + #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define memcpy(a, b, c) CopyMem((a), (b), (c)) #define free(a) FreePool(a) @@ -74,6 +76,13 @@ _expr_; \ }) +#define ASSERT_SE_PTR(expr) \ + ({ \ + typeof(expr) _expr_ = (expr); \ + assert_se(_expr_); \ + _expr_; \ + }) + #if defined(static_assert) #define assert_cc(expr) \ static_assert(expr, #expr) @@ -243,23 +252,23 @@ CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ (CASE_F,__VA_ARGS__) -#define IN_SET(x, ...) \ - ({ \ - sd_bool _found = sd_false; \ +#define IN_SET(x, ...) \ + ({ \ + sd_bool _found = sd_false; \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ * doesn't work, as we want to use this on bitfields and gcc refuses typeof() on bitfields.) */ \ static const long double __assert_in_set[] _unused_ = { __VA_ARGS__ }; \ - assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = sd_true; \ - break; \ - default: \ - break; \ - } \ - _found; \ + assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ + switch (x) { \ + FOR_EACH_MAKE_CASE(__VA_ARGS__) \ + _found = sd_true; \ + break; \ + default: \ + break; \ + } \ + _found; \ }) /* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.c b/src/libnm-systemd-shared/src/shared/dns-domain.c index b82a9a1cc5..e932b6935c 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.c +++ b/src/libnm-systemd-shared/src/shared/dns-domain.c @@ -171,24 +171,18 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha /* Skip current terminal character (and accept domain names ending it ".") */ if (*terminal == 0) - terminal--; + terminal = PTR_SUB1(terminal, name); if (terminal >= name && *terminal == '.') - terminal--; + terminal = PTR_SUB1(terminal, name); /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - + while (terminal) { /* Find the start of the last label */ if (*terminal == '.') { const char *y; unsigned slashes = 0; - for (y = terminal - 1; y >= name && *y == '\\'; y--) + for (y = PTR_SUB1(terminal, name); y && *y == '\\'; y = PTR_SUB1(y, name)) slashes++; if (slashes % 2 == 0) { @@ -201,7 +195,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha } } - terminal--; + terminal = PTR_SUB1(terminal, name); } r = dns_label_unescape(&name, dest, sz, 0); @@ -1424,4 +1418,19 @@ int dns_name_dot_suffixed(const char *name) { return false; } } + +bool dns_name_dont_resolve(const char *name) { + + /* Never respond to some of the domains listed in RFC6303 */ + if (dns_name_endswith(name, "0.in-addr.arpa") > 0 || + dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 || + dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) + return true; + + /* Never respond to some of the domains listed in RFC6761 */ + if (dns_name_endswith(name, "invalid") > 0) + return true; + + return false; +} #endif /* NM_IGNORED */ diff --git a/src/libnm-systemd-shared/src/shared/dns-domain.h b/src/libnm-systemd-shared/src/shared/dns-domain.h index c237f761c9..ec174c0e51 100644 --- a/src/libnm-systemd-shared/src/shared/dns-domain.h +++ b/src/libnm-systemd-shared/src/shared/dns-domain.h @@ -105,3 +105,5 @@ int dns_name_apply_idna(const char *name, char **ret); int dns_name_is_valid_or_address(const char *name); int dns_name_dot_suffixed(const char *name); + +bool dns_name_dont_resolve(const char *name);