dhcp-message: introduce dhcp_message_{append,get}_option_routes()
These are for DHCP options 33 (static route), 121 (classless static route), and 249 (private classless static route).
This commit is contained in:
@@ -6,11 +6,13 @@
|
||||
#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"
|
||||
@@ -227,6 +229,88 @@ int dhcp_message_append_option_string(sd_dhcp_message *message, uint8_t code, co
|
||||
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);
|
||||
@@ -512,6 +596,138 @@ int dhcp_message_get_option_string(sd_dhcp_message *message, uint8_t code, char
|
||||
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;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ int dhcp_message_append_option_sec(sd_dhcp_message *message, uint8_t code, usec_
|
||||
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);
|
||||
@@ -55,6 +56,7 @@ int dhcp_message_get_option_sec(sd_dhcp_message *message, uint8_t code, bool max
|
||||
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);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#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"
|
||||
@@ -105,6 +106,25 @@ static void verify_multiple_strings(sd_dhcp_message *m, char * const *expected)
|
||||
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));
|
||||
@@ -177,6 +197,24 @@ TEST(dhcp_message) {
|
||||
{ .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,
|
||||
@@ -277,6 +315,12 @@ TEST(dhcp_message) {
|
||||
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);
|
||||
@@ -338,6 +382,7 @@ TEST(dhcp_message) {
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user