summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-07-19 09:34:22 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2019-07-19 09:35:22 +0200
commitf7e7bb6546ab5d679ab23dc80c714a26d1a57a24 (patch)
tree779bb86f4378fe222e9b70293a058360995c32b0 /src/network
parent217b7b33ccb29de90c6ab3ff0c43f55d6e31026d (diff)
parent195a18c17dea101c578b8b09f8eb1f9e0f813c1c (diff)
downloadsystemd-f7e7bb6546ab5d679ab23dc80c714a26d1a57a24.tar.gz
Merge pull request #13070 from yuwata/network-set-route-to-dhcp-dns
Diffstat (limited to 'src/network')
-rw-r--r--src/network/networkd-dhcp4.c221
-rw-r--r--src/network/networkd-link.c1
-rw-r--r--src/network/networkd-link.h1
-rw-r--r--src/network/networkd-network-gperf.gperf1
-rw-r--r--src/network/networkd-network.h1
-rw-r--r--src/network/networkd-route.c97
-rw-r--r--src/network/networkd-route.h2
7 files changed, 261 insertions, 63 deletions
diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c
index 1bb1df32e9..f20254fc82 100644
--- a/src/network/networkd-dhcp4.c
+++ b/src/network/networkd-dhcp4.c
@@ -15,12 +15,13 @@
#include "string-util.h"
#include "sysctl-util.h"
-static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address);
-static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
+static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address);
void dhcp4_release_old_lease(Link *link) {
- union in_addr_union address = IN_ADDR_NULL, address_old = IN_ADDR_NULL;
+ struct in_addr address = {}, address_old = {};
assert(link);
@@ -29,15 +30,15 @@ void dhcp4_release_old_lease(Link *link) {
assert(link->dhcp_lease);
- (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old.in);
- (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in);
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old);
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- (void) dhcp_remove_routes(link, link->dhcp_lease_old, link->dhcp_lease, &address_old.in);
+ (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false);
+ (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false);
+ (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false);
- if (!in_addr_equal(AF_INET, &address_old, &address)) {
- (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old.in);
- (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old.in);
- }
+ if (!in4_addr_equal(&address_old, &address))
+ (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old);
link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
link_dirty(link);
@@ -84,6 +85,77 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se
return RT_SCOPE_UNIVERSE;
}
+static int dhcp_route_configure(Route **route, Link *link) {
+ int r;
+
+ assert(route);
+ assert(*route);
+ assert(link);
+
+ if (set_contains(link->dhcp_routes, *route))
+ return 0;
+
+ r = route_configure(*route, link, dhcp4_route_handler);
+ if (r <= 0)
+ return r;
+
+ link->dhcp4_messages++;
+
+ r = set_put(link->dhcp_routes, *route);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(*route);
+ return 0;
+}
+
+static int link_set_dns_routes(Link *link, const struct in_addr *address) {
+ const struct in_addr *dns;
+ uint32_t table;
+ int i, n, r;
+
+ assert(link);
+ assert(link->dhcp_lease);
+ assert(link->network);
+
+ if (!link->network->dhcp_use_dns ||
+ !link->network->dhcp_routes_to_dns)
+ return 0;
+
+ n = sd_dhcp_lease_get_dns(link->dhcp_lease, &dns);
+ if (IN_SET(n, 0, -ENODATA))
+ return 0;
+ if (n < 0)
+ return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m");
+
+ table = link_get_dhcp_route_table(link);
+
+ for (i = 0; i < n; i ++) {
+ _cleanup_(route_freep) Route *route = NULL;
+
+ r = route_new(&route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
+
+ /* Set routes to DNS servers. */
+
+ route->family = AF_INET;
+ route->dst.in = dns[i];
+ route->dst_prefixlen = 32;
+ route->prefsrc.in = *address;
+ route->scope = RT_SCOPE_LINK;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = table;
+
+ r = dhcp_route_configure(&route, link);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set route to DNS server: %m");
+ }
+
+ return 0;
+}
+
static int link_set_dhcp_routes(Link *link) {
_cleanup_free_ sd_dhcp_route **static_routes = NULL;
bool classless_route = false, static_route = false;
@@ -108,6 +180,13 @@ static int link_set_dhcp_routes(Link *link) {
* the addresses now, let's not configure the routes either. */
return 0;
+ r = set_ensure_allocated(&link->dhcp_routes, &route_full_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ /* Clear old entries in case the set was already allocated */
+ set_clear(link->dhcp_routes);
+
table = link_get_dhcp_route_table(link);
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
@@ -155,11 +234,12 @@ static int link_set_dhcp_routes(Link *link) {
if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
route->prefsrc.in = address;
- r = route_configure(route, link, dhcp4_route_handler);
+ if (set_contains(link->dhcp_routes, route))
+ continue;
+
+ r = dhcp_route_configure(&route, link);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set host route: %m");
- if (r > 0)
- link->dhcp4_messages++;
+ return log_link_error_errno(link, r, "Could not set route: %m");
}
r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
@@ -194,11 +274,9 @@ static int link_set_dhcp_routes(Link *link) {
route_gw->priority = link->network->dhcp_route_metric;
route_gw->table = table;
- r = route_configure(route_gw, link, dhcp4_route_handler);
+ r = dhcp_route_configure(&route_gw, link);
if (r < 0)
return log_link_error_errno(link, r, "Could not set host route: %m");
- if (r > 0)
- link->dhcp4_messages++;
r = route_new(&route);
if (r < 0)
@@ -211,42 +289,18 @@ static int link_set_dhcp_routes(Link *link) {
route->priority = link->network->dhcp_route_metric;
route->table = table;
- r = route_configure(route, link, dhcp4_route_handler);
+ r = dhcp_route_configure(&route, link);
if (r < 0)
- return log_link_error_errno(link, r, "Could not set routes: %m");
- if (r > 0)
- link->dhcp4_messages++;
- }
-
- return 0;
-}
-
-static bool route_present_in_routes(const Route *route, sd_dhcp_route **routes, unsigned n_routes) {
- assert(n_routes == 0 || routes);
-
- for (unsigned j = 0; j < n_routes; j++) {
- union in_addr_union a;
- unsigned char l;
-
- assert_se(sd_dhcp_route_get_gateway(routes[j], &a.in) >= 0);
- if (!in_addr_equal(AF_INET, &a, &route->gw))
- continue;
- assert_se(sd_dhcp_route_get_destination(routes[j], &a.in) >= 0);
- if (!in_addr_equal(AF_INET, &a, &route->dst))
- continue;
- assert_se(sd_dhcp_route_get_destination_prefix_length(routes[j], &l) >= 0);
- if (l != route->dst_prefixlen)
- continue;
- return true;
+ return log_link_error_errno(link, r, "Could not set router: %m");
}
- return false;
+ return link_set_dns_routes(link, &address);
}
-static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address) {
- _cleanup_free_ sd_dhcp_route **routes = NULL, **new_routes = NULL;
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL;
uint32_t table;
- int m = 0, n, i, r;
+ int n, i, r;
assert(link);
assert(address);
@@ -260,14 +314,6 @@ static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *n
else if (n < 0)
return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m");
- if (new_lease) {
- m = sd_dhcp_lease_get_routes(new_lease, &new_routes);
- if (m == -ENODATA)
- m = 0;
- else if (m < 0)
- return log_link_error_errno(link, m, "DHCP error: Failed to get routes: %m");
- }
-
table = link_get_dhcp_route_table(link);
for (i = 0; i < n; i++) {
@@ -287,7 +333,7 @@ static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *n
if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
route->prefsrc.in = *address;
- if (route_present_in_routes(route, new_routes, m))
+ if (!remove_all && set_contains(link->dhcp_routes, route))
continue;
(void) route_remove(route, link, NULL);
@@ -296,7 +342,7 @@ static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *n
return n;
}
-static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
_cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
const struct in_addr *router;
uint32_t table;
@@ -334,7 +380,8 @@ static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *
route_gw->priority = link->network->dhcp_route_metric;
route_gw->table = table;
- (void) route_remove(route_gw, link, NULL);
+ if (remove_all || !set_contains(link->dhcp_routes, route_gw))
+ (void) route_remove(route_gw, link, NULL);
r = route_new(&route);
if (r < 0)
@@ -347,12 +394,59 @@ static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *
route->priority = link->network->dhcp_route_metric;
route->table = table;
- (void) route_remove(route, link, NULL);
+ if (remove_all || !set_contains(link->dhcp_routes, route))
+ (void) route_remove(route, link, NULL);
+
+ return 0;
+}
+
+static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
+ const struct in_addr *dns;
+ uint32_t table;
+ int i, n, r;
+
+ assert(link);
+ assert(lease);
+ assert(link->network);
+
+ if (!link->network->dhcp_use_dns ||
+ !link->network->dhcp_routes_to_dns)
+ return 0;
+
+ n = sd_dhcp_lease_get_dns(lease, &dns);
+ if (IN_SET(n, 0, -ENODATA))
+ return 0;
+ if (n < 0)
+ return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m");
+
+ table = link_get_dhcp_route_table(link);
+
+ for (i = 0; i < n; i ++) {
+ _cleanup_(route_freep) Route *route = NULL;
+
+ r = route_new(&route);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not allocate route: %m");
+
+ route->family = AF_INET;
+ route->dst.in = dns[i];
+ route->dst_prefixlen = 32;
+ route->prefsrc.in = *address;
+ route->scope = RT_SCOPE_LINK;
+ route->protocol = RTPROT_DHCP;
+ route->priority = link->network->dhcp_route_metric;
+ route->table = table;
+
+ if (!remove_all && set_contains(link->dhcp_routes, route))
+ continue;
+
+ (void) route_remove(route, link, NULL);
+ }
return 0;
}
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address) {
_cleanup_(address_freep) Address *a = NULL;
struct in_addr netmask;
int r;
@@ -439,8 +533,9 @@ static int dhcp_lease_lost(Link *link) {
link->dhcp4_configured = false;
(void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- (void) dhcp_remove_routes(link, link->dhcp_lease, NULL, &address);
- (void) dhcp_remove_router(link, link->dhcp_lease, &address);
+ (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
+ (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
+ (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
(void) dhcp_remove_address(link, link->dhcp_lease, &address);
(void) dhcp_reset_mtu(link);
(void) dhcp_reset_hostname(link);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 63f7422096..8ca4583034 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -703,6 +703,7 @@ static Link *link_free(Link *link) {
sd_dhcp_server_unref(link->dhcp_server);
sd_dhcp_client_unref(link->dhcp_client);
sd_dhcp_lease_unref(link->dhcp_lease);
+ set_free(link->dhcp_routes);
link_lldp_emit_stop(link);
diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h
index bfdcef0e50..cd88388015 100644
--- a/src/network/networkd-link.h
+++ b/src/network/networkd-link.h
@@ -83,6 +83,7 @@ typedef struct Link {
sd_dhcp_client *dhcp_client;
sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
+ Set *dhcp_routes;
char *lease_file;
uint32_t original_mtu;
unsigned dhcp4_messages;
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 3501ffdd10..f5f837ad63 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -142,6 +142,7 @@ Route.FastOpenNoCookie, config_parse_fast_open_no_cookie,
Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCPv4.RoutesToDNS, config_parse_bool, 0, offsetof(Network, dhcp_routes_to_dns)
DHCPv4.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h
index 82fad492a8..0fa800841c 100644
--- a/src/network/networkd-network.h
+++ b/src/network/networkd-network.h
@@ -91,6 +91,7 @@ struct Network {
bool dhcp_broadcast;
int dhcp_critical;
bool dhcp_use_dns;
+ bool dhcp_routes_to_dns;
bool dhcp_use_ntp;
bool dhcp_use_mtu;
bool dhcp_use_routes;
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index a4b56ab46b..25ab527284 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -207,6 +207,103 @@ static int route_compare_func(const Route *a, const Route *b) {
DEFINE_PRIVATE_HASH_OPS(route_hash_ops, Route, route_hash_func, route_compare_func);
+static void route_full_hash_func(const Route *route, struct siphash *state) {
+ assert(route);
+
+ siphash24_compress(&route->family, sizeof(route->family), state);
+
+ switch (route->family) {
+ case AF_INET:
+ case AF_INET6:
+ siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
+ siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
+ siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
+ siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
+ siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
+ siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
+
+ siphash24_compress(&route->tos, sizeof(route->tos), state);
+ siphash24_compress(&route->priority, sizeof(route->priority), state);
+ siphash24_compress(&route->table, sizeof(route->table), state);
+ siphash24_compress(&route->protocol, sizeof(route->protocol), state);
+ siphash24_compress(&route->scope, sizeof(route->scope), state);
+ siphash24_compress(&route->type, sizeof(route->type), state);
+
+ break;
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ break;
+ }
+}
+
+static int route_full_compare_func(const Route *a, const Route *b) {
+ int r;
+
+ r = CMP(a->family, b->family);
+ if (r != 0)
+ return r;
+
+ switch (a->family) {
+ case AF_INET:
+ case AF_INET6:
+ r = CMP(a->dst_prefixlen, b->dst_prefixlen);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->src_prefixlen, b->src_prefixlen);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->tos, b->tos);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->table, b->table);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->protocol, b->protocol);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->scope, b->scope);
+ if (r != 0)
+ return r;
+
+ r = CMP(a->type, b->type);
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
+ r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
+ if (r != 0)
+ return r;
+
+ return memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
+ default:
+ /* treat any other address family as AF_UNSPEC */
+ return 0;
+ }
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+ route_full_hash_ops,
+ Route,
+ route_full_hash_func,
+ route_full_compare_func,
+ route_free);
+
bool route_equal(Route *r1, Route *r2) {
if (r1 == r2)
return true;
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index fda7ddedfa..9d9c980d90 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -49,6 +49,8 @@ struct Route {
LIST_FIELDS(Route, routes);
};
+extern const struct hash_ops route_full_hash_ops;
+
int route_new(Route **ret);
void route_free(Route *route);
int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);