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