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