sd-dhcp-client: support 6rd option

This commit is contained in:
Yu Watanabe 2021-12-05 02:40:18 +09:00
parent 63dc602570
commit dbdcaca3ab
4 changed files with 103 additions and 0 deletions

View 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);
};

View 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)

View 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,

View 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);