]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: Parse RFC9463 DHCPv6 DNR option
authorRonan Pigott <ronan@rjp.ie>
Wed, 17 Jan 2024 23:49:02 +0000 (16:49 -0700)
committerRonan Pigott <ronan@rjp.ie>
Mon, 21 Oct 2024 16:10:19 +0000 (09:10 -0700)
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
src/libsystemd-network/sd-dhcp6-lease.c
src/systemd/sd-dhcp6-lease.h
src/systemd/sd-dhcp6-protocol.h

index e76a108f60ea05b74ec407e2ddb4c94634d31f2e..60cd84f2d83def396c1d62c6891efaa1764f6fdc 100644 (file)
@@ -8,6 +8,7 @@
 #include <inttypes.h>
 
 #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;
index e5d65475881628794ae2d69f9059abfb9add8923..22707b771d57b4eafaabba185bcffeef705aab19 100644 (file)
@@ -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);
index e18d57817fff4e41adcce40f06f399bd86f95a76..d6bcceb2a2e0daa14189e11de70e9a60a92b12f8 100644 (file)
@@ -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);
index 78c80f7c7ede963ca69a94d1440f4a37f1aeb337..b1d9ca1ffab8be16a3f847d96cf13cd6312f1abf 100644 (file)
@@ -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;