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