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