mirror of
https://github.com/systemd/systemd.git
synced 2025-03-28 02:50:16 +03:00
network/route: improve Gateway=_dhcp4 handling (#36183)
- Also configures route to the gateway and prefix route in the specified table, if necessary. - Also set preferred source address of the route. Closes #36168.
This commit is contained in:
commit
2ee81b556f
@ -2010,8 +2010,10 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||
<term><varname>Gateway=</varname></term>
|
||||
<listitem>
|
||||
<para>Takes the gateway address or the special values <literal>_dhcp4</literal> and
|
||||
<literal>_ipv6ra</literal>. If <literal>_dhcp4</literal> or <literal>_ipv6ra</literal> is
|
||||
set, then the gateway address provided by DHCPv4 or IPv6 RA is used.</para>
|
||||
<literal>_ipv6ra</literal>. If <literal>_dhcp4</literal> or <literal>_ipv6ra</literal> is set, then
|
||||
the gateway address provided by DHCPv4 or IPv6 RA is used. When<literal>_dhcp4</literal>, the
|
||||
acquired DHCPv4 address will be used as the preferred source address of the route, unless it is
|
||||
explicitly configured in <varname>PreferredSource=</varname>.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v211"/>
|
||||
</listitem>
|
||||
@ -2117,10 +2119,12 @@ NFTSet=prefix:netdev:filter:eth_ipv4_prefix</programlisting>
|
||||
<varlistentry>
|
||||
<term><varname>PreferredSource=</varname></term>
|
||||
<listitem>
|
||||
<para>The preferred source address of the route. The address must be in the format described
|
||||
in
|
||||
<para>The preferred source address of the route. Takes <literal>no</literal> or an address
|
||||
in the format described in
|
||||
<citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
|
||||
</para>
|
||||
If <varname>Gateway=_dhcp4</varname> is specified, defaults to the acquired DHCPv4 address.
|
||||
Otherwise, defaults to unset. The value <literal>no</literal> may be useful to configure a route
|
||||
with <varname>Gateway=_dhcp4</varname> without setting preferred source route address.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v227"/>
|
||||
</listitem>
|
||||
|
@ -405,20 +405,20 @@ static int dhcp4_request_route(Route *route, Link *link) {
|
||||
return link_request_route(link, route, &link->dhcp4_messages, dhcp4_route_handler);
|
||||
}
|
||||
|
||||
static bool link_prefixroute(Link *link) {
|
||||
static bool prefixroute_by_kernel(Link *link) {
|
||||
return !link->network->dhcp_route_table_set ||
|
||||
link->network->dhcp_route_table == RT_TABLE_MAIN;
|
||||
}
|
||||
|
||||
static int dhcp4_request_prefix_route(Link *link) {
|
||||
static int dhcp4_request_prefix_route(Link *link, Route *rt) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->dhcp_lease);
|
||||
|
||||
if (link_prefixroute(link))
|
||||
/* When true, the route will be created by kernel. See dhcp4_update_address(). */
|
||||
if (prefixroute_by_kernel(link) && (!rt || !rt->table_set || rt->table == RT_TABLE_MAIN))
|
||||
/* The prefix route in the main table will be created by the kernel. See dhcp4_update_address(). */
|
||||
return 0;
|
||||
|
||||
r = route_new(&route);
|
||||
@ -426,6 +426,10 @@ static int dhcp4_request_prefix_route(Link *link) {
|
||||
return r;
|
||||
|
||||
route->scope = RT_SCOPE_LINK;
|
||||
if (rt) {
|
||||
route->table_set = rt->table_set;
|
||||
route->table = rt->table;
|
||||
}
|
||||
|
||||
r = sd_dhcp_lease_get_prefix(link->dhcp_lease, &route->dst.in, &route->dst_prefixlen);
|
||||
if (r < 0)
|
||||
@ -438,14 +442,19 @@ static int dhcp4_request_prefix_route(Link *link) {
|
||||
return dhcp4_request_route(route, link);
|
||||
}
|
||||
|
||||
static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw) {
|
||||
static int dhcp4_request_route_to_gateway(Link *link, const Route *rt) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
struct in_addr address;
|
||||
int r;
|
||||
|
||||
assert(link);
|
||||
assert(link->dhcp_lease);
|
||||
assert(gw);
|
||||
assert(rt);
|
||||
|
||||
if (in_addr_is_set(rt->nexthop.family, &rt->nexthop.gw) <= 0)
|
||||
return 0;
|
||||
|
||||
assert(rt->nexthop.family == AF_INET);
|
||||
|
||||
r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
|
||||
if (r < 0)
|
||||
@ -455,10 +464,12 @@ static int dhcp4_request_route_to_gateway(Link *link, const struct in_addr *gw)
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->dst.in = *gw;
|
||||
route->dst.in = rt->nexthop.gw.in;
|
||||
route->dst_prefixlen = 32;
|
||||
route->prefsrc.in = address;
|
||||
route->scope = RT_SCOPE_LINK;
|
||||
route->table = rt->table;
|
||||
route->table_set = rt->table_set;
|
||||
|
||||
return dhcp4_request_route(route, link);
|
||||
}
|
||||
@ -526,14 +537,14 @@ static int dhcp4_request_route_auto(
|
||||
route->prefsrc.in = address;
|
||||
|
||||
} else {
|
||||
r = dhcp4_request_route_to_gateway(link, gw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->scope = RT_SCOPE_UNIVERSE;
|
||||
route->nexthop.family = AF_INET;
|
||||
route->nexthop.gw.in = *gw;
|
||||
route->prefsrc.in = address;
|
||||
|
||||
r = dhcp4_request_route_to_gateway(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return dhcp4_request_route(route, link);
|
||||
@ -613,12 +624,6 @@ static int dhcp4_request_default_gateway(Link *link) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host
|
||||
* so that we can route no matter the netmask or existing kernel route tables. */
|
||||
r = dhcp4_request_route_to_gateway(link, &router);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_new(&route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -628,6 +633,12 @@ static int dhcp4_request_default_gateway(Link *link) {
|
||||
route->nexthop.gw.in = router;
|
||||
route->prefsrc.in = address;
|
||||
|
||||
/* The dhcp netmask may mask out the gateway. First, add an explicit route for the gateway host
|
||||
* so that we can route no matter the netmask or existing kernel route tables. */
|
||||
r = dhcp4_request_route_to_gateway(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return dhcp4_request_route(route, link);
|
||||
}
|
||||
|
||||
@ -643,13 +654,11 @@ static int dhcp4_request_semi_static_routes(Link *link) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
struct in_addr gw;
|
||||
|
||||
if (!rt->gateway_from_dhcp_or_ra)
|
||||
continue;
|
||||
|
||||
if (rt->nexthop.family != AF_INET)
|
||||
if (rt->source != NETWORK_CONFIG_SOURCE_DHCP4)
|
||||
continue;
|
||||
|
||||
assert(rt->family == AF_INET);
|
||||
assert(rt->nexthop.family == AF_INET);
|
||||
|
||||
r = dhcp4_find_gateway_for_destination(link, &rt->dst.in, rt->dst_prefixlen, &gw);
|
||||
if (r == -EHOSTUNREACH) {
|
||||
@ -666,16 +675,26 @@ static int dhcp4_request_semi_static_routes(Link *link) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = dhcp4_request_route_to_gateway(link, &gw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = route_dup(rt, NULL, &route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
route->nexthop.gw.in = gw;
|
||||
|
||||
if (!route->prefsrc_set) {
|
||||
r = sd_dhcp_lease_get_address(link->dhcp_lease, &route->prefsrc.in);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dhcp4_request_prefix_route(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp4_request_route_to_gateway(link, route);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dhcp4_request_route(route, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -775,7 +794,7 @@ static int dhcp4_request_routes(Link *link) {
|
||||
assert(link);
|
||||
assert(link->dhcp_lease);
|
||||
|
||||
r = dhcp4_request_prefix_route(link);
|
||||
r = dhcp4_request_prefix_route(link, /* rt = */ NULL);
|
||||
if (r < 0)
|
||||
return log_link_error_errno(link, r, "DHCP error: Could not request prefix route: %m");
|
||||
|
||||
@ -965,7 +984,7 @@ static int dhcp4_request_address(Link *link, bool announce) {
|
||||
r = sd_dhcp_lease_get_broadcast(link->dhcp_lease, &addr->broadcast);
|
||||
if (r < 0 && r != -ENODATA)
|
||||
return log_link_warning_errno(link, r, "DHCP: failed to get broadcast address: %m");
|
||||
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link));
|
||||
SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !prefixroute_by_kernel(link));
|
||||
addr->route_metric = link->network->dhcp_route_metric;
|
||||
addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO;
|
||||
|
||||
|
@ -1083,11 +1083,10 @@ static int ndisc_router_drop_default(Link *link, sd_ndisc_router *rt) {
|
||||
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
|
||||
_cleanup_(route_unrefp) Route *tmp = NULL;
|
||||
|
||||
if (!route_gw->gateway_from_dhcp_or_ra)
|
||||
if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
||||
if (route_gw->nexthop.family != AF_INET6)
|
||||
continue;
|
||||
assert(route_gw->nexthop.family == AF_INET6);
|
||||
|
||||
r = route_dup(route_gw, NULL, &tmp);
|
||||
if (r < 0)
|
||||
@ -1158,11 +1157,10 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
||||
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
|
||||
_cleanup_(route_unrefp) Route *route = NULL;
|
||||
|
||||
if (!route_gw->gateway_from_dhcp_or_ra)
|
||||
if (route_gw->source != NETWORK_CONFIG_SOURCE_NDISC)
|
||||
continue;
|
||||
|
||||
if (route_gw->nexthop.family != AF_INET6)
|
||||
continue;
|
||||
assert(route_gw->nexthop.family == AF_INET6);
|
||||
|
||||
r = route_dup(route_gw, NULL, &route);
|
||||
if (r < 0)
|
||||
|
@ -844,11 +844,24 @@ int route_section_verify_nexthops(Route *route) {
|
||||
return log_route_section(route, "Invalid route family.");
|
||||
}
|
||||
|
||||
if (route->nexthop.family == AF_INET && !FLAGS_SET(route->network->dhcp, ADDRESS_FAMILY_IPV4))
|
||||
return log_route_section(route, "Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled.");
|
||||
switch (route->nexthop.family) {
|
||||
case AF_INET:
|
||||
if (!FLAGS_SET(route->network->dhcp, ADDRESS_FAMILY_IPV4))
|
||||
return log_route_section(route, "Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled.");
|
||||
|
||||
if (route->nexthop.family == AF_INET6 && route->network->ndisc == 0)
|
||||
return log_route_section(route, "Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled.");
|
||||
route->source = NETWORK_CONFIG_SOURCE_DHCP4;
|
||||
break;
|
||||
|
||||
case AF_INET6:
|
||||
if (route->network->ndisc == 0)
|
||||
return log_route_section(route, "Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled.");
|
||||
|
||||
route->source = NETWORK_CONFIG_SOURCE_NDISC;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
/* When only Gateway= is specified, assume the route family based on the Gateway address. */
|
||||
|
@ -1065,7 +1065,7 @@ int link_request_static_routes(Link *link, bool only_ipv4) {
|
||||
link->static_routes_configured = false;
|
||||
|
||||
HASHMAP_FOREACH(route, link->network->routes_by_section) {
|
||||
if (route->gateway_from_dhcp_or_ra)
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
|
||||
if (only_ipv4 && route->family != AF_INET)
|
||||
@ -1519,6 +1519,9 @@ int link_drop_routes(Link *link, bool only_static) {
|
||||
continue;
|
||||
|
||||
HASHMAP_FOREACH(route, other->network->routes_by_section) {
|
||||
if (route->source != NETWORK_CONFIG_SOURCE_STATIC)
|
||||
continue;
|
||||
|
||||
if (route->family == AF_INET || ordered_set_isempty(route->nexthops)) {
|
||||
r = link_unmark_route(other, route, NULL);
|
||||
if (r < 0)
|
||||
@ -1660,7 +1663,19 @@ static int config_parse_preferred_src(
|
||||
Route *route = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(rvalue);
|
||||
if (isempty(rvalue)) {
|
||||
route->prefsrc_set = false;
|
||||
route->prefsrc = IN_ADDR_NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r == 0) {
|
||||
/* Accepts only no. That prohibits prefsrc set by DHCP lease. */
|
||||
route->prefsrc_set = true;
|
||||
route->prefsrc = IN_ADDR_NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (route->family == AF_UNSPEC)
|
||||
r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc);
|
||||
@ -1669,6 +1684,7 @@ static int config_parse_preferred_src(
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
|
||||
route->prefsrc_set = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,7 @@ struct Route {
|
||||
bool expiration_managed_by_kernel:1; /* RTA_CACHEINFO has nonzero rta_expires */
|
||||
|
||||
/* Only used by conf persers and route_section_verify(). */
|
||||
bool prefsrc_set:1;
|
||||
bool scope_set:1;
|
||||
bool table_set:1;
|
||||
bool priority_set:1;
|
||||
|
@ -43,5 +43,9 @@ Destination=192.168.7.0/24
|
||||
|
||||
[Route]
|
||||
Gateway=_dhcp4
|
||||
Destination=10.0.0.0/8
|
||||
Table=211
|
||||
Destination=192.0.2.0/24
|
||||
|
||||
[Route]
|
||||
Gateway=_dhcp4
|
||||
Destination=198.51.100.0/24
|
||||
Table=212
|
||||
|
@ -7263,7 +7263,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
||||
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
|
||||
self.assertRegex(output, f'192.168.5.6 proto dhcp scope link src {address1} metric 24')
|
||||
self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address1} metric 24')
|
||||
self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
|
||||
self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address1}')
|
||||
|
||||
print('## ip route show table 212 dev veth99')
|
||||
output = check_output('ip route show table 212 dev veth99')
|
||||
print(output)
|
||||
self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address1} metric 24')
|
||||
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 24')
|
||||
self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address1}')
|
||||
|
||||
print('## link state file')
|
||||
output = read_link_state_file('veth99')
|
||||
@ -7363,7 +7370,14 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
||||
self.assertNotIn('192.168.5.6', output)
|
||||
self.assertRegex(output, f'192.168.5.7 proto dhcp scope link src {address2} metric 24')
|
||||
self.assertRegex(output, f'192.168.5.8 proto dhcp scope link src {address2} metric 24')
|
||||
self.assertIn('10.0.0.0/8 via 192.168.5.1 proto dhcp', output)
|
||||
self.assertRegex(output, f'192.0.2.0/24 via 192.168.5.1 proto dhcp src {address2}')
|
||||
|
||||
print('## ip route show table 212 dev veth99')
|
||||
output = check_output('ip route show table 212 dev veth99')
|
||||
print(output)
|
||||
self.assertRegex(output, f'192.168.5.0/24 proto dhcp scope link src {address2} metric 24')
|
||||
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 24')
|
||||
self.assertRegex(output, f'198.51.100.0/24 via 192.168.5.1 proto dhcp src {address2}')
|
||||
|
||||
print('## link state file')
|
||||
output = read_link_state_file('veth99')
|
||||
@ -7434,6 +7448,11 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
|
||||
print(output)
|
||||
self.assertNotIn(f'{address2}', output)
|
||||
|
||||
print('## ip route show table 212 dev veth99')
|
||||
output = check_output('ip route show table 212 dev veth99')
|
||||
print(output)
|
||||
self.assertNotIn(f'{address2}', output)
|
||||
|
||||
self.teardown_nftset('addr4', 'network4', 'ifindex')
|
||||
|
||||
def test_dhcp_client_ipv4_dbus_status(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user