From: Yu Watanabe Date: Tue, 9 Feb 2021 09:52:57 +0000 (+0900) Subject: network: add NextHop= setting in [Route] section X-Git-Tag: v248-rc1~31^2~2 X-Git-Url: http://git-history.diyao.me/?a=commitdiff_plain;h=324e3422193fcf85f31866bf8960caba1f045bc2;p=systemd%2F.git network: add NextHop= setting in [Route] section --- diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 610799724b..dfc1dd37ff 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1554,6 +1554,13 @@ IPv6Token=prefixstable:2002:da8:1:: times. If an empty string is assigned, then the all previous assignments are cleared. + + NextHop= + + Specifies the nexthop id. Takes an unsigned integer in the range 1…4294967295. + If set, the corresponding [NextHop] section must be configured. Defaults to unset. + + diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index e7e51e2f19..e74a44b2a6 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -185,6 +185,7 @@ Route.QuickAck, config_parse_route_boolean, Route.FastOpenNoCookie, config_parse_route_boolean, 0, 0 Route.TTLPropagate, config_parse_route_boolean, 0, 0 Route.MultiPathRoute, config_parse_multipath_route, 0, 0 +Route.NextHop, config_parse_route_nexthop, 0, 0 NextHop.Id, config_parse_nexthop_id, 0, 0 NextHop.Gateway, config_parse_nexthop_gateway, 0, 0 NextHop.Family, config_parse_nexthop_family, 0, 0 diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index 9f94b64107..be41f1e9d6 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -132,6 +132,23 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR( nexthop_compare_func, nexthop_free); +int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) { + NextHop *nh; + + assert(manager); + + if (id == 0) + return -EINVAL; + + nh = hashmap_get(manager->nexthops_by_id, UINT32_TO_PTR(id)); + if (!nh) + return -ENOENT; + + if (ret) + *ret = nh; + return 0; +} + static int nexthop_get(Link *link, const NextHop *in, NextHop **ret) { NextHop *existing; diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 80af1e1fee..06673d4369 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -35,6 +35,7 @@ void network_drop_invalid_nexthops(Network *network); int link_set_nexthops(Link *link); +int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret); int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); CONFIG_PARSER_PROTOTYPE(config_parse_nexthop_id); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index bf0e35b7da..09ad846403 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -328,6 +328,7 @@ void route_hash_func(const Route *route, struct siphash *state) { siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state); siphash24_compress(&route->advmss, sizeof(route->advmss), state); + siphash24_compress(&route->nexthop_id, sizeof(route->nexthop_id), state); break; default: @@ -416,6 +417,10 @@ int route_compare_func(const Route *a, const Route *b) { if (r != 0) return r; + r = CMP(a->nexthop_id, b->nexthop_id); + if (r != 0) + return r; + return 0; default: /* treat any other address family as AF_UNSPEC */ @@ -479,7 +484,7 @@ static int route_get(const Manager *manager, const Link *link, const Route *in, return -ENOENT; } -static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) { +static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh) { assert(dest); assert(src); @@ -498,9 +503,14 @@ static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) { dest->initcwnd = src->initcwnd; dest->initrwnd = src->initrwnd; dest->lifetime = src->lifetime; - dest->advmss= src->advmss; + dest->advmss = src->advmss; + dest->nexthop_id = src->nexthop_id; - if (m) { + if (nh) { + dest->gw_family = nh->family; + dest->gw = nh->gw; + dest->gw_weight = src->gw_weight; + } else if (m) { dest->gw_family = m->gateway.family; dest->gw = m->gateway.address; dest->gw_weight = m->weight; @@ -523,7 +533,7 @@ static int route_add_internal(Manager *manager, Link *link, Set **routes, const if (r < 0) return r; - route_copy(route, in, NULL); + route_copy(route, in, NULL, NULL); r = set_ensure_put(routes, &route_hash_ops, route); if (r < 0) @@ -547,7 +557,7 @@ static int route_add_foreign(Manager *manager, Link *link, const Route *in, Rout return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret); } -static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) { +static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, Route **ret) { _cleanup_(route_freep) Route *tmp = NULL; bool is_new = false; Route *route; @@ -556,14 +566,21 @@ static int route_add(Manager *manager, Link *link, const Route *in, const Multip assert(manager || link); assert(in); - if (m) { + if (nh) { + r = route_new(&tmp); + if (r < 0) + return r; + + route_copy(tmp, in, NULL, nh); + in = tmp; + } else if (m) { assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex)); r = route_new(&tmp); if (r < 0) return r; - route_copy(tmp, in, m); + route_copy(tmp, in, m, NULL); in = tmp; } @@ -722,7 +739,7 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req if (r < 0) return log_link_error_errno(link, r, "Could not set route type: %m"); - if (!route_type_is_reject(route)) { + if (!route_type_is_reject(route) && route->nexthop_id == 0) { assert(link); /* Those routes must be attached to a specific link */ r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); @@ -730,6 +747,12 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m"); } + if (route->nexthop_id > 0) { + r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id); + if (r < 0) + return log_link_error_errno(link, r, "Could not append RTA_NH_ID attribute: %m"); + } + r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); if (r < 0) return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m"); @@ -893,7 +916,7 @@ int link_drop_foreign_routes(Link *link) { continue; if (link_has_route(link, route)) - k = route_add(NULL, link, route, NULL, NULL); + k = route_add(NULL, link, route, NULL, NULL, NULL); else k = route_remove(route, NULL, link, NULL); if (k < 0 && r >= 0) @@ -947,16 +970,20 @@ static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdat static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) { _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL; + NextHop *nh = NULL; Route *nr; int r, k; assert(link); + assert(link->manager); assert(route); + (void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh); + if (route_type_is_reject(route)) - k = route_add(link->manager, NULL, route, NULL, &nr); + k = route_add(link->manager, NULL, route, NULL, NULL, &nr); else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex) - k = route_add(NULL, link, route, m, &nr); + k = route_add(NULL, link, route, m, nh, &nr); else { Link *link_gw; @@ -964,7 +991,7 @@ static int route_add_and_setup_timer(Link *link, const Route *route, const Multi if (r < 0) return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex); - k = route_add(NULL, link_gw, route, m, &nr); + k = route_add(NULL, link_gw, route, m, NULL, &nr); } if (k < 0) return log_link_error_errno(link, k, "Could not add route: %m"); @@ -1258,6 +1285,9 @@ static bool route_has_gateway(const Route *route) { if (!ordered_set_isempty(route->multipath_routes)) return true; + if (route->nexthop_id > 0) + return true; + return false; } @@ -1356,13 +1386,30 @@ int link_set_routes(Link *link) { static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) { _cleanup_(route_freep) Route *nr = NULL; Route *route = NULL; + NextHop *nh = NULL; int r; assert(manager); assert(tmp); assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)); - if (m) { + (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh); + + if (nh) { + if (link && link != nh->link) + return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL), + "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring."); + + link = nh->link; + + r = route_new(&nr); + if (r < 0) + return log_oom(); + + route_copy(nr, tmp, NULL, nh); + + tmp = nr; + } else if (m) { if (link) return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL), "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring."); @@ -1381,7 +1428,7 @@ static int process_route_one(Manager *manager, Link *link, uint16_t type, const if (r < 0) return log_oom(); - route_copy(nr, tmp, m); + route_copy(nr, tmp, m, NULL); tmp = nr; } @@ -1573,6 +1620,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Ma return 0; } + r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id); + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m"); + return 0; + } + r = sd_netlink_message_enter_container(message, RTA_METRICS); if (r < 0 && r != -ENODATA) { log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m"); @@ -1966,6 +2019,59 @@ int config_parse_route_scope( return 0; } +int config_parse_route_nexthop( + 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) { + + Network *network = userdata; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + uint32_t id; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, filename, section_line, &n); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to allocate route, ignoring assignment: %m"); + return 0; + } + + if (isempty(rvalue)) { + n->nexthop_id = 0; + TAKE_PTR(n); + return 0; + } + + r = safe_atou32(rvalue, &id); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue); + return 0; + } + if (id == 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue); + return 0; + } + + n->nexthop_id = id; + TAKE_PTR(n); + return 0; +} + int config_parse_route_table( const char *unit, const char *filename, @@ -2656,6 +2762,14 @@ static int route_section_verify(Route *route, Network *network) { route->section->filename, route->section->line); } + if (route->nexthop_id > 0 && + (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; } diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 92ef00e31f..331f1f9ea2 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -44,6 +44,7 @@ typedef struct Route { unsigned char pref; unsigned flags; int gateway_onlink; + uint32_t nexthop_id; bool scope_set:1; bool table_set:1; @@ -105,3 +106,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu); CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route); CONFIG_PARSER_PROTOTYPE(config_parse_tcp_advmss); CONFIG_PARSER_PROTOTYPE(config_parse_route_table_names); +CONFIG_PARSER_PROTOTYPE(config_parse_route_nexthop); diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index d20fb78f15..90420f42b5 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -166,6 +166,7 @@ Metric= TTLPropagate= MultiPathRoute= TCPAdvertisedMaximumSegmentSize= +NextHop= [Network] IPv6DuplicateAddressDetection= IPMasquerade=