From a07e83cc58f6498fca26190063c8616f587aa831 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Wed, 17 Jan 2024 16:49:02 -0700 Subject: [PATCH] network: Parse RFC9463 DHCPv6 DNR option Implement the parsing for V6_DNR DHCPv6 option. This does the same as the DHCP V4_DNR option. --- src/libsystemd-network/dhcp6-lease-internal.h | 3 + src/libsystemd-network/sd-dhcp6-lease.c | 103 ++++++++++++++++++ src/systemd/sd-dhcp6-lease.h | 2 + src/systemd/sd-dhcp6-protocol.h | 5 +- 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index e76a108f60e..60cd84f2d83 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -8,6 +8,7 @@ #include #include "sd-dhcp6-lease.h" +#include "dns-resolver-internal.h" #include "dhcp6-option.h" #include "dhcp6-protocol.h" @@ -38,6 +39,8 @@ struct sd_dhcp6_lease { struct in6_addr *dns; size_t dns_count; + sd_dns_resolver *dnr; + size_t n_dnr; char **domains; struct in6_addr *ntp; size_t ntp_count; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index e5d65475881..22707b771d5 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -9,6 +9,7 @@ #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "network-common.h" +#include "sort-util.h" #include "strv.h" #include "unaligned.h" @@ -438,6 +439,98 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { return strv_length(lease->domains); } +static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { + int r; + + assert(lease); + + _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {}; + + size_t offset = 0; + + /* priority */ + if (optlen - offset < sizeof(uint16_t)) + return -EBADMSG; + res.priority = unaligned_read_be16(optval + offset); + offset += sizeof(uint16_t); + + /* adn */ + if (optlen - offset < sizeof(uint16_t)) + return -EBADMSG; + size_t ilen = unaligned_read_be16(optval + offset); + offset += sizeof(uint16_t); + if (offset + ilen > optlen) + return -EBADMSG; + + r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name); + if (r < 0) + return r; + offset += ilen; + + /* RFC9463 § 3.1.6: adn only mode */ + if (offset == optlen) + return 0; + + /* addrs */ + if (optlen - offset < sizeof(uint16_t)) + return -EBADMSG; + ilen = unaligned_read_be16(optval + offset); + offset += sizeof(uint16_t); + if (offset + ilen > optlen) + return -EBADMSG; + + _cleanup_free_ struct in6_addr *addrs = NULL; + size_t n_addrs = 0; + + r = dhcp6_option_parse_addresses(optval + offset, ilen, &addrs, &n_addrs); + if (r < 0) + return r; + if (n_addrs == 0) + return -EBADMSG; + offset += ilen; + + res.addrs = new(union in_addr_union, n_addrs); + if (!res.addrs) + return -ENOMEM + + for (size_t i = 0; i < n_addrs; i++) { + union in_addr_union addr = {.in6 = addrs[i]}; + /* RFC9463 § 6.2 client MUST discard multicast and host loopback addresses */ + if (in_addr_is_multicast(AF_INET6, &addr) || + in_addr_is_localhost(AF_INET6, &addr)) + return -EBADMSG; + res.addrs[i] = addr; + } + res.n_addrs = n_addrs; + res.family = AF_INET6; + + /* svc params */ + r = dnr_parse_svc_params(optval + offset, optlen-offset, &res); + if (r < 0) + return r; + + /* Append this resolver */ + if (!GREEDY_REALLOC(lease->dnr, lease->n_dnr+1)) + return -ENOMEM; + + lease->dnr[lease->n_dnr++] = TAKE_STRUCT(res); + + typesafe_qsort(lease->dnr, lease->n_dnr, dns_resolver_prio_compare); + + return 1; +} + +int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret) { + assert_return(lease, -EINVAL); + assert_return(ret, -EINVAL); + + if (!lease->dnr) + return -ENODATA; + + *ret = lease->dnr; + return lease->n_dnr; +} + int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; @@ -851,6 +944,15 @@ static int dhcp6_lease_parse_message( irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false); break; + case SD_DHCP6_OPTION_V6_DNR: + r = dhcp6_lease_add_dnr(lease, optval, optlen); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to parse DNR option, ignoring: %m"); + if (r == 0) + log_dhcp6_client(client, "Received ADN-only DNRv6 option, ignoring."); + + break; + case SD_DHCP6_OPTION_VENDOR_OPTS: r = dhcp6_lease_add_vendor_option(lease, optval, optlen); if (r < 0) @@ -904,6 +1006,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { dhcp6_ia_free(lease->ia_na); dhcp6_ia_free(lease->ia_pd); free(lease->dns); + dns_resolver_done_many(lease->dnr, lease->n_dnr); free(lease->fqdn); free(lease->captive_portal); strv_free(lease->domains); diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index e18d57817ff..d6bcceb2a2e 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -30,6 +30,7 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_dhcp6_lease sd_dhcp6_lease; +typedef struct sd_dns_resolver sd_dns_resolver; int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret); int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret); @@ -74,6 +75,7 @@ int sd_dhcp6_lease_get_pd_lifetime_timestamp( int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret); +int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret); int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret); int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret); diff --git a/src/systemd/sd-dhcp6-protocol.h b/src/systemd/sd-dhcp6-protocol.h index 78c80f7c7ed..b1d9ca1ffab 100644 --- a/src/systemd/sd-dhcp6-protocol.h +++ b/src/systemd/sd-dhcp6-protocol.h @@ -165,8 +165,9 @@ enum { SD_DHCP6_OPTION_SLAP_QUAD = 140, /* RFC 8948 */ SD_DHCP6_OPTION_V6_DOTS_RI = 141, /* RFC 8973 */ SD_DHCP6_OPTION_V6_DOTS_ADDRESS = 142, /* RFC 8973 */ - SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143 /* RFC 6153 */ - /* option codes 144-65535 are unassigned */ + SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF = 143, /* RFC 6153 */ + SD_DHCP6_OPTION_V6_DNR = 144 /* RFC 9463 */ + /* option codes 145-65535 are unassigned */ }; _SD_END_DECLARATIONS; -- 2.47.3