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