dhcp-message: introduce dhcp_message_{append,get}_option_6rd()
These are for DHCP option 212 (6rd).
This commit is contained in:
@@ -311,6 +311,56 @@ int dhcp_message_append_option_routes(sd_dhcp_message *message, uint8_t code, si
|
||||
}
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t ipv4masklen,
|
||||
uint8_t prefixlen,
|
||||
const struct in6_addr *prefix,
|
||||
size_t n_br_addresses,
|
||||
const struct in_addr *br_addresses) {
|
||||
|
||||
assert(message);
|
||||
assert(prefix);
|
||||
assert(n_br_addresses == 0 || br_addresses);
|
||||
|
||||
/* See RFC 5969 Section 7.1.1 and dhcp_message_get_option_6rd() below. */
|
||||
|
||||
if (dhcp_message_has_option(message, SD_DHCP_OPTION_6RD))
|
||||
return -EEXIST;
|
||||
|
||||
if (ipv4masklen > 32)
|
||||
return -EINVAL;
|
||||
|
||||
if (32 - ipv4masklen + prefixlen > 128)
|
||||
return -EINVAL;
|
||||
|
||||
if (n_br_addresses == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (size_multiply_overflow(sizeof(struct in_addr), n_br_addresses))
|
||||
return -ENOBUFS;
|
||||
|
||||
size_t buflen = size_add(2 + sizeof(struct in6_addr), sizeof(struct in_addr) * n_br_addresses);
|
||||
if (buflen == SIZE_MAX)
|
||||
return -ENOBUFS;
|
||||
|
||||
_cleanup_free_ uint8_t *buf = new(uint8_t, buflen);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
uint8_t *p = buf;
|
||||
*p++ = ipv4masklen;
|
||||
*p++ = prefixlen;
|
||||
|
||||
struct in6_addr masked = *prefix;
|
||||
(void) in6_addr_mask(&masked, prefixlen);
|
||||
p = mempcpy(p, &masked, sizeof(struct in6_addr));
|
||||
|
||||
memcpy(p, br_addresses, n_br_addresses * sizeof(struct in_addr));
|
||||
|
||||
return dhcp_message_append_option(message, SD_DHCP_OPTION_6RD, buflen, buf);
|
||||
}
|
||||
|
||||
int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id) {
|
||||
assert(message);
|
||||
assert(id);
|
||||
@@ -728,6 +778,78 @@ int dhcp_message_get_option_routes(sd_dhcp_message *message, uint8_t code, size_
|
||||
}
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t *ret_ipv4masklen,
|
||||
uint8_t *ret_prefixlen,
|
||||
struct in6_addr *ret_prefix,
|
||||
size_t *ret_n_br_addresses,
|
||||
struct in_addr **ret_br_addresses) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(ret_n_br_addresses || !ret_br_addresses);
|
||||
|
||||
/* See RFC 5969 Section 7.1.1 */
|
||||
|
||||
_cleanup_(iovec_done) struct iovec iov = {};
|
||||
r = dhcp_message_get_option_alloc(message, SD_DHCP_OPTION_6RD, &iov);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
|
||||
if (iov.iov_len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
|
||||
(iov.iov_len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t n_br_addresses = (iov.iov_len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
|
||||
assert(n_br_addresses > 0); /* We have already checked that in the above. */
|
||||
|
||||
const uint8_t *p = iov.iov_base;
|
||||
|
||||
/* 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. */
|
||||
uint8_t ipv4masklen = *p++;
|
||||
if (ipv4masklen > 32)
|
||||
return -EBADMSG;
|
||||
|
||||
/* 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. */
|
||||
uint8_t prefixlen = *p++;
|
||||
if (32 - ipv4masklen + prefixlen > 128)
|
||||
return -EBADMSG;
|
||||
|
||||
/* 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. */
|
||||
struct in6_addr prefix;
|
||||
memcpy(&prefix, p, sizeof(struct in6_addr));
|
||||
(void) in6_addr_mask(&prefix, prefixlen);
|
||||
p += sizeof(struct in6_addr);
|
||||
|
||||
/* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relays for a given 6rd domain. */
|
||||
if (ret_br_addresses) {
|
||||
struct in_addr *br_addresses = newdup(struct in_addr, p, n_br_addresses);
|
||||
if (!br_addresses)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_br_addresses = br_addresses;
|
||||
}
|
||||
|
||||
if (ret_ipv4masklen)
|
||||
*ret_ipv4masklen = ipv4masklen;
|
||||
if (ret_prefixlen)
|
||||
*ret_prefixlen = prefixlen;
|
||||
if (ret_prefix)
|
||||
*ret_prefix = prefix;
|
||||
if (ret_n_br_addresses)
|
||||
*ret_n_br_addresses = n_br_addresses;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp_message_get_option_client_id(sd_dhcp_message *message, sd_dhcp_client_id *ret) {
|
||||
int r;
|
||||
|
||||
|
||||
@@ -40,6 +40,13 @@ int dhcp_message_append_option_address(sd_dhcp_message *message, uint8_t code, c
|
||||
int dhcp_message_append_option_addresses(sd_dhcp_message *message, uint8_t code, size_t n_addr, const struct in_addr *addr);
|
||||
int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, const char *data);
|
||||
int dhcp_message_append_option_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes);
|
||||
int dhcp_message_append_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t ipv4masklen,
|
||||
uint8_t prefixlen,
|
||||
const struct in6_addr *prefix,
|
||||
size_t n_br_addresses,
|
||||
const struct in_addr *br_addresses);
|
||||
int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id);
|
||||
int dhcp_message_append_option_parameter_request_list(sd_dhcp_message *message, Set *prl);
|
||||
int dhcp_message_append_option_hostname(sd_dhcp_message *message, uint8_t flags, bool is_client, const char *hostname);
|
||||
@@ -57,6 +64,13 @@ int dhcp_message_get_option_address(sd_dhcp_message *message, uint8_t code, stru
|
||||
int dhcp_message_get_option_addresses(sd_dhcp_message *message, uint8_t code, size_t *ret_n_addr, struct in_addr **ret_addr);
|
||||
int dhcp_message_get_option_string(sd_dhcp_message *message, uint8_t code, char **ret);
|
||||
int dhcp_message_get_option_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes);
|
||||
int dhcp_message_get_option_6rd(
|
||||
sd_dhcp_message *message,
|
||||
uint8_t *ret_ipv4masklen,
|
||||
uint8_t *ret_prefixlen,
|
||||
struct in6_addr *ret_prefix,
|
||||
size_t *ret_n_br_addresses,
|
||||
struct in_addr **ret_br_addresses);
|
||||
int dhcp_message_get_option_client_id(sd_dhcp_message *message, sd_dhcp_client_id *ret);
|
||||
int dhcp_message_get_option_parameter_request_list(sd_dhcp_message *message, Set **ret);
|
||||
int dhcp_message_get_option_fqdn(sd_dhcp_message *message, uint8_t *ret_flags, char **ret_fqdn);
|
||||
|
||||
@@ -125,6 +125,28 @@ static void verify_routes(sd_dhcp_message *m, size_t n_expected, const sd_dhcp_r
|
||||
}
|
||||
}
|
||||
|
||||
static void verify_6rd(
|
||||
sd_dhcp_message *m,
|
||||
uint8_t expected_ipv4masklen,
|
||||
uint8_t expected_prefixlen,
|
||||
const struct in6_addr *expected_prefix,
|
||||
size_t expected_n_br_addresses,
|
||||
const struct in_addr *expected_br_addresses) {
|
||||
|
||||
uint8_t ipv4masklen, prefixlen;
|
||||
struct in6_addr prefix;
|
||||
size_t n_br_addresses;
|
||||
_cleanup_free_ struct in_addr *br_addresses = NULL;
|
||||
|
||||
ASSERT_OK(dhcp_message_get_option_6rd(m, NULL, NULL, NULL, NULL, NULL));
|
||||
ASSERT_OK(dhcp_message_get_option_6rd(m, &ipv4masklen, &prefixlen, &prefix, &n_br_addresses, &br_addresses));
|
||||
ASSERT_EQ(ipv4masklen, expected_ipv4masklen);
|
||||
ASSERT_EQ(prefixlen, expected_prefixlen);
|
||||
ASSERT_TRUE(in6_addr_equal(&prefix, expected_prefix));
|
||||
ASSERT_EQ(n_br_addresses, expected_n_br_addresses);
|
||||
ASSERT_EQ(memcmp(br_addresses, expected_br_addresses, sizeof(struct in_addr) * n_br_addresses), 0);
|
||||
}
|
||||
|
||||
static void verify_client_id(sd_dhcp_message *m, const sd_dhcp_client_id *expected) {
|
||||
sd_dhcp_client_id id = {};
|
||||
ASSERT_OK(dhcp_message_get_option_client_id(m, &id));
|
||||
@@ -215,6 +237,17 @@ TEST(dhcp_message) {
|
||||
},
|
||||
};
|
||||
|
||||
uint8_t sixrd_ipv4masklen = 24;
|
||||
uint8_t sixrd_prefixlen = 64;
|
||||
struct in6_addr sixrd_prefix = {
|
||||
.s6_addr = { 0x20, 0x01, 0x0d, 0xb8, },
|
||||
};
|
||||
struct in_addr sixrd_br_addresses[3] = {
|
||||
{ .s_addr = htobe32(0xC0000231) },
|
||||
{ .s_addr = htobe32(0xC0000232) },
|
||||
{ .s_addr = htobe32(0xC0000233) },
|
||||
};
|
||||
|
||||
sd_dhcp_client_id id = {
|
||||
.raw = { 1, 3, 3, 3, 3, 3, 3, },
|
||||
.size = 7,
|
||||
@@ -321,6 +354,16 @@ TEST(dhcp_message) {
|
||||
ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, ELEMENTSOF(routes), routes));
|
||||
verify_routes(m, ELEMENTSOF(routes), routes);
|
||||
|
||||
/* 6rd */
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, 33, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, 127, &sixrd_prefix, 1, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 0, sixrd_br_addresses), EINVAL);
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, SIZE_MAX, sixrd_br_addresses), ENOBUFS);
|
||||
ASSERT_OK(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses));
|
||||
ASSERT_ERROR(dhcp_message_append_option_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, 1, sixrd_br_addresses), EEXIST);
|
||||
ASSERT_OK(dhcp_message_append_option_addresses(m, SD_DHCP_OPTION_6RD, ELEMENTSOF(sixrd_br_addresses) - 1, sixrd_br_addresses + 1));
|
||||
verify_6rd(m, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, ELEMENTSOF(sixrd_br_addresses), sixrd_br_addresses);
|
||||
|
||||
/* client ID */
|
||||
ASSERT_OK(dhcp_message_append_option_client_id(m, &id));
|
||||
verify_client_id(m, &id);
|
||||
@@ -383,6 +426,7 @@ TEST(dhcp_message) {
|
||||
verify_addresses(m2, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
|
||||
verify_string(m2, vendor_class);
|
||||
verify_routes(m2, ELEMENTSOF(routes), routes);
|
||||
verify_6rd(m2, sixrd_ipv4masklen, sixrd_prefixlen, &sixrd_prefix, ELEMENTSOF(sixrd_br_addresses), sixrd_br_addresses);
|
||||
verify_client_id(m2, &id);
|
||||
verify_prl(m2, prl);
|
||||
verify_hostname(m2, hostname);
|
||||
|
||||
Reference in New Issue
Block a user