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