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) {
if (l == dhcp6_link)
continue;
- if (!dhcp6_verify_link(l))
+ if (!dhcp6_get_prefix_delegation(l))
continue;
return true;
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,
union in_addr_union prefix;
uint64_t n_prefixes, n_used = 0;
_cleanup_free_ char *buf = NULL;
+ _cleanup_free_ char *assigned_buf = NULL;
int r;
assert(manager);
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 %" PRIu64 "/%" PRIu64 " %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;
- 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_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;
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);
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);
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;
}
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);
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)
if (r < 0)
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)
- return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
+ 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)
return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");