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