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