]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
Add SPDX license identifiers to source files under the LGPL
[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
371int 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 return 0;
394}
395
91b5f997 396int route_remove(Route *route, Link *link,
1c4baffc 397 sd_netlink_message_handler_t callback) {
4afd3348 398 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
5c1d3fc9
UTL
399 int r;
400
401 assert(link);
402 assert(link->manager);
403 assert(link->manager->rtnl);
404 assert(link->ifindex > 0);
4c701096 405 assert(IN_SET(route->family, AF_INET, AF_INET6));
5c1d3fc9
UTL
406
407 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
408 RTM_DELROUTE, route->family,
409 route->protocol);
f647962d
MS
410 if (r < 0)
411 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 412
2ce40956 413 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 414 if (route->family == AF_INET)
2ce40956 415 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 416 else if (route->family == AF_INET6)
2ce40956 417 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
418 if (r < 0)
419 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
420 }
421
422 if (route->dst_prefixlen) {
423 if (route->family == AF_INET)
2ce40956 424 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
5c1d3fc9 425 else if (route->family == AF_INET6)
2ce40956 426 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
427 if (r < 0)
428 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
429
430 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
431 if (r < 0)
432 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
433 }
434
9e7e4408
TG
435 if (route->src_prefixlen) {
436 if (route->family == AF_INET)
2ce40956 437 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 438 else if (route->family == AF_INET6)
2ce40956 439 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408 440 if (r < 0)
d9d94393 441 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
9e7e4408
TG
442
443 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
444 if (r < 0)
445 return log_error_errno(r, "Could not set source prefix length: %m");
446 }
447
2ce40956 448 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 449 if (route->family == AF_INET)
2ce40956 450 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 451 else if (route->family == AF_INET6)
2ce40956 452 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
453 if (r < 0)
454 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
455 }
456
5c1d3fc9 457 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
458 if (r < 0)
459 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 460
86655331 461 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
462 if (r < 0)
463 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 464
983226f3
SS
465 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
466 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
467 if (r < 0)
468 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
469 }
5c1d3fc9 470
1c4baffc 471 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
472 if (r < 0)
473 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 474
563c69c6
TG
475 link_ref(link);
476
5c1d3fc9
UTL
477 return 0;
478}
479
fe7ca21a
SS
480static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
481 Link *link = userdata;
482 int r;
483
484 assert(rtnl);
485 assert(m);
486 assert(link);
487 assert(link->ifname);
fe7ca21a
SS
488
489 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
490 return 1;
491
fe7ca21a
SS
492 r = sd_netlink_message_get_errno(m);
493 if (r < 0 && r != -EEXIST)
494 log_link_warning_errno(link, r, "could not remove route: %m");
495
fe7ca21a
SS
496 return 1;
497}
498
f833694d
TG
499int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
500 Route *route = userdata;
501 int r;
502
503 assert(route);
504
fe7ca21a 505 r = route_remove(route, route->link, route_expire_callback);
f833694d
TG
506 if (r < 0)
507 log_warning_errno(r, "Could not remove route: %m");
3bdccf69 508 else
fe7ca21a 509 route_free(route);
f833694d
TG
510
511 return 1;
512}
513
1b566071
LP
514int route_configure(
515 Route *route,
516 Link *link,
517 sd_netlink_message_handler_t callback) {
518
4afd3348
LP
519 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
520 _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
f833694d 521 usec_t lifetime;
f579559b
TG
522 int r;
523
f579559b 524 assert(link);
f882c247
TG
525 assert(link->manager);
526 assert(link->manager->rtnl);
f579559b 527 assert(link->ifindex > 0);
4c701096 528 assert(IN_SET(route->family, AF_INET, AF_INET6));
f579559b 529
1b566071 530 if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
47d2d30d 531 set_size(link->routes) >= routes_max())
1b566071
LP
532 return -E2BIG;
533
151b9b96 534 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
535 RTM_NEWROUTE, route->family,
536 route->protocol);
f647962d
MS
537 if (r < 0)
538 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 539
2ce40956 540 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 541 if (route->family == AF_INET)
2ce40956 542 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 543 else if (route->family == AF_INET6)
2ce40956 544 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
545 if (r < 0)
546 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
c953b24c
SS
547
548 r = sd_rtnl_message_route_set_family(req, route->family);
549 if (r < 0)
550 return log_error_errno(r, "Could not set route family: %m");
f579559b
TG
551 }
552
0a0dc69b
TG
553 if (route->dst_prefixlen) {
554 if (route->family == AF_INET)
2ce40956 555 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
0a0dc69b 556 else if (route->family == AF_INET6)
2ce40956 557 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
558 if (r < 0)
559 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 560
ae4c67a7 561 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
562 if (r < 0)
563 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
564 }
565
9e7e4408
TG
566 if (route->src_prefixlen) {
567 if (route->family == AF_INET)
2ce40956 568 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 569 else if (route->family == AF_INET6)
2ce40956 570 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
571 if (r < 0)
572 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
573
574 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
575 if (r < 0)
576 return log_error_errno(r, "Could not set source prefix length: %m");
577 }
578
2ce40956 579 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 580 if (route->family == AF_INET)
2ce40956 581 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 582 else if (route->family == AF_INET6)
2ce40956 583 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
584 if (r < 0)
585 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
586 }
587
5c1d3fc9 588 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
589 if (r < 0)
590 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 591
3b015d40
TG
592 r = sd_rtnl_message_route_set_flags(req, route->flags);
593 if (r < 0)
c953b24c
SS
594 return log_error_errno(r, "Could not set flags: %m");
595
a0d95bbc 596 if (route->table != RT_TABLE_MAIN) {
c953b24c
SS
597 if (route->table < 256) {
598 r = sd_rtnl_message_route_set_table(req, route->table);
599 if (r < 0)
600 return log_error_errno(r, "Could not set route table: %m");
601 } else {
c953b24c
SS
602 r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
603 if (r < 0)
604 return log_error_errno(r, "Could not set route table: %m");
605
06976f5b 606 /* Table attribute to allow more than 256. */
c953b24c
SS
607 r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
608 if (r < 0)
609 return log_error_errno(r, "Could not append RTA_TABLE attribute: %m");
610 }
611 }
3b015d40 612
86655331 613 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
614 if (r < 0)
615 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 616
3b015d40
TG
617 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
618 if (r < 0)
619 return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
620
983226f3 621 r = sd_rtnl_message_route_set_type(req, route->type);
f647962d 622 if (r < 0)
983226f3
SS
623 return log_error_errno(r, "Could not set route type: %m");
624
625 if (!IN_SET(route->type, RTN_UNREACHABLE, RTN_PROHIBIT, RTN_BLACKHOLE)) {
626 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
627 if (r < 0)
628 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
629 }
f579559b 630
d6fceaf1
SS
631 r = sd_netlink_message_open_container(req, RTA_METRICS);
632 if (r < 0)
633 return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
634
635 if (route->mtu > 0) {
636 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
637 if (r < 0)
638 return log_error_errno(r, "Could not append RTAX_MTU attribute: %m");
639 }
640
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
660 if (route->lifetime != USEC_INFINITY) {
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);
668 route->expire = expire;
669 expire = NULL;
670
f579559b
TG
671 return 0;
672}
673
674int config_parse_gateway(const char *unit,
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;
f579559b 686 _cleanup_route_free_ 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;
f579559b
TG
714 n = NULL;
715
716 return 0;
717}
6ae115c1 718
0d07e595
JK
719int config_parse_preferred_src(const char *unit,
720 const char *filename,
721 unsigned line,
722 const char *section,
723 unsigned section_line,
724 const char *lvalue,
725 int ltype,
726 const char *rvalue,
727 void *data,
728 void *userdata) {
729
730 Network *network = userdata;
731 _cleanup_route_free_ Route *n = NULL;
732 union in_addr_union buffer;
733 int r, f;
734
735 assert(filename);
736 assert(section);
737 assert(lvalue);
738 assert(rvalue);
739 assert(data);
740
f4859fc7 741 r = route_new_static(network, filename, section_line, &n);
0d07e595
JK
742 if (r < 0)
743 return r;
744
745 r = in_addr_from_string_auto(rvalue, &f, &buffer);
746 if (r < 0) {
747 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
748 "Preferred source is invalid, ignoring assignment: %s", rvalue);
749 return 0;
750 }
751
752 n->family = f;
2ce40956 753 n->prefsrc = buffer;
0d07e595
JK
754 n = NULL;
755
756 return 0;
757}
758
6ae115c1
TG
759int config_parse_destination(const char *unit,
760 const char *filename,
761 unsigned line,
762 const char *section,
763 unsigned section_line,
764 const char *lvalue,
765 int ltype,
766 const char *rvalue,
767 void *data,
768 void *userdata) {
44e7b949 769
6ae115c1
TG
770 Network *network = userdata;
771 _cleanup_route_free_ Route *n = NULL;
44e7b949 772 union in_addr_union buffer;
9e7e4408 773 unsigned char prefixlen;
ca3bad65 774 int r;
6ae115c1
TG
775
776 assert(filename);
777 assert(section);
778 assert(lvalue);
779 assert(rvalue);
780 assert(data);
781
f4859fc7 782 r = route_new_static(network, filename, section_line, &n);
6ae115c1
TG
783 if (r < 0)
784 return r;
785
36423ff4 786 r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen);
6ae115c1 787 if (r < 0) {
36423ff4 788 r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen);
ae4c67a7 789 if (r < 0) {
d84ed2bd
ZJS
790 log_syntax(unit, LOG_ERR, filename, line, r,
791 "Route %s= prefix is invalid, ignoring assignment: %s",
792 lvalue, rvalue);
ae4c67a7
TG
793 return 0;
794 }
ae4c67a7 795
36423ff4
SS
796 n->family = AF_INET6;
797 } else
798 n->family = AF_INET;
799
9e7e4408 800 if (streq(lvalue, "Destination")) {
2ce40956 801 n->dst = buffer;
9e7e4408
TG
802 n->dst_prefixlen = prefixlen;
803 } else if (streq(lvalue, "Source")) {
2ce40956 804 n->src = buffer;
9e7e4408
TG
805 n->src_prefixlen = prefixlen;
806 } else
807 assert_not_reached(lvalue);
808
6ae115c1
TG
809 n = NULL;
810
811 return 0;
812}
5d8e593d
SS
813
814int config_parse_route_priority(const char *unit,
815 const char *filename,
816 unsigned line,
817 const char *section,
818 unsigned section_line,
819 const char *lvalue,
820 int ltype,
821 const char *rvalue,
822 void *data,
823 void *userdata) {
824 Network *network = userdata;
825 _cleanup_route_free_ Route *n = NULL;
1c4b1179 826 uint32_t k;
5d8e593d
SS
827 int r;
828
829 assert(filename);
830 assert(section);
831 assert(lvalue);
832 assert(rvalue);
833 assert(data);
834
f4859fc7 835 r = route_new_static(network, filename, section_line, &n);
5d8e593d
SS
836 if (r < 0)
837 return r;
838
1c4b1179
SS
839 r = safe_atou32(rvalue, &k);
840 if (r < 0) {
841 log_syntax(unit, LOG_ERR, filename, line, r,
842 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
843 return 0;
844 }
5d8e593d 845
1c4b1179 846 n->priority = k;
5d8e593d
SS
847 n = NULL;
848
849 return 0;
850}
769b56a3
TG
851
852int config_parse_route_scope(const char *unit,
853 const char *filename,
854 unsigned line,
855 const char *section,
856 unsigned section_line,
857 const char *lvalue,
858 int ltype,
859 const char *rvalue,
860 void *data,
861 void *userdata) {
862 Network *network = userdata;
863 _cleanup_route_free_ Route *n = NULL;
864 int r;
865
866 assert(filename);
867 assert(section);
868 assert(lvalue);
869 assert(rvalue);
870 assert(data);
871
f4859fc7 872 r = route_new_static(network, filename, section_line, &n);
769b56a3
TG
873 if (r < 0)
874 return r;
875
876 if (streq(rvalue, "host"))
877 n->scope = RT_SCOPE_HOST;
878 else if (streq(rvalue, "link"))
879 n->scope = RT_SCOPE_LINK;
880 else if (streq(rvalue, "global"))
881 n->scope = RT_SCOPE_UNIVERSE;
882 else {
12ca818f 883 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
884 return 0;
885 }
886
887 n = NULL;
888
889 return 0;
890}
c953b24c
SS
891
892int config_parse_route_table(const char *unit,
893 const char *filename,
894 unsigned line,
895 const char *section,
896 unsigned section_line,
897 const char *lvalue,
898 int ltype,
899 const char *rvalue,
900 void *data,
901 void *userdata) {
902 _cleanup_route_free_ Route *n = NULL;
903 Network *network = userdata;
904 uint32_t k;
905 int r;
906
907 assert(filename);
908 assert(section);
909 assert(lvalue);
910 assert(rvalue);
911 assert(data);
912
f4859fc7 913 r = route_new_static(network, filename, section_line, &n);
c953b24c
SS
914 if (r < 0)
915 return r;
916
917 r = safe_atou32(rvalue, &k);
918 if (r < 0) {
919 log_syntax(unit, LOG_ERR, filename, line, r,
920 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
921 return 0;
922 }
923
924 n->table = k;
925
926 n = NULL;
927
928 return 0;
929}
28959f7d
SS
930
931int config_parse_gateway_onlink(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 Network *network = userdata;
942 _cleanup_route_free_ Route *n = NULL;
943 int r;
944
945 assert(filename);
946 assert(section);
947 assert(lvalue);
948 assert(rvalue);
949 assert(data);
950
951 r = route_new_static(network, filename, section_line, &n);
952 if (r < 0)
953 return r;
954
955 r = parse_boolean(rvalue);
956 if (r < 0) {
957 log_syntax(unit, LOG_ERR, filename, line, r,
958 "Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue);
959 return 0;
960 }
961
ab8ee0f2 962 SET_FLAG(n->flags, RTNH_F_ONLINK, r);
b5bf6f64
SS
963 n = NULL;
964
965 return 0;
966}
967
968int config_parse_ipv6_route_preference(const char *unit,
969 const char *filename,
970 unsigned line,
971 const char *section,
972 unsigned section_line,
973 const char *lvalue,
974 int ltype,
975 const char *rvalue,
976 void *data,
977 void *userdata) {
978 Network *network = userdata;
979 _cleanup_route_free_ Route *n = NULL;
980 int r;
981
4c7bd9cf
SS
982 r = route_new_static(network, filename, section_line, &n);
983 if (r < 0)
984 return r;
985
b5bf6f64
SS
986 if (streq(rvalue, "low"))
987 n->pref = ICMPV6_ROUTER_PREF_LOW;
988 else if (streq(rvalue, "medium"))
989 n->pref = ICMPV6_ROUTER_PREF_MEDIUM;
990 else if (streq(rvalue, "high"))
991 n->pref = ICMPV6_ROUTER_PREF_HIGH;
992 else {
993 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue);
994 return 0;
995 }
28959f7d
SS
996
997 n = NULL;
998
999 return 0;
1000}
c83ecc04
SS
1001
1002int config_parse_route_protocol(const char *unit,
1003 const char *filename,
1004 unsigned line,
1005 const char *section,
1006 unsigned section_line,
1007 const char *lvalue,
1008 int ltype,
1009 const char *rvalue,
1010 void *data,
1011 void *userdata) {
1012 Network *network = userdata;
1013 _cleanup_route_free_ Route *n = NULL;
1014 int r;
1015
1016 r = route_new_static(network, filename, section_line, &n);
1017 if (r < 0)
1018 return r;
1019
1020 if (streq(rvalue, "kernel"))
1021 n->protocol = RTPROT_KERNEL;
1022 else if (streq(rvalue, "boot"))
1023 n->protocol = RTPROT_BOOT;
1024 else if (streq(rvalue, "static"))
1025 n->protocol = RTPROT_STATIC;
1026 else {
1027 r = safe_atou8(rvalue , &n->protocol);
1028 if (r < 0) {
1029 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
1030 return 0;
1031 }
1032 }
1033
1034 n = NULL;
1035
1036 return 0;
1037}
983226f3
SS
1038
1039int config_parse_route_type(const char *unit,
1040 const char *filename,
1041 unsigned line,
1042 const char *section,
1043 unsigned section_line,
1044 const char *lvalue,
1045 int ltype,
1046 const char *rvalue,
1047 void *data,
1048 void *userdata) {
1049 Network *network = userdata;
1050 _cleanup_route_free_ Route *n = NULL;
1051 int r;
1052
1053 r = route_new_static(network, filename, section_line, &n);
1054 if (r < 0)
1055 return r;
1056
1057 if (streq(rvalue, "unicast"))
1058 n->type = RTN_UNICAST;
1059 else if (streq(rvalue, "blackhole"))
1060 n->type = RTN_BLACKHOLE;
1061 else if (streq(rvalue, "unreachable"))
1062 n->type = RTN_UNREACHABLE;
1063 else if (streq(rvalue, "prohibit"))
1064 n->type = RTN_PROHIBIT;
1065 else {
1066 log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
1067 return 0;
1068 }
1069
1070 n = NULL;
1071
1072 return 0;
1073}