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