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