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