Add support for DHCP static route options
This adds support for DHCP options 33 and 121: Static Route and Classless Static Route. To enable this feature, set UseRoutes=true in .network file. Returned routes are added to the routing table.
This commit is contained in:
committed by
Tom Gundersen
parent
ed942a9eb2
commit
e1ea665eda
@@ -402,6 +402,51 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_set_dhcp_routes(Link *link) {
|
||||
struct sd_dhcp_route *static_routes;
|
||||
size_t static_routes_size;
|
||||
int r;
|
||||
unsigned i;
|
||||
|
||||
assert(link);
|
||||
|
||||
r = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes, &static_routes_size);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_warning_link(link, "DHCP error: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
for (i = 0; i < static_routes_size; i++) {
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
|
||||
r = route_new_dynamic(&route);
|
||||
if (r < 0) {
|
||||
log_error_link(link, "Could not allocate route: %s",
|
||||
strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
route->family = AF_INET;
|
||||
route->in_addr.in = static_routes[i].gw_addr;
|
||||
route->dst_addr.in = static_routes[i].dst_addr;
|
||||
route->dst_prefixlen = static_routes[i].dst_prefixlen;
|
||||
route->metrics = DHCP_STATIC_ROUTE_METRIC;
|
||||
|
||||
r = route_configure(route, link, &route_handler);
|
||||
if (r < 0) {
|
||||
log_warning_link(link,
|
||||
"could not set host route: %s", strerror(-r));
|
||||
return r;
|
||||
}
|
||||
|
||||
link_ref(link);
|
||||
link->route_messages ++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int link_enter_set_routes(Link *link) {
|
||||
Route *rt;
|
||||
int r;
|
||||
@@ -525,6 +570,9 @@ static int link_enter_set_routes(Link *link) {
|
||||
link_ref(link);
|
||||
link->route_messages ++;
|
||||
}
|
||||
|
||||
if (link->network->dhcp_routes)
|
||||
link_set_dhcp_routes(link);
|
||||
}
|
||||
|
||||
if (link->route_messages == 0) {
|
||||
@@ -901,12 +949,11 @@ static int link_set_mtu(Link *link, uint32_t mtu) {
|
||||
|
||||
static int dhcp_lease_lost(Link *link) {
|
||||
_cleanup_address_free_ Address *address = NULL;
|
||||
_cleanup_route_free_ Route *route_gw = NULL;
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
struct in_addr addr;
|
||||
struct in_addr netmask;
|
||||
struct in_addr gateway;
|
||||
unsigned prefixlen;
|
||||
unsigned i;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
@@ -914,10 +961,36 @@ static int dhcp_lease_lost(Link *link) {
|
||||
|
||||
log_warning_link(link, "DHCP lease lost");
|
||||
|
||||
if (link->network->dhcp_routes) {
|
||||
struct sd_dhcp_route *routes;
|
||||
size_t routes_size;
|
||||
|
||||
r = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes, &routes_size);
|
||||
if (r >= 0) {
|
||||
for (i = 0; i < routes_size; i++) {
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
|
||||
r = route_new_dynamic(&route);
|
||||
if (r >= 0) {
|
||||
route->family = AF_INET;
|
||||
route->in_addr.in = routes[i].gw_addr;
|
||||
route->dst_addr.in = routes[i].dst_addr;
|
||||
route->dst_prefixlen = routes[i].dst_prefixlen;
|
||||
|
||||
route_drop(route, link, &route_drop_handler);
|
||||
link_ref(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = address_new_dynamic(&address);
|
||||
if (r >= 0) {
|
||||
r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
|
||||
if (r >= 0) {
|
||||
_cleanup_route_free_ Route *route_gw = NULL;
|
||||
_cleanup_route_free_ Route *route = NULL;
|
||||
|
||||
r = route_new_dynamic(&route_gw);
|
||||
if (r >= 0) {
|
||||
route_gw->family = AF_INET;
|
||||
@@ -1952,6 +2025,14 @@ static int link_configure(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
if (link->network->dhcp_routes) {
|
||||
r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_STATIC_ROUTE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (link->network->dhcp_server) {
|
||||
|
||||
@@ -53,4 +53,5 @@ DHCPv4.UseDNS, config_parse_bool, 0,
|
||||
DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu)
|
||||
DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_hostname)
|
||||
DHCPv4.UseDomainName, config_parse_bool, 0, offsetof(Network, dhcp_domainname)
|
||||
DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_routes)
|
||||
DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
|
||||
|
||||
@@ -89,6 +89,7 @@ static int network_load_one(Manager *manager, const char *filename) {
|
||||
network->dhcp_dns = true;
|
||||
network->dhcp_hostname = true;
|
||||
network->dhcp_domainname = true;
|
||||
network->dhcp_routes = true;
|
||||
|
||||
r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
|
||||
(void*) network_network_gperf_lookup, false, false, network);
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
|
||||
#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
|
||||
#define VXLAN_VID_MAX (1u << 24) - 1
|
||||
#define DHCP_STATIC_ROUTE_METRIC 1024
|
||||
|
||||
typedef struct NetDev NetDev;
|
||||
typedef struct Network Network;
|
||||
@@ -169,6 +170,7 @@ struct Network {
|
||||
bool dhcp_hostname;
|
||||
bool dhcp_domainname;
|
||||
bool dhcp_critical;
|
||||
bool dhcp_routes;
|
||||
bool ipv4ll;
|
||||
|
||||
bool dhcp_server;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "networkd.h"
|
||||
#include "network-internal.h"
|
||||
#include "dhcp-lease-internal.h"
|
||||
|
||||
static void test_deserialize_in_addr(void) {
|
||||
_cleanup_free_ struct in_addr *addresses = NULL;
|
||||
@@ -53,6 +54,63 @@ static void test_deserialize_in_addr(void) {
|
||||
assert_se(!memcmp(&f, &addresses6[2], sizeof(struct in6_addr)));
|
||||
}
|
||||
|
||||
static void test_deserialize_dhcp_routes(void) {
|
||||
size_t size, allocated;
|
||||
|
||||
{
|
||||
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
|
||||
assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, "") >= 0);
|
||||
assert_se(size == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* no errors */
|
||||
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
|
||||
const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0/24,10.1.2.1 0.0.0.0/0,10.0.1.1";
|
||||
|
||||
assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
|
||||
|
||||
assert_se(size == 3);
|
||||
assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0"));
|
||||
assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1"));
|
||||
assert_se(routes[0].dst_prefixlen == 16);
|
||||
|
||||
assert_se(routes[1].dst_addr.s_addr == inet_addr("10.1.2.0"));
|
||||
assert_se(routes[1].gw_addr.s_addr == inet_addr("10.1.2.1"));
|
||||
assert_se(routes[1].dst_prefixlen == 24);
|
||||
|
||||
assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0"));
|
||||
assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1"));
|
||||
assert_se(routes[2].dst_prefixlen == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* error in second word */
|
||||
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
|
||||
const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.1";
|
||||
|
||||
assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
|
||||
|
||||
assert_se(size == 2);
|
||||
assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0"));
|
||||
assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1"));
|
||||
assert_se(routes[0].dst_prefixlen == 16);
|
||||
|
||||
assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0"));
|
||||
assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1"));
|
||||
assert_se(routes[2].dst_prefixlen == 0);
|
||||
}
|
||||
|
||||
{
|
||||
/* error in every word */
|
||||
_cleanup_free_ struct sd_dhcp_route *routes = NULL;
|
||||
const char *routes_string = "192.168.0.0/55,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.X";
|
||||
|
||||
assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
|
||||
assert_se(size == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_load_config(Manager *manager) {
|
||||
/* TODO: should_reload, is false if the config dirs do not exist, so
|
||||
* so we can't do this test here, move it to a test for paths_check_timestamps
|
||||
@@ -125,6 +183,7 @@ int main(void) {
|
||||
struct udev_device *loopback;
|
||||
|
||||
test_deserialize_in_addr();
|
||||
test_deserialize_dhcp_routes();
|
||||
test_address_equality();
|
||||
|
||||
assert_se(manager_new(&manager) >= 0);
|
||||
|
||||
Reference in New Issue
Block a user