summaryrefslogtreecommitdiff
path: root/src/network/networkd-dhcp6.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/networkd-dhcp6.c')
-rw-r--r--src/network/networkd-dhcp6.c341
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;
}