#include "dhcp-client-id-internal.h"
#include "dhcp-message.h"
#include "dhcp-protocol.h"
+#include "dhcp-route.h"
#include "dns-def.h"
#include "dns-domain.h"
#include "errno-util.h"
#include "ether-addr-util.h"
#include "hostname-util.h"
+#include "in-addr-util.h"
#include "iovec-util.h"
#include "iovec-wrapper.h"
#include "ip-util.h"
return dhcp_message_append_option(message, code, strlen(data), data);
}
+static int dhcp_message_append_option_static_routes(sd_dhcp_message *message, size_t n_routes, const sd_dhcp_route *routes) {
+ int r;
+
+ assert(message);
+ assert(routes || n_routes == 0);
+
+ if (n_routes == 0)
+ return 0;
+
+ if (size_multiply_overflow(2 * sizeof(struct in_addr), n_routes))
+ return -ENOBUFS;
+
+ _cleanup_free_ struct in_addr *buf = new(struct in_addr, 2 * n_routes);
+ if (!buf)
+ return -ENOMEM;
+
+ size_t count = 0;
+ FOREACH_ARRAY(route, routes, n_routes) {
+ uint8_t prefixlen;
+ r = in4_addr_default_prefixlen(&route->dst_addr, &prefixlen);
+ if (r < 0)
+ return r;
+
+ if (prefixlen != route->dst_prefixlen)
+ return -EINVAL;
+
+ struct in_addr dst = route->dst_addr;
+ (void) in4_addr_mask(&dst, prefixlen);
+
+ buf[count++] = dst;
+ buf[count++] = route->gw_addr;
+ }
+
+ assert(count == 2 * n_routes);
+
+ return dhcp_message_append_option_addresses(message, SD_DHCP_OPTION_STATIC_ROUTE, 2 * n_routes, buf);
+}
+
+static int dhcp_message_append_option_classless_static_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes) {
+ assert(message);
+ assert(routes || n_routes == 0);
+ assert(IN_SET(code,
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE));
+
+ if (n_routes == 0)
+ return 0;
+
+ if (size_multiply_overflow(1 + 2 * sizeof(struct in_addr), n_routes))
+ return -ENOBUFS;
+
+ _cleanup_free_ uint8_t *buf = new(uint8_t, (1 + 2 * sizeof(struct in_addr)) * n_routes);
+ if (!buf)
+ return -ENOMEM;
+
+ uint8_t *p = buf;
+ FOREACH_ARRAY(route, routes, n_routes) {
+ if (route->dst_prefixlen > sizeof(struct in_addr) * 8)
+ return -EINVAL;
+
+ *p++ = route->dst_prefixlen;
+ struct in_addr dst = route->dst_addr;
+ (void) in4_addr_mask(&dst, route->dst_prefixlen);
+ p = mempcpy(p, &dst, DIV_ROUND_UP(route->dst_prefixlen, 8));
+ p = mempcpy(p, &route->gw_addr, sizeof(struct in_addr));
+ }
+
+ return dhcp_message_append_option(message, code, p - buf, buf);
+}
+
+int dhcp_message_append_option_routes(sd_dhcp_message *message, uint8_t code, size_t n_routes, const sd_dhcp_route *routes) {
+ switch (code) {
+ case SD_DHCP_OPTION_STATIC_ROUTE:
+ return dhcp_message_append_option_static_routes(message, n_routes, routes);
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ case SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE:
+ return dhcp_message_append_option_classless_static_routes(message, code, n_routes, routes);
+ default:
+ return -EINVAL;
+ }
+}
+
int dhcp_message_append_option_client_id(sd_dhcp_message *message, const sd_dhcp_client_id *id) {
assert(message);
assert(id);
return 0;
}
+static int dhcp_message_get_option_static_routes(sd_dhcp_message *message, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
+ int r;
+
+ assert(message);
+
+ size_t n;
+ _cleanup_free_ struct in_addr *addrs = NULL;
+ r = dhcp_message_get_option_addresses(message, SD_DHCP_OPTION_STATIC_ROUTE, &n, &addrs);
+ if (r < 0)
+ return r;
+
+ if (n % 2 != 0)
+ return -EBADMSG;
+
+ _cleanup_free_ sd_dhcp_route *routes = NULL;
+ size_t n_routes = 0;
+
+ for (size_t i = 0; i < n; i += 2) {
+ struct in_addr dst = addrs[i];
+
+ uint8_t prefixlen;
+ if (in4_addr_default_prefixlen(&dst, &prefixlen) < 0)
+ continue;
+
+ (void) in4_addr_mask(&dst, prefixlen);
+
+ /* RFC 2132 section 5.8:
+ * The default route (0.0.0.0) is an illegal destination for a static route.*/
+ if (in4_addr_is_null(&dst))
+ continue;
+
+ if (!ret_routes) {
+ n_routes++;
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(routes, n_routes + 1))
+ return -ENOMEM;
+
+ routes[n_routes++] = (struct sd_dhcp_route) {
+ .dst_addr = dst,
+ .gw_addr = addrs[i + 1],
+ .dst_prefixlen = prefixlen,
+ };
+ }
+
+ if (n_routes == 0)
+ return -ENODATA;
+
+ if (ret_routes)
+ *ret_routes = TAKE_PTR(routes);
+ if (ret_n_routes)
+ *ret_n_routes = n_routes;
+ return 0;
+}
+
+static int dhcp_message_get_option_classless_static_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
+ int r;
+
+ assert(message);
+ assert(IN_SET(code,
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE));
+
+ _cleanup_(iovec_done) struct iovec iov = {};
+ r = dhcp_message_get_option_alloc(message, code, &iov);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ sd_dhcp_route *routes = NULL;
+ size_t n_routes = 0;
+
+ for (struct iovec i = iov; iovec_is_set(&i);) {
+ uint8_t prefixlen = *(uint8_t*) i.iov_base;
+ iovec_inc(&i, 1);
+
+ if (prefixlen > 32)
+ return -EBADMSG;
+
+ size_t n = DIV_ROUND_UP(prefixlen, 8);
+ if (n > i.iov_len)
+ return -EBADMSG;
+
+ struct in_addr dst = {};
+ memcpy_safe(&dst, i.iov_base, n);
+ (void) in4_addr_mask(&dst, prefixlen);
+ iovec_inc(&i, n);
+
+ if (i.iov_len < sizeof(struct in_addr))
+ return -EBADMSG;
+
+ struct in_addr gw;
+ memcpy(&gw, i.iov_base, sizeof(struct in_addr));
+ iovec_inc(&i, sizeof(struct in_addr));
+
+ if (!ret_routes) {
+ n_routes++;
+ continue;
+ }
+
+ if (!GREEDY_REALLOC(routes, n_routes + 1))
+ return -ENOMEM;
+
+ routes[n_routes++] = (struct sd_dhcp_route) {
+ .dst_addr = dst,
+ .gw_addr = gw,
+ .dst_prefixlen = prefixlen,
+ };
+ }
+
+ if (n_routes == 0)
+ return -ENODATA;
+
+ if (ret_routes)
+ *ret_routes = TAKE_PTR(routes);
+ if (ret_n_routes)
+ *ret_n_routes = n_routes;
+ return 0;
+}
+
+int dhcp_message_get_option_routes(sd_dhcp_message *message, uint8_t code, size_t *ret_n_routes, sd_dhcp_route **ret_routes) {
+ switch (code) {
+ case SD_DHCP_OPTION_STATIC_ROUTE:
+ return dhcp_message_get_option_static_routes(message, ret_n_routes, ret_routes);
+ case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+ case SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE:
+ return dhcp_message_get_option_classless_static_routes(message, code, ret_n_routes, ret_routes);
+ default:
+ return -EINVAL;
+ }
+}
+
int dhcp_message_get_option_client_id(sd_dhcp_message *message, sd_dhcp_client_id *ret) {
int r;
int dhcp_message_append_option_address(sd_dhcp_message *message, uint8_t code, const struct in_addr *addr);
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_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_address(sd_dhcp_message *message, uint8_t code, struct in_addr *ret);
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_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);
#include "dhcp-client-id-internal.h"
#include "dhcp-message.h"
#include "dhcp-protocol.h"
+#include "dhcp-route.h"
#include "ether-addr-util.h"
#include "iovec-util.h"
#include "iovec-wrapper.h"
ASSERT_STREQ(s, joined);
}
+static void verify_routes(sd_dhcp_message *m, size_t n_expected, const sd_dhcp_route *expected) {
+ uint8_t code;
+ FOREACH_ARGUMENT(code,
+ SD_DHCP_OPTION_STATIC_ROUTE,
+ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE,
+ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE) {
+
+ _cleanup_free_ sd_dhcp_route *routes = NULL;
+ size_t n;
+ ASSERT_OK(dhcp_message_get_option_routes(m, code, &n, &routes));
+ ASSERT_EQ(n, n_expected);
+ for (size_t i = 0; i < n; i++) {
+ ASSERT_EQ(routes[i].dst_addr.s_addr, expected[i].dst_addr.s_addr);
+ ASSERT_EQ(routes[i].gw_addr.s_addr, expected[i].gw_addr.s_addr);
+ ASSERT_EQ(routes[i].dst_prefixlen, expected[i].dst_prefixlen);
+ }
+ }
+}
+
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));
{ .s_addr = htobe32(0xC0000214) },
};
+ struct sd_dhcp_route routes[3] = {
+ { /* class A: 10.0.0.0/8 -> 192.0.2.33 */
+ .dst_addr = { .s_addr = htobe32(0x0A000000) },
+ .gw_addr = { .s_addr = htobe32(0xC0000221) },
+ .dst_prefixlen = 8,
+ },
+ { /* class B: 172.16.0.0/16 -> 192.0.2.34 */
+ .dst_addr = { .s_addr = htobe32(0xAC100000) },
+ .gw_addr = { .s_addr = htobe32(0xC0000222) },
+ .dst_prefixlen = 16,
+ },
+ { /* class C: 192.168.0.0/24 -> 192.0.2.35 */
+ .dst_addr = { .s_addr = htobe32(0xC0A80000) },
+ .gw_addr = { .s_addr = htobe32(0xC0000223) },
+ .dst_prefixlen = 24,
+ },
+ };
+
sd_dhcp_client_id id = {
.raw = { 1, 3, 3, 3, 3, 3, 3, },
.size = 7,
ASSERT_OK(dhcp_message_append_option_string(m, SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, vendor_class));
verify_string(m, vendor_class);
+ /* routes */
+ ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_STATIC_ROUTE, ELEMENTSOF(routes), routes));
+ ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, ELEMENTSOF(routes), routes));
+ ASSERT_OK(dhcp_message_append_option_routes(m, SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, ELEMENTSOF(routes), routes));
+ verify_routes(m, ELEMENTSOF(routes), routes);
+
/* client ID */
ASSERT_OK(dhcp_message_append_option_client_id(m, &id));
verify_client_id(m, &id);
verify_address(m2, &addr);
verify_addresses(m2, ELEMENTSOF(ntp), ntp, ELEMENTSOF(sip), sip);
verify_string(m2, vendor_class);
+ verify_routes(m2, ELEMENTSOF(routes), routes);
verify_client_id(m2, &id);
verify_prl(m2, prl);
verify_hostname(m2, hostname);