]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-dhcp6.c
Delete duplicate lines
[thirdparty/systemd.git] / src / network / networkd-dhcp6.c
index 0ec4deb71642721a120ea8ddad485329d5625efd..f9df26d75e08f029d4f41062dd2ae5f36cafa243 100644 (file)
 
 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,
@@ -121,6 +176,7 @@ 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);
@@ -151,67 +207,57 @@ 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 %" 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;
@@ -221,22 +267,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 +335,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 +501,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 +541,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 +555,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)
@@ -490,15 +654,20 @@ int dhcp6_configure(Link *link) {
         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");