diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 57c3923c1b..0195dce1bf 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -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); } } diff --git a/src/network/networkd-route-nexthop.c b/src/network/networkd-route-nexthop.c index f1d81f2014..a8fddeb6c0 100644 --- a/src/network/networkd-route-nexthop.c +++ b/src/network/networkd-route-nexthop.c @@ -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, diff --git a/src/network/networkd-route-nexthop.h b/src/network/networkd-route-nexthop.h index df5265c508..fb93574986 100644 --- a/src/network/networkd-route-nexthop.h +++ b/src/network/networkd-route-nexthop.h @@ -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); diff --git a/src/network/networkd-route-util.c b/src/network/networkd-route-util.c index d49a0b9512..00c267e692 100644 --- a/src/network/networkd-route-util.c +++ b/src/network/networkd-route-util.c @@ -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); diff --git a/src/network/networkd-route-util.h b/src/network/networkd-route-util.h index f326888c93..eba823a2d0 100644 --- a/src/network/networkd-route-util.h +++ b/src/network/networkd-route-util.h @@ -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); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e5d1baf945..dfbbefdbd6 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -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); } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 140980ca05..5c041953ab 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -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); diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index dfa74c203d..1610321053 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -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)