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