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