From 494c868d1fcdb72e0aed575ff0da36138b5af4b7 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Tue, 18 Sep 2018 18:32:19 -0600 Subject: [PATCH] networkd-dhcp6: Set one unreachable route per DHCPv6 delegated prefix Instead of setting many small unreachable routes for each of the /64 subnets that were not distributed between the links requesting delegated prefixes, set one unreachable route for the size of the delegated prefix. Each subnet asssigned to a downstream link will add a routable subnet for that link, and as the subnet assigned to the downstream link has a longer prefix than the whole delegated prefix, the downstream link subnet routes are preferred over the unroutable delegated one. The unreachable route is not added when the delegated prefix is exactly a /64 as the prefix size cannot be used to sort out the order of routing into a bigger blocking subnet with the smaller /64 punching routable "holes" into it. When stopping the DHCPv6 client, the unroutable delegated prefix is removed before the downstream link prefixes are unassigned. --- src/network/networkd-dhcp6.c | 156 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 28 deletions(-) diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index f9b97d1..c0572ad 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -111,6 +111,80 @@ static Network *dhcp6_reset_pd_prefix_network(Link *link) { return link->manager->networks; } +static int dhcp6_route_remove_cb(sd_netlink *nl, sd_netlink_message *m, + void *userdata) { + Link *l = userdata; + int r; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_debug_errno(l, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m"); + + l = link_unref(l); + + return 0; +} + +static int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { + int r; + sd_dhcp6_lease *lease; + union in_addr_union pd_prefix; + uint8_t pd_prefix_len; + uint32_t lifetime_preferred, lifetime_valid; + + r = sd_dhcp6_client_get_lease(client, &lease); + if (r < 0) + return r; + + dhcp6_reset_pd_prefix_network(link); + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + + while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, + &lifetime_preferred, + &lifetime_valid) >= 0) { + _cleanup_free_ char *buf = NULL; + _cleanup_free_ Route *route; + + if (pd_prefix_len > 64) + continue; + + (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + + if (pd_prefix_len < 64) { + r = route_new(&route); + if (r < 0) { + log_link_warning_errno(link, r, "Cannot create unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; + } + + route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, + 0, 0, 0, &route); + route_update(route, NULL, 0, NULL, NULL, 0, 0, + RTN_UNREACHABLE); + + r = route_remove(route, link, dhcp6_route_remove_cb); + if (r < 0) { + (void) in_addr_to_string(AF_INET6, + &pd_prefix, &buf); + + log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + route_free(route); + continue; + } + link = link_ref(link); + + log_link_debug(link, "Removing unreachable route %s/%u", + strnull(buf), pd_prefix_len); + } + } + + return 0; +} + static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, struct in6_addr *pd_prefix, uint8_t pd_prefix_len, @@ -184,39 +258,28 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, return r; } - if (n_used < n_prefixes) { - Route *route; - uint64_t n = n_used; - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET6; + return 0; +} - while (n < n_prefixes) { - route_update(route, &prefix, pd_prefix_len, NULL, NULL, - 0, 0, RTN_UNREACHABLE); +static int dhcp6_route_add_cb(sd_netlink *nl, sd_netlink_message *m, + void *userdata) { + Link *l = userdata; + int r; - r = route_configure(route, dhcp6_link, NULL); - if (r < 0) { - route_free(route); - return r; - } + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_debug_errno(l, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m"); - r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len); - if (r < 0) - return r; - } - } + l = link_unref(l); - return n_used; + return 0; } + static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { int r; sd_dhcp6_lease *lease; - struct in6_addr pd_prefix; + union in_addr_union pd_prefix; uint8_t pd_prefix_len; uint32_t lifetime_preferred, lifetime_valid; _cleanup_free_ char *buf = NULL; @@ -229,24 +292,60 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { dhcp6_reset_pd_prefix_network(link); sd_dhcp6_lease_reset_pd_prefix_iter(lease); - while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, + while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0) { if (pd_prefix_len > 64) { - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf); + (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", strnull(buf), pd_prefix_len); continue; } if (pd_prefix_len < 48) { - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf); + (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u", strnull(buf), pd_prefix_len); } - r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix, + if (pd_prefix_len < 64) { + Route *route = NULL; + + (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); + + r = route_new(&route); + if (r < 0) { + log_link_warning_errno(link, r, "Cannot create unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; + } + + route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, + 0, 0, 0, &route); + route_update(route, NULL, 0, NULL, NULL, 0, 0, + RTN_UNREACHABLE); + + r = route_configure(route, link, dhcp6_route_add_cb); + if (r < 0) { + log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + route_free(route); + continue; + } + link = link_ref(link); + + route_free(route); + + log_link_debug(link, "Configuring unreachable route for %s/%u", + strnull(buf), pd_prefix_len); + + } else + log_link_debug(link, "Not adding a blocking route since distributed prefix is /64"); + + r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6, pd_prefix_len, lifetime_preferred, lifetime_valid); @@ -364,6 +463,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { if (sd_dhcp6_client_get_lease(client, NULL) >= 0) log_link_warning(link, "DHCPv6 lease lost"); + (void) dhcp6_lease_pd_prefix_lost(client, link); (void) manager_dhcp6_prefix_remove_all(link->manager, link); link->dhcp6_configured = false; -- 2.7.4