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 <linux/icmpv6.h>
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"
30 #include "string-util.h"
31 #include "sysctl-util.h"
34 #define ROUTES_DEFAULT_MAX_PER_FAMILY 4096U
36 static unsigned routes_max(void) {
37 static thread_local
unsigned cached
= 0;
39 _cleanup_free_
char *s4
= NULL
, *s6
= NULL
;
40 unsigned val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
45 if (sysctl_read("net/ipv4/route/max_size", &s4
) >= 0) {
47 if (safe_atou(s4
, &val4
) >= 0 &&
49 /* This is the default "no limit" value in the kernel */
50 val4
= ROUTES_DEFAULT_MAX_PER_FAMILY
;
53 if (sysctl_read("net/ipv6/route/max_size", &s6
) >= 0) {
55 (void) safe_atou(s6
, &val6
);
58 cached
= MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val4
) +
59 MAX(ROUTES_DEFAULT_MAX_PER_FAMILY
, val6
);
63 int route_new(Route
**ret
) {
64 _cleanup_route_free_ Route
*route
= NULL
;
66 route
= new0(Route
, 1);
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
;
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
;
89 assert(!!filename
== (section_line
> 0));
92 r
= network_config_section_new(filename
, section_line
, &n
);
96 route
= hashmap_get(network
->routes_by_section
, n
);
105 if (network
->n_static_routes
>= routes_max())
108 r
= route_new(&route
);
112 route
->protocol
= RTPROT_STATIC
;
118 r
= hashmap_put(network
->routes_by_section
, route
->section
, route
);
123 route
->network
= network
;
124 LIST_PREPEND(routes
, network
->static_routes
, route
);
125 network
->n_static_routes
++;
133 void route_free(Route
*route
) {
137 if (route
->network
) {
138 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
140 assert(route
->network
->n_static_routes
> 0);
141 route
->network
->n_static_routes
--;
144 hashmap_remove(route
->network
->routes_by_section
, route
->section
);
147 network_config_section_free(route
->section
);
150 set_remove(route
->link
->routes
, route
);
151 set_remove(route
->link
->routes_foreign
, route
);
154 sd_event_source_unref(route
->expire
);
159 static void route_hash_func(const void *b
, struct siphash
*state
) {
160 const Route
*route
= b
;
164 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
166 switch (route
->family
) {
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
);
179 /* treat any other address family as AF_UNSPEC */
184 static int route_compare_func(const void *_a
, const void *_b
) {
185 const Route
*a
= _a
, *b
= _b
;
187 if (a
->family
< b
->family
)
189 if (a
->family
> b
->family
)
195 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
197 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
205 if (a
->priority
< b
->priority
)
207 if (a
->priority
> b
->priority
)
210 if (a
->table
< b
->table
)
212 if (a
->table
> b
->table
)
215 return memcmp(&a
->dst
, &b
->dst
, FAMILY_ADDRESS_SIZE(a
->family
));
217 /* treat any other address family as AF_UNSPEC */
222 static const struct hash_ops route_hash_ops
= {
223 .hash
= route_hash_func
,
224 .compare
= route_compare_func
227 int route_get(Link
*link
,
229 const union in_addr_union
*dst
,
230 unsigned char dst_prefixlen
,
236 Route route
, *existing
;
244 .dst_prefixlen
= dst_prefixlen
,
246 .priority
= priority
,
250 existing
= set_get(link
->routes
, &route
);
257 existing
= set_get(link
->routes_foreign
, &route
);
267 static int route_add_internal(
271 const union in_addr_union
*dst
,
272 unsigned char dst_prefixlen
,
278 _cleanup_route_free_ Route
*route
= NULL
;
285 r
= route_new(&route
);
289 route
->family
= family
;
291 route
->dst_prefixlen
= dst_prefixlen
;
293 route
->priority
= priority
;
294 route
->table
= table
;
296 r
= set_ensure_allocated(routes
, &route_hash_ops
);
300 r
= set_put(*routes
, route
);
314 int route_add_foreign(
317 const union in_addr_union
*dst
,
318 unsigned char dst_prefixlen
,
324 return route_add_internal(link
, &link
->routes_foreign
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, ret
);
330 const union in_addr_union
*dst
,
331 unsigned char dst_prefixlen
,
340 r
= route_get(link
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
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
);
347 /* Take over a foreign route */
348 r
= set_ensure_allocated(&link
->routes
, &route_hash_ops
);
352 r
= set_put(link
->routes
, route
);
356 set_remove(link
->routes_foreign
, route
);
358 /* Route exists, do nothing */
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
,
375 unsigned char protocol
) {
383 route
->src_prefixlen
= src_prefixlen
;
385 route
->prefsrc
= *prefsrc
;
386 route
->scope
= scope
;
387 route
->protocol
= protocol
;
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
;
398 assert(link
->manager
);
399 assert(link
->manager
->rtnl
);
400 assert(link
->ifindex
> 0);
401 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
403 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
404 RTM_DELROUTE
, route
->family
,
407 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
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
);
415 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
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
);
424 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
426 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
428 return log_error_errno(r
, "Could not set destination prefix length: %m");
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
);
437 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
439 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
441 return log_error_errno(r
, "Could not set source prefix length: %m");
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
);
450 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
453 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
455 return log_error_errno(r
, "Could not set scope: %m");
457 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
459 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
461 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
463 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
465 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
467 return log_error_errno(r
, "Could not send rtnetlink message: %m");
474 static int route_expire_callback(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
475 Link
*link
= userdata
;
481 assert(link
->ifname
);
483 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
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");
493 int route_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
494 Route
*route
= userdata
;
499 r
= route_remove(route
, route
->link
, route_expire_callback
);
501 log_warning_errno(r
, "Could not remove route: %m");
511 sd_netlink_message_handler_t callback
) {
513 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
514 _cleanup_(sd_event_source_unrefp
) sd_event_source
*expire
= NULL
;
519 assert(link
->manager
);
520 assert(link
->manager
->rtnl
);
521 assert(link
->ifindex
> 0);
522 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
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())
528 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
529 RTM_NEWROUTE
, route
->family
,
532 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
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
);
540 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
542 r
= sd_rtnl_message_route_set_family(req
, route
->family
);
544 return log_error_errno(r
, "Could not set route family: %m");
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
);
553 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
555 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
557 return log_error_errno(r
, "Could not set destination prefix length: %m");
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
);
566 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
568 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
570 return log_error_errno(r
, "Could not set source prefix length: %m");
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
);
579 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
582 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
584 return log_error_errno(r
, "Could not set scope: %m");
586 r
= sd_rtnl_message_route_set_flags(req
, route
->flags
);
588 return log_error_errno(r
, "Could not set flags: %m");
590 if (route
->table
!= RT_TABLE_MAIN
) {
591 if (route
->table
< 256) {
592 r
= sd_rtnl_message_route_set_table(req
, route
->table
);
594 return log_error_errno(r
, "Could not set route table: %m");
596 r
= sd_rtnl_message_route_set_table(req
, RT_TABLE_UNSPEC
);
598 return log_error_errno(r
, "Could not set route table: %m");
600 /* Table attribute to allow more than 256. */
601 r
= sd_netlink_message_append_data(req
, RTA_TABLE
, &route
->table
, sizeof(route
->table
));
603 return log_error_errno(r
, "Could not append RTA_TABLE attribute: %m");
607 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
609 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
611 r
= sd_netlink_message_append_u8(req
, RTA_PREF
, route
->pref
);
613 return log_error_errno(r
, "Could not append RTA_PREF attribute: %m");
615 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
617 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
619 r
= sd_netlink_message_open_container(req
, RTA_METRICS
);
621 return log_error_errno(r
, "Could not append RTA_METRICS attribute: %m");
623 if (route
->mtu
> 0) {
624 r
= sd_netlink_message_append_u32(req
, RTAX_MTU
, route
->mtu
);
626 return log_error_errno(r
, "Could not append RTAX_MTU attribute: %m");
629 r
= sd_netlink_message_close_container(req
);
631 return log_error_errno(r
, "Could not append RTA_METRICS attribute: %m");
633 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
635 return log_error_errno(r
, "Could not send rtnetlink message: %m");
639 lifetime
= route
->lifetime
;
641 r
= route_add(link
, route
->family
, &route
->dst
, route
->dst_prefixlen
, route
->tos
, route
->priority
, route
->table
, &route
);
643 return log_error_errno(r
, "Could not add route: %m");
645 /* TODO: drop expiration handling once it can be pushed into the kernel */
646 route
->lifetime
= lifetime
;
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
);
652 return log_error_errno(r
, "Could not arm expiration timer: %m");
655 sd_event_source_unref(route
->expire
);
656 route
->expire
= expire
;
662 int config_parse_gateway(const char *unit
,
663 const char *filename
,
666 unsigned section_line
,
673 Network
*network
= userdata
;
674 _cleanup_route_free_ Route
*n
= NULL
;
675 union in_addr_union buffer
;
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
);
689 r
= route_new_static(network
, filename
, section_line
, &n
);
694 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
696 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
707 int config_parse_preferred_src(const char *unit
,
708 const char *filename
,
711 unsigned section_line
,
718 Network
*network
= userdata
;
719 _cleanup_route_free_ Route
*n
= NULL
;
720 union in_addr_union buffer
;
729 r
= route_new_static(network
, filename
, section_line
, &n
);
733 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
735 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
736 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
747 int config_parse_destination(const char *unit
,
748 const char *filename
,
751 unsigned section_line
,
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
;
771 r
= route_new_static(network
, filename
, section_line
, &n
);
775 r
= in_addr_prefix_from_string(rvalue
, AF_INET
, &buffer
, &prefixlen
);
777 r
= in_addr_prefix_from_string(rvalue
, AF_INET6
, &buffer
, &prefixlen
);
779 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route source or destination prefix is invalid, ignoring assignment: %s", rvalue
);
783 n
->family
= AF_INET6
;
787 if (streq(lvalue
, "Destination")) {
789 n
->dst_prefixlen
= prefixlen
;
790 } else if (streq(lvalue
, "Source")) {
792 n
->src_prefixlen
= prefixlen
;
794 assert_not_reached(lvalue
);
801 int config_parse_route_priority(const char *unit
,
802 const char *filename
,
805 unsigned section_line
,
811 Network
*network
= userdata
;
812 _cleanup_route_free_ Route
*n
= NULL
;
822 r
= route_new_static(network
, filename
, section_line
, &n
);
826 r
= safe_atou32(rvalue
, &k
);
828 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
829 "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue
);
839 int config_parse_route_scope(const char *unit
,
840 const char *filename
,
843 unsigned section_line
,
849 Network
*network
= userdata
;
850 _cleanup_route_free_ Route
*n
= NULL
;
859 r
= route_new_static(network
, filename
, section_line
, &n
);
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
;
870 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);
879 int config_parse_route_table(const char *unit
,
880 const char *filename
,
883 unsigned section_line
,
889 _cleanup_route_free_ Route
*n
= NULL
;
890 Network
*network
= userdata
;
900 r
= route_new_static(network
, filename
, section_line
, &n
);
904 r
= safe_atou32(rvalue
, &k
);
906 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
907 "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue
);
918 int config_parse_gateway_onlink(const char *unit
,
919 const char *filename
,
922 unsigned section_line
,
928 Network
*network
= userdata
;
929 _cleanup_route_free_ Route
*n
= NULL
;
938 r
= route_new_static(network
, filename
, section_line
, &n
);
942 r
= parse_boolean(rvalue
);
944 log_syntax(unit
, LOG_ERR
, filename
, line
, r
,
945 "Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue
);
949 SET_FLAG(n
->flags
, RTNH_F_ONLINK
, r
);
955 int config_parse_ipv6_route_preference(const char *unit
,
956 const char *filename
,
959 unsigned section_line
,
965 Network
*network
= userdata
;
966 _cleanup_route_free_ Route
*n
= NULL
;
969 r
= route_new_static(network
, filename
, section_line
, &n
);
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
;
980 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route preference: %s", rvalue
);
989 int config_parse_route_protocol(const char *unit
,
990 const char *filename
,
993 unsigned section_line
,
999 Network
*network
= userdata
;
1000 _cleanup_route_free_ Route
*n
= NULL
;
1003 r
= route_new_static(network
, filename
, section_line
, &n
);
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
;
1014 r
= safe_atou8(rvalue
, &n
->protocol
);
1016 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue
);