]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
network: minor coding style update
[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"
23f53b99 10#include "networkd-manager.h"
6bedfcbb 11#include "networkd-route.h"
6bedfcbb 12#include "parse-util.h"
1c8e710c 13#include "set.h"
07630cea 14#include "string-util.h"
47d2d30d 15#include "sysctl-util.h"
07630cea 16#include "util.h"
f579559b 17
47d2d30d
ZJS
18#define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
19
20static unsigned routes_max(void) {
21 static thread_local unsigned cached = 0;
22
23 _cleanup_free_ char *s4 = NULL, *s6 = NULL;
24 unsigned val4 = ROUTES_DEFAULT_MAX_PER_FAMILY, val6 = ROUTES_DEFAULT_MAX_PER_FAMILY;
25
26 if (cached > 0)
27 return cached;
28
29 if (sysctl_read("net/ipv4/route/max_size", &s4) >= 0) {
30 truncate_nl(s4);
31 if (safe_atou(s4, &val4) >= 0 &&
32 val4 == 2147483647U)
33 /* This is the default "no limit" value in the kernel */
34 val4 = ROUTES_DEFAULT_MAX_PER_FAMILY;
35 }
36
37 if (sysctl_read("net/ipv6/route/max_size", &s6) >= 0) {
38 truncate_nl(s6);
39 (void) safe_atou(s6, &val6);
40 }
41
42 cached = MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val4) +
43 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY, val6);
44 return cached;
45}
8c34b963 46
ed9e361a 47int route_new(Route **ret) {
8e766630 48 _cleanup_(route_freep) Route *route = NULL;
f0213e37 49
17f9c355 50 route = new(Route, 1);
f0213e37
TG
51 if (!route)
52 return -ENOMEM;
53
17f9c355
YW
54 *route = (Route) {
55 .family = AF_UNSPEC,
56 .scope = RT_SCOPE_UNIVERSE,
57 .protocol = RTPROT_UNSPEC,
58 .type = RTN_UNICAST,
59 .table = RT_TABLE_MAIN,
60 .lifetime = USEC_INFINITY,
61 .quickack = -1,
62 };
f0213e37 63
1cc6c93a 64 *ret = TAKE_PTR(route);
f0213e37
TG
65
66 return 0;
67}
68
f4859fc7 69int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) {
8e766630
LP
70 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
71 _cleanup_(route_freep) Route *route = NULL;
f0213e37 72 int r;
f579559b 73
8c34b963
LP
74 assert(network);
75 assert(ret);
48317c39 76 assert(!!filename == (section_line > 0));
8c34b963 77
48317c39 78 if (filename) {
f4859fc7
SS
79 r = network_config_section_new(filename, section_line, &n);
80 if (r < 0)
81 return r;
82
83 route = hashmap_get(network->routes_by_section, n);
6ae115c1 84 if (route) {
1cc6c93a 85 *ret = TAKE_PTR(route);
6ae115c1
TG
86
87 return 0;
88 }
89 }
90
47d2d30d 91 if (network->n_static_routes >= routes_max())
8c34b963
LP
92 return -E2BIG;
93
ed9e361a 94 r = route_new(&route);
f0213e37
TG
95 if (r < 0)
96 return r;
801bd9e8 97
ed9e361a 98 route->protocol = RTPROT_STATIC;
0f7f2769
YW
99 route->network = network;
100 LIST_PREPEND(routes, network->static_routes, route);
101 network->n_static_routes++;
f579559b 102
48317c39 103 if (filename) {
1cc6c93a 104 route->section = TAKE_PTR(n);
cacc1dbf 105
3e570042
YW
106 r = hashmap_ensure_allocated(&network->routes_by_section, &network_config_hash_ops);
107 if (r < 0)
108 return r;
109
fcc48287 110 r = hashmap_put(network->routes_by_section, route->section, route);
21b39268
LP
111 if (r < 0)
112 return r;
6ae115c1
TG
113 }
114
1cc6c93a 115 *ret = TAKE_PTR(route);
f579559b
TG
116
117 return 0;
118}
119
120void route_free(Route *route) {
121 if (!route)
122 return;
123
f048a16b 124 if (route->network) {
3d3d4255 125 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 126
8c34b963
LP
127 assert(route->network->n_static_routes > 0);
128 route->network->n_static_routes--;
129
fd45e522 130 if (route->section)
f4859fc7 131 hashmap_remove(route->network->routes_by_section, route->section);
f048a16b 132 }
6ae115c1 133
fd45e522
ZJS
134 network_config_section_free(route->section);
135
1c8e710c
TG
136 if (route->link) {
137 set_remove(route->link->routes, route);
138 set_remove(route->link->routes_foreign, route);
139 }
140
f833694d
TG
141 sd_event_source_unref(route->expire);
142
f579559b
TG
143 free(route);
144}
145
7a08d314 146static void route_hash_func(const Route *route, struct siphash *state) {
bb7ae737
TG
147 assert(route);
148
149 siphash24_compress(&route->family, sizeof(route->family), state);
150
151 switch (route->family) {
152 case AF_INET:
153 case AF_INET6:
154 /* Equality of routes are given by the 4-touple
155 (dst_prefix,dst_prefixlen,tos,priority,table) */
2ce40956 156 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
bb7ae737
TG
157 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
158 siphash24_compress(&route->tos, sizeof(route->tos), state);
159 siphash24_compress(&route->priority, sizeof(route->priority), state);
160 siphash24_compress(&route->table, sizeof(route->table), state);
161
162 break;
163 default:
164 /* treat any other address family as AF_UNSPEC */
165 break;
166 }
167}
168
7a08d314 169static int route_compare_func(const Route *a, const Route *b) {
a0edd02e 170 int r;
bb7ae737 171
a0edd02e
FB
172 r = CMP(a->family, b->family);
173 if (r != 0)
174 return r;
bb7ae737
TG
175
176 switch (a->family) {
177 case AF_INET:
178 case AF_INET6:
a0edd02e
FB
179 r = CMP(a->dst_prefixlen, b->dst_prefixlen);
180 if (r != 0)
181 return r;
182
183 r = CMP(a->tos, b->tos);
184 if (r != 0)
185 return r;
186
187 r = CMP(a->priority, b->priority);
188 if (r != 0)
189 return r;
190
191 r = CMP(a->table, b->table);
192 if (r != 0)
193 return r;
bb7ae737 194
2ce40956 195 return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
bb7ae737
TG
196 default:
197 /* treat any other address family as AF_UNSPEC */
198 return 0;
199 }
200}
201
7a08d314 202DEFINE_PRIVATE_HASH_OPS(route_hash_ops, Route, route_hash_func, route_compare_func);
bb7ae737 203
7ecf0c3e
TJ
204bool route_equal(Route *r1, Route *r2) {
205 if (r1 == r2)
206 return true;
207
208 if (!r1 || !r2)
209 return false;
210
211 return route_compare_func(r1, r2) == 0;
212}
213
1c8e710c
TG
214int route_get(Link *link,
215 int family,
1b566071 216 const union in_addr_union *dst,
1c8e710c
TG
217 unsigned char dst_prefixlen,
218 unsigned char tos,
219 uint32_t priority,
14d20d2b 220 uint32_t table,
1c8e710c 221 Route **ret) {
1b566071
LP
222
223 Route route, *existing;
224
225 assert(link);
226 assert(dst);
227
228 route = (Route) {
1c8e710c 229 .family = family,
1b566071 230 .dst = *dst,
1c8e710c
TG
231 .dst_prefixlen = dst_prefixlen,
232 .tos = tos,
233 .priority = priority,
234 .table = table,
1b566071 235 };
1c8e710c
TG
236
237 existing = set_get(link->routes, &route);
238 if (existing) {
1b566071
LP
239 if (ret)
240 *ret = existing;
1c8e710c 241 return 1;
1c8e710c
TG
242 }
243
1b566071
LP
244 existing = set_get(link->routes_foreign, &route);
245 if (existing) {
246 if (ret)
247 *ret = existing;
248 return 0;
249 }
1c8e710c 250
1b566071 251 return -ENOENT;
1c8e710c
TG
252}
253
889b550f
LP
254static int route_add_internal(
255 Link *link,
256 Set **routes,
257 int family,
258 const union in_addr_union *dst,
259 unsigned char dst_prefixlen,
260 unsigned char tos,
261 uint32_t priority,
14d20d2b 262 uint32_t table,
889b550f
LP
263 Route **ret) {
264
8e766630 265 _cleanup_(route_freep) Route *route = NULL;
1c8e710c
TG
266 int r;
267
268 assert(link);
269 assert(routes);
270 assert(dst);
271
272 r = route_new(&route);
273 if (r < 0)
274 return r;
275
276 route->family = family;
277 route->dst = *dst;
278 route->dst_prefixlen = dst_prefixlen;
279 route->tos = tos;
280 route->priority = priority;
281 route->table = table;
282
283 r = set_ensure_allocated(routes, &route_hash_ops);
284 if (r < 0)
285 return r;
286
287 r = set_put(*routes, route);
288 if (r < 0)
289 return r;
290
291 route->link = link;
292
293 if (ret)
294 *ret = route;
295
296 route = NULL;
297
298 return 0;
299}
300
889b550f
LP
301int route_add_foreign(
302 Link *link,
303 int family,
304 const union in_addr_union *dst,
305 unsigned char dst_prefixlen,
306 unsigned char tos,
307 uint32_t priority,
14d20d2b 308 uint32_t table,
889b550f
LP
309 Route **ret) {
310
1c8e710c
TG
311 return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
312}
313
27efb52b 314int route_add(Link *link,
1c8e710c 315 int family,
889b550f 316 const union in_addr_union *dst,
1c8e710c
TG
317 unsigned char dst_prefixlen,
318 unsigned char tos,
319 uint32_t priority,
14d20d2b 320 uint32_t table,
889b550f
LP
321 Route **ret) {
322
1c8e710c
TG
323 Route *route;
324 int r;
325
326 r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
327 if (r == -ENOENT) {
328 /* Route does not exist, create a new one */
329 r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
330 if (r < 0)
331 return r;
332 } else if (r == 0) {
333 /* Take over a foreign route */
334 r = set_ensure_allocated(&link->routes, &route_hash_ops);
335 if (r < 0)
336 return r;
337
338 r = set_put(link->routes, route);
339 if (r < 0)
340 return r;
341
342 set_remove(link->routes_foreign, route);
343 } else if (r == 1) {
344 /* Route exists, do nothing */
345 ;
346 } else
347 return r;
348
856e309d
MC
349 if (ret)
350 *ret = route;
1c8e710c
TG
351
352 return 0;
353}
354
bbd15900 355void route_update(Route *route,
27efb52b
YW
356 const union in_addr_union *src,
357 unsigned char src_prefixlen,
358 const union in_addr_union *gw,
359 const union in_addr_union *prefsrc,
360 unsigned char scope,
361 unsigned char protocol,
362 unsigned char type) {
889b550f 363
1c8e710c 364 assert(route);
26db55f3 365 assert(src || src_prefixlen == 0);
1c8e710c 366
3c7911e8 367 route->src = src ? *src : IN_ADDR_NULL;
1c8e710c 368 route->src_prefixlen = src_prefixlen;
3c7911e8
YW
369 route->gw = gw ? *gw : IN_ADDR_NULL;
370 route->prefsrc = prefsrc ? *prefsrc : IN_ADDR_NULL;
1c8e710c
TG
371 route->scope = scope;
372 route->protocol = protocol;
983226f3 373 route->type = type;
1c8e710c
TG
374}
375
302a796f 376static int route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
4645ad47
YW
377 int r;
378
379 assert(m);
380 assert(link);
381 assert(link->ifname);
382
383 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
384 return 1;
385
386 r = sd_netlink_message_get_errno(m);
387 if (r < 0 && r != -ESRCH)
388 log_link_warning_errno(link, r, "Could not drop route: %m");
389
390 return 1;
391}
392
91b5f997 393int route_remove(Route *route, Link *link,
302a796f 394 link_netlink_message_handler_t callback) {
27efb52b 395
4afd3348 396 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
397 int r;
398
399 assert(link);
400 assert(link->manager);
401 assert(link->manager->rtnl);
402 assert(link->ifindex > 0);
4c701096 403 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9
UTL
404
405 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
406 RTM_DELROUTE, route->family,
407 route->protocol);
f647962d
MS
408 if (r < 0)
409 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 410
2ce40956 411 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 412 if (route->family == AF_INET)
2ce40956 413 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 414 else if (route->family == AF_INET6)
2ce40956 415 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
416 if (r < 0)
417 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
418 }
419
420 if (route->dst_prefixlen) {
421 if (route->family == AF_INET)
2ce40956 422 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
5c1d3fc9 423 else if (route->family == AF_INET6)
2ce40956 424 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
425 if (r < 0)
426 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
427
428 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
429 if (r < 0)
430 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
431 }
432
9e7e4408
TG
433 if (route->src_prefixlen) {
434 if (route->family == AF_INET)
2ce40956 435 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 436 else if (route->family == AF_INET6)
2ce40956 437 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408 438 if (r < 0)
d9d94393 439 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
440
441 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
442 if (r < 0)
443 return log_error_errno(r, "Could not set source prefix length: %m");
444 }
445
2ce40956 446 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 447 if (route->family == AF_INET)
2ce40956 448 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 449 else if (route->family == AF_INET6)
2ce40956 450 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
451 if (r < 0)
452 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
453 }
454
5c1d3fc9 455 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
456 if (r < 0)
457 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 458
86655331 459 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
460 if (r < 0)
461 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 462
2d53f310 463 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
464 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
465 if (r < 0)
466 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
467 }
5c1d3fc9 468
302a796f
YW
469 r = netlink_call_async(link->manager->rtnl, NULL, req,
470 callback ?: route_remove_handler,
471 link_netlink_destroy_callback, link);
f647962d
MS
472 if (r < 0)
473 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 474
563c69c6
TG
475 link_ref(link);
476
5c1d3fc9
UTL
477 return 0;
478}
479
f833694d
TG
480int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
481 Route *route = userdata;
482 int r;
483
484 assert(route);
485
4645ad47 486 r = route_remove(route, route->link, NULL);
f833694d
TG
487 if (r < 0)
488 log_warning_errno(r, "Could not remove route: %m");
3bdccf69 489 else
fe7ca21a 490 route_free(route);
f833694d
TG
491
492 return 1;
493}
494
1b566071
LP
495int route_configure(
496 Route *route,
497 Link *link,
302a796f 498 link_netlink_message_handler_t callback) {
1b566071 499
4afd3348
LP
500 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
501 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
f833694d 502 usec_t lifetime;
f579559b
TG
503 int r;
504
f579559b 505 assert(link);
f882c247
TG
506 assert(link->manager);
507 assert(link->manager->rtnl);
f579559b 508 assert(link->ifindex > 0);
4c701096 509 assert(IN_SET(route->family, AF_INET, AF_INET6));
bd1175bc 510 assert(callback);
f579559b 511
1b566071 512 if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
47d2d30d 513 set_size(link->routes) >= routes_max())
1b566071
LP
514 return -E2BIG;
515
151b9b96 516 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
517 RTM_NEWROUTE, route->family,
518 route->protocol);
f647962d
MS
519 if (r < 0)
520 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 521
2ce40956 522 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 523 if (route->family == AF_INET)
2ce40956 524 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 525 else if (route->family == AF_INET6)
2ce40956 526 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
527 if (r < 0)
528 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
c953b24c
SS
529
530 r = sd_rtnl_message_route_set_family(req, route->family);
531 if (r < 0)
532 return log_error_errno(r, "Could not set route family: %m");
f579559b
TG
533 }
534
0a0dc69b
TG
535 if (route->dst_prefixlen) {
536 if (route->family == AF_INET)
2ce40956 537 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
0a0dc69b 538 else if (route->family == AF_INET6)
2ce40956 539 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
540 if (r < 0)
541 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 542
ae4c67a7 543 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
544 if (r < 0)
545 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
546 }
547
9e7e4408
TG
548 if (route->src_prefixlen) {
549 if (route->family == AF_INET)
2ce40956 550 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 551 else if (route->family == AF_INET6)
2ce40956 552 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
553 if (r < 0)
554 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
555
556 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
557 if (r < 0)
558 return log_error_errno(r, "Could not set source prefix length: %m");
559 }
560
2ce40956 561 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 562 if (route->family == AF_INET)
2ce40956 563 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 564 else if (route->family == AF_INET6)
2ce40956 565 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
566 if (r < 0)
567 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
568 }
569
5c1d3fc9 570 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
571 if (r < 0)
572 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 573
3b015d40
TG
574 r = sd_rtnl_message_route_set_flags(req, route->flags);
575 if (r < 0)
c953b24c
SS
576 return log_error_errno(r, "Could not set flags: %m");
577
a0d95bbc 578 if (route->table != RT_TABLE_MAIN) {
c953b24c
SS
579 if (route->table < 256) {
580 r = sd_rtnl_message_route_set_table(req, route->table);
581 if (r < 0)
582 return log_error_errno(r, "Could not set route table: %m");
583 } else {
c953b24c
SS
584 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
585 if (r < 0)
586 return log_error_errno(r, "Could not set route table: %m");
587
06976f5b 588 /* Table attribute to allow more than 256. */
c953b24c
SS
589 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
590 if (r < 0)
591 return log_error_errno(r, "Could not append RTA_TABLE attribute: %m");
592 }
593 }
3b015d40 594
86655331 595 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
596 if (r < 0)
597 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 598
3b015d40
TG
599 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
600 if (r < 0)
601 return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
602
f02ba163
DD
603 if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) {
604 r = sd_netlink_message_append_u32(req, RTA_EXPIRES,
605 DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC));
606 if (r < 0)
607 return log_error_errno(r, "Could not append RTA_EXPIRES attribute: %m");
608 }
609
983226f3 610 r = sd_rtnl_message_route_set_type(req, route->type);
f647962d 611 if (r < 0)
983226f3
SS
612 return log_error_errno(r, "Could not set route type: %m");
613
2d53f310 614 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE, RTN_THROW)) {
983226f3
SS
615 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
616 if (r < 0)
617 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
618 }
f579559b 619
d6fceaf1
SS
620 r = sd_netlink_message_open_container(req, RTA_METRICS);
621 if (r < 0)
622 return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
623
624 if (route->mtu > 0) {
625 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
626 if (r < 0)
627 return log_error_errno(r, "Could not append RTAX_MTU attribute: %m");
628 }
629
6b21ad33 630 if (route->initcwnd > 0) {
323d9329
SS
631 r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd);
632 if (r < 0)
633 return log_error_errno(r, "Could not append RTAX_INITCWND attribute: %m");
634 }
635
6b21ad33 636 if (route->initrwnd > 0) {
323d9329
SS
637 r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd);
638 if (r < 0)
639 return log_error_errno(r, "Could not append RTAX_INITRWND attribute: %m");
640 }
641
09f5dfad
SS
642 if (route->quickack != -1) {
643 r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack);
644 if (r < 0)
645 return log_error_errno(r, "Could not append RTAX_QUICKACK attribute: %m");
646 }
647
d6fceaf1
SS
648 r = sd_netlink_message_close_container(req);
649 if (r < 0)
650 return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
651
302a796f
YW
652 r = netlink_call_async(link->manager->rtnl, NULL, req, callback,
653 link_netlink_destroy_callback, link);
f647962d
MS
654 if (r < 0)
655 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 656
563c69c6
TG
657 link_ref(link);
658
f833694d
TG
659 lifetime = route->lifetime;
660
661 r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
1c8e710c
TG
662 if (r < 0)
663 return log_error_errno(r, "Could not add route: %m");
664
f833694d
TG
665 /* TODO: drop expiration handling once it can be pushed into the kernel */
666 route->lifetime = lifetime;
667
f02ba163 668 if (route->lifetime != USEC_INFINITY && !kernel_route_expiration_supported()) {
f833694d
TG
669 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
670 route->lifetime, 0, route_expire_handler, route);
671 if (r < 0)
672 return log_error_errno(r, "Could not arm expiration timer: %m");
673 }
674
675 sd_event_source_unref(route->expire);
1cc6c93a 676 route->expire = TAKE_PTR(expire);
f833694d 677
f579559b
TG
678 return 0;
679}
680
27efb52b
YW
681int config_parse_gateway(
682 const char *unit,
f579559b
TG
683 const char *filename,
684 unsigned line,
685 const char *section,
71a61510 686 unsigned section_line,
f579559b
TG
687 const char *lvalue,
688 int ltype,
689 const char *rvalue,
690 void *data,
691 void *userdata) {
44e7b949 692
6ae115c1 693 Network *network = userdata;
8e766630 694 _cleanup_(route_freep) Route *n = NULL;
7934dede 695 int r;
f579559b
TG
696
697 assert(filename);
6ae115c1 698 assert(section);
f579559b
TG
699 assert(lvalue);
700 assert(rvalue);
701 assert(data);
702
92fe133a
TG
703 if (streq(section, "Network")) {
704 /* we are not in an Route section, so treat
705 * this as the special '0' section */
f4859fc7
SS
706 r = route_new_static(network, NULL, 0, &n);
707 } else
708 r = route_new_static(network, filename, section_line, &n);
92fe133a 709
f579559b
TG
710 if (r < 0)
711 return r;
712
7934dede 713 r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
f579559b 714 if (r < 0) {
12ca818f 715 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
716 return 0;
717 }
718
aff44301 719 TAKE_PTR(n);
f579559b
TG
720
721 return 0;
722}
6ae115c1 723
27efb52b
YW
724int config_parse_preferred_src(
725 const char *unit,
0d07e595
JK
726 const char *filename,
727 unsigned line,
728 const char *section,
729 unsigned section_line,
730 const char *lvalue,
731 int ltype,
732 const char *rvalue,
733 void *data,
734 void *userdata) {
735
736 Network *network = userdata;
8e766630 737 _cleanup_(route_freep) Route *n = NULL;
7934dede 738 int r;
0d07e595
JK
739
740 assert(filename);
741 assert(section);
742 assert(lvalue);
743 assert(rvalue);
744 assert(data);
745
f4859fc7 746 r = route_new_static(network, filename, section_line, &n);
0d07e595
JK
747 if (r < 0)
748 return r;
749
7934dede 750 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
0d07e595
JK
751 if (r < 0) {
752 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
753 "Preferred source is invalid, ignoring assignment: %s", rvalue);
754 return 0;
755 }
756
aff44301 757 TAKE_PTR(n);
0d07e595
JK
758
759 return 0;
760}
761
27efb52b
YW
762int config_parse_destination(
763 const char *unit,
6ae115c1
TG
764 const char *filename,
765 unsigned line,
766 const char *section,
767 unsigned section_line,
768 const char *lvalue,
769 int ltype,
770 const char *rvalue,
771 void *data,
772 void *userdata) {
44e7b949 773
6ae115c1 774 Network *network = userdata;
8e766630 775 _cleanup_(route_freep) Route *n = NULL;
7934dede
YW
776 union in_addr_union *buffer;
777 unsigned char *prefixlen;
ca3bad65 778 int r;
6ae115c1
TG
779
780 assert(filename);
781 assert(section);
782 assert(lvalue);
783 assert(rvalue);
784 assert(data);
785
f4859fc7 786 r = route_new_static(network, filename, section_line, &n);
6ae115c1
TG
787 if (r < 0)
788 return r;
789
9e7e4408 790 if (streq(lvalue, "Destination")) {
7934dede
YW
791 buffer = &n->dst;
792 prefixlen = &n->dst_prefixlen;
9e7e4408 793 } else if (streq(lvalue, "Source")) {
7934dede
YW
794 buffer = &n->src;
795 prefixlen = &n->src_prefixlen;
9e7e4408
TG
796 } else
797 assert_not_reached(lvalue);
798
7934dede
YW
799 r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen);
800 if (r < 0) {
801 log_syntax(unit, LOG_ERR, filename, line, r,
802 "Route %s= prefix is invalid, ignoring assignment: %s",
803 lvalue, rvalue);
804 return 0;
805 }
806
aff44301 807 TAKE_PTR(n);
6ae115c1
TG
808 return 0;
809}
5d8e593d 810
27efb52b
YW
811int config_parse_route_priority(
812 const char *unit,
813 const char *filename,
814 unsigned line,
815 const char *section,
816 unsigned section_line,
817 const char *lvalue,
818 int ltype,
819 const char *rvalue,
820 void *data,
821 void *userdata) {
822
5d8e593d 823 Network *network = userdata;
8e766630 824 _cleanup_(route_freep) Route *n = NULL;
5d8e593d
SS
825 int r;
826
827 assert(filename);
828 assert(section);
829 assert(lvalue);
830 assert(rvalue);
831 assert(data);
832
f4859fc7 833 r = route_new_static(network, filename, section_line, &n);
5d8e593d
SS
834 if (r < 0)
835 return r;
836
aff44301 837 r = safe_atou32(rvalue, &n->priority);
1c4b1179
SS
838 if (r < 0) {
839 log_syntax(unit, LOG_ERR, filename, line, r,
840 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
841 return 0;
842 }
5d8e593d 843
aff44301 844 TAKE_PTR(n);
5d8e593d
SS
845 return 0;
846}
769b56a3 847
27efb52b
YW
848int config_parse_route_scope(
849 const char *unit,
850 const char *filename,
851 unsigned line,
852 const char *section,
853 unsigned section_line,
854 const char *lvalue,
855 int ltype,
856 const char *rvalue,
857 void *data,
858 void *userdata) {
859
769b56a3 860 Network *network = userdata;
8e766630 861 _cleanup_(route_freep) Route *n = NULL;
769b56a3
TG
862 int r;
863
864 assert(filename);
865 assert(section);
866 assert(lvalue);
867 assert(rvalue);
868 assert(data);
869
f4859fc7 870 r = route_new_static(network, filename, section_line, &n);
769b56a3
TG
871 if (r < 0)
872 return r;
873
874 if (streq(rvalue, "host"))
875 n->scope = RT_SCOPE_HOST;
876 else if (streq(rvalue, "link"))
877 n->scope = RT_SCOPE_LINK;
878 else if (streq(rvalue, "global"))
879 n->scope = RT_SCOPE_UNIVERSE;
880 else {
12ca818f 881 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
882 return 0;
883 }
884
aff44301 885 TAKE_PTR(n);
769b56a3
TG
886 return 0;
887}
c953b24c 888
27efb52b
YW
889int config_parse_route_table(
890 const char *unit,
891 const char *filename,
892 unsigned line,
893 const char *section,
894 unsigned section_line,
895 const char *lvalue,
896 int ltype,
897 const char *rvalue,
898 void *data,
899 void *userdata) {
900
8e766630 901 _cleanup_(route_freep) Route *n = NULL;
c953b24c 902 Network *network = userdata;
c953b24c
SS
903 int r;
904
905 assert(filename);
906 assert(section);
907 assert(lvalue);
908 assert(rvalue);
909 assert(data);
910
f4859fc7 911 r = route_new_static(network, filename, section_line, &n);
c953b24c
SS
912 if (r < 0)
913 return r;
914
aff44301 915 r = safe_atou32(rvalue, &n->table);
c953b24c
SS
916 if (r < 0) {
917 log_syntax(unit, LOG_ERR, filename, line, r,
918 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
919 return 0;
920 }
921
aff44301 922 TAKE_PTR(n);
c953b24c
SS
923 return 0;
924}
28959f7d 925
27efb52b
YW
926int config_parse_gateway_onlink(
927 const char *unit,
928 const char *filename,
929 unsigned line,
930 const char *section,
931 unsigned section_line,
932 const char *lvalue,
933 int ltype,
934 const char *rvalue,
935 void *data,
936 void *userdata) {
937
28959f7d 938 Network *network = userdata;
8e766630 939 _cleanup_(route_freep) Route *n = NULL;
28959f7d
SS
940 int r;
941
942 assert(filename);
943 assert(section);
944 assert(lvalue);
945 assert(rvalue);
946 assert(data);
947
948 r = route_new_static(network, filename, section_line, &n);
949 if (r < 0)
950 return r;
951
952 r = parse_boolean(rvalue);
953 if (r < 0) {
954 log_syntax(unit, LOG_ERR, filename, line, r,
955 "Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue);
956 return 0;
957 }
958
ab8ee0f2 959 SET_FLAG(n->flags, RTNH_F_ONLINK, r);
aff44301 960 TAKE_PTR(n);
b5bf6f64
SS
961 return 0;
962}
963
27efb52b
YW
964int config_parse_ipv6_route_preference(
965 const char *unit,
966 const char *filename,
967 unsigned line,
968 const char *section,
969 unsigned section_line,
970 const char *lvalue,
971 int ltype,
972 const char *rvalue,
973 void *data,
974 void *userdata) {
975
b5bf6f64 976 Network *network = userdata;
8e766630 977 _cleanup_(route_freep) Route *n = NULL;
b5bf6f64
SS
978 int r;
979
4c7bd9cf
SS
980 r = route_new_static(network, filename, section_line, &n);
981 if (r < 0)
982 return r;
983
b5bf6f64
SS
984 if (streq(rvalue, "low"))
985 n->pref = ICMPV6_ROUTER_PREF_LOW;
986 else if (streq(rvalue, "medium"))
987 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
988 else if (streq(rvalue, "high"))
989 n->pref = ICMPV6_ROUTER_PREF_HIGH;
990 else {
991 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue);
992 return 0;
993 }
28959f7d 994
aff44301 995 TAKE_PTR(n);
28959f7d
SS
996 return 0;
997}
c83ecc04 998
27efb52b
YW
999int config_parse_route_protocol(
1000 const char *unit,
1001 const char *filename,
1002 unsigned line,
1003 const char *section,
1004 unsigned section_line,
1005 const char *lvalue,
1006 int ltype,
1007 const char *rvalue,
1008 void *data,
1009 void *userdata) {
1010
c83ecc04 1011 Network *network = userdata;
8e766630 1012 _cleanup_(route_freep) Route *n = NULL;
c83ecc04
SS
1013 int r;
1014
1015 r = route_new_static(network, filename, section_line, &n);
1016 if (r < 0)
1017 return r;
1018
1019 if (streq(rvalue, "kernel"))
1020 n->protocol = RTPROT_KERNEL;
1021 else if (streq(rvalue, "boot"))
1022 n->protocol = RTPROT_BOOT;
1023 else if (streq(rvalue, "static"))
1024 n->protocol = RTPROT_STATIC;
1025 else {
1026 r = safe_atou8(rvalue , &n->protocol);
1027 if (r < 0) {
1028 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
1029 return 0;
1030 }
1031 }
1032
aff44301 1033 TAKE_PTR(n);
c83ecc04
SS
1034 return 0;
1035}
983226f3 1036
27efb52b
YW
1037int config_parse_route_type(
1038 const char *unit,
1039 const char *filename,
1040 unsigned line,
1041 const char *section,
1042 unsigned section_line,
1043 const char *lvalue,
1044 int ltype,
1045 const char *rvalue,
1046 void *data,
1047 void *userdata) {
1048
983226f3 1049 Network *network = userdata;
8e766630 1050 _cleanup_(route_freep) Route *n = NULL;
983226f3
SS
1051 int r;
1052
1053 r = route_new_static(network, filename, section_line, &n);
1054 if (r < 0)
1055 return r;
1056
1057 if (streq(rvalue, "unicast"))
1058 n->type = RTN_UNICAST;
1059 else if (streq(rvalue, "blackhole"))
1060 n->type = RTN_BLACKHOLE;
1061 else if (streq(rvalue, "unreachable"))
1062 n->type = RTN_UNREACHABLE;
1063 else if (streq(rvalue, "prohibit"))
1064 n->type = RTN_PROHIBIT;
2d53f310
HY
1065 else if (streq(rvalue, "throw"))
1066 n->type = RTN_THROW;
983226f3
SS
1067 else {
1068 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
1069 return 0;
1070 }
1071
aff44301 1072 TAKE_PTR(n);
983226f3
SS
1073 return 0;
1074}
323d9329 1075
27efb52b
YW
1076int config_parse_tcp_window(
1077 const char *unit,
1078 const char *filename,
1079 unsigned line,
1080 const char *section,
1081 unsigned section_line,
1082 const char *lvalue,
1083 int ltype,
1084 const char *rvalue,
1085 void *data,
1086 void *userdata) {
1087
8e766630 1088 _cleanup_(route_freep) Route *n = NULL;
6b21ad33
SS
1089 Network *network = userdata;
1090 uint64_t k;
323d9329
SS
1091 int r;
1092
1093 assert(filename);
1094 assert(section);
1095 assert(lvalue);
1096 assert(rvalue);
1097 assert(data);
1098
1099 r = route_new_static(network, filename, section_line, &n);
1100 if (r < 0)
1101 return r;
1102
6b21ad33
SS
1103 r = parse_size(rvalue, 1024, &k);
1104 if (r < 0 || k > UINT32_MAX) {
323d9329 1105 log_syntax(unit, LOG_ERR, filename, line, r,
6b21ad33 1106 "Could not parse TCP %s \"%s\" bytes, ignoring assignment: %m", rvalue, lvalue);
323d9329
SS
1107 return 0;
1108 }
1109
1110 if (streq(lvalue, "InitialCongestionWindow"))
1111 n->initcwnd = k;
1112 else if (streq(lvalue, "InitialAdvertisedReceiveWindow"))
1113 n->initrwnd = k;
1114 else {
1115 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse TCP %s: %s", lvalue, rvalue);
1116 return 0;
1117 }
1118
aff44301 1119 TAKE_PTR(n);
323d9329
SS
1120 return 0;
1121}
09f5dfad 1122
27efb52b
YW
1123int config_parse_quickack(
1124 const char *unit,
1125 const char *filename,
1126 unsigned line,
1127 const char *section,
1128 unsigned section_line,
1129 const char *lvalue,
1130 int ltype,
1131 const char *rvalue,
1132 void *data,
1133 void *userdata) {
1134
8e766630 1135 _cleanup_(route_freep) Route *n = NULL;
09f5dfad
SS
1136 Network *network = userdata;
1137 int k, r;
1138
1139 assert(filename);
1140 assert(section);
1141 assert(lvalue);
1142 assert(rvalue);
1143 assert(data);
1144
1145 r = route_new_static(network, filename, section_line, &n);
1146 if (r < 0)
1147 return r;
1148
1149 k = parse_boolean(rvalue);
1150 if (k < 0) {
1151 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse TCP quickack, ignoring: %s", rvalue);
1152 return 0;
1153 }
1154
1155 n->quickack = !!k;
aff44301 1156 TAKE_PTR(n);
09f5dfad
SS
1157 return 0;
1158}
cea79e66
SS
1159
1160int config_parse_route_mtu(
1161 const char *unit,
1162 const char *filename,
1163 unsigned line,
1164 const char *section,
1165 unsigned section_line,
1166 const char *lvalue,
1167 int ltype,
1168 const char *rvalue,
1169 void *data,
1170 void *userdata) {
1171
1172 Network *network = userdata;
1173 _cleanup_(route_freep) Route *n = NULL;
1174 int r;
1175
1176 assert(filename);
1177 assert(section);
1178 assert(lvalue);
1179 assert(rvalue);
1180 assert(data);
1181
1182 r = route_new_static(network, filename, section_line, &n);
1183 if (r < 0)
1184 return r;
1185
1186 r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
1187 if (r < 0)
1188 return r;
1189
aff44301 1190 TAKE_PTR(n);
cea79e66
SS
1191 return 0;
1192}