return 0;
}
+static bool multipath_routes_needs_adjust(const Route *route) {
+ assert(route);
+
+ RouteNextHop *nh;
+ ORDERED_SET_FOREACH(nh, route->nexthops)
+ if (route->nexthop.ifindex == 0)
+ return true;
+
+ return false;
+}
+
+bool route_nexthops_needs_adjust(const Route *route) {
+ assert(route);
+
+ if (route->nexthop_id != 0)
+ /* At this stage, the nexthop may not be configured, or may be under reconfiguring.
+ * Hence, we cannot know if the nexthop is blackhole or not. */
+ return route->type != RTN_BLACKHOLE;
+
+ if (route_type_is_reject(route))
+ return false;
+
+ if (ordered_set_isempty(route->nexthops))
+ return route->nexthop.ifindex == 0;
+
+ return multipath_routes_needs_adjust(route);
+}
+
+static bool route_nexthop_set_ifindex(RouteNextHop *nh, Link *link) {
+ assert(nh);
+ assert(link);
+ assert(link->manager);
+
+ if (nh->ifindex > 0) {
+ nh->ifname = mfree(nh->ifname);
+ return false;
+ }
+
+ /* If an interface name is specified, use it. Otherwise, use the interface that requests this route. */
+ if (nh->ifname && link_get_by_name(link->manager, nh->ifname, &link) < 0)
+ return false;
+
+ nh->ifindex = link->ifindex;
+ nh->ifname = mfree(nh->ifname);
+ return true; /* updated */
+}
+
+int route_adjust_nexthops(Route *route, Link *link) {
+ int r;
+
+ assert(route);
+ assert(link);
+ assert(link->manager);
+
+ /* When an IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel sends
+ * RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type fib_rt_info::type
+ * may not be blackhole. Thus, we cannot know the internal value. Moreover, on route removal, the
+ * matching is done with the hidden value if we set non-zero type in RTM_DELROUTE message. So,
+ * here let's set route type to BLACKHOLE when the nexthop is blackhole. */
+ if (route->nexthop_id != 0) {
+ NextHop *nexthop;
+
+ r = nexthop_is_ready(link->manager, route->nexthop_id, &nexthop);
+ if (r <= 0)
+ return r; /* r == 0 means the nexthop is under (re-)configuring.
+ * We cannot use the currently remembered information. */
+
+ if (!nexthop->blackhole)
+ return false;
+
+ if (route->type == RTN_BLACKHOLE)
+ return false;
+
+ route->type = RTN_BLACKHOLE;
+ return true; /* updated */
+ }
+
+ if (route_type_is_reject(route))
+ return false;
+
+ if (ordered_set_isempty(route->nexthops))
+ return route_nexthop_set_ifindex(&route->nexthop, link);
+
+ if (!multipath_routes_needs_adjust(route))
+ return false;
+
+ _cleanup_ordered_set_free_ OrderedSet *nexthops = NULL;
+ for (;;) {
+ _cleanup_(route_nexthop_freep) RouteNextHop *nh = NULL;
+
+ nh = ordered_set_steal_first(route->nexthops);
+ if (!nh)
+ break;
+
+ (void) route_nexthop_set_ifindex(nh, link);
+
+ r = ordered_set_ensure_put(&nexthops, &route_nexthop_hash_ops, nh);
+ if (r == -EEXIST)
+ continue; /* Duplicated? Let's drop the nexthop. */
+ if (r < 0)
+ return r;
+ assert(r > 0);
+
+ TAKE_PTR(nh);
+ }
+
+ ordered_set_free(route->nexthops);
+ route->nexthops = TAKE_PTR(nexthops);
+ return true; /* updated */
+}
+
int route_nexthop_get_link(Manager *manager, Link *link, const RouteNextHop *nh, Link **ret) {
assert(manager);
assert(nh);