]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
tree-wide: fix typo
[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) {
5380707a
YW
617 _cleanup_free_ char *dst = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL,
618 *table = NULL, *scope = NULL, *proto = NULL;
167a5561 619
5380707a
YW
620 if (in_addr_is_set(route->family, &route->dst))
621 (void) in_addr_prefix_to_string(route->family, &route->dst, route->dst_prefixlen, &dst);
94876904 622 if (in_addr_is_set(route->family, &route->src))
167a5561 623 (void) in_addr_to_string(route->family, &route->src, &src);
94876904 624 if (in_addr_is_set(route->gw_family, &route->gw))
167a5561 625 (void) in_addr_to_string(route->gw_family, &route->gw, &gw);
94876904 626 if (in_addr_is_set(route->family, &route->prefsrc))
167a5561 627 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
e4ffe103 628 (void) route_scope_to_string_alloc(route->scope, &scope);
552b90a2 629 (void) manager_get_route_table_to_string(m, route->table, &table);
e4ffe103 630 (void) route_protocol_full_to_string_alloc(route->protocol, &proto);
167a5561
YW
631
632 log_link_debug(link,
5380707a
YW
633 "%s route: dst: %s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
634 str, strna(dst), strna(src), strna(gw), strna(prefsrc),
e4ffe103 635 strna(scope), strna(table), strna(proto),
167a5561
YW
636 strna(route_type_to_string(route->type)));
637 }
638}
639
5f4d7aa4
YW
640static int route_set_netlink_message(const Route *route, sd_netlink_message *req, Link *link) {
641 unsigned flags;
642 int r;
643
644 assert(route);
645 assert(req);
646
647 /* link may be NULL */
648
94876904 649 if (in_addr_is_set(route->gw_family, &route->gw)) {
5f4d7aa4
YW
650 if (route->gw_family == route->family) {
651 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw);
652 if (r < 0)
653 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
654 } else {
655 RouteVia rtvia = {
656 .family = route->gw_family,
657 .address = route->gw,
658 };
659
660 r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia));
661 if (r < 0)
662 return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m");
663 }
664 }
665
666 if (route->dst_prefixlen > 0) {
667 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
668 if (r < 0)
669 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
670
671 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
672 if (r < 0)
673 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
674 }
675
676 if (route->src_prefixlen > 0) {
677 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
678 if (r < 0)
679 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
680
681 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
682 if (r < 0)
683 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
684 }
685
94876904 686 if (in_addr_is_set(route->family, &route->prefsrc)) {
5f4d7aa4
YW
687 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
688 if (r < 0)
689 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
690 }
691
692 r = sd_rtnl_message_route_set_scope(req, route->scope);
693 if (r < 0)
694 return log_link_error_errno(link, r, "Could not set scope: %m");
695
696 flags = route->flags;
697 if (route->gateway_onlink >= 0)
698 SET_FLAG(flags, RTNH_F_ONLINK, route->gateway_onlink);
699
700 r = sd_rtnl_message_route_set_flags(req, flags);
701 if (r < 0)
702 return log_link_error_errno(link, r, "Could not set flags: %m");
703
704 if (route->table != RT_TABLE_MAIN) {
705 if (route->table < 256) {
706 r = sd_rtnl_message_route_set_table(req, route->table);
707 if (r < 0)
708 return log_link_error_errno(link, r, "Could not set route table: %m");
709 } else {
710 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
711 if (r < 0)
712 return log_link_error_errno(link, r, "Could not set route table: %m");
713
714 /* Table attribute to allow more than 256. */
715 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
716 if (r < 0)
717 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
718 }
719 }
720
721 r = sd_rtnl_message_route_set_type(req, route->type);
722 if (r < 0)
723 return log_link_error_errno(link, r, "Could not set route type: %m");
724
6c252588 725 if (!route_type_is_reject(route)) {
5f4d7aa4
YW
726 assert(link); /* Those routes must be attached to a specific link */
727
728 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
729 if (r < 0)
730 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
731 }
732
733 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
734 if (r < 0)
735 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
736
737 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
738 if (r < 0)
739 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
740
741 return 0;
742}
743
302a796f 744static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
745 int r;
746
747 assert(m);
4645ad47 748
ad208fac
YW
749 /* Note that link may be NULL. */
750 if (link && IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
4645ad47
YW
751 return 1;
752
753 r = sd_netlink_message_get_errno(m);
754 if (r < 0 && r != -ESRCH)
5ecb131d 755 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
4645ad47
YW
756
757 return 1;
758}
759
ad208fac 760int route_remove(
423c249c 761 const Route *route,
ad208fac
YW
762 Manager *manager,
763 Link *link,
764 link_netlink_message_handler_t callback) {
27efb52b 765
4afd3348 766 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
767 int r;
768
ad208fac 769 assert(link || manager);
4c701096 770 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9 771
ad208fac
YW
772 if (!manager)
773 manager = link->manager;
081b3009 774 /* link may be NULL! */
ad208fac 775
552b90a2 776 log_route_debug(route, "Removing", link, manager);
167a5561 777
ad208fac 778 r = sd_rtnl_message_new_route(manager->rtnl, &req,
28cc555d
DW
779 RTM_DELROUTE, route->family,
780 route->protocol);
f647962d 781 if (r < 0)
7750b796 782 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 783
5f4d7aa4 784 r = route_set_netlink_message(route, req, link);
f647962d 785 if (r < 0)
5f4d7aa4 786 return r;
5c1d3fc9 787
bac319a7 788 r = netlink_call_async(manager->rtnl, NULL, req,
302a796f
YW
789 callback ?: route_remove_handler,
790 link_netlink_destroy_callback, link);
f647962d 791 if (r < 0)
7750b796 792 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
5c1d3fc9 793
081b3009 794 link_ref(link); /* link may be NULL, link_ref() is OK with that */
563c69c6 795
5c1d3fc9
UTL
796 return 0;
797}
798
1132a714 799static bool link_has_route(const Link *link, const Route *route) {
779804dd
YW
800 Route *net_route;
801
802 assert(link);
803 assert(route);
804
805 if (!link->network)
806 return false;
807
2a54a044 808 HASHMAP_FOREACH(net_route, link->network->routes_by_section)
779804dd
YW
809 if (route_equal(net_route, route))
810 return true;
811
812 return false;
813}
814
4055ec93 815static bool links_have_route(const Manager *manager, const Route *route, const Link *except) {
1132a714
YW
816 Link *link;
817
818 assert(manager);
819
820 HASHMAP_FOREACH(link, manager->links) {
821 if (link == except)
822 continue;
823
824 if (link_has_route(link, route))
825 return true;
826 }
827
828 return false;
829}
830
4055ec93 831static int manager_drop_routes_internal(Manager *manager, bool foreign, const Link *except) {
1132a714
YW
832 Route *route;
833 int k, r = 0;
4055ec93 834 Set *routes;
1132a714
YW
835
836 assert(manager);
837
4055ec93
YW
838 routes = foreign ? manager->routes_foreign : manager->routes;
839 SET_FOREACH(route, routes) {
0f1f933b 840 /* Do not touch routes managed by the kernel. */
1132a714
YW
841 if (route->protocol == RTPROT_KERNEL)
842 continue;
843
0f1f933b 844 /* The route will be configured later, or already configured by a link. */
4055ec93 845 if (links_have_route(manager, route, except))
1132a714
YW
846 continue;
847
0f1f933b 848 /* The existing links do not have the route. Let's drop this now. It may be
1132a714
YW
849 * re-configured later. */
850 k = route_remove(route, manager, NULL, NULL);
851 if (k < 0 && r >= 0)
852 r = k;
853 }
854
855 return r;
856}
857
4055ec93
YW
858static int manager_drop_foreign_routes(Manager *manager) {
859 return manager_drop_routes_internal(manager, true, NULL);
860}
1132a714 861
4055ec93
YW
862static int manager_drop_routes(Manager *manager, const Link *except) {
863 return manager_drop_routes_internal(manager, false, except);
1132a714
YW
864}
865
779804dd
YW
866int link_drop_foreign_routes(Link *link) {
867 Route *route;
868 int k, r = 0;
869
870 assert(link);
1132a714 871 assert(link->manager);
779804dd
YW
872
873 SET_FOREACH(route, link->routes_foreign) {
874 /* do not touch routes managed by the kernel */
875 if (route->protocol == RTPROT_KERNEL)
876 continue;
877
878 /* do not touch multicast route added by kernel */
879 /* FIXME: Why the kernel adds this route with protocol RTPROT_BOOT??? We need to investigate that.
880 * https://tools.ietf.org/html/rfc4862#section-5.4 may explain why. */
881 if (route->protocol == RTPROT_BOOT &&
882 route->family == AF_INET6 &&
883 route->dst_prefixlen == 8 &&
884 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 }}} }))
885 continue;
886
887 if (route->protocol == RTPROT_STATIC && link->network &&
888 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
889 continue;
890
891 if (route->protocol == RTPROT_DHCP && link->network &&
892 FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
893 continue;
894
1132a714 895 if (link_has_route(link, route))
f9bb3338 896 k = route_add(NULL, link, route, NULL, NULL);
779804dd 897 else
ad208fac 898 k = route_remove(route, NULL, link, NULL);
779804dd
YW
899 if (k < 0 && r >= 0)
900 r = k;
901 }
902
1132a714
YW
903 k = manager_drop_foreign_routes(link->manager);
904 if (k < 0 && r >= 0)
905 r = k;
906
779804dd
YW
907 return r;
908}
909
62f0ea5f
YW
910int link_drop_routes(Link *link) {
911 Route *route;
912 int k, r = 0;
913
914 assert(link);
915
916 SET_FOREACH(route, link->routes) {
917 /* do not touch routes managed by the kernel */
918 if (route->protocol == RTPROT_KERNEL)
919 continue;
920
ad208fac 921 k = route_remove(route, NULL, link, NULL);
62f0ea5f
YW
922 if (k < 0 && r >= 0)
923 r = k;
924 }
925
1132a714
YW
926 k = manager_drop_routes(link->manager, link);
927 if (k < 0 && r >= 0)
928 r = k;
929
62f0ea5f
YW
930 return r;
931}
932
74154c2e 933static int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
f833694d
TG
934 Route *route = userdata;
935 int r;
936
937 assert(route);
938
ad208fac 939 r = route_remove(route, route->manager, route->link, NULL);
d6ad41e2 940 if (r < 0) {
98b02994 941 log_link_warning_errno(route->link, r, "Could not remove route: %m");
fe7ca21a 942 route_free(route);
d6ad41e2 943 }
f833694d
TG
944
945 return 1;
946}
947
f9bb3338
YW
948static int route_add_and_setup_timer(Link *link, const Route *route, const MultipathRoute *m, Route **ret) {
949 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
950 Route *nr;
d9eee312 951 int r, k;
f9bb3338
YW
952
953 assert(link);
954 assert(route);
955
6c252588 956 if (route_type_is_reject(route))
d9eee312 957 k = route_add(link->manager, NULL, route, NULL, &nr);
f9bb3338 958 else if (!m || m->ifindex == 0 || m->ifindex == link->ifindex)
d9eee312 959 k = route_add(NULL, link, route, m, &nr);
f9bb3338
YW
960 else {
961 Link *link_gw;
962
963 r = link_get(link->manager, m->ifindex, &link_gw);
964 if (r < 0)
965 return log_link_error_errno(link, r, "Failed to get link with ifindex %d: %m", m->ifindex);
966
d9eee312 967 k = route_add(NULL, link_gw, route, m, &nr);
f9bb3338 968 }
d9eee312
YW
969 if (k < 0)
970 return log_link_error_errno(link, k, "Could not add route: %m");
f9bb3338
YW
971
972 /* TODO: drop expiration handling once it can be pushed into the kernel */
973 if (nr->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
974 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
975 nr->lifetime, 0, route_expire_handler, nr);
976 if (r < 0)
977 return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
978 }
979
980 sd_event_source_unref(nr->expire);
981 nr->expire = TAKE_PTR(expire);
982
983 if (ret)
984 *ret = nr;
985
d9eee312 986 return k;
f9bb3338
YW
987}
988
423c249c 989static int append_nexthop_one(const Route *route, const MultipathRoute *m, struct rtattr **rta, size_t offset) {
6ff5cc6b
YW
990 struct rtnexthop *rtnh;
991 struct rtattr *new_rta;
992 int r;
993
994 assert(route);
995 assert(m);
996 assert(rta);
997 assert(*rta);
998
999 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
1000 if (!new_rta)
1001 return -ENOMEM;
1002 *rta = new_rta;
1003
1004 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1005 *rtnh = (struct rtnexthop) {
1006 .rtnh_len = sizeof(*rtnh),
1007 .rtnh_ifindex = m->ifindex,
1008 .rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
1009 };
1010
1011 (*rta)->rta_len += sizeof(struct rtnexthop);
1012
1013 if (route->family == m->gateway.family) {
1014 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
1015 if (r < 0)
1016 goto clear;
1017 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1018 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
1019 } else {
1020 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1021 if (r < 0)
1022 goto clear;
1023 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
1024 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
1025 }
1026
1027 return 0;
1028
1029clear:
1030 (*rta)->rta_len -= sizeof(struct rtnexthop);
1031 return r;
1032}
1033
423c249c 1034static int append_nexthops(const Route *route, sd_netlink_message *req) {
6ff5cc6b
YW
1035 _cleanup_free_ struct rtattr *rta = NULL;
1036 struct rtnexthop *rtnh;
1037 MultipathRoute *m;
1038 size_t offset;
6ff5cc6b
YW
1039 int r;
1040
1041 if (ordered_set_isempty(route->multipath_routes))
1042 return 0;
1043
1044 rta = new(struct rtattr, 1);
1045 if (!rta)
1046 return -ENOMEM;
1047
1048 *rta = (struct rtattr) {
1049 .rta_type = RTA_MULTIPATH,
1050 .rta_len = RTA_LENGTH(0),
1051 };
1052 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
1053
90e74a66 1054 ORDERED_SET_FOREACH(m, route->multipath_routes) {
6ff5cc6b
YW
1055 r = append_nexthop_one(route, m, &rta, offset);
1056 if (r < 0)
1057 return r;
1058
1059 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
1060 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
1061 }
1062
1063 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
1064 if (r < 0)
1065 return r;
1066
1067 return 0;
1068}
1069
1b566071 1070int route_configure(
423c249c 1071 const Route *route,
1b566071 1072 Link *link,
80b0e860
YW
1073 link_netlink_message_handler_t callback,
1074 Route **ret) {
1b566071 1075
4afd3348 1076 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
d9eee312 1077 int r, k = 0;
0ef9f3c7 1078 Route *nr;
f579559b 1079
f579559b 1080 assert(link);
f882c247
TG
1081 assert(link->manager);
1082 assert(link->manager->rtnl);
f579559b 1083 assert(link->ifindex > 0);
4c701096 1084 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 1085 assert(callback);
f579559b 1086
ad208fac 1087 if (route_get(link->manager, link, route, NULL) <= 0 &&
47d2d30d 1088 set_size(link->routes) >= routes_max())
7750b796
YW
1089 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
1090 "Too many routes are configured, refusing: %m");
1b566071 1091
552b90a2 1092 log_route_debug(route, "Configuring", link, link->manager);
156ed65e 1093
151b9b96 1094 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
1095 RTM_NEWROUTE, route->family,
1096 route->protocol);
f647962d 1097 if (r < 0)
7750b796 1098 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
f579559b 1099
5f4d7aa4 1100 r = route_set_netlink_message(route, req, link);
f647962d 1101 if (r < 0)
5f4d7aa4 1102 return r;
3b015d40 1103
f02ba163
DD
1104 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
1105 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
1106 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
1107 if (r < 0)
7750b796 1108 return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
f02ba163
DD
1109 }
1110
9b88f20a
SS
1111 if (route->ttl_propagate >= 0) {
1112 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
1113 if (r < 0)
1114 return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
1115 }
1116
d6fceaf1
SS
1117 r = sd_netlink_message_open_container(req, RTA_METRICS);
1118 if (r < 0)
7750b796 1119 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1
SS
1120
1121 if (route->mtu > 0) {
1122 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
1123 if (r < 0)
7750b796 1124 return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
d6fceaf1
SS
1125 }
1126
6b21ad33 1127 if (route->initcwnd > 0) {
323d9329
SS
1128 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
1129 if (r < 0)
7750b796 1130 return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
323d9329
SS
1131 }
1132
6b21ad33 1133 if (route->initrwnd > 0) {
323d9329
SS
1134 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
1135 if (r < 0)
7750b796 1136 return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
323d9329
SS
1137 }
1138
67c193bf 1139 if (route->quickack >= 0) {
09f5dfad
SS
1140 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
1141 if (r < 0)
7750b796 1142 return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
09f5dfad
SS
1143 }
1144
633c7258
SS
1145 if (route->fast_open_no_cookie >= 0) {
1146 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
1147 if (r < 0)
1148 return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
1149 }
1150
007cac09
SS
1151 if (route->advmss > 0) {
1152 r = sd_netlink_message_append_u32(req, RTAX_ADVMSS, route->advmss);
1153 if (r < 0)
1154 return log_link_error_errno(link, r, "Could not append RTAX_ADVMSS attribute: %m");
1155 }
1156
d6fceaf1
SS
1157 r = sd_netlink_message_close_container(req);
1158 if (r < 0)
7750b796 1159 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1 1160
6ff5cc6b
YW
1161 r = append_nexthops(route, req);
1162 if (r < 0)
1163 return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
1164
f9bb3338 1165 if (ordered_set_isempty(route->multipath_routes)) {
d9eee312
YW
1166 k = route_add_and_setup_timer(link, route, NULL, &nr);
1167 if (k < 0)
1168 return k;
f9bb3338
YW
1169 } else {
1170 MultipathRoute *m;
f833694d 1171
f9bb3338
YW
1172 assert(!ret);
1173
1174 ORDERED_SET_FOREACH(m, route->multipath_routes) {
1175 r = route_add_and_setup_timer(link, route, m, NULL);
1176 if (r < 0)
1177 return r;
d9eee312
YW
1178 if (r > 0)
1179 k = 1;
f9bb3338
YW
1180 }
1181 }
80b0e860 1182
0c54bfd6
YW
1183 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
1184 link_netlink_destroy_callback, link);
1185 if (r < 0)
1186 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
1187
1188 link_ref(link);
1189
0ef9f3c7
YW
1190 if (ret)
1191 *ret = nr;
1192
d9eee312 1193 return k;
f579559b
TG
1194}
1195
141318f7
YW
1196static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
1197 int r;
1198
1199 assert(link);
1200 assert(link->route_messages > 0);
141318f7
YW
1201
1202 link->route_messages--;
1203
1204 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
1205 return 1;
1206
1207 r = sd_netlink_message_get_errno(m);
1208 if (r < 0 && r != -EEXIST) {
1209 log_link_message_warning_errno(link, m, r, "Could not set route");
1210 link_enter_failed(link);
1211 return 1;
1212 }
1213
1214 if (link->route_messages == 0) {
1215 log_link_debug(link, "Routes set");
1216 link->static_routes_configured = true;
1217 link_set_nexthop(link);
1218 }
1219
1220 return 1;
1221}
1222
1223int link_set_routes(Link *link) {
1224 enum {
1225 PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
1226 PHASE_GATEWAY, /* Second phase: Routes with a gateway */
1227 _PHASE_MAX
1228 } phase;
1229 Route *rt;
1230 int r;
1231
1232 assert(link);
1233 assert(link->network);
1234 assert(link->state != _LINK_STATE_INVALID);
1235
1236 link->static_routes_configured = false;
1237
1238 if (!link->addresses_ready)
1239 return 0;
1240
1241 if (!link_has_carrier(link) && !link->network->configure_without_carrier)
1242 /* During configuring addresses, the link lost its carrier. As networkd is dropping
1243 * the addresses now, let's not configure the routes either. */
1244 return 0;
1245
bd4733da
YW
1246 if (link->route_messages != 0) {
1247 log_link_debug(link, "Static routes are configuring.");
1248 return 0;
1249 }
1250
141318f7
YW
1251 r = link_set_routing_policy_rules(link);
1252 if (r < 0)
1253 return r;
1254
1255 /* First add the routes that enable us to talk to gateways, then add in the others that need a gateway. */
1256 for (phase = 0; phase < _PHASE_MAX; phase++)
2a54a044 1257 HASHMAP_FOREACH(rt, link->network->routes_by_section) {
1a3a6309 1258 if (rt->gateway_from_dhcp_or_ra)
141318f7
YW
1259 continue;
1260
94876904 1261 if ((!in_addr_is_set(rt->gw_family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
141318f7
YW
1262 continue;
1263
1264 r = route_configure(rt, link, route_handler, NULL);
1265 if (r < 0)
1266 return log_link_warning_errno(link, r, "Could not set routes: %m");
8c212f76
YW
1267
1268 link->route_messages++;
141318f7
YW
1269 }
1270
1271 if (link->route_messages == 0) {
1272 link->static_routes_configured = true;
1273 link_set_nexthop(link);
1274 } else {
1275 log_link_debug(link, "Setting routes");
1276 link_set_state(link, LINK_STATE_CONFIGURING);
1277 }
1278
1279 return 0;
1280}
1281
f9bb3338
YW
1282static int process_route_one(Manager *manager, Link *link, uint16_t type, const Route *tmp, const MultipathRoute *m) {
1283 _cleanup_(route_freep) Route *nr = NULL;
1284 Route *route = NULL;
1285 int r;
1286
1287 assert(manager);
1288 assert(tmp);
1289 assert(IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE));
1290
1291 if (m) {
1292 if (link)
1293 return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL),
1294 "rtnl: received route contains both RTA_OIF and RTA_MULTIPATH, ignoring.");
1295
1296 if (m->ifindex <= 0)
1297 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
1298 "rtnl: received multipath route with invalid ifindex, ignoring.");
1299
1300 r = link_get(manager, m->ifindex, &link);
1301 if (r < 0) {
1302 log_warning_errno(r, "rtnl: received multipath route for link (%d) we do not know, ignoring: %m", m->ifindex);
1303 return 0;
1304 }
1305
1306 r = route_new(&nr);
1307 if (r < 0)
1308 return log_oom();
1309
1310 route_copy(nr, tmp, m);
1311
1312 tmp = nr;
1313 }
1314
1315 (void) route_get(manager, link, tmp, &route);
1316
f9bb3338
YW
1317 switch (type) {
1318 case RTM_NEWROUTE:
167a5561
YW
1319 if (!route) {
1320 if (!manager->manage_foreign_routes)
552b90a2 1321 log_route_debug(tmp, "Ignoring received foreign", link, manager);
167a5561
YW
1322 else {
1323 /* A route appeared that we did not request */
552b90a2 1324 log_route_debug(tmp, "Remembering foreign", link, manager);
167a5561
YW
1325 r = route_add_foreign(manager, link, tmp, NULL);
1326 if (r < 0) {
1327 log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
1328 return 0;
1329 }
f9bb3338 1330 }
167a5561 1331 } else
552b90a2 1332 log_route_debug(tmp, "Received remembered", link, manager);
f9bb3338
YW
1333
1334 break;
1335
1336 case RTM_DELROUTE:
167a5561
YW
1337 log_route_debug(tmp,
1338 route ? "Forgetting" :
552b90a2
YW
1339 manager->manage_foreign_routes ? "Kernel removed unknown" : "Ignoring received foreign",
1340 link, manager);
f9bb3338
YW
1341 route_free(route);
1342 break;
1343
1344 default:
1345 assert_not_reached("Received route message with invalid RTNL message type");
1346 }
1347
1348 return 1;
1349}
1350
4468f01b 1351int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
f9bb3338 1352 _cleanup_ordered_set_free_free_ OrderedSet *multipath_routes = NULL;
4468f01b 1353 _cleanup_(route_freep) Route *tmp = NULL;
f9bb3338 1354 _cleanup_free_ void *rta_multipath = NULL;
4468f01b
YW
1355 Link *link = NULL;
1356 uint32_t ifindex;
1357 uint16_t type;
1358 unsigned char table;
f9bb3338 1359 size_t rta_len;
4468f01b
YW
1360 int r;
1361
1362 assert(rtnl);
1363 assert(message);
1364 assert(m);
1365
1366 if (sd_netlink_message_is_error(message)) {
1367 r = sd_netlink_message_get_errno(message);
1368 if (r < 0)
1369 log_message_warning_errno(message, r, "rtnl: failed to receive route message, ignoring");
1370
1371 return 0;
1372 }
1373
1374 r = sd_netlink_message_get_type(message, &type);
1375 if (r < 0) {
1376 log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
1377 return 0;
1378 } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
1379 log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
1380 return 0;
1381 }
1382
1383 r = sd_netlink_message_read_u32(message, RTA_OIF, &ifindex);
ad208fac 1384 if (r < 0 && r != -ENODATA) {
4468f01b
YW
1385 log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
1386 return 0;
ad208fac
YW
1387 } else if (r >= 0) {
1388 if (ifindex <= 0) {
1389 log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
1390 return 0;
1391 }
4468f01b 1392
ad208fac
YW
1393 r = link_get(m, ifindex, &link);
1394 if (r < 0 || !link) {
1395 /* when enumerating we might be out of sync, but we will
1396 * get the route again, so just ignore it */
1397 if (!m->enumerating)
1398 log_warning("rtnl: received route message for link (%d) we do not know about, ignoring", ifindex);
1399 return 0;
1400 }
4468f01b
YW
1401 }
1402
1403 r = route_new(&tmp);
1404 if (r < 0)
1405 return log_oom();
1406
1407 r = sd_rtnl_message_route_get_family(message, &tmp->family);
1408 if (r < 0) {
1409 log_link_warning(link, "rtnl: received route message without family, ignoring");
1410 return 0;
1411 } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
1412 log_link_debug(link, "rtnl: received route message with invalid family '%i', ignoring", tmp->family);
1413 return 0;
1414 }
1415
1416 r = sd_rtnl_message_route_get_protocol(message, &tmp->protocol);
1417 if (r < 0) {
1418 log_warning_errno(r, "rtnl: received route message without route protocol: %m");
1419 return 0;
1420 }
1421
ad6df717
YW
1422 r = netlink_message_read_in_addr_union(message, RTA_DST, tmp->family, &tmp->dst);
1423 if (r < 0 && r != -ENODATA) {
1424 log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
1425 return 0;
1426 }
4468f01b 1427
ad6df717
YW
1428 r = netlink_message_read_in_addr_union(message, RTA_GATEWAY, tmp->family, &tmp->gw);
1429 if (r < 0 && r != -ENODATA) {
1430 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1431 return 0;
1432 } else if (r >= 0)
1433 tmp->gw_family = tmp->family;
1434 else if (tmp->family == AF_INET) {
1435 RouteVia via;
6dd53981
YW
1436
1437 r = sd_netlink_message_read(message, RTA_VIA, sizeof(via), &via);
1438 if (r < 0 && r != -ENODATA) {
1439 log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
1440 return 0;
1441 } else if (r >= 0) {
1442 tmp->gw_family = via.family;
1443 tmp->gw = via.address;
4468f01b 1444 }
ad6df717 1445 }
4468f01b 1446
ad6df717
YW
1447 r = netlink_message_read_in_addr_union(message, RTA_SRC, tmp->family, &tmp->src);
1448 if (r < 0 && r != -ENODATA) {
1449 log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
1450 return 0;
1451 }
4468f01b 1452
ad6df717
YW
1453 r = netlink_message_read_in_addr_union(message, RTA_PREFSRC, tmp->family, &tmp->prefsrc);
1454 if (r < 0 && r != -ENODATA) {
1455 log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
4468f01b
YW
1456 return 0;
1457 }
1458
1459 r = sd_rtnl_message_route_get_dst_prefixlen(message, &tmp->dst_prefixlen);
1460 if (r < 0) {
1461 log_link_warning_errno(link, r, "rtnl: received route message with invalid destination prefixlen, ignoring: %m");
1462 return 0;
1463 }
1464
1465 r = sd_rtnl_message_route_get_src_prefixlen(message, &tmp->src_prefixlen);
1466 if (r < 0) {
1467 log_link_warning_errno(link, r, "rtnl: received route message with invalid source prefixlen, ignoring: %m");
1468 return 0;
1469 }
1470
1471 r = sd_rtnl_message_route_get_scope(message, &tmp->scope);
1472 if (r < 0) {
1473 log_link_warning_errno(link, r, "rtnl: received route message with invalid scope, ignoring: %m");
1474 return 0;
1475 }
1476
1477 r = sd_rtnl_message_route_get_tos(message, &tmp->tos);
1478 if (r < 0) {
1479 log_link_warning_errno(link, r, "rtnl: received route message with invalid tos, ignoring: %m");
1480 return 0;
1481 }
1482
1483 r = sd_rtnl_message_route_get_type(message, &tmp->type);
1484 if (r < 0) {
1485 log_link_warning_errno(link, r, "rtnl: received route message with invalid type, ignoring: %m");
1486 return 0;
1487 }
1488
1489 r = sd_rtnl_message_route_get_table(message, &table);
1490 if (r < 0) {
1491 log_link_warning_errno(link, r, "rtnl: received route message with invalid table, ignoring: %m");
1492 return 0;
1493 }
1494 tmp->table = table;
1495
1496 r = sd_netlink_message_read_u32(message, RTA_PRIORITY, &tmp->priority);
1497 if (r < 0 && r != -ENODATA) {
1498 log_link_warning_errno(link, r, "rtnl: received route message with invalid priority, ignoring: %m");
1499 return 0;
1500 }
1501
1502 r = sd_netlink_message_enter_container(message, RTA_METRICS);
1503 if (r < 0 && r != -ENODATA) {
1504 log_link_error_errno(link, r, "rtnl: Could not enter RTA_METRICS container: %m");
1505 return 0;
1506 }
1507 if (r >= 0) {
1508 r = sd_netlink_message_read_u32(message, RTAX_INITCWND, &tmp->initcwnd);
1509 if (r < 0 && r != -ENODATA) {
1510 log_link_warning_errno(link, r, "rtnl: received route message with invalid initcwnd, ignoring: %m");
1511 return 0;
1512 }
1513
1514 r = sd_netlink_message_read_u32(message, RTAX_INITRWND, &tmp->initrwnd);
1515 if (r < 0 && r != -ENODATA) {
1516 log_link_warning_errno(link, r, "rtnl: received route message with invalid initrwnd, ignoring: %m");
1517 return 0;
386e8908
YW
1518 }
1519
1520 r = sd_netlink_message_read_u32(message, RTAX_ADVMSS, &tmp->advmss);
1521 if (r < 0 && r != -ENODATA) {
1522 log_link_warning_errno(link, r, "rtnl: received route message with invalid advmss, ignoring: %m");
1523 return 0;
4468f01b
YW
1524 }
1525
1526 r = sd_netlink_message_exit_container(message);
1527 if (r < 0) {
1528 log_link_error_errno(link, r, "rtnl: Could not exit from RTA_METRICS container: %m");
1529 return 0;
1530 }
1531 }
1532
f9bb3338
YW
1533 r = sd_netlink_message_read_data(message, RTA_MULTIPATH, &rta_len, &rta_multipath);
1534 if (r < 0 && r != -ENODATA) {
1535 log_link_warning_errno(link, r, "rtnl: failed to read RTA_MULTIPATH attribute, ignoring: %m");
1536 return 0;
1537 } else if (r >= 0) {
1538 r = rtattr_read_nexthop(rta_multipath, rta_len, tmp->family, &multipath_routes);
1539 if (r < 0) {
1540 log_link_warning_errno(link, r, "rtnl: failed to parse RTA_MULTIPATH attribute, ignoring: %m");
1541 return 0;
4468f01b 1542 }
4468f01b
YW
1543 }
1544
575f14ee
YW
1545 /* IPv6 routes with reject type are always assigned to the loopback interface. See kernel's
1546 * fib6_nh_init() in net/ipv6/route.c. However, we'd like to manage them by Manager. Hence, set
1547 * link to NULL here. */
1548 if (route_type_is_reject(tmp))
1549 link = NULL;
1550
f9bb3338
YW
1551 if (ordered_set_isempty(multipath_routes))
1552 (void) process_route_one(m, link, type, tmp, NULL);
1553 else {
1554 MultipathRoute *mr;
4468f01b 1555
f9bb3338
YW
1556 ORDERED_SET_FOREACH(mr, multipath_routes) {
1557 r = process_route_one(m, link, type, tmp, mr);
1558 if (r < 0)
1559 break;
1560 }
4468f01b
YW
1561 }
1562
1563 return 1;
1564}
1565
fa7cd711 1566int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 1567 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1568 unsigned section_line;
fa7cd711
YW
1569 int r;
1570
1571 assert(network);
1572
1573 if (!network->ipv4ll_route)
1574 return 0;
1575
2a54a044
YW
1576 section_line = hashmap_find_free_section_line(network->routes_by_section);
1577
fa7cd711 1578 /* IPv4LLRoute= is in [Network] section. */
2a54a044 1579 r = route_new_static(network, network->filename, section_line, &n);
fa7cd711
YW
1580 if (r < 0)
1581 return r;
1582
1583 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
1584 if (r < 0)
1585 return r;
1586
1587 n->family = AF_INET;
1588 n->dst_prefixlen = 16;
1589 n->scope = RT_SCOPE_LINK;
94d6e299
YW
1590 n->scope_set = true;
1591 n->table_set = true;
fa7cd711
YW
1592 n->priority = IPV4LL_ROUTE_METRIC;
1593 n->protocol = RTPROT_STATIC;
1594
1595 TAKE_PTR(n);
1596 return 0;
1597}
1598
5d5003ab
YW
1599int network_add_default_route_on_device(Network *network) {
1600 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2a54a044 1601 unsigned section_line;
5d5003ab
YW
1602 int r;
1603
1604 assert(network);
1605
1606 if (!network->default_route_on_device)
1607 return 0;
1608
2a54a044
YW
1609 section_line = hashmap_find_free_section_line(network->routes_by_section);
1610
5d5003ab 1611 /* DefaultRouteOnDevice= is in [Network] section. */
2a54a044 1612 r = route_new_static(network, network->filename, section_line, &n);
5d5003ab
YW
1613 if (r < 0)
1614 return r;
1615
5d5003ab 1616 n->family = AF_INET;
c697db75
YW
1617 n->scope = RT_SCOPE_LINK;
1618 n->scope_set = true;
1619 n->protocol = RTPROT_STATIC;
5d5003ab
YW
1620
1621 TAKE_PTR(n);
1622 return 0;
1623}
1624
27efb52b
YW
1625int config_parse_gateway(
1626 const char *unit,
f579559b
TG
1627 const char *filename,
1628 unsigned line,
1629 const char *section,
71a61510 1630 unsigned section_line,
f579559b
TG
1631 const char *lvalue,
1632 int ltype,
1633 const char *rvalue,
1634 void *data,
1635 void *userdata) {
44e7b949 1636
6ae115c1 1637 Network *network = userdata;
fcbf4cb7 1638 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1639 int r;
f579559b
TG
1640
1641 assert(filename);
6ae115c1 1642 assert(section);
f579559b
TG
1643 assert(lvalue);
1644 assert(rvalue);
1645 assert(data);
1646
92fe133a 1647 if (streq(section, "Network")) {
2a54a044
YW
1648 /* we are not in an Route section, so use line number instead */
1649 r = route_new_static(network, filename, line, &n);
d96edb2c
YW
1650 if (r == -ENOMEM)
1651 return log_oom();
1652 if (r < 0) {
1653 log_syntax(unit, LOG_WARNING, filename, line, r,
1654 "Failed to allocate route, ignoring assignment: %m");
1655 return 0;
1656 }
1985c54f 1657 } else {
f4859fc7 1658 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1659 if (r == -ENOMEM)
1660 return log_oom();
1661 if (r < 0) {
1662 log_syntax(unit, LOG_WARNING, filename, line, r,
1663 "Failed to allocate route, ignoring assignment: %m");
1664 return 0;
1665 }
1985c54f 1666
d442bb37 1667 if (isempty(rvalue)) {
1a3a6309 1668 n->gateway_from_dhcp_or_ra = false;
d442bb37
YW
1669 n->gw_family = AF_UNSPEC;
1670 n->gw = IN_ADDR_NULL;
1671 TAKE_PTR(n);
1672 return 0;
1673 }
1674
427928ca 1675 if (streq(rvalue, "_dhcp")) {
1a3a6309 1676 n->gateway_from_dhcp_or_ra = true;
1985c54f
YW
1677 TAKE_PTR(n);
1678 return 0;
1679 }
d306d1d0
YW
1680
1681 if (streq(rvalue, "_dhcp4")) {
1682 n->gw_family = AF_INET;
1a3a6309 1683 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
1684 TAKE_PTR(n);
1685 return 0;
1686 }
1687
b8caa4ef 1688 if (streq(rvalue, "_ipv6ra")) {
d306d1d0 1689 n->gw_family = AF_INET6;
1a3a6309 1690 n->gateway_from_dhcp_or_ra = true;
d306d1d0
YW
1691 TAKE_PTR(n);
1692 return 0;
1693 }
1985c54f 1694 }
f579559b 1695
6dd53981 1696 r = in_addr_from_string_auto(rvalue, &n->gw_family, &n->gw);
f579559b 1697 if (r < 0) {
d96edb2c 1698 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 1699 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
1700 return 0;
1701 }
1702
1a3a6309 1703 n->gateway_from_dhcp_or_ra = false;
aff44301 1704 TAKE_PTR(n);
f579559b
TG
1705 return 0;
1706}
6ae115c1 1707
27efb52b
YW
1708int config_parse_preferred_src(
1709 const char *unit,
0d07e595
JK
1710 const char *filename,
1711 unsigned line,
1712 const char *section,
1713 unsigned section_line,
1714 const char *lvalue,
1715 int ltype,
1716 const char *rvalue,
1717 void *data,
1718 void *userdata) {
1719
1720 Network *network = userdata;
fcbf4cb7 1721 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1722 int r;
0d07e595
JK
1723
1724 assert(filename);
1725 assert(section);
1726 assert(lvalue);
1727 assert(rvalue);
1728 assert(data);
1729
f4859fc7 1730 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1731 if (r == -ENOMEM)
1732 return log_oom();
1733 if (r < 0) {
1734 log_syntax(unit, LOG_WARNING, filename, line, r,
1735 "Failed to allocate route, ignoring assignment: %m");
1736 return 0;
1737 }
0d07e595 1738
01d4e732
YW
1739 if (n->family == AF_UNSPEC)
1740 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
1741 else
1742 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 1743 if (r < 0) {
d96edb2c 1744 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1745 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1746 return 0;
1747 }
1748
aff44301 1749 TAKE_PTR(n);
0d07e595
JK
1750 return 0;
1751}
1752
27efb52b
YW
1753int config_parse_destination(
1754 const char *unit,
6ae115c1
TG
1755 const char *filename,
1756 unsigned line,
1757 const char *section,
1758 unsigned section_line,
1759 const char *lvalue,
1760 int ltype,
1761 const char *rvalue,
1762 void *data,
1763 void *userdata) {
44e7b949 1764
6ae115c1 1765 Network *network = userdata;
fcbf4cb7 1766 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
1767 union in_addr_union *buffer;
1768 unsigned char *prefixlen;
ca3bad65 1769 int r;
6ae115c1
TG
1770
1771 assert(filename);
1772 assert(section);
1773 assert(lvalue);
1774 assert(rvalue);
1775 assert(data);
1776
f4859fc7 1777 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1778 if (r == -ENOMEM)
1779 return log_oom();
1780 if (r < 0) {
1781 log_syntax(unit, LOG_WARNING, filename, line, r,
1782 "Failed to allocate route, ignoring assignment: %m");
1783 return 0;
1784 }
6ae115c1 1785
9e7e4408 1786 if (streq(lvalue, "Destination")) {
7934dede
YW
1787 buffer = &n->dst;
1788 prefixlen = &n->dst_prefixlen;
9e7e4408 1789 } else if (streq(lvalue, "Source")) {
7934dede
YW
1790 buffer = &n->src;
1791 prefixlen = &n->src_prefixlen;
9e7e4408
TG
1792 } else
1793 assert_not_reached(lvalue);
1794
01d4e732
YW
1795 if (n->family == AF_UNSPEC)
1796 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
1797 else
1798 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 1799 if (r < 0) {
d96edb2c 1800 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1801 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1802 return 0;
1803 }
1804
aff44301 1805 TAKE_PTR(n);
6ae115c1
TG
1806 return 0;
1807}
5d8e593d 1808
27efb52b
YW
1809int config_parse_route_priority(
1810 const char *unit,
1811 const char *filename,
1812 unsigned line,
1813 const char *section,
1814 unsigned section_line,
1815 const char *lvalue,
1816 int ltype,
1817 const char *rvalue,
1818 void *data,
1819 void *userdata) {
1820
5d8e593d 1821 Network *network = userdata;
fcbf4cb7 1822 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
1823 int r;
1824
1825 assert(filename);
1826 assert(section);
1827 assert(lvalue);
1828 assert(rvalue);
1829 assert(data);
1830
f4859fc7 1831 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1832 if (r == -ENOMEM)
1833 return log_oom();
1834 if (r < 0) {
1835 log_syntax(unit, LOG_WARNING, filename, line, r,
1836 "Failed to allocate route, ignoring assignment: %m");
1837 return 0;
1838 }
5d8e593d 1839
aff44301 1840 r = safe_atou32(rvalue, &n->priority);
1c4b1179 1841 if (r < 0) {
d96edb2c 1842 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1843 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1844 return 0;
1845 }
5d8e593d 1846
c27abcf4 1847 n->priority_set = true;
aff44301 1848 TAKE_PTR(n);
5d8e593d
SS
1849 return 0;
1850}
769b56a3 1851
27efb52b
YW
1852int config_parse_route_scope(
1853 const char *unit,
1854 const char *filename,
1855 unsigned line,
1856 const char *section,
1857 unsigned section_line,
1858 const char *lvalue,
1859 int ltype,
1860 const char *rvalue,
1861 void *data,
1862 void *userdata) {
1863
769b56a3 1864 Network *network = userdata;
fcbf4cb7 1865 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
1866 int r;
1867
1868 assert(filename);
1869 assert(section);
1870 assert(lvalue);
1871 assert(rvalue);
1872 assert(data);
1873
f4859fc7 1874 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1875 if (r == -ENOMEM)
1876 return log_oom();
1877 if (r < 0) {
1878 log_syntax(unit, LOG_WARNING, filename, line, r,
1879 "Failed to allocate route, ignoring assignment: %m");
1880 return 0;
1881 }
769b56a3 1882
41b90a1e
YW
1883 r = route_scope_from_string(rvalue);
1884 if (r < 0) {
b98680b2 1885 log_syntax(unit, LOG_WARNING, filename, line, r, "Unknown route scope: %s", rvalue);
769b56a3
TG
1886 return 0;
1887 }
1888
41b90a1e 1889 n->scope = r;
94d6e299 1890 n->scope_set = true;
aff44301 1891 TAKE_PTR(n);
769b56a3
TG
1892 return 0;
1893}
c953b24c 1894
27efb52b
YW
1895int config_parse_route_table(
1896 const char *unit,
1897 const char *filename,
1898 unsigned line,
1899 const char *section,
1900 unsigned section_line,
1901 const char *lvalue,
1902 int ltype,
1903 const char *rvalue,
1904 void *data,
1905 void *userdata) {
1906
fcbf4cb7 1907 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 1908 Network *network = userdata;
c953b24c
SS
1909 int r;
1910
1911 assert(filename);
1912 assert(section);
1913 assert(lvalue);
1914 assert(rvalue);
1915 assert(data);
1916
f4859fc7 1917 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1918 if (r == -ENOMEM)
1919 return log_oom();
1920 if (r < 0) {
1921 log_syntax(unit, LOG_WARNING, filename, line, r,
1922 "Failed to allocate route, ignoring assignment: %m");
1923 return 0;
1924 }
c953b24c 1925
552b90a2 1926 r = manager_get_route_table_from_string(network->manager, rvalue, &n->table);
c038ce46
SS
1927 if (r < 0) {
1928 log_syntax(unit, LOG_WARNING, filename, line, r,
1929 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
1930 return 0;
c953b24c
SS
1931 }
1932
94d6e299 1933 n->table_set = true;
aff44301 1934 TAKE_PTR(n);
c953b24c
SS
1935 return 0;
1936}
28959f7d 1937
d96edb2c 1938int config_parse_route_boolean(
27efb52b
YW
1939 const char *unit,
1940 const char *filename,
1941 unsigned line,
1942 const char *section,
1943 unsigned section_line,
1944 const char *lvalue,
1945 int ltype,
1946 const char *rvalue,
1947 void *data,
1948 void *userdata) {
1949
28959f7d 1950 Network *network = userdata;
fcbf4cb7 1951 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
1952 int r;
1953
1954 assert(filename);
1955 assert(section);
1956 assert(lvalue);
1957 assert(rvalue);
1958 assert(data);
1959
1960 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1961 if (r == -ENOMEM)
1962 return log_oom();
1963 if (r < 0) {
1964 log_syntax(unit, LOG_WARNING, filename, line, r,
1965 "Failed to allocate route, ignoring assignment: %m");
1966 return 0;
1967 }
28959f7d
SS
1968
1969 r = parse_boolean(rvalue);
1970 if (r < 0) {
d96edb2c 1971 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 1972 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
1973 return 0;
1974 }
1975
d96edb2c
YW
1976 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
1977 n->gateway_onlink = r;
1978 else if (streq(lvalue, "QuickAck"))
1979 n->quickack = r;
1980 else if (streq(lvalue, "FastOpenNoCookie"))
1981 n->fast_open_no_cookie = r;
1982 else if (streq(lvalue, "TTLPropagate"))
1983 n->ttl_propagate = r;
1984 else
1985 assert_not_reached("Invalid lvalue");
54901fd2 1986
aff44301 1987 TAKE_PTR(n);
b5bf6f64
SS
1988 return 0;
1989}
1990
27efb52b
YW
1991int config_parse_ipv6_route_preference(
1992 const char *unit,
1993 const char *filename,
1994 unsigned line,
1995 const char *section,
1996 unsigned section_line,
1997 const char *lvalue,
1998 int ltype,
1999 const char *rvalue,
2000 void *data,
2001 void *userdata) {
2002
b5bf6f64 2003 Network *network = userdata;
fcbf4cb7 2004 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
2005 int r;
2006
4c7bd9cf 2007 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2008 if (r == -ENOMEM)
2009 return log_oom();
2010 if (r < 0) {
2011 log_syntax(unit, LOG_WARNING, filename, line, r,
2012 "Failed to allocate route, ignoring assignment: %m");
2013 return 0;
2014 }
4c7bd9cf 2015
b5bf6f64
SS
2016 if (streq(rvalue, "low"))
2017 n->pref = ICMPV6_ROUTER_PREF_LOW;
2018 else if (streq(rvalue, "medium"))
2019 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
2020 else if (streq(rvalue, "high"))
2021 n->pref = ICMPV6_ROUTER_PREF_HIGH;
2022 else {
d96edb2c 2023 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
2024 return 0;
2025 }
28959f7d 2026
c27abcf4 2027 n->pref_set = true;
aff44301 2028 TAKE_PTR(n);
28959f7d
SS
2029 return 0;
2030}
c83ecc04 2031
27efb52b
YW
2032int config_parse_route_protocol(
2033 const char *unit,
2034 const char *filename,
2035 unsigned line,
2036 const char *section,
2037 unsigned section_line,
2038 const char *lvalue,
2039 int ltype,
2040 const char *rvalue,
2041 void *data,
2042 void *userdata) {
2043
c83ecc04 2044 Network *network = userdata;
fcbf4cb7 2045 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
2046 int r;
2047
2048 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2049 if (r == -ENOMEM)
2050 return log_oom();
2051 if (r < 0) {
2052 log_syntax(unit, LOG_WARNING, filename, line, r,
2053 "Failed to allocate route, ignoring assignment: %m");
2054 return 0;
2055 }
c83ecc04 2056
1b64651b 2057 r = route_protocol_from_string(rvalue);
e4ffe103
YW
2058 if (r < 0) {
2059 log_syntax(unit, LOG_WARNING, filename, line, r,
2060 "Failed to parse route protocol \"%s\", ignoring assignment: %m", rvalue);
2061 return 0;
c83ecc04
SS
2062 }
2063
e4ffe103
YW
2064 n->protocol = r;
2065
aff44301 2066 TAKE_PTR(n);
c83ecc04
SS
2067 return 0;
2068}
983226f3 2069
27efb52b
YW
2070int config_parse_route_type(
2071 const char *unit,
2072 const char *filename,
2073 unsigned line,
2074 const char *section,
2075 unsigned section_line,
2076 const char *lvalue,
2077 int ltype,
2078 const char *rvalue,
2079 void *data,
2080 void *userdata) {
2081
983226f3 2082 Network *network = userdata;
fcbf4cb7 2083 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 2084 int t, r;
983226f3
SS
2085
2086 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2087 if (r == -ENOMEM)
2088 return log_oom();
2089 if (r < 0) {
2090 log_syntax(unit, LOG_WARNING, filename, line, r,
2091 "Failed to allocate route, ignoring assignment: %m");
2092 return 0;
2093 }
983226f3 2094
7a22312d
YW
2095 t = route_type_from_string(rvalue);
2096 if (t < 0) {
b98680b2 2097 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 2098 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
2099 return 0;
2100 }
2101
7a22312d
YW
2102 n->type = (unsigned char) t;
2103
aff44301 2104 TAKE_PTR(n);
983226f3
SS
2105 return 0;
2106}
323d9329 2107
007cac09
SS
2108int config_parse_tcp_advmss(
2109 const char *unit,
2110 const char *filename,
2111 unsigned line,
2112 const char *section,
2113 unsigned section_line,
2114 const char *lvalue,
2115 int ltype,
2116 const char *rvalue,
2117 void *data,
2118 void *userdata) {
2119
2120 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2121 Network *network = userdata;
2122 uint64_t u;
2123 int r;
2124
2125 assert(filename);
2126 assert(section);
2127 assert(lvalue);
2128 assert(rvalue);
2129 assert(data);
2130
2131 r = route_new_static(network, filename, section_line, &n);
2132 if (r == -ENOMEM)
2133 return log_oom();
2134 if (r < 0) {
2135 log_syntax(unit, LOG_WARNING, filename, line, r,
2136 "Failed to allocate route, ignoring assignment: %m");
2137 return 0;
2138 }
2139
2140 if (isempty(rvalue)) {
2141 n->advmss = 0;
78ebbf02 2142 TAKE_PTR(n);
007cac09
SS
2143 return 0;
2144 }
2145
2146 r = parse_size(rvalue, 1024, &u);
2147 if (r < 0) {
2148 log_syntax(unit, LOG_WARNING, filename, line, r,
2149 "Could not parse TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2150 return 0;
2151 }
2152
2153 if (u == 0 || u > UINT32_MAX) {
2154 log_syntax(unit, LOG_WARNING, filename, line, 0,
2155 "Invalid TCPAdvertisedMaximumSegmentSize= \"%s\", ignoring assignment: %m", rvalue);
2156 return 0;
2157 }
2158
2159 n->advmss = u;
2160
2161 TAKE_PTR(n);
2162 return 0;
2163}
2164
27efb52b
YW
2165int config_parse_tcp_window(
2166 const char *unit,
2167 const char *filename,
2168 unsigned line,
2169 const char *section,
2170 unsigned section_line,
2171 const char *lvalue,
2172 int ltype,
2173 const char *rvalue,
2174 void *data,
2175 void *userdata) {
2176
fcbf4cb7 2177 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33 2178 Network *network = userdata;
fef160b5 2179 uint32_t k;
323d9329
SS
2180 int r;
2181
2182 assert(filename);
2183 assert(section);
2184 assert(lvalue);
2185 assert(rvalue);
2186 assert(data);
2187
2188 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2189 if (r == -ENOMEM)
2190 return log_oom();
2191 if (r < 0) {
2192 log_syntax(unit, LOG_WARNING, filename, line, r,
2193 "Failed to allocate route, ignoring assignment: %m");
2194 return 0;
2195 }
323d9329 2196
fef160b5 2197 r = safe_atou32(rvalue, &k);
f205a92a 2198 if (r < 0) {
d96edb2c 2199 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
2200 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2201 return 0;
2202 }
fef160b5 2203 if (k >= 1024) {
d96edb2c 2204 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 2205 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
2206 return 0;
2207 }
fd9d7de1
SS
2208 if (k == 0) {
2209 log_syntax(unit, LOG_WARNING, filename, line, 0,
2210 "Invalid TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
2211 return 0;
2212 }
323d9329
SS
2213
2214 if (streq(lvalue, "InitialCongestionWindow"))
2215 n->initcwnd = k;
2216 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
2217 n->initrwnd = k;
f205a92a
YW
2218 else
2219 assert_not_reached("Invalid TCP window type.");
323d9329 2220
aff44301 2221 TAKE_PTR(n);
323d9329
SS
2222 return 0;
2223}
09f5dfad 2224
cea79e66
SS
2225int config_parse_route_mtu(
2226 const char *unit,
2227 const char *filename,
2228 unsigned line,
2229 const char *section,
2230 unsigned section_line,
2231 const char *lvalue,
2232 int ltype,
2233 const char *rvalue,
2234 void *data,
2235 void *userdata) {
2236
2237 Network *network = userdata;
fcbf4cb7 2238 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
2239 int r;
2240
2241 assert(filename);
2242 assert(section);
2243 assert(lvalue);
2244 assert(rvalue);
2245 assert(data);
2246
2247 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2248 if (r == -ENOMEM)
2249 return log_oom();
2250 if (r < 0) {
2251 log_syntax(unit, LOG_WARNING, filename, line, r,
2252 "Failed to allocate route, ignoring assignment: %m");
2253 return 0;
2254 }
cea79e66
SS
2255
2256 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
2257 if (r < 0)
2258 return r;
2259
aff44301 2260 TAKE_PTR(n);
cea79e66
SS
2261 return 0;
2262}
fcbf4cb7 2263
6ff5cc6b
YW
2264int config_parse_multipath_route(
2265 const char *unit,
2266 const char *filename,
2267 unsigned line,
2268 const char *section,
2269 unsigned section_line,
2270 const char *lvalue,
2271 int ltype,
2272 const char *rvalue,
2273 void *data,
2274 void *userdata) {
2275
2276 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
2277 _cleanup_free_ char *word = NULL, *buf = NULL;
2278 _cleanup_free_ MultipathRoute *m = NULL;
2279 Network *network = userdata;
2280 const char *p, *ip, *dev;
2281 union in_addr_union a;
2282 int family, r;
2283
2284 assert(filename);
2285 assert(section);
2286 assert(lvalue);
2287 assert(rvalue);
2288 assert(data);
2289
2290 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
2291 if (r == -ENOMEM)
2292 return log_oom();
2293 if (r < 0) {
2294 log_syntax(unit, LOG_WARNING, filename, line, r,
2295 "Failed to allocate route, ignoring assignment: %m");
2296 return 0;
2297 }
6ff5cc6b
YW
2298
2299 if (isempty(rvalue)) {
2300 n->multipath_routes = ordered_set_free_free(n->multipath_routes);
2301 return 0;
2302 }
2303
2304 m = new0(MultipathRoute, 1);
2305 if (!m)
2306 return log_oom();
2307
2308 p = rvalue;
2309 r = extract_first_word(&p, &word, NULL, 0);
2310 if (r == -ENOMEM)
2311 return log_oom();
2312 if (r <= 0) {
d96edb2c 2313 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2314 "Invalid multipath route option, ignoring assignment: %s", rvalue);
2315 return 0;
2316 }
2317
2318 dev = strchr(word, '@');
2319 if (dev) {
2320 buf = strndup(word, dev - word);
2321 if (!buf)
2322 return log_oom();
2323 ip = buf;
2324 dev++;
2325 } else
2326 ip = word;
2327
2328 r = in_addr_from_string_auto(ip, &family, &a);
2329 if (r < 0) {
d96edb2c 2330 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2331 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
2332 return 0;
2333 }
2334 m->gateway.address = a;
2335 m->gateway.family = family;
2336
2337 if (dev) {
d308bb99 2338 r = resolve_interface(NULL, dev);
6ff5cc6b 2339 if (r < 0) {
d96edb2c 2340 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2341 "Invalid interface name or index, ignoring assignment: %s", dev);
2342 return 0;
2343 }
597da51b 2344 m->ifindex = r;
6ff5cc6b
YW
2345 }
2346
2347 if (!isempty(p)) {
2348 r = safe_atou32(p, &m->weight);
2349 if (r < 0) {
d96edb2c 2350 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2351 "Invalid multipath route weight, ignoring assignment: %s", p);
2352 return 0;
2353 }
2354 if (m->weight == 0 || m->weight > 256) {
d96edb2c 2355 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
2356 "Invalid multipath route weight, ignoring assignment: %s", p);
2357 return 0;
2358 }
2359 }
2360
8cb34651
SS
2361 r = ordered_set_ensure_put(&n->multipath_routes, NULL, m);
2362 if (r == -ENOMEM)
6ff5cc6b 2363 return log_oom();
6ff5cc6b 2364 if (r < 0) {
d96edb2c 2365 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
2366 "Failed to store multipath route, ignoring assignment: %m");
2367 return 0;
2368 }
2369
2370 TAKE_PTR(m);
2371 TAKE_PTR(n);
2372 return 0;
2373}
2374
c038ce46
SS
2375int config_parse_route_table_names(
2376 const char *unit,
2377 const char *filename,
2378 unsigned line,
2379 const char *section,
2380 unsigned section_line,
2381 const char *lvalue,
2382 int ltype,
2383 const char *rvalue,
2384 void *data,
2385 void *userdata) {
2386
552b90a2 2387 Manager *m = userdata;
c038ce46
SS
2388 int r;
2389
2390 assert(filename);
2391 assert(lvalue);
2392 assert(rvalue);
552b90a2 2393 assert(userdata);
c038ce46
SS
2394
2395 if (isempty(rvalue)) {
552b90a2
YW
2396 m->route_table_names_by_number = hashmap_free(m->route_table_names_by_number);
2397 m->route_table_numbers_by_name = hashmap_free(m->route_table_numbers_by_name);
c038ce46
SS
2398 return 0;
2399 }
2400
310eff72
YW
2401 for (const char *p = rvalue;;) {
2402 _cleanup_free_ char *name = NULL;
2403 uint32_t table;
2404 char *num;
c038ce46 2405
310eff72
YW
2406 r = extract_first_word(&p, &name, NULL, 0);
2407 if (r == -ENOMEM)
2408 return log_oom();
2409 if (r < 0) {
2410 log_syntax(unit, LOG_WARNING, filename, line, r,
2411 "Invalid RouteTable=, ignoring assignment: %s", rvalue);
2412 return 0;
2413 }
2414 if (r == 0)
2415 return 0;
c038ce46 2416
310eff72
YW
2417 num = strchr(name, ':');
2418 if (!num) {
2419 log_syntax(unit, LOG_WARNING, filename, line, 0,
2420 "Invalid route table name and number pair, ignoring assignment: %s", name);
2421 continue;
2422 }
c038ce46 2423
310eff72 2424 *num++ = '\0';
c038ce46 2425
310eff72
YW
2426 if (STR_IN_SET(name, "default", "main", "local")) {
2427 log_syntax(unit, LOG_WARNING, filename, line, 0,
2428 "Route table name %s already predefined. Ignoring assignment: %s:%s", name, name, num);
2429 continue;
2430 }
c038ce46 2431
310eff72
YW
2432 r = safe_atou32(num, &table);
2433 if (r < 0) {
2434 log_syntax(unit, LOG_WARNING, filename, line, r,
2435 "Failed to parse route table number '%s', ignoring assignment: %s:%s", num, name, num);
2436 continue;
2437 }
2438 if (table == 0) {
2439 log_syntax(unit, LOG_WARNING, filename, line, 0,
2440 "Invalid route table number, ignoring assignment: %s:%s", name, num);
2441 continue;
2442 }
2443
552b90a2 2444 r = hashmap_ensure_put(&m->route_table_numbers_by_name, &string_hash_ops_free, name, UINT32_TO_PTR(table));
310eff72
YW
2445 if (r == -ENOMEM)
2446 return log_oom();
2447 if (r == -EEXIST) {
2448 log_syntax(unit, LOG_WARNING, filename, line, r,
2449 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
2450 continue;
2451 }
2452 if (r < 0) {
2453 log_syntax(unit, LOG_WARNING, filename, line, r,
2454 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
2455 continue;
2456 }
552b90a2
YW
2457 if (r == 0)
2458 /* The entry is duplicated. It should not be added to route_table_names_by_number hashmap. */
2459 continue;
2460
2461 r = hashmap_ensure_put(&m->route_table_names_by_number, NULL, UINT32_TO_PTR(table), name);
2462 if (r < 0) {
2463 hashmap_remove(m->route_table_numbers_by_name, name);
2464
2465 if (r == -ENOMEM)
2466 return log_oom();
2467 if (r == -EEXIST)
2468 log_syntax(unit, LOG_WARNING, filename, line, r,
2469 "Specified route table name and number pair conflicts with others, ignoring assignment: %s:%s", name, num);
2470 else
2471 log_syntax(unit, LOG_WARNING, filename, line, r,
2472 "Failed to store route table name and number pair, ignoring assignment: %s:%s", name, num);
2473 continue;
2474 }
2475 assert(r > 0);
2476
2477 TAKE_PTR(name);
310eff72 2478 }
c038ce46
SS
2479}
2480
d9940a3f 2481static int route_section_verify(Route *route, Network *network) {
fcbf4cb7
YW
2482 if (section_is_invalid(route->section))
2483 return -EINVAL;
2484
956dbf36 2485 if (route->gateway_from_dhcp_or_ra) {
c3d679c4
YW
2486 if (route->gw_family == AF_UNSPEC) {
2487 /* When deprecated Gateway=_dhcp is set, then assume gateway family based on other settings. */
2488 switch (route->family) {
2489 case AF_UNSPEC:
2490 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2491 "Please use \"_dhcp4\" or \"_ipv6ra\" instead. Assuming \"_dhcp4\".",
2492 route->section->filename, route->section->line);
2493 route->family = AF_INET;
2494 break;
2495 case AF_INET:
2496 case AF_INET6:
2497 log_warning("%s: Deprecated value \"_dhcp\" is specified for Gateway= in [Route] section from line %u. "
2498 "Assuming \"%s\" based on Destination=, Source=, or PreferredSource= setting.",
2499 route->section->filename, route->section->line, route->family == AF_INET ? "_dhcp4" : "_ipv6ra");
2500 break;
2501 default:
2502 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2503 "%s: Invalid route family. Ignoring [Route] section from line %u.",
2504 route->section->filename, route->section->line);
2505 }
2506 route->gw_family = route->family;
2507 }
2508
956dbf36
YW
2509 if (route->gw_family == AF_INET && !FLAGS_SET(network->dhcp, ADDRESS_FAMILY_IPV4))
2510 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2511 "%s: Gateway=\"_dhcp4\" is specified but DHCPv4 client is disabled. "
2512 "Ignoring [Route] section from line %u.",
2513 route->section->filename, route->section->line);
2514
2515 if (route->gw_family == AF_INET6 && !network->ipv6_accept_ra)
2516 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2517 "%s: Gateway=\"_ipv6ra\" is specified but IPv6AcceptRA= is disabled. "
2518 "Ignoring [Route] section from line %u.",
2519 route->section->filename, route->section->line);
2520 }
2521
c3d679c4 2522 /* When only Gateway= is specified, assume the route family based on the Gateway address. */
6dd53981
YW
2523 if (route->family == AF_UNSPEC)
2524 route->family = route->gw_family;
2525
fcbf4cb7
YW
2526 if (route->family == AF_UNSPEC) {
2527 assert(route->section);
2528
c3d679c4
YW
2529 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2530 "%s: Route section without Gateway=, Destination=, Source=, "
2531 "or PreferredSource= field configured. "
2532 "Ignoring [Route] section from line %u.",
2533 route->section->filename, route->section->line);
fcbf4cb7
YW
2534 }
2535
6dd53981
YW
2536 if (route->family == AF_INET6 && route->gw_family == AF_INET)
2537 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2538 "%s: IPv4 gateway is configured for IPv6 route. "
2539 "Ignoring [Route] section from line %u.",
2540 route->section->filename, route->section->line);
2541
c0d48bc5
YW
2542 if (!route->table_set && network->vrf) {
2543 route->table = VRF(network->vrf)->table;
2544 route->table_set = true;
2545 }
2546
f5c38922
YW
2547 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
2548 route->table = RT_TABLE_LOCAL;
2549
2550 if (!route->scope_set && route->family != AF_INET6) {
2551 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
2552 route->scope = RT_SCOPE_HOST;
2553 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
2554 route->scope = RT_SCOPE_LINK;
94d6e299
YW
2555 }
2556
fd7701bf
YW
2557 if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) {
2558 log_warning("%s: Scope= is specified for IPv6 route. It will be ignored.", route->section->filename);
2559 route->scope = RT_SCOPE_UNIVERSE;
2560 }
2561
8973df5c
YW
2562 if (route->family == AF_INET6 && route->priority == 0)
2563 route->priority = IP6_RT_PRIO_USER;
2564
9cd9fc8f 2565 if (ordered_hashmap_isempty(network->addresses_by_section) &&
94876904 2566 in_addr_is_set(route->gw_family, &route->gw) &&
fcbf4cb7
YW
2567 route->gateway_onlink < 0) {
2568 log_warning("%s: Gateway= without static address configured. "
2569 "Enabling GatewayOnLink= option.",
2570 network->filename);
2571 route->gateway_onlink = true;
2572 }
2573
40785f53
YW
2574 if (route->family == AF_INET6) {
2575 MultipathRoute *m;
2576
2577 ORDERED_SET_FOREACH(m, route->multipath_routes)
2578 if (m->gateway.family == AF_INET)
2579 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
2580 "%s: IPv4 multipath route is specified for IPv6 route. "
2581 "Ignoring [Route] section from line %u.",
2582 route->section->filename, route->section->line);
2583 }
2584
fcbf4cb7
YW
2585 return 0;
2586}
d9940a3f 2587
13ffa39f 2588void network_drop_invalid_routes(Network *network) {
2a54a044 2589 Route *route;
d9940a3f
YW
2590
2591 assert(network);
2592
2a54a044 2593 HASHMAP_FOREACH(route, network->routes_by_section)
d9940a3f
YW
2594 if (route_section_verify(route, network) < 0)
2595 route_free(route);
2596}