]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
network/route: introduce route_remove_and_cancel()
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
f579559b 2
b5bf6f64 3#include <linux/icmpv6.h>
8973df5c 4#include <linux/ipv6_route.h>
228c3e21 5#include <linux/nexthop.h>
b5bf6f64 6
b5efdb8a 7#include "alloc-util.h"
0b0c81bb 8#include "event-util.h"
fc2f9534 9#include "netlink-util.h"
3b6a3bde 10#include "networkd-address.h"
ca5ad760 11#include "networkd-ipv4ll.h"
23f53b99 12#include "networkd-manager.h"
e2263711 13#include "networkd-network.h"
141318f7 14#include "networkd-nexthop.h"
76c5a0f2 15#include "networkd-queue.h"
344b3cff 16#include "networkd-route-util.h"
6bedfcbb 17#include "networkd-route.h"
6bedfcbb 18#include "parse-util.h"
07630cea 19#include "string-util.h"
d96edb2c 20#include "strv.h"
c0d48bc5 21#include "vrf.h"
e9084344 22#include "wireguard.h"
f579559b 23
1bcd7cd0 24Route* route_free(Route *route) {
f579559b 25 if (!route)
169948e9 26 return NULL;
f579559b 27
f048a16b 28 if (route->network) {
2a54a044
YW
29 assert(route->section);
30 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 31 }
6ae115c1 32
3b6a3bde 33 if (route->manager)
ad208fac 34 set_remove(route->manager->routes, route);
ad208fac 35
4db8ccbb
YW
36 if (route->wireguard)
37 set_remove(route->wireguard->routes, route);
38
1bcd7cd0 39 config_section_free(route->section);
fea879cd 40 route_nexthops_done(route);
d32a520b 41 route_metric_done(&route->metric);
d105befc 42 sd_event_source_disable_unref(route->expire);
f833694d 43
169948e9 44 return mfree(route);
f579559b
TG
45}
46
09d09207 47static void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
48 assert(route);
49
c01a5c05 50 siphash24_compress_typesafe(route->family, state);
bb7ae737 51
01aaa3df
YW
52 switch (route->family) {
53 case AF_INET:
47420573
YW
54 /* First, the table, destination prefix, priority, and tos (dscp), are used to find routes.
55 * See fib_table_insert(), fib_find_node(), and fib_find_alias() in net/ipv4/fib_trie.c of the kernel. */
56 siphash24_compress_typesafe(route->table, state);
c01a5c05 57 in_addr_hash_func(&route->dst, route->family, state);
47420573 58 siphash24_compress_typesafe(route->dst_prefixlen, state);
c01a5c05 59 siphash24_compress_typesafe(route->priority, state);
47420573
YW
60 siphash24_compress_typesafe(route->tos, state);
61
62 /* Then, protocol, scope, type, flags, prefsrc, metrics (RTAX_* attributes), and nexthops (gateways)
63 * are used to find routes. See fib_find_info() in net/ipv4/fib_semantics.c of the kernel. */
c01a5c05
YW
64 siphash24_compress_typesafe(route->protocol, state);
65 siphash24_compress_typesafe(route->scope, state);
66 siphash24_compress_typesafe(route->type, state);
47420573
YW
67 unsigned flags = route->flags & ~RTNH_COMPARE_MASK;
68 siphash24_compress_typesafe(flags, state);
69 in_addr_hash_func(&route->prefsrc, route->family, state);
70
71 /* nexthops (id, number of nexthops, nexthop) */
72 route_nexthops_hash_func(route, state);
73
74 /* metrics */
d32a520b 75 route_metric_hash_func(&route->metric, state);
47420573
YW
76 break;
77
78 case AF_INET6:
79 /* First, table and destination prefix are used for classifying routes.
80 * See fib6_add() and fib6_add_1() in net/ipv6/ip6_fib.c of the kernel. */
81 siphash24_compress_typesafe(route->table, state);
82 in_addr_hash_func(&route->dst, route->family, state);
83 siphash24_compress_typesafe(route->dst_prefixlen, state);
84
85 /* Then, source prefix is used. See fib6_add(). */
86 in_addr_hash_func(&route->src, route->family, state);
87 siphash24_compress_typesafe(route->src_prefixlen, state);
88
89 /* See fib6_add_rt2node(). */
90 siphash24_compress_typesafe(route->priority, state);
007cac09 91
47420573
YW
92 /* See rt6_duplicate_nexthop() in include/net/ip6_route.h of the kernel.
93 * Here, we hash nexthop in a similar way as the one for IPv4. */
94 route_nexthops_hash_func(route, state);
95
96 /* Unlike IPv4 routes, metrics are not taken into account. */
01aaa3df 97 break;
47420573 98
01aaa3df
YW
99 default:
100 /* treat any other address family as AF_UNSPEC */
101 break;
102 }
103}
104
09d09207 105static int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
106 int r;
107
108 r = CMP(a->family, b->family);
109 if (r != 0)
110 return r;
111
112 switch (a->family) {
113 case AF_INET:
47420573 114 r = CMP(a->table, b->table);
01aaa3df
YW
115 if (r != 0)
116 return r;
117
67e05dd8
ZJS
118 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
119 if (r != 0)
120 return r;
121
47420573 122 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
01aaa3df
YW
123 if (r != 0)
124 return r;
125
47420573 126 r = CMP(a->priority, b->priority);
01aaa3df
YW
127 if (r != 0)
128 return r;
129
47420573 130 r = CMP(a->tos, b->tos);
01aaa3df
YW
131 if (r != 0)
132 return r;
133
47420573
YW
134 r = CMP(a->protocol, b->protocol);
135 if (r != 0)
136 return r;
40075951 137
47420573
YW
138 r = CMP(a->scope, b->scope);
139 if (r != 0)
140 return r;
6dd53981 141
47420573 142 r = CMP(a->type, b->type);
01aaa3df
YW
143 if (r != 0)
144 return r;
145
47420573 146 r = CMP(a->flags & ~RTNH_COMPARE_MASK, b->flags & ~RTNH_COMPARE_MASK);
01aaa3df
YW
147 if (r != 0)
148 return r;
149
47420573
YW
150 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
151 if (r != 0)
152 return r;
153
154 r = route_nexthops_compare_func(a, b);
01aaa3df
YW
155 if (r != 0)
156 return r;
157
47420573
YW
158 return route_metric_compare_func(&a->metric, &b->metric);
159
160 case AF_INET6:
67e05dd8 161 r = CMP(a->table, b->table);
01aaa3df
YW
162 if (r != 0)
163 return r;
164
47420573 165 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
fa3e401a
YW
166 if (r != 0)
167 return r;
168
47420573 169 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
fa3e401a
YW
170 if (r != 0)
171 return r;
172
47420573 173 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
174 if (r != 0)
175 return r;
176
47420573 177 r = CMP(a->src_prefixlen, b->src_prefixlen);
007cac09
SS
178 if (r != 0)
179 return r;
180
47420573 181 r = CMP(a->priority, b->priority);
324e3422
YW
182 if (r != 0)
183 return r;
184
47420573
YW
185 return route_nexthops_compare_func(a, b);
186
01aaa3df
YW
187 default:
188 /* treat any other address family as AF_UNSPEC */
189 return 0;
190 }
191}
192
28870a9d 193DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 194 route_hash_ops,
01aaa3df 195 Route,
c077a205
YW
196 route_hash_func,
197 route_compare_func,
01aaa3df
YW
198 route_free);
199
1bcd7cd0
YW
200int route_new(Route **ret) {
201 _cleanup_(route_freep) Route *route = NULL;
202
203 route = new(Route, 1);
204 if (!route)
205 return -ENOMEM;
206
207 *route = (Route) {
208 .family = AF_UNSPEC,
209 .scope = RT_SCOPE_UNIVERSE,
210 .protocol = RTPROT_UNSPEC,
211 .type = RTN_UNICAST,
212 .table = RT_TABLE_MAIN,
213 .lifetime_usec = USEC_INFINITY,
214 .gateway_onlink = -1,
215 };
216
217 *ret = TAKE_PTR(route);
218
219 return 0;
220}
221
222int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
223 _cleanup_(config_section_freep) ConfigSection *n = NULL;
224 _cleanup_(route_freep) Route *route = NULL;
225 int r;
226
227 assert(network);
228 assert(ret);
229 assert(filename);
230 assert(section_line > 0);
231
232 r = config_section_new(filename, section_line, &n);
233 if (r < 0)
234 return r;
235
236 route = hashmap_get(network->routes_by_section, n);
237 if (route) {
238 *ret = TAKE_PTR(route);
239 return 0;
240 }
241
242 if (hashmap_size(network->routes_by_section) >= routes_max())
243 return -E2BIG;
244
245 r = route_new(&route);
246 if (r < 0)
247 return r;
248
249 route->protocol = RTPROT_STATIC;
250 route->network = network;
251 route->section = TAKE_PTR(n);
252 route->source = NETWORK_CONFIG_SOURCE_STATIC;
253
254 r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route);
255 if (r < 0)
256 return r;
257
258 *ret = TAKE_PTR(route);
259 return 0;
260}
261
5a18697d 262static int route_add(Manager *manager, Route *route) {
3b6a3bde 263 int r;
1b566071 264
5a18697d 265 assert(manager);
3b6a3bde 266 assert(route);
5a18697d
YW
267 assert(!route->network);
268 assert(!route->wireguard);
1c8e710c 269
5a18697d
YW
270 r = set_ensure_put(&manager->routes, &route_hash_ops, route);
271 if (r < 0)
272 return r;
273 if (r == 0)
274 return -EEXIST;
275
276 route->manager = manager;
3b6a3bde 277 return 0;
1c8e710c
TG
278}
279
8d01e44c 280int route_get(Manager *manager, const Route *route, Route **ret) {
5a18697d 281 Route *existing;
3b6a3bde 282
8d01e44c 283 assert(manager);
5a18697d 284 assert(route);
3b6a3bde 285
8d01e44c 286 existing = set_get(manager->routes, route);
5a18697d 287 if (!existing)
3b6a3bde
YW
288 return -ENOENT;
289
290 if (ret)
5a18697d 291 *ret = existing;
3b6a3bde
YW
292
293 return 0;
f9bb3338 294}
889b550f 295
3518ff4a
YW
296static int route_get_link(Manager *manager, const Route *route, Link **ret) {
297 int r;
298
299 assert(manager);
300 assert(route);
301
302 if (route->nexthop_id != 0) {
303 NextHop *nh;
304
305 r = nexthop_get_by_id(manager, route->nexthop_id, &nh);
306 if (r < 0)
307 return r;
308
309 return link_get_by_index(manager, nh->ifindex, ret);
310 }
311
10b08b67 312 return route_nexthop_get_link(manager, &route->nexthop, ret);
3518ff4a
YW
313}
314
5a18697d 315static int route_get_request(Manager *manager, const Route *route, Request **ret) {
3babbdf6
YW
316 Request *req;
317
5a18697d 318 assert(manager);
3babbdf6
YW
319 assert(route);
320
5a18697d 321 req = ordered_set_get(manager->request_queue,
3babbdf6 322 &(const Request) {
3babbdf6
YW
323 .type = REQUEST_TYPE_ROUTE,
324 .userdata = (void*) route,
325 .hash_func = (hash_func_t) route_hash_func,
326 .compare_func = (compare_func_t) route_compare_func,
327 });
328 if (!req)
329 return -ENOENT;
330
331 if (ret)
332 *ret = req;
333 return 0;
334}
335
413ea20a 336int route_dup(const Route *src, const RouteNextHop *nh, Route **ret) {
c0bd9eb1 337 _cleanup_(route_freep) Route *dest = NULL;
dc7c21f0 338 int r;
3b6a3bde 339
c0bd9eb1
YW
340 assert(src);
341 assert(ret);
342
343 dest = newdup(Route, src, 1);
344 if (!dest)
345 return -ENOMEM;
346
347 /* Unset all pointers */
1bcd7cd0 348 dest->manager = NULL;
c0bd9eb1 349 dest->network = NULL;
4db8ccbb 350 dest->wireguard = NULL;
c0bd9eb1 351 dest->section = NULL;
413ea20a 352 dest->nexthop = ROUTE_NEXTHOP_NULL;
fea879cd 353 dest->nexthops = NULL;
d32a520b 354 dest->metric = ROUTE_METRIC_NULL;
c0bd9eb1 355 dest->expire = NULL;
dc7c21f0 356
413ea20a
YW
357 r = route_nexthops_copy(src, nh, dest);
358 if (r < 0)
359 return r;
360
d32a520b 361 r = route_metric_copy(&src->metric, &dest->metric);
dc7c21f0
YW
362 if (r < 0)
363 return r;
c0bd9eb1 364
3b6a3bde
YW
365 *ret = TAKE_PTR(dest);
366 return 0;
367}
c0bd9eb1 368
3518ff4a 369static void log_route_debug(const Route *route, const char *str, Manager *manager) {
f47d38c6 370 _cleanup_free_ char *state = NULL, *nexthop = NULL, *prefsrc = NULL,
b07d8145 371 *table = NULL, *scope = NULL, *proto = NULL, *flags = NULL;
f47d38c6 372 const char *dst, *src;
3518ff4a 373 Link *link = NULL;
7653a9dc 374
167a5561
YW
375 assert(route);
376 assert(str);
b19afdfe 377 assert(manager);
167a5561 378
7653a9dc
YW
379 if (!DEBUG_LOGGING)
380 return;
381
3518ff4a
YW
382 (void) route_get_link(manager, route, &link);
383
3b6a3bde 384 (void) network_config_state_to_string_alloc(route->state, &state);
c71384a9
ZJS
385
386 dst = in_addr_is_set(route->family, &route->dst) || route->dst_prefixlen > 0 ?
387 IN_ADDR_PREFIX_TO_STRING(route->family, &route->dst, route->dst_prefixlen) : NULL;
388 src = in_addr_is_set(route->family, &route->src) || route->src_prefixlen > 0 ?
389 IN_ADDR_PREFIX_TO_STRING(route->family, &route->src, route->src_prefixlen) : NULL;
390
f47d38c6
YW
391 (void) route_nexthops_to_string(route, &nexthop);
392
7653a9dc
YW
393 if (in_addr_is_set(route->family, &route->prefsrc))
394 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
395 (void) route_scope_to_string_alloc(route->scope, &scope);
f4defbdc 396 (void) manager_get_route_table_to_string(manager, route->table, /* append_num = */ true, &table);
7653a9dc 397 (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
b07d8145 398 (void) route_flags_to_string_alloc(route->flags, &flags);
7653a9dc
YW
399
400 log_link_debug(link,
f47d38c6
YW
401 "%s %s route (%s): dst: %s, src: %s, %s, prefsrc: %s, "
402 "table: %s, priority: %"PRIu32", "
403 "proto: %s, scope: %s, type: %s, flags: %s",
3b6a3bde 404 str, strna(network_config_source_to_string(route->source)), strna(state),
f47d38c6
YW
405 strna(dst), strna(src), strna(nexthop), strna(prefsrc),
406 strna(table), route->priority,
407 strna(proto), strna(scope), strna(route_type_to_string(route->type)), strna(flags));
167a5561
YW
408}
409
10b08b67 410static int route_set_netlink_message(const Route *route, sd_netlink_message *m) {
5f4d7aa4
YW
411 int r;
412
413 assert(route);
31b26dee 414 assert(m);
5f4d7aa4 415
31b26dee 416 /* rtmsg header (and relevant attributes) */
5f4d7aa4 417 if (route->dst_prefixlen > 0) {
31b26dee 418 r = netlink_message_append_in_addr_union(m, RTA_DST, route->family, &route->dst);
5f4d7aa4 419 if (r < 0)
3bcb5dc1 420 return r;
5f4d7aa4 421
31b26dee 422 r = sd_rtnl_message_route_set_dst_prefixlen(m, route->dst_prefixlen);
5f4d7aa4 423 if (r < 0)
3bcb5dc1 424 return r;
5f4d7aa4
YW
425 }
426
427 if (route->src_prefixlen > 0) {
31b26dee 428 r = netlink_message_append_in_addr_union(m, RTA_SRC, route->family, &route->src);
5f4d7aa4 429 if (r < 0)
3bcb5dc1 430 return r;
5f4d7aa4 431
31b26dee 432 r = sd_rtnl_message_route_set_src_prefixlen(m, route->src_prefixlen);
5f4d7aa4 433 if (r < 0)
3bcb5dc1 434 return r;
5f4d7aa4
YW
435 }
436
31b26dee
YW
437 r = sd_rtnl_message_route_set_tos(m, route->tos);
438 if (r < 0)
439 return r;
5f4d7aa4 440
31b26dee 441 r = sd_rtnl_message_route_set_scope(m, route->scope);
5f4d7aa4 442 if (r < 0)
3bcb5dc1 443 return r;
5f4d7aa4 444
31b26dee 445 r = sd_rtnl_message_route_set_type(m, route->type);
5f4d7aa4 446 if (r < 0)
3bcb5dc1 447 return r;
5f4d7aa4 448
31b26dee
YW
449 r = sd_rtnl_message_route_set_flags(m, route->flags & ~RTNH_COMPARE_MASK);
450 if (r < 0)
451 return r;
452
453 /* attributes */
454 r = sd_netlink_message_append_u32(m, RTA_PRIORITY, route->priority);
455 if (r < 0)
456 return r;
457
458 if (in_addr_is_set(route->family, &route->prefsrc)) {
459 r = netlink_message_append_in_addr_union(m, RTA_PREFSRC, route->family, &route->prefsrc);
460 if (r < 0)
461 return r;
462 }
463
3e0eeb8e 464 if (route->table < 256) {
31b26dee 465 r = sd_rtnl_message_route_set_table(m, route->table);
3e0eeb8e 466 if (r < 0)
3bcb5dc1 467 return r;
3e0eeb8e 468 } else {
31b26dee 469 r = sd_rtnl_message_route_set_table(m, RT_TABLE_UNSPEC);
3e0eeb8e 470 if (r < 0)
3bcb5dc1 471 return r;
5f4d7aa4 472
3e0eeb8e 473 /* Table attribute to allow more than 256. */
31b26dee 474 r = sd_netlink_message_append_u32(m, RTA_TABLE, route->table);
3e0eeb8e 475 if (r < 0)
3bcb5dc1 476 return r;
5f4d7aa4
YW
477 }
478
31b26dee 479 r = sd_netlink_message_append_u8(m, RTA_PREF, route->pref);
5f4d7aa4 480 if (r < 0)
3bcb5dc1 481 return r;
5f4d7aa4 482
31b26dee 483 /* nexthops */
10b08b67 484 r = route_nexthops_set_netlink_message(route, m);
5f4d7aa4 485 if (r < 0)
3bcb5dc1 486 return r;
5f4d7aa4 487
31b26dee
YW
488 /* metrics */
489 r = route_metric_set_netlink_message(&route->metric, m);
54f9fba5
YW
490 if (r < 0)
491 return r;
492
5f4d7aa4
YW
493 return 0;
494}
495
3b6a3bde 496static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
497 int r;
498
499 assert(m);
4645ad47 500
3b6a3bde 501 /* link may be NULL. */
76c5a0f2 502
3b6a3bde 503 if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
5a07fa9d
YW
504 return 0;
505
506 r = sd_netlink_message_get_errno(m);
507 if (r < 0 && r != -ESRCH)
f4cc1364 508 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
5a07fa9d
YW
509
510 return 1;
511}
512
3caed9ea 513int route_remove(Route *route, Manager *manager) {
31b26dee 514 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
8d01e44c 515 Link *link = NULL;
5c1d3fc9
UTL
516 int r;
517
3b6a3bde 518 assert(route);
3caed9ea 519 assert(manager);
ad208fac 520
3518ff4a 521 log_route_debug(route, "Removing", manager);
167a5561 522
8d01e44c
YW
523 (void) route_get_link(manager, route, &link);
524
31b26dee 525 r = sd_rtnl_message_new_route(manager->rtnl, &m, RTM_DELROUTE, route->family, route->protocol);
f647962d 526 if (r < 0)
0a94e19c 527 return log_link_warning_errno(link, r, "Could not create netlink message: %m");
5c1d3fc9 528
10b08b67 529 r = route_set_netlink_message(route, m);
f647962d 530 if (r < 0)
0a94e19c 531 return log_link_warning_errno(link, r, "Could not fill netlink message: %m");
5c1d3fc9 532
31b26dee 533 r = netlink_call_async(manager->rtnl, NULL, m, route_remove_handler,
3b6a3bde
YW
534 link ? link_netlink_destroy_callback : NULL, link);
535 if (r < 0)
0a94e19c 536 return log_link_warning_errno(link, r, "Could not send netlink message: %m");
76c5a0f2 537
3b6a3bde 538 link_ref(link);
563c69c6 539
3b6a3bde 540 route_enter_removing(route);
5c1d3fc9
UTL
541 return 0;
542}
543
3caed9ea
YW
544int route_remove_and_cancel(Route *route, Manager *manager) {
545 bool waiting = false;
546 Request *req;
d4b76314 547
3caed9ea
YW
548 assert(route);
549 assert(manager);
550
551 /* If the route is remembered by the manager, then use the remembered object. */
552 (void) route_get(manager, route, &route);
d4b76314 553
3caed9ea
YW
554 /* Cancel the request for the route. If the request is already called but we have not received the
555 * notification about the request, then explicitly remove the route. */
556 if (route_get_request(manager, route, &req) >= 0) {
557 waiting = req->waiting_reply;
558 request_detach(req);
559 route_cancel_requesting(route);
560 }
d4b76314 561
3caed9ea
YW
562 /* If we know that the route will come or already exists, remove it. */
563 if (waiting || (route->manager && route_exists(route)))
564 return route_remove(route, manager);
d4b76314
YW
565
566 return 0;
567}
568
74154c2e 569static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
99534007 570 Route *route = ASSERT_PTR(userdata);
f833694d
TG
571 int r;
572
8d01e44c 573 assert(route->manager);
f833694d 574
3caed9ea 575 r = route_remove(route, route->manager);
d6ad41e2 576 if (r < 0) {
8d01e44c
YW
577 Link *link = NULL;
578 (void) route_get_link(route->manager, route, &link);
70b06526
YW
579 log_link_warning_errno(link, r, "Could not remove route: %m");
580 if (link)
581 link_enter_failed(link);
d6ad41e2 582 }
f833694d
TG
583
584 return 1;
585}
586
0b0c81bb 587static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo) {
0b0c81bb 588 int r;
f9bb3338 589
228c3e21 590 assert(route);
228c3e21 591
0b0c81bb 592 if (cacheinfo && cacheinfo->rta_expires != 0)
14436bf5
YW
593 route->expiration_managed_by_kernel = true;
594
595 if (route->lifetime_usec == USEC_INFINITY || /* We do not request expiration for the route. */
596 route->expiration_managed_by_kernel) { /* We have received nonzero expiration previously. The expiration is managed by the kernel. */
597 route->expire = sd_event_source_disable_unref(route->expire);
3b6a3bde 598 return 0;
14436bf5 599 }
228c3e21 600
8d01e44c 601 Manager *manager = ASSERT_PTR(route->manager);
ba4e0427 602 r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME,
91fc5135 603 route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true);
8d01e44c
YW
604 if (r < 0) {
605 Link *link = NULL;
606 (void) route_get_link(manager, route, &link);
607 return log_link_warning_errno(link, r, "Failed to configure expiration timer for route, ignoring: %m");
608 }
228c3e21 609
3518ff4a 610 log_route_debug(route, "Configured expiration timer for", manager);
0b0c81bb 611 return 1;
f9bb3338
YW
612}
613
2c0b49ba 614int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, Route *route, const char *error_msg) {
5a07fa9d
YW
615 int r;
616
617 assert(m);
618 assert(link);
2c0b49ba
YW
619 assert(link->manager);
620 assert(route);
5a07fa9d
YW
621 assert(error_msg);
622
5a07fa9d 623 r = sd_netlink_message_get_errno(m);
2c0b49ba
YW
624 if (r == -EEXIST) {
625 Route *existing;
626
8d01e44c 627 if (route_get(link->manager, route, &existing) >= 0) {
2c0b49ba
YW
628 /* When re-configuring an existing route, kernel does not send RTM_NEWROUTE
629 * notification, so we need to update the timer here. */
630 existing->lifetime_usec = route->lifetime_usec;
631 (void) route_setup_timer(existing, NULL);
1c62c4fe
YW
632
633 /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only
634 * when the route has an expiration timer managed by the kernel (not by us).
635 * See fib6_add_rt2node() in net/ipv6/ip6_fib.c of the kernel. */
636 if (existing->family == AF_INET6 &&
637 existing->expiration_managed_by_kernel) {
638 r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&route->metric, RTAX_MTU));
639 if (r < 0) {
640 log_oom();
641 link_enter_failed(link);
642 return 0;
643 }
644 }
2c0b49ba
YW
645 }
646
647 } else if (r < 0) {
a5a0a4eb 648 log_link_message_warning_errno(link, m, r, error_msg);
5a07fa9d
YW
649 link_enter_failed(link);
650 return 0;
651 }
652
653 return 1;
654}
655
b9e4ec50 656static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
a79a8d16 657 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
7b3a7581 658 int r;
f579559b 659
3b6a3bde 660 assert(route);
f579559b 661 assert(link);
f882c247 662 assert(link->manager);
54ff39f7 663 assert(req);
1b566071 664
3518ff4a 665 log_route_debug(route, "Configuring", link->manager);
156ed65e 666
a79a8d16 667 r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol);
f647962d 668 if (r < 0)
a79a8d16 669 return r;
f579559b 670
10b08b67 671 r = route_set_netlink_message(route, m);
f647962d 672 if (r < 0)
a79a8d16 673 return r;
3b015d40 674
b9e4ec50
YW
675 if (lifetime_sec != UINT32_MAX) {
676 r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
677 if (r < 0)
678 return r;
f02ba163
DD
679 }
680
80d62d4f 681 return request_call_netlink_async(link->manager->rtnl, m, req);
3b6a3bde 682}
0c54bfd6 683
5a18697d
YW
684static int route_requeue_request(Request *req, Link *link, const Route *route) {
685 _unused_ _cleanup_(request_unrefp) Request *req_unref = NULL;
686 _cleanup_(route_freep) Route *tmp = NULL;
687 int r;
688
689 assert(req);
690 assert(link);
691 assert(link->manager);
692 assert(route);
693
694 /* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage
695 * Request objects in the queue. Hence, we need to re-request with the updated Route object. */
696
697 if (!route_nexthops_needs_adjust(route))
698 return 0; /* The Route object does not need the adjustment. Continue with it. */
699
700 r = route_dup(route, NULL, &tmp);
701 if (r < 0)
702 return r;
703
704 r = route_adjust_nexthops(tmp, link);
705 if (r <= 0)
706 return r;
707
708 if (route_compare_func(route, tmp) == 0 && route->type == tmp->type)
709 return 0; /* No effective change?? That's OK. */
710
711 /* Avoid the request to be freed by request_detach(). */
712 req_unref = request_ref(req);
713
714 /* Detach the request from the queue, to make not the new request is deduped.
715 * Why this is necessary? IPv6 routes with different type may be handled as the same,
716 * As commented in route_adjust_nexthops(), we need to configure the adjusted type,
717 * otherwise we cannot remove the route on reconfigure or so. If we request the new Route object
718 * without detaching the current request, the new request is deduped, and the route is configured
719 * with unmodified type. */
720 request_detach(req);
721
722 /* Request the route with the adjusted Route object combined with the same other parameters. */
723 r = link_queue_request_full(link,
724 req->type,
725 tmp,
726 req->free_func,
727 req->hash_func,
728 req->compare_func,
729 req->process,
730 req->counter,
731 req->netlink_handler,
732 NULL);
733 if (r < 0)
734 return r;
735 if (r == 0)
736 return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */
737
738 TAKE_PTR(tmp);
739 return 1; /* New request is queued. Finish to process this request. */
740}
741
8bed7c55
YW
742static int route_is_ready_to_configure(const Route *route, Link *link) {
743 int r;
744
745 assert(route);
746 assert(link);
747
0a94e19c 748 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
8bed7c55
YW
749 return false;
750
8bed7c55 751 if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
1a4b0459 752 r = manager_has_address(link->manager, route->family, &route->prefsrc);
8bed7c55
YW
753 if (r <= 0)
754 return r;
755 }
756
10b08b67 757 return route_nexthops_is_ready_to_configure(route, link->manager);
8bed7c55
YW
758}
759
09d09207 760static int route_process_request(Request *req, Link *link, Route *route) {
5a18697d 761 Route *existing;
8bed7c55
YW
762 int r;
763
764 assert(req);
ff51134c 765 assert(link);
b9e4ec50 766 assert(link->manager);
ff51134c 767 assert(route);
8bed7c55
YW
768
769 r = route_is_ready_to_configure(route, link);
770 if (r < 0)
771 return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
772 if (r == 0)
773 return 0;
774
b9e4ec50
YW
775 usec_t now_usec;
776 assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
777 uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
778 if (sec == 0) {
779 log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
780 network_config_source_to_string(route->source));
781
5a18697d 782 route_cancel_requesting(route);
8d01e44c 783 if (route_get(link->manager, route, &existing) >= 0)
5a18697d 784 route_cancel_requesting(existing);
0a8720c7 785 return 1;
b9e4ec50
YW
786 }
787
5a18697d
YW
788 r = route_requeue_request(req, link, route);
789 if (r != 0)
790 return r;
791
b9e4ec50 792 r = route_configure(route, sec, link, req);
8bed7c55
YW
793 if (r < 0)
794 return log_link_warning_errno(link, r, "Failed to configure route: %m");
795
5a18697d 796 route_enter_configuring(route);
8d01e44c 797 if (route_get(link->manager, route, &existing) >= 0)
5a18697d 798 route_enter_configuring(existing);
8bed7c55
YW
799 return 1;
800}
801
5a18697d 802static int link_request_route_one(
76c5a0f2 803 Link *link,
5a18697d
YW
804 const Route *route,
805 const RouteNextHop *nh,
76c5a0f2 806 unsigned *message_counter,
5a18697d 807 route_netlink_handler_t netlink_handler) {
f345918d 808
5a18697d 809 _cleanup_(route_freep) Route *tmp = NULL;
af2aea8b 810 Route *existing = NULL;
3b6a3bde
YW
811 int r;
812
f345918d 813 assert(link);
76c5a0f2 814 assert(link->manager);
f345918d 815 assert(route);
303dfa73 816
5a18697d
YW
817 r = route_dup(route, nh, &tmp);
818 if (r < 0)
819 return r;
3b6a3bde 820
5a18697d
YW
821 r = route_adjust_nexthops(tmp, link);
822 if (r < 0)
823 return r;
3b6a3bde 824
8d01e44c 825 if (route_get(link->manager, tmp, &existing) >= 0)
5a18697d
YW
826 /* Copy state for logging below. */
827 tmp->state = existing->state;
3b6a3bde 828
5a18697d 829 log_route_debug(tmp, "Requesting", link->manager);
09d09207 830 r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
5a18697d
YW
831 tmp,
832 route_free,
09d09207
YW
833 route_hash_func,
834 route_compare_func,
835 route_process_request,
5a18697d
YW
836 message_counter,
837 netlink_handler,
838 NULL);
3b6a3bde
YW
839 if (r <= 0)
840 return r;
841
5a18697d
YW
842 route_enter_requesting(tmp);
843 if (existing)
844 route_enter_requesting(existing);
845
846 TAKE_PTR(tmp);
3b6a3bde
YW
847 return 1;
848}
849
5a18697d
YW
850int link_request_route(
851 Link *link,
852 const Route *route,
853 unsigned *message_counter,
854 route_netlink_handler_t netlink_handler) {
855
856 int r;
857
858 assert(link);
859 assert(link->manager);
860 assert(route);
861 assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
862
863 if (route->family == AF_INET || route_type_is_reject(route) || ordered_set_isempty(route->nexthops))
864 return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
865
866 RouteNextHop *nh;
867 ORDERED_SET_FOREACH(nh, route->nexthops) {
868 r = link_request_route_one(link, route, nh, message_counter, netlink_handler);
869 if (r < 0)
870 return r;
871 }
872
873 return 0;
874}
875
80d62d4f 876static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
e1e4cd1e
YW
877 int r;
878
879 assert(link);
e1e4cd1e 880
2c0b49ba 881 r = route_configure_handler_internal(rtnl, m, link, route, "Could not set route");
e1e4cd1e
YW
882 if (r <= 0)
883 return r;
884
885 if (link->static_route_messages == 0) {
886 log_link_debug(link, "Routes set");
887 link->static_routes_configured = true;
888 link_check_ready(link);
889 }
890
891 return 1;
892}
893
e9084344
YW
894static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
895 NetDev *netdev;
e9084344
YW
896 Route *route;
897 int r;
898
899 assert(link);
900
901 if (!streq_ptr(link->kind, "wireguard"))
902 return 0;
903
904 if (netdev_get(link->manager, link->ifname, &netdev) < 0)
905 return 0;
906
117843fe 907 Wireguard *w = WIREGUARD(netdev);
e9084344
YW
908
909 SET_FOREACH(route, w->routes) {
910 if (only_ipv4 && route->family != AF_INET)
911 continue;
912
5a18697d 913 r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
e9084344
YW
914 if (r < 0)
915 return r;
916 }
917
918 return 0;
919}
920
76c5a0f2 921int link_request_static_routes(Link *link, bool only_ipv4) {
e8f52f3c 922 Route *route;
141318f7
YW
923 int r;
924
f345918d
YW
925 assert(link);
926 assert(link->network);
927
76c5a0f2 928 link->static_routes_configured = false;
e8f52f3c 929
76c5a0f2 930 HASHMAP_FOREACH(route, link->network->routes_by_section) {
e8f52f3c
YW
931 if (route->gateway_from_dhcp_or_ra)
932 continue;
933
76c5a0f2 934 if (only_ipv4 && route->family != AF_INET)
f345918d
YW
935 continue;
936
5a18697d 937 r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
f345918d 938 if (r < 0)
76c5a0f2
YW
939 return r;
940 }
f345918d 941
e9084344
YW
942 r = link_request_wireguard_routes(link, only_ipv4);
943 if (r < 0)
944 return r;
945
76c5a0f2
YW
946 if (link->static_route_messages == 0) {
947 link->static_routes_configured = true;
948 link_check_ready(link);
949 } else {
950 log_link_debug(link, "Requesting routes");
951 link_set_state(link, LINK_STATE_CONFIGURING);
f345918d
YW
952 }
953
954 return 0;
955}
956
0b0c81bb
YW
957static int process_route_one(
958 Manager *manager,
0b0c81bb
YW
959 uint16_t type,
960 Route *in,
961 const struct rta_cacheinfo *cacheinfo) {
962
3b6a3bde 963 _cleanup_(route_freep) Route *tmp = in;
5a18697d 964 Request *req = NULL;
f9bb3338 965 Route *route = NULL;
5a18697d 966 Link *link = NULL;
f86575ca 967 bool is_new = false, update_dhcp4;
f9bb3338
YW
968 int r;
969
970 assert(manager);
971 assert(tmp);
972 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
973
8d01e44c 974 (void) route_get(manager, tmp, &route);
5a18697d
YW
975 (void) route_get_request(manager, tmp, &req);
976 (void) route_get_link(manager, tmp, &link);
f9bb3338 977
fc35a9f8
YW
978 update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0;
979
f9bb3338
YW
980 switch (type) {
981 case RTM_NEWROUTE:
f86575ca 982 if (!route) {
5a18697d 983 if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) {
f86575ca 984 route_enter_configured(tmp);
3518ff4a 985 log_route_debug(tmp, "Ignoring received", manager);
f86575ca
YW
986 return 0;
987 }
3b6a3bde 988
f86575ca 989 /* If we do not know the route, then save it. */
5a18697d 990 r = route_add(manager, tmp);
3b6a3bde
YW
991 if (r < 0) {
992 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
993 return 0;
f9bb3338 994 }
f86575ca
YW
995
996 route = TAKE_PTR(tmp);
997 is_new = true;
998
999 } else
1000 /* Update remembered route with the received notification. */
5a18697d
YW
1001 route->nexthop.weight = tmp->nexthop.weight;
1002
1003 /* Also update information that cannot be obtained through netlink notification. */
1004 if (req && req->waiting_reply) {
1005 Route *rt = ASSERT_PTR(req->userdata);
1006
1007 route->source = rt->source;
1008 route->provider = rt->provider;
1009 route->lifetime_usec = rt->lifetime_usec;
1010 }
f86575ca
YW
1011
1012 route_enter_configured(route);
3518ff4a 1013 log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
f86575ca 1014
14436bf5 1015 (void) route_setup_timer(route, cacheinfo);
f9bb3338
YW
1016
1017 break;
1018
1019 case RTM_DELROUTE:
3b6a3bde
YW
1020 if (route) {
1021 route_enter_removed(route);
5a18697d
YW
1022 log_route_debug(route, "Forgetting removed", manager);
1023 route_free(route);
3b6a3bde
YW
1024 } else
1025 log_route_debug(tmp,
1026 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
3518ff4a 1027 manager);
3b6a3bde 1028
5a18697d
YW
1029 if (req)
1030 route_enter_removed(req->userdata);
1031
f9bb3338
YW
1032 break;
1033
1034 default:
04499a70 1035 assert_not_reached();
f9bb3338
YW
1036 }
1037
fc35a9f8
YW
1038 if (update_dhcp4) {
1039 r = dhcp4_update_ipv6_connectivity(link);
1040 if (r < 0) {
1041 log_link_warning_errno(link, r, "Failed to notify IPv6 connectivity to DHCPv4 client: %m");
1042 link_enter_failed(link);
1043 }
1044 }
1045
f9bb3338
YW
1046 return 1;
1047}
1048
4468f01b
YW
1049int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
1050 _cleanup_(route_freep) Route *tmp = NULL;
4468f01b
YW
1051 int r;
1052
1053 assert(rtnl);
1054 assert(message);
1055 assert(m);
1056
1057 if (sd_netlink_message_is_error(message)) {
1058 r = sd_netlink_message_get_errno(message);
1059 if (r < 0)
1060 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1061
1062 return 0;
1063 }
1064
13efe0ab 1065 uint16_t type;
4468f01b
YW
1066 r = sd_netlink_message_get_type(message, &type);
1067 if (r < 0) {
1068 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1069 return 0;
1070 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1071 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1072 return 0;
1073 }
1074
4468f01b
YW
1075 r = route_new(&tmp);
1076 if (r < 0)
1077 return log_oom();
1078
00138639 1079 /* rtmsg header */
4468f01b
YW
1080 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1081 if (r < 0) {
13efe0ab 1082 log_warning_errno(r, "rtnl: received route message without family, ignoring: %m");
4468f01b
YW
1083 return 0;
1084 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
13efe0ab 1085 log_debug("rtnl: received route message with invalid family '%i', ignoring.", tmp->family);
4468f01b
YW
1086 return 0;
1087 }
1088
00138639 1089 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
4468f01b 1090 if (r < 0) {
00138639 1091 log_warning_errno(r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
4468f01b
YW
1092 return 0;
1093 }
1094
00138639 1095 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
17f8d8f9 1096 if (r < 0) {
00138639 1097 log_warning_errno(r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
17f8d8f9
YW
1098 return 0;
1099 }
1100
00138639
YW
1101 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
1102 if (r < 0) {
1103 log_warning_errno(r, "rtnl: received route message with invalid tos, ignoring: %m");
ad6df717
YW
1104 return 0;
1105 }
4468f01b 1106
00138639
YW
1107 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1108 if (r < 0) {
1109 log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
ad6df717
YW
1110 return 0;
1111 }
4468f01b 1112
00138639
YW
1113 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
1114 if (r < 0) {
1115 log_warning_errno(r, "rtnl: received route message with invalid scope, ignoring: %m");
4468f01b
YW
1116 return 0;
1117 }
1118
00138639 1119 r = sd_rtnl_message_route_get_type(message, &tmp->type);
4468f01b 1120 if (r < 0) {
00138639 1121 log_warning_errno(r, "rtnl: received route message with invalid type, ignoring: %m");
4468f01b
YW
1122 return 0;
1123 }
1124
00138639 1125 r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
4468f01b 1126 if (r < 0) {
00138639 1127 log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
4468f01b
YW
1128 return 0;
1129 }
1130
00138639
YW
1131 /* attributes */
1132 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1133 if (r < 0 && r != -ENODATA) {
1134 log_warning_errno(r, "rtnl: received route message without valid destination, ignoring: %m");
4468f01b
YW
1135 return 0;
1136 }
1137
00138639
YW
1138 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1139 if (r < 0 && r != -ENODATA) {
1140 log_warning_errno(r, "rtnl: received route message without valid source, ignoring: %m");
4468f01b
YW
1141 return 0;
1142 }
1143
00138639
YW
1144 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
1145 if (r < 0 && r != -ENODATA) {
1146 log_warning_errno(r, "rtnl: received route message with invalid priority, ignoring: %m");
1147 return 0;
1148 }
1149
1150 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
1151 if (r < 0 && r != -ENODATA) {
1152 log_warning_errno(r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
1153 return 0;
1154 }
1155
5e82a613
YW
1156 r = sd_netlink_message_read_u32(message, RTA_TABLE, &tmp->table);
1157 if (r == -ENODATA) {
f14a6e7f
YW
1158 unsigned char table;
1159
5e82a613
YW
1160 r = sd_rtnl_message_route_get_table(message, &table);
1161 if (r >= 0)
1162 tmp->table = table;
1163 }
4468f01b 1164 if (r < 0) {
13efe0ab 1165 log_warning_errno(r, "rtnl: received route message with invalid table, ignoring: %m");
4468f01b
YW
1166 return 0;
1167 }
4468f01b 1168
00138639 1169 r = sd_netlink_message_read_u8(message, RTA_PREF, &tmp->pref);
4468f01b 1170 if (r < 0 && r != -ENODATA) {
00138639 1171 log_warning_errno(r, "rtnl: received route message with invalid preference, ignoring: %m");
4468f01b
YW
1172 return 0;
1173 }
1174
54f9fba5
YW
1175 /* nexthops */
1176 if (route_nexthops_read_netlink_message(tmp, message) < 0)
324e3422 1177 return 0;
324e3422 1178
d32a520b
YW
1179 /* metrics */
1180 if (route_metric_read_netlink_message(&tmp->metric, message) < 0)
4468f01b 1181 return 0;
4468f01b 1182
13efe0ab
YW
1183 bool has_cacheinfo;
1184 struct rta_cacheinfo cacheinfo;
0b0c81bb
YW
1185 r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
1186 if (r < 0 && r != -ENODATA) {
13efe0ab 1187 log_warning_errno(r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
0b0c81bb
YW
1188 return 0;
1189 }
1190 has_cacheinfo = r >= 0;
1191
5a18697d
YW
1192 if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
1193 return process_route_one(m, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
4468f01b 1194
5a18697d
YW
1195 RouteNextHop *nh;
1196 ORDERED_SET_FOREACH(nh, tmp->nexthops) {
1197 _cleanup_(route_freep) Route *dup = NULL;
4468f01b 1198
5a18697d
YW
1199 r = route_dup(tmp, nh, &dup);
1200 if (r < 0)
1201 return log_oom();
3b6a3bde 1202
5a18697d
YW
1203 r = process_route_one(m, type, TAKE_PTR(dup), has_cacheinfo ? &cacheinfo : NULL);
1204 if (r < 0)
1205 return r;
1206 }
3b6a3bde 1207
4468f01b
YW
1208 return 1;
1209}
1210
8d01e44c
YW
1211void manager_mark_routes(Manager *manager, Link *link, NetworkConfigSource source) {
1212 Route *route;
1213
1214 assert(manager);
1215
1216 SET_FOREACH(route, manager->routes) {
1217 if (route->source != source)
1218 continue;
1219
1220 if (link) {
1221 Link *route_link;
1222
1223 if (route_get_link(manager, route, &route_link) < 0)
1224 continue;
1225 if (route_link != link)
1226 continue;
1227 }
1228
1229 route_mark(route);
1230 }
1231}
1232
1233static bool route_by_kernel(const Route *route) {
1234 assert(route);
1235
1236 if (route->protocol == RTPROT_KERNEL)
1237 return true;
1238
1239 /* The kernels older than a826b04303a40d52439aa141035fca5654ccaccd (v5.11) create the IPv6
1240 * multicast with RTPROT_BOOT. Do not touch it. */
1241 if (route->protocol == RTPROT_BOOT &&
1242 route->family == AF_INET6 &&
1243 route->dst_prefixlen == 8 &&
1244 in6_addr_equal(&route->dst.in6, & (struct in6_addr) {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}}))
1245 return true;
1246
1247 return false;
1248}
1249
1250static int link_unmark_route(Link *link, const Route *route, const RouteNextHop *nh) {
1251 _cleanup_(route_freep) Route *tmp = NULL;
1252 Route *existing;
1253 int r;
1254
1255 assert(link);
1256 assert(route);
1257
1258 r = route_dup(route, nh, &tmp);
1259 if (r < 0)
1260 return r;
1261
1262 r = route_adjust_nexthops(tmp, link);
1263 if (r < 0)
1264 return r;
1265
1266 if (route_get(link->manager, tmp, &existing) < 0)
1267 return 0;
1268
1269 route_unmark(existing);
1270 return 1;
1271}
1272
1273static int link_mark_routes(Link *link, bool foreign) {
1274 Route *route;
1275 Link *other;
1276 int r;
1277
1278 assert(link);
1279 assert(link->manager);
1280
1281 /* First, mark all routes. */
1282 SET_FOREACH(route, link->manager->routes) {
1283 /* Do not touch routes managed by the kernel. */
1284 if (route_by_kernel(route))
1285 continue;
1286
1287 /* When 'foreign' is true, mark only foreign routes, and vice versa.
1288 * Note, do not touch dynamic routes. They will removed by when e.g. lease is lost. */
1289 if (route->source != (foreign ? NETWORK_CONFIG_SOURCE_FOREIGN : NETWORK_CONFIG_SOURCE_STATIC))
1290 continue;
1291
1292 /* Ignore routes not assigned yet or already removed. */
1293 if (!route_exists(route))
1294 continue;
1295
1296 if (link->network) {
1297 if (route->protocol == RTPROT_STATIC &&
1298 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
1299 continue;
1300
1301 if (route->protocol == RTPROT_DHCP &&
1302 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
1303 continue;
1304 }
1305
1306 /* When we mark foreign routes, do not mark routes assigned to other interfaces.
1307 * Otherwise, routes assigned to unmanaged interfaces will be dropped.
1308 * Note, route_get_link() does not provide assigned link for routes with an unreachable type
1309 * or IPv4 multipath routes. So, the current implementation does not support managing such
1310 * routes by other daemon or so, unless ManageForeignRoutes=no. */
1311 if (foreign) {
1312 Link *route_link;
1313
1314 if (route_get_link(link->manager, route, &route_link) >= 0 && route_link != link)
1315 continue;
1316 }
1317
1318 route_mark(route);
1319 }
1320
1321 /* Then, unmark all routes requested by active links. */
1322 HASHMAP_FOREACH(other, link->manager->links_by_index) {
1323 if (!foreign && other == link)
1324 continue;
1325
1326 if (!IN_SET(other->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
1327 continue;
1328
1329 HASHMAP_FOREACH(route, other->network->routes_by_section) {
1330 if (route->family == AF_INET || ordered_set_isempty(route->nexthops)) {
1331 r = link_unmark_route(other, route, NULL);
1332 if (r < 0)
1333 return r;
1334
1335 } else {
1336 RouteNextHop *nh;
1337 ORDERED_SET_FOREACH(nh, route->nexthops) {
1338 r = link_unmark_route(other, route, nh);
1339 if (r < 0)
1340 return r;
1341 }
1342 }
1343 }
1344 }
1345
1346 /* Also unmark routes requested in .netdev file. */
1347 if (foreign && link->netdev && link->netdev->kind == NETDEV_KIND_WIREGUARD) {
1348 Wireguard *w = WIREGUARD(link->netdev);
1349
1350 SET_FOREACH(route, w->routes) {
1351 r = link_unmark_route(link, route, NULL);
1352 if (r < 0)
1353 return r;
1354 }
1355 }
1356
1357 return 0;
1358}
1359
1360int link_drop_routes(Link *link, bool foreign) {
1361 Route *route;
1362 int r;
1363
1364 assert(link);
1365 assert(link->manager);
1366
1367 r = link_mark_routes(link, foreign);
1368 if (r < 0)
1369 return r;
1370
1371 SET_FOREACH(route, link->manager->routes) {
1372 if (!route_is_marked(route))
1373 continue;
1374
3caed9ea 1375 RET_GATHER(r, route_remove(route, link->manager));
8d01e44c
YW
1376 }
1377
1378 return r;
1379}
1380
1381int link_foreignize_routes(Link *link) {
1382 Route *route;
1383 int r;
1384
1385 assert(link);
1386 assert(link->manager);
1387
1388 r = link_mark_routes(link, /* foreign = */ false);
1389 if (r < 0)
1390 return r;
1391
1392 SET_FOREACH(route, link->manager->routes) {
1393 if (!route_is_marked(route))
1394 continue;
1395
1396 route->source = NETWORK_CONFIG_SOURCE_FOREIGN;
1397 }
1398
1399 return 0;
1400}
1401
fa7cd711 1402int network_add_ipv4ll_route(Network *network) {
f48e52bd 1403 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
2a54a044 1404 unsigned section_line;
fa7cd711
YW
1405 int r;
1406
1407 assert(network);
1408
1409 if (!network->ipv4ll_route)
1410 return 0;
1411
d9171a23
YW
1412 r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
1413 if (r < 0)
1414 return r;
2a54a044 1415
fa7cd711 1416 /* IPv4LLRoute= is in [Network] section. */
f48e52bd 1417 r = route_new_static(network, network->filename, section_line, &route);
fa7cd711
YW
1418 if (r < 0)
1419 return r;
1420
f48e52bd 1421 r = in_addr_from_string(AF_INET, "169.254.0.0", &route->dst);
fa7cd711
YW
1422 if (r < 0)
1423 return r;
1424
f48e52bd
YW
1425 route->family = AF_INET;
1426 route->dst_prefixlen = 16;
1427 route->scope = RT_SCOPE_LINK;
1428 route->scope_set = true;
1429 route->table_set = true;
1430 route->priority = IPV4LL_ROUTE_METRIC;
1431 route->protocol = RTPROT_STATIC;
fa7cd711 1432
f48e52bd 1433 TAKE_PTR(route);
fa7cd711
YW
1434 return 0;
1435}
1436
5d5003ab 1437int network_add_default_route_on_device(Network *network) {
f48e52bd 1438 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
2a54a044 1439 unsigned section_line;
5d5003ab
YW
1440 int r;
1441
1442 assert(network);
1443
1444 if (!network->default_route_on_device)
1445 return 0;
1446
d9171a23
YW
1447 r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
1448 if (r < 0)
1449 return r;
2a54a044 1450
5d5003ab 1451 /* DefaultRouteOnDevice= is in [Network] section. */
f48e52bd 1452 r = route_new_static(network, network->filename, section_line, &route);
5d5003ab
YW
1453 if (r < 0)
1454 return r;
1455
f48e52bd
YW
1456 route->family = AF_INET;
1457 route->scope = RT_SCOPE_LINK;
1458 route->scope_set = true;
1459 route->protocol = RTPROT_STATIC;
5d5003ab 1460
f48e52bd 1461 TAKE_PTR(route);
5d5003ab
YW
1462 return 0;
1463}
1464
27efb52b
YW
1465int config_parse_preferred_src(
1466 const char *unit,
0d07e595
JK
1467 const char *filename,
1468 unsigned line,
1469 const char *section,
1470 unsigned section_line,
1471 const char *lvalue,
1472 int ltype,
1473 const char *rvalue,
1474 void *data,
1475 void *userdata) {
1476
1477 Network *network = userdata;
f48e52bd 1478 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7934dede 1479 int r;
0d07e595
JK
1480
1481 assert(filename);
1482 assert(section);
1483 assert(lvalue);
1484 assert(rvalue);
1485 assert(data);
1486
f48e52bd 1487 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1488 if (r == -ENOMEM)
1489 return log_oom();
1490 if (r < 0) {
1491 log_syntax(unit, LOG_WARNING, filename, line, r,
1492 "Failed to allocate route, ignoring assignment: %m");
1493 return 0;
1494 }
0d07e595 1495
f48e52bd
YW
1496 if (route->family == AF_UNSPEC)
1497 r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc);
01d4e732 1498 else
f48e52bd 1499 r = in_addr_from_string(route->family, rvalue, &route->prefsrc);
0d07e595 1500 if (r < 0) {
d96edb2c 1501 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1502 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1503 return 0;
1504 }
1505
f48e52bd 1506 TAKE_PTR(route);
0d07e595
JK
1507 return 0;
1508}
1509
27efb52b
YW
1510int config_parse_destination(
1511 const char *unit,
6ae115c1
TG
1512 const char *filename,
1513 unsigned line,
1514 const char *section,
1515 unsigned section_line,
1516 const char *lvalue,
1517 int ltype,
1518 const char *rvalue,
1519 void *data,
1520 void *userdata) {
44e7b949 1521
6ae115c1 1522 Network *network = userdata;
f48e52bd 1523 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7934dede
YW
1524 union in_addr_union *buffer;
1525 unsigned char *prefixlen;
ca3bad65 1526 int r;
6ae115c1
TG
1527
1528 assert(filename);
1529 assert(section);
1530 assert(lvalue);
1531 assert(rvalue);
1532 assert(data);
1533
f48e52bd 1534 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1535 if (r == -ENOMEM)
1536 return log_oom();
1537 if (r < 0) {
1538 log_syntax(unit, LOG_WARNING, filename, line, r,
1539 "Failed to allocate route, ignoring assignment: %m");
1540 return 0;
1541 }
6ae115c1 1542
9e7e4408 1543 if (streq(lvalue, "Destination")) {
f48e52bd
YW
1544 buffer = &route->dst;
1545 prefixlen = &route->dst_prefixlen;
9e7e4408 1546 } else if (streq(lvalue, "Source")) {
f48e52bd
YW
1547 buffer = &route->src;
1548 prefixlen = &route->src_prefixlen;
9e7e4408 1549 } else
04499a70 1550 assert_not_reached();
9e7e4408 1551
f48e52bd
YW
1552 if (route->family == AF_UNSPEC)
1553 r = in_addr_prefix_from_string_auto(rvalue, &route->family, buffer, prefixlen);
01d4e732 1554 else
f48e52bd 1555 r = in_addr_prefix_from_string(rvalue, route->family, buffer, prefixlen);
7934dede 1556 if (r < 0) {
d96edb2c 1557 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1558 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1559 return 0;
1560 }
1561
f48e52bd 1562 (void) in_addr_mask(route->family, buffer, *prefixlen);
72fa1923 1563
f48e52bd 1564 TAKE_PTR(route);
6ae115c1
TG
1565 return 0;
1566}
5d8e593d 1567
27efb52b
YW
1568int config_parse_route_priority(
1569 const char *unit,
1570 const char *filename,
1571 unsigned line,
1572 const char *section,
1573 unsigned section_line,
1574 const char *lvalue,
1575 int ltype,
1576 const char *rvalue,
1577 void *data,
1578 void *userdata) {
1579
5d8e593d 1580 Network *network = userdata;
f48e52bd 1581 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
5d8e593d
SS
1582 int r;
1583
1584 assert(filename);
1585 assert(section);
1586 assert(lvalue);
1587 assert(rvalue);
1588 assert(data);
1589
f48e52bd 1590 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1591 if (r == -ENOMEM)
1592 return log_oom();
1593 if (r < 0) {
1594 log_syntax(unit, LOG_WARNING, filename, line, r,
1595 "Failed to allocate route, ignoring assignment: %m");
1596 return 0;
1597 }
5d8e593d 1598
f48e52bd 1599 r = safe_atou32(rvalue, &route->priority);
1c4b1179 1600 if (r < 0) {
d96edb2c 1601 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1602 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1603 return 0;
1604 }
5d8e593d 1605
f48e52bd
YW
1606 route->priority_set = true;
1607 TAKE_PTR(route);
5d8e593d
SS
1608 return 0;
1609}
769b56a3 1610
27efb52b
YW
1611int config_parse_route_scope(
1612 const char *unit,
1613 const char *filename,
1614 unsigned line,
1615 const char *section,
1616 unsigned section_line,
1617 const char *lvalue,
1618 int ltype,
1619 const char *rvalue,
1620 void *data,
1621 void *userdata) {
1622
769b56a3 1623 Network *network = userdata;
f48e52bd 1624 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
769b56a3
TG
1625 int r;
1626
1627 assert(filename);
1628 assert(section);
1629 assert(lvalue);
1630 assert(rvalue);
1631 assert(data);
1632
f48e52bd 1633 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1634 if (r == -ENOMEM)
1635 return log_oom();
1636 if (r < 0) {
1637 log_syntax(unit, LOG_WARNING, filename, line, r,
1638 "Failed to allocate route, ignoring assignment: %m");
1639 return 0;
1640 }
769b56a3 1641
41b90a1e
YW
1642 r = route_scope_from_string(rvalue);
1643 if (r < 0) {
b98680b2 1644 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
1645 return 0;
1646 }
1647
f48e52bd
YW
1648 route->scope = r;
1649 route->scope_set = true;
1650 TAKE_PTR(route);
769b56a3
TG
1651 return 0;
1652}
c953b24c 1653
27efb52b
YW
1654int config_parse_route_table(
1655 const char *unit,
1656 const char *filename,
1657 unsigned line,
1658 const char *section,
1659 unsigned section_line,
1660 const char *lvalue,
1661 int ltype,
1662 const char *rvalue,
1663 void *data,
1664 void *userdata) {
1665
f48e52bd 1666 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
c953b24c 1667 Network *network = userdata;
c953b24c
SS
1668 int r;
1669
1670 assert(filename);
1671 assert(section);
1672 assert(lvalue);
1673 assert(rvalue);
1674 assert(data);
1675
f48e52bd 1676 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1677 if (r == -ENOMEM)
1678 return log_oom();
1679 if (r < 0) {
1680 log_syntax(unit, LOG_WARNING, filename, line, r,
1681 "Failed to allocate route, ignoring assignment: %m");
1682 return 0;
1683 }
c953b24c 1684
f48e52bd 1685 r = manager_get_route_table_from_string(network->manager, rvalue, &route->table);
c038ce46
SS
1686 if (r < 0) {
1687 log_syntax(unit, LOG_WARNING, filename, line, r,
29de4f73 1688 "Could not parse route table \"%s\", ignoring assignment: %m", rvalue);
c038ce46 1689 return 0;
c953b24c
SS
1690 }
1691
f48e52bd
YW
1692 route->table_set = true;
1693 TAKE_PTR(route);
c953b24c
SS
1694 return 0;
1695}
28959f7d 1696
27efb52b
YW
1697int config_parse_ipv6_route_preference(
1698 const char *unit,
1699 const char *filename,
1700 unsigned line,
1701 const char *section,
1702 unsigned section_line,
1703 const char *lvalue,
1704 int ltype,
1705 const char *rvalue,
1706 void *data,
1707 void *userdata) {
1708
b5bf6f64 1709 Network *network = userdata;
f48e52bd 1710 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
b5bf6f64
SS
1711 int r;
1712
f48e52bd 1713 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1714 if (r == -ENOMEM)
1715 return log_oom();
1716 if (r < 0) {
1717 log_syntax(unit, LOG_WARNING, filename, line, r,
1718 "Failed to allocate route, ignoring assignment: %m");
1719 return 0;
1720 }
4c7bd9cf 1721
b5bf6f64 1722 if (streq(rvalue, "low"))
f48e52bd 1723 route->pref = ICMPV6_ROUTER_PREF_LOW;
b5bf6f64 1724 else if (streq(rvalue, "medium"))
f48e52bd 1725 route->pref = ICMPV6_ROUTER_PREF_MEDIUM;
b5bf6f64 1726 else if (streq(rvalue, "high"))
f48e52bd 1727 route->pref = ICMPV6_ROUTER_PREF_HIGH;
b5bf6f64 1728 else {
d96edb2c 1729 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
1730 return 0;
1731 }
28959f7d 1732
f48e52bd
YW
1733 route->pref_set = true;
1734 TAKE_PTR(route);
28959f7d
SS
1735 return 0;
1736}
c83ecc04 1737
27efb52b
YW
1738int config_parse_route_protocol(
1739 const char *unit,
1740 const char *filename,
1741 unsigned line,
1742 const char *section,
1743 unsigned section_line,
1744 const char *lvalue,
1745 int ltype,
1746 const char *rvalue,
1747 void *data,
1748 void *userdata) {
1749
c83ecc04 1750 Network *network = userdata;
f48e52bd 1751 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
c83ecc04
SS
1752 int r;
1753
f48e52bd 1754 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1755 if (r == -ENOMEM)
1756 return log_oom();
1757 if (r < 0) {
1758 log_syntax(unit, LOG_WARNING, filename, line, r,
1759 "Failed to allocate route, ignoring assignment: %m");
1760 return 0;
1761 }
c83ecc04 1762
1b64651b 1763 r = route_protocol_from_string(rvalue);
e4ffe103
YW
1764 if (r < 0) {
1765 log_syntax(unit, LOG_WARNING, filename, line, r,
1766 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
1767 return 0;
c83ecc04
SS
1768 }
1769
f48e52bd 1770 route->protocol = r;
e4ffe103 1771
f48e52bd 1772 TAKE_PTR(route);
c83ecc04
SS
1773 return 0;
1774}
983226f3 1775
27efb52b
YW
1776int config_parse_route_type(
1777 const char *unit,
1778 const char *filename,
1779 unsigned line,
1780 const char *section,
1781 unsigned section_line,
1782 const char *lvalue,
1783 int ltype,
1784 const char *rvalue,
1785 void *data,
1786 void *userdata) {
1787
983226f3 1788 Network *network = userdata;
f48e52bd 1789 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7a22312d 1790 int t, r;
983226f3 1791
f48e52bd 1792 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1793 if (r == -ENOMEM)
1794 return log_oom();
1795 if (r < 0) {
1796 log_syntax(unit, LOG_WARNING, filename, line, r,
1797 "Failed to allocate route, ignoring assignment: %m");
1798 return 0;
1799 }
983226f3 1800
7a22312d
YW
1801 t = route_type_from_string(rvalue);
1802 if (t < 0) {
b98680b2 1803 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 1804 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
1805 return 0;
1806 }
1807
f48e52bd 1808 route->type = (unsigned char) t;
7a22312d 1809
f48e52bd 1810 TAKE_PTR(route);
983226f3
SS
1811 return 0;
1812}
323d9329 1813
4db8ccbb 1814int route_section_verify(Route *route) {
0db96961
YW
1815 int r;
1816
1817 assert(route);
1818 assert(route->section);
0db96961 1819
fcbf4cb7
YW
1820 if (section_is_invalid(route->section))
1821 return -EINVAL;
1822
3b6a3bde 1823 /* Currently, we do not support static route with finite lifetime. */
91fc5135 1824 assert(route->lifetime_usec == USEC_INFINITY);
3b6a3bde 1825
0db96961
YW
1826 r = route_section_verify_nexthops(route);
1827 if (r < 0)
1828 return r;
6dd53981 1829
0db96961 1830 /* table */
4db8ccbb 1831 if (!route->table_set && route->network && route->network->vrf) {
0db96961 1832 route->table = VRF(route->network->vrf)->table;
c0d48bc5
YW
1833 route->table_set = true;
1834 }
1835
f5c38922
YW
1836 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
1837 route->table = RT_TABLE_LOCAL;
1838
0db96961
YW
1839 /* scope */
1840 if (!route->scope_set && route->family == AF_INET) {
f5c38922
YW
1841 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
1842 route->scope = RT_SCOPE_HOST;
1843 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
1844 route->scope = RT_SCOPE_LINK;
902bbdc4
YW
1845 else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) &&
1846 !route->gateway_from_dhcp_or_ra &&
054b8c28 1847 !in_addr_is_set(route->nexthop.family, &route->nexthop.gw) &&
fea879cd 1848 ordered_set_isempty(route->nexthops) &&
902bbdc4
YW
1849 route->nexthop_id == 0)
1850 route->scope = RT_SCOPE_LINK;
94d6e299
YW
1851 }
1852
0db96961 1853 /* IPv6 route */
40785f53 1854 if (route->family == AF_INET6) {
0db96961
YW
1855 if (route->scope != RT_SCOPE_UNIVERSE) {
1856 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
1857 route->scope = RT_SCOPE_UNIVERSE;
1858 }
40785f53 1859
0db96961
YW
1860 if (route->priority == 0)
1861 route->priority = IP6_RT_PRIO_USER;
1862 }
324e3422 1863
fcbf4cb7
YW
1864 return 0;
1865}
d9940a3f 1866
13ffa39f 1867void network_drop_invalid_routes(Network *network) {
2a54a044 1868 Route *route;
d9940a3f
YW
1869
1870 assert(network);
1871
2a54a044 1872 HASHMAP_FOREACH(route, network->routes_by_section)
0db96961 1873 if (route_section_verify(route) < 0)
d9940a3f
YW
1874 route_free(route);
1875}