]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: fix NFTSet population when [DHCPPrefixDelegation] Assign=no (#40049)
authorGovind Venugopal <gvenugo3@asu.edu>
Fri, 30 Jan 2026 14:38:13 +0000 (07:38 -0700)
committerGitHub <noreply@github.com>
Fri, 30 Jan 2026 14:38:13 +0000 (23:38 +0900)
When [DHCPPrefixDelegation] Assign=no, networkd creates routes instead of
addresses. These routes need to populate nftables sets for firewall
rules to work correctly.

This commit adds dhcp_pd_route_modify_nft_set() to handle NFT set
updates for DHCP-PD routes, similar to how address_modify_nft_set()
handles them for addresses.

Fixes: #38383
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-prefix-delegation.c
src/network/networkd-dhcp-prefix-delegation.h

index 2d8ee46ad9b7aebfe8ae81cf5f8b2920c7941d42..619c2f6377093cd3bd45654f3adac08f4e09f449 100644 (file)
@@ -18,6 +18,7 @@
 #include "hexdecoct.h"
 #include "in-addr-prefix-util.h"
 #include "networkd-dhcp-common.h"
+#include "networkd-dhcp-prefix-delegation.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
@@ -106,6 +107,7 @@ void network_adjust_dhcp(Network *network) {
         }
 
         network_adjust_dhcp4(network);
+        network_adjust_dhcp_prefix_delegation(network);
 }
 
 static bool duid_needs_product_uuid(const DUID *duid) {
index 59ad2daaee36efcee772dbc6c73e5a1a509dd43d..f53d7d1c5aada8da150cbdea3cb990fd2f5f72b0 100644 (file)
@@ -157,6 +157,63 @@ static int dhcp_pd_get_assigned_subnet_prefix(Link *link, const struct in6_addr
         return -ENOENT;
 }
 
+static void dhcp_pd_route_modify_nft_set(Route *route, Link *link, bool add) {
+        int r;
+
+        assert(route);
+        assert(link);
+        assert(link->manager);
+        assert(link->network);
+
+        if (!link->manager->nfnl)
+                return;
+
+        if (route->family != AF_INET6)
+                return;
+
+        if (route->source != NETWORK_CONFIG_SOURCE_DHCP_PD)
+                return;
+
+        /* When Assign=yes, address_modify_nft_set() manages the NFT set, not this function. */
+        if (link->network->dhcp_pd_assign)
+                return;
+
+        NFTSetContext *nft_set_context = &link->network->dhcp_pd_nft_set_context;
+
+        FOREACH_ARRAY(nft_set, nft_set_context->sets, nft_set_context->n_sets) {
+                assert(nft_set);
+
+                switch (nft_set->source) {
+                case NFT_SET_SOURCE_ADDRESS:
+                        /* Should be already warned in network_adjust_dhcp_prefix_delegation(). */
+                        continue;
+                case NFT_SET_SOURCE_PREFIX:
+                        r = nft_set_element_modify_iprange(link->manager->nfnl, add, nft_set->nfproto, route->family, nft_set->table, nft_set->set,
+                                                           &route->dst, route->dst_prefixlen);
+                        break;
+                case NFT_SET_SOURCE_IFINDEX: {
+                        uint32_t ifindex = link->ifindex;
+                        r = nft_set_element_modify_any(link->manager->nfnl, add, nft_set->nfproto, nft_set->table, nft_set->set,
+                                                       &ifindex, sizeof(ifindex));
+                        break;
+                }
+                default:
+                        assert_not_reached();
+                }
+
+                if (r < 0)
+                        log_warning_errno(r, "Failed to %s NFT set entry: family %s, table %s, set %s, IP prefix %s, ignoring: %m",
+                                          add ? "add" : "delete",
+                                          nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
+                                          IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen));
+                else
+                        log_debug("%s NFT set entry: family %s, table %s, set %s, IP prefix %s",
+                                  add ? "Added" : "Deleted",
+                                  nfproto_to_string(nft_set->nfproto), nft_set->table, nft_set->set,
+                                  IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen));
+        }
+}
+
 int dhcp_pd_remove(Link *link, bool only_marked) {
         int ret = 0;
 
@@ -185,6 +242,9 @@ int dhcp_pd_remove(Link *link, bool only_marked) {
 
                         link_remove_dhcp_pd_subnet_prefix(link, &route->dst.in6);
 
+                        /* Remove NFTSet entries before removing the route */
+                        dhcp_pd_route_modify_nft_set(route, link, /* add= */ false);
+
                         RET_GATHER(ret, route_remove_and_cancel(route, link->manager));
                 }
         } else {
@@ -285,6 +345,9 @@ static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Reques
         if (r <= 0)
                 return r;
 
+        /* Update NFTSet entries when route is successfully configured */
+        dhcp_pd_route_modify_nft_set(route, link, /* add= */ true);
+
         r = dhcp_pd_check_ready(link);
         if (r < 0)
                 link_enter_failed(link);
@@ -1333,6 +1396,27 @@ int link_drop_dhcp_pd_config(Link *link, Network *network) {
         return 0;
 }
 
+void network_adjust_dhcp_prefix_delegation(Network *network) {
+        assert(network);
+
+        if (!network->dhcp_pd)
+                return;
+
+        if (network->dhcp_pd_assign)
+                return;
+
+        /* If Assign=no, then DHCPv6 PD will create routes instead of addresses.
+         * NFTSet=address:... is not supported in this case. */
+
+        FOREACH_ARRAY(nft_set, network->dhcp_pd_nft_set_context.sets, network->dhcp_pd_nft_set_context.n_sets)
+                if (nft_set->source == NFT_SET_SOURCE_ADDRESS) {
+                        log_warning("%s: In [DHCPPrefixDelegation] section, when Assign= is disabled, "
+                                    "NFTSet=address:... is not supported and will be ignored.",
+                                    network->filename);
+                        break;
+                }
+}
+
 int config_parse_dhcp_pd_subnet_id(
                 const char *unit,
                 const char *filename,
index 3ffc2beb5411d5dee4bdf216d811b1c42540f01d..502f98eb029756e668a4029ebc9f1bd89bbb36f5 100644 (file)
@@ -14,6 +14,7 @@ int dhcp6_pd_prefix_acquired(Link *uplink);
 void dhcp4_pd_prefix_lost(Link *uplink);
 void dhcp6_pd_prefix_lost(Link *uplink);
 int dhcp_pd_reconfigure_address(Address *address, Link *link);
+void network_adjust_dhcp_prefix_delegation(Network *network);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_subnet_id);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_prefix_route_type);