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