]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network/address-generation: regenerate IPv6 prefix stable address on conflict
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 9 Apr 2024 02:27:41 +0000 (11:27 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 9 Apr 2024 19:36:03 +0000 (04:36 +0900)
If a generated address with IPv6Token=prefixstable conflicts with the
one on another node or interface, let's generate another address and try
to assign it.

This improves support of RFC 7217.

Fixes #31605.

src/network/networkd-address-generation.c
src/network/networkd-address-generation.h
src/network/networkd-address.c
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-prefix-delegation.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-radv.c
src/network/networkd-radv.h

index d28094d53cc224bcd4b2ab4868cc6188b753407a..816cdf713ac5d650376efd2207c8066365ad4320 100644 (file)
@@ -390,6 +390,39 @@ int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *pref
         return generate_addresses(link, tokens, &RADV_APP_ID, prefix, prefixlen, ret);
 }
 
+int regenerate_address(Address *address, Link *link) {
+        struct in6_addr masked;
+        sd_id128_t app_id;
+
+        assert(link);
+        assert(address);
+        assert(address->family == AF_INET6);
+        assert(!address->link && !address->network);
+
+        if (!address->token ||
+            address->token->type != ADDRESS_GENERATION_PREFIXSTABLE)
+                return 0;
+
+        switch (address->source) {
+        case NETWORK_CONFIG_SOURCE_STATIC:
+                app_id = RADV_APP_ID;
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP_PD:
+                app_id = DHCP_PD_APP_ID;
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                app_id = NDISC_APP_ID;
+                break;
+        default:
+                assert_not_reached();
+        }
+
+        masked = address->in_addr.in6;
+        in6_addr_mask(&masked, address->prefixlen);
+
+        return generate_stable_private_address(link, &app_id, &address->token->secret_key, &masked, &address->in_addr.in6, &address->in_addr.in6);
+}
+
 int config_parse_address_generation_type(
                 const char *unit,
                 const char *filename,
index 68828b44ca016594b19025f97d3c343a01b210fa..2c6091321aeb6bc727ef32ef5390ed983ff8f028 100644 (file)
@@ -5,6 +5,7 @@
 #include "hashmap.h"
 #include "in-addr-util.h"
 
+typedef struct Address Address;
 typedef struct IPv6Token IPv6Token;
 typedef struct Link Link;
 
@@ -15,4 +16,6 @@ int dhcp_pd_generate_addresses(Link *link, const struct in6_addr *prefix, Hashma
 int ndisc_generate_addresses(Link *link, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
 int radv_generate_addresses(Link *link, Set *tokens, const struct in6_addr *prefix, uint8_t prefixlen, Hashmap **ret);
 
+int regenerate_address(Address *address, Link *link);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
index df9d0c03886e4f0a260e4fef9d7ff2056f5ebf49..b4ac0bc41b6e3e576ba44de824488a6917432e4d 100644 (file)
 #include "netlink-util.h"
 #include "networkd-address-pool.h"
 #include "networkd-address.h"
+#include "networkd-dhcp-prefix-delegation.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-ipv4acd.h"
 #include "networkd-manager.h"
+#include "networkd-ndisc.h"
 #include "networkd-netlabel.h"
 #include "networkd-network.h"
 #include "networkd-queue.h"
@@ -802,8 +804,49 @@ static int address_update(Address *address) {
         return 0;
 }
 
-static int address_drop(Address *address) {
-        Link *link = ASSERT_PTR(ASSERT_PTR(address)->link);
+static int address_removed_maybe_kernel_dad(Link *link, Address *address) {
+        int r;
+
+        assert(link);
+        assert(address);
+
+        if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+                return 0;
+
+        if (address->family != AF_INET6)
+                return 0;
+
+        if (!FLAGS_SET(address->flags, IFA_F_TENTATIVE))
+                return 0;
+
+        log_link_info(link, "Address %s with tentative flag is removed, maybe a duplicated address is assigned on another node or link?",
+                      IN6_ADDR_TO_STRING(&address->in_addr.in6));
+
+        /* Reset the address state, as the object may be reused in the below. */
+        address->state = 0;
+
+        switch (address->source) {
+        case NETWORK_CONFIG_SOURCE_STATIC:
+                r = link_reconfigure_radv_address(address, link);
+                break;
+        case NETWORK_CONFIG_SOURCE_DHCP_PD:
+                r = dhcp_pd_reconfigure_address(address, link);
+                break;
+        case NETWORK_CONFIG_SOURCE_NDISC:
+                r = ndisc_reconfigure_address(address, link);
+                break;
+        default:
+                r = 0;
+        }
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to configure an alternative address: %m");
+
+        return 0;
+}
+
+static int address_drop(Address *in, bool removed_by_us) {
+        _cleanup_(address_unrefp) Address *address = address_ref(ASSERT_PTR(in));
+        Link *link = ASSERT_PTR(address->link);
         int r;
 
         r = address_set_masquerade(address, /* add = */ false);
@@ -823,6 +866,14 @@ static int address_drop(Address *address) {
 
         address_detach(address);
 
+        if (!removed_by_us) {
+                r = address_removed_maybe_kernel_dad(link, address);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return r;
+                }
+        }
+
         link_update_operstate(link, /* also_update_master = */ true);
         link_check_ready(link);
         return 0;
@@ -1137,7 +1188,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Remov
                         if (address_get_request(link, address, &req) >= 0)
                                 address_enter_removed(req->userdata);
 
-                        (void) address_drop(address);
+                        (void) address_drop(address, /* removed_by_us = */ true);
                 }
         }
 
@@ -1847,9 +1898,11 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
 
         if (type == RTM_DELADDR) {
                 if (address) {
+                        bool removed_by_us = FLAGS_SET(address->state, NETWORK_CONFIG_STATE_REMOVING);
+
                         address_enter_removed(address);
                         log_address_debug(address, "Forgetting removed", link);
-                        (void) address_drop(address);
+                        (void) address_drop(address, removed_by_us);
                 } else
                         log_address_debug(tmp, "Kernel removed unknown", link);
 
index 6e83163b047251f1bc2482ce5cac9c275aaaa4f4..2e660b77631a5efacc36df7911425eb556c95534 100644 (file)
@@ -369,6 +369,28 @@ static int dhcp_pd_request_address_one(Address *address, Link *link) {
         return link_request_address(link, address, &link->dhcp_pd_messages, dhcp_pd_address_handler, NULL);
 }
 
+int dhcp_pd_reconfigure_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_DHCP_PD);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = dhcp_pd_request_address_one(address, link);
+        if (r < 0)
+                return r;
+
+        if (!link->dhcp_pd_configured)
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
+        link_check_ready(link);
+        return 0;
+}
+
 static int dhcp_pd_request_address(
                 Link *link,
                 const struct in6_addr *prefix,
index e591b8ae2e0947587abcbeb9ea3d83ee1f3a6a37..4a8cca92b6dfccd745a3688bacaef7ce38b46f31 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "conf-parser.h"
 
+typedef struct Address Address;
 typedef struct Link Link;
 
 bool link_dhcp_pd_is_enabled(Link *link);
@@ -19,5 +20,6 @@ int dhcp4_pd_prefix_acquired(Link *uplink);
 int dhcp6_pd_prefix_acquired(Link *uplink);
 void dhcp_pd_prefix_lost(Link *uplink);
 void dhcp4_pd_prefix_lost(Link *uplink);
+int dhcp_pd_reconfigure_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
index e160014c5ab34ecf4ab8d83a9769f0799bcad08b..2b039bed968c2d16e7831a34936946dde8dcde79 100644 (file)
@@ -377,6 +377,28 @@ static int ndisc_request_address(Address *address, Link *link, sd_ndisc_router *
         return 0;
 }
 
+int ndisc_reconfigure_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_NDISC);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = ndisc_request_address(address, link, NULL);
+        if (r < 0)
+                return r;
+
+        if (!link->ndisc_configured)
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
+        link_check_ready(link);
+        return 0;
+}
+
 static int ndisc_redirect_route_new(sd_ndisc_redirect *rd, Route **ret) {
         _cleanup_(route_unrefp) Route *route = NULL;
         struct in6_addr gateway, destination;
index 2766f5e4350d8ba0569e2d59c65277ca10104dc1..019fe4da425a0a7c2d2ff14ab19bbe3f102e0728 100644 (file)
@@ -4,6 +4,7 @@
 #include "conf-parser.h"
 #include "time-util.h"
 
+typedef struct Address Address;
 typedef struct Link Link;
 typedef struct Network Network;
 
@@ -61,6 +62,7 @@ int ndisc_stop(Link *link);
 void ndisc_flush(Link *link);
 
 int link_request_ndisc(Link *link);
+int ndisc_reconfigure_address(Address *address, Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_start_dhcp6_client);
 CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_use_domains);
index 279ea8d043b1dda972be9ae56cf5b9f532736686..6ff09119e3389f229c90c2c126f2e3c96516e251 100644 (file)
@@ -280,6 +280,29 @@ int link_request_radv_addresses(Link *link) {
         return 0;
 }
 
+int link_reconfigure_radv_address(Address *address, Link *link) {
+        int r;
+
+        assert(address);
+        assert(address->source == NETWORK_CONFIG_SOURCE_STATIC);
+        assert(link);
+
+        r = regenerate_address(address, link);
+        if (r <= 0)
+                return r;
+
+        r = link_request_static_address(link, address);
+        if (r < 0)
+                return r;
+
+        if (link->static_address_messages != 0) {
+                link->static_addresses_configured = false;
+                link_set_state(link, LINK_STATE_CONFIGURING);
+        }
+
+        return 0;
+}
+
 static int radv_set_prefix(Link *link, Prefix *prefix) {
         _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
         int r;
index 48677b50de4b775829c6debe272d17c8e67fdaab..94834e77a8e0d10a91335ba3f4dc262f31b09233 100644 (file)
@@ -71,6 +71,7 @@ void network_drop_invalid_pref64_prefixes(Network *network);
 void network_adjust_radv(Network *network);
 
 int link_request_radv_addresses(Link *link);
+int link_reconfigure_radv_address(Address *address, Link *link);
 
 bool link_radv_enabled(Link *link);
 int radv_start(Link *link);