]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-route.c
Merge pull request #1979 from evverx/build-install-systemd-path-completion
[thirdparty/systemd.git] / src / network / networkd-route.c
CommitLineData
f579559b
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
b5efdb8a 22#include "alloc-util.h"
f579559b 23#include "conf-parser.h"
f833694d 24#include "event-util.h"
bb7ae737 25#include "in-addr-util.h"
fc2f9534 26#include "netlink-util.h"
6bedfcbb 27#include "networkd-route.h"
fc2f9534 28#include "networkd.h"
6bedfcbb 29#include "parse-util.h"
1c8e710c 30#include "set.h"
07630cea
LP
31#include "string-util.h"
32#include "util.h"
f579559b 33
ed9e361a 34int route_new(Route **ret) {
f0213e37
TG
35 _cleanup_route_free_ Route *route = NULL;
36
37 route = new0(Route, 1);
38 if (!route)
39 return -ENOMEM;
40
41 route->family = AF_UNSPEC;
42 route->scope = RT_SCOPE_UNIVERSE;
ed9e361a 43 route->protocol = RTPROT_UNSPEC;
bb7ae737 44 route->table = RT_TABLE_DEFAULT;
f833694d 45 route->lifetime = USEC_INFINITY;
f0213e37
TG
46
47 *ret = route;
48 route = NULL;
49
50 return 0;
51}
52
f048a16b 53int route_new_static(Network *network, unsigned section, Route **ret) {
f579559b 54 _cleanup_route_free_ Route *route = NULL;
f0213e37 55 int r;
f579559b 56
6ae115c1 57 if (section) {
16aa63a0
TG
58 route = hashmap_get(network->routes_by_section,
59 UINT_TO_PTR(section));
6ae115c1
TG
60 if (route) {
61 *ret = route;
62 route = NULL;
63
64 return 0;
65 }
66 }
67
ed9e361a 68 r = route_new(&route);
f0213e37
TG
69 if (r < 0)
70 return r;
801bd9e8 71
ed9e361a 72 route->protocol = RTPROT_STATIC;
f579559b
TG
73 route->network = network;
74
3d3d4255 75 LIST_PREPEND(routes, network->static_routes, route);
f579559b 76
6ae115c1
TG
77 if (section) {
78 route->section = section;
16aa63a0
TG
79 hashmap_put(network->routes_by_section,
80 UINT_TO_PTR(route->section), route);
6ae115c1
TG
81 }
82
f579559b
TG
83 *ret = route;
84 route = NULL;
85
86 return 0;
87}
88
89void route_free(Route *route) {
90 if (!route)
91 return;
92
f048a16b 93 if (route->network) {
3d3d4255 94 LIST_REMOVE(routes, route->network->static_routes, route);
f579559b 95
f048a16b
TG
96 if (route->section)
97 hashmap_remove(route->network->routes_by_section,
16aa63a0 98 UINT_TO_PTR(route->section));
f048a16b 99 }
6ae115c1 100
1c8e710c
TG
101 if (route->link) {
102 set_remove(route->link->routes, route);
103 set_remove(route->link->routes_foreign, route);
104 }
105
f833694d
TG
106 sd_event_source_unref(route->expire);
107
f579559b
TG
108 free(route);
109}
110
bb7ae737
TG
111static void route_hash_func(const void *b, struct siphash *state) {
112 const Route *route = b;
113
114 assert(route);
115
116 siphash24_compress(&route->family, sizeof(route->family), state);
117
118 switch (route->family) {
119 case AF_INET:
120 case AF_INET6:
121 /* Equality of routes are given by the 4-touple
122 (dst_prefix,dst_prefixlen,tos,priority,table) */
2ce40956 123 siphash24_compress(&route->dst, FAMILY_ADDRESS_SIZE(route->family), state);
bb7ae737
TG
124 siphash24_compress(&route->dst_prefixlen, sizeof(route->dst_prefixlen), state);
125 siphash24_compress(&route->tos, sizeof(route->tos), state);
126 siphash24_compress(&route->priority, sizeof(route->priority), state);
127 siphash24_compress(&route->table, sizeof(route->table), state);
128
129 break;
130 default:
131 /* treat any other address family as AF_UNSPEC */
132 break;
133 }
134}
135
136static int route_compare_func(const void *_a, const void *_b) {
137 const Route *a = _a, *b = _b;
138
139 if (a->family < b->family)
140 return -1;
141 if (a->family > b->family)
142 return 1;
143
144 switch (a->family) {
145 case AF_INET:
146 case AF_INET6:
bb7ae737
TG
147 if (a->dst_prefixlen < b->dst_prefixlen)
148 return -1;
149 if (a->dst_prefixlen > b->dst_prefixlen)
150 return 1;
151
152 if (a->tos < b->tos)
153 return -1;
154 if (a->tos > b->tos)
155 return 1;
156
157 if (a->priority < b->priority)
158 return -1;
159 if (a->priority > b->priority)
160 return 1;
161
162 if (a->table < b->table)
163 return -1;
164 if (a->table > b->table)
165 return 1;
166
2ce40956 167 return memcmp(&a->dst, &b->dst, FAMILY_ADDRESS_SIZE(a->family));
bb7ae737
TG
168 default:
169 /* treat any other address family as AF_UNSPEC */
170 return 0;
171 }
172}
173
174static const struct hash_ops route_hash_ops = {
175 .hash = route_hash_func,
176 .compare = route_compare_func
177};
178
1c8e710c
TG
179int route_get(Link *link,
180 int family,
181 union in_addr_union *dst,
182 unsigned char dst_prefixlen,
183 unsigned char tos,
184 uint32_t priority,
185 unsigned char table,
186 Route **ret) {
187 Route route = {
188 .family = family,
189 .dst_prefixlen = dst_prefixlen,
190 .tos = tos,
191 .priority = priority,
192 .table = table,
193 }, *existing;
194
195 assert(link);
196 assert(dst);
197 assert(ret);
198
199 route.dst = *dst;
200
201 existing = set_get(link->routes, &route);
202 if (existing) {
203 *ret = existing;
204 return 1;
205 } else {
206 existing = set_get(link->routes_foreign, &route);
207 if (!existing)
208 return -ENOENT;
209 }
210
211 *ret = existing;
212
213 return 0;
214}
215
216static int route_add_internal(Link *link, Set **routes,
217 int family,
218 union in_addr_union *dst,
219 unsigned char dst_prefixlen,
220 unsigned char tos,
221 uint32_t priority,
222 unsigned char table, Route **ret) {
223 _cleanup_route_free_ Route *route = NULL;
224 int r;
225
226 assert(link);
227 assert(routes);
228 assert(dst);
229
230 r = route_new(&route);
231 if (r < 0)
232 return r;
233
234 route->family = family;
235 route->dst = *dst;
236 route->dst_prefixlen = dst_prefixlen;
237 route->tos = tos;
238 route->priority = priority;
239 route->table = table;
240
241 r = set_ensure_allocated(routes, &route_hash_ops);
242 if (r < 0)
243 return r;
244
245 r = set_put(*routes, route);
246 if (r < 0)
247 return r;
248
249 route->link = link;
250
251 if (ret)
252 *ret = route;
253
254 route = NULL;
255
256 return 0;
257}
258
259int route_add_foreign(Link *link,
260 int family,
261 union in_addr_union *dst,
262 unsigned char dst_prefixlen,
263 unsigned char tos,
264 uint32_t priority,
265 unsigned char table, Route **ret) {
266 return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret);
267}
268
269int route_add(Link *link,
270 int family,
271 union in_addr_union *dst,
272 unsigned char dst_prefixlen,
273 unsigned char tos,
274 uint32_t priority,
275 unsigned char table, Route **ret) {
276 Route *route;
277 int r;
278
279 r = route_get(link, family, dst, dst_prefixlen, tos, priority, table, &route);
280 if (r == -ENOENT) {
281 /* Route does not exist, create a new one */
282 r = route_add_internal(link, &link->routes, family, dst, dst_prefixlen, tos, priority, table, &route);
283 if (r < 0)
284 return r;
285 } else if (r == 0) {
286 /* Take over a foreign route */
287 r = set_ensure_allocated(&link->routes, &route_hash_ops);
288 if (r < 0)
289 return r;
290
291 r = set_put(link->routes, route);
292 if (r < 0)
293 return r;
294
295 set_remove(link->routes_foreign, route);
296 } else if (r == 1) {
297 /* Route exists, do nothing */
298 ;
299 } else
300 return r;
301
302 *ret = route;
303
304 return 0;
305}
306
307int route_update(Route *route,
308 union in_addr_union *src,
309 unsigned char src_prefixlen,
310 union in_addr_union *gw,
311 union in_addr_union *prefsrc,
312 unsigned char scope,
313 unsigned char protocol) {
314 assert(route);
315 assert(src);
316 assert(gw);
317 assert(prefsrc);
318
319 route->src = *src;
320 route->src_prefixlen = src_prefixlen;
321 route->gw = *gw;
322 route->prefsrc = *prefsrc;
323 route->scope = scope;
324 route->protocol = protocol;
325
326 return 0;
327}
328
329void route_drop(Route *route) {
330 assert(route);
331
332 route_free(route);
333}
334
91b5f997 335int route_remove(Route *route, Link *link,
1c4baffc
TG
336 sd_netlink_message_handler_t callback) {
337 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
5c1d3fc9
UTL
338 int r;
339
340 assert(link);
341 assert(link->manager);
342 assert(link->manager->rtnl);
343 assert(link->ifindex > 0);
344 assert(route->family == AF_INET || route->family == AF_INET6);
345
346 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
347 RTM_DELROUTE, route->family,
348 route->protocol);
f647962d
MS
349 if (r < 0)
350 return log_error_errno(r, "Could not create RTM_DELROUTE message: %m");
5c1d3fc9 351
2ce40956 352 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 353 if (route->family == AF_INET)
2ce40956 354 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 355 else if (route->family == AF_INET6)
2ce40956 356 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
357 if (r < 0)
358 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
5c1d3fc9
UTL
359 }
360
361 if (route->dst_prefixlen) {
362 if (route->family == AF_INET)
2ce40956 363 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
5c1d3fc9 364 else if (route->family == AF_INET6)
2ce40956 365 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
366 if (r < 0)
367 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
5c1d3fc9
UTL
368
369 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
370 if (r < 0)
371 return log_error_errno(r, "Could not set destination prefix length: %m");
5c1d3fc9
UTL
372 }
373
9e7e4408
TG
374 if (route->src_prefixlen) {
375 if (route->family == AF_INET)
2ce40956 376 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 377 else if (route->family == AF_INET6)
2ce40956 378 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
379 if (r < 0)
380 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
381
382 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
383 if (r < 0)
384 return log_error_errno(r, "Could not set source prefix length: %m");
385 }
386
2ce40956 387 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 388 if (route->family == AF_INET)
2ce40956 389 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 390 else if (route->family == AF_INET6)
2ce40956 391 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
392 if (r < 0)
393 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
394 }
395
5c1d3fc9 396 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
397 if (r < 0)
398 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 399
86655331 400 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
401 if (r < 0)
402 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 403
1c4baffc 404 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
405 if (r < 0)
406 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
5c1d3fc9 407
1c4baffc 408 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
409 if (r < 0)
410 return log_error_errno(r, "Could not send rtnetlink message: %m");
5c1d3fc9 411
563c69c6
TG
412 link_ref(link);
413
5c1d3fc9
UTL
414 return 0;
415}
416
f833694d
TG
417int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) {
418 Route *route = userdata;
419 int r;
420
421 assert(route);
422
423 r = route_remove(route, route->link, NULL);
424 if (r < 0)
425 log_warning_errno(r, "Could not remove route: %m");
426
427 return 1;
428}
429
f882c247 430int route_configure(Route *route, Link *link,
1c4baffc
TG
431 sd_netlink_message_handler_t callback) {
432 _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL;
f833694d
TG
433 _cleanup_event_source_unref_ sd_event_source *expire = NULL;
434 usec_t lifetime;
f579559b
TG
435 int r;
436
f579559b 437 assert(link);
f882c247
TG
438 assert(link->manager);
439 assert(link->manager->rtnl);
f579559b
TG
440 assert(link->ifindex > 0);
441 assert(route->family == AF_INET || route->family == AF_INET6);
442
151b9b96 443 r = sd_rtnl_message_new_route(link->manager->rtnl, &req,
28cc555d
DW
444 RTM_NEWROUTE, route->family,
445 route->protocol);
f647962d
MS
446 if (r < 0)
447 return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m");
f579559b 448
2ce40956 449 if (!in_addr_is_null(route->family, &route->gw)) {
59580681 450 if (route->family == AF_INET)
2ce40956 451 r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in);
59580681 452 else if (route->family == AF_INET6)
2ce40956 453 r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
f647962d
MS
454 if (r < 0)
455 return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
f579559b
TG
456 }
457
0a0dc69b
TG
458 if (route->dst_prefixlen) {
459 if (route->family == AF_INET)
2ce40956 460 r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in);
0a0dc69b 461 else if (route->family == AF_INET6)
2ce40956 462 r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6);
f647962d
MS
463 if (r < 0)
464 return log_error_errno(r, "Could not append RTA_DST attribute: %m");
6ae115c1 465
ae4c67a7 466 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
f647962d
MS
467 if (r < 0)
468 return log_error_errno(r, "Could not set destination prefix length: %m");
1f01fb4f
TG
469 }
470
9e7e4408
TG
471 if (route->src_prefixlen) {
472 if (route->family == AF_INET)
2ce40956 473 r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in);
9e7e4408 474 else if (route->family == AF_INET6)
2ce40956 475 r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6);
9e7e4408
TG
476 if (r < 0)
477 return log_error_errno(r, "Could not append RTA_SRC attribute: %m");
478
479 r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen);
480 if (r < 0)
481 return log_error_errno(r, "Could not set source prefix length: %m");
482 }
483
2ce40956 484 if (!in_addr_is_null(route->family, &route->prefsrc)) {
46b0c76e 485 if (route->family == AF_INET)
2ce40956 486 r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in);
46b0c76e 487 else if (route->family == AF_INET6)
2ce40956 488 r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6);
f647962d
MS
489 if (r < 0)
490 return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m");
46b0c76e
ERB
491 }
492
5c1d3fc9 493 r = sd_rtnl_message_route_set_scope(req, route->scope);
f647962d
MS
494 if (r < 0)
495 return log_error_errno(r, "Could not set scope: %m");
5c1d3fc9 496
3b015d40
TG
497 r = sd_rtnl_message_route_set_flags(req, route->flags);
498 if (r < 0)
499 return log_error_errno(r, "Colud not set flags: %m");
500
86655331 501 r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
f647962d
MS
502 if (r < 0)
503 return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m");
5c1d3fc9 504
3b015d40
TG
505 r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref);
506 if (r < 0)
507 return log_error_errno(r, "Could not append RTA_PREF attribute: %m");
508
1c4baffc 509 r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex);
f647962d
MS
510 if (r < 0)
511 return log_error_errno(r, "Could not append RTA_OIF attribute: %m");
f579559b 512
1c4baffc 513 r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
f647962d
MS
514 if (r < 0)
515 return log_error_errno(r, "Could not send rtnetlink message: %m");
f579559b 516
563c69c6
TG
517 link_ref(link);
518
f833694d
TG
519 lifetime = route->lifetime;
520
521 r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route);
1c8e710c
TG
522 if (r < 0)
523 return log_error_errno(r, "Could not add route: %m");
524
f833694d
TG
525 /* TODO: drop expiration handling once it can be pushed into the kernel */
526 route->lifetime = lifetime;
527
528 if (route->lifetime != USEC_INFINITY) {
529 r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(),
530 route->lifetime, 0, route_expire_handler, route);
531 if (r < 0)
532 return log_error_errno(r, "Could not arm expiration timer: %m");
533 }
534
535 sd_event_source_unref(route->expire);
536 route->expire = expire;
537 expire = NULL;
538
f579559b
TG
539 return 0;
540}
541
542int config_parse_gateway(const char *unit,
543 const char *filename,
544 unsigned line,
545 const char *section,
71a61510 546 unsigned section_line,
f579559b
TG
547 const char *lvalue,
548 int ltype,
549 const char *rvalue,
550 void *data,
551 void *userdata) {
44e7b949 552
6ae115c1 553 Network *network = userdata;
f579559b 554 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
555 union in_addr_union buffer;
556 int r, f;
f579559b
TG
557
558 assert(filename);
6ae115c1 559 assert(section);
f579559b
TG
560 assert(lvalue);
561 assert(rvalue);
562 assert(data);
563
92fe133a
TG
564 if (streq(section, "Network")) {
565 /* we are not in an Route section, so treat
566 * this as the special '0' section */
567 section_line = 0;
568 }
569
f048a16b 570 r = route_new_static(network, section_line, &n);
f579559b
TG
571 if (r < 0)
572 return r;
573
44e7b949 574 r = in_addr_from_string_auto(rvalue, &f, &buffer);
f579559b 575 if (r < 0) {
12ca818f 576 log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue);
f579559b
TG
577 return 0;
578 }
579
44e7b949 580 n->family = f;
2ce40956 581 n->gw = buffer;
f579559b
TG
582 n = NULL;
583
584 return 0;
585}
6ae115c1 586
0d07e595
JK
587int config_parse_preferred_src(const char *unit,
588 const char *filename,
589 unsigned line,
590 const char *section,
591 unsigned section_line,
592 const char *lvalue,
593 int ltype,
594 const char *rvalue,
595 void *data,
596 void *userdata) {
597
598 Network *network = userdata;
599 _cleanup_route_free_ Route *n = NULL;
600 union in_addr_union buffer;
601 int r, f;
602
603 assert(filename);
604 assert(section);
605 assert(lvalue);
606 assert(rvalue);
607 assert(data);
608
609 r = route_new_static(network, section_line, &n);
610 if (r < 0)
611 return r;
612
613 r = in_addr_from_string_auto(rvalue, &f, &buffer);
614 if (r < 0) {
615 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
616 "Preferred source is invalid, ignoring assignment: %s", rvalue);
617 return 0;
618 }
619
620 n->family = f;
2ce40956 621 n->prefsrc = buffer;
0d07e595
JK
622 n = NULL;
623
624 return 0;
625}
626
6ae115c1
TG
627int config_parse_destination(const char *unit,
628 const char *filename,
629 unsigned line,
630 const char *section,
631 unsigned section_line,
632 const char *lvalue,
633 int ltype,
634 const char *rvalue,
635 void *data,
636 void *userdata) {
44e7b949 637
6ae115c1
TG
638 Network *network = userdata;
639 _cleanup_route_free_ Route *n = NULL;
44e7b949
LP
640 const char *address, *e;
641 union in_addr_union buffer;
9e7e4408 642 unsigned char prefixlen;
44e7b949 643 int r, f;
6ae115c1
TG
644
645 assert(filename);
646 assert(section);
647 assert(lvalue);
648 assert(rvalue);
649 assert(data);
650
f048a16b 651 r = route_new_static(network, section_line, &n);
6ae115c1
TG
652 if (r < 0)
653 return r;
654
9e7e4408 655 /* Destination|Source=address/prefixlen */
6ae115c1 656
ae4c67a7 657 /* address */
6ae115c1 658 e = strchr(rvalue, '/');
44e7b949
LP
659 if (e)
660 address = strndupa(rvalue, e - rvalue);
661 else
662 address = rvalue;
6ae115c1 663
44e7b949 664 r = in_addr_from_string_auto(address, &f, &buffer);
6ae115c1 665 if (r < 0) {
12ca818f 666 log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address);
6ae115c1
TG
667 return 0;
668 }
669
935c0d26 670 if (f != AF_INET && f != AF_INET6) {
12ca818f 671 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address);
935c0d26
TG
672 return 0;
673 }
674
ae4c67a7
TG
675 /* prefixlen */
676 if (e) {
9e7e4408 677 r = safe_atou8(e + 1, &prefixlen);
ae4c67a7 678 if (r < 0) {
12ca818f 679 log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1);
ae4c67a7
TG
680 return 0;
681 }
ae4c67a7 682 } else {
935c0d26 683 switch (f) {
ae4c67a7 684 case AF_INET:
9e7e4408 685 prefixlen = 32;
ae4c67a7
TG
686 break;
687 case AF_INET6:
9e7e4408 688 prefixlen = 128;
ae4c67a7
TG
689 break;
690 }
691 }
692
44e7b949 693 n->family = f;
9e7e4408 694 if (streq(lvalue, "Destination")) {
2ce40956 695 n->dst = buffer;
9e7e4408
TG
696 n->dst_prefixlen = prefixlen;
697 } else if (streq(lvalue, "Source")) {
2ce40956 698 n->src = buffer;
9e7e4408
TG
699 n->src_prefixlen = prefixlen;
700 } else
701 assert_not_reached(lvalue);
702
6ae115c1
TG
703 n = NULL;
704
705 return 0;
706}
5d8e593d
SS
707
708int config_parse_route_priority(const char *unit,
709 const char *filename,
710 unsigned line,
711 const char *section,
712 unsigned section_line,
713 const char *lvalue,
714 int ltype,
715 const char *rvalue,
716 void *data,
717 void *userdata) {
718 Network *network = userdata;
719 _cleanup_route_free_ Route *n = NULL;
5d8e593d
SS
720 int r;
721
722 assert(filename);
723 assert(section);
724 assert(lvalue);
725 assert(rvalue);
726 assert(data);
727
728 r = route_new_static(network, section_line, &n);
729 if (r < 0)
730 return r;
731
86655331
TG
732 r = config_parse_uint32(unit, filename, line, section,
733 section_line, lvalue, ltype,
734 rvalue, &n->priority, userdata);
5d8e593d
SS
735 if (r < 0)
736 return r;
737
738 n = NULL;
739
740 return 0;
741}
769b56a3
TG
742
743int config_parse_route_scope(const char *unit,
744 const char *filename,
745 unsigned line,
746 const char *section,
747 unsigned section_line,
748 const char *lvalue,
749 int ltype,
750 const char *rvalue,
751 void *data,
752 void *userdata) {
753 Network *network = userdata;
754 _cleanup_route_free_ Route *n = NULL;
755 int r;
756
757 assert(filename);
758 assert(section);
759 assert(lvalue);
760 assert(rvalue);
761 assert(data);
762
763 r = route_new_static(network, section_line, &n);
764 if (r < 0)
765 return r;
766
767 if (streq(rvalue, "host"))
768 n->scope = RT_SCOPE_HOST;
769 else if (streq(rvalue, "link"))
770 n->scope = RT_SCOPE_LINK;
771 else if (streq(rvalue, "global"))
772 n->scope = RT_SCOPE_UNIVERSE;
773 else {
12ca818f 774 log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
769b56a3
TG
775 return 0;
776 }
777
778 n = NULL;
779
780 return 0;
781}