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