From f7e6645ac64043f57363e37b0f8c45f7efaeaee7 Mon Sep 17 00:00:00 2001 From: Nandakumar Raghavan Date: Tue, 28 Apr 2026 11:14:48 +0000 Subject: [PATCH] networkd: add RouteTable= to [DHCPv6] section Allow users to allow DHCPv6 unreachable/blackhole routes (installed for delegated prefixes) into a specific routing table, analogous to the existing RouteTable= in [DHCPv4] and [IPv6AcceptRA]. The config parser config_parse_dhcp_or_ra_route_table() is extended with an AF_UNSPEC ltype discriminator for DHCPv6 (AF_INET6 is already taken by NDISC/RA). link_get_dhcp6_route_table() follows the same pattern as link_get_dhcp4_route_table() and link_get_ndisc_route_table(), falling back to the VRF table when not explicitly set. In dhcp_request_unreachable_route(), the table is applied only for NETWORK_CONFIG_SOURCE_DHCP6 routes (the uplink unreachable aggregates), not DHCP_PD routes (per-subnet routes on downstream interfaces), matching the intent of the feature. The !route->table_set guard avoids overriding a table already set by the route code. --- man/systemd.network.xml | 17 ++++++++++++++++ src/network/networkd-dhcp-common.c | 20 ++++++++++++++++--- src/network/networkd-dhcp-common.h | 1 + src/network/networkd-dhcp-prefix-delegation.c | 3 +++ src/network/networkd-network-gperf.gperf | 7 ++++--- src/network/networkd-network.c | 1 + src/network/networkd-network.h | 2 ++ 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 554d8da8ef..d62e4d6232 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -3188,6 +3188,23 @@ MultiPathRoute=2001:db8::1@eth0 + + RouteTable=num + + The table identifier for routes installed by the DHCPv6 client, e.g. unreachable + routes for delegated prefixes. Takes one of the predefined names default, + main, and local, names defined in + RouteTable= in + networkd.conf5, + or a number between 1…4294967295. + + When used in combination with VRF=, the VRF's routing table is + used when this parameter is not specified. + + + + + RapidCommit= diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index d2e51e7e91..3368145981 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -24,6 +24,7 @@ #include "networkd-manager.h" #include "networkd-network.h" #include "networkd-route-util.h" +#include "networkd-util.h" #include "parse-util.h" #include "set.h" #include "socket-util.h" @@ -60,6 +61,15 @@ uint32_t link_get_ndisc_route_table(Link *link) { return link_get_vrf_table(link); } +uint32_t link_get_dhcp6_route_table(Link *link) { + assert(link); + assert(link->network); + + if (link->network->dhcp6_route_table_set) + return link->network->dhcp6_route_table; + return link_get_vrf_table(link); +} + bool link_dhcp_enabled(Link *link, int family) { assert(link); assert(IN_SET(family, AF_INET, AF_INET6)); @@ -565,7 +575,7 @@ int config_parse_dhcp_or_ra_route_table( assert(filename); assert(lvalue); - assert(IN_SET(ltype, AF_INET, AF_INET6)); + assert(IN_SET(ltype, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6, NETWORK_CONFIG_SOURCE_NDISC)); assert(rvalue); r = manager_get_route_table_from_string(network->manager, rvalue, &rt); @@ -576,11 +586,15 @@ int config_parse_dhcp_or_ra_route_table( } switch (ltype) { - case AF_INET: + case NETWORK_CONFIG_SOURCE_DHCP4: network->dhcp_route_table = rt; network->dhcp_route_table_set = true; break; - case AF_INET6: + case NETWORK_CONFIG_SOURCE_DHCP6: + network->dhcp6_route_table = rt; + network->dhcp6_route_table_set = true; + break; + case NETWORK_CONFIG_SOURCE_NDISC: network->ndisc_route_table = rt; network->ndisc_route_table_set = true; break; diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 6be8bcd6dc..b6385492c3 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -41,6 +41,7 @@ typedef struct DUID { uint32_t link_get_dhcp4_route_table(Link *link); uint32_t link_get_ndisc_route_table(Link *link); +uint32_t link_get_dhcp6_route_table(Link *link); bool link_dhcp_enabled(Link *link, int family); static inline bool link_dhcp4_enabled(Link *link) { diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index e734b0171d..85c5233472 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -14,6 +14,7 @@ #include "in-addr-prefix-util.h" #include "networkd-address.h" #include "networkd-address-generation.h" +#include "networkd-dhcp-common.h" #include "networkd-dhcp-prefix-delegation.h" #include "networkd-dhcp6.h" #include "networkd-link.h" @@ -851,6 +852,8 @@ static int dhcp_request_unreachable_route( route->protocol = RTPROT_DHCP; route->priority = IP6_RT_PRIO_USER; route->lifetime_usec = lifetime_usec; + if (source == NETWORK_CONFIG_SOURCE_DHCP6 && !route->table_set) + route->table = link_get_dhcp6_route_table(link); r = route_adjust_nexthops(route, link); if (r < 0) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a5afeed1cb..9e6eb7c799 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -283,7 +283,7 @@ DHCPv4.IAID, config_parse_iaid, DHCPv4.DUIDType, config_parse_network_duid_type, 0, 0 DHCPv4.DUIDRawData, config_parse_network_duid_rawdata, 0, 0 DHCPv4.RouteMetric, config_parse_dhcp_route_metric, AF_INET, 0 -DHCPv4.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0 +DHCPv4.RouteTable, config_parse_dhcp_or_ra_route_table, NETWORK_CONFIG_SOURCE_DHCP4, 0 DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCPv4.ServerPort, config_parse_uint16, 0, offsetof(Network, dhcp_port) @@ -332,6 +332,7 @@ DHCPv6.RapidCommit, config_parse_bool, DHCPv6.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp6_netlabel) DHCPv6.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp6_send_release) DHCPv6.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp6_nft_set_context) +DHCPv6.RouteTable, config_parse_dhcp_or_ra_route_table, NETWORK_CONFIG_SOURCE_DHCP6, 0 IPv6AcceptRA.UseRedirect, config_parse_bool, 0, offsetof(Network, ndisc_use_redirect) IPv6AcceptRA.UseGateway, config_parse_bool, 0, offsetof(Network, ndisc_use_gateway) IPv6AcceptRA.UseRoutePrefix, config_parse_bool, 0, offsetof(Network, ndisc_use_route_prefix) @@ -346,7 +347,7 @@ IPv6AcceptRA.UseHopLimit, config_parse_bool, IPv6AcceptRA.UseReachableTime, config_parse_bool, 0, offsetof(Network, ndisc_use_reachable_time) IPv6AcceptRA.UseRetransmissionTime, config_parse_bool, 0, offsetof(Network, ndisc_use_retransmission_time) IPv6AcceptRA.DHCPv6Client, config_parse_ndisc_start_dhcp6_client, 0, offsetof(Network, ndisc_start_dhcp6_client) -IPv6AcceptRA.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET6, 0 +IPv6AcceptRA.RouteTable, config_parse_dhcp_or_ra_route_table, NETWORK_CONFIG_SOURCE_NDISC, 0 IPv6AcceptRA.RouteMetric, config_parse_ndisc_route_metric, 0, 0 IPv6AcceptRA.QuickAck, config_parse_bool, 0, offsetof(Network, ndisc_quickack) IPv6AcceptRA.UseCaptivePortal, config_parse_bool, 0, offsetof(Network, ndisc_use_captive_portal) @@ -669,7 +670,7 @@ DHCP.IAID, config_parse_iaid, DHCP.DUIDType, config_parse_network_duid_type, 0, 0 DHCP.DUIDRawData, config_parse_network_duid_rawdata, 0, 0 DHCP.RouteMetric, config_parse_dhcp_route_metric, AF_UNSPEC, 0 -DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, AF_INET, 0 +DHCP.RouteTable, config_parse_dhcp_or_ra_route_table, NETWORK_CONFIG_SOURCE_DHCP4, 0 DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, dhcp6_use_rapid_commit) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 17cf78db37..dfe524d150 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -424,6 +424,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID, .dhcp6_send_release = true, .dhcp6_pd_prefix_route_type = RTN_UNREACHABLE, + .dhcp6_route_table = RT_TABLE_MAIN, .dhcp_pd = -1, .dhcp_pd_announce = true, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index d1765a2bf4..625b3b9a08 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -205,6 +205,8 @@ typedef struct Network { char *dhcp6_netlabel; bool dhcp6_send_release; NFTSetContext dhcp6_nft_set_context; + uint32_t dhcp6_route_table; + bool dhcp6_route_table_set; /* DHCP Server Support */ bool dhcp_server;