]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
Merge pull request #16321 from bluca/mount_images
[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"
6bedfcbb 12#include "networkd-route.h"
6bedfcbb 13#include "parse-util.h"
1c8e710c 14#include "set.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
25static unsigned routes_max(void) {
26 static thread_local unsigned cached = 0;
27
28 _cleanup_free_ char *s4 = NULL, *s6 = NULL;
29 unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
30
31 if (cached > 0)
32 return cached;
33
34 if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
35 truncate_nl(s4);
36 if (safe_atou(s4, &val4) >= 0 &&
37 val4 == 2147483647U)
38 /* This is the default "no limit" value in the kernel */
39 val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
40 }
41
42 if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
43 truncate_nl(s6);
44 (void) safe_atou(s6, &val6);
45 }
46
47 cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
48 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
49 return cached;
50}
8c34b963 51
ed9e361a 52int route_new(Route **ret) {
8e766630 53 _cleanup_(route_freep) Route *route = NULL;
f0213e37 54
17f9c355 55 route = new(Route, 1);
f0213e37
TG
56 if (!route)
57 return -ENOMEM;
58
17f9c355
YW
59 *route = (Route) {
60 .family = AF_UNSPEC,
61 .scope = RT_SCOPE_UNIVERSE,
62 .protocol = RTPROT_UNSPEC,
63 .type = RTN_UNICAST,
64 .table = RT_TABLE_MAIN,
65 .lifetime = USEC_INFINITY,
66 .quickack = -1,
633c7258 67 .fast_open_no_cookie = -1,
54901fd2 68 .gateway_onlink = -1,
9b88f20a 69 .ttl_propagate = -1,
17f9c355 70 };
f0213e37 71
1cc6c93a 72 *ret = TAKE_PTR(route);
f0213e37
TG
73
74 return 0;
75}
76
9560e5b3 77static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
8e766630
LP
78 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
79 _cleanup_(route_freep) Route *route = NULL;
f0213e37 80 int r;
f579559b 81
8c34b963
LP
82 assert(network);
83 assert(ret);
48317c39 84 assert(!!filename == (section_line > 0));
8c34b963 85
48317c39 86 if (filename) {
f4859fc7
SS
87 r = network_config_section_new(filename, section_line, &n);
88 if (r < 0)
89 return r;
90
91 route = hashmap_get(network->routes_by_section, n);
6ae115c1 92 if (route) {
1cc6c93a 93 *ret = TAKE_PTR(route);
6ae115c1
TG
94
95 return 0;
96 }
97 }
98
47d2d30d 99 if (network->n_static_routes >= routes_max())
8c34b963
LP
100 return -E2BIG;
101
ed9e361a 102 r = route_new(&route);
f0213e37
TG
103 if (r < 0)
104 return r;
801bd9e8 105
ed9e361a 106 route->protocol = RTPROT_STATIC;
0f7f2769
YW
107 route->network = network;
108 LIST_PREPEND(routes, network->static_routes, route);
109 network->n_static_routes++;
f579559b 110
48317c39 111 if (filename) {
1cc6c93a 112 route->section = TAKE_PTR(n);
cacc1dbf 113
3e570042
YW
114 r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
115 if (r < 0)
116 return r;
117
fcc48287 118 r = hashmap_put(network->routes_by_section, route->section, route);
21b39268
LP
119 if (r < 0)
120 return r;
6ae115c1
TG
121 }
122
1cc6c93a 123 *ret = TAKE_PTR(route);
f579559b
TG
124
125 return 0;
126}
127
128void route_free(Route *route) {
129 if (!route)
130 return;
131
f048a16b 132 if (route->network) {
3d3d4255 133 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 134
8c34b963
LP
135 assert(route->network->n_static_routes > 0);
136 route->network->n_static_routes--;
137
fd45e522 138 if (route->section)
f4859fc7 139 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 140 }
6ae115c1 141
fd45e522
ZJS
142 network_config_section_free(route->section);
143
1c8e710c
TG
144 if (route->link) {
145 set_remove(route->link->routes, route);
146 set_remove(route->link->routes_foreign, route);
6e537f62
YW
147 set_remove(route->link->dhcp_routes, route);
148 set_remove(route->link->dhcp_routes_old, route);
1633c457
YW
149 set_remove(route->link->dhcp6_routes, route);
150 set_remove(route->link->dhcp6_routes_old, route);
151 set_remove(route->link->dhcp6_pd_routes, route);
152 set_remove(route->link->dhcp6_pd_routes_old, route);
69203fba
YW
153 set_remove(route->link->ndisc_routes, route);
154 set_remove(route->link->ndisc_routes_old, route);
1c8e710c
TG
155 }
156
6ff5cc6b
YW
157 ordered_set_free_free(route->multipath_routes);
158
f833694d
TG
159 sd_event_source_unref(route->expire);
160
f579559b
TG
161 free(route);
162}
163
7a08d314 164static void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
165 assert(route);
166
167 siphash24_compress(&route->family, sizeof(route->family), state);
168
01aaa3df
YW
169 switch (route->family) {
170 case AF_INET:
171 case AF_INET6:
01aaa3df 172 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
67e05dd8
ZJS
173 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
174
01aaa3df 175 siphash24_compress(&route->src_prefixlen, sizeof(route->src_prefixlen), state);
67e05dd8
ZJS
176 siphash24_compress(&route->src, FAMILY_ADDRESS_SIZE(route->family), state);
177
178 siphash24_compress(&route->gw, FAMILY_ADDRESS_SIZE(route->family), state);
179
01aaa3df
YW
180 siphash24_compress(&route->prefsrc, FAMILY_ADDRESS_SIZE(route->family), state);
181
182 siphash24_compress(&route->tos, sizeof(route->tos), state);
183 siphash24_compress(&route->priority, sizeof(route->priority), state);
184 siphash24_compress(&route->table, sizeof(route->table), state);
185 siphash24_compress(&route->protocol, sizeof(route->protocol), state);
186 siphash24_compress(&route->scope, sizeof(route->scope), state);
187 siphash24_compress(&route->type, sizeof(route->type), state);
67e05dd8 188
fa3e401a
YW
189 siphash24_compress(&route->initcwnd, sizeof(route->initcwnd), state);
190 siphash24_compress(&route->initrwnd, sizeof(route->initrwnd), state);
01aaa3df
YW
191
192 break;
193 default:
194 /* treat any other address family as AF_UNSPEC */
195 break;
196 }
197}
198
c077a205 199static int route_compare_func(const Route *a, const Route *b) {
01aaa3df
YW
200 int r;
201
202 r = CMP(a->family, b->family);
203 if (r != 0)
204 return r;
205
206 switch (a->family) {
207 case AF_INET:
208 case AF_INET6:
209 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
210 if (r != 0)
211 return r;
212
67e05dd8
ZJS
213 r = memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
214 if (r != 0)
215 return r;
216
01aaa3df
YW
217 r = CMP(a->src_prefixlen, b->src_prefixlen);
218 if (r != 0)
219 return r;
220
67e05dd8 221 r = memcmp(&a->src, &b->src, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
222 if (r != 0)
223 return r;
224
67e05dd8 225 r = memcmp(&a->gw, &b->gw, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
226 if (r != 0)
227 return r;
228
67e05dd8 229 r = memcmp(&a->prefsrc, &b->prefsrc, FAMILY_ADDRESS_SIZE(a->family));
01aaa3df
YW
230 if (r != 0)
231 return r;
232
67e05dd8 233 r = CMP(a->tos, b->tos);
01aaa3df
YW
234 if (r != 0)
235 return r;
236
67e05dd8 237 r = CMP(a->priority, b->priority);
01aaa3df
YW
238 if (r != 0)
239 return r;
240
67e05dd8 241 r = CMP(a->table, b->table);
01aaa3df
YW
242 if (r != 0)
243 return r;
244
67e05dd8 245 r = CMP(a->protocol, b->protocol);
fa3e401a
YW
246 if (r != 0)
247 return r;
248
67e05dd8 249 r = CMP(a->scope, b->scope);
fa3e401a
YW
250 if (r != 0)
251 return r;
252
67e05dd8 253 r = CMP(a->type, b->type);
01aaa3df
YW
254 if (r != 0)
255 return r;
256
67e05dd8 257 r = CMP(a->initcwnd, b->initcwnd);
01aaa3df
YW
258 if (r != 0)
259 return r;
260
67e05dd8 261 r = CMP(a->initrwnd, b->initrwnd);
01aaa3df
YW
262 if (r != 0)
263 return r;
264
67e05dd8 265 return 0;
01aaa3df
YW
266 default:
267 /* treat any other address family as AF_UNSPEC */
268 return 0;
269 }
270}
271
272DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
c077a205 273 route_hash_ops,
01aaa3df 274 Route,
c077a205
YW
275 route_hash_func,
276 route_compare_func,
01aaa3df
YW
277 route_free);
278
7ecf0c3e
TJ
279bool route_equal(Route *r1, Route *r2) {
280 if (r1 == r2)
281 return true;
282
283 if (!r1 || !r2)
284 return false;
285
286 return route_compare_func(r1, r2) == 0;
287}
288
f1368755
YW
289int route_get(Link *link, Route *in, Route **ret) {
290
291 Route *existing;
1b566071
LP
292
293 assert(link);
f1368755 294 assert(in);
1c8e710c 295
f1368755 296 existing = set_get(link->routes, in);
1c8e710c 297 if (existing) {
1b566071
LP
298 if (ret)
299 *ret = existing;
1c8e710c 300 return 1;
1c8e710c
TG
301 }
302
f1368755 303 existing = set_get(link->routes_foreign, in);
1b566071
LP
304 if (existing) {
305 if (ret)
306 *ret = existing;
307 return 0;
308 }
1c8e710c 309
1b566071 310 return -ENOENT;
1c8e710c
TG
311}
312
f1368755 313static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret) {
889b550f 314
8e766630 315 _cleanup_(route_freep) Route *route = NULL;
1c8e710c
TG
316 int r;
317
318 assert(link);
319 assert(routes);
f1368755 320 assert(in);
1c8e710c
TG
321
322 r = route_new(&route);
323 if (r < 0)
324 return r;
325
f1368755
YW
326 route->family = in->family;
327 route->src = in->src;
328 route->src_prefixlen = in->src_prefixlen;
329 route->dst = in->dst;
330 route->dst_prefixlen = in->dst_prefixlen;
331 route->gw = in->gw;
332 route->prefsrc = in->prefsrc;
333 route->scope = in->scope;
334 route->protocol = in->protocol;
335 route->type = in->type;
336 route->tos = in->tos;
337 route->priority = in->priority;
338 route->table = in->table;
339 route->initcwnd = in->initcwnd;
340 route->initrwnd = in->initrwnd;
341 route->lifetime = in->lifetime;
1c8e710c 342
de7fef4b 343 r = set_ensure_put(routes, &route_hash_ops, route);
1c8e710c
TG
344 if (r < 0)
345 return r;
75a302b5
YW
346 if (r == 0)
347 return -EEXIST;
1c8e710c
TG
348
349 route->link = link;
350
351 if (ret)
352 *ret = route;
353
354 route = NULL;
355
356 return 0;
357}
358
f1368755
YW
359int route_add_foreign(Link *link, Route *in, Route **ret) {
360 return route_add_internal(link, &link->routes_foreign, in, ret);
1c8e710c
TG
361}
362
f1368755 363int route_add(Link *link, Route *in, Route **ret) {
889b550f 364
1c8e710c
TG
365 Route *route;
366 int r;
367
f1368755 368 r = route_get(link, in, &route);
1c8e710c
TG
369 if (r == -ENOENT) {
370 /* Route does not exist, create a new one */
f1368755 371 r = route_add_internal(link, &link->routes, in, &route);
1c8e710c
TG
372 if (r < 0)
373 return r;
374 } else if (r == 0) {
375 /* Take over a foreign route */
de7fef4b 376 r = set_ensure_put(&link->routes, &route_hash_ops, route);
1c8e710c
TG
377 if (r < 0)
378 return r;
379
380 set_remove(link->routes_foreign, route);
381 } else if (r == 1) {
382 /* Route exists, do nothing */
383 ;
384 } else
385 return r;
386
856e309d
MC
387 if (ret)
388 *ret = route;
1c8e710c
TG
389
390 return 0;
391}
392
302a796f 393static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
394 int r;
395
396 assert(m);
397 assert(link);
398 assert(link->ifname);
399
400 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
401 return 1;
402
403 r = sd_netlink_message_get_errno(m);
404 if (r < 0 && r != -ESRCH)
5ecb131d 405 log_link_message_warning_errno(link, m, r, "Could not drop route, ignoring");
4645ad47
YW
406
407 return 1;
408}
409
91b5f997 410int route_remove(Route *route, Link *link,
302a796f 411 link_netlink_message_handler_t callback) {
27efb52b 412
4afd3348 413 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
414 int r;
415
416 assert(link);
417 assert(link->manager);
418 assert(link->manager->rtnl);
419 assert(link->ifindex > 0);
4c701096 420 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9
UTL
421
422 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
423 RTM_DELROUTE, route->family,
424 route->protocol);
f647962d 425 if (r < 0)
7750b796 426 return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 427
b297e0a7
YW
428 if (DEBUG_LOGGING) {
429 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
d3e291fd 430 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
b297e0a7
YW
431
432 if (!in_addr_is_null(route->family, &route->dst)) {
433 (void) in_addr_to_string(route->family, &route->dst, &dst);
434 (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
435 }
436 if (!in_addr_is_null(route->family, &route->src))
437 (void) in_addr_to_string(route->family, &route->src, &src);
438 if (!in_addr_is_null(route->family, &route->gw))
439 (void) in_addr_to_string(route->family, &route->gw, &gw);
440 if (!in_addr_is_null(route->family, &route->prefsrc))
441 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
442
d3e291fd 443 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
444 strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
445 format_route_scope(route->scope, scope, sizeof(scope)),
446 format_route_table(route->table, table, sizeof(table)),
d3e291fd 447 format_route_protocol(route->protocol, protocol, sizeof(protocol)),
b297e0a7
YW
448 strna(route_type_to_string(route->type)));
449 }
450
d40b01e4 451 if (in_addr_is_null(route->family, &route->gw) == 0) {
43409486 452 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
f647962d 453 if (r < 0)
7750b796 454 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
455 }
456
457 if (route->dst_prefixlen) {
43409486 458 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
f647962d 459 if (r < 0)
7750b796 460 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
461
462 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d 463 if (r < 0)
7750b796 464 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
465 }
466
9e7e4408 467 if (route->src_prefixlen) {
43409486 468 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
9e7e4408 469 if (r < 0)
7750b796 470 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
471
472 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
473 if (r < 0)
7750b796 474 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
9e7e4408
TG
475 }
476
d40b01e4 477 if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
43409486 478 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
f647962d 479 if (r < 0)
7750b796 480 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
481 }
482
5c1d3fc9 483 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d 484 if (r < 0)
7750b796 485 return log_link_error_errno(link, r, "Could not set scope: %m");
5c1d3fc9 486
86655331 487 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d 488 if (r < 0)
7750b796 489 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 490
2d53f310 491 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
492 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
493 if (r < 0)
7750b796 494 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
983226f3 495 }
5c1d3fc9 496
302a796f
YW
497 r = netlink_call_async(link->manager->rtnl, NULL, req,
498 callback ?: route_remove_handler,
499 link_netlink_destroy_callback, link);
f647962d 500 if (r < 0)
7750b796 501 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
5c1d3fc9 502
563c69c6
TG
503 link_ref(link);
504
5c1d3fc9
UTL
505 return 0;
506}
507
f833694d
TG
508int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
509 Route *route = userdata;
510 int r;
511
512 assert(route);
513
4645ad47 514 r = route_remove(route, route->link, NULL);
f833694d 515 if (r < 0)
98b02994 516 log_link_warning_errno(route->link, r, "Could not remove route: %m");
3bdccf69 517 else
fe7ca21a 518 route_free(route);
f833694d
TG
519
520 return 1;
521}
522
6ff5cc6b
YW
523static int append_nexthop_one(Route *route, MultipathRoute *m, struct rtattr **rta, size_t offset) {
524 struct rtnexthop *rtnh;
525 struct rtattr *new_rta;
526 int r;
527
528 assert(route);
529 assert(m);
530 assert(rta);
531 assert(*rta);
532
533 new_rta = realloc(*rta, RTA_ALIGN((*rta)->rta_len) + RTA_SPACE(sizeof(struct rtnexthop)));
534 if (!new_rta)
535 return -ENOMEM;
536 *rta = new_rta;
537
538 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
539 *rtnh = (struct rtnexthop) {
540 .rtnh_len = sizeof(*rtnh),
541 .rtnh_ifindex = m->ifindex,
542 .rtnh_hops = m->weight > 0 ? m->weight - 1 : 0,
543 };
544
545 (*rta)->rta_len += sizeof(struct rtnexthop);
546
547 if (route->family == m->gateway.family) {
548 r = rtattr_append_attribute(rta, RTA_GATEWAY, &m->gateway.address, FAMILY_ADDRESS_SIZE(m->gateway.family));
549 if (r < 0)
550 goto clear;
551 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
552 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family));
553 } else {
554 r = rtattr_append_attribute(rta, RTA_VIA, &m->gateway, FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
555 if (r < 0)
556 goto clear;
557 rtnh = (struct rtnexthop *)((uint8_t *) *rta + offset);
558 rtnh->rtnh_len += RTA_SPACE(FAMILY_ADDRESS_SIZE(m->gateway.family) + sizeof(m->gateway.family));
559 }
560
561 return 0;
562
563clear:
564 (*rta)->rta_len -= sizeof(struct rtnexthop);
565 return r;
566}
567
568static int append_nexthops(Route *route, sd_netlink_message *req) {
569 _cleanup_free_ struct rtattr *rta = NULL;
570 struct rtnexthop *rtnh;
571 MultipathRoute *m;
572 size_t offset;
573 Iterator i;
574 int r;
575
576 if (ordered_set_isempty(route->multipath_routes))
577 return 0;
578
579 rta = new(struct rtattr, 1);
580 if (!rta)
581 return -ENOMEM;
582
583 *rta = (struct rtattr) {
584 .rta_type = RTA_MULTIPATH,
585 .rta_len = RTA_LENGTH(0),
586 };
587 offset = (uint8_t *) RTA_DATA(rta) - (uint8_t *) rta;
588
589 ORDERED_SET_FOREACH(m, route->multipath_routes, i) {
590 r = append_nexthop_one(route, m, &rta, offset);
591 if (r < 0)
592 return r;
593
594 rtnh = (struct rtnexthop *)((uint8_t *) rta + offset);
595 offset = (uint8_t *) RTNH_NEXT(rtnh) - (uint8_t *) rta;
596 }
597
598 r = sd_netlink_message_append_data(req, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
599 if (r < 0)
600 return r;
601
602 return 0;
603}
604
1b566071
LP
605int route_configure(
606 Route *route,
607 Link *link,
80b0e860
YW
608 link_netlink_message_handler_t callback,
609 Route **ret) {
1b566071 610
4afd3348
LP
611 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
612 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
f579559b
TG
613 int r;
614
f579559b 615 assert(link);
f882c247
TG
616 assert(link->manager);
617 assert(link->manager->rtnl);
f579559b 618 assert(link->ifindex > 0);
4c701096 619 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 620 assert(callback);
f579559b 621
f1368755 622 if (route_get(link, route, NULL) <= 0 &&
47d2d30d 623 set_size(link->routes) >= routes_max())
7750b796
YW
624 return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
625 "Too many routes are configured, refusing: %m");
1b566071 626
156ed65e
YW
627 if (DEBUG_LOGGING) {
628 _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL;
d3e291fd 629 char scope[ROUTE_SCOPE_STR_MAX], table[ROUTE_TABLE_STR_MAX], protocol[ROUTE_PROTOCOL_STR_MAX];
156ed65e
YW
630
631 if (!in_addr_is_null(route->family, &route->dst)) {
632 (void) in_addr_to_string(route->family, &route->dst, &dst);
633 (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen);
634 }
635 if (!in_addr_is_null(route->family, &route->src))
636 (void) in_addr_to_string(route->family, &route->src, &src);
637 if (!in_addr_is_null(route->family, &route->gw))
638 (void) in_addr_to_string(route->family, &route->gw, &gw);
639 if (!in_addr_is_null(route->family, &route->prefsrc))
640 (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc);
641
d3e291fd 642 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
643 strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc),
644 format_route_scope(route->scope, scope, sizeof(scope)),
645 format_route_table(route->table, table, sizeof(table)),
d3e291fd 646 format_route_protocol(route->protocol, protocol, sizeof(protocol)),
b297e0a7 647 strna(route_type_to_string(route->type)));
156ed65e
YW
648 }
649
151b9b96 650 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
651 RTM_NEWROUTE, route->family,
652 route->protocol);
f647962d 653 if (r < 0)
7750b796 654 return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m");
f579559b 655
d40b01e4 656 if (in_addr_is_null(route->family, &route->gw) == 0) {
43409486 657 r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw);
f647962d 658 if (r < 0)
7750b796 659 return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m");
c953b24c
SS
660
661 r = sd_rtnl_message_route_set_family(req, route->family);
662 if (r < 0)
7750b796 663 return log_link_error_errno(link, r, "Could not set route family: %m");
f579559b
TG
664 }
665
70dc2362 666 if (route->dst_prefixlen > 0) {
43409486 667 r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst);
f647962d 668 if (r < 0)
7750b796 669 return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m");
6ae115c1 670
ae4c67a7 671 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d 672 if (r < 0)
7750b796 673 return log_link_error_errno(link, r, "Could not set destination prefix length: %m");
1f01fb4f
TG
674 }
675
70dc2362 676 if (route->src_prefixlen > 0) {
43409486 677 r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src);
9e7e4408 678 if (r < 0)
7750b796 679 return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
680
681 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
682 if (r < 0)
7750b796 683 return log_link_error_errno(link, r, "Could not set source prefix length: %m");
9e7e4408
TG
684 }
685
d40b01e4 686 if (in_addr_is_null(route->family, &route->prefsrc) == 0) {
43409486 687 r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc);
f647962d 688 if (r < 0)
7750b796 689 return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
690 }
691
5c1d3fc9 692 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d 693 if (r < 0)
7750b796 694 return log_link_error_errno(link, r, "Could not set scope: %m");
5c1d3fc9 695
54901fd2
YW
696 if (route->gateway_onlink >= 0)
697 SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink);
698
3b015d40
TG
699 r = sd_rtnl_message_route_set_flags(req, route->flags);
700 if (r < 0)
7750b796 701 return log_link_error_errno(link, r, "Could not set flags: %m");
c953b24c 702
a0d95bbc 703 if (route->table != RT_TABLE_MAIN) {
c953b24c
SS
704 if (route->table < 256) {
705 r = sd_rtnl_message_route_set_table(req, route->table);
706 if (r < 0)
7750b796 707 return log_link_error_errno(link, r, "Could not set route table: %m");
c953b24c 708 } else {
c953b24c
SS
709 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
710 if (r < 0)
7750b796 711 return log_link_error_errno(link, r, "Could not set route table: %m");
c953b24c 712
06976f5b 713 /* Table attribute to allow more than 256. */
c953b24c
SS
714 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
715 if (r < 0)
7750b796 716 return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m");
c953b24c
SS
717 }
718 }
3b015d40 719
86655331 720 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d 721 if (r < 0)
7750b796 722 return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 723
3b015d40
TG
724 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
725 if (r < 0)
7750b796 726 return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m");
3b015d40 727
f02ba163
DD
728 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
729 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
730 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
731 if (r < 0)
7750b796 732 return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m");
f02ba163
DD
733 }
734
983226f3 735 r = sd_rtnl_message_route_set_type(req, route->type);
f647962d 736 if (r < 0)
7750b796 737 return log_link_error_errno(link, r, "Could not set route type: %m");
983226f3 738
2d53f310 739 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
740 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
741 if (r < 0)
7750b796 742 return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m");
983226f3 743 }
f579559b 744
9b88f20a
SS
745 if (route->ttl_propagate >= 0) {
746 r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate);
747 if (r < 0)
748 return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m");
749 }
750
d6fceaf1
SS
751 r = sd_netlink_message_open_container(req, RTA_METRICS);
752 if (r < 0)
7750b796 753 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1
SS
754
755 if (route->mtu > 0) {
756 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
757 if (r < 0)
7750b796 758 return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m");
d6fceaf1
SS
759 }
760
6b21ad33 761 if (route->initcwnd > 0) {
323d9329
SS
762 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
763 if (r < 0)
7750b796 764 return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m");
323d9329
SS
765 }
766
6b21ad33 767 if (route->initrwnd > 0) {
323d9329
SS
768 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
769 if (r < 0)
7750b796 770 return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m");
323d9329
SS
771 }
772
67c193bf 773 if (route->quickack >= 0) {
09f5dfad
SS
774 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
775 if (r < 0)
7750b796 776 return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m");
09f5dfad
SS
777 }
778
633c7258
SS
779 if (route->fast_open_no_cookie >= 0) {
780 r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie);
781 if (r < 0)
782 return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m");
783 }
784
d6fceaf1
SS
785 r = sd_netlink_message_close_container(req);
786 if (r < 0)
7750b796 787 return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m");
d6fceaf1 788
6ff5cc6b
YW
789 r = append_nexthops(route, req);
790 if (r < 0)
791 return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m");
792
302a796f
YW
793 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
794 link_netlink_destroy_callback, link);
f647962d 795 if (r < 0)
7750b796 796 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
f579559b 797
563c69c6
TG
798 link_ref(link);
799
f1368755 800 r = route_add(link, route, &route);
1c8e710c 801 if (r < 0)
7750b796 802 return log_link_error_errno(link, r, "Could not add route: %m");
1c8e710c 803
f833694d 804 /* TODO: drop expiration handling once it can be pushed into the kernel */
f02ba163 805 if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
f833694d
TG
806 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
807 route->lifetime, 0, route_expire_handler, route);
808 if (r < 0)
7750b796 809 return log_link_error_errno(link, r, "Could not arm expiration timer: %m");
f833694d
TG
810 }
811
812 sd_event_source_unref(route->expire);
1cc6c93a 813 route->expire = TAKE_PTR(expire);
f833694d 814
80b0e860
YW
815 if (ret)
816 *ret = route;
817
c4423317 818 return 1;
f579559b
TG
819}
820
fa7cd711 821int network_add_ipv4ll_route(Network *network) {
fcbf4cb7 822 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
fa7cd711
YW
823 int r;
824
825 assert(network);
826
827 if (!network->ipv4ll_route)
828 return 0;
829
830 /* IPv4LLRoute= is in [Network] section. */
831 r = route_new_static(network, NULL, 0, &n);
832 if (r < 0)
833 return r;
834
835 r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst);
836 if (r < 0)
837 return r;
838
839 n->family = AF_INET;
840 n->dst_prefixlen = 16;
841 n->scope = RT_SCOPE_LINK;
94d6e299
YW
842 n->scope_set = true;
843 n->table_set = true;
fa7cd711
YW
844 n->priority = IPV4LL_ROUTE_METRIC;
845 n->protocol = RTPROT_STATIC;
846
847 TAKE_PTR(n);
848 return 0;
849}
850
5d5003ab
YW
851int network_add_default_route_on_device(Network *network) {
852 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
853 int r;
854
855 assert(network);
856
857 if (!network->default_route_on_device)
858 return 0;
859
860 /* DefaultRouteOnDevice= is in [Network] section. */
861 r = route_new_static(network, NULL, 0, &n);
862 if (r < 0)
863 return r;
864
5d5003ab 865 n->family = AF_INET;
c697db75
YW
866 n->scope = RT_SCOPE_LINK;
867 n->scope_set = true;
868 n->protocol = RTPROT_STATIC;
5d5003ab
YW
869
870 TAKE_PTR(n);
871 return 0;
872}
873
7a22312d
YW
874static const char * const route_type_table[__RTN_MAX] = {
875 [RTN_UNICAST] = "unicast",
94d6e299
YW
876 [RTN_LOCAL] = "local",
877 [RTN_BROADCAST] = "broadcast",
878 [RTN_ANYCAST] = "anycast",
879 [RTN_MULTICAST] = "multicast",
7a22312d
YW
880 [RTN_BLACKHOLE] = "blackhole",
881 [RTN_UNREACHABLE] = "unreachable",
882 [RTN_PROHIBIT] = "prohibit",
883 [RTN_THROW] = "throw",
94d6e299
YW
884 [RTN_NAT] = "nat",
885 [RTN_XRESOLVE] = "xresolve",
7a22312d
YW
886};
887
888assert_cc(__RTN_MAX <= UCHAR_MAX);
889DEFINE_STRING_TABLE_LOOKUP(route_type, int);
890
b297e0a7
YW
891static const char * const route_scope_table[] = {
892 [RT_SCOPE_UNIVERSE] = "global",
893 [RT_SCOPE_SITE] = "site",
894 [RT_SCOPE_LINK] = "link",
895 [RT_SCOPE_HOST] = "host",
896 [RT_SCOPE_NOWHERE] = "nowhere",
897};
898
41b90a1e 899DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_scope, int);
b297e0a7
YW
900
901const char *format_route_scope(int scope, char *buf, size_t size) {
902 const char *s;
903 char *p = buf;
904
905 s = route_scope_to_string(scope);
906 if (s)
907 strpcpy(&p, size, s);
908 else
909 strpcpyf(&p, size, "%d", scope);
910
911 return buf;
912}
913
914static const char * const route_table_table[] = {
915 [RT_TABLE_DEFAULT] = "default",
916 [RT_TABLE_MAIN] = "main",
917 [RT_TABLE_LOCAL] = "local",
918};
919
41b90a1e 920DEFINE_PRIVATE_STRING_TABLE_LOOKUP(route_table, int);
b297e0a7
YW
921
922const char *format_route_table(int table, char *buf, size_t size) {
923 const char *s;
924 char *p = buf;
925
926 s = route_table_to_string(table);
927 if (s)
928 strpcpy(&p, size, s);
929 else
930 strpcpyf(&p, size, "%d", table);
931
932 return buf;
933}
934
1b64651b
YW
935static const char * const route_protocol_table[] = {
936 [RTPROT_KERNEL] = "kernel",
937 [RTPROT_BOOT] = "boot",
938 [RTPROT_STATIC] = "static",
939};
940
ca420b62
YW
941DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(route_protocol, int);
942
943static const char * const route_protocol_full_table[] = {
944 [RTPROT_REDIRECT] = "redirect",
945 [RTPROT_KERNEL] = "kernel",
946 [RTPROT_BOOT] = "boot",
947 [RTPROT_STATIC] = "static",
948 [RTPROT_GATED] = "gated",
949 [RTPROT_RA] = "ra",
950 [RTPROT_MRT] = "mrt",
951 [RTPROT_ZEBRA] = "zebra",
952 [RTPROT_BIRD] = "bird",
953 [RTPROT_DNROUTED] = "dnrouted",
954 [RTPROT_XORP] = "xorp",
955 [RTPROT_NTK] = "ntk",
956 [RTPROT_DHCP] = "dhcp",
957 [RTPROT_MROUTED] = "mrouted",
958 [RTPROT_BABEL] = "babel",
959 [RTPROT_BGP] = "bgp",
960 [RTPROT_ISIS] = "isis",
961 [RTPROT_OSPF] = "ospf",
962 [RTPROT_RIP] = "rip",
963 [RTPROT_EIGRP] = "eigrp",
964};
965
966DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(route_protocol_full, int);
d3e291fd
YW
967
968const char *format_route_protocol(int protocol, char *buf, size_t size) {
969 const char *s;
970 char *p = buf;
971
ca420b62 972 s = route_protocol_full_to_string(protocol);
d3e291fd
YW
973 if (s)
974 strpcpy(&p, size, s);
975 else
976 strpcpyf(&p, size, "%d", protocol);
977
978 return buf;
979}
1b64651b 980
27efb52b
YW
981int config_parse_gateway(
982 const char *unit,
f579559b
TG
983 const char *filename,
984 unsigned line,
985 const char *section,
71a61510 986 unsigned section_line,
f579559b
TG
987 const char *lvalue,
988 int ltype,
989 const char *rvalue,
990 void *data,
991 void *userdata) {
44e7b949 992
6ae115c1 993 Network *network = userdata;
fcbf4cb7 994 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 995 int r;
f579559b
TG
996
997 assert(filename);
6ae115c1 998 assert(section);
f579559b
TG
999 assert(lvalue);
1000 assert(rvalue);
1001 assert(data);
1002
92fe133a
TG
1003 if (streq(section, "Network")) {
1004 /* we are not in an Route section, so treat
1005 * this as the special '0' section */
f4859fc7 1006 r = route_new_static(network, NULL, 0, &n);
d96edb2c
YW
1007 if (r == -ENOMEM)
1008 return log_oom();
1009 if (r < 0) {
1010 log_syntax(unit, LOG_WARNING, filename, line, r,
1011 "Failed to allocate route, ignoring assignment: %m");
1012 return 0;
1013 }
1985c54f 1014 } else {
f4859fc7 1015 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1016 if (r == -ENOMEM)
1017 return log_oom();
1018 if (r < 0) {
1019 log_syntax(unit, LOG_WARNING, filename, line, r,
1020 "Failed to allocate route, ignoring assignment: %m");
1021 return 0;
1022 }
1985c54f 1023
427928ca 1024 if (streq(rvalue, "_dhcp")) {
1985c54f
YW
1025 n->gateway_from_dhcp = true;
1026 TAKE_PTR(n);
1027 return 0;
1028 }
1029 }
f579559b 1030
01d4e732
YW
1031 if (n->family == AF_UNSPEC)
1032 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
1033 else
1034 r = in_addr_from_string(n->family, rvalue, &n->gw);
f579559b 1035 if (r < 0) {
d96edb2c 1036 log_syntax(unit, LOG_WARNING, filename, line, r,
01d4e732 1037 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
f579559b
TG
1038 return 0;
1039 }
1040
aff44301 1041 TAKE_PTR(n);
f579559b
TG
1042 return 0;
1043}
6ae115c1 1044
27efb52b
YW
1045int config_parse_preferred_src(
1046 const char *unit,
0d07e595
JK
1047 const char *filename,
1048 unsigned line,
1049 const char *section,
1050 unsigned section_line,
1051 const char *lvalue,
1052 int ltype,
1053 const char *rvalue,
1054 void *data,
1055 void *userdata) {
1056
1057 Network *network = userdata;
fcbf4cb7 1058 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede 1059 int r;
0d07e595
JK
1060
1061 assert(filename);
1062 assert(section);
1063 assert(lvalue);
1064 assert(rvalue);
1065 assert(data);
1066
f4859fc7 1067 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1068 if (r == -ENOMEM)
1069 return log_oom();
1070 if (r < 0) {
1071 log_syntax(unit, LOG_WARNING, filename, line, r,
1072 "Failed to allocate route, ignoring assignment: %m");
1073 return 0;
1074 }
0d07e595 1075
01d4e732
YW
1076 if (n->family == AF_UNSPEC)
1077 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
1078 else
1079 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
0d07e595 1080 if (r < 0) {
d96edb2c 1081 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1082 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
0d07e595
JK
1083 return 0;
1084 }
1085
aff44301 1086 TAKE_PTR(n);
0d07e595
JK
1087 return 0;
1088}
1089
27efb52b
YW
1090int config_parse_destination(
1091 const char *unit,
6ae115c1
TG
1092 const char *filename,
1093 unsigned line,
1094 const char *section,
1095 unsigned section_line,
1096 const char *lvalue,
1097 int ltype,
1098 const char *rvalue,
1099 void *data,
1100 void *userdata) {
44e7b949 1101
6ae115c1 1102 Network *network = userdata;
fcbf4cb7 1103 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7934dede
YW
1104 union in_addr_union *buffer;
1105 unsigned char *prefixlen;
ca3bad65 1106 int r;
6ae115c1
TG
1107
1108 assert(filename);
1109 assert(section);
1110 assert(lvalue);
1111 assert(rvalue);
1112 assert(data);
1113
f4859fc7 1114 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1115 if (r == -ENOMEM)
1116 return log_oom();
1117 if (r < 0) {
1118 log_syntax(unit, LOG_WARNING, filename, line, r,
1119 "Failed to allocate route, ignoring assignment: %m");
1120 return 0;
1121 }
6ae115c1 1122
9e7e4408 1123 if (streq(lvalue, "Destination")) {
7934dede
YW
1124 buffer = &n->dst;
1125 prefixlen = &n->dst_prefixlen;
9e7e4408 1126 } else if (streq(lvalue, "Source")) {
7934dede
YW
1127 buffer = &n->src;
1128 prefixlen = &n->src_prefixlen;
9e7e4408
TG
1129 } else
1130 assert_not_reached(lvalue);
1131
01d4e732
YW
1132 if (n->family == AF_UNSPEC)
1133 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
1134 else
1135 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
7934dede 1136 if (r < 0) {
d96edb2c 1137 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
01d4e732 1138 "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
7934dede
YW
1139 return 0;
1140 }
1141
aff44301 1142 TAKE_PTR(n);
6ae115c1
TG
1143 return 0;
1144}
5d8e593d 1145
27efb52b
YW
1146int config_parse_route_priority(
1147 const char *unit,
1148 const char *filename,
1149 unsigned line,
1150 const char *section,
1151 unsigned section_line,
1152 const char *lvalue,
1153 int ltype,
1154 const char *rvalue,
1155 void *data,
1156 void *userdata) {
1157
5d8e593d 1158 Network *network = userdata;
fcbf4cb7 1159 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
5d8e593d
SS
1160 int r;
1161
1162 assert(filename);
1163 assert(section);
1164 assert(lvalue);
1165 assert(rvalue);
1166 assert(data);
1167
f4859fc7 1168 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1169 if (r == -ENOMEM)
1170 return log_oom();
1171 if (r < 0) {
1172 log_syntax(unit, LOG_WARNING, filename, line, r,
1173 "Failed to allocate route, ignoring assignment: %m");
1174 return 0;
1175 }
5d8e593d 1176
aff44301 1177 r = safe_atou32(rvalue, &n->priority);
1c4b1179 1178 if (r < 0) {
d96edb2c 1179 log_syntax(unit, LOG_WARNING, filename, line, r,
1c4b1179
SS
1180 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
1181 return 0;
1182 }
5d8e593d 1183
aff44301 1184 TAKE_PTR(n);
5d8e593d
SS
1185 return 0;
1186}
769b56a3 1187
27efb52b
YW
1188int config_parse_route_scope(
1189 const char *unit,
1190 const char *filename,
1191 unsigned line,
1192 const char *section,
1193 unsigned section_line,
1194 const char *lvalue,
1195 int ltype,
1196 const char *rvalue,
1197 void *data,
1198 void *userdata) {
1199
769b56a3 1200 Network *network = userdata;
fcbf4cb7 1201 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
769b56a3
TG
1202 int r;
1203
1204 assert(filename);
1205 assert(section);
1206 assert(lvalue);
1207 assert(rvalue);
1208 assert(data);
1209
f4859fc7 1210 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1211 if (r == -ENOMEM)
1212 return log_oom();
1213 if (r < 0) {
1214 log_syntax(unit, LOG_WARNING, filename, line, r,
1215 "Failed to allocate route, ignoring assignment: %m");
1216 return 0;
1217 }
769b56a3 1218
41b90a1e
YW
1219 r = route_scope_from_string(rvalue);
1220 if (r < 0) {
d96edb2c 1221 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
1222 return 0;
1223 }
1224
41b90a1e 1225 n->scope = r;
94d6e299 1226 n->scope_set = true;
aff44301 1227 TAKE_PTR(n);
769b56a3
TG
1228 return 0;
1229}
c953b24c 1230
27efb52b
YW
1231int config_parse_route_table(
1232 const char *unit,
1233 const char *filename,
1234 unsigned line,
1235 const char *section,
1236 unsigned section_line,
1237 const char *lvalue,
1238 int ltype,
1239 const char *rvalue,
1240 void *data,
1241 void *userdata) {
1242
fcbf4cb7 1243 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c953b24c 1244 Network *network = userdata;
c953b24c
SS
1245 int r;
1246
1247 assert(filename);
1248 assert(section);
1249 assert(lvalue);
1250 assert(rvalue);
1251 assert(data);
1252
f4859fc7 1253 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1254 if (r == -ENOMEM)
1255 return log_oom();
1256 if (r < 0) {
1257 log_syntax(unit, LOG_WARNING, filename, line, r,
1258 "Failed to allocate route, ignoring assignment: %m");
1259 return 0;
1260 }
c953b24c 1261
41b90a1e
YW
1262 r = route_table_from_string(rvalue);
1263 if (r >= 0)
1264 n->table = r;
1265 else {
1266 r = safe_atou32(rvalue, &n->table);
1267 if (r < 0) {
d96edb2c 1268 log_syntax(unit, LOG_WARNING, filename, line, r,
41b90a1e
YW
1269 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
1270 return 0;
1271 }
c953b24c
SS
1272 }
1273
94d6e299 1274 n->table_set = true;
aff44301 1275 TAKE_PTR(n);
c953b24c
SS
1276 return 0;
1277}
28959f7d 1278
d96edb2c 1279int config_parse_route_boolean(
27efb52b
YW
1280 const char *unit,
1281 const char *filename,
1282 unsigned line,
1283 const char *section,
1284 unsigned section_line,
1285 const char *lvalue,
1286 int ltype,
1287 const char *rvalue,
1288 void *data,
1289 void *userdata) {
1290
28959f7d 1291 Network *network = userdata;
fcbf4cb7 1292 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
28959f7d
SS
1293 int r;
1294
1295 assert(filename);
1296 assert(section);
1297 assert(lvalue);
1298 assert(rvalue);
1299 assert(data);
1300
1301 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1302 if (r == -ENOMEM)
1303 return log_oom();
1304 if (r < 0) {
1305 log_syntax(unit, LOG_WARNING, filename, line, r,
1306 "Failed to allocate route, ignoring assignment: %m");
1307 return 0;
1308 }
28959f7d
SS
1309
1310 r = parse_boolean(rvalue);
1311 if (r < 0) {
d96edb2c 1312 log_syntax(unit, LOG_WARNING, filename, line, r,
9cb8c559 1313 "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
28959f7d
SS
1314 return 0;
1315 }
1316
d96edb2c
YW
1317 if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
1318 n->gateway_onlink = r;
1319 else if (streq(lvalue, "QuickAck"))
1320 n->quickack = r;
1321 else if (streq(lvalue, "FastOpenNoCookie"))
1322 n->fast_open_no_cookie = r;
1323 else if (streq(lvalue, "TTLPropagate"))
1324 n->ttl_propagate = r;
1325 else
1326 assert_not_reached("Invalid lvalue");
54901fd2 1327
aff44301 1328 TAKE_PTR(n);
b5bf6f64
SS
1329 return 0;
1330}
1331
27efb52b
YW
1332int config_parse_ipv6_route_preference(
1333 const char *unit,
1334 const char *filename,
1335 unsigned line,
1336 const char *section,
1337 unsigned section_line,
1338 const char *lvalue,
1339 int ltype,
1340 const char *rvalue,
1341 void *data,
1342 void *userdata) {
1343
b5bf6f64 1344 Network *network = userdata;
fcbf4cb7 1345 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
b5bf6f64
SS
1346 int r;
1347
4c7bd9cf 1348 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1349 if (r == -ENOMEM)
1350 return log_oom();
1351 if (r < 0) {
1352 log_syntax(unit, LOG_WARNING, filename, line, r,
1353 "Failed to allocate route, ignoring assignment: %m");
1354 return 0;
1355 }
4c7bd9cf 1356
b5bf6f64
SS
1357 if (streq(rvalue, "low"))
1358 n->pref = ICMPV6_ROUTER_PREF_LOW;
1359 else if (streq(rvalue, "medium"))
1360 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
1361 else if (streq(rvalue, "high"))
1362 n->pref = ICMPV6_ROUTER_PREF_HIGH;
1363 else {
d96edb2c 1364 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
b5bf6f64
SS
1365 return 0;
1366 }
28959f7d 1367
aff44301 1368 TAKE_PTR(n);
28959f7d
SS
1369 return 0;
1370}
c83ecc04 1371
27efb52b
YW
1372int config_parse_route_protocol(
1373 const char *unit,
1374 const char *filename,
1375 unsigned line,
1376 const char *section,
1377 unsigned section_line,
1378 const char *lvalue,
1379 int ltype,
1380 const char *rvalue,
1381 void *data,
1382 void *userdata) {
1383
c83ecc04 1384 Network *network = userdata;
fcbf4cb7 1385 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
c83ecc04
SS
1386 int r;
1387
1388 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1389 if (r == -ENOMEM)
1390 return log_oom();
1391 if (r < 0) {
1392 log_syntax(unit, LOG_WARNING, filename, line, r,
1393 "Failed to allocate route, ignoring assignment: %m");
1394 return 0;
1395 }
c83ecc04 1396
1b64651b
YW
1397 r = route_protocol_from_string(rvalue);
1398 if (r >= 0)
1399 n->protocol = r;
c83ecc04
SS
1400 else {
1401 r = safe_atou8(rvalue , &n->protocol);
1402 if (r < 0) {
d96edb2c 1403 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a 1404 "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
c83ecc04
SS
1405 return 0;
1406 }
1407 }
1408
aff44301 1409 TAKE_PTR(n);
c83ecc04
SS
1410 return 0;
1411}
983226f3 1412
27efb52b
YW
1413int config_parse_route_type(
1414 const char *unit,
1415 const char *filename,
1416 unsigned line,
1417 const char *section,
1418 unsigned section_line,
1419 const char *lvalue,
1420 int ltype,
1421 const char *rvalue,
1422 void *data,
1423 void *userdata) {
1424
983226f3 1425 Network *network = userdata;
fcbf4cb7 1426 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
7a22312d 1427 int t, r;
983226f3
SS
1428
1429 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1430 if (r == -ENOMEM)
1431 return log_oom();
1432 if (r < 0) {
1433 log_syntax(unit, LOG_WARNING, filename, line, r,
1434 "Failed to allocate route, ignoring assignment: %m");
1435 return 0;
1436 }
983226f3 1437
7a22312d
YW
1438 t = route_type_from_string(rvalue);
1439 if (t < 0) {
d96edb2c 1440 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 1441 "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
983226f3
SS
1442 return 0;
1443 }
1444
7a22312d
YW
1445 n->type = (unsigned char) t;
1446
aff44301 1447 TAKE_PTR(n);
983226f3
SS
1448 return 0;
1449}
323d9329 1450
27efb52b
YW
1451int config_parse_tcp_window(
1452 const char *unit,
1453 const char *filename,
1454 unsigned line,
1455 const char *section,
1456 unsigned section_line,
1457 const char *lvalue,
1458 int ltype,
1459 const char *rvalue,
1460 void *data,
1461 void *userdata) {
1462
fcbf4cb7 1463 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
6b21ad33
SS
1464 Network *network = userdata;
1465 uint64_t k;
323d9329
SS
1466 int r;
1467
1468 assert(filename);
1469 assert(section);
1470 assert(lvalue);
1471 assert(rvalue);
1472 assert(data);
1473
1474 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1475 if (r == -ENOMEM)
1476 return log_oom();
1477 if (r < 0) {
1478 log_syntax(unit, LOG_WARNING, filename, line, r,
1479 "Failed to allocate route, ignoring assignment: %m");
1480 return 0;
1481 }
323d9329 1482
6b21ad33 1483 r = parse_size(rvalue, 1024, &k);
f205a92a 1484 if (r < 0) {
d96edb2c 1485 log_syntax(unit, LOG_WARNING, filename, line, r,
f205a92a
YW
1486 "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
1487 return 0;
1488 }
1489 if (k > UINT32_MAX) {
d96edb2c 1490 log_syntax(unit, LOG_WARNING, filename, line, 0,
f205a92a 1491 "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
323d9329
SS
1492 return 0;
1493 }
1494
1495 if (streq(lvalue, "InitialCongestionWindow"))
1496 n->initcwnd = k;
1497 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
1498 n->initrwnd = k;
f205a92a
YW
1499 else
1500 assert_not_reached("Invalid TCP window type.");
323d9329 1501
aff44301 1502 TAKE_PTR(n);
323d9329
SS
1503 return 0;
1504}
09f5dfad 1505
cea79e66
SS
1506int config_parse_route_mtu(
1507 const char *unit,
1508 const char *filename,
1509 unsigned line,
1510 const char *section,
1511 unsigned section_line,
1512 const char *lvalue,
1513 int ltype,
1514 const char *rvalue,
1515 void *data,
1516 void *userdata) {
1517
1518 Network *network = userdata;
fcbf4cb7 1519 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
cea79e66
SS
1520 int r;
1521
1522 assert(filename);
1523 assert(section);
1524 assert(lvalue);
1525 assert(rvalue);
1526 assert(data);
1527
1528 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1529 if (r == -ENOMEM)
1530 return log_oom();
1531 if (r < 0) {
1532 log_syntax(unit, LOG_WARNING, filename, line, r,
1533 "Failed to allocate route, ignoring assignment: %m");
1534 return 0;
1535 }
cea79e66
SS
1536
1537 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
1538 if (r < 0)
1539 return r;
1540
aff44301 1541 TAKE_PTR(n);
cea79e66
SS
1542 return 0;
1543}
fcbf4cb7 1544
6ff5cc6b
YW
1545int config_parse_multipath_route(
1546 const char *unit,
1547 const char *filename,
1548 unsigned line,
1549 const char *section,
1550 unsigned section_line,
1551 const char *lvalue,
1552 int ltype,
1553 const char *rvalue,
1554 void *data,
1555 void *userdata) {
1556
1557 _cleanup_(route_free_or_set_invalidp) Route *n = NULL;
1558 _cleanup_free_ char *word = NULL, *buf = NULL;
1559 _cleanup_free_ MultipathRoute *m = NULL;
1560 Network *network = userdata;
1561 const char *p, *ip, *dev;
1562 union in_addr_union a;
1563 int family, r;
1564
1565 assert(filename);
1566 assert(section);
1567 assert(lvalue);
1568 assert(rvalue);
1569 assert(data);
1570
1571 r = route_new_static(network, filename, section_line, &n);
d96edb2c
YW
1572 if (r == -ENOMEM)
1573 return log_oom();
1574 if (r < 0) {
1575 log_syntax(unit, LOG_WARNING, filename, line, r,
1576 "Failed to allocate route, ignoring assignment: %m");
1577 return 0;
1578 }
6ff5cc6b
YW
1579
1580 if (isempty(rvalue)) {
1581 n->multipath_routes = ordered_set_free_free(n->multipath_routes);
1582 return 0;
1583 }
1584
1585 m = new0(MultipathRoute, 1);
1586 if (!m)
1587 return log_oom();
1588
1589 p = rvalue;
1590 r = extract_first_word(&p, &word, NULL, 0);
1591 if (r == -ENOMEM)
1592 return log_oom();
1593 if (r <= 0) {
d96edb2c 1594 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1595 "Invalid multipath route option, ignoring assignment: %s", rvalue);
1596 return 0;
1597 }
1598
1599 dev = strchr(word, '@');
1600 if (dev) {
1601 buf = strndup(word, dev - word);
1602 if (!buf)
1603 return log_oom();
1604 ip = buf;
1605 dev++;
1606 } else
1607 ip = word;
1608
1609 r = in_addr_from_string_auto(ip, &family, &a);
1610 if (r < 0) {
d96edb2c 1611 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1612 "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
1613 return 0;
1614 }
1615 m->gateway.address = a;
1616 m->gateway.family = family;
1617
1618 if (dev) {
d308bb99 1619 r = resolve_interface(NULL, dev);
6ff5cc6b 1620 if (r < 0) {
d96edb2c 1621 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1622 "Invalid interface name or index, ignoring assignment: %s", dev);
1623 return 0;
1624 }
597da51b 1625 m->ifindex = r;
6ff5cc6b
YW
1626 }
1627
1628 if (!isempty(p)) {
1629 r = safe_atou32(p, &m->weight);
1630 if (r < 0) {
d96edb2c 1631 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1632 "Invalid multipath route weight, ignoring assignment: %s", p);
1633 return 0;
1634 }
1635 if (m->weight == 0 || m->weight > 256) {
d96edb2c 1636 log_syntax(unit, LOG_WARNING, filename, line, 0,
6ff5cc6b
YW
1637 "Invalid multipath route weight, ignoring assignment: %s", p);
1638 return 0;
1639 }
1640 }
1641
1642 r = ordered_set_ensure_allocated(&n->multipath_routes, NULL);
1643 if (r < 0)
1644 return log_oom();
1645
1646 r = ordered_set_put(n->multipath_routes, m);
1647 if (r < 0) {
d96edb2c 1648 log_syntax(unit, LOG_WARNING, filename, line, r,
6ff5cc6b
YW
1649 "Failed to store multipath route, ignoring assignment: %m");
1650 return 0;
1651 }
1652
1653 TAKE_PTR(m);
1654 TAKE_PTR(n);
1655 return 0;
1656}
1657
fcbf4cb7
YW
1658int route_section_verify(Route *route, Network *network) {
1659 if (section_is_invalid(route->section))
1660 return -EINVAL;
1661
1662 if (route->family == AF_UNSPEC) {
1663 assert(route->section);
1664
1665 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
1666 "%s: Route section without Gateway=, Destination=, Source=, "
1667 "or PreferredSource= field configured. "
1668 "Ignoring [Route] section from line %u.",
1669 route->section->filename, route->section->line);
1670 }
1671
c0d48bc5
YW
1672 if (!route->table_set && network->vrf) {
1673 route->table = VRF(network->vrf)->table;
1674 route->table_set = true;
1675 }
1676
f5c38922
YW
1677 if (!route->table_set && IN_SET(route->type, RTN_LOCAL, RTN_BROADCAST, RTN_ANYCAST, RTN_NAT))
1678 route->table = RT_TABLE_LOCAL;
1679
1680 if (!route->scope_set && route->family != AF_INET6) {
1681 if (IN_SET(route->type, RTN_LOCAL, RTN_NAT))
1682 route->scope = RT_SCOPE_HOST;
1683 else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST))
1684 route->scope = RT_SCOPE_LINK;
94d6e299
YW
1685 }
1686
fcbf4cb7
YW
1687 if (network->n_static_addresses == 0 &&
1688 in_addr_is_null(route->family, &route->gw) == 0 &&
1689 route->gateway_onlink < 0) {
1690 log_warning("%s: Gateway= without static address configured. "
1691 "Enabling GatewayOnLink= option.",
1692 network->filename);
1693 route->gateway_onlink = true;
1694 }
1695
1696 return 0;
1697}