]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
network/route: update MTU of IPv6 route even if the kernel returns -EEXIST
[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);
1c62c4fe
YW
877
878 /* This may be a bug in the kernel, but the MTU of an IPv6 route can be updated only
879 * when the route has an expiration timer managed by the kernel (not by us).
880 * See fib6_add_rt2node() in net/ipv6/ip6_fib.c of the kernel. */
881 if (existing->family == AF_INET6 &&
882 existing->expiration_managed_by_kernel) {
883 r = route_metric_set(&existing->metric, RTAX_MTU, route_metric_get(&route->metric, RTAX_MTU));
884 if (r < 0) {
885 log_oom();
886 link_enter_failed(link);
887 return 0;
888 }
889 }
2c0b49ba
YW
890 }
891
892 } else if (r < 0) {
a5a0a4eb 893 log_link_message_warning_errno(link, m, r, error_msg);
5a07fa9d
YW
894 link_enter_failed(link);
895 return 0;
896 }
897
898 return 1;
899}
900
b9e4ec50 901static int route_configure(const Route *route, uint32_t lifetime_sec, Link *link, Request *req) {
a79a8d16 902 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
7b3a7581 903 int r;
f579559b 904
3b6a3bde 905 assert(route);
f579559b 906 assert(link);
f882c247 907 assert(link->manager);
54ff39f7 908 assert(req);
1b566071 909
3518ff4a 910 log_route_debug(route, "Configuring", link->manager);
156ed65e 911
a79a8d16 912 r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol);
f647962d 913 if (r < 0)
a79a8d16 914 return r;
f579559b 915
10b08b67 916 r = route_set_netlink_message(route, m);
f647962d 917 if (r < 0)
a79a8d16 918 return r;
3b015d40 919
b9e4ec50
YW
920 if (lifetime_sec != UINT32_MAX) {
921 r = sd_netlink_message_append_u32(m, RTA_EXPIRES, lifetime_sec);
922 if (r < 0)
923 return r;
f02ba163
DD
924 }
925
80d62d4f 926 return request_call_netlink_async(link->manager->rtnl, m, req);
3b6a3bde 927}
0c54bfd6 928
5a18697d
YW
929static int route_requeue_request(Request *req, Link *link, const Route *route) {
930 _unused_ _cleanup_(request_unrefp) Request *req_unref = NULL;
931 _cleanup_(route_freep) Route *tmp = NULL;
932 int r;
933
934 assert(req);
935 assert(link);
936 assert(link->manager);
937 assert(route);
938
939 /* It is not possible to adjust the Route object owned by Request, as it is used as a key to manage
940 * Request objects in the queue. Hence, we need to re-request with the updated Route object. */
941
942 if (!route_nexthops_needs_adjust(route))
943 return 0; /* The Route object does not need the adjustment. Continue with it. */
944
945 r = route_dup(route, NULL, &tmp);
946 if (r < 0)
947 return r;
948
949 r = route_adjust_nexthops(tmp, link);
950 if (r <= 0)
951 return r;
952
953 if (route_compare_func(route, tmp) == 0 && route->type == tmp->type)
954 return 0; /* No effective change?? That's OK. */
955
956 /* Avoid the request to be freed by request_detach(). */
957 req_unref = request_ref(req);
958
959 /* Detach the request from the queue, to make not the new request is deduped.
960 * Why this is necessary? IPv6 routes with different type may be handled as the same,
961 * As commented in route_adjust_nexthops(), we need to configure the adjusted type,
962 * otherwise we cannot remove the route on reconfigure or so. If we request the new Route object
963 * without detaching the current request, the new request is deduped, and the route is configured
964 * with unmodified type. */
965 request_detach(req);
966
967 /* Request the route with the adjusted Route object combined with the same other parameters. */
968 r = link_queue_request_full(link,
969 req->type,
970 tmp,
971 req->free_func,
972 req->hash_func,
973 req->compare_func,
974 req->process,
975 req->counter,
976 req->netlink_handler,
977 NULL);
978 if (r < 0)
979 return r;
980 if (r == 0)
981 return 1; /* Already queued?? That's OK. Maybe, [Route] section is effectively duplicated. */
982
983 TAKE_PTR(tmp);
984 return 1; /* New request is queued. Finish to process this request. */
985}
986
8bed7c55
YW
987static int route_is_ready_to_configure(const Route *route, Link *link) {
988 int r;
989
990 assert(route);
991 assert(link);
992
0a94e19c 993 if (!link_is_ready_to_configure(link, /* allow_unmanaged = */ false))
8bed7c55
YW
994 return false;
995
996 if (set_size(link->routes) >= routes_max())
997 return false;
998
8bed7c55 999 if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
1a4b0459 1000 r = manager_has_address(link->manager, route->family, &route->prefsrc);
8bed7c55
YW
1001 if (r <= 0)
1002 return r;
1003 }
1004
10b08b67 1005 return route_nexthops_is_ready_to_configure(route, link->manager);
8bed7c55
YW
1006}
1007
09d09207 1008static int route_process_request(Request *req, Link *link, Route *route) {
5a18697d 1009 Route *existing;
8bed7c55
YW
1010 int r;
1011
1012 assert(req);
ff51134c 1013 assert(link);
b9e4ec50 1014 assert(link->manager);
ff51134c 1015 assert(route);
8bed7c55
YW
1016
1017 r = route_is_ready_to_configure(route, link);
1018 if (r < 0)
1019 return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m");
1020 if (r == 0)
1021 return 0;
1022
b9e4ec50
YW
1023 usec_t now_usec;
1024 assert_se(sd_event_now(link->manager->event, CLOCK_BOOTTIME, &now_usec) >= 0);
1025 uint32_t sec = usec_to_sec(route->lifetime_usec, now_usec);
1026 if (sec == 0) {
1027 log_link_debug(link, "Refuse to configure %s route with zero lifetime.",
1028 network_config_source_to_string(route->source));
1029
5a18697d
YW
1030 route_cancel_requesting(route);
1031 if (route_get(link->manager, link, route, &existing) >= 0)
1032 route_cancel_requesting(existing);
0a8720c7 1033 return 1;
b9e4ec50
YW
1034 }
1035
5a18697d
YW
1036 r = route_requeue_request(req, link, route);
1037 if (r != 0)
1038 return r;
1039
b9e4ec50 1040 r = route_configure(route, sec, link, req);
8bed7c55
YW
1041 if (r < 0)
1042 return log_link_warning_errno(link, r, "Failed to configure route: %m");
1043
5a18697d
YW
1044 route_enter_configuring(route);
1045 if (route_get(link->manager, link, route, &existing) >= 0)
1046 route_enter_configuring(existing);
8bed7c55
YW
1047 return 1;
1048}
1049
5a18697d 1050static int link_request_route_one(
76c5a0f2 1051 Link *link,
5a18697d
YW
1052 const Route *route,
1053 const RouteNextHop *nh,
76c5a0f2 1054 unsigned *message_counter,
5a18697d 1055 route_netlink_handler_t netlink_handler) {
f345918d 1056
5a18697d 1057 _cleanup_(route_freep) Route *tmp = NULL;
af2aea8b 1058 Route *existing = NULL;
3b6a3bde
YW
1059 int r;
1060
f345918d 1061 assert(link);
76c5a0f2 1062 assert(link->manager);
f345918d 1063 assert(route);
303dfa73 1064
5a18697d
YW
1065 r = route_dup(route, nh, &tmp);
1066 if (r < 0)
1067 return r;
3b6a3bde 1068
5a18697d
YW
1069 r = route_adjust_nexthops(tmp, link);
1070 if (r < 0)
1071 return r;
3b6a3bde 1072
5a18697d
YW
1073 if (route_get(link->manager, link, tmp, &existing) >= 0)
1074 /* Copy state for logging below. */
1075 tmp->state = existing->state;
3b6a3bde 1076
5a18697d 1077 log_route_debug(tmp, "Requesting", link->manager);
09d09207 1078 r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE,
5a18697d
YW
1079 tmp,
1080 route_free,
09d09207
YW
1081 route_hash_func,
1082 route_compare_func,
1083 route_process_request,
5a18697d
YW
1084 message_counter,
1085 netlink_handler,
1086 NULL);
3b6a3bde
YW
1087 if (r <= 0)
1088 return r;
1089
5a18697d
YW
1090 route_enter_requesting(tmp);
1091 if (existing)
1092 route_enter_requesting(existing);
1093
1094 TAKE_PTR(tmp);
3b6a3bde
YW
1095 return 1;
1096}
1097
5a18697d
YW
1098int link_request_route(
1099 Link *link,
1100 const Route *route,
1101 unsigned *message_counter,
1102 route_netlink_handler_t netlink_handler) {
1103
1104 int r;
1105
1106 assert(link);
1107 assert(link->manager);
1108 assert(route);
1109 assert(route->source != NETWORK_CONFIG_SOURCE_FOREIGN);
1110
1111 if (route->family == AF_INET || route_type_is_reject(route) || ordered_set_isempty(route->nexthops))
1112 return link_request_route_one(link, route, NULL, message_counter, netlink_handler);
1113
1114 RouteNextHop *nh;
1115 ORDERED_SET_FOREACH(nh, route->nexthops) {
1116 r = link_request_route_one(link, route, nh, message_counter, netlink_handler);
1117 if (r < 0)
1118 return r;
1119 }
1120
1121 return 0;
1122}
1123
80d62d4f 1124static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
e1e4cd1e
YW
1125 int r;
1126
1127 assert(link);
e1e4cd1e 1128
2c0b49ba 1129 r = route_configure_handler_internal(rtnl, m, link, route, "Could not set route");
e1e4cd1e
YW
1130 if (r <= 0)
1131 return r;
1132
1133 if (link->static_route_messages == 0) {
1134 log_link_debug(link, "Routes set");
1135 link->static_routes_configured = true;
1136 link_check_ready(link);
1137 }
1138
1139 return 1;
1140}
1141
e9084344
YW
1142static int link_request_wireguard_routes(Link *link, bool only_ipv4) {
1143 NetDev *netdev;
e9084344
YW
1144 Route *route;
1145 int r;
1146
1147 assert(link);
1148
1149 if (!streq_ptr(link->kind, "wireguard"))
1150 return 0;
1151
1152 if (netdev_get(link->manager, link->ifname, &netdev) < 0)
1153 return 0;
1154
117843fe 1155 Wireguard *w = WIREGUARD(netdev);
e9084344
YW
1156
1157 SET_FOREACH(route, w->routes) {
1158 if (only_ipv4 && route->family != AF_INET)
1159 continue;
1160
5a18697d 1161 r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
e9084344
YW
1162 if (r < 0)
1163 return r;
1164 }
1165
1166 return 0;
1167}
1168
76c5a0f2 1169int link_request_static_routes(Link *link, bool only_ipv4) {
e8f52f3c 1170 Route *route;
141318f7
YW
1171 int r;
1172
f345918d
YW
1173 assert(link);
1174 assert(link->network);
1175
76c5a0f2 1176 link->static_routes_configured = false;
e8f52f3c 1177
76c5a0f2 1178 HASHMAP_FOREACH(route, link->network->routes_by_section) {
e8f52f3c
YW
1179 if (route->gateway_from_dhcp_or_ra)
1180 continue;
1181
76c5a0f2 1182 if (only_ipv4 && route->family != AF_INET)
f345918d
YW
1183 continue;
1184
5a18697d 1185 r = link_request_route(link, route, &link->static_route_messages, static_route_handler);
f345918d 1186 if (r < 0)
76c5a0f2
YW
1187 return r;
1188 }
f345918d 1189
e9084344
YW
1190 r = link_request_wireguard_routes(link, only_ipv4);
1191 if (r < 0)
1192 return r;
1193
76c5a0f2
YW
1194 if (link->static_route_messages == 0) {
1195 link->static_routes_configured = true;
1196 link_check_ready(link);
1197 } else {
1198 log_link_debug(link, "Requesting routes");
1199 link_set_state(link, LINK_STATE_CONFIGURING);
f345918d
YW
1200 }
1201
1202 return 0;
1203}
1204
e1e4cd1e 1205void route_cancel_request(Route *route, Link *link) {
e1e4cd1e 1206 assert(route);
5a18697d
YW
1207 Manager *manager = ASSERT_PTR(route->manager ?:
1208 route->link ? route->link->manager :
1209 ASSERT_PTR(link)->manager);
e1e4cd1e
YW
1210
1211 if (!route_is_requesting(route))
1212 return;
1213
3babbdf6 1214 Request *req;
5a18697d 1215 if (route_get_request(manager, route, &req) >= 0)
b9b5096e 1216 request_detach(req);
3babbdf6 1217
e1e4cd1e
YW
1218 route_cancel_requesting(route);
1219}
1220
0b0c81bb
YW
1221static int process_route_one(
1222 Manager *manager,
0b0c81bb
YW
1223 uint16_t type,
1224 Route *in,
1225 const struct rta_cacheinfo *cacheinfo) {
1226
3b6a3bde 1227 _cleanup_(route_freep) Route *tmp = in;
5a18697d 1228 Request *req = NULL;
f9bb3338 1229 Route *route = NULL;
5a18697d 1230 Link *link = NULL;
f86575ca 1231 bool is_new = false, update_dhcp4;
f9bb3338
YW
1232 int r;
1233
1234 assert(manager);
1235 assert(tmp);
1236 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
1237
5a18697d
YW
1238 (void) route_get(manager, NULL, tmp, &route);
1239 (void) route_get_request(manager, tmp, &req);
1240 (void) route_get_link(manager, tmp, &link);
f9bb3338 1241
fc35a9f8
YW
1242 update_dhcp4 = link && tmp->family == AF_INET6 && tmp->dst_prefixlen == 0;
1243
f9bb3338
YW
1244 switch (type) {
1245 case RTM_NEWROUTE:
f86575ca 1246 if (!route) {
5a18697d 1247 if (!manager->manage_foreign_routes && !(req && req->waiting_reply)) {
f86575ca 1248 route_enter_configured(tmp);
3518ff4a 1249 log_route_debug(tmp, "Ignoring received", manager);
f86575ca
YW
1250 return 0;
1251 }
3b6a3bde 1252
f86575ca 1253 /* If we do not know the route, then save it. */
5a18697d 1254 r = route_add(manager, tmp);
3b6a3bde
YW
1255 if (r < 0) {
1256 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
1257 return 0;
f9bb3338 1258 }
f86575ca
YW
1259
1260 route = TAKE_PTR(tmp);
1261 is_new = true;
1262
1263 } else
1264 /* Update remembered route with the received notification. */
5a18697d
YW
1265 route->nexthop.weight = tmp->nexthop.weight;
1266
1267 /* Also update information that cannot be obtained through netlink notification. */
1268 if (req && req->waiting_reply) {
1269 Route *rt = ASSERT_PTR(req->userdata);
1270
1271 route->source = rt->source;
1272 route->provider = rt->provider;
1273 route->lifetime_usec = rt->lifetime_usec;
1274 }
f86575ca
YW
1275
1276 route_enter_configured(route);
3518ff4a 1277 log_route_debug(route, is_new ? "Received new" : "Received remembered", manager);
f86575ca 1278
14436bf5 1279 (void) route_setup_timer(route, cacheinfo);
f9bb3338
YW
1280
1281 break;
1282
1283 case RTM_DELROUTE:
3b6a3bde
YW
1284 if (route) {
1285 route_enter_removed(route);
5a18697d
YW
1286 log_route_debug(route, "Forgetting removed", manager);
1287 route_free(route);
3b6a3bde
YW
1288 } else
1289 log_route_debug(tmp,
1290 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received",
3518ff4a 1291 manager);
3b6a3bde 1292
5a18697d
YW
1293 if (req)
1294 route_enter_removed(req->userdata);
1295
f9bb3338
YW
1296 break;
1297
1298 default:
04499a70 1299 assert_not_reached();
f9bb3338
YW
1300 }
1301
fc35a9f8
YW
1302 if (update_dhcp4) {
1303 r = dhcp4_update_ipv6_connectivity(link);
1304 if (r < 0) {
1305 log_link_warning_errno(link, r, "Failed to notify IPv6 connectivity to DHCPv4 client: %m");
1306 link_enter_failed(link);
1307 }
1308 }
1309
f9bb3338
YW
1310 return 1;
1311}
1312
4468f01b
YW
1313int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
1314 _cleanup_(route_freep) Route *tmp = NULL;
4468f01b
YW
1315 int r;
1316
1317 assert(rtnl);
1318 assert(message);
1319 assert(m);
1320
1321 if (sd_netlink_message_is_error(message)) {
1322 r = sd_netlink_message_get_errno(message);
1323 if (r < 0)
1324 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1325
1326 return 0;
1327 }
1328
13efe0ab 1329 uint16_t type;
4468f01b
YW
1330 r = sd_netlink_message_get_type(message, &type);
1331 if (r < 0) {
1332 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1333 return 0;
1334 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1335 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1336 return 0;
1337 }
1338
4468f01b
YW
1339 r = route_new(&tmp);
1340 if (r < 0)
1341 return log_oom();
1342
00138639 1343 /* rtmsg header */
4468f01b
YW
1344 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1345 if (r < 0) {
13efe0ab 1346 log_warning_errno(r, "rtnl: received route message without family, ignoring: %m");
4468f01b
YW
1347 return 0;
1348 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
13efe0ab 1349 log_debug("rtnl: received route message with invalid family '%i', ignoring.", tmp->family);
4468f01b
YW
1350 return 0;
1351 }
1352
00138639 1353 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
4468f01b 1354 if (r < 0) {
00138639 1355 log_warning_errno(r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
4468f01b
YW
1356 return 0;
1357 }
1358
00138639 1359 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
17f8d8f9 1360 if (r < 0) {
00138639 1361 log_warning_errno(r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
17f8d8f9
YW
1362 return 0;
1363 }
1364
00138639
YW
1365 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
1366 if (r < 0) {
1367 log_warning_errno(r, "rtnl: received route message with invalid tos, ignoring: %m");
ad6df717
YW
1368 return 0;
1369 }
4468f01b 1370
00138639
YW
1371 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1372 if (r < 0) {
1373 log_warning_errno(r, "rtnl: received route message without route protocol, ignoring: %m");
ad6df717
YW
1374 return 0;
1375 }
4468f01b 1376
00138639
YW
1377 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
1378 if (r < 0) {
1379 log_warning_errno(r, "rtnl: received route message with invalid scope, ignoring: %m");
4468f01b
YW
1380 return 0;
1381 }
1382
00138639 1383 r = sd_rtnl_message_route_get_type(message, &tmp->type);
4468f01b 1384 if (r < 0) {
00138639 1385 log_warning_errno(r, "rtnl: received route message with invalid type, ignoring: %m");
4468f01b
YW
1386 return 0;
1387 }
1388
00138639 1389 r = sd_rtnl_message_route_get_flags(message, &tmp->flags);
4468f01b 1390 if (r < 0) {
00138639 1391 log_warning_errno(r, "rtnl: received route message without route flags, ignoring: %m");
4468f01b
YW
1392 return 0;
1393 }
1394
00138639
YW
1395 /* attributes */
1396 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1397 if (r < 0 && r != -ENODATA) {
1398 log_warning_errno(r, "rtnl: received route message without valid destination, ignoring: %m");
4468f01b
YW
1399 return 0;
1400 }
1401
00138639
YW
1402 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1403 if (r < 0 && r != -ENODATA) {
1404 log_warning_errno(r, "rtnl: received route message without valid source, ignoring: %m");
4468f01b
YW
1405 return 0;
1406 }
1407
00138639
YW
1408 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
1409 if (r < 0 && r != -ENODATA) {
1410 log_warning_errno(r, "rtnl: received route message with invalid priority, ignoring: %m");
1411 return 0;
1412 }
1413
1414 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
1415 if (r < 0 && r != -ENODATA) {
1416 log_warning_errno(r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
1417 return 0;
1418 }
1419
5e82a613
YW
1420 r = sd_netlink_message_read_u32(message, RTA_TABLE, &tmp->table);
1421 if (r == -ENODATA) {
f14a6e7f
YW
1422 unsigned char table;
1423
5e82a613
YW
1424 r = sd_rtnl_message_route_get_table(message, &table);
1425 if (r >= 0)
1426 tmp->table = table;
1427 }
4468f01b 1428 if (r < 0) {
13efe0ab 1429 log_warning_errno(r, "rtnl: received route message with invalid table, ignoring: %m");
4468f01b
YW
1430 return 0;
1431 }
4468f01b 1432
00138639 1433 r = sd_netlink_message_read_u8(message, RTA_PREF, &tmp->pref);
4468f01b 1434 if (r < 0 && r != -ENODATA) {
00138639 1435 log_warning_errno(r, "rtnl: received route message with invalid preference, ignoring: %m");
4468f01b
YW
1436 return 0;
1437 }
1438
54f9fba5
YW
1439 /* nexthops */
1440 if (route_nexthops_read_netlink_message(tmp, message) < 0)
324e3422 1441 return 0;
324e3422 1442
d32a520b
YW
1443 /* metrics */
1444 if (route_metric_read_netlink_message(&tmp->metric, message) < 0)
4468f01b 1445 return 0;
4468f01b 1446
13efe0ab
YW
1447 bool has_cacheinfo;
1448 struct rta_cacheinfo cacheinfo;
0b0c81bb
YW
1449 r = sd_netlink_message_read(message, RTA_CACHEINFO, sizeof(cacheinfo), &cacheinfo);
1450 if (r < 0 && r != -ENODATA) {
13efe0ab 1451 log_warning_errno(r, "rtnl: failed to read RTA_CACHEINFO attribute, ignoring: %m");
0b0c81bb
YW
1452 return 0;
1453 }
1454 has_cacheinfo = r >= 0;
1455
5a18697d
YW
1456 if (tmp->family == AF_INET || ordered_set_isempty(tmp->nexthops))
1457 return process_route_one(m, type, TAKE_PTR(tmp), has_cacheinfo ? &cacheinfo : NULL);
4468f01b 1458
5a18697d
YW
1459 RouteNextHop *nh;
1460 ORDERED_SET_FOREACH(nh, tmp->nexthops) {
1461 _cleanup_(route_freep) Route *dup = NULL;
4468f01b 1462
5a18697d
YW
1463 r = route_dup(tmp, nh, &dup);
1464 if (r < 0)
1465 return log_oom();
3b6a3bde 1466
5a18697d
YW
1467 r = process_route_one(m, type, TAKE_PTR(dup), has_cacheinfo ? &cacheinfo : NULL);
1468 if (r < 0)
1469 return r;
1470 }
3b6a3bde 1471
4468f01b
YW
1472 return 1;
1473}
1474
fa7cd711 1475int network_add_ipv4ll_route(Network *network) {
f48e52bd 1476 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
2a54a044 1477 unsigned section_line;
fa7cd711
YW
1478 int r;
1479
1480 assert(network);
1481
1482 if (!network->ipv4ll_route)
1483 return 0;
1484
d9171a23
YW
1485 r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
1486 if (r < 0)
1487 return r;
2a54a044 1488
fa7cd711 1489 /* IPv4LLRoute= is in [Network] section. */
f48e52bd 1490 r = route_new_static(network, network->filename, section_line, &route);
fa7cd711
YW
1491 if (r < 0)
1492 return r;
1493
f48e52bd 1494 r = in_addr_from_string(AF_INET, "169.254.0.0", &route->dst);
fa7cd711
YW
1495 if (r < 0)
1496 return r;
1497
f48e52bd
YW
1498 route->family = AF_INET;
1499 route->dst_prefixlen = 16;
1500 route->scope = RT_SCOPE_LINK;
1501 route->scope_set = true;
1502 route->table_set = true;
1503 route->priority = IPV4LL_ROUTE_METRIC;
1504 route->protocol = RTPROT_STATIC;
fa7cd711 1505
f48e52bd 1506 TAKE_PTR(route);
fa7cd711
YW
1507 return 0;
1508}
1509
5d5003ab 1510int network_add_default_route_on_device(Network *network) {
f48e52bd 1511 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
2a54a044 1512 unsigned section_line;
5d5003ab
YW
1513 int r;
1514
1515 assert(network);
1516
1517 if (!network->default_route_on_device)
1518 return 0;
1519
d9171a23
YW
1520 r = hashmap_by_section_find_unused_line(network->routes_by_section, network->filename, &section_line);
1521 if (r < 0)
1522 return r;
2a54a044 1523
5d5003ab 1524 /* DefaultRouteOnDevice= is in [Network] section. */
f48e52bd 1525 r = route_new_static(network, network->filename, section_line, &route);
5d5003ab
YW
1526 if (r < 0)
1527 return r;
1528
f48e52bd
YW
1529 route->family = AF_INET;
1530 route->scope = RT_SCOPE_LINK;
1531 route->scope_set = true;
1532 route->protocol = RTPROT_STATIC;
5d5003ab 1533
f48e52bd 1534 TAKE_PTR(route);
5d5003ab
YW
1535 return 0;
1536}
1537
27efb52b
YW
1538int config_parse_preferred_src(
1539 const char *unit,
0d07e595
JK
1540 const char *filename,
1541 unsigned line,
1542 const char *section,
1543 unsigned section_line,
1544 const char *lvalue,
1545 int ltype,
1546 const char *rvalue,
1547 void *data,
1548 void *userdata) {
1549
1550 Network *network = userdata;
f48e52bd 1551 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7934dede 1552 int r;
0d07e595
JK
1553
1554 assert(filename);
1555 assert(section);
1556 assert(lvalue);
1557 assert(rvalue);
1558 assert(data);
1559
f48e52bd 1560 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1561 if (r == -ENOMEM)
1562 return log_oom();
1563 if (r < 0) {
1564 log_syntax(unit, LOG_WARNING, filename, line, r,
1565 "Failed to allocate route, ignoring assignment: %m");
1566 return 0;
1567 }
0d07e595 1568
f48e52bd
YW
1569 if (route->family == AF_UNSPEC)
1570 r = in_addr_from_string_auto(rvalue, &route->family, &route->prefsrc);
01d4e732 1571 else
f48e52bd 1572 r = in_addr_from_string(route->family, rvalue, &route->prefsrc);
0d07e595 1573 if (r < 0) {
d96edb2c 1574 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1575 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1576 return 0;
1577 }
1578
f48e52bd 1579 TAKE_PTR(route);
0d07e595
JK
1580 return 0;
1581}
1582
27efb52b
YW
1583int config_parse_destination(
1584 const char *unit,
6ae115c1
TG
1585 const char *filename,
1586 unsigned line,
1587 const char *section,
1588 unsigned section_line,
1589 const char *lvalue,
1590 int ltype,
1591 const char *rvalue,
1592 void *data,
1593 void *userdata) {
44e7b949 1594
6ae115c1 1595 Network *network = userdata;
f48e52bd 1596 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7934dede
YW
1597 union in_addr_union *buffer;
1598 unsigned char *prefixlen;
ca3bad65 1599 int r;
6ae115c1
TG
1600
1601 assert(filename);
1602 assert(section);
1603 assert(lvalue);
1604 assert(rvalue);
1605 assert(data);
1606
f48e52bd 1607 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1608 if (r == -ENOMEM)
1609 return log_oom();
1610 if (r < 0) {
1611 log_syntax(unit, LOG_WARNING, filename, line, r,
1612 "Failed to allocate route, ignoring assignment: %m");
1613 return 0;
1614 }
6ae115c1 1615
9e7e4408 1616 if (streq(lvalue, "Destination")) {
f48e52bd
YW
1617 buffer = &route->dst;
1618 prefixlen = &route->dst_prefixlen;
9e7e4408 1619 } else if (streq(lvalue, "Source")) {
f48e52bd
YW
1620 buffer = &route->src;
1621 prefixlen = &route->src_prefixlen;
9e7e4408 1622 } else
04499a70 1623 assert_not_reached();
9e7e4408 1624
f48e52bd
YW
1625 if (route->family == AF_UNSPEC)
1626 r = in_addr_prefix_from_string_auto(rvalue, &route->family, buffer, prefixlen);
01d4e732 1627 else
f48e52bd 1628 r = in_addr_prefix_from_string(rvalue, route->family, buffer, prefixlen);
7934dede 1629 if (r < 0) {
d96edb2c 1630 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1631 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1632 return 0;
1633 }
1634
f48e52bd 1635 (void) in_addr_mask(route->family, buffer, *prefixlen);
72fa1923 1636
f48e52bd 1637 TAKE_PTR(route);
6ae115c1
TG
1638 return 0;
1639}
5d8e593d 1640
27efb52b
YW
1641int config_parse_route_priority(
1642 const char *unit,
1643 const char *filename,
1644 unsigned line,
1645 const char *section,
1646 unsigned section_line,
1647 const char *lvalue,
1648 int ltype,
1649 const char *rvalue,
1650 void *data,
1651 void *userdata) {
1652
5d8e593d 1653 Network *network = userdata;
f48e52bd 1654 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
5d8e593d
SS
1655 int r;
1656
1657 assert(filename);
1658 assert(section);
1659 assert(lvalue);
1660 assert(rvalue);
1661 assert(data);
1662
f48e52bd 1663 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1664 if (r == -ENOMEM)
1665 return log_oom();
1666 if (r < 0) {
1667 log_syntax(unit, LOG_WARNING, filename, line, r,
1668 "Failed to allocate route, ignoring assignment: %m");
1669 return 0;
1670 }
5d8e593d 1671
f48e52bd 1672 r = safe_atou32(rvalue, &route->priority);
1c4b1179 1673 if (r < 0) {
d96edb2c 1674 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1675 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1676 return 0;
1677 }
5d8e593d 1678
f48e52bd
YW
1679 route->priority_set = true;
1680 TAKE_PTR(route);
5d8e593d
SS
1681 return 0;
1682}
769b56a3 1683
27efb52b
YW
1684int config_parse_route_scope(
1685 const char *unit,
1686 const char *filename,
1687 unsigned line,
1688 const char *section,
1689 unsigned section_line,
1690 const char *lvalue,
1691 int ltype,
1692 const char *rvalue,
1693 void *data,
1694 void *userdata) {
1695
769b56a3 1696 Network *network = userdata;
f48e52bd 1697 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
769b56a3
TG
1698 int r;
1699
1700 assert(filename);
1701 assert(section);
1702 assert(lvalue);
1703 assert(rvalue);
1704 assert(data);
1705
f48e52bd 1706 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1707 if (r == -ENOMEM)
1708 return log_oom();
1709 if (r < 0) {
1710 log_syntax(unit, LOG_WARNING, filename, line, r,
1711 "Failed to allocate route, ignoring assignment: %m");
1712 return 0;
1713 }
769b56a3 1714
41b90a1e
YW
1715 r = route_scope_from_string(rvalue);
1716 if (r < 0) {
b98680b2 1717 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
1718 return 0;
1719 }
1720
f48e52bd
YW
1721 route->scope = r;
1722 route->scope_set = true;
1723 TAKE_PTR(route);
769b56a3
TG
1724 return 0;
1725}
c953b24c 1726
27efb52b
YW
1727int config_parse_route_table(
1728 const char *unit,
1729 const char *filename,
1730 unsigned line,
1731 const char *section,
1732 unsigned section_line,
1733 const char *lvalue,
1734 int ltype,
1735 const char *rvalue,
1736 void *data,
1737 void *userdata) {
1738
f48e52bd 1739 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
c953b24c 1740 Network *network = userdata;
c953b24c
SS
1741 int r;
1742
1743 assert(filename);
1744 assert(section);
1745 assert(lvalue);
1746 assert(rvalue);
1747 assert(data);
1748
f48e52bd 1749 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1750 if (r == -ENOMEM)
1751 return log_oom();
1752 if (r < 0) {
1753 log_syntax(unit, LOG_WARNING, filename, line, r,
1754 "Failed to allocate route, ignoring assignment: %m");
1755 return 0;
1756 }
c953b24c 1757
f48e52bd 1758 r = manager_get_route_table_from_string(network->manager, rvalue, &route->table);
c038ce46
SS
1759 if (r < 0) {
1760 log_syntax(unit, LOG_WARNING, filename, line, r,
29de4f73 1761 "Could not parse route table \"%s\", ignoring assignment: %m", rvalue);
c038ce46 1762 return 0;
c953b24c
SS
1763 }
1764
f48e52bd
YW
1765 route->table_set = true;
1766 TAKE_PTR(route);
c953b24c
SS
1767 return 0;
1768}
28959f7d 1769
27efb52b
YW
1770int config_parse_ipv6_route_preference(
1771 const char *unit,
1772 const char *filename,
1773 unsigned line,
1774 const char *section,
1775 unsigned section_line,
1776 const char *lvalue,
1777 int ltype,
1778 const char *rvalue,
1779 void *data,
1780 void *userdata) {
1781
b5bf6f64 1782 Network *network = userdata;
f48e52bd 1783 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
b5bf6f64
SS
1784 int r;
1785
f48e52bd 1786 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1787 if (r == -ENOMEM)
1788 return log_oom();
1789 if (r < 0) {
1790 log_syntax(unit, LOG_WARNING, filename, line, r,
1791 "Failed to allocate route, ignoring assignment: %m");
1792 return 0;
1793 }
4c7bd9cf 1794
b5bf6f64 1795 if (streq(rvalue, "low"))
f48e52bd 1796 route->pref = ICMPV6_ROUTER_PREF_LOW;
b5bf6f64 1797 else if (streq(rvalue, "medium"))
f48e52bd 1798 route->pref = ICMPV6_ROUTER_PREF_MEDIUM;
b5bf6f64 1799 else if (streq(rvalue, "high"))
f48e52bd 1800 route->pref = ICMPV6_ROUTER_PREF_HIGH;
b5bf6f64 1801 else {
d96edb2c 1802 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
1803 return 0;
1804 }
28959f7d 1805
f48e52bd
YW
1806 route->pref_set = true;
1807 TAKE_PTR(route);
28959f7d
SS
1808 return 0;
1809}
c83ecc04 1810
27efb52b
YW
1811int config_parse_route_protocol(
1812 const char *unit,
1813 const char *filename,
1814 unsigned line,
1815 const char *section,
1816 unsigned section_line,
1817 const char *lvalue,
1818 int ltype,
1819 const char *rvalue,
1820 void *data,
1821 void *userdata) {
1822
c83ecc04 1823 Network *network = userdata;
f48e52bd 1824 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
c83ecc04
SS
1825 int r;
1826
f48e52bd 1827 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1828 if (r == -ENOMEM)
1829 return log_oom();
1830 if (r < 0) {
1831 log_syntax(unit, LOG_WARNING, filename, line, r,
1832 "Failed to allocate route, ignoring assignment: %m");
1833 return 0;
1834 }
c83ecc04 1835
1b64651b 1836 r = route_protocol_from_string(rvalue);
e4ffe103
YW
1837 if (r < 0) {
1838 log_syntax(unit, LOG_WARNING, filename, line, r,
1839 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
1840 return 0;
c83ecc04
SS
1841 }
1842
f48e52bd 1843 route->protocol = r;
e4ffe103 1844
f48e52bd 1845 TAKE_PTR(route);
c83ecc04
SS
1846 return 0;
1847}
983226f3 1848
27efb52b
YW
1849int config_parse_route_type(
1850 const char *unit,
1851 const char *filename,
1852 unsigned line,
1853 const char *section,
1854 unsigned section_line,
1855 const char *lvalue,
1856 int ltype,
1857 const char *rvalue,
1858 void *data,
1859 void *userdata) {
1860
983226f3 1861 Network *network = userdata;
f48e52bd 1862 _cleanup_(route_free_or_set_invalidp) Route *route = NULL;
7a22312d 1863 int t, r;
983226f3 1864
f48e52bd 1865 r = route_new_static(network, filename, section_line, &route);
d96edb2c
YW
1866 if (r == -ENOMEM)
1867 return log_oom();
1868 if (r < 0) {
1869 log_syntax(unit, LOG_WARNING, filename, line, r,
1870 "Failed to allocate route, ignoring assignment: %m");
1871 return 0;
1872 }
983226f3 1873
7a22312d
YW
1874 t = route_type_from_string(rvalue);
1875 if (t < 0) {
b98680b2 1876 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 1877 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
1878 return 0;
1879 }
1880
f48e52bd 1881 route->type = (unsigned char) t;
7a22312d 1882
f48e52bd 1883 TAKE_PTR(route);
983226f3
SS
1884 return 0;
1885}
323d9329 1886
4db8ccbb 1887int route_section_verify(Route *route) {
0db96961
YW
1888 int r;
1889
1890 assert(route);
1891 assert(route->section);
0db96961 1892
fcbf4cb7
YW
1893 if (section_is_invalid(route->section))
1894 return -EINVAL;
1895
3b6a3bde 1896 /* Currently, we do not support static route with finite lifetime. */
91fc5135 1897 assert(route->lifetime_usec == USEC_INFINITY);
3b6a3bde 1898
0db96961
YW
1899 r = route_section_verify_nexthops(route);
1900 if (r < 0)
1901 return r;
6dd53981 1902
0db96961 1903 /* table */
4db8ccbb 1904 if (!route->table_set && route->network && route->network->vrf) {
0db96961 1905 route->table = VRF(route->network->vrf)->table;
c0d48bc5
YW
1906 route->table_set = true;
1907 }
1908
f5c38922
YW
1909 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
1910 route->table = RT_TABLE_LOCAL;
1911
0db96961
YW
1912 /* scope */
1913 if (!route->scope_set && route->family == AF_INET) {
f5c38922
YW
1914 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
1915 route->scope = RT_SCOPE_HOST;
1916 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
1917 route->scope = RT_SCOPE_LINK;
902bbdc4
YW
1918 else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) &&
1919 !route->gateway_from_dhcp_or_ra &&
054b8c28 1920 !in_addr_is_set(route->nexthop.family, &route->nexthop.gw) &&
fea879cd 1921 ordered_set_isempty(route->nexthops) &&
902bbdc4
YW
1922 route->nexthop_id == 0)
1923 route->scope = RT_SCOPE_LINK;
94d6e299
YW
1924 }
1925
0db96961 1926 /* IPv6 route */
40785f53 1927 if (route->family == AF_INET6) {
0db96961
YW
1928 if (route->scope != RT_SCOPE_UNIVERSE) {
1929 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
1930 route->scope = RT_SCOPE_UNIVERSE;
1931 }
40785f53 1932
0db96961
YW
1933 if (route->priority == 0)
1934 route->priority = IP6_RT_PRIO_USER;
1935 }
324e3422 1936
fcbf4cb7
YW
1937 return 0;
1938}
d9940a3f 1939
13ffa39f 1940void network_drop_invalid_routes(Network *network) {
2a54a044 1941 Route *route;
d9940a3f
YW
1942
1943 assert(network);
1944
2a54a044 1945 HASHMAP_FOREACH(route, network->routes_by_section)
0db96961 1946 if (route_section_verify(route) < 0)
d9940a3f
YW
1947 route_free(route);
1948}