]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
virt: swap order of cpuid and dmi again, but properly detect oracle (#5355)
[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
d6fceaf1
SS
608 r = sd_netlink_message_open_container(req, RTA_METRICS);
609 if (r < 0)
610 return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
611
612 if (route->mtu > 0) {
613 r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu);
614 if (r < 0)
615 return log_error_errno(r, "Could not append RTAX_MTU attribute: %m");
616 }
617
618 r = sd_netlink_message_close_container(req);
619 if (r < 0)
620 return log_error_errno(r, "Could not append RTA_METRICS attribute: %m");
621
1c4baffc 622 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
623 if (r < 0)
624 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 625
563c69c6
TG
626 link_ref(link);
627
f833694d
TG
628 lifetime = route->lifetime;
629
630 r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
1c8e710c
TG
631 if (r < 0)
632 return log_error_errno(r, "Could not add route: %m");
633
f833694d
TG
634 /* TODO: drop expiration handling once it can be pushed into the kernel */
635 route->lifetime = lifetime;
636
637 if (route->lifetime != USEC_INFINITY) {
638 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
639 route->lifetime, 0, route_expire_handler, route);
640 if (r < 0)
641 return log_error_errno(r, "Could not arm expiration timer: %m");
642 }
643
644 sd_event_source_unref(route->expire);
645 route->expire = expire;
646 expire = NULL;
647
f579559b
TG
648 return 0;
649}
650
651int config_parse_gateway(const char *unit,
652 const char *filename,
653 unsigned line,
654 const char *section,
71a61510 655 unsigned section_line,
f579559b
TG
656 const char *lvalue,
657 int ltype,
658 const char *rvalue,
659 void *data,
660 void *userdata) {
44e7b949 661
6ae115c1 662 Network *network = userdata;
f579559b 663 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
664 union in_addr_union buffer;
665 int r, f;
f579559b
TG
666
667 assert(filename);
6ae115c1 668 assert(section);
f579559b
TG
669 assert(lvalue);
670 assert(rvalue);
671 assert(data);
672
92fe133a
TG
673 if (streq(section, "Network")) {
674 /* we are not in an Route section, so treat
675 * this as the special '0' section */
676 section_line = 0;
677 }
678
f048a16b 679 r = route_new_static(network, section_line, &n);
f579559b
TG
680 if (r < 0)
681 return r;
682
44e7b949 683 r = in_addr_from_string_auto(rvalue, &f, &buffer);
f579559b 684 if (r < 0) {
12ca818f 685 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
686 return 0;
687 }
688
44e7b949 689 n->family = f;
2ce40956 690 n->gw = buffer;
f579559b
TG
691 n = NULL;
692
693 return 0;
694}
6ae115c1 695
0d07e595
JK
696int config_parse_preferred_src(const char *unit,
697 const char *filename,
698 unsigned line,
699 const char *section,
700 unsigned section_line,
701 const char *lvalue,
702 int ltype,
703 const char *rvalue,
704 void *data,
705 void *userdata) {
706
707 Network *network = userdata;
708 _cleanup_route_free_ Route *n = NULL;
709 union in_addr_union buffer;
710 int r, f;
711
712 assert(filename);
713 assert(section);
714 assert(lvalue);
715 assert(rvalue);
716 assert(data);
717
718 r = route_new_static(network, section_line, &n);
719 if (r < 0)
720 return r;
721
722 r = in_addr_from_string_auto(rvalue, &f, &buffer);
723 if (r < 0) {
724 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
725 "Preferred source is invalid, ignoring assignment: %s", rvalue);
726 return 0;
727 }
728
729 n->family = f;
2ce40956 730 n->prefsrc = buffer;
0d07e595
JK
731 n = NULL;
732
733 return 0;
734}
735
6ae115c1
TG
736int config_parse_destination(const char *unit,
737 const char *filename,
738 unsigned line,
739 const char *section,
740 unsigned section_line,
741 const char *lvalue,
742 int ltype,
743 const char *rvalue,
744 void *data,
745 void *userdata) {
44e7b949 746
6ae115c1
TG
747 Network *network = userdata;
748 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
749 const char *address, *e;
750 union in_addr_union buffer;
9e7e4408 751 unsigned char prefixlen;
44e7b949 752 int r, f;
6ae115c1
TG
753
754 assert(filename);
755 assert(section);
756 assert(lvalue);
757 assert(rvalue);
758 assert(data);
759
f048a16b 760 r = route_new_static(network, section_line, &n);
6ae115c1
TG
761 if (r < 0)
762 return r;
763
9e7e4408 764 /* Destination|Source=address/prefixlen */
6ae115c1 765
ae4c67a7 766 /* address */
6ae115c1 767 e = strchr(rvalue, '/');
44e7b949
LP
768 if (e)
769 address = strndupa(rvalue, e - rvalue);
770 else
771 address = rvalue;
6ae115c1 772
44e7b949 773 r = in_addr_from_string_auto(address, &f, &buffer);
6ae115c1 774 if (r < 0) {
12ca818f 775 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
6ae115c1
TG
776 return 0;
777 }
778
935c0d26 779 if (f != AF_INET && f != AF_INET6) {
12ca818f 780 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
935c0d26
TG
781 return 0;
782 }
783
ae4c67a7
TG
784 /* prefixlen */
785 if (e) {
9e7e4408 786 r = safe_atou8(e + 1, &prefixlen);
ae4c67a7 787 if (r < 0) {
12ca818f 788 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
ae4c67a7
TG
789 return 0;
790 }
ae4c67a7 791 } else {
935c0d26 792 switch (f) {
ae4c67a7 793 case AF_INET:
9e7e4408 794 prefixlen = 32;
ae4c67a7
TG
795 break;
796 case AF_INET6:
9e7e4408 797 prefixlen = 128;
ae4c67a7
TG
798 break;
799 }
800 }
801
44e7b949 802 n->family = f;
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
838 r = route_new_static(network, section_line, &n);
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
875 r = route_new_static(network, section_line, &n);
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
916 r = route_new_static(network, section_line, &n);
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}