These are for DHCP option 212 (6rd).
}
}
+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);
}
}
+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;
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);
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);
}
}
+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));
},
};
+ 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,
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);
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);