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