diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2019-07-19 09:34:22 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2019-07-19 09:35:22 +0200 |
commit | f7e7bb6546ab5d679ab23dc80c714a26d1a57a24 (patch) | |
tree | 779bb86f4378fe222e9b70293a058360995c32b0 /src/network | |
parent | 217b7b33ccb29de90c6ab3ff0c43f55d6e31026d (diff) | |
parent | 195a18c17dea101c578b8b09f8eb1f9e0f813c1c (diff) | |
download | systemd-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.c | 221 | ||||
-rw-r--r-- | src/network/networkd-link.c | 1 | ||||
-rw-r--r-- | src/network/networkd-link.h | 1 | ||||
-rw-r--r-- | src/network/networkd-network-gperf.gperf | 1 | ||||
-rw-r--r-- | src/network/networkd-network.h | 1 | ||||
-rw-r--r-- | src/network/networkd-route.c | 97 | ||||
-rw-r--r-- | src/network/networkd-route.h | 2 |
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); |