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