Merge pull request #30916 from yuwata/network-route-section-verify

network: several cleanups for verifying [Route] section
This commit is contained in:
Yu Watanabe
2024-01-14 04:57:05 +09:00
committed by GitHub
8 changed files with 185 additions and 124 deletions
+18 -5
View File
@@ -1196,20 +1196,33 @@ static int wireguard_verify(NetDev *netdev, const char *filename) {
if (r < 0)
return log_oom();
/* For route_section_verify() below. */
r = config_section_new(peer->section->filename, peer->section->line, &route->section);
if (r < 0)
return log_oom();
route->source = NETWORK_CONFIG_SOURCE_STATIC;
route->family = ipmask->family;
route->dst = ipmask->ip;
route->dst_prefixlen = ipmask->cidr;
route->scope = RT_SCOPE_UNIVERSE;
route->protocol = RTPROT_STATIC;
route->protocol_set = true;
route->table = peer->route_table_set ? peer->route_table : w->route_table;
route->table_set = true;
route->priority = peer->route_priority_set ? peer->route_priority : w->route_priority;
if (route->priority == 0 && route->family == AF_INET6)
route->priority = IP6_RT_PRIO_USER;
route->source = NETWORK_CONFIG_SOURCE_STATIC;
route->priority_set = true;
r = set_ensure_consume(&w->routes, &route_hash_ops, TAKE_PTR(route));
if (route_section_verify(route) < 0)
continue;
r = set_ensure_put(&w->routes, &route_hash_ops, route);
if (r < 0)
return log_oom();
if (r == 0)
continue;
route->wireguard = w;
TAKE_PTR(route);
}
}
+118
View File
@@ -5,11 +5,129 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "netlink-util.h"
#include "networkd-network.h"
#include "networkd-route.h"
#include "networkd-route-nexthop.h"
#include "networkd-route-util.h"
#include "parse-util.h"
#include "string-util.h"
int route_section_verify_nexthops(Route *route) {
assert(route);
assert(route->section);
if (route->gateway_from_dhcp_or_ra) {
assert(route->network);
if (route->gw_family == AF_UNSPEC)
/* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
switch (route->family) {
case AF_UNSPEC:
log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
"Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
route->section->filename, route->section->line);
route->gw_family = route->family = AF_INET;
break;
case AF_INET:
case AF_INET6:
log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
"Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
route->gw_family = route->family;
break;
default:
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Invalid route family. Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
if (route->gw_family == AF_INET && !FLAGS_SET(route->network->dhcp, ADDRESS_FAMILY_IPV4))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->gw_family == AF_INET6 && !route->network->ipv6_accept_ra)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
/* When only Gateway= is specified, assume the route family based on the Gateway address. */
if (route->family == AF_UNSPEC)
route->family = route->gw_family;
if (route->family == AF_UNSPEC) {
assert(route->section);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Route section without Gateway=, Destination=, Source=, "
"or PreferredSource= field configured. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
if (route->gateway_onlink < 0 && in_addr_is_set(route->gw_family, &route->gw) &&
route->network && ordered_hashmap_isempty(route->network->addresses_by_section)) {
/* If no address is configured, in most cases the gateway cannot be reachable.
* TODO: we may need to improve the condition above. */
log_warning("%s: Gateway= without static address configured. "
"Enabling GatewayOnLink= option.",
route->section->filename);
route->gateway_onlink = true;
}
if (route->gateway_onlink >= 0)
SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
if (route->family == AF_INET6) {
if (route->gw_family == AF_INET)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: IPv4 gateway is configured for IPv6 route. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
MultipathRoute *m;
ORDERED_SET_FOREACH(m, route->multipath_routes)
if (m->gateway.family == AF_INET)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: IPv4 multipath route is specified for IPv6 route. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
if (route->nexthop_id != 0 &&
(route->gateway_from_dhcp_or_ra ||
in_addr_is_set(route->gw_family, &route->gw) ||
!ordered_set_isempty(route->multipath_routes)))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route_type_is_reject(route) &&
(route->gateway_from_dhcp_or_ra ||
in_addr_is_set(route->gw_family, &route->gw) ||
!ordered_set_isempty(route->multipath_routes)))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: reject type route cannot be specified with Gateway= or MultiPathRoute=. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if ((route->gateway_from_dhcp_or_ra ||
in_addr_is_set(route->gw_family, &route->gw)) &&
!ordered_set_isempty(route->multipath_routes))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway= cannot be specified with MultiPathRoute=. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
return 0;
}
int config_parse_gateway(
const char *unit,
const char *filename,
+4
View File
@@ -3,6 +3,10 @@
#include "conf-parser.h"
typedef struct Route Route;
int route_section_verify_nexthops(Route *route);
CONFIG_PARSER_PROTOTYPE(config_parse_gateway);
CONFIG_PARSER_PROTOTYPE(config_parse_route_gateway_onlink);
CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop);
+6
View File
@@ -39,6 +39,12 @@ unsigned routes_max(void) {
return cached;
}
bool route_type_is_reject(const Route *route) {
assert(route);
return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
}
static bool route_lifetime_is_valid(const Route *route) {
assert(route);
+2
View File
@@ -13,6 +13,8 @@ typedef struct Route Route;
unsigned routes_max(void);
bool route_type_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) {
return link_find_default_gateway(link, family, NULL);
+32 -118
View File
@@ -100,6 +100,9 @@ Route *route_free(Route *route) {
if (route->manager)
set_remove(route->manager->routes, route);
if (route->wireguard)
set_remove(route->wireguard->routes, route);
ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
route_metric_done(&route->metric);
sd_event_source_disable_unref(route->expire);
@@ -235,18 +238,6 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
route_compare_func,
route_free);
static bool route_type_is_reject(const Route *route) {
assert(route);
return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
}
static bool route_needs_convert(const Route *route) {
assert(route);
return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
}
static int route_add(Manager *manager, Link *link, Route *route) {
int r;
@@ -317,6 +308,7 @@ int route_dup(const Route *src, Route **ret) {
/* Unset all pointers */
dest->network = NULL;
dest->wireguard = NULL;
dest->section = NULL;
dest->link = NULL;
dest->manager = NULL;
@@ -427,6 +419,12 @@ static int converted_routes_new(size_t n, ConvertedRoutes **ret) {
return 0;
}
static bool route_needs_convert(const Route *route) {
assert(route);
return route->nexthop_id > 0 || !ordered_set_isempty(route->multipath_routes);
}
static int route_convert(Manager *manager, const Route *route, ConvertedRoutes **ret) {
_cleanup_(converted_routes_freep) ConvertedRoutes *c = NULL;
int r;
@@ -2242,79 +2240,33 @@ int config_parse_route_type(
return 0;
}
static int route_section_verify(Route *route, Network *network) {
int route_section_verify(Route *route) {
int r;
assert(route);
assert(route->section);
if (section_is_invalid(route->section))
return -EINVAL;
/* Currently, we do not support static route with finite lifetime. */
assert(route->lifetime_usec == USEC_INFINITY);
if (route->gateway_from_dhcp_or_ra) {
if (route->gw_family == AF_UNSPEC) {
/* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
switch (route->family) {
case AF_UNSPEC:
log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
"Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
route->section->filename, route->section->line);
route->family = AF_INET;
break;
case AF_INET:
case AF_INET6:
log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
"Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
break;
default:
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Invalid route family. Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
route->gw_family = route->family;
}
r = route_section_verify_nexthops(route);
if (r < 0)
return r;
if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
/* When only Gateway= is specified, assume the route family based on the Gateway address. */
if (route->family == AF_UNSPEC)
route->family = route->gw_family;
if (route->family == AF_UNSPEC) {
assert(route->section);
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Route section without Gateway=, Destination=, Source=, "
"or PreferredSource= field configured. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
}
if (route->family == AF_INET6 && route->gw_family == AF_INET)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: IPv4 gateway is configured for IPv6 route. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (!route->table_set && network->vrf) {
route->table = VRF(network->vrf)->table;
/* table */
if (!route->table_set && route->network && route->network->vrf) {
route->table = VRF(route->network->vrf)->table;
route->table_set = true;
}
if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
route->table = RT_TABLE_LOCAL;
if (!route->scope_set && route->family != AF_INET6) {
/* scope */
if (!route->scope_set && route->family == AF_INET) {
if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
route->scope = RT_SCOPE_HOST;
else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
@@ -2327,55 +2279,17 @@ static int route_section_verify(Route *route, Network *network) {
route->scope = RT_SCOPE_LINK;
}
if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
route->scope = RT_SCOPE_UNIVERSE;
}
if (route->family == AF_INET6 && route->priority == 0)
route->priority = IP6_RT_PRIO_USER;
if (route->gateway_onlink < 0 && in_addr_is_set(route->gw_family, &route->gw) &&
ordered_hashmap_isempty(network->addresses_by_section)) {
/* If no address is configured, in most cases the gateway cannot be reachable.
* TODO: we may need to improve the condition above. */
log_warning("%s: Gateway= without static address configured. "
"Enabling GatewayOnLink= option.",
network->filename);
route->gateway_onlink = true;
}
if (route->gateway_onlink >= 0)
SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
/* IPv6 route */
if (route->family == AF_INET6) {
MultipathRoute *m;
if (route->scope != RT_SCOPE_UNIVERSE) {
log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
route->scope = RT_SCOPE_UNIVERSE;
}
ORDERED_SET_FOREACH(m, route->multipath_routes)
if (m->gateway.family == AF_INET)
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: IPv4 multipath route is specified for IPv6 route. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->priority == 0)
route->priority = IP6_RT_PRIO_USER;
}
if ((route->gateway_from_dhcp_or_ra ||
in_addr_is_set(route->gw_family, &route->gw)) &&
!ordered_set_isempty(route->multipath_routes))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: Gateway= cannot be specified with MultiPathRoute=. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
if (route->nexthop_id > 0 &&
(route->gateway_from_dhcp_or_ra ||
in_addr_is_set(route->gw_family, &route->gw) ||
!ordered_set_isempty(route->multipath_routes)))
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
"Ignoring [Route] section from line %u.",
route->section->filename, route->section->line);
return 0;
}
@@ -2385,6 +2299,6 @@ void network_drop_invalid_routes(Network *network) {
assert(network);
HASHMAP_FOREACH(route, network->routes_by_section)
if (route_section_verify(route, network) < 0)
if (route_section_verify(route) < 0)
route_free(route);
}
+4
View File
@@ -17,6 +17,8 @@ typedef struct Manager Manager;
typedef struct Network Network;
typedef struct Request Request;
typedef struct Route Route;
typedef struct Wireguard Wireguard;
typedef int (*route_netlink_handler_t)(
sd_netlink *rtnl,
sd_netlink_message *m,
@@ -28,6 +30,7 @@ struct Route {
Link *link;
Manager *manager;
Network *network;
Wireguard *wireguard;
ConfigSection *section;
NetworkConfigSource source;
NetworkConfigState state;
@@ -108,6 +111,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma
int network_add_ipv4ll_route(Network *network);
int network_add_default_route_on_device(Network *network);
void network_drop_invalid_routes(Network *network);
int route_section_verify(Route *route);
DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Route, route);
void link_mark_routes(Link *link, NetworkConfigSource source);
+1 -1
View File
@@ -1823,7 +1823,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
output = check_output('ip -4 route show dev wg99 table 1234')
print(output)
self.assertIn('192.168.26.0/24 proto static metric 123', output)
self.assertIn('192.168.26.0/24 proto static scope link metric 123', output)
output = check_output('ip -6 route show dev wg99 table 1234')
print(output)