hashmap_remove(nexthop->manager->nexthops_by_id, UINT32_TO_PTR(nexthop->id));
}
+ hashmap_free_free(nexthop->group);
+
return mfree(nexthop);
}
return nexthop_compare_func(a, b) == 0;
}
-static void nexthop_copy(NextHop *dest, const NextHop *src) {
- assert(dest);
+static int nexthop_dup(const NextHop *src, NextHop **ret) {
+ _cleanup_(nexthop_freep) NextHop *dest = NULL;
+ struct nexthop_grp *nhg;
+ int r;
+
assert(src);
+ assert(ret);
+
+ dest = newdup(NextHop, src, 1);
+ if (!dest)
+ return -ENOMEM;
+
+ /* unset all pointers */
+ dest->manager = NULL;
+ dest->link = NULL;
+ dest->network = NULL;
+ dest->section = NULL;
+ dest->group = NULL;
- /* This only copies entries used in the above hash and compare functions. */
+ HASHMAP_FOREACH(nhg, src->group) {
+ _cleanup_free_ struct nexthop_grp *g = NULL;
- dest->protocol = src->protocol;
- dest->id = src->id;
- dest->blackhole = src->blackhole;
- dest->family = src->family;
- dest->gw = src->gw;
+ g = newdup(struct nexthop_grp, nhg, 1);
+ if (!g)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(&dest->group, NULL, UINT32_TO_PTR(g->id), g);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ TAKE_PTR(g);
+ }
+
+ *ret = TAKE_PTR(dest);
+ return 0;
}
int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret) {
assert(nexthops);
assert(in);
- r = nexthop_new(&nexthop);
+ r = nexthop_dup(in, &nexthop);
if (r < 0)
return r;
- nexthop_copy(nexthop, in);
-
r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop);
if (r < 0)
return r;
return nexthop_add_internal(manager, link, link ? &link->nexthops_foreign : &manager->nexthops_foreign, in, ret);
}
+static bool nexthop_has_link(const NextHop *nexthop) {
+ return !nexthop->blackhole && hashmap_isempty(nexthop->group);
+}
+
static int nexthop_add(Link *link, const NextHop *in, NextHop **ret) {
+ bool by_manager;
NextHop *nexthop;
int r;
assert(link);
assert(in);
- if (in->blackhole)
+ by_manager = !nexthop_has_link(in);
+
+ if (by_manager)
r = nexthop_get(link->manager, NULL, in, &nexthop);
else
r = nexthop_get(NULL, link, in, &nexthop);
if (r == -ENOENT) {
/* NextHop does not exist, create a new one */
r = nexthop_add_internal(link->manager,
- in->blackhole ? NULL : link,
- in->blackhole ? &link->manager->nexthops : &link->nexthops,
+ by_manager ? NULL : link,
+ by_manager ? &link->manager->nexthops : &link->nexthops,
in, &nexthop);
if (r < 0)
return r;
} else if (r == 0) {
/* Take over a foreign nexthop */
- r = set_ensure_put(in->blackhole ? &link->manager->nexthops : &link->nexthops,
+ r = set_ensure_put(by_manager ? &link->manager->nexthops : &link->nexthops,
&nexthop_hash_ops, nexthop);
if (r < 0)
return r;
- set_remove(in->blackhole ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop);
+ set_remove(by_manager ? link->manager->nexthops_foreign : link->nexthops_foreign, nexthop);
} else if (r == 1) {
/* NextHop exists, do nothing */
;
}
static void log_nexthop_debug(const NextHop *nexthop, uint32_t id, const char *str, const Link *link) {
- _cleanup_free_ char *gw = NULL;
+ _cleanup_free_ char *gw = NULL, *new_id = NULL, *group = NULL;
+ struct nexthop_grp *nhg;
assert(nexthop);
assert(str);
if (!DEBUG_LOGGING)
return;
+ if (nexthop->id != id)
+ (void) asprintf(&new_id, "→%"PRIu32, id);
+
(void) in_addr_to_string(nexthop->family, &nexthop->gw, &gw);
- if (nexthop->id == id)
- log_link_debug(link, "%s nexthop: id: %"PRIu32", gw: %s, blackhole: %s",
- str, nexthop->id, strna(gw), yes_no(nexthop->blackhole));
- else
- log_link_debug(link, "%s nexthop: id: %"PRIu32"→%"PRIu32", gw: %s, blackhole: %s",
- str, nexthop->id, id, strna(gw), yes_no(nexthop->blackhole));
+ HASHMAP_FOREACH(nhg, nexthop->group)
+ (void) strextendf_with_separator(&group, ",", "%"PRIu32":%"PRIu32, nhg->id, nhg->weight+1);
+
+ log_link_debug(link, "%s nexthop: id: %"PRIu32"%s, gw: %s, blackhole: %s, group: %s",
+ str, nexthop->id, strempty(new_id), strna(gw), yes_no(nexthop->blackhole), strna(group));
}
static int link_nexthop_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
assert(link->manager);
assert(link->manager->rtnl);
assert(link->ifindex > 0);
- assert(IN_SET(nexthop->family, AF_INET, AF_INET6));
+ assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6));
assert(callback);
log_nexthop_debug(nexthop, nexthop->id, "Configuring", link);
return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m");
}
- if (nexthop->blackhole) {
+ if (!hashmap_isempty(nexthop->group)) {
+ _cleanup_free_ struct nexthop_grp *group = NULL;
+ struct nexthop_grp *p, *nhg;
+
+ group = new(struct nexthop_grp, hashmap_size(nexthop->group));
+ if (!group)
+ return log_oom();
+
+ p = group;
+ HASHMAP_FOREACH(nhg, nexthop->group)
+ *p++ = *nhg;
+
+ r = sd_netlink_message_append_data(req, NHA_GROUP, group, sizeof(struct nexthop_grp) * hashmap_size(nexthop->group));
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append NHA_GROUP attribute: %m");
+
+ } else if (nexthop->blackhole) {
r = sd_netlink_message_append_flag(req, NHA_BLACKHOLE);
if (r < 0)
return log_link_error_errno(link, r, "Could not append NHA_BLACKHOLE attribute: %m");
}
static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) {
+ struct nexthop_grp *nhg;
+
assert(link);
assert(nexthop);
if (!link_is_ready_to_configure(link, false))
return false;
- if (nexthop->blackhole) {
+ if (!nexthop_has_link(nexthop)) {
if (link->manager->nexthop_remove_messages > 0)
return false;
} else {
}
}
+ /* All group members must be configured first. */
+ HASHMAP_FOREACH(nhg, nexthop->group)
+ if (manager_get_nexthop_by_id(link->manager, nhg->id, NULL) < 0)
+ return false;
+
if (nexthop->id == 0) {
Request *req;
int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
_cleanup_(nexthop_freep) NextHop *tmp = NULL;
+ _cleanup_free_ void *raw_group = NULL;
NextHop *nexthop = NULL;
+ size_t raw_group_size;
uint32_t ifindex;
uint16_t type;
Link *link = NULL;
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: could not get nexthop family, ignoring: %m");
return 0;
- } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+ } else if (!IN_SET(tmp->family, AF_UNSPEC, AF_INET, AF_INET6)) {
log_link_debug(link, "rtnl: received nexthop message with invalid family %d, ignoring.", tmp->family);
return 0;
}
return 0;
}
- r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
+ r = sd_netlink_message_read_data(message, NHA_GROUP, &raw_group_size, &raw_group);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: could not get NHA_GROUP attribute, ignoring: %m");
return 0;
+ } else if (r >= 0) {
+ struct nexthop_grp *group = raw_group;
+ size_t n_group;
+
+ if (raw_group_size == 0 || raw_group_size % sizeof(struct nexthop_grp) != 0) {
+ log_link_warning(link, "rtnl: received nexthop message with invalid nexthop group size, ignoring.");
+ return 0;
+ }
+
+ assert((uintptr_t) group % __alignof__(struct nexthop_grp) == 0);
+
+ n_group = raw_group_size / sizeof(struct nexthop_grp);
+ for (size_t i = 0; i < n_group; i++) {
+ _cleanup_free_ struct nexthop_grp *nhg = NULL;
+
+ if (group[i].id == 0) {
+ log_link_warning(link, "rtnl: received nexthop message with invalid ID in group, ignoring.");
+ return 0;
+ }
+ if (group[i].weight > 254) {
+ log_link_warning(link, "rtnl: received nexthop message with invalid weight in group, ignoring.");
+ return 0;
+ }
+
+ nhg = newdup(struct nexthop_grp, group + i, 1);
+ if (!nhg)
+ return log_oom();
+
+ r = hashmap_ensure_put(&tmp->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_link_warning_errno(link, r, "Failed to store nexthop group, ignoring: %m");
+ return 0;
+ }
+ if (r > 0)
+ TAKE_PTR(nhg);
+ }
+ }
+
+ if (tmp->family != AF_UNSPEC) {
+ r = netlink_message_read_in_addr_union(message, NHA_GATEWAY, tmp->family, &tmp->gw);
+ if (r < 0 && r != -ENODATA) {
+ log_link_warning_errno(link, r, "rtnl: could not get NHA_GATEWAY attribute, ignoring: %m");
+ return 0;
+ }
}
r = sd_netlink_message_has_flag(message, NHA_BLACKHOLE);
return 0;
}
- /* All blackhole nexthops are managed by Manager. Note that the linux kernel does not set
- * NHA_OID attribute when NHA_BLACKHOLE is set. Just for safety. */
- if (tmp->blackhole)
+ /* All blackhole or group nexthops are managed by Manager. Note that the linux kernel does not
+ * set NHA_OID attribute when NHA_BLACKHOLE or NHA_GROUP is set. Just for safety. */
+ if (!nexthop_has_link(tmp))
link = NULL;
r = nexthop_get(m, link, tmp, &nexthop);
if (section_is_invalid(nh->section))
return -EINVAL;
- if (nh->family == AF_UNSPEC)
- /* When no Gateway= is specified, assume IPv4. */
+ if (!hashmap_isempty(nh->group)) {
+ if (in_addr_is_set(nh->family, &nh->gw))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: nexthop group cannot have gateway address. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+
+ if (nh->family != AF_UNSPEC)
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: nexthop group cannot have Family= setting. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+
+ if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
+ return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+ "%s: nexthop group cannot be a blackhole. "
+ "Ignoring [NextHop] section from line %u.",
+ nh->section->filename, nh->section->line);
+ } else if (nh->family == AF_UNSPEC)
+ /* When neither Family=, Gateway=, nor Group= is specified, assume IPv4. */
nh->family = AF_INET;
if (nh->blackhole && in_addr_is_set(nh->family, &nh->gw))
TAKE_PTR(n);
return 0;
}
+
+int config_parse_nexthop_group(
+ 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) {
+
+ _cleanup_(nexthop_free_or_set_invalidp) NextHop *n = NULL;
+ Network *network = userdata;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = nexthop_new_static(network, filename, section_line, &n);
+ if (r < 0)
+ return log_oom();
+
+ if (isempty(rvalue)) {
+ n->group = hashmap_free_free(n->group);
+ TAKE_PTR(n);
+ return 0;
+ }
+
+ for (const char *p = rvalue;;) {
+ _cleanup_free_ struct nexthop_grp *nhg = NULL;
+ _cleanup_free_ char *word = NULL;
+ uint32_t w;
+ char *sep;
+
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Invalid %s=, ignoring assignment: %s", lvalue, rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ nhg = new0(struct nexthop_grp, 1);
+ if (!nhg)
+ return log_oom();
+
+ sep = strchr(word, ':');
+ if (sep) {
+ *sep++ = '\0';
+ r = safe_atou32(sep, &w);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse weight for nexthop group, ignoring assignment: %s:%s",
+ word, sep);
+ continue;
+ }
+ if (w == 0 || w > 256) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Invalid weight for nexthop group, ignoring assignment: %s:%s",
+ word, sep);
+ continue;
+ }
+ /* See comments in config_parse_multipath_route(). */
+ nhg->weight = w - 1;
+ }
+
+ r = safe_atou32(word, &nhg->id);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Failed to parse nexthop ID in %s=, ignoring assignment: %s%s%s",
+ lvalue, word, sep ? ":" : "", strempty(sep));
+ continue;
+ }
+ if (nhg->id == 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Nexthop ID in %s= must be positive, ignoring assignment: %s%s%s",
+ lvalue, word, sep ? ":" : "", strempty(sep));
+ continue;
+ }
+
+ r = hashmap_ensure_put(&n->group, NULL, UINT32_TO_PTR(nhg->id), nhg);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r == -EEXIST) {
+ log_syntax(unit, LOG_WARNING, filename, line, r,
+ "Nexthop ID %"PRIu32" is specified multiple times in %s=, ignoring assignment: %s%s%s",
+ nhg->id, lvalue, word, sep ? ":" : "", strempty(sep));
+ continue;
+ }
+ assert(r > 0);
+ TAKE_PTR(nhg);
+ }
+
+ TAKE_PTR(n);
+ return 0;
+}
#include <linux/icmpv6.h>
#include <linux/ipv6_route.h>
+#include <linux/nexthop.h>
#include "alloc-util.h"
#include "netlink-util.h"
return -ENOENT;
}
-static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh) {
+static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight) {
assert(dest);
assert(src);
dest->nexthop_id = src->nexthop_id;
if (nh) {
+ assert(hashmap_isempty(nh->group));
+
dest->gw_family = nh->family;
dest->gw = nh->gw;
- dest->gw_weight = src->gw_weight;
+ dest->gw_weight = nh_weight != UINT8_MAX ? nh_weight : src->gw_weight;
} else if (m) {
dest->gw_family = m->gateway.family;
dest->gw = m->gateway.address;
if (r < 0)
return r;
- route_copy(route, in, NULL, NULL);
+ route_copy(route, in, NULL, NULL, UINT8_MAX);
r = set_ensure_put(routes, &route_hash_ops, route);
if (r < 0)
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, const NextHop *nh, Route **ret) {
+static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
_cleanup_(route_freep) Route *tmp = NULL;
Route *route;
int r;
assert(in);
if (nh) {
+ assert(hashmap_isempty(nh->group));
+
r = route_new(&tmp);
if (r < 0)
return r;
- route_copy(tmp, in, NULL, nh);
+ route_copy(tmp, in, NULL, nh, nh_weight);
in = tmp;
} else if (m) {
assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
if (r < 0)
return r;
- route_copy(tmp, in, m, NULL);
+ route_copy(tmp, in, m, NULL, UINT8_MAX);
in = tmp;
}
return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
}
+static int link_has_route_one(Link *link, const Route *route, const NextHop *nh, uint8_t nh_weight) {
+ _cleanup_(route_freep) Route *tmp = NULL;
+ int r;
+
+ assert(link);
+ assert(route);
+ assert(nh);
+
+ r = route_new(&tmp);
+ if (r < 0)
+ return r;
+
+ route_copy(tmp, route, NULL, nh, nh_weight);
+
+ if (route_type_is_reject(route) || (nh && nh->blackhole))
+ return route_get(link->manager, NULL, tmp, NULL) >= 0;
+ else
+ return route_get(NULL, link, tmp, NULL) >= 0;
+}
+
int link_has_route(Link *link, const Route *route) {
MultipathRoute *m;
int r;
assert(route);
if (route->nexthop_id > 0) {
- _cleanup_(route_freep) Route *tmp = NULL;
+ struct nexthop_grp *nhg;
NextHop *nh;
if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
return false;
- r = route_new(&tmp);
- if (r < 0)
- return r;
+ if (hashmap_isempty(nh->group))
+ return link_has_route_one(link, route, nh, UINT8_MAX);
- route_copy(tmp, route, NULL, nh);
+ HASHMAP_FOREACH(nhg, nh->group) {
+ NextHop *h;
- if (route_type_is_reject(route) || (nh && nh->blackhole))
- return route_get(link->manager, NULL, tmp, NULL) >= 0;
- else
- return route_get(NULL, link, tmp, NULL) >= 0;
+ if (manager_get_nexthop_by_id(link->manager, nhg->id, &h) < 0)
+ return false;
+
+ r = link_has_route_one(link, route, h, nhg->weight);
+ if (r <= 0)
+ return r;
+ }
+
+ return true;
}
if (ordered_set_isempty(route->multipath_routes)) {
if (r < 0)
return r;
- route_copy(tmp, route, m, NULL);
+ route_copy(tmp, route, m, NULL, UINT8_MAX);
if (route_get(NULL, l, tmp, NULL) < 0)
return false;
continue;
if (link_has_static_route(link, route))
- k = route_add(NULL, link, route, NULL, NULL, NULL);
+ k = route_add(NULL, link, route, NULL, NULL, UINT8_MAX, NULL);
else
k = route_remove(route, NULL, link);
if (k < 0 && r >= 0)
return 1;
}
-static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
+static int route_add_and_setup_timer_one(Link *link, const Route *route, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
_cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
- NextHop *nh = NULL;
Route *nr;
int r;
assert(link);
assert(link->manager);
assert(route);
-
- (void) manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
+ assert(!(m && nh));
+ assert(ret);
if (route_type_is_reject(route) || (nh && nh->blackhole))
- r = route_add(link->manager, NULL, route, NULL, nh, &nr);
- else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
- r = route_add(NULL, link, route, m, nh, &nr);
- else {
+ r = route_add(link->manager, NULL, route, NULL, nh, nh_weight, &nr);
+ else if (nh) {
+ assert(nh->link);
+ assert(hashmap_isempty(nh->group));
+
+ r = route_add(NULL, nh->link, route, NULL, nh, nh_weight, &nr);
+ } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) {
Link *link_gw;
r = link_get(link->manager, m->ifindex, &link_gw);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
- r = route_add(NULL, link_gw, route, m, NULL, &nr);
- }
+ r = route_add(NULL, link_gw, route, m, NULL, UINT8_MAX, &nr);
+ } else
+ r = route_add(NULL, link, route, m, NULL, UINT8_MAX, &nr);
if (r < 0)
return log_link_error_errno(link, r, "Could not add route: %m");
sd_event_source_unref(nr->expire);
nr->expire = TAKE_PTR(expire);
- if (ret)
- *ret = nr;
+ *ret = nr;
+ return 0;
+}
+
+static int route_add_and_setup_timer(Link *link, const Route *route, unsigned *ret_n_routes, Route ***ret_routes) {
+ _cleanup_free_ Route **routes = NULL;
+ unsigned n_routes;
+ NextHop *nh;
+ Route **p;
+ int r;
+
+ assert(link);
+ assert(route);
+ assert(ret_n_routes);
+ assert(ret_routes);
+
+ if (route->nexthop_id > 0) {
+ r = manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not get nexthop by ID %"PRIu32": %m", route->nexthop_id);
+ } else
+ nh = NULL;
+
+ if (nh && !hashmap_isempty(nh->group)) {
+ struct nexthop_grp *nhg;
+
+ n_routes = hashmap_size(nh->group);
+ p = routes = new(Route*, n_routes);
+ if (!routes)
+ return log_oom();
+ HASHMAP_FOREACH(nhg, nh->group) {
+ NextHop *h;
+
+ r = manager_get_nexthop_by_id(link->manager, nhg->id, &h);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not get nexthop group member by ID %"PRIu32": %m", nhg->id);
+
+ /* The nexthop h may be a blackhole nexthop. In that case, h->link is NULL. */
+ r = route_add_and_setup_timer_one(h->link ?: link, route, NULL, h, nhg->weight, p++);
+ if (r < 0)
+ return r;
+ }
+ } else if (!ordered_set_isempty(route->multipath_routes)) {
+ MultipathRoute *m;
+
+ assert(!nh);
+ assert(!in_addr_is_set(route->gw_family, &route->gw));
+
+ n_routes = ordered_set_size(route->multipath_routes);
+ p = routes = new(Route*, n_routes);
+ if (!routes)
+ return log_oom();
+
+ ORDERED_SET_FOREACH(m, route->multipath_routes) {
+ r = route_add_and_setup_timer_one(link, route, m, NULL, UINT8_MAX, p++);
+ if (r < 0)
+ return r;
+ }
+ } else {
+ n_routes = 1;
+ routes = new(Route*, n_routes);
+ if (!routes)
+ return log_oom();
+
+ r = route_add_and_setup_timer_one(link, route, NULL, nh, UINT8_MAX, routes);
+ if (r < 0)
+ return r;
+ }
+
+ *ret_n_routes = n_routes;
+ *ret_routes = TAKE_PTR(routes);
return 0;
}
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
- if (route->nexthop_id != 0 ||
- in_addr_is_set(route->gw_family, &route->gw) ||
- ordered_set_isempty(route->multipath_routes)) {
-
- if (ret_routes) {
- n_routes = 1;
- routes = new(Route*, n_routes);
- if (!routes)
- return log_oom();
- }
-
- r = route_add_and_setup_timer(link, route, NULL, routes);
- if (r < 0)
- return r;
- } else {
- MultipathRoute *m;
- Route **p;
+ if (!ordered_set_isempty(route->multipath_routes)) {
+ assert(route->nexthop_id == 0);
+ assert(!in_addr_is_set(route->gw_family, &route->gw));
r = append_nexthops(route, req);
if (r < 0)
return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
-
- if (ret_routes) {
- n_routes = ordered_set_size(route->multipath_routes);
- routes = new(Route*, n_routes);
- if (!routes)
- return log_oom();
- }
-
- p = routes;
- ORDERED_SET_FOREACH(m, route->multipath_routes) {
- r = route_add_and_setup_timer(link, route, m, ret_routes ? p++ : NULL);
- if (r < 0)
- return r;
- }
}
+ r = route_add_and_setup_timer(link, route, &n_routes, &routes);
+ if (r < 0)
+ return r;
+
r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
link_netlink_destroy_callback, link);
if (r < 0)
*ret_routes = TAKE_PTR(routes);
}
- return 0;
+ return r;
}
static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
assert(route);
assert(link);
- if (route->nexthop_id > 0 &&
- manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
- return false;
+ if (route->nexthop_id > 0) {
+ struct nexthop_grp *nhg;
+
+ if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
+ return false;
+
+ HASHMAP_FOREACH(nhg, nh->group)
+ if (manager_get_nexthop_by_id(link->manager, nhg->id, NULL) < 0)
+ return false;
+ }
if (route_type_is_reject(route) || (nh && nh->blackhole)) {
if (nh && link->manager->nexthop_remove_messages > 0)
(void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
- if (nh) {
- if (link && link != nh->link)
+ if (nh && hashmap_isempty(nh->group)) {
+ if (link && nh->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;
+ if (nh->link)
+ link = nh->link;
r = route_new(&nr);
if (r < 0)
return log_oom();
- route_copy(nr, tmp, NULL, nh);
+ route_copy(nr, tmp, NULL, nh, UINT8_MAX);
tmp = nr;
} else if (m) {
if (r < 0)
return log_oom();
- route_copy(nr, tmp, m, NULL);
+ route_copy(nr, tmp, m, NULL, UINT8_MAX);
tmp = nr;
}