]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
Merge pull request #20789 from poettering/initrd-cpio
[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"
fc2f9534 8#include "netlink-util.h"
ca5ad760 9#include "networkd-ipv4ll.h"
23f53b99 10#include "networkd-manager.h"
e2263711 11#include "networkd-network.h"
141318f7 12#include "networkd-nexthop.h"
76c5a0f2 13#include "networkd-queue.h"
6bedfcbb 14#include "networkd-route.h"
6bedfcbb 15#include "parse-util.h"
7a22312d 16#include "string-table.h"
07630cea 17#include "string-util.h"
d96edb2c 18#include "strv.h"
b297e0a7 19#include "strxcpyx.h"
47d2d30d 20#include "sysctl-util.h"
c0d48bc5 21#include "vrf.h"
f579559b 22
47d2d30d
ZJS
23#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
24
74154c2e
YW
25static const char * const route_type_table[__RTN_MAX] = {
26 [RTN_UNICAST] = "unicast",
27 [RTN_LOCAL] = "local",
28 [RTN_BROADCAST] = "broadcast",
29 [RTN_ANYCAST] = "anycast",
30 [RTN_MULTICAST] = "multicast",
31 [RTN_BLACKHOLE] = "blackhole",
32 [RTN_UNREACHABLE] = "unreachable",
33 [RTN_PROHIBIT] = "prohibit",
34 [RTN_THROW] = "throw",
35 [RTN_NAT] = "nat",
36 [RTN_XRESOLVE] = "xresolve",
37};
38
39assert_cc(__RTN_MAX <= UCHAR_MAX);
40DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_type, int);
41
42static const char * const route_scope_table[] = {
43 [RT_SCOPE_UNIVERSE] = "global",
44 [RT_SCOPE_SITE] = "site",
45 [RT_SCOPE_LINK] = "link",
46 [RT_SCOPE_HOST] = "host",
47 [RT_SCOPE_NOWHERE] = "nowhere",
48};
49
e4ffe103
YW
50DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_scope, int);
51DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_scope, int, UINT8_MAX);
74154c2e
YW
52
53static const char * const route_table_table[] = {
54 [RT_TABLE_DEFAULT] = "default",
55 [RT_TABLE_MAIN] = "main",
56 [RT_TABLE_LOCAL] = "local",
57};
58
552b90a2 59DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
74154c2e 60
552b90a2
YW
61int manager_get_route_table_from_string(const Manager *m, const char *s, uint32_t *ret) {
62 uint32_t t;
63 int r;
64
65 assert(m);
66 assert(s);
67 assert(ret);
68
69 r = route_table_from_string(s);
70 if (r >= 0) {
71 *ret = (uint32_t) r;
72 return 0;
73 }
74
75 t = PTR_TO_UINT32(hashmap_get(m->route_table_numbers_by_name, s));
76 if (t != 0) {
77 *ret = t;
78 return 0;
79 }
80
81 r = safe_atou32(s, &t);
82 if (r < 0)
83 return r;
84
85 if (t == 0)
86 return -ERANGE;
87
88 *ret = t;
89 return 0;
90}
91
92int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret) {
93 _cleanup_free_ char *str = NULL;
74154c2e 94 const char *s;
ae732f6e 95 int r;
552b90a2
YW
96
97 assert(m);
98 assert(ret);
99
100 if (table == 0)
101 return -EINVAL;
74154c2e
YW
102
103 s = route_table_to_string(table);
552b90a2
YW
104 if (!s)
105 s = hashmap_get(m->route_table_names_by_number, UINT32_TO_PTR(table));
74154c2e 106
ae732f6e 107 if (s)
552b90a2
YW
108 /* Currently, this is only used in debugging logs. To not confuse any bug
109 * reports, let's include the table number. */
ae732f6e
ZJS
110 r = asprintf(&str, "%s(%" PRIu32 ")", s, table);
111 else
112 r = asprintf(&str, "%" PRIu32, table);
113 if (r < 0)
552b90a2
YW
114 return -ENOMEM;
115
116 *ret = TAKE_PTR(str);
117 return 0;
74154c2e
YW
118}
119
120static const char * const route_protocol_table[] = {
121 [RTPROT_KERNEL] = "kernel",
122 [RTPROT_BOOT] = "boot",
123 [RTPROT_STATIC] = "static",
124};
125
e4ffe103 126DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(route_protocol, int, UINT8_MAX);
74154c2e
YW
127
128static const char * const route_protocol_full_table[] = {
129 [RTPROT_REDIRECT] = "redirect",
130 [RTPROT_KERNEL] = "kernel",
131 [RTPROT_BOOT] = "boot",
132 [RTPROT_STATIC] = "static",
133 [RTPROT_GATED] = "gated",
134 [RTPROT_RA] = "ra",
135 [RTPROT_MRT] = "mrt",
136 [RTPROT_ZEBRA] = "zebra",
137 [RTPROT_BIRD] = "bird",
138 [RTPROT_DNROUTED] = "dnrouted",
139 [RTPROT_XORP] = "xorp",
140 [RTPROT_NTK] = "ntk",
141 [RTPROT_DHCP] = "dhcp",
142 [RTPROT_MROUTED] = "mrouted",
143 [RTPROT_BABEL] = "babel",
144 [RTPROT_BGP] = "bgp",
145 [RTPROT_ISIS] = "isis",
146 [RTPROT_OSPF] = "ospf",
147 [RTPROT_RIP] = "rip",
148 [RTPROT_EIGRP] = "eigrp",
149};
150
e4ffe103 151DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_protocol_full, int, UINT8_MAX);
74154c2e 152
47d2d30d
ZJS
153static unsigned routes_max(void) {
154 static thread_local unsigned cached = 0;
47d2d30d
ZJS
155 _cleanup_free_ char *s4 = NULL, *s6 = NULL;
156 unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
157
158 if (cached > 0)
159 return cached;
160
778c8795
YW
161 if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0)
162 if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U)
47d2d30d
ZJS
163 /* This is the default "no limit" value in the kernel */
164 val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
47d2d30d 165
778c8795 166 if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0)
47d2d30d 167 (void) safe_atou(s6, &val6);
47d2d30d
ZJS
168
169 cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
170 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
171 return cached;
172}
8c34b963 173
ed9e361a 174int route_new(Route **ret) {
8e766630 175 _cleanup_(route_freep) Route *route = NULL;
f0213e37 176
17f9c355 177 route = new(Route, 1);
f0213e37
TG
178 if (!route)
179 return -ENOMEM;
180
17f9c355
YW
181 *route = (Route) {
182 .family = AF_UNSPEC,
183 .scope = RT_SCOPE_UNIVERSE,
184 .protocol = RTPROT_UNSPEC,
185 .type = RTN_UNICAST,
186 .table = RT_TABLE_MAIN,
187 .lifetime = USEC_INFINITY,
188 .quickack = -1,
633c7258 189 .fast_open_no_cookie = -1,
54901fd2 190 .gateway_onlink = -1,
9b88f20a 191 .ttl_propagate = -1,
17f9c355 192 };
f0213e37 193
1cc6c93a 194 *ret = TAKE_PTR(route);
f0213e37
TG
195
196 return 0;
197}
198
9560e5b3 199static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
8e766630
LP
200 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
201 _cleanup_(route_freep) Route *route = NULL;
f0213e37 202 int r;
f579559b 203
8c34b963
LP
204 assert(network);
205 assert(ret);
2a54a044
YW
206 assert(filename);
207 assert(section_line > 0);
f4859fc7 208
2a54a044
YW
209 r = network_config_section_new(filename, section_line, &n);
210 if (r < 0)
211 return r;
6ae115c1 212
2a54a044
YW
213 route = hashmap_get(network->routes_by_section, n);
214 if (route) {
215 *ret = TAKE_PTR(route);
216 return 0;
6ae115c1
TG
217 }
218
2a54a044 219 if (hashmap_size(network->routes_by_section) >= routes_max())
8c34b963
LP
220 return -E2BIG;
221
ed9e361a 222 r = route_new(&route);
f0213e37
TG
223 if (r < 0)
224 return r;
801bd9e8 225
ed9e361a 226 route->protocol = RTPROT_STATIC;
0f7f2769 227 route->network = network;
2a54a044 228 route->section = TAKE_PTR(n);
cacc1dbf 229
ecd80ce2 230 r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
2a54a044
YW
231 if (r < 0)
232 return r;
6ae115c1 233
1cc6c93a 234 *ret = TAKE_PTR(route);
f579559b
TG
235 return 0;
236}
237
169948e9 238Route *route_free(Route *route) {
f579559b 239 if (!route)
169948e9 240 return NULL;
f579559b 241
f048a16b 242 if (route->network) {
2a54a044
YW
243 assert(route->section);
244 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 245 }
6ae115c1 246
fd45e522
ZJS
247 network_config_section_free(route->section);
248
1c8e710c 249 if (route->link) {
50550722 250 NDiscRoute *n;
50550722 251
1c8e710c
TG
252 set_remove(route->link->routes, route);
253 set_remove(route->link->routes_foreign, route);
6e537f62
YW
254 set_remove(route->link->dhcp_routes, route);
255 set_remove(route->link->dhcp_routes_old, route);
1633c457
YW
256 set_remove(route->link->dhcp6_routes, route);
257 set_remove(route->link->dhcp6_routes_old, route);
258 set_remove(route->link->dhcp6_pd_routes, route);
259 set_remove(route->link->dhcp6_pd_routes_old, route);
90e74a66 260 SET_FOREACH(n, route->link->ndisc_routes)
10e417b3 261 if (route_equal(n->route, route))
50550722 262 free(set_remove(route->link->ndisc_routes, n));
1c8e710c
TG
263 }
264
ad208fac
YW
265 if (route->manager) {
266 set_remove(route->manager->routes, route);
267 set_remove(route->manager->routes_foreign, route);
268 }
269
e8f52f3c 270 ordered_set_free_with_destructor(route->multipath_routes, multipath_route_free);
6ff5cc6b 271
f833694d
TG
272 sd_event_source_unref(route->expire);
273
169948e9 274 return mfree(route);
f579559b
TG
275}
276
501b09db 277void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
278 assert(route);
279
280 siphash24_compress(&route->family, sizeof(route->family), state);
281
01aaa3df
YW
282 switch (route->family) {
283 case AF_INET:
284 case AF_INET6:
01aaa3df 285 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
67e05dd8
ZJS
286 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
287
01aaa3df 288 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
67e05dd8
ZJS
289 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
290
6dd53981 291 siphash24_compress(&route->gw_family, sizeof(route->gw_family), state);
40075951 292 if (IN_SET(route->gw_family, AF_INET, AF_INET6)) {
6dd53981 293 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->gw_family), state);
40075951
YW
294 siphash24_compress(&route->gw_weight, sizeof(route->gw_weight), state);
295 }
67e05dd8 296
01aaa3df
YW
297 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
298
299 siphash24_compress(&route->tos, sizeof(route->tos), state);
300 siphash24_compress(&route->priority, sizeof(route->priority), state);
301 siphash24_compress(&route->table, sizeof(route->table), state);
302 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
303 siphash24_compress(&route->scope, sizeof(route->scope), state);
304 siphash24_compress(&route->type, sizeof(route->type), state);
67e05dd8 305
fa3e401a
YW
306 siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
307 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
01aaa3df 308
007cac09 309 siphash24_compress(&route->advmss, sizeof(route->advmss), state);
324e3422 310 siphash24_compress(&route->nexthop_id, sizeof(route->nexthop_id), state);
007cac09 311
01aaa3df
YW
312 break;
313 default:
314 /* treat any other address family as AF_UNSPEC */
315 break;
316 }
317}
318
501b09db 319int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
320 int r;
321
322 r = CMP(a->family, b->family);
323 if (r != 0)
324 return r;
325
326 switch (a->family) {
327 case AF_INET:
328 case AF_INET6:
329 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
330 if (r != 0)
331 return r;
332
67e05dd8
ZJS
333 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
334 if (r != 0)
335 return r;
336
01aaa3df
YW
337 r = CMP(a->src_prefixlen, b->src_prefixlen);
338 if (r != 0)
339 return r;
340
67e05dd8 341 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
342 if (r != 0)
343 return r;
344
6dd53981 345 r = CMP(a->gw_family, b->gw_family);
01aaa3df
YW
346 if (r != 0)
347 return r;
348
6dd53981
YW
349 if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
350 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
351 if (r != 0)
352 return r;
40075951
YW
353
354 r = CMP(a->gw_weight, b->gw_weight);
355 if (r != 0)
356 return r;
6dd53981
YW
357 }
358
67e05dd8 359 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
360 if (r != 0)
361 return r;
362
67e05dd8 363 r = CMP(a->tos, b->tos);
01aaa3df
YW
364 if (r != 0)
365 return r;
366
67e05dd8 367 r = CMP(a->priority, b->priority);
01aaa3df
YW
368 if (r != 0)
369 return r;
370
67e05dd8 371 r = CMP(a->table, b->table);
01aaa3df
YW
372 if (r != 0)
373 return r;
374
67e05dd8 375 r = CMP(a->protocol, b->protocol);
fa3e401a
YW
376 if (r != 0)
377 return r;
378
67e05dd8 379 r = CMP(a->scope, b->scope);
fa3e401a
YW
380 if (r != 0)
381 return r;
382
67e05dd8 383 r = CMP(a->type, b->type);
01aaa3df
YW
384 if (r != 0)
385 return r;
386
67e05dd8 387 r = CMP(a->initcwnd, b->initcwnd);
01aaa3df
YW
388 if (r != 0)
389 return r;
390
67e05dd8 391 r = CMP(a->initrwnd, b->initrwnd);
01aaa3df
YW
392 if (r != 0)
393 return r;
394
007cac09
SS
395 r = CMP(a->advmss, b->advmss);
396 if (r != 0)
397 return r;
398
324e3422
YW
399 r = CMP(a->nexthop_id, b->nexthop_id);
400 if (r != 0)
401 return r;
402
67e05dd8 403 return 0;
01aaa3df
YW
404 default:
405 /* treat any other address family as AF_UNSPEC */
406 return 0;
407 }
408}
409
410DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 411 route_hash_ops,
01aaa3df 412 Route,
c077a205
YW
413 route_hash_func,
414 route_compare_func,
01aaa3df
YW
415 route_free);
416
10e417b3 417bool route_equal(const Route *r1, const Route *r2) {
7ecf0c3e
TJ
418 if (r1 == r2)
419 return true;
420
421 if (!r1 || !r2)
422 return false;
423
424 return route_compare_func(r1, r2) == 0;
425}
426
423c249c 427static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) {
f1368755 428 Route *existing;
1b566071 429
ad208fac 430 assert(manager || link);
f1368755 431 assert(in);
1c8e710c 432
484555f3
YW
433 existing = set_get(link ? link->routes : manager->routes, in);
434 if (existing) {
435 if (ret)
436 *ret = existing;
437 return 1;
438 }
ad208fac 439
484555f3
YW
440 existing = set_get(link ? link->routes_foreign : manager->routes_foreign, in);
441 if (existing) {
442 if (ret)
443 *ret = existing;
444 return 0;
1b566071 445 }
1c8e710c 446
1b566071 447 return -ENOENT;
1c8e710c
TG
448}
449
228c3e21 450static void route_copy(Route *dest, const Route *src, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight) {
f9bb3338
YW
451 assert(dest);
452 assert(src);
453
484555f3
YW
454 /* This only copies entries used by the above hash and compare functions. */
455
f9bb3338
YW
456 dest->family = src->family;
457 dest->src = src->src;
458 dest->src_prefixlen = src->src_prefixlen;
459 dest->dst = src->dst;
460 dest->dst_prefixlen = src->dst_prefixlen;
461 dest->prefsrc = src->prefsrc;
462 dest->scope = src->scope;
463 dest->protocol = src->protocol;
30f10837
YW
464 if (nh && nh->blackhole)
465 dest->type = RTN_BLACKHOLE;
466 else
467 dest->type = src->type;
f9bb3338
YW
468 dest->tos = src->tos;
469 dest->priority = src->priority;
470 dest->table = src->table;
471 dest->initcwnd = src->initcwnd;
472 dest->initrwnd = src->initrwnd;
473 dest->lifetime = src->lifetime;
324e3422
YW
474 dest->advmss = src->advmss;
475 dest->nexthop_id = src->nexthop_id;
f9bb3338 476
324e3422 477 if (nh) {
228c3e21
YW
478 assert(hashmap_isempty(nh->group));
479
324e3422
YW
480 dest->gw_family = nh->family;
481 dest->gw = nh->gw;
228c3e21 482 dest->gw_weight = nh_weight != UINT8_MAX ? nh_weight : src->gw_weight;
324e3422 483 } else if (m) {
f9bb3338
YW
484 dest->gw_family = m->gateway.family;
485 dest->gw = m->gateway.address;
486 dest->gw_weight = m->weight;
487 } else {
488 dest->gw_family = src->gw_family;
489 dest->gw = src->gw;
40075951 490 dest->gw_weight = src->gw_weight;
f9bb3338
YW
491 }
492}
889b550f 493
c0bd9eb1
YW
494int route_dup(const Route *src, Route **ret) {
495 _cleanup_(route_freep) Route *dest = NULL;
496 MultipathRoute *m;
497 int r;
498
499 assert(src);
500 assert(ret);
501
502 dest = newdup(Route, src, 1);
503 if (!dest)
504 return -ENOMEM;
505
506 /* Unset all pointers */
507 dest->network = NULL;
508 dest->section = NULL;
509 dest->link = NULL;
510 dest->manager = NULL;
511 dest->multipath_routes = NULL;
512 dest->expire = NULL;
513
514 ORDERED_SET_FOREACH(m, src->multipath_routes) {
515 _cleanup_(multipath_route_freep) MultipathRoute *n = NULL;
516
517 r = multipath_route_dup(m, &n);
518 if (r < 0)
519 return r;
520
521 r = ordered_set_ensure_put(&dest->multipath_routes, NULL, n);
522 if (r < 0)
523 return r;
524
525 TAKE_PTR(n);
526 }
527
528 *ret = TAKE_PTR(dest);
529 return 0;
530}
531
cc17f75f 532static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
8e766630 533 _cleanup_(route_freep) Route *route = NULL;
1c8e710c
TG
534 int r;
535
ad208fac 536 assert(manager || link);
1c8e710c 537 assert(routes);
f1368755 538 assert(in);
1c8e710c
TG
539
540 r = route_new(&route);
541 if (r < 0)
542 return r;
543
228c3e21 544 route_copy(route, in, NULL, NULL, UINT8_MAX);
1c8e710c 545
de7fef4b 546 r = set_ensure_put(routes, &route_hash_ops, route);
1c8e710c
TG
547 if (r < 0)
548 return r;
75a302b5
YW
549 if (r == 0)
550 return -EEXIST;
1c8e710c
TG
551
552 route->link = link;
ad208fac 553 route->manager = manager;
1c8e710c
TG
554
555 if (ret)
556 *ret = route;
557
558 route = NULL;
559
560 return 0;
561}
562
423c249c 563static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
ad208fac 564 assert(manager || link);
cc17f75f 565 return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
1c8e710c
TG
566}
567
228c3e21 568static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
cc17f75f 569 _cleanup_(route_freep) Route *tmp = NULL;
1c8e710c
TG
570 Route *route;
571 int r;
572
ad208fac
YW
573 assert(manager || link);
574 assert(in);
575
324e3422 576 if (nh) {
228c3e21
YW
577 assert(hashmap_isempty(nh->group));
578
324e3422
YW
579 r = route_new(&tmp);
580 if (r < 0)
581 return r;
582
228c3e21 583 route_copy(tmp, in, NULL, nh, nh_weight);
324e3422
YW
584 in = tmp;
585 } else if (m) {
cc17f75f
YW
586 assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
587
588 r = route_new(&tmp);
589 if (r < 0)
590 return r;
591
228c3e21 592 route_copy(tmp, in, m, NULL, UINT8_MAX);
cc17f75f
YW
593 in = tmp;
594 }
595
ad208fac 596 r = route_get(manager, link, in, &route);
1c8e710c
TG
597 if (r == -ENOENT) {
598 /* Route does not exist, create a new one */
cc17f75f 599 r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
1c8e710c
TG
600 if (r < 0)
601 return r;
602 } else if (r == 0) {
603 /* Take over a foreign route */
484555f3
YW
604 r = set_ensure_put(link ? &link->routes : &manager->routes, &route_hash_ops, route);
605 if (r < 0)
606 return r;
ad208fac 607
484555f3 608 set_remove(link ? link->routes_foreign : manager->routes_foreign, route);
1c8e710c
TG
609 } else if (r == 1) {
610 /* Route exists, do nothing */
611 ;
612 } else
613 return r;
614
856e309d
MC
615 if (ret)
616 *ret = route;
7b3a7581 617 return 0;
1c8e710c
TG
618}
619
6c252588
YW
620static bool route_type_is_reject(const Route *route) {
621 assert(route);
622
623 return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
624}
625
228c3e21
YW
626static int link_has_route_one(Link *link, const Route *route, const NextHop *nh, uint8_t nh_weight) {
627 _cleanup_(route_freep) Route *tmp = NULL;
628 int r;
629
630 assert(link);
631 assert(route);
632 assert(nh);
633
634 r = route_new(&tmp);
635 if (r < 0)
636 return r;
637
638 route_copy(tmp, route, NULL, nh, nh_weight);
639
640 if (route_type_is_reject(route) || (nh && nh->blackhole))
641 return route_get(link->manager, NULL, tmp, NULL) >= 0;
642 else
643 return route_get(NULL, link, tmp, NULL) >= 0;
644}
645
884a63d7
YW
646int link_has_route(Link *link, const Route *route) {
647 MultipathRoute *m;
648 int r;
649
650 assert(link);
651 assert(route);
652
653 if (route->nexthop_id > 0) {
228c3e21 654 struct nexthop_grp *nhg;
884a63d7
YW
655 NextHop *nh;
656
657 if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
658 return false;
659
228c3e21
YW
660 if (hashmap_isempty(nh->group))
661 return link_has_route_one(link, route, nh, UINT8_MAX);
884a63d7 662
228c3e21
YW
663 HASHMAP_FOREACH(nhg, nh->group) {
664 NextHop *h;
884a63d7 665
228c3e21
YW
666 if (manager_get_nexthop_by_id(link->manager, nhg->id, &h) < 0)
667 return false;
668
669 r = link_has_route_one(link, route, h, nhg->weight);
670 if (r <= 0)
671 return r;
672 }
673
674 return true;
884a63d7
YW
675 }
676
677 if (ordered_set_isempty(route->multipath_routes)) {
678 if (route_type_is_reject(route))
679 return route_get(link->manager, NULL, route, NULL) >= 0;
680 else
681 return route_get(NULL, link, route, NULL) >= 0;
682 }
683
684 ORDERED_SET_FOREACH(m, route->multipath_routes) {
685 _cleanup_(route_freep) Route *tmp = NULL;
686 Link *l;
687
688 if (m->ifname) {
f0ad7aed 689 if (link_get_by_name(link->manager, m->ifname, &l) < 0)
884a63d7 690 return false;
884a63d7 691
f0ad7aed 692 m->ifindex = l->ifindex;
884a63d7
YW
693 } else
694 l = link;
695
696 r = route_new(&tmp);
697 if (r < 0)
698 return r;
699
228c3e21 700 route_copy(tmp, route, m, NULL, UINT8_MAX);
884a63d7
YW
701
702 if (route_get(NULL, l, tmp, NULL) < 0)
703 return false;
704 }
705
706 return true;
707}
708
d94e8ba0
YW
709static bool route_address_is_reachable(const Route *route, int family, const union in_addr_union *address) {
710 assert(route);
711 assert(IN_SET(family, AF_INET, AF_INET6));
712 assert(address);
713
714 if (route->family != family)
715 return false;
716
717 if (!in_addr_is_set(route->family, &route->dst))
718 return false;
719
720 return in_addr_prefix_intersect(
721 route->family,
722 &route->dst,
723 route->dst_prefixlen,
724 address,
725 FAMILY_ADDRESS_SIZE(family) * 8) > 0;
726}
727
11046cea
YW
728static bool prefix_route_address_is_reachable(const Address *a, int family, const union in_addr_union *address) {
729 assert(a);
730 assert(IN_SET(family, AF_INET, AF_INET6));
731 assert(address);
732
733 if (a->family != family)
734 return false;
3333350a
YW
735 if (!address_is_ready(a))
736 return false;
11046cea
YW
737 if (FLAGS_SET(a->flags, IFA_F_NOPREFIXROUTE))
738 return false;
739 if (in_addr_is_set(a->family, &a->in_addr_peer))
740 return false;
741
742 return in_addr_prefix_intersect(
743 family,
744 &a->in_addr,
745 a->prefixlen,
746 address,
747 FAMILY_ADDRESS_SIZE(family) * 8) > 0;
748}
749
3333350a
YW
750static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) {
751 Route *route;
d94e8ba0 752
3333350a
YW
753 assert(link);
754 assert(link->manager);
d94e8ba0
YW
755 assert(IN_SET(family, AF_INET, AF_INET6));
756 assert(address);
757
d94e8ba0 758
3333350a
YW
759 SET_FOREACH(route, link->routes)
760 if (route_address_is_reachable(route, family, address))
761 return true;
762 SET_FOREACH(route, link->routes_foreign)
763 if (route_address_is_reachable(route, family, address))
764 return true;
d94e8ba0 765
11046cea
YW
766 /* If we do not manage foreign routes, then there may exist a prefix route we do not know,
767 * which was created on configuring an address. Hence, also check the addresses. */
3333350a
YW
768 if (!link->manager->manage_foreign_routes) {
769 Address *a;
770
771 SET_FOREACH(a, link->addresses)
772 if (prefix_route_address_is_reachable(a, family, address))
773 return true;
774 SET_FOREACH(a, link->addresses_foreign)
775 if (prefix_route_address_is_reachable(a, family, address))
776 return true;
777 }
11046cea 778
d94e8ba0
YW
779 return false;
780}
781
fb3aec45
YW
782static Route *routes_get_default_gateway(Set *routes, int family, Route *gw) {
783 Route *route;
784
785 SET_FOREACH(route, routes) {
786 if (family != AF_UNSPEC && route->family != family)
787 continue;
788 if (route->dst_prefixlen != 0)
789 continue;
790 if (route->src_prefixlen != 0)
791 continue;
792 if (route->table != RT_TABLE_MAIN)
793 continue;
794 if (route->type != RTN_UNICAST)
795 continue;
796 if (route->scope != RT_SCOPE_UNIVERSE)
797 continue;
798 if (!in_addr_is_set(route->gw_family, &route->gw))
799 continue;
800 if (gw) {
801 if (route->gw_weight > gw->gw_weight)
802 continue;
803 if (route->priority >= gw->priority)
804 continue;
805 }
806 gw = route;
807 }
808
809 return gw;
810}
811
812int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) {
813 Route *gw = NULL;
814 Link *link;
815
816 assert(m);
817 assert(IN_SET(family, AF_UNSPEC, AF_INET, AF_INET6));
818
819 /* Looks for a suitable "uplink", via black magic: an interface that is up and where the
820 * default route with the highest priority points to. */
821
6eab614d 822 HASHMAP_FOREACH(link, m->links_by_index) {
fb3aec45
YW
823 if (link == exclude)
824 continue;
825
826 if (link->state != LINK_STATE_CONFIGURED)
827 continue;
828
829 gw = routes_get_default_gateway(link->routes, family, gw);
830 gw = routes_get_default_gateway(link->routes_foreign, family, gw);
831 }
832
833 if (!gw)
834 return -ENOENT;
835
836 assert(gw->link);
837 *ret = gw->link;
838 return 0;
839}
840
b19afdfe
YW
841static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *manager) {
842 _cleanup_free_ char *dst = NULL, *src = NULL, *gw_alloc = NULL, *prefsrc = NULL,
7653a9dc 843 *table = NULL, *scope = NULL, *proto = NULL;
b19afdfe 844 const char *gw = NULL;
7653a9dc 845
167a5561
YW
846 assert(route);
847 assert(str);
b19afdfe 848 assert(manager);
167a5561
YW
849
850 /* link may be NULL. */
851
7653a9dc
YW
852 if (!DEBUG_LOGGING)
853 return;
854
855 if (in_addr_is_set(route->family, &route->dst))
856 (void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst);
857 if (in_addr_is_set(route->family, &route->src))
858 (void) in_addr_to_string(route->family, &route->src, &src);
b19afdfe
YW
859 if (in_addr_is_set(route->gw_family, &route->gw)) {
860 (void) in_addr_to_string(route->gw_family, &route->gw, &gw_alloc);
861 gw = gw_alloc;
862 } else if (route->gateway_from_dhcp_or_ra) {
863 if (route->gw_family == AF_INET)
864 gw = "_dhcp4";
865 else if (route->gw_family == AF_INET6)
866 gw = "_ipv6ra";
867 } else {
868 MultipathRoute *m;
869
870 ORDERED_SET_FOREACH(m, route->multipath_routes) {
4304f68d 871 _cleanup_free_ char *buf = NULL;
b19afdfe
YW
872 union in_addr_union a = m->gateway.address;
873
874 (void) in_addr_to_string(m->gateway.family, &a, &buf);
4304f68d
YW
875 (void) strextend_with_separator(&gw_alloc, ",", strna(buf));
876 if (m->ifname)
877 (void) strextend(&gw_alloc, "@", m->ifname);
878 else if (m->ifindex > 0)
879 (void) strextendf(&gw_alloc, "@%"PRIu32, m->ifindex);
880 /* See comments in config_parse_multipath_route(). */
881 (void) strextendf(&gw_alloc, ":%"PRIu32, m->weight + 1);
b19afdfe
YW
882 }
883 gw = gw_alloc;
884 }
7653a9dc
YW
885 if (in_addr_is_set(route->family, &route->prefsrc))
886 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
887 (void) route_scope_to_string_alloc(route->scope, &scope);
b19afdfe 888 (void) manager_get_route_table_to_string(manager, route->table, &table);
7653a9dc
YW
889 (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
890
891 log_link_debug(link,
892 "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s, nexthop: %"PRIu32", priority: %"PRIu32,
893 str, strna(dst), strna(src), strna(gw), strna(prefsrc),
894 strna(scope), strna(table), strna(proto),
895 strna(route_type_to_string(route->type)),
896 route->nexthop_id, route->priority);
167a5561
YW
897}
898
5f4d7aa4
YW
899static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
900 unsigned flags;
901 int r;
902
903 assert(route);
904 assert(req);
905
906 /* link may be NULL */
907
3c7f1c07 908 if (in_addr_is_set(route->gw_family, &route->gw) && route->nexthop_id == 0) {
5f4d7aa4
YW
909 if (route->gw_family == route->family) {
910 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
911 if (r < 0)
912 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
913 } else {
914 RouteVia rtvia = {
915 .family = route->gw_family,
916 .address = route->gw,
917 };
918
919 r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
920 if (r < 0)
921 return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
922 }
923 }
924
925 if (route->dst_prefixlen > 0) {
926 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
927 if (r < 0)
928 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
929
930 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
931 if (r < 0)
932 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
933 }
934
935 if (route->src_prefixlen > 0) {
936 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
937 if (r < 0)
938 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
939
940 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
941 if (r < 0)
942 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
943 }
944
94876904 945 if (in_addr_is_set(route->family, &route->prefsrc)) {
5f4d7aa4
YW
946 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
947 if (r < 0)
948 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
949 }
950
951 r = sd_rtnl_message_route_set_scope(req, route->scope);
952 if (r < 0)
953 return log_link_error_errno(link, r, "Could not set scope: %m");
954
955 flags = route->flags;
956 if (route->gateway_onlink >= 0)
957 SET_FLAG(flags, RTNH_F_ONLINK, route->gateway_onlink);
958
959 r = sd_rtnl_message_route_set_flags(req, flags);
960 if (r < 0)
961 return log_link_error_errno(link, r, "Could not set flags: %m");
962
3e0eeb8e
YW
963 if (route->table < 256) {
964 r = sd_rtnl_message_route_set_table(req, route->table);
965 if (r < 0)
966 return log_link_error_errno(link, r, "Could not set route table: %m");
967 } else {
968 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
969 if (r < 0)
970 return log_link_error_errno(link, r, "Could not set route table: %m");
5f4d7aa4 971
3e0eeb8e
YW
972 /* Table attribute to allow more than 256. */
973 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
974 if (r < 0)
975 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
5f4d7aa4
YW
976 }
977
08c2fcdc
YW
978 if (!route_type_is_reject(route) &&
979 route->nexthop_id == 0 &&
980 ordered_set_isempty(route->multipath_routes)) {
5f4d7aa4
YW
981 assert(link); /* Those routes must be attached to a specific link */
982
983 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
984 if (r < 0)
985 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
986 }
987
324e3422
YW
988 if (route->nexthop_id > 0) {
989 r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id);
990 if (r < 0)
991 return log_link_error_errno(link, r, "Could not append RTA_NH_ID attribute: %m");
992 }
993
5f4d7aa4
YW
994 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
995 if (r < 0)
996 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
997
998 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
999 if (r < 0)
1000 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
1001
1002 return 0;
1003}
1004
f4cc1364 1005static int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
1006 int r;
1007
1008 assert(m);
5a07fa9d 1009 assert(link);
76c5a0f2 1010 assert(link->route_remove_messages > 0);
4645ad47 1011
76c5a0f2
YW
1012 link->route_remove_messages--;
1013
5a07fa9d
YW
1014 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1015 return 0;
1016
1017 r = sd_netlink_message_get_errno(m);
1018 if (r < 0 && r != -ESRCH)
f4cc1364 1019 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
5a07fa9d
YW
1020
1021 return 1;
1022}
1023
5a07fa9d
YW
1024static int manager_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Manager *manager) {
1025 int r;
1026
1027 assert(m);
76c5a0f2
YW
1028 assert(manager);
1029 assert(manager->route_remove_messages > 0);
1030
1031 manager->route_remove_messages--;
4645ad47
YW
1032
1033 r = sd_netlink_message_get_errno(m);
1034 if (r < 0 && r != -ESRCH)
5a07fa9d 1035 log_message_warning_errno(m, r, "Could not drop route, ignoring");
4645ad47
YW
1036
1037 return 1;
1038}
1039
f4cc1364 1040int route_remove(const Route *route, Manager *manager, Link *link) {
4afd3348 1041 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
c3fa1257 1042 unsigned char type;
5c1d3fc9
UTL
1043 int r;
1044
ad208fac 1045 assert(link || manager);
4c701096 1046 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9 1047
ad208fac
YW
1048 if (!manager)
1049 manager = link->manager;
1050
552b90a2 1051 log_route_debug(route, "Removing", link, manager);
167a5561 1052
ad208fac 1053 r = sd_rtnl_message_new_route(manager->rtnl, &req,
28cc555d
DW
1054 RTM_DELROUTE, route->family,
1055 route->protocol);
f647962d 1056 if (r < 0)
7750b796 1057 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 1058
c3fa1257
YW
1059 if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE)
1060 /* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel
1061 * sends RTM_NEWROUTE netlink message with blackhole type, kernel's internal route type
1062 * fib_rt_info::type may not be blackhole. Thus, we cannot know the internal value.
1063 * Moreover, on route removal, the matching is done with the hidden value if we set
1064 * non-zero type in RTM_DELROUTE message. Note, sd_rtnl_message_new_route() sets
1065 * RTN_UNICAST by default. So, we need to clear the type here. */
1066 type = RTN_UNSPEC;
1067 else
1068 type = route->type;
1069
1070 r = sd_rtnl_message_route_set_type(req, type);
1071 if (r < 0)
1072 return log_link_error_errno(link, r, "Could not set route type: %m");
1073
5f4d7aa4 1074 r = route_set_netlink_message(route, req, link);
f647962d 1075 if (r < 0)
5f4d7aa4 1076 return r;
5c1d3fc9 1077
5a07fa9d
YW
1078 if (link) {
1079 r = netlink_call_async(manager->rtnl, NULL, req,
f4cc1364 1080 link_route_remove_handler,
5a07fa9d
YW
1081 link_netlink_destroy_callback, link);
1082 if (r < 0)
1083 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
5c1d3fc9 1084
5a07fa9d 1085 link_ref(link);
76c5a0f2 1086 link->route_remove_messages++;
5a07fa9d
YW
1087 } else {
1088 r = netlink_call_async(manager->rtnl, NULL, req,
1089 manager_route_remove_handler,
1090 NULL, manager);
1091 if (r < 0)
1092 return log_error_errno(r, "Could not send rtnetlink message: %m");
76c5a0f2
YW
1093
1094 manager->route_remove_messages++;
5a07fa9d 1095 }
563c69c6 1096
5c1d3fc9
UTL
1097 return 0;
1098}
1099
884a63d7 1100static bool link_has_static_route(const Link *link, const Route *route) {
779804dd
YW
1101 Route *net_route;
1102
1103 assert(link);
1104 assert(route);
1105
1106 if (!link->network)
1107 return false;
1108
2a54a044 1109 HASHMAP_FOREACH(net_route, link->network->routes_by_section)
779804dd
YW
1110 if (route_equal(net_route, route))
1111 return true;
1112
1113 return false;
1114}
1115
884a63d7 1116static bool links_have_static_route(const Manager *manager, const Route *route, const Link *except) {
1132a714
YW
1117 Link *link;
1118
1119 assert(manager);
1120
6eab614d 1121 HASHMAP_FOREACH(link, manager->links_by_index) {
1132a714
YW
1122 if (link == except)
1123 continue;
1124
884a63d7 1125 if (link_has_static_route(link, route))
1132a714
YW
1126 return true;
1127 }
1128
1129 return false;
1130}
1131
4055ec93 1132static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) {
1132a714
YW
1133 Route *route;
1134 int k, r = 0;
4055ec93 1135 Set *routes;
1132a714
YW
1136
1137 assert(manager);
1138
4055ec93
YW
1139 routes = foreign ? manager->routes_foreign : manager->routes;
1140 SET_FOREACH(route, routes) {
8a9ce525
YW
1141 if (route->removing)
1142 continue;
1143
0f1f933b 1144 /* Do not touch routes managed by the kernel. */
1132a714
YW
1145 if (route->protocol == RTPROT_KERNEL)
1146 continue;
1147
0f1f933b 1148 /* The route will be configured later, or already configured by a link. */
884a63d7 1149 if (links_have_static_route(manager, route, except))
1132a714
YW
1150 continue;
1151
0f1f933b 1152 /* The existing links do not have the route. Let's drop this now. It may be
1132a714 1153 * re-configured later. */
f4cc1364 1154 k = route_remove(route, manager, NULL);
1132a714
YW
1155 if (k < 0 && r >= 0)
1156 r = k;
8a9ce525
YW
1157
1158 route->removing = true;
1132a714
YW
1159 }
1160
1161 return r;
1162}
1163
4055ec93
YW
1164static int manager_drop_foreign_routes(Manager *manager) {
1165 return manager_drop_routes_internal(manager, true, NULL);
1166}
1132a714 1167
4055ec93
YW
1168static int manager_drop_routes(Manager *manager, const Link *except) {
1169 return manager_drop_routes_internal(manager, false, except);
1132a714
YW
1170}
1171
dbf8942a
YW
1172static bool route_by_kernel(const Route *route) {
1173 assert(route);
1174
1175 if (route->protocol == RTPROT_KERNEL)
1176 return true;
1177
1178 /* Do not touch multicast route added by kernel. See issue #6088.
1179 * TODO: Why the kernel adds this route with protocol RTPROT_BOOT?
1180 * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
1181 if (route->protocol == RTPROT_BOOT &&
1182 route->family == AF_INET6 &&
1183 route->dst_prefixlen == 8 &&
1184 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 }}}))
1185 return true;
1186
1187 return false;
1188}
1189
779804dd
YW
1190int link_drop_foreign_routes(Link *link) {
1191 Route *route;
1192 int k, r = 0;
1193
1194 assert(link);
1132a714 1195 assert(link->manager);
779804dd
YW
1196
1197 SET_FOREACH(route, link->routes_foreign) {
1198 /* do not touch routes managed by the kernel */
dbf8942a 1199 if (route_by_kernel(route))
779804dd
YW
1200 continue;
1201
1202 if (route->protocol == RTPROT_STATIC && link->network &&
1203 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
1204 continue;
1205
1206 if (route->protocol == RTPROT_DHCP && link->network &&
1207 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
1208 continue;
1209
884a63d7 1210 if (link_has_static_route(link, route))
228c3e21 1211 k = route_add(NULL, link, route, NULL, NULL, UINT8_MAX, NULL);
779804dd 1212 else
f4cc1364 1213 k = route_remove(route, NULL, link);
779804dd
YW
1214 if (k < 0 && r >= 0)
1215 r = k;
1216 }
1217
1132a714
YW
1218 k = manager_drop_foreign_routes(link->manager);
1219 if (k < 0 && r >= 0)
1220 r = k;
1221
779804dd
YW
1222 return r;
1223}
1224
62f0ea5f
YW
1225int link_drop_routes(Link *link) {
1226 Route *route;
1227 int k, r = 0;
1228
1229 assert(link);
1230
1231 SET_FOREACH(route, link->routes) {
1232 /* do not touch routes managed by the kernel */
1233 if (route->protocol == RTPROT_KERNEL)
1234 continue;
1235
f4cc1364 1236 k = route_remove(route, NULL, link);
62f0ea5f
YW
1237 if (k < 0 && r >= 0)
1238 r = k;
1239 }
1240
1132a714
YW
1241 k = manager_drop_routes(link->manager, link);
1242 if (k < 0 && r >= 0)
1243 r = k;
1244
62f0ea5f
YW
1245 return r;
1246}
1247
74154c2e 1248static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
f833694d
TG
1249 Route *route = userdata;
1250 int r;
1251
1252 assert(route);
1253
f4cc1364 1254 r = route_remove(route, route->manager, route->link);
d6ad41e2 1255 if (r < 0) {
98b02994 1256 log_link_warning_errno(route->link, r, "Could not remove route: %m");
fe7ca21a 1257 route_free(route);
d6ad41e2 1258 }
f833694d
TG
1259
1260 return 1;
1261}
1262
228c3e21 1263static int route_add_and_setup_timer_one(Link *link, const Route *route, const MultipathRoute *m, const NextHop *nh, uint8_t nh_weight, Route **ret) {
f9bb3338
YW
1264 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
1265 Route *nr;
7b3a7581 1266 int r;
f9bb3338
YW
1267
1268 assert(link);
324e3422 1269 assert(link->manager);
f9bb3338 1270 assert(route);
228c3e21
YW
1271 assert(!(m && nh));
1272 assert(ret);
324e3422 1273
30f10837 1274 if (route_type_is_reject(route) || (nh && nh->blackhole))
228c3e21
YW
1275 r = route_add(link->manager, NULL, route, NULL, nh, nh_weight, &nr);
1276 else if (nh) {
1277 assert(nh->link);
1278 assert(hashmap_isempty(nh->group));
1279
1280 r = route_add(NULL, nh->link, route, NULL, nh, nh_weight, &nr);
1281 } else if (m && m->ifindex != 0 && m->ifindex != link->ifindex) {
f9bb3338
YW
1282 Link *link_gw;
1283
6eab614d 1284 r = link_get_by_index(link->manager, m->ifindex, &link_gw);
f9bb3338
YW
1285 if (r < 0)
1286 return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
1287
228c3e21
YW
1288 r = route_add(NULL, link_gw, route, m, NULL, UINT8_MAX, &nr);
1289 } else
1290 r = route_add(NULL, link, route, m, NULL, UINT8_MAX, &nr);
7b3a7581
YW
1291 if (r < 0)
1292 return log_link_error_errno(link, r, "Could not add route: %m");
f9bb3338
YW
1293
1294 /* TODO: drop expiration handling once it can be pushed into the kernel */
1295 if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
1296 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
1297 nr->lifetime, 0, route_expire_handler, nr);
1298 if (r < 0)
1299 return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
1300 }
1301
1302 sd_event_source_unref(nr->expire);
1303 nr->expire = TAKE_PTR(expire);
1304
228c3e21
YW
1305 *ret = nr;
1306 return 0;
1307}
1308
1309static int route_add_and_setup_timer(Link *link, const Route *route, unsigned *ret_n_routes, Route ***ret_routes) {
1310 _cleanup_free_ Route **routes = NULL;
1311 unsigned n_routes;
1312 NextHop *nh;
1313 Route **p;
1314 int r;
1315
1316 assert(link);
1317 assert(route);
1318 assert(ret_n_routes);
1319 assert(ret_routes);
1320
1321 if (route->nexthop_id > 0) {
1322 r = manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh);
1323 if (r < 0)
1324 return log_link_error_errno(link, r, "Could not get nexthop by ID %"PRIu32": %m", route->nexthop_id);
1325 } else
1326 nh = NULL;
1327
1328 if (nh && !hashmap_isempty(nh->group)) {
1329 struct nexthop_grp *nhg;
1330
1331 n_routes = hashmap_size(nh->group);
1332 p = routes = new(Route*, n_routes);
1333 if (!routes)
1334 return log_oom();
f9bb3338 1335
228c3e21
YW
1336 HASHMAP_FOREACH(nhg, nh->group) {
1337 NextHop *h;
1338
1339 r = manager_get_nexthop_by_id(link->manager, nhg->id, &h);
1340 if (r < 0)
1341 return log_link_error_errno(link, r, "Could not get nexthop group member by ID %"PRIu32": %m", nhg->id);
1342
1343 /* The nexthop h may be a blackhole nexthop. In that case, h->link is NULL. */
1344 r = route_add_and_setup_timer_one(h->link ?: link, route, NULL, h, nhg->weight, p++);
1345 if (r < 0)
1346 return r;
1347 }
1348 } else if (!ordered_set_isempty(route->multipath_routes)) {
1349 MultipathRoute *m;
1350
1351 assert(!nh);
1352 assert(!in_addr_is_set(route->gw_family, &route->gw));
1353
1354 n_routes = ordered_set_size(route->multipath_routes);
1355 p = routes = new(Route*, n_routes);
1356 if (!routes)
1357 return log_oom();
1358
1359 ORDERED_SET_FOREACH(m, route->multipath_routes) {
1360 r = route_add_and_setup_timer_one(link, route, m, NULL, UINT8_MAX, p++);
1361 if (r < 0)
1362 return r;
1363 }
1364 } else {
1365 n_routes = 1;
1366 routes = new(Route*, n_routes);
1367 if (!routes)
1368 return log_oom();
1369
1370 r = route_add_and_setup_timer_one(link, route, NULL, nh, UINT8_MAX, routes);
1371 if (r < 0)
1372 return r;
1373 }
1374
1375 *ret_n_routes = n_routes;
1376 *ret_routes = TAKE_PTR(routes);
7b3a7581 1377 return 0;
f9bb3338
YW
1378}
1379
08c2fcdc 1380static int append_nexthop_one(const Link *link, const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
6ff5cc6b
YW
1381 struct rtnexthop *rtnh;
1382 struct rtattr *new_rta;
1383 int r;
1384
1385 assert(route);
1386 assert(m);
1387 assert(rta);
1388 assert(*rta);
1389
1390 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
1391 if (!new_rta)
1392 return -ENOMEM;
1393 *rta = new_rta;
1394
1395 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1396 *rtnh = (struct rtnexthop) {
1397 .rtnh_len = sizeof(*rtnh),
08c2fcdc 1398 .rtnh_ifindex = m->ifindex > 0 ? m->ifindex : link->ifindex,
234106db 1399 .rtnh_hops = m->weight,
6ff5cc6b
YW
1400 };
1401
1402 (*rta)->rta_len += sizeof(struct rtnexthop);
1403
1404 if (route->family == m->gateway.family) {
1405 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
1406 if (r < 0)
1407 goto clear;
1408 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1409 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
1410 } else {
1411 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1412 if (r < 0)
1413 goto clear;
1414 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1415 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1416 }
1417
1418 return 0;
1419
1420clear:
1421 (*rta)->rta_len -= sizeof(struct rtnexthop);
1422 return r;
1423}
1424
08c2fcdc 1425static int append_nexthops(const Link *link, const Route *route, sd_netlink_message *req) {
6ff5cc6b
YW
1426 _cleanup_free_ struct rtattr *rta = NULL;
1427 struct rtnexthop *rtnh;
1428 MultipathRoute *m;
1429 size_t offset;
6ff5cc6b
YW
1430 int r;
1431
08c2fcdc
YW
1432 assert(link);
1433 assert(route);
1434 assert(req);
1435
6ff5cc6b
YW
1436 if (ordered_set_isempty(route->multipath_routes))
1437 return 0;
1438
1439 rta = new(struct rtattr, 1);
1440 if (!rta)
1441 return -ENOMEM;
1442
1443 *rta = (struct rtattr) {
1444 .rta_type = RTA_MULTIPATH,
1445 .rta_len = RTA_LENGTH(0),
1446 };
1447 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
1448
90e74a66 1449 ORDERED_SET_FOREACH(m, route->multipath_routes) {
08c2fcdc 1450 r = append_nexthop_one(link, route, m, &rta, offset);
6ff5cc6b
YW
1451 if (r < 0)
1452 return r;
1453
1454 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
1455 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
1456 }
1457
1458 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
1459 if (r < 0)
1460 return r;
1461
1462 return 0;
1463}
1464
5a07fa9d
YW
1465int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg) {
1466 int r;
1467
1468 assert(m);
1469 assert(link);
1470 assert(error_msg);
1471
1472 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1473 return 0;
1474
1475 r = sd_netlink_message_get_errno(m);
1476 if (r < 0 && r != -EEXIST) {
e95c06c9 1477 log_link_message_warning_errno(link, m, r, "Could not set route");
5a07fa9d
YW
1478 link_enter_failed(link);
1479 return 0;
1480 }
1481
1482 return 1;
1483}
1484
76c5a0f2 1485static int route_configure(
423c249c 1486 const Route *route,
1b566071 1487 Link *link,
80b0e860 1488 link_netlink_message_handler_t callback,
5cb76467
YW
1489 unsigned *ret_n_routes,
1490 Route ***ret_routes) {
1b566071 1491
4afd3348 1492 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5cb76467
YW
1493 _cleanup_free_ Route **routes = NULL;
1494 unsigned n_routes = 0; /* avoid false maybe-uninitialized warning */
7b3a7581 1495 int r;
f579559b 1496
f579559b 1497 assert(link);
f882c247
TG
1498 assert(link->manager);
1499 assert(link->manager->rtnl);
f579559b 1500 assert(link->ifindex > 0);
4c701096 1501 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 1502 assert(callback);
5cb76467 1503 assert(!!ret_n_routes == !!ret_routes);
f579559b 1504
ad208fac 1505 if (route_get(link->manager, link, route, NULL) <= 0 &&
47d2d30d 1506 set_size(link->routes) >= routes_max())
7750b796
YW
1507 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
1508 "Too many routes are configured, refusing: %m");
1b566071 1509
552b90a2 1510 log_route_debug(route, "Configuring", link, link->manager);
156ed65e 1511
151b9b96 1512 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
1513 RTM_NEWROUTE, route->family,
1514 route->protocol);
f647962d 1515 if (r < 0)
7750b796 1516 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
f579559b 1517
c3fa1257
YW
1518 r = sd_rtnl_message_route_set_type(req, route->type);
1519 if (r < 0)
1520 return log_link_error_errno(link, r, "Could not set route type: %m");
1521
5f4d7aa4 1522 r = route_set_netlink_message(route, req, link);
f647962d 1523 if (r < 0)
5f4d7aa4 1524 return r;
3b015d40 1525
f02ba163
DD
1526 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
1527 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
1528 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
1529 if (r < 0)
7750b796 1530 return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
f02ba163
DD
1531 }
1532
9b88f20a
SS
1533 if (route->ttl_propagate >= 0) {
1534 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
1535 if (r < 0)
1536 return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
1537 }
1538
d6fceaf1
SS
1539 r = sd_netlink_message_open_container(req, RTA_METRICS);
1540 if (r < 0)
7750b796 1541 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1
SS
1542
1543 if (route->mtu > 0) {
1544 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
1545 if (r < 0)
7750b796 1546 return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
d6fceaf1
SS
1547 }
1548
6b21ad33 1549 if (route->initcwnd > 0) {
323d9329
SS
1550 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
1551 if (r < 0)
7750b796 1552 return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
323d9329
SS
1553 }
1554
6b21ad33 1555 if (route->initrwnd > 0) {
323d9329
SS
1556 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
1557 if (r < 0)
7750b796 1558 return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
323d9329
SS
1559 }
1560
67c193bf 1561 if (route->quickack >= 0) {
09f5dfad
SS
1562 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
1563 if (r < 0)
7750b796 1564 return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
09f5dfad
SS
1565 }
1566
633c7258
SS
1567 if (route->fast_open_no_cookie >= 0) {
1568 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
1569 if (r < 0)
1570 return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
1571 }
1572
007cac09
SS
1573 if (route->advmss > 0) {
1574 r = sd_netlink_message_append_u32(req, RTAX_ADVMSS, route->advmss);
1575 if (r < 0)
1576 return log_link_error_errno(link, r, "Could not append RTAX_ADVMSS attribute: %m");
1577 }
1578
d6fceaf1
SS
1579 r = sd_netlink_message_close_container(req);
1580 if (r < 0)
7750b796 1581 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1 1582
228c3e21
YW
1583 if (!ordered_set_isempty(route->multipath_routes)) {
1584 assert(route->nexthop_id == 0);
1585 assert(!in_addr_is_set(route->gw_family, &route->gw));
f833694d 1586
08c2fcdc 1587 r = append_nexthops(link, route, req);
3c7f1c07
YW
1588 if (r < 0)
1589 return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
f9bb3338 1590 }
80b0e860 1591
228c3e21
YW
1592 r = route_add_and_setup_timer(link, route, &n_routes, &routes);
1593 if (r < 0)
1594 return r;
1595
0c54bfd6
YW
1596 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
1597 link_netlink_destroy_callback, link);
1598 if (r < 0)
1599 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
1600
1601 link_ref(link);
1602
5cb76467
YW
1603 if (ret_routes) {
1604 *ret_n_routes = n_routes;
1605 *ret_routes = TAKE_PTR(routes);
1606 }
0ef9f3c7 1607
228c3e21 1608 return r;
f579559b
TG
1609}
1610
76c5a0f2 1611static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
141318f7
YW
1612 int r;
1613
1614 assert(link);
76c5a0f2 1615 assert(link->static_route_messages > 0);
141318f7 1616
76c5a0f2 1617 link->static_route_messages--;
141318f7 1618
76c5a0f2 1619 r = route_configure_handler_internal(rtnl, m, link, "Could not set route");
5a07fa9d
YW
1620 if (r <= 0)
1621 return r;
141318f7 1622
76c5a0f2
YW
1623 if (link->static_route_messages == 0) {
1624 log_link_debug(link, "Routes set");
141318f7 1625 link->static_routes_configured = true;
f345918d 1626 link_check_ready(link);
141318f7
YW
1627 }
1628
1629 return 1;
1630}
1631
76c5a0f2
YW
1632int link_request_route(
1633 Link *link,
1634 Route *route,
1635 bool consume_object,
1636 unsigned *message_counter,
1637 link_netlink_message_handler_t netlink_handler,
1638 Request **ret) {
f345918d
YW
1639
1640 assert(link);
76c5a0f2 1641 assert(link->manager);
f345918d
YW
1642 assert(route);
1643
76c5a0f2
YW
1644 log_route_debug(route, "Requesting", link, link->manager);
1645 return link_queue_request(link, REQUEST_TYPE_ROUTE, route, consume_object,
1646 message_counter, netlink_handler, ret);
f345918d
YW
1647}
1648
76c5a0f2 1649int link_request_static_routes(Link *link, bool only_ipv4) {
e8f52f3c 1650 Route *route;
141318f7
YW
1651 int r;
1652
f345918d
YW
1653 assert(link);
1654 assert(link->network);
1655
76c5a0f2 1656 link->static_routes_configured = false;
e8f52f3c 1657
76c5a0f2 1658 HASHMAP_FOREACH(route, link->network->routes_by_section) {
e8f52f3c
YW
1659 if (route->gateway_from_dhcp_or_ra)
1660 continue;
1661
76c5a0f2 1662 if (only_ipv4 && route->family != AF_INET)
f345918d
YW
1663 continue;
1664
76c5a0f2
YW
1665 r = link_request_route(link, route, false, &link->static_route_messages,
1666 static_route_handler, NULL);
f345918d 1667 if (r < 0)
76c5a0f2
YW
1668 return r;
1669 }
f345918d 1670
76c5a0f2
YW
1671 if (link->static_route_messages == 0) {
1672 link->static_routes_configured = true;
1673 link_check_ready(link);
1674 } else {
1675 log_link_debug(link, "Requesting routes");
1676 link_set_state(link, LINK_STATE_CONFIGURING);
f345918d
YW
1677 }
1678
1679 return 0;
1680}
1681
3333350a
YW
1682bool gateway_is_ready(Link *link, int onlink, int family, const union in_addr_union *gw) {
1683 assert(link);
1684 assert(gw);
1685
1686 if (onlink > 0)
1687 return true;
1688
1689 if (!in_addr_is_set(family, gw))
1690 return true;
1691
1692 if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6))
1693 return true;
1694
1695 return link_address_is_reachable(link, family, gw);
1696}
1697
76c5a0f2
YW
1698static int route_is_ready_to_configure(const Route *route, Link *link) {
1699 MultipathRoute *m;
1700 NextHop *nh = NULL;
f345918d
YW
1701 int r;
1702
76c5a0f2 1703 assert(route);
f345918d 1704 assert(link);
f345918d 1705
228c3e21
YW
1706 if (route->nexthop_id > 0) {
1707 struct nexthop_grp *nhg;
1708
1709 if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0)
1710 return false;
1711
89fa0133
YW
1712 if (!nexthop_exists(nh))
1713 return false;
1714
1715 HASHMAP_FOREACH(nhg, nh->group) {
1716 NextHop *g;
1717
1718 if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0)
228c3e21 1719 return false;
89fa0133
YW
1720
1721 if (!nexthop_exists(g))
1722 return false;
1723 }
228c3e21 1724 }
f345918d 1725
89fa0133 1726 if (route_type_is_reject(route)) {
76c5a0f2
YW
1727 if (link->manager->route_remove_messages > 0)
1728 return false;
f345918d 1729 } else {
76c5a0f2
YW
1730 Link *l;
1731
6eab614d 1732 HASHMAP_FOREACH(l, link->manager->links_by_index) {
76c5a0f2
YW
1733 if (l->address_remove_messages > 0)
1734 return false;
76c5a0f2
YW
1735 if (l->route_remove_messages > 0)
1736 return false;
1737 }
f345918d
YW
1738 }
1739
76c5a0f2
YW
1740 if (in_addr_is_set(route->family, &route->prefsrc) > 0) {
1741 r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6);
1742 if (r <= 0)
1743 return r;
1744 }
f345918d 1745
3333350a 1746 if (!gateway_is_ready(link, route->gateway_onlink, route->gw_family, &route->gw))
76c5a0f2 1747 return false;
f345918d 1748
76c5a0f2
YW
1749 ORDERED_SET_FOREACH(m, route->multipath_routes) {
1750 union in_addr_union a = m->gateway.address;
8ed87c49 1751 Link *l = NULL;
141318f7 1752
76c5a0f2 1753 if (m->ifname) {
8ed87c49 1754 if (link_get_by_name(link->manager, m->ifname, &l) < 0)
76c5a0f2 1755 return false;
141318f7 1756
8ed87c49
YW
1757 m->ifindex = l->ifindex;
1758 } else if (m->ifindex > 0) {
6eab614d 1759 if (link_get_by_index(link->manager, m->ifindex, &l) < 0)
76c5a0f2 1760 return false;
76c5a0f2 1761 }
8ed87c49
YW
1762 if (l && !link_is_ready_to_configure(l, true))
1763 return false;
3333350a
YW
1764
1765 if (!gateway_is_ready(l ?: link, route->gateway_onlink, m->gateway.family, &a))
1766 return false;
bd4733da
YW
1767 }
1768
76c5a0f2
YW
1769 return true;
1770}
1771
1772int request_process_route(Request *req) {
5cb76467
YW
1773 _cleanup_free_ Route **routes = NULL;
1774 unsigned n_routes;
76c5a0f2
YW
1775 int r;
1776
1777 assert(req);
1778 assert(req->link);
1779 assert(req->route);
1780 assert(req->type == REQUEST_TYPE_ROUTE);
1781
1782 if (!link_is_ready_to_configure(req->link, false))
1783 return 0;
1784
1785 r = route_is_ready_to_configure(req->route, req->link);
1786 if (r <= 0)
f345918d 1787 return r;
8c212f76 1788
76c5a0f2 1789 r = route_configure(req->route, req->link, req->netlink_handler,
5cb76467
YW
1790 req->after_configure ? &n_routes : NULL,
1791 req->after_configure ? &routes : NULL);
76c5a0f2
YW
1792 if (r < 0)
1793 return r;
141318f7 1794
ce9388d7
YW
1795 /* To prevent a double decrement on failure in after_configure(). */
1796 req->message_counter = NULL;
1797
76c5a0f2 1798 if (req->after_configure) {
5cb76467
YW
1799 assert(n_routes > 0);
1800
1801 for (unsigned i = 0; i < n_routes; i++) {
1802 r = req->after_configure(req, routes[i]);
1803 if (r < 0)
1804 return r;
1805 }
76c5a0f2 1806 }
141318f7 1807
76c5a0f2 1808 return 1;
141318f7
YW
1809}
1810
f9bb3338
YW
1811static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
1812 _cleanup_(route_freep) Route *nr = NULL;
1813 Route *route = NULL;
324e3422 1814 NextHop *nh = NULL;
f9bb3338
YW
1815 int r;
1816
1817 assert(manager);
1818 assert(tmp);
1819 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
1820
324e3422
YW
1821 (void) manager_get_nexthop_by_id(manager, tmp->nexthop_id, &nh);
1822
228c3e21
YW
1823 if (nh && hashmap_isempty(nh->group)) {
1824 if (link && nh->link && link != nh->link)
324e3422
YW
1825 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
1826 "rtnl: received RTA_OIF and ifindex of nexthop corresponding to RTA_NH_ID do not match, ignoring.");
1827
228c3e21
YW
1828 if (nh->link)
1829 link = nh->link;
324e3422
YW
1830
1831 r = route_new(&nr);
1832 if (r < 0)
1833 return log_oom();
1834
228c3e21 1835 route_copy(nr, tmp, NULL, nh, UINT8_MAX);
324e3422
YW
1836
1837 tmp = nr;
1838 } else if (m) {
f9bb3338
YW
1839 if (link)
1840 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
1841 "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
1842
1843 if (m->ifindex <= 0)
1844 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
1845 "rtnl: received multipath route with invalid ifindex, ignoring.");
1846
6eab614d 1847 r = link_get_by_index(manager, m->ifindex, &link);
f9bb3338
YW
1848 if (r < 0) {
1849 log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
1850 return 0;
1851 }
1852
1853 r = route_new(&nr);
1854 if (r < 0)
1855 return log_oom();
1856
228c3e21 1857 route_copy(nr, tmp, m, NULL, UINT8_MAX);
f9bb3338
YW
1858
1859 tmp = nr;
1860 }
1861
1862 (void) route_get(manager, link, tmp, &route);
1863
f9bb3338
YW
1864 switch (type) {
1865 case RTM_NEWROUTE:
167a5561
YW
1866 if (!route) {
1867 if (!manager->manage_foreign_routes)
552b90a2 1868 log_route_debug(tmp, "Ignoring received foreign", link, manager);
167a5561
YW
1869 else {
1870 /* A route appeared that we did not request */
552b90a2 1871 log_route_debug(tmp, "Remembering foreign", link, manager);
167a5561
YW
1872 r = route_add_foreign(manager, link, tmp, NULL);
1873 if (r < 0) {
1874 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
1875 return 0;
1876 }
f9bb3338 1877 }
167a5561 1878 } else
552b90a2 1879 log_route_debug(tmp, "Received remembered", link, manager);
f9bb3338
YW
1880
1881 break;
1882
1883 case RTM_DELROUTE:
167a5561
YW
1884 log_route_debug(tmp,
1885 route ? "Forgetting" :
552b90a2
YW
1886 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign",
1887 link, manager);
f9bb3338
YW
1888 route_free(route);
1889 break;
1890
1891 default:
04499a70 1892 assert_not_reached();
f9bb3338
YW
1893 }
1894
1895 return 1;
1896}
1897
4468f01b 1898int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
f9bb3338 1899 _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
4468f01b 1900 _cleanup_(route_freep) Route *tmp = NULL;
f9bb3338 1901 _cleanup_free_ void *rta_multipath = NULL;
4468f01b
YW
1902 Link *link = NULL;
1903 uint32_t ifindex;
1904 uint16_t type;
1905 unsigned char table;
f9bb3338 1906 size_t rta_len;
4468f01b
YW
1907 int r;
1908
1909 assert(rtnl);
1910 assert(message);
1911 assert(m);
1912
1913 if (sd_netlink_message_is_error(message)) {
1914 r = sd_netlink_message_get_errno(message);
1915 if (r < 0)
1916 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1917
1918 return 0;
1919 }
1920
1921 r = sd_netlink_message_get_type(message, &type);
1922 if (r < 0) {
1923 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1924 return 0;
1925 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1926 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1927 return 0;
1928 }
1929
1930 r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
ad208fac 1931 if (r < 0 && r != -ENODATA) {
4468f01b
YW
1932 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
1933 return 0;
ad208fac
YW
1934 } else if (r >= 0) {
1935 if (ifindex <= 0) {
1936 log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
1937 return 0;
1938 }
4468f01b 1939
6eab614d 1940 r = link_get_by_index(m, ifindex, &link);
ad208fac
YW
1941 if (r < 0 || !link) {
1942 /* when enumerating we might be out of sync, but we will
1943 * get the route again, so just ignore it */
1944 if (!m->enumerating)
1945 log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
1946 return 0;
1947 }
4468f01b
YW
1948 }
1949
1950 r = route_new(&tmp);
1951 if (r < 0)
1952 return log_oom();
1953
1954 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1955 if (r < 0) {
1956 log_link_warning(link, "rtnl: received route message without family, ignoring");
1957 return 0;
1958 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
1959 log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
1960 return 0;
1961 }
1962
1963 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1964 if (r < 0) {
1965 log_warning_errno(r, "rtnl: received route message without route protocol: %m");
1966 return 0;
1967 }
1968
ad6df717
YW
1969 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1970 if (r < 0 && r != -ENODATA) {
1971 log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
1972 return 0;
1973 }
4468f01b 1974
ad6df717
YW
1975 r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, tmp->family, &tmp->gw);
1976 if (r < 0 && r != -ENODATA) {
1977 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1978 return 0;
1979 } else if (r >= 0)
1980 tmp->gw_family = tmp->family;
1981 else if (tmp->family == AF_INET) {
1982 RouteVia via;
6dd53981
YW
1983
1984 r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
1985 if (r < 0 && r != -ENODATA) {
1986 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1987 return 0;
1988 } else if (r >= 0) {
1989 tmp->gw_family = via.family;
1990 tmp->gw = via.address;
4468f01b 1991 }
ad6df717 1992 }
4468f01b 1993
ad6df717
YW
1994 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1995 if (r < 0 && r != -ENODATA) {
1996 log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
1997 return 0;
1998 }
4468f01b 1999
ad6df717
YW
2000 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
2001 if (r < 0 && r != -ENODATA) {
2002 log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
2003 return 0;
2004 }
2005
2006 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
2007 if (r < 0) {
2008 log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
2009 return 0;
2010 }
2011
2012 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
2013 if (r < 0) {
2014 log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
2015 return 0;
2016 }
2017
2018 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
2019 if (r < 0) {
2020 log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
2021 return 0;
2022 }
2023
2024 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
2025 if (r < 0) {
2026 log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
2027 return 0;
2028 }
2029
2030 r = sd_rtnl_message_route_get_type(message, &tmp->type);
2031 if (r < 0) {
2032 log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
2033 return 0;
2034 }
2035
2036 r = sd_rtnl_message_route_get_table(message, &table);
2037 if (r < 0) {
2038 log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
2039 return 0;
2040 }
2041 tmp->table = table;
2042
2043 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
2044 if (r < 0 && r != -ENODATA) {
2045 log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
2046 return 0;
2047 }
2048
324e3422
YW
2049 r = sd_netlink_message_read_u32(message, RTA_NH_ID, &tmp->nexthop_id);
2050 if (r < 0 && r != -ENODATA) {
2051 log_link_warning_errno(link, r, "rtnl: received route message with invalid nexthop id, ignoring: %m");
2052 return 0;
2053 }
2054
4468f01b
YW
2055 r = sd_netlink_message_enter_container(message, RTA_METRICS);
2056 if (r < 0 && r != -ENODATA) {
2057 log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
2058 return 0;
2059 }
2060 if (r >= 0) {
2061 r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
2062 if (r < 0 && r != -ENODATA) {
2063 log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
2064 return 0;
2065 }
2066
2067 r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
2068 if (r < 0 && r != -ENODATA) {
2069 log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
2070 return 0;
386e8908
YW
2071 }
2072
2073 r = sd_netlink_message_read_u32(message, RTAX_ADVMSS, &tmp->advmss);
2074 if (r < 0 && r != -ENODATA) {
2075 log_link_warning_errno(link, r, "rtnl: received route message with invalid advmss, ignoring: %m");
2076 return 0;
4468f01b
YW
2077 }
2078
2079 r = sd_netlink_message_exit_container(message);
2080 if (r < 0) {
2081 log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
2082 return 0;
2083 }
2084 }
2085
f9bb3338
YW
2086 r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
2087 if (r < 0 && r != -ENODATA) {
2088 log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
2089 return 0;
2090 } else if (r >= 0) {
2091 r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
2092 if (r < 0) {
2093 log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
2094 return 0;
4468f01b 2095 }
4468f01b
YW
2096 }
2097
575f14ee
YW
2098 /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
2099 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
2100 * link to NULL here. */
2101 if (route_type_is_reject(tmp))
2102 link = NULL;
2103
f9bb3338
YW
2104 if (ordered_set_isempty(multipath_routes))
2105 (void) process_route_one(m, link, type, tmp, NULL);
2106 else {
2107 MultipathRoute *mr;
4468f01b 2108
f9bb3338
YW
2109 ORDERED_SET_FOREACH(mr, multipath_routes) {
2110 r = process_route_one(m, link, type, tmp, mr);
2111 if (r < 0)
2112 break;
2113 }
4468f01b
YW
2114 }
2115
2116 return 1;
2117}
2118
fa7cd711 2119int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 2120 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 2121 unsigned section_line;
fa7cd711
YW
2122 int r;
2123
2124 assert(network);
2125
2126 if (!network->ipv4ll_route)
2127 return 0;
2128
2a54a044
YW
2129 section_line = hashmap_find_free_section_line(network->routes_by_section);
2130
fa7cd711 2131 /* IPv4LLRoute= is in [Network] section. */
2a54a044 2132 r = route_new_static(network, network->filename, section_line, &n);
fa7cd711
YW
2133 if (r < 0)
2134 return r;
2135
2136 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
2137 if (r < 0)
2138 return r;
2139
2140 n->family = AF_INET;
2141 n->dst_prefixlen = 16;
2142 n->scope = RT_SCOPE_LINK;
94d6e299
YW
2143 n->scope_set = true;
2144 n->table_set = true;
fa7cd711
YW
2145 n->priority = IPV4LL_ROUTE_METRIC;
2146 n->protocol = RTPROT_STATIC;
2147
2148 TAKE_PTR(n);
2149 return 0;
2150}
2151
5d5003ab
YW
2152int network_add_default_route_on_device(Network *network) {
2153 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 2154 unsigned section_line;
5d5003ab
YW
2155 int r;
2156
2157 assert(network);
2158
2159 if (!network->default_route_on_device)
2160 return 0;
2161
2a54a044
YW
2162 section_line = hashmap_find_free_section_line(network->routes_by_section);
2163
5d5003ab 2164 /* DefaultRouteOnDevice= is in [Network] section. */
2a54a044 2165 r = route_new_static(network, network->filename, section_line, &n);
5d5003ab
YW
2166 if (r < 0)
2167 return r;
2168
5d5003ab 2169 n->family = AF_INET;
c697db75
YW
2170 n->scope = RT_SCOPE_LINK;
2171 n->scope_set = true;
2172 n->protocol = RTPROT_STATIC;
5d5003ab
YW
2173
2174 TAKE_PTR(n);
2175 return 0;
2176}
2177
27efb52b
YW
2178int config_parse_gateway(
2179 const char *unit,
f579559b
TG
2180 const char *filename,
2181 unsigned line,
2182 const char *section,
71a61510 2183 unsigned section_line,
f579559b
TG
2184 const char *lvalue,
2185 int ltype,
2186 const char *rvalue,
2187 void *data,
2188 void *userdata) {
44e7b949 2189
6ae115c1 2190 Network *network = userdata;
fcbf4cb7 2191 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 2192 int r;
f579559b
TG
2193
2194 assert(filename);
6ae115c1 2195 assert(section);
f579559b
TG
2196 assert(lvalue);
2197 assert(rvalue);
2198 assert(data);
2199
92fe133a 2200 if (streq(section, "Network")) {
2a54a044
YW
2201 /* we are not in an Route section, so use line number instead */
2202 r = route_new_static(network, filename, line, &n);
d96edb2c
YW
2203 if (r == -ENOMEM)
2204 return log_oom();
2205 if (r < 0) {
2206 log_syntax(unit, LOG_WARNING, filename, line, r,
2207 "Failed to allocate route, ignoring assignment: %m");
2208 return 0;
2209 }
1985c54f 2210 } else {
f4859fc7 2211 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2212 if (r == -ENOMEM)
2213 return log_oom();
2214 if (r < 0) {
2215 log_syntax(unit, LOG_WARNING, filename, line, r,
2216 "Failed to allocate route, ignoring assignment: %m");
2217 return 0;
2218 }
1985c54f 2219
d442bb37 2220 if (isempty(rvalue)) {
1a3a6309 2221 n->gateway_from_dhcp_or_ra = false;
d442bb37
YW
2222 n->gw_family = AF_UNSPEC;
2223 n->gw = IN_ADDR_NULL;
2224 TAKE_PTR(n);
2225 return 0;
2226 }
2227
427928ca 2228 if (streq(rvalue, "_dhcp")) {
1a3a6309 2229 n->gateway_from_dhcp_or_ra = true;
1985c54f
YW
2230 TAKE_PTR(n);
2231 return 0;
2232 }
d306d1d0
YW
2233
2234 if (streq(rvalue, "_dhcp4")) {
2235 n->gw_family = AF_INET;
1a3a6309 2236 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
2237 TAKE_PTR(n);
2238 return 0;
2239 }
2240
b8caa4ef 2241 if (streq(rvalue, "_ipv6ra")) {
d306d1d0 2242 n->gw_family = AF_INET6;
1a3a6309 2243 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
2244 TAKE_PTR(n);
2245 return 0;
2246 }
1985c54f 2247 }
f579559b 2248
6dd53981 2249 r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
f579559b 2250 if (r < 0) {
d96edb2c 2251 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 2252 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
2253 return 0;
2254 }
2255
1a3a6309 2256 n->gateway_from_dhcp_or_ra = false;
aff44301 2257 TAKE_PTR(n);
f579559b
TG
2258 return 0;
2259}
6ae115c1 2260
27efb52b
YW
2261int config_parse_preferred_src(
2262 const char *unit,
0d07e595
JK
2263 const char *filename,
2264 unsigned line,
2265 const char *section,
2266 unsigned section_line,
2267 const char *lvalue,
2268 int ltype,
2269 const char *rvalue,
2270 void *data,
2271 void *userdata) {
2272
2273 Network *network = userdata;
fcbf4cb7 2274 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 2275 int r;
0d07e595
JK
2276
2277 assert(filename);
2278 assert(section);
2279 assert(lvalue);
2280 assert(rvalue);
2281 assert(data);
2282
f4859fc7 2283 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2284 if (r == -ENOMEM)
2285 return log_oom();
2286 if (r < 0) {
2287 log_syntax(unit, LOG_WARNING, filename, line, r,
2288 "Failed to allocate route, ignoring assignment: %m");
2289 return 0;
2290 }
0d07e595 2291
01d4e732
YW
2292 if (n->family == AF_UNSPEC)
2293 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
2294 else
2295 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 2296 if (r < 0) {
d96edb2c 2297 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 2298 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
2299 return 0;
2300 }
2301
aff44301 2302 TAKE_PTR(n);
0d07e595
JK
2303 return 0;
2304}
2305
27efb52b
YW
2306int config_parse_destination(
2307 const char *unit,
6ae115c1
TG
2308 const char *filename,
2309 unsigned line,
2310 const char *section,
2311 unsigned section_line,
2312 const char *lvalue,
2313 int ltype,
2314 const char *rvalue,
2315 void *data,
2316 void *userdata) {
44e7b949 2317
6ae115c1 2318 Network *network = userdata;
fcbf4cb7 2319 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
2320 union in_addr_union *buffer;
2321 unsigned char *prefixlen;
ca3bad65 2322 int r;
6ae115c1
TG
2323
2324 assert(filename);
2325 assert(section);
2326 assert(lvalue);
2327 assert(rvalue);
2328 assert(data);
2329
f4859fc7 2330 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2331 if (r == -ENOMEM)
2332 return log_oom();
2333 if (r < 0) {
2334 log_syntax(unit, LOG_WARNING, filename, line, r,
2335 "Failed to allocate route, ignoring assignment: %m");
2336 return 0;
2337 }
6ae115c1 2338
9e7e4408 2339 if (streq(lvalue, "Destination")) {
7934dede
YW
2340 buffer = &n->dst;
2341 prefixlen = &n->dst_prefixlen;
9e7e4408 2342 } else if (streq(lvalue, "Source")) {
7934dede
YW
2343 buffer = &n->src;
2344 prefixlen = &n->src_prefixlen;
9e7e4408 2345 } else
04499a70 2346 assert_not_reached();
9e7e4408 2347
01d4e732
YW
2348 if (n->family == AF_UNSPEC)
2349 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
2350 else
2351 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 2352 if (r < 0) {
d96edb2c 2353 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 2354 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
2355 return 0;
2356 }
2357
aff44301 2358 TAKE_PTR(n);
6ae115c1
TG
2359 return 0;
2360}
5d8e593d 2361
27efb52b
YW
2362int config_parse_route_priority(
2363 const char *unit,
2364 const char *filename,
2365 unsigned line,
2366 const char *section,
2367 unsigned section_line,
2368 const char *lvalue,
2369 int ltype,
2370 const char *rvalue,
2371 void *data,
2372 void *userdata) {
2373
5d8e593d 2374 Network *network = userdata;
fcbf4cb7 2375 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
2376 int r;
2377
2378 assert(filename);
2379 assert(section);
2380 assert(lvalue);
2381 assert(rvalue);
2382 assert(data);
2383
f4859fc7 2384 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2385 if (r == -ENOMEM)
2386 return log_oom();
2387 if (r < 0) {
2388 log_syntax(unit, LOG_WARNING, filename, line, r,
2389 "Failed to allocate route, ignoring assignment: %m");
2390 return 0;
2391 }
5d8e593d 2392
aff44301 2393 r = safe_atou32(rvalue, &n->priority);
1c4b1179 2394 if (r < 0) {
d96edb2c 2395 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
2396 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
2397 return 0;
2398 }
5d8e593d 2399
c27abcf4 2400 n->priority_set = true;
aff44301 2401 TAKE_PTR(n);
5d8e593d
SS
2402 return 0;
2403}
769b56a3 2404
27efb52b
YW
2405int config_parse_route_scope(
2406 const char *unit,
2407 const char *filename,
2408 unsigned line,
2409 const char *section,
2410 unsigned section_line,
2411 const char *lvalue,
2412 int ltype,
2413 const char *rvalue,
2414 void *data,
2415 void *userdata) {
2416
769b56a3 2417 Network *network = userdata;
fcbf4cb7 2418 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
2419 int r;
2420
2421 assert(filename);
2422 assert(section);
2423 assert(lvalue);
2424 assert(rvalue);
2425 assert(data);
2426
f4859fc7 2427 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2428 if (r == -ENOMEM)
2429 return log_oom();
2430 if (r < 0) {
2431 log_syntax(unit, LOG_WARNING, filename, line, r,
2432 "Failed to allocate route, ignoring assignment: %m");
2433 return 0;
2434 }
769b56a3 2435
41b90a1e
YW
2436 r = route_scope_from_string(rvalue);
2437 if (r < 0) {
b98680b2 2438 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
2439 return 0;
2440 }
2441
41b90a1e 2442 n->scope = r;
94d6e299 2443 n->scope_set = true;
aff44301 2444 TAKE_PTR(n);
769b56a3
TG
2445 return 0;
2446}
c953b24c 2447
324e3422
YW
2448int config_parse_route_nexthop(
2449 const char *unit,
2450 const char *filename,
2451 unsigned line,
2452 const char *section,
2453 unsigned section_line,
2454 const char *lvalue,
2455 int ltype,
2456 const char *rvalue,
2457 void *data,
2458 void *userdata) {
2459
2460 Network *network = userdata;
2461 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2462 uint32_t id;
2463 int r;
2464
2465 assert(filename);
2466 assert(section);
2467 assert(lvalue);
2468 assert(rvalue);
2469 assert(data);
2470
2471 r = route_new_static(network, filename, section_line, &n);
2472 if (r == -ENOMEM)
2473 return log_oom();
2474 if (r < 0) {
2475 log_syntax(unit, LOG_WARNING, filename, line, r,
2476 "Failed to allocate route, ignoring assignment: %m");
2477 return 0;
2478 }
2479
2480 if (isempty(rvalue)) {
2481 n->nexthop_id = 0;
2482 TAKE_PTR(n);
2483 return 0;
2484 }
2485
2486 r = safe_atou32(rvalue, &id);
2487 if (r < 0) {
2488 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse nexthop ID, ignoring assignment: %s", rvalue);
2489 return 0;
2490 }
2491 if (id == 0) {
2492 log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid nexthop ID, ignoring assignment: %s", rvalue);
2493 return 0;
2494 }
2495
2496 n->nexthop_id = id;
2497 TAKE_PTR(n);
2498 return 0;
2499}
2500
27efb52b
YW
2501int config_parse_route_table(
2502 const char *unit,
2503 const char *filename,
2504 unsigned line,
2505 const char *section,
2506 unsigned section_line,
2507 const char *lvalue,
2508 int ltype,
2509 const char *rvalue,
2510 void *data,
2511 void *userdata) {
2512
fcbf4cb7 2513 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 2514 Network *network = userdata;
c953b24c
SS
2515 int r;
2516
2517 assert(filename);
2518 assert(section);
2519 assert(lvalue);
2520 assert(rvalue);
2521 assert(data);
2522
f4859fc7 2523 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2524 if (r == -ENOMEM)
2525 return log_oom();
2526 if (r < 0) {
2527 log_syntax(unit, LOG_WARNING, filename, line, r,
2528 "Failed to allocate route, ignoring assignment: %m");
2529 return 0;
2530 }
c953b24c 2531
552b90a2 2532 r = manager_get_route_table_from_string(network->manager, rvalue, &n->table);
c038ce46
SS
2533 if (r < 0) {
2534 log_syntax(unit, LOG_WARNING, filename, line, r,
2535 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
2536 return 0;
c953b24c
SS
2537 }
2538
94d6e299 2539 n->table_set = true;
aff44301 2540 TAKE_PTR(n);
c953b24c
SS
2541 return 0;
2542}
28959f7d 2543
d96edb2c 2544int config_parse_route_boolean(
27efb52b
YW
2545 const char *unit,
2546 const char *filename,
2547 unsigned line,
2548 const char *section,
2549 unsigned section_line,
2550 const char *lvalue,
2551 int ltype,
2552 const char *rvalue,
2553 void *data,
2554 void *userdata) {
2555
28959f7d 2556 Network *network = userdata;
fcbf4cb7 2557 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
2558 int r;
2559
2560 assert(filename);
2561 assert(section);
2562 assert(lvalue);
2563 assert(rvalue);
2564 assert(data);
2565
2566 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2567 if (r == -ENOMEM)
2568 return log_oom();
2569 if (r < 0) {
2570 log_syntax(unit, LOG_WARNING, filename, line, r,
2571 "Failed to allocate route, ignoring assignment: %m");
2572 return 0;
2573 }
28959f7d
SS
2574
2575 r = parse_boolean(rvalue);
2576 if (r < 0) {
d96edb2c 2577 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 2578 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
2579 return 0;
2580 }
2581
d96edb2c
YW
2582 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
2583 n->gateway_onlink = r;
2584 else if (streq(lvalue, "QuickAck"))
2585 n->quickack = r;
2586 else if (streq(lvalue, "FastOpenNoCookie"))
2587 n->fast_open_no_cookie = r;
2588 else if (streq(lvalue, "TTLPropagate"))
2589 n->ttl_propagate = r;
2590 else
04499a70 2591 assert_not_reached();
54901fd2 2592
aff44301 2593 TAKE_PTR(n);
b5bf6f64
SS
2594 return 0;
2595}
2596
27efb52b
YW
2597int config_parse_ipv6_route_preference(
2598 const char *unit,
2599 const char *filename,
2600 unsigned line,
2601 const char *section,
2602 unsigned section_line,
2603 const char *lvalue,
2604 int ltype,
2605 const char *rvalue,
2606 void *data,
2607 void *userdata) {
2608
b5bf6f64 2609 Network *network = userdata;
fcbf4cb7 2610 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
2611 int r;
2612
4c7bd9cf 2613 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2614 if (r == -ENOMEM)
2615 return log_oom();
2616 if (r < 0) {
2617 log_syntax(unit, LOG_WARNING, filename, line, r,
2618 "Failed to allocate route, ignoring assignment: %m");
2619 return 0;
2620 }
4c7bd9cf 2621
b5bf6f64
SS
2622 if (streq(rvalue, "low"))
2623 n->pref = ICMPV6_ROUTER_PREF_LOW;
2624 else if (streq(rvalue, "medium"))
2625 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
2626 else if (streq(rvalue, "high"))
2627 n->pref = ICMPV6_ROUTER_PREF_HIGH;
2628 else {
d96edb2c 2629 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
2630 return 0;
2631 }
28959f7d 2632
c27abcf4 2633 n->pref_set = true;
aff44301 2634 TAKE_PTR(n);
28959f7d
SS
2635 return 0;
2636}
c83ecc04 2637
27efb52b
YW
2638int config_parse_route_protocol(
2639 const char *unit,
2640 const char *filename,
2641 unsigned line,
2642 const char *section,
2643 unsigned section_line,
2644 const char *lvalue,
2645 int ltype,
2646 const char *rvalue,
2647 void *data,
2648 void *userdata) {
2649
c83ecc04 2650 Network *network = userdata;
fcbf4cb7 2651 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
2652 int r;
2653
2654 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2655 if (r == -ENOMEM)
2656 return log_oom();
2657 if (r < 0) {
2658 log_syntax(unit, LOG_WARNING, filename, line, r,
2659 "Failed to allocate route, ignoring assignment: %m");
2660 return 0;
2661 }
c83ecc04 2662
1b64651b 2663 r = route_protocol_from_string(rvalue);
e4ffe103
YW
2664 if (r < 0) {
2665 log_syntax(unit, LOG_WARNING, filename, line, r,
2666 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
2667 return 0;
c83ecc04
SS
2668 }
2669
e4ffe103
YW
2670 n->protocol = r;
2671
aff44301 2672 TAKE_PTR(n);
c83ecc04
SS
2673 return 0;
2674}
983226f3 2675
27efb52b
YW
2676int config_parse_route_type(
2677 const char *unit,
2678 const char *filename,
2679 unsigned line,
2680 const char *section,
2681 unsigned section_line,
2682 const char *lvalue,
2683 int ltype,
2684 const char *rvalue,
2685 void *data,
2686 void *userdata) {
2687
983226f3 2688 Network *network = userdata;
fcbf4cb7 2689 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 2690 int t, r;
983226f3
SS
2691
2692 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2693 if (r == -ENOMEM)
2694 return log_oom();
2695 if (r < 0) {
2696 log_syntax(unit, LOG_WARNING, filename, line, r,
2697 "Failed to allocate route, ignoring assignment: %m");
2698 return 0;
2699 }
983226f3 2700
7a22312d
YW
2701 t = route_type_from_string(rvalue);
2702 if (t < 0) {
b98680b2 2703 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 2704 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
2705 return 0;
2706 }
2707
7a22312d
YW
2708 n->type = (unsigned char) t;
2709
aff44301 2710 TAKE_PTR(n);
983226f3
SS
2711 return 0;
2712}
323d9329 2713
007cac09
SS
2714int config_parse_tcp_advmss(
2715 const char *unit,
2716 const char *filename,
2717 unsigned line,
2718 const char *section,
2719 unsigned section_line,
2720 const char *lvalue,
2721 int ltype,
2722 const char *rvalue,
2723 void *data,
2724 void *userdata) {
2725
2726 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2727 Network *network = userdata;
2728 uint64_t u;
2729 int r;
2730
2731 assert(filename);
2732 assert(section);
2733 assert(lvalue);
2734 assert(rvalue);
2735 assert(data);
2736
2737 r = route_new_static(network, filename, section_line, &n);
2738 if (r == -ENOMEM)
2739 return log_oom();
2740 if (r < 0) {
2741 log_syntax(unit, LOG_WARNING, filename, line, r,
2742 "Failed to allocate route, ignoring assignment: %m");
2743 return 0;
2744 }
2745
2746 if (isempty(rvalue)) {
2747 n->advmss = 0;
78ebbf02 2748 TAKE_PTR(n);
007cac09
SS
2749 return 0;
2750 }
2751
2752 r = parse_size(rvalue, 1024, &u);
2753 if (r < 0) {
2754 log_syntax(unit, LOG_WARNING, filename, line, r,
2755 "Could not parse TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2756 return 0;
2757 }
2758
2759 if (u == 0 || u > UINT32_MAX) {
2760 log_syntax(unit, LOG_WARNING, filename, line, 0,
2761 "Invalid TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2762 return 0;
2763 }
2764
2765 n->advmss = u;
2766
2767 TAKE_PTR(n);
2768 return 0;
2769}
2770
27efb52b
YW
2771int config_parse_tcp_window(
2772 const char *unit,
2773 const char *filename,
2774 unsigned line,
2775 const char *section,
2776 unsigned section_line,
2777 const char *lvalue,
2778 int ltype,
2779 const char *rvalue,
2780 void *data,
2781 void *userdata) {
2782
fcbf4cb7 2783 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33 2784 Network *network = userdata;
fef160b5 2785 uint32_t k;
323d9329
SS
2786 int r;
2787
2788 assert(filename);
2789 assert(section);
2790 assert(lvalue);
2791 assert(rvalue);
2792 assert(data);
2793
2794 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2795 if (r == -ENOMEM)
2796 return log_oom();
2797 if (r < 0) {
2798 log_syntax(unit, LOG_WARNING, filename, line, r,
2799 "Failed to allocate route, ignoring assignment: %m");
2800 return 0;
2801 }
323d9329 2802
fef160b5 2803 r = safe_atou32(rvalue, &k);
f205a92a 2804 if (r < 0) {
d96edb2c 2805 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
2806 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2807 return 0;
2808 }
fef160b5 2809 if (k >= 1024) {
d96edb2c 2810 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 2811 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
2812 return 0;
2813 }
fd9d7de1
SS
2814 if (k == 0) {
2815 log_syntax(unit, LOG_WARNING, filename, line, 0,
2816 "Invalid TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2817 return 0;
2818 }
323d9329
SS
2819
2820 if (streq(lvalue, "InitialCongestionWindow"))
2821 n->initcwnd = k;
2822 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
2823 n->initrwnd = k;
f205a92a 2824 else
04499a70 2825 assert_not_reached();
323d9329 2826
aff44301 2827 TAKE_PTR(n);
323d9329
SS
2828 return 0;
2829}
09f5dfad 2830
cea79e66
SS
2831int config_parse_route_mtu(
2832 const char *unit,
2833 const char *filename,
2834 unsigned line,
2835 const char *section,
2836 unsigned section_line,
2837 const char *lvalue,
2838 int ltype,
2839 const char *rvalue,
2840 void *data,
2841 void *userdata) {
2842
2843 Network *network = userdata;
fcbf4cb7 2844 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
2845 int r;
2846
2847 assert(filename);
2848 assert(section);
2849 assert(lvalue);
2850 assert(rvalue);
2851 assert(data);
2852
2853 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2854 if (r == -ENOMEM)
2855 return log_oom();
2856 if (r < 0) {
2857 log_syntax(unit, LOG_WARNING, filename, line, r,
2858 "Failed to allocate route, ignoring assignment: %m");
2859 return 0;
2860 }
cea79e66
SS
2861
2862 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
2863 if (r < 0)
2864 return r;
2865
aff44301 2866 TAKE_PTR(n);
cea79e66
SS
2867 return 0;
2868}
fcbf4cb7 2869
6ff5cc6b
YW
2870int config_parse_multipath_route(
2871 const char *unit,
2872 const char *filename,
2873 unsigned line,
2874 const char *section,
2875 unsigned section_line,
2876 const char *lvalue,
2877 int ltype,
2878 const char *rvalue,
2879 void *data,
2880 void *userdata) {
2881
e8f52f3c 2882 _cleanup_(multipath_route_freep) MultipathRoute *m = NULL;
6ff5cc6b 2883 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
e8f52f3c 2884 _cleanup_free_ char *word = NULL;
6ff5cc6b 2885 Network *network = userdata;
6ff5cc6b
YW
2886 union in_addr_union a;
2887 int family, r;
e8f52f3c
YW
2888 const char *p;
2889 char *dev;
6ff5cc6b
YW
2890
2891 assert(filename);
2892 assert(section);
2893 assert(lvalue);
2894 assert(rvalue);
2895 assert(data);
2896
2897 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2898 if (r == -ENOMEM)
2899 return log_oom();
2900 if (r < 0) {
2901 log_syntax(unit, LOG_WARNING, filename, line, r,
2902 "Failed to allocate route, ignoring assignment: %m");
2903 return 0;
2904 }
6ff5cc6b
YW
2905
2906 if (isempty(rvalue)) {
e8f52f3c 2907 n->multipath_routes = ordered_set_free_with_destructor(n->multipath_routes, multipath_route_free);
6ff5cc6b
YW
2908 return 0;
2909 }
2910
2911 m = new0(MultipathRoute, 1);
2912 if (!m)
2913 return log_oom();
2914
2915 p = rvalue;
2916 r = extract_first_word(&p, &word, NULL, 0);
2917 if (r == -ENOMEM)
2918 return log_oom();
2919 if (r <= 0) {
d96edb2c 2920 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2921 "Invalid multipath route option, ignoring assignment: %s", rvalue);
2922 return 0;
2923 }
2924
2925 dev = strchr(word, '@');
2926 if (dev) {
e8f52f3c
YW
2927 *dev++ = '\0';
2928
8ed87c49
YW
2929 r = parse_ifindex(dev);
2930 if (r > 0)
2931 m->ifindex = r;
2932 else {
70a2d9dd
YW
2933 if (!ifname_valid_full(dev, IFNAME_VALID_ALTERNATIVE)) {
2934 log_syntax(unit, LOG_WARNING, filename, line, 0,
2935 "Invalid interface name '%s' in %s=, ignoring: %s", dev, lvalue, rvalue);
2936 return 0;
2937 }
2938
8ed87c49
YW
2939 m->ifname = strdup(dev);
2940 if (!m->ifname)
2941 return log_oom();
2942 }
e8f52f3c 2943 }
6ff5cc6b 2944
e8f52f3c 2945 r = in_addr_from_string_auto(word, &family, &a);
6ff5cc6b 2946 if (r < 0) {
d96edb2c 2947 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2948 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
2949 return 0;
2950 }
2951 m->gateway.address = a;
2952 m->gateway.family = family;
2953
6ff5cc6b
YW
2954 if (!isempty(p)) {
2955 r = safe_atou32(p, &m->weight);
2956 if (r < 0) {
d96edb2c 2957 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2958 "Invalid multipath route weight, ignoring assignment: %s", p);
2959 return 0;
2960 }
234106db
YW
2961 /* ip command takes weight in the range 1…255, while kernel takes the value in the
2962 * range 0…254. MultiPathRoute= setting also takes weight in the same range which ip
2963 * command uses, then networkd decreases by one and stores it to match the range which
2964 * kernel uses. */
6ff5cc6b 2965 if (m->weight == 0 || m->weight > 256) {
d96edb2c 2966 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
2967 "Invalid multipath route weight, ignoring assignment: %s", p);
2968 return 0;
2969 }
234106db 2970 m->weight--;
6ff5cc6b
YW
2971 }
2972
8cb34651
SS
2973 r = ordered_set_ensure_put(&n->multipath_routes, NULL, m);
2974 if (r == -ENOMEM)
6ff5cc6b 2975 return log_oom();
6ff5cc6b 2976 if (r < 0) {
d96edb2c 2977 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2978 "Failed to store multipath route, ignoring assignment: %m");
2979 return 0;
2980 }
2981
2982 TAKE_PTR(m);
2983 TAKE_PTR(n);
2984 return 0;
2985}
2986
c038ce46
SS
2987int config_parse_route_table_names(
2988 const char *unit,
2989 const char *filename,
2990 unsigned line,
2991 const char *section,
2992 unsigned section_line,
2993 const char *lvalue,
2994 int ltype,
2995 const char *rvalue,
2996 void *data,
2997 void *userdata) {
2998
552b90a2 2999 Manager *m = userdata;
c038ce46
SS
3000 int r;
3001
3002 assert(filename);
3003 assert(lvalue);
3004 assert(rvalue);
552b90a2 3005 assert(userdata);
c038ce46
SS
3006
3007 if (isempty(rvalue)) {
552b90a2
YW
3008 m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number);
3009 m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name);
c038ce46
SS
3010 return 0;
3011 }
3012
310eff72
YW
3013 for (const char *p = rvalue;;) {
3014 _cleanup_free_ char *name = NULL;
3015 uint32_t table;
3016 char *num;
c038ce46 3017
310eff72
YW
3018 r = extract_first_word(&p, &name, NULL, 0);
3019 if (r == -ENOMEM)
3020 return log_oom();
3021 if (r < 0) {
3022 log_syntax(unit, LOG_WARNING, filename, line, r,
3023 "Invalid RouteTable=, ignoring assignment: %s", rvalue);
3024 return 0;
3025 }
3026 if (r == 0)
3027 return 0;
c038ce46 3028
310eff72
YW
3029 num = strchr(name, ':');
3030 if (!num) {
3031 log_syntax(unit, LOG_WARNING, filename, line, 0,
3032 "Invalid route table name and number pair, ignoring assignment: %s", name);
3033 continue;
3034 }
c038ce46 3035
310eff72 3036 *num++ = '\0';
c038ce46 3037
310eff72
YW
3038 if (STR_IN_SET(name, "default", "main", "local")) {
3039 log_syntax(unit, LOG_WARNING, filename, line, 0,
3040 "Route table name %s already predefined. Ignoring assignment: %s:%s", name, name, num);
3041 continue;
3042 }
c038ce46 3043
310eff72
YW
3044 r = safe_atou32(num, &table);
3045 if (r < 0) {
3046 log_syntax(unit, LOG_WARNING, filename, line, r,
3047 "Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num);
3048 continue;
3049 }
3050 if (table == 0) {
3051 log_syntax(unit, LOG_WARNING, filename, line, 0,
3052 "Invalid route table number, ignoring assignment: %s:%s", name, num);
3053 continue;
3054 }
3055
552b90a2 3056 r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table));
310eff72
YW
3057 if (r == -ENOMEM)
3058 return log_oom();
3059 if (r == -EEXIST) {
3060 log_syntax(unit, LOG_WARNING, filename, line, r,
3061 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
3062 continue;
3063 }
3064 if (r < 0) {
3065 log_syntax(unit, LOG_WARNING, filename, line, r,
3066 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
3067 continue;
3068 }
552b90a2
YW
3069 if (r == 0)
3070 /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
3071 continue;
3072
3073 r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name);
3074 if (r < 0) {
3075 hashmap_remove(m->route_table_numbers_by_name, name);
3076
3077 if (r == -ENOMEM)
3078 return log_oom();
3079 if (r == -EEXIST)
3080 log_syntax(unit, LOG_WARNING, filename, line, r,
3081 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
3082 else
3083 log_syntax(unit, LOG_WARNING, filename, line, r,
3084 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
3085 continue;
3086 }
3087 assert(r > 0);
3088
3089 TAKE_PTR(name);
310eff72 3090 }
c038ce46
SS
3091}
3092
d9940a3f 3093static int route_section_verify(Route *route, Network *network) {
fcbf4cb7
YW
3094 if (section_is_invalid(route->section))
3095 return -EINVAL;
3096
956dbf36 3097 if (route->gateway_from_dhcp_or_ra) {
c3d679c4
YW
3098 if (route->gw_family == AF_UNSPEC) {
3099 /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
3100 switch (route->family) {
3101 case AF_UNSPEC:
3102 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
3103 "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
3104 route->section->filename, route->section->line);
3105 route->family = AF_INET;
3106 break;
3107 case AF_INET:
3108 case AF_INET6:
3109 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
3110 "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
3111 route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
3112 break;
3113 default:
3114 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3115 "%s: Invalid route family. Ignoring [Route] section from line %u.",
3116 route->section->filename, route->section->line);
3117 }
3118 route->gw_family = route->family;
3119 }
3120
956dbf36
YW
3121 if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
3122 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3123 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
3124 "Ignoring [Route] section from line %u.",
3125 route->section->filename, route->section->line);
3126
3127 if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
3128 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3129 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
3130 "Ignoring [Route] section from line %u.",
3131 route->section->filename, route->section->line);
3132 }
3133
c3d679c4 3134 /* When only Gateway= is specified, assume the route family based on the Gateway address. */
6dd53981
YW
3135 if (route->family == AF_UNSPEC)
3136 route->family = route->gw_family;
3137
fcbf4cb7
YW
3138 if (route->family == AF_UNSPEC) {
3139 assert(route->section);
3140
c3d679c4
YW
3141 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3142 "%s: Route section without Gateway=, Destination=, Source=, "
3143 "or PreferredSource= field configured. "
3144 "Ignoring [Route] section from line %u.",
3145 route->section->filename, route->section->line);
fcbf4cb7
YW
3146 }
3147
6dd53981
YW
3148 if (route->family == AF_INET6 && route->gw_family == AF_INET)
3149 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3150 "%s: IPv4 gateway is configured for IPv6 route. "
3151 "Ignoring [Route] section from line %u.",
3152 route->section->filename, route->section->line);
3153
c0d48bc5
YW
3154 if (!route->table_set && network->vrf) {
3155 route->table = VRF(network->vrf)->table;
3156 route->table_set = true;
3157 }
3158
f5c38922
YW
3159 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
3160 route->table = RT_TABLE_LOCAL;
3161
3162 if (!route->scope_set && route->family != AF_INET6) {
3163 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
3164 route->scope = RT_SCOPE_HOST;
3165 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
3166 route->scope = RT_SCOPE_LINK;
94d6e299
YW
3167 }
3168
fd7701bf
YW
3169 if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
3170 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
3171 route->scope = RT_SCOPE_UNIVERSE;
3172 }
3173
8973df5c
YW
3174 if (route->family == AF_INET6 && route->priority == 0)
3175 route->priority = IP6_RT_PRIO_USER;
3176
2ddd52d1
YW
3177 if (route->gateway_onlink < 0 && in_addr_is_set(route->gw_family, &route->gw) &&
3178 ordered_hashmap_isempty(network->addresses_by_section)) {
3179 /* If no address is configured, in most cases the gateway cannot be reachable.
3180 * TODO: we may need to improve the condition above. */
fcbf4cb7
YW
3181 log_warning("%s: Gateway= without static address configured. "
3182 "Enabling GatewayOnLink= option.",
3183 network->filename);
3184 route->gateway_onlink = true;
3185 }
3186
40785f53
YW
3187 if (route->family == AF_INET6) {
3188 MultipathRoute *m;
3189
3190 ORDERED_SET_FOREACH(m, route->multipath_routes)
3191 if (m->gateway.family == AF_INET)
3192 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3193 "%s: IPv4 multipath route is specified for IPv6 route. "
3194 "Ignoring [Route] section from line %u.",
3195 route->section->filename, route->section->line);
3196 }
3197
3c7f1c07
YW
3198 if ((route->gateway_from_dhcp_or_ra ||
3199 in_addr_is_set(route->gw_family, &route->gw)) &&
3200 !ordered_set_isempty(route->multipath_routes))
3201 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3202 "%s: Gateway= cannot be specified with MultiPathRoute=. "
3203 "Ignoring [Route] section from line %u.",
3204 route->section->filename, route->section->line);
3205
324e3422 3206 if (route->nexthop_id > 0 &&
3c7f1c07
YW
3207 (route->gateway_from_dhcp_or_ra ||
3208 in_addr_is_set(route->gw_family, &route->gw) ||
324e3422
YW
3209 !ordered_set_isempty(route->multipath_routes)))
3210 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
3211 "%s: NextHopId= cannot be specified with Gateway= or MultiPathRoute=. "
3212 "Ignoring [Route] section from line %u.",
3213 route->section->filename, route->section->line);
3214
fcbf4cb7
YW
3215 return 0;
3216}
d9940a3f 3217
13ffa39f 3218void network_drop_invalid_routes(Network *network) {
2a54a044 3219 Route *route;
d9940a3f
YW
3220
3221 assert(network);
3222
2a54a044 3223 HASHMAP_FOREACH(route, network->routes_by_section)
d9940a3f
YW
3224 if (route_section_verify(route, network) < 0)
3225 route_free(route);
3226}