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