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