if (r < 0)
return r;
- if (route->pref_set) {
- ndisc_set_route_priority(link, route);
- return route_remove_and_cancel(route, link->manager);
- }
-
- uint8_t pref;
+ uint8_t pref, pref_original = route->pref;
FOREACH_ARGUMENT(pref, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_MEDIUM, SD_NDISC_PREFERENCE_HIGH) {
+ Route *existing;
+ Request *req;
+
+ /* If the preference is specified by the user config (that is, for semi-static routes),
+ * rather than RA, then only search conflicting routes that have the same preference. */
+ if (route->pref_set && pref != pref_original)
+ continue;
+
route->pref = pref;
ndisc_set_route_priority(link, route);
- r = route_remove_and_cancel(route, link->manager);
- if (r < 0)
- return r;
+
+ /* Unfortunately, we cannot directly pass 'route' to route_remove_and_cancel() here, as the
+ * same or similar route may be configured or requested statically. */
+
+ /* First, check if the route is already requested. If there is an existing route, and also an
+ * existing pending request, then the source may be updated by the request. So, we first need
+ * to check the source of the requested route. */
+ if (route_get_request(link->manager, route, &req) >= 0) {
+ existing = ASSERT_PTR(req->userdata);
+ if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
+ continue;
+
+ r = route_remove_and_cancel(existing, link->manager);
+ if (r < 0)
+ return r;
+ }
+
+ /* Then, check if the route exists. */
+ if (route_get(link->manager, route, &existing) >= 0) {
+ if (existing->source == NETWORK_CONFIG_SOURCE_STATIC)
+ continue;
+
+ r = route_remove_and_cancel(existing, link->manager);
+ if (r < 0)
+ return r;
+ }
}
return 0;