1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "conf-parser.h"
24 #include "event-util.h"
25 #include "in-addr-util.h"
26 #include "netlink-util.h"
27 #include "networkd-route.h"
29 #include "parse-util.h"
31 #include "string-util.h"
34 int route_new(Route
**ret
) {
35 _cleanup_route_free_ Route
*route
= NULL
;
37 route
= new0(Route
, 1);
41 route
->family
= AF_UNSPEC
;
42 route
->scope
= RT_SCOPE_UNIVERSE
;
43 route
->protocol
= RTPROT_UNSPEC
;
44 route
->table
= RT_TABLE_DEFAULT
;
45 route
->lifetime
= USEC_INFINITY
;
53 int route_new_static(Network
*network
, unsigned section
, Route
**ret
) {
54 _cleanup_route_free_ Route
*route
= NULL
;
58 route
= hashmap_get(network
->routes_by_section
,
59 UINT_TO_PTR(section
));
68 r
= route_new(&route
);
72 route
->protocol
= RTPROT_STATIC
;
73 route
->network
= network
;
75 LIST_PREPEND(routes
, network
->static_routes
, route
);
78 route
->section
= section
;
79 hashmap_put(network
->routes_by_section
,
80 UINT_TO_PTR(route
->section
), route
);
89 void route_free(Route
*route
) {
94 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
97 hashmap_remove(route
->network
->routes_by_section
,
98 UINT_TO_PTR(route
->section
));
102 set_remove(route
->link
->routes
, route
);
103 set_remove(route
->link
->routes_foreign
, route
);
106 sd_event_source_unref(route
->expire
);
111 static void route_hash_func(const void *b
, struct siphash
*state
) {
112 const Route
*route
= b
;
116 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
118 switch (route
->family
) {
121 /* Equality of routes are given by the 4-touple
122 (dst_prefix,dst_prefixlen,tos,priority,table) */
123 siphash24_compress(&route
->dst
, FAMILY_ADDRESS_SIZE(route
->family
), state
);
124 siphash24_compress(&route
->dst_prefixlen
, sizeof(route
->dst_prefixlen
), state
);
125 siphash24_compress(&route
->tos
, sizeof(route
->tos
), state
);
126 siphash24_compress(&route
->priority
, sizeof(route
->priority
), state
);
127 siphash24_compress(&route
->table
, sizeof(route
->table
), state
);
131 /* treat any other address family as AF_UNSPEC */
136 static int route_compare_func(const void *_a
, const void *_b
) {
137 const Route
*a
= _a
, *b
= _b
;
139 if (a
->family
< b
->family
)
141 if (a
->family
> b
->family
)
147 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
149 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
157 if (a
->priority
< b
->priority
)
159 if (a
->priority
> b
->priority
)
162 if (a
->table
< b
->table
)
164 if (a
->table
> b
->table
)
167 return memcmp(&a
->dst
, &b
->dst
, FAMILY_ADDRESS_SIZE(a
->family
));
169 /* treat any other address family as AF_UNSPEC */
174 static const struct hash_ops route_hash_ops
= {
175 .hash
= route_hash_func
,
176 .compare
= route_compare_func
179 int route_get(Link
*link
,
181 union in_addr_union
*dst
,
182 unsigned char dst_prefixlen
,
189 .dst_prefixlen
= dst_prefixlen
,
191 .priority
= priority
,
201 existing
= set_get(link
->routes
, &route
);
206 existing
= set_get(link
->routes_foreign
, &route
);
216 static int route_add_internal(Link
*link
, Set
**routes
,
218 union in_addr_union
*dst
,
219 unsigned char dst_prefixlen
,
222 unsigned char table
, Route
**ret
) {
223 _cleanup_route_free_ Route
*route
= NULL
;
230 r
= route_new(&route
);
234 route
->family
= family
;
236 route
->dst_prefixlen
= dst_prefixlen
;
238 route
->priority
= priority
;
239 route
->table
= table
;
241 r
= set_ensure_allocated(routes
, &route_hash_ops
);
245 r
= set_put(*routes
, route
);
259 int route_add_foreign(Link
*link
,
261 union in_addr_union
*dst
,
262 unsigned char dst_prefixlen
,
265 unsigned char table
, Route
**ret
) {
266 return route_add_internal(link
, &link
->routes_foreign
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, ret
);
269 int route_add(Link
*link
,
271 union in_addr_union
*dst
,
272 unsigned char dst_prefixlen
,
275 unsigned char table
, Route
**ret
) {
279 r
= route_get(link
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
281 /* Route does not exist, create a new one */
282 r
= route_add_internal(link
, &link
->routes
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
286 /* Take over a foreign route */
287 r
= set_ensure_allocated(&link
->routes
, &route_hash_ops
);
291 r
= set_put(link
->routes
, route
);
295 set_remove(link
->routes_foreign
, route
);
297 /* Route exists, do nothing */
307 int route_update(Route
*route
,
308 union in_addr_union
*src
,
309 unsigned char src_prefixlen
,
310 union in_addr_union
*gw
,
311 union in_addr_union
*prefsrc
,
313 unsigned char protocol
) {
320 route
->src_prefixlen
= src_prefixlen
;
322 route
->prefsrc
= *prefsrc
;
323 route
->scope
= scope
;
324 route
->protocol
= protocol
;
329 void route_drop(Route
*route
) {
335 int route_remove(Route
*route
, Link
*link
,
336 sd_netlink_message_handler_t callback
) {
337 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
341 assert(link
->manager
);
342 assert(link
->manager
->rtnl
);
343 assert(link
->ifindex
> 0);
344 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
346 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
347 RTM_DELROUTE
, route
->family
,
350 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
352 if (!in_addr_is_null(route
->family
, &route
->gw
)) {
353 if (route
->family
== AF_INET
)
354 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->gw
.in
);
355 else if (route
->family
== AF_INET6
)
356 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->gw
.in6
);
358 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
361 if (route
->dst_prefixlen
) {
362 if (route
->family
== AF_INET
)
363 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst
.in
);
364 else if (route
->family
== AF_INET6
)
365 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst
.in6
);
367 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
369 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
371 return log_error_errno(r
, "Could not set destination prefix length: %m");
374 if (route
->src_prefixlen
) {
375 if (route
->family
== AF_INET
)
376 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src
.in
);
377 else if (route
->family
== AF_INET6
)
378 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src
.in6
);
380 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
382 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
384 return log_error_errno(r
, "Could not set source prefix length: %m");
387 if (!in_addr_is_null(route
->family
, &route
->prefsrc
)) {
388 if (route
->family
== AF_INET
)
389 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in
);
390 else if (route
->family
== AF_INET6
)
391 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in6
);
393 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
396 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
398 return log_error_errno(r
, "Could not set scope: %m");
400 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
402 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
404 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
406 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
408 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
410 return log_error_errno(r
, "Could not send rtnetlink message: %m");
417 int route_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
418 Route
*route
= userdata
;
423 r
= route_remove(route
, route
->link
, NULL
);
425 log_warning_errno(r
, "Could not remove route: %m");
430 int route_configure(Route
*route
, Link
*link
,
431 sd_netlink_message_handler_t callback
) {
432 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
433 _cleanup_event_source_unref_ sd_event_source
*expire
= NULL
;
438 assert(link
->manager
);
439 assert(link
->manager
->rtnl
);
440 assert(link
->ifindex
> 0);
441 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
443 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
444 RTM_NEWROUTE
, route
->family
,
447 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
449 if (!in_addr_is_null(route
->family
, &route
->gw
)) {
450 if (route
->family
== AF_INET
)
451 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->gw
.in
);
452 else if (route
->family
== AF_INET6
)
453 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->gw
.in6
);
455 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
458 if (route
->dst_prefixlen
) {
459 if (route
->family
== AF_INET
)
460 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst
.in
);
461 else if (route
->family
== AF_INET6
)
462 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst
.in6
);
464 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
466 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
468 return log_error_errno(r
, "Could not set destination prefix length: %m");
471 if (route
->src_prefixlen
) {
472 if (route
->family
== AF_INET
)
473 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src
.in
);
474 else if (route
->family
== AF_INET6
)
475 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src
.in6
);
477 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
479 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
481 return log_error_errno(r
, "Could not set source prefix length: %m");
484 if (!in_addr_is_null(route
->family
, &route
->prefsrc
)) {
485 if (route
->family
== AF_INET
)
486 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in
);
487 else if (route
->family
== AF_INET6
)
488 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in6
);
490 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
493 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
495 return log_error_errno(r
, "Could not set scope: %m");
497 r
= sd_rtnl_message_route_set_flags(req
, route
->flags
);
499 return log_error_errno(r
, "Colud not set flags: %m");
501 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
503 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
505 r
= sd_netlink_message_append_u8(req
, RTA_PREF
, route
->pref
);
507 return log_error_errno(r
, "Could not append RTA_PREF attribute: %m");
509 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
511 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
513 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
515 return log_error_errno(r
, "Could not send rtnetlink message: %m");
519 lifetime
= route
->lifetime
;
521 r
= route_add(link
, route
->family
, &route
->dst
, route
->dst_prefixlen
, route
->tos
, route
->priority
, route
->table
, &route
);
523 return log_error_errno(r
, "Could not add route: %m");
525 /* TODO: drop expiration handling once it can be pushed into the kernel */
526 route
->lifetime
= lifetime
;
528 if (route
->lifetime
!= USEC_INFINITY
) {
529 r
= sd_event_add_time(link
->manager
->event
, &expire
, clock_boottime_or_monotonic(),
530 route
->lifetime
, 0, route_expire_handler
, route
);
532 return log_error_errno(r
, "Could not arm expiration timer: %m");
535 sd_event_source_unref(route
->expire
);
536 route
->expire
= expire
;
542 int config_parse_gateway(const char *unit
,
543 const char *filename
,
546 unsigned section_line
,
553 Network
*network
= userdata
;
554 _cleanup_route_free_ Route
*n
= NULL
;
555 union in_addr_union buffer
;
564 if (streq(section
, "Network")) {
565 /* we are not in an Route section, so treat
566 * this as the special '0' section */
570 r
= route_new_static(network
, section_line
, &n
);
574 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
576 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
587 int config_parse_preferred_src(const char *unit
,
588 const char *filename
,
591 unsigned section_line
,
598 Network
*network
= userdata
;
599 _cleanup_route_free_ Route
*n
= NULL
;
600 union in_addr_union buffer
;
609 r
= route_new_static(network
, section_line
, &n
);
613 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
615 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
616 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
627 int config_parse_destination(const char *unit
,
628 const char *filename
,
631 unsigned section_line
,
638 Network
*network
= userdata
;
639 _cleanup_route_free_ Route
*n
= NULL
;
640 const char *address
, *e
;
641 union in_addr_union buffer
;
642 unsigned char prefixlen
;
651 r
= route_new_static(network
, section_line
, &n
);
655 /* Destination|Source=address/prefixlen */
658 e
= strchr(rvalue
, '/');
660 address
= strndupa(rvalue
, e
- rvalue
);
664 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
666 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
670 if (f
!= AF_INET
&& f
!= AF_INET6
) {
671 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
677 r
= safe_atou8(e
+ 1, &prefixlen
);
679 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
694 if (streq(lvalue
, "Destination")) {
696 n
->dst_prefixlen
= prefixlen
;
697 } else if (streq(lvalue
, "Source")) {
699 n
->src_prefixlen
= prefixlen
;
701 assert_not_reached(lvalue
);
708 int config_parse_route_priority(const char *unit
,
709 const char *filename
,
712 unsigned section_line
,
718 Network
*network
= userdata
;
719 _cleanup_route_free_ Route
*n
= NULL
;
728 r
= route_new_static(network
, section_line
, &n
);
732 r
= config_parse_uint32(unit
, filename
, line
, section
,
733 section_line
, lvalue
, ltype
,
734 rvalue
, &n
->priority
, userdata
);
743 int config_parse_route_scope(const char *unit
,
744 const char *filename
,
747 unsigned section_line
,
753 Network
*network
= userdata
;
754 _cleanup_route_free_ Route
*n
= NULL
;
763 r
= route_new_static(network
, section_line
, &n
);
767 if (streq(rvalue
, "host"))
768 n
->scope
= RT_SCOPE_HOST
;
769 else if (streq(rvalue
, "link"))
770 n
->scope
= RT_SCOPE_LINK
;
771 else if (streq(rvalue
, "global"))
772 n
->scope
= RT_SCOPE_UNIVERSE
;
774 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);