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