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