Skip to content

Commit dbdcaca

Browse files
committed
sd-dhcp-client: support 6rd option
1 parent 63dc602 commit dbdcaca

File tree

4 files changed

+103
-0
lines changed

4 files changed

+103
-0
lines changed

src/libsystemd-network/dhcp-lease-internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ struct sd_dhcp_lease {
7070

7171
char *timezone;
7272

73+
uint8_t sixrd_ipv4masklen;
74+
uint8_t sixrd_prefixlen;
75+
struct in6_addr sixrd_prefix;
76+
struct in_addr *sixrd_br_addresses;
77+
size_t sixrd_n_br_addresses;
78+
7379
LIST_HEAD(struct sd_dhcp_raw_option, private_options);
7480
};
7581

src/libsystemd-network/sd-dhcp-lease.c

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
251251
return -ENODATA;
252252
}
253253

254+
int sd_dhcp_lease_get_6rd(
255+
sd_dhcp_lease *lease,
256+
uint8_t *ret_ipv4masklen,
257+
uint8_t *ret_prefixlen,
258+
struct in6_addr *ret_prefix,
259+
const struct in_addr **ret_br_addresses,
260+
size_t *ret_n_br_addresses) {
261+
262+
assert_return(lease, -EINVAL);
263+
264+
if (lease->sixrd_n_br_addresses <= 0)
265+
return -ENODATA;
266+
267+
if (ret_ipv4masklen)
268+
*ret_ipv4masklen = lease->sixrd_ipv4masklen;
269+
if (ret_prefixlen)
270+
*ret_prefixlen = lease->sixrd_prefixlen;
271+
if (ret_prefix)
272+
*ret_prefix = lease->sixrd_prefix;
273+
if (ret_br_addresses)
274+
*ret_br_addresses = lease->sixrd_br_addresses;
275+
if (ret_n_br_addresses)
276+
*ret_n_br_addresses = lease->sixrd_n_br_addresses;
277+
278+
return 0;
279+
}
280+
254281
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
255282
assert_return(lease, -EINVAL);
256283
assert_return(data, -EINVAL);
@@ -289,6 +316,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
289316
free(lease->client_id);
290317
free(lease->vendor_specific);
291318
strv_free(lease->search_domains);
319+
free(lease->sixrd_br_addresses);
292320
return mfree(lease);
293321
}
294322

@@ -534,6 +562,61 @@ static int lease_parse_classless_routes(
534562
return 0;
535563
}
536564

565+
static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
566+
uint8_t ipv4masklen, prefixlen;
567+
struct in6_addr prefix;
568+
_cleanup_free_ struct in_addr *br_addresses = NULL;
569+
size_t n_br_addresses;
570+
571+
assert(lease);
572+
assert(option);
573+
574+
/* See RFC 5969 Section 7.1.1 */
575+
576+
if (lease->sixrd_n_br_addresses > 0)
577+
/* Multiple 6rd option?? */
578+
return -EINVAL;
579+
580+
/* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
581+
if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
582+
(len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
583+
return -EINVAL;
584+
585+
/* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
586+
* within a given 6rd domain. This may be any value between 0 and 32. Any value
587+
* greater than 32 is invalid. */
588+
ipv4masklen = option[0];
589+
if (ipv4masklen > 32)
590+
return -EINVAL;
591+
592+
/* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
593+
* purpose of bounds checking by DHCP option processing, the sum of
594+
* (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
595+
prefixlen = option[1];
596+
if (32 - ipv4masklen + prefixlen > 128)
597+
return -EINVAL;
598+
599+
/* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
600+
* The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
601+
* MUST be initialized to zero by the sender and ignored by the receiver. */
602+
memcpy(&prefix, option + 2, sizeof(struct in6_addr));
603+
(void) in6_addr_mask(&prefix, prefixlen);
604+
605+
/* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */
606+
n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
607+
br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
608+
if (!br_addresses)
609+
return -ENOMEM;
610+
611+
lease->sixrd_ipv4masklen = ipv4masklen;
612+
lease->sixrd_prefixlen = prefixlen;
613+
lease->sixrd_prefix = prefix;
614+
lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
615+
lease->sixrd_n_br_addresses = n_br_addresses;
616+
617+
return 0;
618+
}
619+
537620
int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
538621
sd_dhcp_lease *lease = userdata;
539622
int r;
@@ -719,6 +802,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
719802
lease->vendor_specific_len = len;
720803
break;
721804

805+
case SD_DHCP_OPTION_6RD:
806+
r = lease_parse_6rd(lease, option, len);
807+
if (r < 0)
808+
log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
809+
break;
810+
722811
case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
723812
r = dhcp_lease_insert_private_option(lease, code, option, len);
724813
if (r < 0)

src/systemd/sd-dhcp-client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ enum {
9797
SD_DHCP_OPTION_SIP_SERVER = 120,
9898
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
9999
SD_DHCP_OPTION_MUD_URL = 161,
100+
SD_DHCP_OPTION_6RD = 212,
100101
SD_DHCP_OPTION_PRIVATE_BASE = 224,
101102
/* Windows 10 option to send when Anonymize=true */
102103
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,

src/systemd/sd-dhcp-lease.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
7171
int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
7272
int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
7373
int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
74+
int sd_dhcp_lease_get_6rd(
75+
sd_dhcp_lease *lease,
76+
uint8_t *ret_ipv4masklen,
77+
uint8_t *ret_prefixlen,
78+
struct in6_addr *ret_prefix,
79+
const struct in_addr **ret_br_addresses,
80+
size_t *ret_n_br_addresses);
7481

7582
int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
7683
int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);

0 commit comments

Comments
 (0)