]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp-client: support 6rd option
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 4 Dec 2021 17:40:18 +0000 (02:40 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 7 Dec 2021 11:32:02 +0000 (20:32 +0900)
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/sd-dhcp-lease.c
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp-lease.h

index 35a74dd7c27874ac6ab752773e3819a52b04daaa..992ac9f3255255c927d33ce55acf33467488954a 100644 (file)
@@ -70,6 +70,12 @@ struct sd_dhcp_lease {
 
         char *timezone;
 
+        uint8_t sixrd_ipv4masklen;
+        uint8_t sixrd_prefixlen;
+        struct in6_addr sixrd_prefix;
+        struct in_addr *sixrd_br_addresses;
+        size_t sixrd_n_br_addresses;
+
         LIST_HEAD(struct sd_dhcp_raw_option, private_options);
 };
 
index dd82163d9f7795d06a1753e2da467be9f9a9f1e5..7f6d15cb0993acd68753a47e8b2d95df192abbce 100644 (file)
@@ -251,6 +251,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
         return -ENODATA;
 }
 
+int sd_dhcp_lease_get_6rd(
+                sd_dhcp_lease *lease,
+                uint8_t *ret_ipv4masklen,
+                uint8_t *ret_prefixlen,
+                struct in6_addr *ret_prefix,
+                const struct in_addr **ret_br_addresses,
+                size_t *ret_n_br_addresses) {
+
+        assert_return(lease, -EINVAL);
+
+        if (lease->sixrd_n_br_addresses <= 0)
+                return -ENODATA;
+
+        if (ret_ipv4masklen)
+                *ret_ipv4masklen = lease->sixrd_ipv4masklen;
+        if (ret_prefixlen)
+                *ret_prefixlen = lease->sixrd_prefixlen;
+        if (ret_prefix)
+                *ret_prefix = lease->sixrd_prefix;
+        if (ret_br_addresses)
+                *ret_br_addresses = lease->sixrd_br_addresses;
+        if (ret_n_br_addresses)
+                *ret_n_br_addresses = lease->sixrd_n_br_addresses;
+
+        return 0;
+}
+
 int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
         assert_return(lease, -EINVAL);
         assert_return(data, -EINVAL);
@@ -289,6 +316,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
         free(lease->client_id);
         free(lease->vendor_specific);
         strv_free(lease->search_domains);
+        free(lease->sixrd_br_addresses);
         return mfree(lease);
 }
 
@@ -534,6 +562,61 @@ static int lease_parse_classless_routes(
         return 0;
 }
 
+static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
+        uint8_t ipv4masklen, prefixlen;
+        struct in6_addr prefix;
+        _cleanup_free_ struct in_addr *br_addresses = NULL;
+        size_t n_br_addresses;
+
+        assert(lease);
+        assert(option);
+
+        /* See RFC 5969 Section 7.1.1 */
+
+        if (lease->sixrd_n_br_addresses > 0)
+                /* Multiple 6rd option?? */
+                return -EINVAL;
+
+        /* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
+        if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
+            (len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
+                return -EINVAL;
+
+        /* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
+         *              within a given 6rd domain. This may be any value between 0 and 32. Any value
+         *              greater than 32 is invalid. */
+        ipv4masklen = option[0];
+        if (ipv4masklen > 32)
+                return -EINVAL;
+
+        /* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
+         *               purpose of bounds checking by DHCP option processing, the sum of
+         *               (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
+        prefixlen = option[1];
+        if (32 - ipv4masklen + prefixlen > 128)
+                return -EINVAL;
+
+        /* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
+         *            The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
+         *            MUST be initialized to zero by the sender and ignored by the receiver. */
+        memcpy(&prefix, option + 2, sizeof(struct in6_addr));
+        (void) in6_addr_mask(&prefix, prefixlen);
+
+        /* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */
+        n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
+        br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
+        if (!br_addresses)
+                return -ENOMEM;
+
+        lease->sixrd_ipv4masklen = ipv4masklen;
+        lease->sixrd_prefixlen = prefixlen;
+        lease->sixrd_prefix = prefix;
+        lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
+        lease->sixrd_n_br_addresses = n_br_addresses;
+
+        return 0;
+}
+
 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
         sd_dhcp_lease *lease = userdata;
         int r;
@@ -719,6 +802,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 lease->vendor_specific_len = len;
                 break;
 
+        case SD_DHCP_OPTION_6RD:
+                r = lease_parse_6rd(lease, option, len);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
+                break;
+
         case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
                 r = dhcp_lease_insert_private_option(lease, code, option, len);
                 if (r < 0)
index 285febd0d4dc6e378af74c32e80e6092e5584246..4af4b45f2d4cf4829ea2d9409e63b7e9098b0ad4 100644 (file)
@@ -97,6 +97,7 @@ enum {
         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_PRIVATE_BASE                   = 224,
         /* Windows 10 option to send when Anonymize=true */
         SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
index 5abf9a406c07aa3f152fcf3911cc36a2f945cbc1..478bbfd7a6d9e091854eefccc77ea8bd56aa5948 100644 (file)
@@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
 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);
+int sd_dhcp_lease_get_6rd(
+                sd_dhcp_lease *lease,
+                uint8_t *ret_ipv4masklen,
+                uint8_t *ret_prefixlen,
+                struct in6_addr *ret_prefix,
+                const struct in_addr **ret_br_addresses,
+                size_t *ret_n_br_addresses);
 
 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);