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