]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
ci: switch back to meson-0.56.2
[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>
b5bf6f64 5
b5efdb8a 6#include "alloc-util.h"
fc2f9534 7#include "netlink-util.h"
ca5ad760 8#include "networkd-ipv4ll.h"
23f53b99 9#include "networkd-manager.h"
e2263711 10#include "networkd-network.h"
141318f7 11#include "networkd-nexthop.h"
6bedfcbb 12#include "networkd-route.h"
141318f7 13#include "networkd-routing-policy-rule.h"
6bedfcbb 14#include "parse-util.h"
d308bb99 15#include "socket-netlink.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
423c249c 25static uint32_t link_get_vrf_table(const Link *link) {
ac49887e
YW
26 return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN;
27}
28
423c249c 29uint32_t link_get_dhcp_route_table(const Link *link) {
ac49887e
YW
30 /* When the interface is part of an VRF use the VRFs routing table, unless
31 * another table is explicitly specified. */
32 if (link->network->dhcp_route_table_set)
33 return link->network->dhcp_route_table;
34 return link_get_vrf_table(link);
35}
36
423c249c 37uint32_t link_get_ipv6_accept_ra_route_table(const Link *link) {
ac49887e
YW
38 if (link->network->ipv6_accept_ra_route_table_set)
39 return link->network->ipv6_accept_ra_route_table;
40 return link_get_vrf_table(link);
41}
42
74154c2e
YW
43static const char * const route_type_table[__RTN_MAX] = {
44 [RTN_UNICAST] = "unicast",
45 [RTN_LOCAL] = "local",
46 [RTN_BROADCAST] = "broadcast",
47 [RTN_ANYCAST] = "anycast",
48 [RTN_MULTICAST] = "multicast",
49 [RTN_BLACKHOLE] = "blackhole",
50 [RTN_UNREACHABLE] = "unreachable",
51 [RTN_PROHIBIT] = "prohibit",
52 [RTN_THROW] = "throw",
53 [RTN_NAT] = "nat",
54 [RTN_XRESOLVE] = "xresolve",
55};
56
57assert_cc(__RTN_MAX <= UCHAR_MAX);
58DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_type, int);
59
60static const char * const route_scope_table[] = {
61 [RT_SCOPE_UNIVERSE] = "global",
62 [RT_SCOPE_SITE] = "site",
63 [RT_SCOPE_LINK] = "link",
64 [RT_SCOPE_HOST] = "host",
65 [RT_SCOPE_NOWHERE] = "nowhere",
66};
67
e4ffe103
YW
68DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_scope, int);
69DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_scope, int, UINT8_MAX);
74154c2e
YW
70
71static const char * const route_table_table[] = {
72 [RT_TABLE_DEFAULT] = "default",
73 [RT_TABLE_MAIN] = "main",
74 [RT_TABLE_LOCAL] = "local",
75};
76
552b90a2 77DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
74154c2e 78
552b90a2
YW
79int manager_get_route_table_from_string(const Manager *m, const char *s, uint32_t *ret) {
80 uint32_t t;
81 int r;
82
83 assert(m);
84 assert(s);
85 assert(ret);
86
87 r = route_table_from_string(s);
88 if (r >= 0) {
89 *ret = (uint32_t) r;
90 return 0;
91 }
92
93 t = PTR_TO_UINT32(hashmap_get(m->route_table_numbers_by_name, s));
94 if (t != 0) {
95 *ret = t;
96 return 0;
97 }
98
99 r = safe_atou32(s, &t);
100 if (r < 0)
101 return r;
102
103 if (t == 0)
104 return -ERANGE;
105
106 *ret = t;
107 return 0;
108}
109
110int manager_get_route_table_to_string(const Manager *m, uint32_t table, char **ret) {
111 _cleanup_free_ char *str = NULL;
74154c2e 112 const char *s;
552b90a2
YW
113
114 assert(m);
115 assert(ret);
116
117 if (table == 0)
118 return -EINVAL;
74154c2e
YW
119
120 s = route_table_to_string(table);
552b90a2
YW
121 if (!s)
122 s = hashmap_get(m->route_table_names_by_number, UINT32_TO_PTR(table));
74154c2e 123
552b90a2
YW
124 if (s) {
125 /* Currently, this is only used in debugging logs. To not confuse any bug
126 * reports, let's include the table number. */
127 if (asprintf(&str, "%s(%" PRIu32 ")", s, table) < 0)
128 return -ENOMEM;
129
130 *ret = TAKE_PTR(str);
131 return 0;
132 }
133
134 if (asprintf(&str, "%" PRIu32, table) < 0)
135 return -ENOMEM;
136
137 *ret = TAKE_PTR(str);
138 return 0;
74154c2e
YW
139}
140
141static const char * const route_protocol_table[] = {
142 [RTPROT_KERNEL] = "kernel",
143 [RTPROT_BOOT] = "boot",
144 [RTPROT_STATIC] = "static",
145};
146
e4ffe103 147DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(route_protocol, int, UINT8_MAX);
74154c2e
YW
148
149static const char * const route_protocol_full_table[] = {
150 [RTPROT_REDIRECT] = "redirect",
151 [RTPROT_KERNEL] = "kernel",
152 [RTPROT_BOOT] = "boot",
153 [RTPROT_STATIC] = "static",
154 [RTPROT_GATED] = "gated",
155 [RTPROT_RA] = "ra",
156 [RTPROT_MRT] = "mrt",
157 [RTPROT_ZEBRA] = "zebra",
158 [RTPROT_BIRD] = "bird",
159 [RTPROT_DNROUTED] = "dnrouted",
160 [RTPROT_XORP] = "xorp",
161 [RTPROT_NTK] = "ntk",
162 [RTPROT_DHCP] = "dhcp",
163 [RTPROT_MROUTED] = "mrouted",
164 [RTPROT_BABEL] = "babel",
165 [RTPROT_BGP] = "bgp",
166 [RTPROT_ISIS] = "isis",
167 [RTPROT_OSPF] = "ospf",
168 [RTPROT_RIP] = "rip",
169 [RTPROT_EIGRP] = "eigrp",
170};
171
e4ffe103 172DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(route_protocol_full, int, UINT8_MAX);
74154c2e 173
47d2d30d
ZJS
174static unsigned routes_max(void) {
175 static thread_local unsigned cached = 0;
47d2d30d
ZJS
176 _cleanup_free_ char *s4 = NULL, *s6 = NULL;
177 unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
178
179 if (cached > 0)
180 return cached;
181
778c8795
YW
182 if (sysctl_read_ip_property(AF_INET, NULL, "route/max_size", &s4) >= 0)
183 if (safe_atou(s4, &val4) >= 0 && val4 == 2147483647U)
47d2d30d
ZJS
184 /* This is the default "no limit" value in the kernel */
185 val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
47d2d30d 186
778c8795 187 if (sysctl_read_ip_property(AF_INET6, NULL, "route/max_size", &s6) >= 0)
47d2d30d 188 (void) safe_atou(s6, &val6);
47d2d30d
ZJS
189
190 cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
191 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
192 return cached;
193}
8c34b963 194
ed9e361a 195int route_new(Route **ret) {
8e766630 196 _cleanup_(route_freep) Route *route = NULL;
f0213e37 197
17f9c355 198 route = new(Route, 1);
f0213e37
TG
199 if (!route)
200 return -ENOMEM;
201
17f9c355
YW
202 *route = (Route) {
203 .family = AF_UNSPEC,
204 .scope = RT_SCOPE_UNIVERSE,
205 .protocol = RTPROT_UNSPEC,
206 .type = RTN_UNICAST,
207 .table = RT_TABLE_MAIN,
208 .lifetime = USEC_INFINITY,
209 .quickack = -1,
633c7258 210 .fast_open_no_cookie = -1,
54901fd2 211 .gateway_onlink = -1,
9b88f20a 212 .ttl_propagate = -1,
17f9c355 213 };
f0213e37 214
1cc6c93a 215 *ret = TAKE_PTR(route);
f0213e37
TG
216
217 return 0;
218}
219
9560e5b3 220static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
8e766630
LP
221 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
222 _cleanup_(route_freep) Route *route = NULL;
f0213e37 223 int r;
f579559b 224
8c34b963
LP
225 assert(network);
226 assert(ret);
2a54a044
YW
227 assert(filename);
228 assert(section_line > 0);
f4859fc7 229
2a54a044
YW
230 r = network_config_section_new(filename, section_line, &n);
231 if (r < 0)
232 return r;
6ae115c1 233
2a54a044
YW
234 route = hashmap_get(network->routes_by_section, n);
235 if (route) {
236 *ret = TAKE_PTR(route);
237 return 0;
6ae115c1
TG
238 }
239
2a54a044 240 if (hashmap_size(network->routes_by_section) >= routes_max())
8c34b963
LP
241 return -E2BIG;
242
ed9e361a 243 r = route_new(&route);
f0213e37
TG
244 if (r < 0)
245 return r;
801bd9e8 246
ed9e361a 247 route->protocol = RTPROT_STATIC;
0f7f2769 248 route->network = network;
2a54a044 249 route->section = TAKE_PTR(n);
cacc1dbf 250
ecd80ce2 251 r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route);
2a54a044
YW
252 if (r < 0)
253 return r;
6ae115c1 254
1cc6c93a 255 *ret = TAKE_PTR(route);
f579559b
TG
256 return 0;
257}
258
169948e9 259Route *route_free(Route *route) {
f579559b 260 if (!route)
169948e9 261 return NULL;
f579559b 262
f048a16b 263 if (route->network) {
2a54a044
YW
264 assert(route->section);
265 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 266 }
6ae115c1 267
fd45e522
ZJS
268 network_config_section_free(route->section);
269
1c8e710c 270 if (route->link) {
50550722 271 NDiscRoute *n;
50550722 272
1c8e710c
TG
273 set_remove(route->link->routes, route);
274 set_remove(route->link->routes_foreign, route);
6e537f62
YW
275 set_remove(route->link->dhcp_routes, route);
276 set_remove(route->link->dhcp_routes_old, route);
1633c457
YW
277 set_remove(route->link->dhcp6_routes, route);
278 set_remove(route->link->dhcp6_routes_old, route);
279 set_remove(route->link->dhcp6_pd_routes, route);
280 set_remove(route->link->dhcp6_pd_routes_old, route);
90e74a66 281 SET_FOREACH(n, route->link->ndisc_routes)
50550722
YW
282 if (n->route == route)
283 free(set_remove(route->link->ndisc_routes, n));
1c8e710c
TG
284 }
285
ad208fac
YW
286 if (route->manager) {
287 set_remove(route->manager->routes, route);
288 set_remove(route->manager->routes_foreign, route);
289 }
290
6ff5cc6b
YW
291 ordered_set_free_free(route->multipath_routes);
292
f833694d
TG
293 sd_event_source_unref(route->expire);
294
169948e9 295 return mfree(route);
f579559b
TG
296}
297
501b09db 298void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
299 assert(route);
300
301 siphash24_compress(&route->family, sizeof(route->family), state);
302
01aaa3df
YW
303 switch (route->family) {
304 case AF_INET:
305 case AF_INET6:
01aaa3df 306 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
67e05dd8
ZJS
307 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
308
01aaa3df 309 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
67e05dd8
ZJS
310 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
311
6dd53981 312 siphash24_compress(&route->gw_family, sizeof(route->gw_family), state);
40075951 313 if (IN_SET(route->gw_family, AF_INET, AF_INET6)) {
6dd53981 314 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->gw_family), state);
40075951
YW
315 siphash24_compress(&route->gw_weight, sizeof(route->gw_weight), state);
316 }
67e05dd8 317
01aaa3df
YW
318 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
319
320 siphash24_compress(&route->tos, sizeof(route->tos), state);
321 siphash24_compress(&route->priority, sizeof(route->priority), state);
322 siphash24_compress(&route->table, sizeof(route->table), state);
323 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
324 siphash24_compress(&route->scope, sizeof(route->scope), state);
325 siphash24_compress(&route->type, sizeof(route->type), state);
67e05dd8 326
fa3e401a
YW
327 siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
328 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
01aaa3df 329
007cac09
SS
330 siphash24_compress(&route->advmss, sizeof(route->advmss), state);
331
01aaa3df
YW
332 break;
333 default:
334 /* treat any other address family as AF_UNSPEC */
335 break;
336 }
337}
338
501b09db 339int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
340 int r;
341
342 r = CMP(a->family, b->family);
343 if (r != 0)
344 return r;
345
346 switch (a->family) {
347 case AF_INET:
348 case AF_INET6:
349 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
350 if (r != 0)
351 return r;
352
67e05dd8
ZJS
353 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
354 if (r != 0)
355 return r;
356
01aaa3df
YW
357 r = CMP(a->src_prefixlen, b->src_prefixlen);
358 if (r != 0)
359 return r;
360
67e05dd8 361 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
362 if (r != 0)
363 return r;
364
6dd53981 365 r = CMP(a->gw_family, b->gw_family);
01aaa3df
YW
366 if (r != 0)
367 return r;
368
6dd53981
YW
369 if (IN_SET(a->gw_family, AF_INET, AF_INET6)) {
370 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
371 if (r != 0)
372 return r;
40075951
YW
373
374 r = CMP(a->gw_weight, b->gw_weight);
375 if (r != 0)
376 return r;
6dd53981
YW
377 }
378
67e05dd8 379 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
380 if (r != 0)
381 return r;
382
67e05dd8 383 r = CMP(a->tos, b->tos);
01aaa3df
YW
384 if (r != 0)
385 return r;
386
67e05dd8 387 r = CMP(a->priority, b->priority);
01aaa3df
YW
388 if (r != 0)
389 return r;
390
67e05dd8 391 r = CMP(a->table, b->table);
01aaa3df
YW
392 if (r != 0)
393 return r;
394
67e05dd8 395 r = CMP(a->protocol, b->protocol);
fa3e401a
YW
396 if (r != 0)
397 return r;
398
67e05dd8 399 r = CMP(a->scope, b->scope);
fa3e401a
YW
400 if (r != 0)
401 return r;
402
67e05dd8 403 r = CMP(a->type, b->type);
01aaa3df
YW
404 if (r != 0)
405 return r;
406
67e05dd8 407 r = CMP(a->initcwnd, b->initcwnd);
01aaa3df
YW
408 if (r != 0)
409 return r;
410
67e05dd8 411 r = CMP(a->initrwnd, b->initrwnd);
01aaa3df
YW
412 if (r != 0)
413 return r;
414
007cac09
SS
415 r = CMP(a->advmss, b->advmss);
416 if (r != 0)
417 return r;
418
67e05dd8 419 return 0;
01aaa3df
YW
420 default:
421 /* treat any other address family as AF_UNSPEC */
422 return 0;
423 }
424}
425
426DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 427 route_hash_ops,
01aaa3df 428 Route,
c077a205
YW
429 route_hash_func,
430 route_compare_func,
01aaa3df
YW
431 route_free);
432
423c249c 433static bool route_equal(const Route *r1, const Route *r2) {
7ecf0c3e
TJ
434 if (r1 == r2)
435 return true;
436
437 if (!r1 || !r2)
438 return false;
439
440 return route_compare_func(r1, r2) == 0;
441}
442
423c249c 443static int route_get(const Manager *manager, const Link *link, const Route *in, Route **ret) {
f1368755 444 Route *existing;
1b566071 445
ad208fac 446 assert(manager || link);
f1368755 447 assert(in);
1c8e710c 448
ad208fac
YW
449 if (link) {
450 existing = set_get(link->routes, in);
451 if (existing) {
452 if (ret)
453 *ret = existing;
454 return 1;
455 }
1c8e710c 456
ad208fac
YW
457 existing = set_get(link->routes_foreign, in);
458 if (existing) {
459 if (ret)
460 *ret = existing;
461 return 0;
462 }
463 } else {
464 existing = set_get(manager->routes, in);
465 if (existing) {
466 if (ret)
467 *ret = existing;
468 return 1;
469 }
470
471 existing = set_get(manager->routes_foreign, in);
472 if (existing) {
473 if (ret)
474 *ret = existing;
475 return 0;
476 }
1b566071 477 }
1c8e710c 478
1b566071 479 return -ENOENT;
1c8e710c
TG
480}
481
f9bb3338
YW
482static void route_copy(Route *dest, const Route *src, const MultipathRoute *m) {
483 assert(dest);
484 assert(src);
485
486 dest->family = src->family;
487 dest->src = src->src;
488 dest->src_prefixlen = src->src_prefixlen;
489 dest->dst = src->dst;
490 dest->dst_prefixlen = src->dst_prefixlen;
491 dest->prefsrc = src->prefsrc;
492 dest->scope = src->scope;
493 dest->protocol = src->protocol;
494 dest->type = src->type;
495 dest->tos = src->tos;
496 dest->priority = src->priority;
497 dest->table = src->table;
498 dest->initcwnd = src->initcwnd;
499 dest->initrwnd = src->initrwnd;
500 dest->lifetime = src->lifetime;
007cac09 501 dest->advmss= src->advmss;
f9bb3338
YW
502
503 if (m) {
504 dest->gw_family = m->gateway.family;
505 dest->gw = m->gateway.address;
506 dest->gw_weight = m->weight;
507 } else {
508 dest->gw_family = src->gw_family;
509 dest->gw = src->gw;
40075951 510 dest->gw_weight = src->gw_weight;
f9bb3338
YW
511 }
512}
889b550f 513
cc17f75f 514static int route_add_internal(Manager *manager, Link *link, Set **routes, const Route *in, Route **ret) {
8e766630 515 _cleanup_(route_freep) Route *route = NULL;
1c8e710c
TG
516 int r;
517
ad208fac 518 assert(manager || link);
1c8e710c 519 assert(routes);
f1368755 520 assert(in);
1c8e710c
TG
521
522 r = route_new(&route);
523 if (r < 0)
524 return r;
525
cc17f75f 526 route_copy(route, in, NULL);
1c8e710c 527
de7fef4b 528 r = set_ensure_put(routes, &route_hash_ops, route);
1c8e710c
TG
529 if (r < 0)
530 return r;
75a302b5
YW
531 if (r == 0)
532 return -EEXIST;
1c8e710c
TG
533
534 route->link = link;
ad208fac 535 route->manager = manager;
1c8e710c
TG
536
537 if (ret)
538 *ret = route;
539
540 route = NULL;
541
542 return 0;
543}
544
423c249c 545static int route_add_foreign(Manager *manager, Link *link, const Route *in, Route **ret) {
ad208fac 546 assert(manager || link);
cc17f75f 547 return route_add_internal(manager, link, link ? &link->routes_foreign : &manager->routes_foreign, in, ret);
1c8e710c
TG
548}
549
f9bb3338 550static int route_add(Manager *manager, Link *link, const Route *in, const MultipathRoute *m, Route **ret) {
cc17f75f 551 _cleanup_(route_freep) Route *tmp = NULL;
d9eee312 552 bool is_new = false;
1c8e710c
TG
553 Route *route;
554 int r;
555
ad208fac
YW
556 assert(manager || link);
557 assert(in);
558
cc17f75f
YW
559 if (m) {
560 assert(link && (m->ifindex == 0 || m->ifindex == link->ifindex));
561
562 r = route_new(&tmp);
563 if (r < 0)
564 return r;
565
566 route_copy(tmp, in, m);
567 in = tmp;
568 }
569
ad208fac 570 r = route_get(manager, link, in, &route);
1c8e710c
TG
571 if (r == -ENOENT) {
572 /* Route does not exist, create a new one */
cc17f75f 573 r = route_add_internal(manager, link, link ? &link->routes : &manager->routes, in, &route);
1c8e710c
TG
574 if (r < 0)
575 return r;
d9eee312 576 is_new = true;
1c8e710c
TG
577 } else if (r == 0) {
578 /* Take over a foreign route */
ad208fac
YW
579 if (link) {
580 r = set_ensure_put(&link->routes, &route_hash_ops, route);
581 if (r < 0)
582 return r;
1c8e710c 583
ad208fac
YW
584 set_remove(link->routes_foreign, route);
585 } else {
586 r = set_ensure_put(&manager->routes, &route_hash_ops, route);
587 if (r < 0)
588 return r;
589
590 set_remove(manager->routes_foreign, route);
591 }
1c8e710c
TG
592 } else if (r == 1) {
593 /* Route exists, do nothing */
594 ;
595 } else
596 return r;
597
856e309d
MC
598 if (ret)
599 *ret = route;
d9eee312 600 return is_new;
1c8e710c
TG
601}
602
6c252588
YW
603static bool route_type_is_reject(const Route *route) {
604 assert(route);
605
606 return IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW);
607}
608
552b90a2 609static void log_route_debug(const Route *route, const char *str, const Link *link, const Manager *m) {
167a5561
YW
610 assert(route);
611 assert(str);
552b90a2 612 assert(m);
167a5561
YW
613
614 /* link may be NULL. */
615
616 if (DEBUG_LOGGING) {
e4ffe103
YW
617 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL,
618 *prefsrc = NULL, *table = NULL, *scope = NULL, *proto = NULL;
167a5561
YW
619
620 if (!in_addr_is_null(route->family, &route->dst)) {
621 (void) in_addr_to_string(route->family, &route->dst, &dst);
622 (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
623 }
624 if (!in_addr_is_null(route->family, &route->src))
625 (void) in_addr_to_string(route->family, &route->src, &src);
626 if (!in_addr_is_null(route->gw_family, &route->gw))
627 (void) in_addr_to_string(route->gw_family, &route->gw, &gw);
628 if (!in_addr_is_null(route->family, &route->prefsrc))
629 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
e4ffe103 630 (void) route_scope_to_string_alloc(route->scope, &scope);
552b90a2 631 (void) manager_get_route_table_to_string(m, route->table, &table);
e4ffe103 632 (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
167a5561
YW
633
634 log_link_debug(link,
635 "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
636 str, strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
e4ffe103 637 strna(scope), strna(table), strna(proto),
167a5561
YW
638 strna(route_type_to_string(route->type)));
639 }
640}
641
5f4d7aa4
YW
642static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
643 unsigned flags;
644 int r;
645
646 assert(route);
647 assert(req);
648
649 /* link may be NULL */
650
651 if (in_addr_is_null(route->gw_family, &route->gw) == 0) {
652 if (route->gw_family == route->family) {
653 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
654 if (r < 0)
655 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
656 } else {
657 RouteVia rtvia = {
658 .family = route->gw_family,
659 .address = route->gw,
660 };
661
662 r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
663 if (r < 0)
664 return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
665 }
666 }
667
668 if (route->dst_prefixlen > 0) {
669 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
670 if (r < 0)
671 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
672
673 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
674 if (r < 0)
675 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
676 }
677
678 if (route->src_prefixlen > 0) {
679 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
680 if (r < 0)
681 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
682
683 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
684 if (r < 0)
685 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
686 }
687
688 if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
689 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
690 if (r < 0)
691 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
692 }
693
694 r = sd_rtnl_message_route_set_scope(req, route->scope);
695 if (r < 0)
696 return log_link_error_errno(link, r, "Could not set scope: %m");
697
698 flags = route->flags;
699 if (route->gateway_onlink >= 0)
700 SET_FLAG(flags, RTNH_F_ONLINK, route->gateway_onlink);
701
702 r = sd_rtnl_message_route_set_flags(req, flags);
703 if (r < 0)
704 return log_link_error_errno(link, r, "Could not set flags: %m");
705
706 if (route->table != RT_TABLE_MAIN) {
707 if (route->table < 256) {
708 r = sd_rtnl_message_route_set_table(req, route->table);
709 if (r < 0)
710 return log_link_error_errno(link, r, "Could not set route table: %m");
711 } else {
712 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
713 if (r < 0)
714 return log_link_error_errno(link, r, "Could not set route table: %m");
715
716 /* Table attribute to allow more than 256. */
717 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
718 if (r < 0)
719 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
720 }
721 }
722
723 r = sd_rtnl_message_route_set_type(req, route->type);
724 if (r < 0)
725 return log_link_error_errno(link, r, "Could not set route type: %m");
726
6c252588 727 if (!route_type_is_reject(route)) {
5f4d7aa4
YW
728 assert(link); /* Those routes must be attached to a specific link */
729
730 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
731 if (r < 0)
732 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
733 }
734
735 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
736 if (r < 0)
737 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
738
739 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
740 if (r < 0)
741 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
742
743 return 0;
744}
745
302a796f 746static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
747 int r;
748
749 assert(m);
4645ad47 750
ad208fac
YW
751 /* Note that link may be NULL. */
752 if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4645ad47
YW
753 return 1;
754
755 r = sd_netlink_message_get_errno(m);
756 if (r < 0 && r != -ESRCH)
5ecb131d 757 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
4645ad47
YW
758
759 return 1;
760}
761
ad208fac 762int route_remove(
423c249c 763 const Route *route,
ad208fac
YW
764 Manager *manager,
765 Link *link,
766 link_netlink_message_handler_t callback) {
27efb52b 767
4afd3348 768 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
769 int r;
770
ad208fac 771 assert(link || manager);
4c701096 772 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9 773
ad208fac
YW
774 if (!manager)
775 manager = link->manager;
081b3009 776 /* link may be NULL! */
ad208fac 777
552b90a2 778 log_route_debug(route, "Removing", link, manager);
167a5561 779
ad208fac 780 r = sd_rtnl_message_new_route(manager->rtnl, &req,
28cc555d
DW
781 RTM_DELROUTE, route->family,
782 route->protocol);
f647962d 783 if (r < 0)
7750b796 784 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 785
5f4d7aa4 786 r = route_set_netlink_message(route, req, link);
f647962d 787 if (r < 0)
5f4d7aa4 788 return r;
5c1d3fc9 789
bac319a7 790 r = netlink_call_async(manager->rtnl, NULL, req,
302a796f
YW
791 callback ?: route_remove_handler,
792 link_netlink_destroy_callback, link);
f647962d 793 if (r < 0)
7750b796 794 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
5c1d3fc9 795
081b3009 796 link_ref(link); /* link may be NULL, link_ref() is OK with that */
563c69c6 797
5c1d3fc9
UTL
798 return 0;
799}
800
1132a714 801static bool link_has_route(const Link *link, const Route *route) {
779804dd
YW
802 Route *net_route;
803
804 assert(link);
805 assert(route);
806
807 if (!link->network)
808 return false;
809
2a54a044 810 HASHMAP_FOREACH(net_route, link->network->routes_by_section)
779804dd
YW
811 if (route_equal(net_route, route))
812 return true;
813
814 return false;
815}
816
4055ec93 817static bool links_have_route(const Manager *manager, const Route *route, const Link *except) {
1132a714
YW
818 Link *link;
819
820 assert(manager);
821
822 HASHMAP_FOREACH(link, manager->links) {
823 if (link == except)
824 continue;
825
826 if (link_has_route(link, route))
827 return true;
828 }
829
830 return false;
831}
832
4055ec93 833static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) {
1132a714
YW
834 Route *route;
835 int k, r = 0;
4055ec93 836 Set *routes;
1132a714
YW
837
838 assert(manager);
839
4055ec93
YW
840 routes = foreign ? manager->routes_foreign : manager->routes;
841 SET_FOREACH(route, routes) {
842 /* Do not touch routes managed by the kernel */
1132a714
YW
843 if (route->protocol == RTPROT_KERNEL)
844 continue;
845
4055ec93
YW
846 /* The route will be configured later, or already configured by a link */
847 if (links_have_route(manager, route, except))
1132a714
YW
848 continue;
849
850 /* The existing links do not have the route. Let's drop this now. It may by
851 * re-configured later. */
852 k = route_remove(route, manager, NULL, NULL);
853 if (k < 0 && r >= 0)
854 r = k;
855 }
856
857 return r;
858}
859
4055ec93
YW
860static int manager_drop_foreign_routes(Manager *manager) {
861 return manager_drop_routes_internal(manager, true, NULL);
862}
1132a714 863
4055ec93
YW
864static int manager_drop_routes(Manager *manager, const Link *except) {
865 return manager_drop_routes_internal(manager, false, except);
1132a714
YW
866}
867
779804dd
YW
868int link_drop_foreign_routes(Link *link) {
869 Route *route;
870 int k, r = 0;
871
872 assert(link);
1132a714 873 assert(link->manager);
779804dd
YW
874
875 SET_FOREACH(route, link->routes_foreign) {
876 /* do not touch routes managed by the kernel */
877 if (route->protocol == RTPROT_KERNEL)
878 continue;
879
880 /* do not touch multicast route added by kernel */
881 /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that.
882 * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
883 if (route->protocol == RTPROT_BOOT &&
884 route->family == AF_INET6 &&
885 route->dst_prefixlen == 8 &&
886 in_addr_equal(AF_INET6, &route->dst, &(union in_addr_union) { .in6 = {{{ 0xff,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }}} }))
887 continue;
888
889 if (route->protocol == RTPROT_STATIC && link->network &&
890 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
891 continue;
892
893 if (route->protocol == RTPROT_DHCP && link->network &&
894 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
895 continue;
896
1132a714 897 if (link_has_route(link, route))
f9bb3338 898 k = route_add(NULL, link, route, NULL, NULL);
779804dd 899 else
ad208fac 900 k = route_remove(route, NULL, link, NULL);
779804dd
YW
901 if (k < 0 && r >= 0)
902 r = k;
903 }
904
1132a714
YW
905 k = manager_drop_foreign_routes(link->manager);
906 if (k < 0 && r >= 0)
907 r = k;
908
779804dd
YW
909 return r;
910}
911
62f0ea5f
YW
912int link_drop_routes(Link *link) {
913 Route *route;
914 int k, r = 0;
915
916 assert(link);
917
918 SET_FOREACH(route, link->routes) {
919 /* do not touch routes managed by the kernel */
920 if (route->protocol == RTPROT_KERNEL)
921 continue;
922
ad208fac 923 k = route_remove(route, NULL, link, NULL);
62f0ea5f
YW
924 if (k < 0 && r >= 0)
925 r = k;
926 }
927
1132a714
YW
928 k = manager_drop_routes(link->manager, link);
929 if (k < 0 && r >= 0)
930 r = k;
931
62f0ea5f
YW
932 return r;
933}
934
74154c2e 935static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
f833694d
TG
936 Route *route = userdata;
937 int r;
938
939 assert(route);
940
ad208fac 941 r = route_remove(route, route->manager, route->link, NULL);
d6ad41e2 942 if (r < 0) {
98b02994 943 log_link_warning_errno(route->link, r, "Could not remove route: %m");
fe7ca21a 944 route_free(route);
d6ad41e2 945 }
f833694d
TG
946
947 return 1;
948}
949
f9bb3338
YW
950static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
951 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
952 Route *nr;
d9eee312 953 int r, k;
f9bb3338
YW
954
955 assert(link);
956 assert(route);
957
6c252588 958 if (route_type_is_reject(route))
d9eee312 959 k = route_add(link->manager, NULL, route, NULL, &nr);
f9bb3338 960 else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
d9eee312 961 k = route_add(NULL, link, route, m, &nr);
f9bb3338
YW
962 else {
963 Link *link_gw;
964
965 r = link_get(link->manager, m->ifindex, &link_gw);
966 if (r < 0)
967 return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
968
d9eee312 969 k = route_add(NULL, link_gw, route, m, &nr);
f9bb3338 970 }
d9eee312
YW
971 if (k < 0)
972 return log_link_error_errno(link, k, "Could not add route: %m");
f9bb3338
YW
973
974 /* TODO: drop expiration handling once it can be pushed into the kernel */
975 if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
976 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
977 nr->lifetime, 0, route_expire_handler, nr);
978 if (r < 0)
979 return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
980 }
981
982 sd_event_source_unref(nr->expire);
983 nr->expire = TAKE_PTR(expire);
984
985 if (ret)
986 *ret = nr;
987
d9eee312 988 return k;
f9bb3338
YW
989}
990
423c249c 991static int append_nexthop_one(const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
6ff5cc6b
YW
992 struct rtnexthop *rtnh;
993 struct rtattr *new_rta;
994 int r;
995
996 assert(route);
997 assert(m);
998 assert(rta);
999 assert(*rta);
1000
1001 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
1002 if (!new_rta)
1003 return -ENOMEM;
1004 *rta = new_rta;
1005
1006 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1007 *rtnh = (struct rtnexthop) {
1008 .rtnh_len = sizeof(*rtnh),
1009 .rtnh_ifindex = m->ifindex,
1010 .rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
1011 };
1012
1013 (*rta)->rta_len += sizeof(struct rtnexthop);
1014
1015 if (route->family == m->gateway.family) {
1016 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
1017 if (r < 0)
1018 goto clear;
1019 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1020 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
1021 } else {
1022 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1023 if (r < 0)
1024 goto clear;
1025 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1026 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1027 }
1028
1029 return 0;
1030
1031clear:
1032 (*rta)->rta_len -= sizeof(struct rtnexthop);
1033 return r;
1034}
1035
423c249c 1036static int append_nexthops(const Route *route, sd_netlink_message *req) {
6ff5cc6b
YW
1037 _cleanup_free_ struct rtattr *rta = NULL;
1038 struct rtnexthop *rtnh;
1039 MultipathRoute *m;
1040 size_t offset;
6ff5cc6b
YW
1041 int r;
1042
1043 if (ordered_set_isempty(route->multipath_routes))
1044 return 0;
1045
1046 rta = new(struct rtattr, 1);
1047 if (!rta)
1048 return -ENOMEM;
1049
1050 *rta = (struct rtattr) {
1051 .rta_type = RTA_MULTIPATH,
1052 .rta_len = RTA_LENGTH(0),
1053 };
1054 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
1055
90e74a66 1056 ORDERED_SET_FOREACH(m, route->multipath_routes) {
6ff5cc6b
YW
1057 r = append_nexthop_one(route, m, &rta, offset);
1058 if (r < 0)
1059 return r;
1060
1061 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
1062 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
1063 }
1064
1065 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
1066 if (r < 0)
1067 return r;
1068
1069 return 0;
1070}
1071
1b566071 1072int route_configure(
423c249c 1073 const Route *route,
1b566071 1074 Link *link,
80b0e860
YW
1075 link_netlink_message_handler_t callback,
1076 Route **ret) {
1b566071 1077
4afd3348 1078 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
d9eee312 1079 int r, k = 0;
0ef9f3c7 1080 Route *nr;
f579559b 1081
f579559b 1082 assert(link);
f882c247
TG
1083 assert(link->manager);
1084 assert(link->manager->rtnl);
f579559b 1085 assert(link->ifindex > 0);
4c701096 1086 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 1087 assert(callback);
f579559b 1088
ad208fac 1089 if (route_get(link->manager, link, route, NULL) <= 0 &&
47d2d30d 1090 set_size(link->routes) >= routes_max())
7750b796
YW
1091 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
1092 "Too many routes are configured, refusing: %m");
1b566071 1093
552b90a2 1094 log_route_debug(route, "Configuring", link, link->manager);
156ed65e 1095
151b9b96 1096 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
1097 RTM_NEWROUTE, route->family,
1098 route->protocol);
f647962d 1099 if (r < 0)
7750b796 1100 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
f579559b 1101
5f4d7aa4 1102 r = route_set_netlink_message(route, req, link);
f647962d 1103 if (r < 0)
5f4d7aa4 1104 return r;
3b015d40 1105
f02ba163
DD
1106 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
1107 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
1108 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
1109 if (r < 0)
7750b796 1110 return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
f02ba163
DD
1111 }
1112
9b88f20a
SS
1113 if (route->ttl_propagate >= 0) {
1114 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
1115 if (r < 0)
1116 return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
1117 }
1118
d6fceaf1
SS
1119 r = sd_netlink_message_open_container(req, RTA_METRICS);
1120 if (r < 0)
7750b796 1121 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1
SS
1122
1123 if (route->mtu > 0) {
1124 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
1125 if (r < 0)
7750b796 1126 return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
d6fceaf1
SS
1127 }
1128
6b21ad33 1129 if (route->initcwnd > 0) {
323d9329
SS
1130 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
1131 if (r < 0)
7750b796 1132 return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
323d9329
SS
1133 }
1134
6b21ad33 1135 if (route->initrwnd > 0) {
323d9329
SS
1136 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
1137 if (r < 0)
7750b796 1138 return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
323d9329
SS
1139 }
1140
67c193bf 1141 if (route->quickack >= 0) {
09f5dfad
SS
1142 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
1143 if (r < 0)
7750b796 1144 return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
09f5dfad
SS
1145 }
1146
633c7258
SS
1147 if (route->fast_open_no_cookie >= 0) {
1148 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
1149 if (r < 0)
1150 return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
1151 }
1152
007cac09
SS
1153 if (route->advmss > 0) {
1154 r = sd_netlink_message_append_u32(req, RTAX_ADVMSS, route->advmss);
1155 if (r < 0)
1156 return log_link_error_errno(link, r, "Could not append RTAX_ADVMSS attribute: %m");
1157 }
1158
d6fceaf1
SS
1159 r = sd_netlink_message_close_container(req);
1160 if (r < 0)
7750b796 1161 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1 1162
6ff5cc6b
YW
1163 r = append_nexthops(route, req);
1164 if (r < 0)
1165 return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
1166
f9bb3338 1167 if (ordered_set_isempty(route->multipath_routes)) {
d9eee312
YW
1168 k = route_add_and_setup_timer(link, route, NULL, &nr);
1169 if (k < 0)
1170 return k;
f9bb3338
YW
1171 } else {
1172 MultipathRoute *m;
f833694d 1173
f9bb3338
YW
1174 assert(!ret);
1175
1176 ORDERED_SET_FOREACH(m, route->multipath_routes) {
1177 r = route_add_and_setup_timer(link, route, m, NULL);
1178 if (r < 0)
1179 return r;
d9eee312
YW
1180 if (r > 0)
1181 k = 1;
f9bb3338
YW
1182 }
1183 }
80b0e860 1184
0c54bfd6
YW
1185 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
1186 link_netlink_destroy_callback, link);
1187 if (r < 0)
1188 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
1189
1190 link_ref(link);
1191
0ef9f3c7
YW
1192 if (ret)
1193 *ret = nr;
1194
d9eee312 1195 return k;
f579559b
TG
1196}
1197
141318f7
YW
1198static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
1199 int r;
1200
1201 assert(link);
1202 assert(link->route_messages > 0);
141318f7
YW
1203
1204 link->route_messages--;
1205
1206 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1207 return 1;
1208
1209 r = sd_netlink_message_get_errno(m);
1210 if (r < 0 && r != -EEXIST) {
1211 log_link_message_warning_errno(link, m, r, "Could not set route");
1212 link_enter_failed(link);
1213 return 1;
1214 }
1215
1216 if (link->route_messages == 0) {
1217 log_link_debug(link, "Routes set");
1218 link->static_routes_configured = true;
1219 link_set_nexthop(link);
1220 }
1221
1222 return 1;
1223}
1224
1225int link_set_routes(Link *link) {
1226 enum {
1227 PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
1228 PHASE_GATEWAY, /* Second phase: Routes with a gateway */
1229 _PHASE_MAX
1230 } phase;
1231 Route *rt;
1232 int r;
1233
1234 assert(link);
1235 assert(link->network);
1236 assert(link->state != _LINK_STATE_INVALID);
1237
1238 link->static_routes_configured = false;
1239
1240 if (!link->addresses_ready)
1241 return 0;
1242
1243 if (!link_has_carrier(link) && !link->network->configure_without_carrier)
1244 /* During configuring addresses, the link lost its carrier. As networkd is dropping
1245 * the addresses now, let's not configure the routes either. */
1246 return 0;
1247
bd4733da
YW
1248 if (link->route_messages != 0) {
1249 log_link_debug(link, "Static routes are configuring.");
1250 return 0;
1251 }
1252
141318f7
YW
1253 r = link_set_routing_policy_rules(link);
1254 if (r < 0)
1255 return r;
1256
1257 /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
1258 for (phase = 0; phase < _PHASE_MAX; phase++)
2a54a044 1259 HASHMAP_FOREACH(rt, link->network->routes_by_section) {
1a3a6309 1260 if (rt->gateway_from_dhcp_or_ra)
141318f7
YW
1261 continue;
1262
6dd53981 1263 if ((in_addr_is_null(rt->gw_family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
141318f7
YW
1264 continue;
1265
1266 r = route_configure(rt, link, route_handler, NULL);
1267 if (r < 0)
1268 return log_link_warning_errno(link, r, "Could not set routes: %m");
8c212f76
YW
1269
1270 link->route_messages++;
141318f7
YW
1271 }
1272
1273 if (link->route_messages == 0) {
1274 link->static_routes_configured = true;
1275 link_set_nexthop(link);
1276 } else {
1277 log_link_debug(link, "Setting routes");
1278 link_set_state(link, LINK_STATE_CONFIGURING);
1279 }
1280
1281 return 0;
1282}
1283
f9bb3338
YW
1284static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
1285 _cleanup_(route_freep) Route *nr = NULL;
1286 Route *route = NULL;
1287 int r;
1288
1289 assert(manager);
1290 assert(tmp);
1291 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
1292
1293 if (m) {
1294 if (link)
1295 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
1296 "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
1297
1298 if (m->ifindex <= 0)
1299 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
1300 "rtnl: received multipath route with invalid ifindex, ignoring.");
1301
1302 r = link_get(manager, m->ifindex, &link);
1303 if (r < 0) {
1304 log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
1305 return 0;
1306 }
1307
1308 r = route_new(&nr);
1309 if (r < 0)
1310 return log_oom();
1311
1312 route_copy(nr, tmp, m);
1313
1314 tmp = nr;
1315 }
1316
1317 (void) route_get(manager, link, tmp, &route);
1318
f9bb3338
YW
1319 switch (type) {
1320 case RTM_NEWROUTE:
167a5561
YW
1321 if (!route) {
1322 if (!manager->manage_foreign_routes)
552b90a2 1323 log_route_debug(tmp, "Ignoring received foreign", link, manager);
167a5561
YW
1324 else {
1325 /* A route appeared that we did not request */
552b90a2 1326 log_route_debug(tmp, "Remembering foreign", link, manager);
167a5561
YW
1327 r = route_add_foreign(manager, link, tmp, NULL);
1328 if (r < 0) {
1329 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
1330 return 0;
1331 }
f9bb3338 1332 }
167a5561 1333 } else
552b90a2 1334 log_route_debug(tmp, "Received remembered", link, manager);
f9bb3338
YW
1335
1336 break;
1337
1338 case RTM_DELROUTE:
167a5561
YW
1339 log_route_debug(tmp,
1340 route ? "Forgetting" :
552b90a2
YW
1341 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign",
1342 link, manager);
f9bb3338
YW
1343 route_free(route);
1344 break;
1345
1346 default:
1347 assert_not_reached("Received route message with invalid RTNL message type");
1348 }
1349
1350 return 1;
1351}
1352
4468f01b 1353int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
f9bb3338 1354 _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
4468f01b 1355 _cleanup_(route_freep) Route *tmp = NULL;
f9bb3338 1356 _cleanup_free_ void *rta_multipath = NULL;
4468f01b
YW
1357 Link *link = NULL;
1358 uint32_t ifindex;
1359 uint16_t type;
1360 unsigned char table;
f9bb3338 1361 size_t rta_len;
4468f01b
YW
1362 int r;
1363
1364 assert(rtnl);
1365 assert(message);
1366 assert(m);
1367
1368 if (sd_netlink_message_is_error(message)) {
1369 r = sd_netlink_message_get_errno(message);
1370 if (r < 0)
1371 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1372
1373 return 0;
1374 }
1375
1376 r = sd_netlink_message_get_type(message, &type);
1377 if (r < 0) {
1378 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1379 return 0;
1380 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1381 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1382 return 0;
1383 }
1384
1385 r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
ad208fac 1386 if (r < 0 && r != -ENODATA) {
4468f01b
YW
1387 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
1388 return 0;
ad208fac
YW
1389 } else if (r >= 0) {
1390 if (ifindex <= 0) {
1391 log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
1392 return 0;
1393 }
4468f01b 1394
ad208fac
YW
1395 r = link_get(m, ifindex, &link);
1396 if (r < 0 || !link) {
1397 /* when enumerating we might be out of sync, but we will
1398 * get the route again, so just ignore it */
1399 if (!m->enumerating)
1400 log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
1401 return 0;
1402 }
4468f01b
YW
1403 }
1404
1405 r = route_new(&tmp);
1406 if (r < 0)
1407 return log_oom();
1408
1409 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1410 if (r < 0) {
1411 log_link_warning(link, "rtnl: received route message without family, ignoring");
1412 return 0;
1413 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
1414 log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
1415 return 0;
1416 }
1417
1418 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1419 if (r < 0) {
1420 log_warning_errno(r, "rtnl: received route message without route protocol: %m");
1421 return 0;
1422 }
1423
ad6df717
YW
1424 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1425 if (r < 0 && r != -ENODATA) {
1426 log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
1427 return 0;
1428 }
4468f01b 1429
ad6df717
YW
1430 r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, tmp->family, &tmp->gw);
1431 if (r < 0 && r != -ENODATA) {
1432 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1433 return 0;
1434 } else if (r >= 0)
1435 tmp->gw_family = tmp->family;
1436 else if (tmp->family == AF_INET) {
1437 RouteVia via;
6dd53981
YW
1438
1439 r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
1440 if (r < 0 && r != -ENODATA) {
1441 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1442 return 0;
1443 } else if (r >= 0) {
1444 tmp->gw_family = via.family;
1445 tmp->gw = via.address;
4468f01b 1446 }
ad6df717 1447 }
4468f01b 1448
ad6df717
YW
1449 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1450 if (r < 0 && r != -ENODATA) {
1451 log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
1452 return 0;
1453 }
4468f01b 1454
ad6df717
YW
1455 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
1456 if (r < 0 && r != -ENODATA) {
1457 log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
1458 return 0;
1459 }
1460
1461 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
1462 if (r < 0) {
1463 log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
1464 return 0;
1465 }
1466
1467 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
1468 if (r < 0) {
1469 log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
1470 return 0;
1471 }
1472
1473 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
1474 if (r < 0) {
1475 log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
1476 return 0;
1477 }
1478
1479 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
1480 if (r < 0) {
1481 log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
1482 return 0;
1483 }
1484
1485 r = sd_rtnl_message_route_get_type(message, &tmp->type);
1486 if (r < 0) {
1487 log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
1488 return 0;
1489 }
1490
1491 r = sd_rtnl_message_route_get_table(message, &table);
1492 if (r < 0) {
1493 log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
1494 return 0;
1495 }
1496 tmp->table = table;
1497
1498 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
1499 if (r < 0 && r != -ENODATA) {
1500 log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
1501 return 0;
1502 }
1503
1504 r = sd_netlink_message_enter_container(message, RTA_METRICS);
1505 if (r < 0 && r != -ENODATA) {
1506 log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
1507 return 0;
1508 }
1509 if (r >= 0) {
1510 r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
1511 if (r < 0 && r != -ENODATA) {
1512 log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
1513 return 0;
1514 }
1515
1516 r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
1517 if (r < 0 && r != -ENODATA) {
1518 log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
1519 return 0;
386e8908
YW
1520 }
1521
1522 r = sd_netlink_message_read_u32(message, RTAX_ADVMSS, &tmp->advmss);
1523 if (r < 0 && r != -ENODATA) {
1524 log_link_warning_errno(link, r, "rtnl: received route message with invalid advmss, ignoring: %m");
1525 return 0;
4468f01b
YW
1526 }
1527
1528 r = sd_netlink_message_exit_container(message);
1529 if (r < 0) {
1530 log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
1531 return 0;
1532 }
1533 }
1534
f9bb3338
YW
1535 r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
1536 if (r < 0 && r != -ENODATA) {
1537 log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
1538 return 0;
1539 } else if (r >= 0) {
1540 r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
1541 if (r < 0) {
1542 log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
1543 return 0;
4468f01b 1544 }
4468f01b
YW
1545 }
1546
575f14ee
YW
1547 /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
1548 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
1549 * link to NULL here. */
1550 if (route_type_is_reject(tmp))
1551 link = NULL;
1552
f9bb3338
YW
1553 if (ordered_set_isempty(multipath_routes))
1554 (void) process_route_one(m, link, type, tmp, NULL);
1555 else {
1556 MultipathRoute *mr;
4468f01b 1557
f9bb3338
YW
1558 ORDERED_SET_FOREACH(mr, multipath_routes) {
1559 r = process_route_one(m, link, type, tmp, mr);
1560 if (r < 0)
1561 break;
1562 }
4468f01b
YW
1563 }
1564
1565 return 1;
1566}
1567
fa7cd711 1568int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 1569 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1570 unsigned section_line;
fa7cd711
YW
1571 int r;
1572
1573 assert(network);
1574
1575 if (!network->ipv4ll_route)
1576 return 0;
1577
2a54a044
YW
1578 section_line = hashmap_find_free_section_line(network->routes_by_section);
1579
fa7cd711 1580 /* IPv4LLRoute= is in [Network] section. */
2a54a044 1581 r = route_new_static(network, network->filename, section_line, &n);
fa7cd711
YW
1582 if (r < 0)
1583 return r;
1584
1585 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
1586 if (r < 0)
1587 return r;
1588
1589 n->family = AF_INET;
1590 n->dst_prefixlen = 16;
1591 n->scope = RT_SCOPE_LINK;
94d6e299
YW
1592 n->scope_set = true;
1593 n->table_set = true;
fa7cd711
YW
1594 n->priority = IPV4LL_ROUTE_METRIC;
1595 n->protocol = RTPROT_STATIC;
1596
1597 TAKE_PTR(n);
1598 return 0;
1599}
1600
5d5003ab
YW
1601int network_add_default_route_on_device(Network *network) {
1602 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1603 unsigned section_line;
5d5003ab
YW
1604 int r;
1605
1606 assert(network);
1607
1608 if (!network->default_route_on_device)
1609 return 0;
1610
2a54a044
YW
1611 section_line = hashmap_find_free_section_line(network->routes_by_section);
1612
5d5003ab 1613 /* DefaultRouteOnDevice= is in [Network] section. */
2a54a044 1614 r = route_new_static(network, network->filename, section_line, &n);
5d5003ab
YW
1615 if (r < 0)
1616 return r;
1617
5d5003ab 1618 n->family = AF_INET;
c697db75
YW
1619 n->scope = RT_SCOPE_LINK;
1620 n->scope_set = true;
1621 n->protocol = RTPROT_STATIC;
5d5003ab
YW
1622
1623 TAKE_PTR(n);
1624 return 0;
1625}
1626
27efb52b
YW
1627int config_parse_gateway(
1628 const char *unit,
f579559b
TG
1629 const char *filename,
1630 unsigned line,
1631 const char *section,
71a61510 1632 unsigned section_line,
f579559b
TG
1633 const char *lvalue,
1634 int ltype,
1635 const char *rvalue,
1636 void *data,
1637 void *userdata) {
44e7b949 1638
6ae115c1 1639 Network *network = userdata;
fcbf4cb7 1640 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1641 int r;
f579559b
TG
1642
1643 assert(filename);
6ae115c1 1644 assert(section);
f579559b
TG
1645 assert(lvalue);
1646 assert(rvalue);
1647 assert(data);
1648
92fe133a 1649 if (streq(section, "Network")) {
2a54a044
YW
1650 /* we are not in an Route section, so use line number instead */
1651 r = route_new_static(network, filename, line, &n);
d96edb2c
YW
1652 if (r == -ENOMEM)
1653 return log_oom();
1654 if (r < 0) {
1655 log_syntax(unit, LOG_WARNING, filename, line, r,
1656 "Failed to allocate route, ignoring assignment: %m");
1657 return 0;
1658 }
1985c54f 1659 } else {
f4859fc7 1660 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1661 if (r == -ENOMEM)
1662 return log_oom();
1663 if (r < 0) {
1664 log_syntax(unit, LOG_WARNING, filename, line, r,
1665 "Failed to allocate route, ignoring assignment: %m");
1666 return 0;
1667 }
1985c54f 1668
d442bb37 1669 if (isempty(rvalue)) {
1a3a6309 1670 n->gateway_from_dhcp_or_ra = false;
d442bb37
YW
1671 n->gw_family = AF_UNSPEC;
1672 n->gw = IN_ADDR_NULL;
1673 TAKE_PTR(n);
1674 return 0;
1675 }
1676
427928ca 1677 if (streq(rvalue, "_dhcp")) {
1a3a6309 1678 n->gateway_from_dhcp_or_ra = true;
1985c54f
YW
1679 TAKE_PTR(n);
1680 return 0;
1681 }
d306d1d0
YW
1682
1683 if (streq(rvalue, "_dhcp4")) {
1684 n->gw_family = AF_INET;
1a3a6309 1685 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
1686 TAKE_PTR(n);
1687 return 0;
1688 }
1689
b8caa4ef 1690 if (streq(rvalue, "_ipv6ra")) {
d306d1d0 1691 n->gw_family = AF_INET6;
1a3a6309 1692 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
1693 TAKE_PTR(n);
1694 return 0;
1695 }
1985c54f 1696 }
f579559b 1697
6dd53981 1698 r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
f579559b 1699 if (r < 0) {
d96edb2c 1700 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 1701 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
1702 return 0;
1703 }
1704
1a3a6309 1705 n->gateway_from_dhcp_or_ra = false;
aff44301 1706 TAKE_PTR(n);
f579559b
TG
1707 return 0;
1708}
6ae115c1 1709
27efb52b
YW
1710int config_parse_preferred_src(
1711 const char *unit,
0d07e595
JK
1712 const char *filename,
1713 unsigned line,
1714 const char *section,
1715 unsigned section_line,
1716 const char *lvalue,
1717 int ltype,
1718 const char *rvalue,
1719 void *data,
1720 void *userdata) {
1721
1722 Network *network = userdata;
fcbf4cb7 1723 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1724 int r;
0d07e595
JK
1725
1726 assert(filename);
1727 assert(section);
1728 assert(lvalue);
1729 assert(rvalue);
1730 assert(data);
1731
f4859fc7 1732 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1733 if (r == -ENOMEM)
1734 return log_oom();
1735 if (r < 0) {
1736 log_syntax(unit, LOG_WARNING, filename, line, r,
1737 "Failed to allocate route, ignoring assignment: %m");
1738 return 0;
1739 }
0d07e595 1740
01d4e732
YW
1741 if (n->family == AF_UNSPEC)
1742 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
1743 else
1744 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 1745 if (r < 0) {
d96edb2c 1746 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1747 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1748 return 0;
1749 }
1750
aff44301 1751 TAKE_PTR(n);
0d07e595
JK
1752 return 0;
1753}
1754
27efb52b
YW
1755int config_parse_destination(
1756 const char *unit,
6ae115c1
TG
1757 const char *filename,
1758 unsigned line,
1759 const char *section,
1760 unsigned section_line,
1761 const char *lvalue,
1762 int ltype,
1763 const char *rvalue,
1764 void *data,
1765 void *userdata) {
44e7b949 1766
6ae115c1 1767 Network *network = userdata;
fcbf4cb7 1768 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
1769 union in_addr_union *buffer;
1770 unsigned char *prefixlen;
ca3bad65 1771 int r;
6ae115c1
TG
1772
1773 assert(filename);
1774 assert(section);
1775 assert(lvalue);
1776 assert(rvalue);
1777 assert(data);
1778
f4859fc7 1779 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1780 if (r == -ENOMEM)
1781 return log_oom();
1782 if (r < 0) {
1783 log_syntax(unit, LOG_WARNING, filename, line, r,
1784 "Failed to allocate route, ignoring assignment: %m");
1785 return 0;
1786 }
6ae115c1 1787
9e7e4408 1788 if (streq(lvalue, "Destination")) {
7934dede
YW
1789 buffer = &n->dst;
1790 prefixlen = &n->dst_prefixlen;
9e7e4408 1791 } else if (streq(lvalue, "Source")) {
7934dede
YW
1792 buffer = &n->src;
1793 prefixlen = &n->src_prefixlen;
9e7e4408
TG
1794 } else
1795 assert_not_reached(lvalue);
1796
01d4e732
YW
1797 if (n->family == AF_UNSPEC)
1798 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
1799 else
1800 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 1801 if (r < 0) {
d96edb2c 1802 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1803 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1804 return 0;
1805 }
1806
aff44301 1807 TAKE_PTR(n);
6ae115c1
TG
1808 return 0;
1809}
5d8e593d 1810
27efb52b
YW
1811int config_parse_route_priority(
1812 const char *unit,
1813 const char *filename,
1814 unsigned line,
1815 const char *section,
1816 unsigned section_line,
1817 const char *lvalue,
1818 int ltype,
1819 const char *rvalue,
1820 void *data,
1821 void *userdata) {
1822
5d8e593d 1823 Network *network = userdata;
fcbf4cb7 1824 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
1825 int r;
1826
1827 assert(filename);
1828 assert(section);
1829 assert(lvalue);
1830 assert(rvalue);
1831 assert(data);
1832
f4859fc7 1833 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1834 if (r == -ENOMEM)
1835 return log_oom();
1836 if (r < 0) {
1837 log_syntax(unit, LOG_WARNING, filename, line, r,
1838 "Failed to allocate route, ignoring assignment: %m");
1839 return 0;
1840 }
5d8e593d 1841
aff44301 1842 r = safe_atou32(rvalue, &n->priority);
1c4b1179 1843 if (r < 0) {
d96edb2c 1844 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1845 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1846 return 0;
1847 }
5d8e593d 1848
c27abcf4 1849 n->priority_set = true;
aff44301 1850 TAKE_PTR(n);
5d8e593d
SS
1851 return 0;
1852}
769b56a3 1853
27efb52b
YW
1854int config_parse_route_scope(
1855 const char *unit,
1856 const char *filename,
1857 unsigned line,
1858 const char *section,
1859 unsigned section_line,
1860 const char *lvalue,
1861 int ltype,
1862 const char *rvalue,
1863 void *data,
1864 void *userdata) {
1865
769b56a3 1866 Network *network = userdata;
fcbf4cb7 1867 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
1868 int r;
1869
1870 assert(filename);
1871 assert(section);
1872 assert(lvalue);
1873 assert(rvalue);
1874 assert(data);
1875
f4859fc7 1876 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1877 if (r == -ENOMEM)
1878 return log_oom();
1879 if (r < 0) {
1880 log_syntax(unit, LOG_WARNING, filename, line, r,
1881 "Failed to allocate route, ignoring assignment: %m");
1882 return 0;
1883 }
769b56a3 1884
41b90a1e
YW
1885 r = route_scope_from_string(rvalue);
1886 if (r < 0) {
b98680b2 1887 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
1888 return 0;
1889 }
1890
41b90a1e 1891 n->scope = r;
94d6e299 1892 n->scope_set = true;
aff44301 1893 TAKE_PTR(n);
769b56a3
TG
1894 return 0;
1895}
c953b24c 1896
27efb52b
YW
1897int config_parse_route_table(
1898 const char *unit,
1899 const char *filename,
1900 unsigned line,
1901 const char *section,
1902 unsigned section_line,
1903 const char *lvalue,
1904 int ltype,
1905 const char *rvalue,
1906 void *data,
1907 void *userdata) {
1908
fcbf4cb7 1909 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 1910 Network *network = userdata;
c953b24c
SS
1911 int r;
1912
1913 assert(filename);
1914 assert(section);
1915 assert(lvalue);
1916 assert(rvalue);
1917 assert(data);
1918
f4859fc7 1919 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1920 if (r == -ENOMEM)
1921 return log_oom();
1922 if (r < 0) {
1923 log_syntax(unit, LOG_WARNING, filename, line, r,
1924 "Failed to allocate route, ignoring assignment: %m");
1925 return 0;
1926 }
c953b24c 1927
552b90a2 1928 r = manager_get_route_table_from_string(network->manager, rvalue, &n->table);
c038ce46
SS
1929 if (r < 0) {
1930 log_syntax(unit, LOG_WARNING, filename, line, r,
1931 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
1932 return 0;
c953b24c
SS
1933 }
1934
94d6e299 1935 n->table_set = true;
aff44301 1936 TAKE_PTR(n);
c953b24c
SS
1937 return 0;
1938}
28959f7d 1939
d96edb2c 1940int config_parse_route_boolean(
27efb52b
YW
1941 const char *unit,
1942 const char *filename,
1943 unsigned line,
1944 const char *section,
1945 unsigned section_line,
1946 const char *lvalue,
1947 int ltype,
1948 const char *rvalue,
1949 void *data,
1950 void *userdata) {
1951
28959f7d 1952 Network *network = userdata;
fcbf4cb7 1953 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
1954 int r;
1955
1956 assert(filename);
1957 assert(section);
1958 assert(lvalue);
1959 assert(rvalue);
1960 assert(data);
1961
1962 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1963 if (r == -ENOMEM)
1964 return log_oom();
1965 if (r < 0) {
1966 log_syntax(unit, LOG_WARNING, filename, line, r,
1967 "Failed to allocate route, ignoring assignment: %m");
1968 return 0;
1969 }
28959f7d
SS
1970
1971 r = parse_boolean(rvalue);
1972 if (r < 0) {
d96edb2c 1973 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 1974 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
1975 return 0;
1976 }
1977
d96edb2c
YW
1978 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
1979 n->gateway_onlink = r;
1980 else if (streq(lvalue, "QuickAck"))
1981 n->quickack = r;
1982 else if (streq(lvalue, "FastOpenNoCookie"))
1983 n->fast_open_no_cookie = r;
1984 else if (streq(lvalue, "TTLPropagate"))
1985 n->ttl_propagate = r;
1986 else
1987 assert_not_reached("Invalid lvalue");
54901fd2 1988
aff44301 1989 TAKE_PTR(n);
b5bf6f64
SS
1990 return 0;
1991}
1992
27efb52b
YW
1993int config_parse_ipv6_route_preference(
1994 const char *unit,
1995 const char *filename,
1996 unsigned line,
1997 const char *section,
1998 unsigned section_line,
1999 const char *lvalue,
2000 int ltype,
2001 const char *rvalue,
2002 void *data,
2003 void *userdata) {
2004
b5bf6f64 2005 Network *network = userdata;
fcbf4cb7 2006 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
2007 int r;
2008
4c7bd9cf 2009 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2010 if (r == -ENOMEM)
2011 return log_oom();
2012 if (r < 0) {
2013 log_syntax(unit, LOG_WARNING, filename, line, r,
2014 "Failed to allocate route, ignoring assignment: %m");
2015 return 0;
2016 }
4c7bd9cf 2017
b5bf6f64
SS
2018 if (streq(rvalue, "low"))
2019 n->pref = ICMPV6_ROUTER_PREF_LOW;
2020 else if (streq(rvalue, "medium"))
2021 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
2022 else if (streq(rvalue, "high"))
2023 n->pref = ICMPV6_ROUTER_PREF_HIGH;
2024 else {
d96edb2c 2025 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
2026 return 0;
2027 }
28959f7d 2028
c27abcf4 2029 n->pref_set = true;
aff44301 2030 TAKE_PTR(n);
28959f7d
SS
2031 return 0;
2032}
c83ecc04 2033
27efb52b
YW
2034int config_parse_route_protocol(
2035 const char *unit,
2036 const char *filename,
2037 unsigned line,
2038 const char *section,
2039 unsigned section_line,
2040 const char *lvalue,
2041 int ltype,
2042 const char *rvalue,
2043 void *data,
2044 void *userdata) {
2045
c83ecc04 2046 Network *network = userdata;
fcbf4cb7 2047 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
2048 int r;
2049
2050 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2051 if (r == -ENOMEM)
2052 return log_oom();
2053 if (r < 0) {
2054 log_syntax(unit, LOG_WARNING, filename, line, r,
2055 "Failed to allocate route, ignoring assignment: %m");
2056 return 0;
2057 }
c83ecc04 2058
1b64651b 2059 r = route_protocol_from_string(rvalue);
e4ffe103
YW
2060 if (r < 0) {
2061 log_syntax(unit, LOG_WARNING, filename, line, r,
2062 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
2063 return 0;
c83ecc04
SS
2064 }
2065
e4ffe103
YW
2066 n->protocol = r;
2067
aff44301 2068 TAKE_PTR(n);
c83ecc04
SS
2069 return 0;
2070}
983226f3 2071
27efb52b
YW
2072int config_parse_route_type(
2073 const char *unit,
2074 const char *filename,
2075 unsigned line,
2076 const char *section,
2077 unsigned section_line,
2078 const char *lvalue,
2079 int ltype,
2080 const char *rvalue,
2081 void *data,
2082 void *userdata) {
2083
983226f3 2084 Network *network = userdata;
fcbf4cb7 2085 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 2086 int t, r;
983226f3
SS
2087
2088 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2089 if (r == -ENOMEM)
2090 return log_oom();
2091 if (r < 0) {
2092 log_syntax(unit, LOG_WARNING, filename, line, r,
2093 "Failed to allocate route, ignoring assignment: %m");
2094 return 0;
2095 }
983226f3 2096
7a22312d
YW
2097 t = route_type_from_string(rvalue);
2098 if (t < 0) {
b98680b2 2099 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 2100 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
2101 return 0;
2102 }
2103
7a22312d
YW
2104 n->type = (unsigned char) t;
2105
aff44301 2106 TAKE_PTR(n);
983226f3
SS
2107 return 0;
2108}
323d9329 2109
007cac09
SS
2110int config_parse_tcp_advmss(
2111 const char *unit,
2112 const char *filename,
2113 unsigned line,
2114 const char *section,
2115 unsigned section_line,
2116 const char *lvalue,
2117 int ltype,
2118 const char *rvalue,
2119 void *data,
2120 void *userdata) {
2121
2122 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2123 Network *network = userdata;
2124 uint64_t u;
2125 int r;
2126
2127 assert(filename);
2128 assert(section);
2129 assert(lvalue);
2130 assert(rvalue);
2131 assert(data);
2132
2133 r = route_new_static(network, filename, section_line, &n);
2134 if (r == -ENOMEM)
2135 return log_oom();
2136 if (r < 0) {
2137 log_syntax(unit, LOG_WARNING, filename, line, r,
2138 "Failed to allocate route, ignoring assignment: %m");
2139 return 0;
2140 }
2141
2142 if (isempty(rvalue)) {
2143 n->advmss = 0;
78ebbf02 2144 TAKE_PTR(n);
007cac09
SS
2145 return 0;
2146 }
2147
2148 r = parse_size(rvalue, 1024, &u);
2149 if (r < 0) {
2150 log_syntax(unit, LOG_WARNING, filename, line, r,
2151 "Could not parse TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2152 return 0;
2153 }
2154
2155 if (u == 0 || u > UINT32_MAX) {
2156 log_syntax(unit, LOG_WARNING, filename, line, 0,
2157 "Invalid TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2158 return 0;
2159 }
2160
2161 n->advmss = u;
2162
2163 TAKE_PTR(n);
2164 return 0;
2165}
2166
27efb52b
YW
2167int config_parse_tcp_window(
2168 const char *unit,
2169 const char *filename,
2170 unsigned line,
2171 const char *section,
2172 unsigned section_line,
2173 const char *lvalue,
2174 int ltype,
2175 const char *rvalue,
2176 void *data,
2177 void *userdata) {
2178
fcbf4cb7 2179 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33 2180 Network *network = userdata;
fef160b5 2181 uint32_t k;
323d9329
SS
2182 int r;
2183
2184 assert(filename);
2185 assert(section);
2186 assert(lvalue);
2187 assert(rvalue);
2188 assert(data);
2189
2190 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2191 if (r == -ENOMEM)
2192 return log_oom();
2193 if (r < 0) {
2194 log_syntax(unit, LOG_WARNING, filename, line, r,
2195 "Failed to allocate route, ignoring assignment: %m");
2196 return 0;
2197 }
323d9329 2198
fef160b5 2199 r = safe_atou32(rvalue, &k);
f205a92a 2200 if (r < 0) {
d96edb2c 2201 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
2202 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2203 return 0;
2204 }
fef160b5 2205 if (k >= 1024) {
d96edb2c 2206 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 2207 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
2208 return 0;
2209 }
fd9d7de1
SS
2210 if (k == 0) {
2211 log_syntax(unit, LOG_WARNING, filename, line, 0,
2212 "Invalid TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2213 return 0;
2214 }
323d9329
SS
2215
2216 if (streq(lvalue, "InitialCongestionWindow"))
2217 n->initcwnd = k;
2218 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
2219 n->initrwnd = k;
f205a92a
YW
2220 else
2221 assert_not_reached("Invalid TCP window type.");
323d9329 2222
aff44301 2223 TAKE_PTR(n);
323d9329
SS
2224 return 0;
2225}
09f5dfad 2226
cea79e66
SS
2227int config_parse_route_mtu(
2228 const char *unit,
2229 const char *filename,
2230 unsigned line,
2231 const char *section,
2232 unsigned section_line,
2233 const char *lvalue,
2234 int ltype,
2235 const char *rvalue,
2236 void *data,
2237 void *userdata) {
2238
2239 Network *network = userdata;
fcbf4cb7 2240 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
2241 int r;
2242
2243 assert(filename);
2244 assert(section);
2245 assert(lvalue);
2246 assert(rvalue);
2247 assert(data);
2248
2249 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2250 if (r == -ENOMEM)
2251 return log_oom();
2252 if (r < 0) {
2253 log_syntax(unit, LOG_WARNING, filename, line, r,
2254 "Failed to allocate route, ignoring assignment: %m");
2255 return 0;
2256 }
cea79e66
SS
2257
2258 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
2259 if (r < 0)
2260 return r;
2261
aff44301 2262 TAKE_PTR(n);
cea79e66
SS
2263 return 0;
2264}
fcbf4cb7 2265
6ff5cc6b
YW
2266int config_parse_multipath_route(
2267 const char *unit,
2268 const char *filename,
2269 unsigned line,
2270 const char *section,
2271 unsigned section_line,
2272 const char *lvalue,
2273 int ltype,
2274 const char *rvalue,
2275 void *data,
2276 void *userdata) {
2277
2278 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2279 _cleanup_free_ char *word = NULL, *buf = NULL;
2280 _cleanup_free_ MultipathRoute *m = NULL;
2281 Network *network = userdata;
2282 const char *p, *ip, *dev;
2283 union in_addr_union a;
2284 int family, r;
2285
2286 assert(filename);
2287 assert(section);
2288 assert(lvalue);
2289 assert(rvalue);
2290 assert(data);
2291
2292 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2293 if (r == -ENOMEM)
2294 return log_oom();
2295 if (r < 0) {
2296 log_syntax(unit, LOG_WARNING, filename, line, r,
2297 "Failed to allocate route, ignoring assignment: %m");
2298 return 0;
2299 }
6ff5cc6b
YW
2300
2301 if (isempty(rvalue)) {
2302 n->multipath_routes = ordered_set_free_free(n->multipath_routes);
2303 return 0;
2304 }
2305
2306 m = new0(MultipathRoute, 1);
2307 if (!m)
2308 return log_oom();
2309
2310 p = rvalue;
2311 r = extract_first_word(&p, &word, NULL, 0);
2312 if (r == -ENOMEM)
2313 return log_oom();
2314 if (r <= 0) {
d96edb2c 2315 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2316 "Invalid multipath route option, ignoring assignment: %s", rvalue);
2317 return 0;
2318 }
2319
2320 dev = strchr(word, '@');
2321 if (dev) {
2322 buf = strndup(word, dev - word);
2323 if (!buf)
2324 return log_oom();
2325 ip = buf;
2326 dev++;
2327 } else
2328 ip = word;
2329
2330 r = in_addr_from_string_auto(ip, &family, &a);
2331 if (r < 0) {
d96edb2c 2332 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2333 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
2334 return 0;
2335 }
2336 m->gateway.address = a;
2337 m->gateway.family = family;
2338
2339 if (dev) {
d308bb99 2340 r = resolve_interface(NULL, dev);
6ff5cc6b 2341 if (r < 0) {
d96edb2c 2342 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2343 "Invalid interface name or index, ignoring assignment: %s", dev);
2344 return 0;
2345 }
597da51b 2346 m->ifindex = r;
6ff5cc6b
YW
2347 }
2348
2349 if (!isempty(p)) {
2350 r = safe_atou32(p, &m->weight);
2351 if (r < 0) {
d96edb2c 2352 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2353 "Invalid multipath route weight, ignoring assignment: %s", p);
2354 return 0;
2355 }
2356 if (m->weight == 0 || m->weight > 256) {
d96edb2c 2357 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
2358 "Invalid multipath route weight, ignoring assignment: %s", p);
2359 return 0;
2360 }
2361 }
2362
8cb34651
SS
2363 r = ordered_set_ensure_put(&n->multipath_routes, NULL, m);
2364 if (r == -ENOMEM)
6ff5cc6b 2365 return log_oom();
6ff5cc6b 2366 if (r < 0) {
d96edb2c 2367 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2368 "Failed to store multipath route, ignoring assignment: %m");
2369 return 0;
2370 }
2371
2372 TAKE_PTR(m);
2373 TAKE_PTR(n);
2374 return 0;
2375}
2376
c038ce46
SS
2377int config_parse_route_table_names(
2378 const char *unit,
2379 const char *filename,
2380 unsigned line,
2381 const char *section,
2382 unsigned section_line,
2383 const char *lvalue,
2384 int ltype,
2385 const char *rvalue,
2386 void *data,
2387 void *userdata) {
2388
552b90a2 2389 Manager *m = userdata;
c038ce46
SS
2390 int r;
2391
2392 assert(filename);
2393 assert(lvalue);
2394 assert(rvalue);
552b90a2 2395 assert(userdata);
c038ce46
SS
2396
2397 if (isempty(rvalue)) {
552b90a2
YW
2398 m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number);
2399 m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name);
c038ce46
SS
2400 return 0;
2401 }
2402
310eff72
YW
2403 for (const char *p = rvalue;;) {
2404 _cleanup_free_ char *name = NULL;
2405 uint32_t table;
2406 char *num;
c038ce46 2407
310eff72
YW
2408 r = extract_first_word(&p, &name, NULL, 0);
2409 if (r == -ENOMEM)
2410 return log_oom();
2411 if (r < 0) {
2412 log_syntax(unit, LOG_WARNING, filename, line, r,
2413 "Invalid RouteTable=, ignoring assignment: %s", rvalue);
2414 return 0;
2415 }
2416 if (r == 0)
2417 return 0;
c038ce46 2418
310eff72
YW
2419 num = strchr(name, ':');
2420 if (!num) {
2421 log_syntax(unit, LOG_WARNING, filename, line, 0,
2422 "Invalid route table name and number pair, ignoring assignment: %s", name);
2423 continue;
2424 }
c038ce46 2425
310eff72 2426 *num++ = '\0';
c038ce46 2427
310eff72
YW
2428 if (STR_IN_SET(name, "default", "main", "local")) {
2429 log_syntax(unit, LOG_WARNING, filename, line, 0,
2430 "Route table name %s already predefined. Ignoring assignment: %s:%s", name, name, num);
2431 continue;
2432 }
c038ce46 2433
310eff72
YW
2434 r = safe_atou32(num, &table);
2435 if (r < 0) {
2436 log_syntax(unit, LOG_WARNING, filename, line, r,
2437 "Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num);
2438 continue;
2439 }
2440 if (table == 0) {
2441 log_syntax(unit, LOG_WARNING, filename, line, 0,
2442 "Invalid route table number, ignoring assignment: %s:%s", name, num);
2443 continue;
2444 }
2445
552b90a2 2446 r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table));
310eff72
YW
2447 if (r == -ENOMEM)
2448 return log_oom();
2449 if (r == -EEXIST) {
2450 log_syntax(unit, LOG_WARNING, filename, line, r,
2451 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
2452 continue;
2453 }
2454 if (r < 0) {
2455 log_syntax(unit, LOG_WARNING, filename, line, r,
2456 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
2457 continue;
2458 }
552b90a2
YW
2459 if (r == 0)
2460 /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
2461 continue;
2462
2463 r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name);
2464 if (r < 0) {
2465 hashmap_remove(m->route_table_numbers_by_name, name);
2466
2467 if (r == -ENOMEM)
2468 return log_oom();
2469 if (r == -EEXIST)
2470 log_syntax(unit, LOG_WARNING, filename, line, r,
2471 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
2472 else
2473 log_syntax(unit, LOG_WARNING, filename, line, r,
2474 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
2475 continue;
2476 }
2477 assert(r > 0);
2478
2479 TAKE_PTR(name);
310eff72 2480 }
c038ce46
SS
2481}
2482
d9940a3f 2483static int route_section_verify(Route *route, Network *network) {
fcbf4cb7
YW
2484 if (section_is_invalid(route->section))
2485 return -EINVAL;
2486
956dbf36 2487 if (route->gateway_from_dhcp_or_ra) {
c3d679c4
YW
2488 if (route->gw_family == AF_UNSPEC) {
2489 /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
2490 switch (route->family) {
2491 case AF_UNSPEC:
2492 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2493 "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
2494 route->section->filename, route->section->line);
2495 route->family = AF_INET;
2496 break;
2497 case AF_INET:
2498 case AF_INET6:
2499 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2500 "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
2501 route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
2502 break;
2503 default:
2504 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2505 "%s: Invalid route family. Ignoring [Route] section from line %u.",
2506 route->section->filename, route->section->line);
2507 }
2508 route->gw_family = route->family;
2509 }
2510
956dbf36
YW
2511 if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
2512 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2513 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
2514 "Ignoring [Route] section from line %u.",
2515 route->section->filename, route->section->line);
2516
2517 if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
2518 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2519 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
2520 "Ignoring [Route] section from line %u.",
2521 route->section->filename, route->section->line);
2522 }
2523
c3d679c4 2524 /* When only Gateway= is specified, assume the route family based on the Gateway address. */
6dd53981
YW
2525 if (route->family == AF_UNSPEC)
2526 route->family = route->gw_family;
2527
fcbf4cb7
YW
2528 if (route->family == AF_UNSPEC) {
2529 assert(route->section);
2530
c3d679c4
YW
2531 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2532 "%s: Route section without Gateway=, Destination=, Source=, "
2533 "or PreferredSource= field configured. "
2534 "Ignoring [Route] section from line %u.",
2535 route->section->filename, route->section->line);
fcbf4cb7
YW
2536 }
2537
6dd53981
YW
2538 if (route->family == AF_INET6 && route->gw_family == AF_INET)
2539 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2540 "%s: IPv4 gateway is configured for IPv6 route. "
2541 "Ignoring [Route] section from line %u.",
2542 route->section->filename, route->section->line);
2543
c0d48bc5
YW
2544 if (!route->table_set && network->vrf) {
2545 route->table = VRF(network->vrf)->table;
2546 route->table_set = true;
2547 }
2548
f5c38922
YW
2549 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
2550 route->table = RT_TABLE_LOCAL;
2551
2552 if (!route->scope_set && route->family != AF_INET6) {
2553 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
2554 route->scope = RT_SCOPE_HOST;
2555 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
2556 route->scope = RT_SCOPE_LINK;
94d6e299
YW
2557 }
2558
fd7701bf
YW
2559 if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
2560 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
2561 route->scope = RT_SCOPE_UNIVERSE;
2562 }
2563
8973df5c
YW
2564 if (route->family == AF_INET6 && route->priority == 0)
2565 route->priority = IP6_RT_PRIO_USER;
2566
9cd9fc8f 2567 if (ordered_hashmap_isempty(network->addresses_by_section) &&
6dd53981 2568 in_addr_is_null(route->gw_family, &route->gw) == 0 &&
fcbf4cb7
YW
2569 route->gateway_onlink < 0) {
2570 log_warning("%s: Gateway= without static address configured. "
2571 "Enabling GatewayOnLink= option.",
2572 network->filename);
2573 route->gateway_onlink = true;
2574 }
2575
2576 return 0;
2577}
d9940a3f 2578
13ffa39f 2579void network_drop_invalid_routes(Network *network) {
2a54a044 2580 Route *route;
d9940a3f
YW
2581
2582 assert(network);
2583
2a54a044 2584 HASHMAP_FOREACH(route, network->routes_by_section)
d9940a3f
YW
2585 if (route_section_verify(route, network) < 0)
2586 route_free(route);
2587}