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