2 This file is part of systemd.
4 Copyright 2013 Tom Gundersen <teg@jklm.no>
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.
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.
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/>.
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"
28 #include "string-util.h"
29 #include "sysctl-util.h"
32 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
34 static unsigned routes_max(void) {
35 static thread_local
unsigned cached
= 0;
37 _cleanup_free_
char *s4
= NULL
, *s6
= NULL
;
38 unsigned val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
43 if (sysctl_read("net/ipv4/route/max_size", &s4
) >= 0) {
45 if (safe_atou(s4
, &val4
) >= 0 &&
47 /* This is the default "no limit" value in the kernel */
48 val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
51 if (sysctl_read("net/ipv6/route/max_size", &s6
) >= 0) {
53 (void) safe_atou(s6
, &val6
);
56 cached
= MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val4
) +
57 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
);
61 int route_new(Route
**ret
) {
62 _cleanup_route_free_ Route
*route
= NULL
;
64 route
= new0(Route
, 1);
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
;
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
;
87 assert(!!filename
== (section_line
> 0));
90 r
= network_config_section_new(filename
, section_line
, &n
);
94 route
= hashmap_get(network
->routes_by_section
, n
);
103 if (network
->n_static_routes
>= routes_max())
106 r
= route_new(&route
);
110 route
->protocol
= RTPROT_STATIC
;
116 r
= hashmap_put(network
->routes_by_section
, n
, route
);
121 route
->network
= network
;
122 LIST_PREPEND(routes
, network
->static_routes
, route
);
123 network
->n_static_routes
++;
131 void route_free(Route
*route
) {
135 if (route
->network
) {
136 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
138 assert(route
->network
->n_static_routes
> 0);
139 route
->network
->n_static_routes
--;
142 hashmap_remove(route
->network
->routes_by_section
, route
->section
);
145 network_config_section_free(route
->section
);
148 set_remove(route
->link
->routes
, route
);
149 set_remove(route
->link
->routes_foreign
, route
);
152 sd_event_source_unref(route
->expire
);
157 static void route_hash_func(const void *b
, struct siphash
*state
) {
158 const Route
*route
= b
;
162 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
164 switch (route
->family
) {
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
);
177 /* treat any other address family as AF_UNSPEC */
182 static int route_compare_func(const void *_a
, const void *_b
) {
183 const Route
*a
= _a
, *b
= _b
;
185 if (a
->family
< b
->family
)
187 if (a
->family
> b
->family
)
193 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
195 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
203 if (a
->priority
< b
->priority
)
205 if (a
->priority
> b
->priority
)
208 if (a
->table
< b
->table
)
210 if (a
->table
> b
->table
)
213 return memcmp(&a
->dst
, &b
->dst
, FAMILY_ADDRESS_SIZE(a
->family
));
215 /* treat any other address family as AF_UNSPEC */
220 static const struct hash_ops route_hash_ops
= {
221 .hash
= route_hash_func
,
222 .compare
= route_compare_func
225 int route_get(Link
*link
,
227 const union in_addr_union
*dst
,
228 unsigned char dst_prefixlen
,
234 Route route
, *existing
;
242 .dst_prefixlen
= dst_prefixlen
,
244 .priority
= priority
,
248 existing
= set_get(link
->routes
, &route
);
255 existing
= set_get(link
->routes_foreign
, &route
);
265 static int route_add_internal(
269 const union in_addr_union
*dst
,
270 unsigned char dst_prefixlen
,
276 _cleanup_route_free_ Route
*route
= NULL
;
283 r
= route_new(&route
);
287 route
->family
= family
;
289 route
->dst_prefixlen
= dst_prefixlen
;
291 route
->priority
= priority
;
292 route
->table
= table
;
294 r
= set_ensure_allocated(routes
, &route_hash_ops
);
298 r
= set_put(*routes
, route
);
312 int route_add_foreign(
315 const union in_addr_union
*dst
,
316 unsigned char dst_prefixlen
,
322 return route_add_internal(link
, &link
->routes_foreign
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, ret
);
328 const union in_addr_union
*dst
,
329 unsigned char dst_prefixlen
,
338 r
= route_get(link
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
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
);
345 /* Take over a foreign route */
346 r
= set_ensure_allocated(&link
->routes
, &route_hash_ops
);
350 r
= set_put(link
->routes
, route
);
354 set_remove(link
->routes_foreign
, route
);
356 /* Route exists, do nothing */
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
,
373 unsigned char protocol
) {
381 route
->src_prefixlen
= src_prefixlen
;
383 route
->prefsrc
= *prefsrc
;
384 route
->scope
= scope
;
385 route
->protocol
= protocol
;
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
;
396 assert(link
->manager
);
397 assert(link
->manager
->rtnl
);
398 assert(link
->ifindex
> 0);
399 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
401 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
402 RTM_DELROUTE
, route
->family
,
405 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
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
);
413 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
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
);
422 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
424 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
426 return log_error_errno(r
, "Could not set destination prefix length: %m");
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
);
435 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
437 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
439 return log_error_errno(r
, "Could not set source prefix length: %m");
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
);
448 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
451 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
453 return log_error_errno(r
, "Could not set scope: %m");
455 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
457 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
459 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
461 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
463 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
465 return log_error_errno(r
, "Could not send rtnetlink message: %m");
472 static int route_expire_callback(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
473 Link
*link
= userdata
;
479 assert(link
->ifname
);
481 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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");
491 int route_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
492 Route
*route
= userdata
;
497 r
= route_remove(route
, route
->link
, route_expire_callback
);
499 log_warning_errno(r
, "Could not remove route: %m");
509 sd_netlink_message_handler_t callback
) {
511 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
512 _cleanup_(sd_event_source_unrefp
) sd_event_source
*expire
= NULL
;
517 assert(link
->manager
);
518 assert(link
->manager
->rtnl
);
519 assert(link
->ifindex
> 0);
520 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
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())
526 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
527 RTM_NEWROUTE
, route
->family
,
530 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
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
);
538 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
540 r
= sd_rtnl_message_route_set_family(req
, route
->family
);
542 return log_error_errno(r
, "Could not set route family: %m");
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
);
551 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
553 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
555 return log_error_errno(r
, "Could not set destination prefix length: %m");
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
);
564 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
566 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
568 return log_error_errno(r
, "Could not set source prefix length: %m");
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
);
577 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
580 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
582 return log_error_errno(r
, "Could not set scope: %m");
584 r
= sd_rtnl_message_route_set_flags(req
, route
->flags
);
586 return log_error_errno(r
, "Could not set flags: %m");
588 if (route
->table
!= RT_TABLE_MAIN
) {
589 if (route
->table
< 256) {
590 r
= sd_rtnl_message_route_set_table(req
, route
->table
);
592 return log_error_errno(r
, "Could not set route table: %m");
594 r
= sd_rtnl_message_route_set_table(req
, RT_TABLE_UNSPEC
);
596 return log_error_errno(r
, "Could not set route table: %m");
598 /* Table attribute to allow more than 256. */
599 r
= sd_netlink_message_append_data(req
, RTA_TABLE
, &route
->table
, sizeof(route
->table
));
601 return log_error_errno(r
, "Could not append RTA_TABLE attribute: %m");
605 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
607 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
609 r
= sd_netlink_message_append_u8(req
, RTA_PREF
, route
->pref
);
611 return log_error_errno(r
, "Could not append RTA_PREF attribute: %m");
613 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
615 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
617 r
= sd_netlink_message_open_container(req
, RTA_METRICS
);
619 return log_error_errno(r
, "Could not append RTA_METRICS attribute: %m");
621 if (route
->mtu
> 0) {
622 r
= sd_netlink_message_append_u32(req
, RTAX_MTU
, route
->mtu
);
624 return log_error_errno(r
, "Could not append RTAX_MTU attribute: %m");
627 r
= sd_netlink_message_close_container(req
);
629 return log_error_errno(r
, "Could not append RTA_METRICS attribute: %m");
631 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
633 return log_error_errno(r
, "Could not send rtnetlink message: %m");
637 lifetime
= route
->lifetime
;
639 r
= route_add(link
, route
->family
, &route
->dst
, route
->dst_prefixlen
, route
->tos
, route
->priority
, route
->table
, &route
);
641 return log_error_errno(r
, "Could not add route: %m");
643 /* TODO: drop expiration handling once it can be pushed into the kernel */
644 route
->lifetime
= lifetime
;
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
);
650 return log_error_errno(r
, "Could not arm expiration timer: %m");
653 sd_event_source_unref(route
->expire
);
654 route
->expire
= expire
;
660 int config_parse_gateway(const char *unit
,
661 const char *filename
,
664 unsigned section_line
,
671 Network
*network
= userdata
;
672 _cleanup_route_free_ Route
*n
= NULL
;
673 union in_addr_union buffer
;
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
);
687 r
= route_new_static(network
, filename
, section_line
, &n
);
692 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
694 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
705 int config_parse_preferred_src(const char *unit
,
706 const char *filename
,
709 unsigned section_line
,
716 Network
*network
= userdata
;
717 _cleanup_route_free_ Route
*n
= NULL
;
718 union in_addr_union buffer
;
727 r
= route_new_static(network
, filename
, section_line
, &n
);
731 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
733 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
734 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
745 int config_parse_destination(const char *unit
,
746 const char *filename
,
749 unsigned section_line
,
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
;
769 r
= route_new_static(network
, filename
, section_line
, &n
);
773 /* Destination|Source=address/prefixlen */
776 e
= strchr(rvalue
, '/');
778 address
= strndupa(rvalue
, e
- rvalue
);
782 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
784 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
788 if (f
!= AF_INET
&& f
!= AF_INET6
) {
789 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
795 r
= safe_atou8(e
+ 1, &prefixlen
);
797 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
812 if (streq(lvalue
, "Destination")) {
814 n
->dst_prefixlen
= prefixlen
;
815 } else if (streq(lvalue
, "Source")) {
817 n
->src_prefixlen
= prefixlen
;
819 assert_not_reached(lvalue
);
826 int config_parse_route_priority(const char *unit
,
827 const char *filename
,
830 unsigned section_line
,
836 Network
*network
= userdata
;
837 _cleanup_route_free_ Route
*n
= NULL
;
847 r
= route_new_static(network
, filename
, section_line
, &n
);
851 r
= safe_atou32(rvalue
, &k
);
853 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
854 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue
);
864 int config_parse_route_scope(const char *unit
,
865 const char *filename
,
868 unsigned section_line
,
874 Network
*network
= userdata
;
875 _cleanup_route_free_ Route
*n
= NULL
;
884 r
= route_new_static(network
, filename
, section_line
, &n
);
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
;
895 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);
904 int config_parse_route_table(const char *unit
,
905 const char *filename
,
908 unsigned section_line
,
914 _cleanup_route_free_ Route
*n
= NULL
;
915 Network
*network
= userdata
;
925 r
= route_new_static(network
, filename
, section_line
, &n
);
929 r
= safe_atou32(rvalue
, &k
);
931 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
932 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue
);