Merge pull request #30916 from yuwata/network-route-section-verify
network: several cleanups for verifying [Route] section
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user