Merge pull request #33941 from yuwata/network-dhcp-pd-route-type
network/dhcp-pd: introduce UnassignedSubnetPolicy= to customize type of 'catch-all' route
This commit is contained in:
@@ -2762,6 +2762,23 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>UnassignedSubnetPolicy=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes <literal>none</literal>, or one of the reject types: <literal>unreachable</literal>,
|
||||
<literal>prohibit</literal>, <literal>blackhole</literal>, or <literal>throw</literal>. If a reject
|
||||
type is specified, the reject route corresponding to the acquired 6RD prefix will be configured.
|
||||
For example, when <literal>unreachable</literal>,
|
||||
<programlisting>unreachable 2001:db8::/56 dev lo proto dhcp metric 1024 pref medium</programlisting>
|
||||
will be configured. See <ulink url="https://datatracker.ietf.org/doc/html/rfc7084">RFC 7084</ulink>.
|
||||
If <literal>none</literal> is specified, such route will not be configured. This may be useful when
|
||||
custom firewall rules that handle packets for unassigned subnets will be configured.
|
||||
Defaults to <literal>unreachable</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>IPv6OnlyMode=</varname></term>
|
||||
<listitem>
|
||||
@@ -3002,6 +3019,23 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>UnassignedSubnetPolicy=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes <literal>none</literal> or one of the reject types: <literal>unreachable</literal>,
|
||||
<literal>prohibit</literal>, <literal>blackhole</literal>, or <literal>throw</literal>. If a reject
|
||||
type is specified, the reject route corresponding to the delegated prefix will be configured.
|
||||
For example, when <literal>unreachable</literal>,
|
||||
<programlisting>unreachable 2001:db8::/56 dev lo proto dhcp metric 1024 pref medium</programlisting>
|
||||
will be configured. See <ulink url="https://datatracker.ietf.org/doc/html/rfc7084">RFC 7084</ulink>.
|
||||
If <literal>none</literal> is specified, such route will not be configured. This may be useful when
|
||||
custom firewall rules that handle packets for unassigned subnets will be configured.
|
||||
Defaults to <literal>unreachable</literal>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RapidCommit=</varname></term>
|
||||
<listitem>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "networkd-manager.h"
|
||||
#include "networkd-queue.h"
|
||||
#include "networkd-radv.h"
|
||||
#include "networkd-route-util.h"
|
||||
#include "networkd-route.h"
|
||||
#include "networkd-setlink.h"
|
||||
#include "parse-util.h"
|
||||
@@ -697,6 +698,7 @@ static int dhcp_request_unreachable_route(
|
||||
usec_t lifetime_usec,
|
||||
NetworkConfigSource source,
|
||||
const union in_addr_union *server_address,
|
||||
uint8_t type, /* RTN_* */
|
||||
unsigned *counter,
|
||||
route_netlink_handler_t callback,
|
||||
bool *configured) {
|
||||
@@ -710,10 +712,14 @@ static int dhcp_request_unreachable_route(
|
||||
assert(addr);
|
||||
assert(IN_SET(source, NETWORK_CONFIG_SOURCE_DHCP4, NETWORK_CONFIG_SOURCE_DHCP6));
|
||||
assert(server_address);
|
||||
assert(type == RTN_UNSPEC || route_type_is_reject(type));
|
||||
assert(counter);
|
||||
assert(callback);
|
||||
assert(configured);
|
||||
|
||||
if (type == RTN_UNSPEC)
|
||||
return 0; /* Disabled. */
|
||||
|
||||
if (prefixlen >= 64) {
|
||||
log_link_debug(link, "Not adding a blocking route for DHCP delegated prefix %s since the prefix has length >= 64.",
|
||||
IN6_ADDR_PREFIX_TO_STRING(addr, prefixlen));
|
||||
@@ -729,7 +735,7 @@ static int dhcp_request_unreachable_route(
|
||||
route->family = AF_INET6;
|
||||
route->dst.in6 = *addr;
|
||||
route->dst_prefixlen = prefixlen;
|
||||
route->type = RTN_UNREACHABLE;
|
||||
route->type = type;
|
||||
route->protocol = RTPROT_DHCP;
|
||||
route->priority = IP6_RT_PRIO_USER;
|
||||
route->lifetime_usec = lifetime_usec;
|
||||
@@ -758,8 +764,12 @@ static int dhcp4_request_unreachable_route(
|
||||
usec_t lifetime_usec,
|
||||
const union in_addr_union *server_address) {
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
return dhcp_request_unreachable_route(link, addr, prefixlen, lifetime_usec,
|
||||
NETWORK_CONFIG_SOURCE_DHCP4, server_address,
|
||||
link->network->dhcp_6rd_prefix_route_type,
|
||||
&link->dhcp4_messages, dhcp4_unreachable_route_handler,
|
||||
&link->dhcp4_configured);
|
||||
}
|
||||
@@ -771,8 +781,12 @@ static int dhcp6_request_unreachable_route(
|
||||
usec_t lifetime_usec,
|
||||
const union in_addr_union *server_address) {
|
||||
|
||||
assert(link);
|
||||
assert(link->network);
|
||||
|
||||
return dhcp_request_unreachable_route(link, addr, prefixlen, lifetime_usec,
|
||||
NETWORK_CONFIG_SOURCE_DHCP6, server_address,
|
||||
link->network->dhcp6_pd_prefix_route_type,
|
||||
&link->dhcp6_messages, dhcp6_unreachable_route_handler,
|
||||
&link->dhcp6_configured);
|
||||
}
|
||||
@@ -1297,3 +1311,51 @@ int config_parse_dhcp_pd_subnet_id(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_dhcp_pd_prefix_route_type(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
uint8_t *p = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
assert(filename);
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*p = RTN_UNREACHABLE; /* Defaults to unreachable. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (streq(rvalue, "none")) {
|
||||
*p = RTN_UNSPEC; /* Indicate that the route is disabled. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = route_type_from_string(rvalue);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r,
|
||||
"Failed to parse %s=, ignoring assignment: %s",
|
||||
lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!route_type_is_reject(r)) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, 0,
|
||||
"Invalid route type is specified to %s=, ignoring assignment: %s",
|
||||
lvalue, rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*p = r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,3 +23,4 @@ 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);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_pd_prefix_route_type);
|
||||
|
||||
@@ -269,6 +269,7 @@ DHCPv4.InitialCongestionWindow, config_parse_tcp_window,
|
||||
DHCPv4.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, offsetof(Network, dhcp_advertised_receive_window)
|
||||
DHCPv4.FallbackLeaseLifetimeSec, config_parse_dhcp_fallback_lease_lifetime, 0, 0
|
||||
DHCPv4.Use6RD, config_parse_bool, 0, offsetof(Network, dhcp_use_6rd)
|
||||
DHCPv4.UnassignedSubnetPolicy, config_parse_dhcp_pd_prefix_route_type, 0, offsetof(Network, dhcp_6rd_prefix_route_type)
|
||||
DHCPv4.IPv6OnlyMode, config_parse_tristate, 0, offsetof(Network, dhcp_ipv6_only_mode)
|
||||
DHCPv4.NetLabel, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_netlabel)
|
||||
DHCPv4.NFTSet, config_parse_nft_set, NFT_SET_PARSE_NETWORK, offsetof(Network, dhcp_nft_set_context)
|
||||
@@ -288,6 +289,7 @@ DHCPv6.UserClass, config_parse_dhcp_user_or_vendor_cl
|
||||
DHCPv6.VendorClass, config_parse_dhcp_user_or_vendor_class, AF_INET6, offsetof(Network, dhcp6_vendor_class)
|
||||
DHCPv6.SendVendorOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_vendor_options)
|
||||
DHCPv6.PrefixDelegationHint, config_parse_dhcp6_pd_prefix_hint, 0, 0
|
||||
DHCPv6.UnassignedSubnetPolicy, config_parse_dhcp_pd_prefix_route_type, 0, offsetof(Network, dhcp6_pd_prefix_route_type)
|
||||
DHCPv6.WithoutRA, config_parse_dhcp6_client_start_mode, 0, offsetof(Network, dhcp6_client_start_mode)
|
||||
DHCPv6.SendOption, config_parse_dhcp_send_option, AF_INET6, offsetof(Network, dhcp6_client_send_options)
|
||||
DHCPv6.IAID, config_parse_iaid, AF_INET6, 0
|
||||
|
||||
@@ -402,6 +402,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
.dhcp_ip_service_type = -1,
|
||||
.dhcp_broadcast = -1,
|
||||
.dhcp_ipv6_only_mode = -1,
|
||||
.dhcp_6rd_prefix_route_type = RTN_UNREACHABLE,
|
||||
|
||||
.dhcp6_use_address = true,
|
||||
.dhcp6_use_pd_prefix = true,
|
||||
@@ -415,6 +416,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
|
||||
.dhcp6_duid.type = _DUID_TYPE_INVALID,
|
||||
.dhcp6_client_start_mode = _DHCP6_CLIENT_START_MODE_INVALID,
|
||||
.dhcp6_send_release = true,
|
||||
.dhcp6_pd_prefix_route_type = RTN_UNREACHABLE,
|
||||
|
||||
.dhcp_pd = -1,
|
||||
.dhcp_pd_announce = true,
|
||||
|
||||
@@ -167,6 +167,7 @@ struct Network {
|
||||
bool dhcp_use_timezone;
|
||||
bool dhcp_use_hostname;
|
||||
bool dhcp_use_6rd;
|
||||
uint8_t dhcp_6rd_prefix_route_type;
|
||||
bool dhcp_send_release;
|
||||
bool dhcp_send_decline;
|
||||
UseDomains dhcp_use_domains;
|
||||
@@ -195,6 +196,7 @@ struct Network {
|
||||
DUID dhcp6_duid;
|
||||
uint8_t dhcp6_pd_prefix_length;
|
||||
struct in6_addr dhcp6_pd_prefix_hint;
|
||||
uint8_t dhcp6_pd_prefix_route_type;
|
||||
char *dhcp6_hostname;
|
||||
char *dhcp6_mudurl;
|
||||
char **dhcp6_user_class;
|
||||
|
||||
@@ -148,7 +148,7 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
|
||||
route_nexthop_free);
|
||||
|
||||
static size_t route_n_nexthops(const Route *route) {
|
||||
if (route->nexthop_id != 0 || route_type_is_reject(route))
|
||||
if (route->nexthop_id != 0 || route_is_reject(route))
|
||||
return 0;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
@@ -246,7 +246,7 @@ int route_nexthops_copy(const Route *src, const RouteNextHop *nh, Route *dest) {
|
||||
assert(src);
|
||||
assert(dest);
|
||||
|
||||
if (src->nexthop_id != 0 || route_type_is_reject(src))
|
||||
if (src->nexthop_id != 0 || route_is_reject(src))
|
||||
return 0;
|
||||
|
||||
if (nh)
|
||||
@@ -292,7 +292,7 @@ bool route_nexthops_needs_adjust(const Route *route) {
|
||||
* Hence, we cannot know if the nexthop is blackhole or not. */
|
||||
return route->type != RTN_BLACKHOLE;
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
if (route_is_reject(route))
|
||||
return false;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
@@ -350,7 +350,7 @@ int route_adjust_nexthops(Route *route, Link *link) {
|
||||
return true; /* updated */
|
||||
}
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
if (route_is_reject(route))
|
||||
return false;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
@@ -439,7 +439,7 @@ int route_nexthops_is_ready_to_configure(const Route *route, Manager *manager) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
if (route_is_reject(route))
|
||||
return true;
|
||||
|
||||
if (ordered_set_isempty(route->nexthops))
|
||||
@@ -468,7 +468,7 @@ int route_nexthops_to_string(const Route *route, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (route_type_is_reject(route)) {
|
||||
if (route_is_reject(route)) {
|
||||
buf = strdup("gw: n/a");
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
@@ -625,7 +625,7 @@ int route_nexthops_set_netlink_message(const Route *route, sd_netlink_message *m
|
||||
if (route->nexthop_id != 0)
|
||||
return sd_netlink_message_append_u32(message, RTA_NH_ID, route->nexthop_id);
|
||||
|
||||
if (route_type_is_reject(route))
|
||||
if (route_is_reject(route))
|
||||
return 0;
|
||||
|
||||
/* We request IPv6 multipath routes separately. Even though, if weight is non-zero, we need to use
|
||||
@@ -750,7 +750,7 @@ int route_nexthops_read_netlink_message(Route *route, sd_netlink_message *messag
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return log_warning_errno(r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
|
||||
|
||||
if (route->nexthop_id != 0 || route_type_is_reject(route))
|
||||
if (route->nexthop_id != 0 || route_is_reject(route))
|
||||
/* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
|
||||
* fib6_nh_init() in net/ipv6/route.c. However, we'd like to make it consistent with IPv4
|
||||
* routes. Hence, skip reading of RTA_OIF. */
|
||||
@@ -899,7 +899,7 @@ int route_section_verify_nexthops(Route *route) {
|
||||
"Ignoring [Route] section from line %u.",
|
||||
route->section->filename, route->section->line);
|
||||
|
||||
if (route_type_is_reject(route) &&
|
||||
if (route_is_reject(route) &&
|
||||
(route->gateway_from_dhcp_or_ra ||
|
||||
in_addr_is_set(route->nexthop.family, &route->nexthop.gw) ||
|
||||
!ordered_set_isempty(route->nexthops)))
|
||||
|
||||
@@ -39,10 +39,12 @@ unsigned routes_max(void) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
bool route_type_is_reject(const Route *route) {
|
||||
assert(route);
|
||||
bool route_type_is_reject(uint8_t type) {
|
||||
return IN_SET(type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
|
||||
}
|
||||
|
||||
return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
|
||||
bool route_is_reject(const Route *route) {
|
||||
return route_type_is_reject(ASSERT_PTR(route)->type);
|
||||
}
|
||||
|
||||
static bool route_lifetime_is_valid(const Route *route) {
|
||||
|
||||
@@ -13,7 +13,8 @@ typedef struct Route Route;
|
||||
|
||||
unsigned routes_max(void);
|
||||
|
||||
bool route_type_is_reject(const Route *route);
|
||||
bool route_type_is_reject(uint8_t type);
|
||||
bool route_is_reject(const Route *route);
|
||||
|
||||
bool link_find_default_gateway(Link *link, int family, Route **gw);
|
||||
static inline bool link_has_default_gateway(Link *link, int family) {
|
||||
|
||||
@@ -912,7 +912,7 @@ int link_request_route(
|
||||
assert(route);
|
||||
assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
|
||||
|
||||
if (route->family == AF_INET || route_type_is_reject(route) || ordered_set_isempty(route->nexthops))
|
||||
if (route->family == AF_INET || route_is_reject(route) || ordered_set_isempty(route->nexthops))
|
||||
return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
|
||||
|
||||
RouteNextHop *nh;
|
||||
|
||||
@@ -11,6 +11,7 @@ DHCPPrefixDelegation=yes
|
||||
[DHCPv6]
|
||||
WithoutRA=solicit
|
||||
UseAddress=no
|
||||
UnassignedSubnetPolicy=none
|
||||
|
||||
[DHCPPrefixDelegation]
|
||||
UplinkInterface=:self
|
||||
|
||||
@@ -10,6 +10,7 @@ DHCPPrefixDelegation=yes
|
||||
|
||||
[DHCPv6]
|
||||
WithoutRA=solicit
|
||||
UnassignedSubnetPolicy=blackhole
|
||||
|
||||
[DHCPPrefixDelegation]
|
||||
UplinkInterface=:self
|
||||
|
||||
@@ -7147,6 +7147,11 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
|
||||
print(output)
|
||||
self.assertNotIn('inet6 3ffe:501:ffff', output)
|
||||
|
||||
print('### ip -6 route show dev lo')
|
||||
output = check_output('ip -6 route show dev lo')
|
||||
print(output)
|
||||
self.assertNotRegex(output, '3ffe:501:ffff:[2-9a-f]00::/56')
|
||||
|
||||
self.check_dhcp6_prefix('veth99')
|
||||
|
||||
@unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available on CentOS Stream 10")
|
||||
@@ -7165,6 +7170,11 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities):
|
||||
print(output)
|
||||
self.assertNotIn('inet6 3ffe:501:ffff', output)
|
||||
|
||||
print('### ip -6 route show type blackhole')
|
||||
output = check_output('ip -6 route show type blackhole')
|
||||
print(output)
|
||||
self.assertRegex(output, 'blackhole 3ffe:501:ffff:[2-9a-f]00::/56 dev lo proto dhcp')
|
||||
|
||||
self.check_dhcp6_prefix('veth99')
|
||||
|
||||
@unittest.skipUnless(shutil.which('dhcpd'), reason="dhcpd is not available on CentOS Stream 10")
|
||||
|
||||
Reference in New Issue
Block a user