diff options
Diffstat (limited to 'src/network/networkd-dhcp6.c')
-rw-r--r-- | src/network/networkd-dhcp6.c | 341 |
1 files changed, 258 insertions, 83 deletions
diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0fcd2cab3e..ed6b9df72b 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -20,20 +20,13 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); -static bool dhcp6_verify_link(Link *link) { - if (!link->network) { - log_link_info(link, "Link is not managed by us"); +static bool dhcp6_get_prefix_delegation(Link *link) { + if (!link->network) return false; - } - if (!IN_SET(link->network->router_prefix_delegation, - RADV_PREFIX_DELEGATION_DHCP6, - RADV_PREFIX_DELEGATION_BOTH)) { - log_link_debug(link, "Link does not request DHCPv6 prefix delegation"); - return false; - } - - return true; + return IN_SET(link->network->router_prefix_delegation, + RADV_PREFIX_DELEGATION_DHCP6, + RADV_PREFIX_DELEGATION_BOTH); } static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { @@ -50,7 +43,7 @@ static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { if (l == dhcp6_link) continue; - if (!dhcp6_verify_link(l)) + if (!dhcp6_get_prefix_delegation(l)) continue; return true; @@ -103,12 +96,74 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, return sd_radv_start(radv); } -static Network *dhcp6_reset_pd_prefix_network(Link *link) { +static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { + int r; + assert(link); - assert(link->manager); - assert(link->manager->networks); - return link->manager->networks; + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m"); + + return 1; +} + +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; + + 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 = NULL; + + 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_handler); + 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); + + continue; + } + + 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, @@ -119,8 +174,9 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, Link *link; Manager *manager = dhcp6_link->manager; union in_addr_union prefix; - uint8_t n_prefixes, n_used = 0; + uint64_t n_prefixes, n_used = 0; _cleanup_free_ char *buf = NULL; + _cleanup_free_ char *assigned_buf = NULL; int r; assert(manager); @@ -132,17 +188,17 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, if (r < 0) return r; - n_prefixes = 1 << (64 - pd_prefix_len); + n_prefixes = UINT64_C(1) << (64 - pd_prefix_len); (void) in_addr_to_string(AF_INET6, &prefix, &buf); - log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u", + log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u", n_prefixes, strnull(buf), pd_prefix_len); while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) { Link *assigned_link; if (n_used == n_prefixes) { - log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u", + log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u", n_used, n_prefixes, strnull(buf), pd_prefix_len); return -EAGAIN; @@ -151,67 +207,58 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, if (link == dhcp6_link) continue; - if (!dhcp6_verify_link(link)) + if (!dhcp6_get_prefix_delegation(link)) continue; assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6); if (assigned_link != NULL && assigned_link != link) continue; + (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf); r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64, lifetime_preferred, lifetime_valid); if (r < 0) { - log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m", + log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m", assigned_link ? "update": "assign", + strnull(assigned_buf), strnull(buf), pd_prefix_len); if (assigned_link == NULL) continue; } else - log_link_debug(link, "Assigned prefix %u/%u %s/64 to link", - n_used + 1, n_prefixes, strnull(buf)); + log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link", + n_used + 1, n_prefixes, + strnull(assigned_buf), + strnull(buf), pd_prefix_len); n_used++; - r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len); + r = in_addr_prefix_next(AF_INET6, &prefix, 64); if (r < 0 && n_used < n_prefixes) return r; } - if (n_used < n_prefixes) { - Route *route; - int 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_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) { + int r; - r = route_configure(route, dhcp6_link, NULL); - if (r < 0) { - route_free(route); - return r; - } + assert(link); - r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len); - if (r < 0) - return r; - } - } + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m"); - return n_used; + return 1; } + 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; @@ -221,22 +268,61 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { if (r < 0) return r; - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf); - - 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, &pd_prefix, &buf); log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", strnull(buf), pd_prefix_len); continue; } - r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix, + if (pd_prefix_len < 48) { + (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); + } + + 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_handler); + 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; + } + + 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); @@ -250,9 +336,71 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { return 0; } -static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, - void *userdata) { - _cleanup_(link_unrefp) Link *link = userdata; +int dhcp6_request_prefix_delegation(Link *link) { + Link *l; + Iterator i; + + assert_return(link, -EINVAL); + assert_return(link->manager, -EOPNOTSUPP); + + if (dhcp6_get_prefix_delegation(link) <= 0) + return 0; + + log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link"); + + HASHMAP_FOREACH(l, link->manager->links, i) { + int r, enabled; + + if (l == link) + continue; + + if (!l->dhcp6_client) + continue; + + r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link"); + continue; + } + + if (enabled == 0) { + r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link"); + continue; + } + } + + r = sd_dhcp6_client_is_running(l->dhcp6_client); + if (r <= 0) + continue; + + if (enabled != 0) { + log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link"); + (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l); + + continue; + } + + r = sd_dhcp6_client_stop(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link"); + continue; + } + + r = sd_dhcp6_client_start(l->dhcp6_client); + if (r < 0) { + log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link"); + continue; + } + + log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link"); + } + + return 0; +} + +static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; assert(link); @@ -354,6 +502,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; @@ -393,11 +542,12 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { } int dhcp6_request_address(Link *link, int ir) { - int r, inf_req; + int r, inf_req, pd; bool running; assert(link); assert(link->dhcp6_client); + assert(link->network); assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); r = sd_dhcp6_client_is_running(link->dhcp6_client); @@ -406,6 +556,21 @@ int dhcp6_request_address(Link *link, int ir) { else running = r; + r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd); + if (r < 0) + return r; + + if (pd && ir && link->network->dhcp6_force_pd_other_information) { + log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set"); + + r = sd_dhcp6_client_set_address_request(link->dhcp6_client, + false); + if (r < 0 ) + return r; + + ir = false; + } + if (running) { r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req); if (r < 0) @@ -453,11 +618,18 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { hn = hostname; } - return sd_dhcp6_client_set_fqdn(client, hn); + r = sd_dhcp6_client_set_fqdn(client, hn); + if (r == -EINVAL && hostname) + /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */ + log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m"); + else if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m"); + + return 0; } int dhcp6_configure(Link *link) { - sd_dhcp6_client *client = NULL; + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; const DUID *duid; int r; @@ -468,60 +640,63 @@ int dhcp6_configure(Link *link) { return 0; r = sd_dhcp6_client_new(&client); + if (r == -ENOMEM) + return log_oom(); if (r < 0) - return r; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m"); r = sd_dhcp6_client_attach_event(client, NULL, 0); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m"); r = sd_dhcp6_client_set_mac(client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m"); - r = sd_dhcp6_client_set_iaid(client, link->network->iaid); - if (r < 0) - goto error; + if (link->network->iaid_set) { + r = sd_dhcp6_client_set_iaid(client, link->network->iaid); + if (r < 0) + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); + } - duid = link_duid(link); - r = sd_dhcp6_client_set_duid(client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); + duid = link_get_duid(link); + if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) + r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time); + else + r = sd_dhcp6_client_set_duid(client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m"); r = dhcp6_set_hostname(client, link); if (r < 0) - goto error; + return r; r = sd_dhcp6_client_set_ifindex(client, link->ifindex); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m"); if (link->network->rapid_commit) { r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m"); } r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m"); if (dhcp6_enable_prefix_delegation(link)) { r = sd_dhcp6_client_set_prefix_delegation(client, true); if (r < 0) - goto error; + return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m"); } - link->dhcp6_client = client; + link->dhcp6_client = TAKE_PTR(client); return 0; - -error: - sd_dhcp6_client_unref(client); - return r; } |