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 "in-addr-util.h"
25 #include "netlink-util.h"
26 #include "networkd-route.h"
28 #include "parse-util.h"
30 #include "string-util.h"
33 int route_new(Route
**ret
) {
34 _cleanup_route_free_ Route
*route
= NULL
;
36 route
= new0(Route
, 1);
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
;
52 int route_new_static(Network
*network
, unsigned section
, Route
**ret
) {
53 _cleanup_route_free_ Route
*route
= NULL
;
57 route
= hashmap_get(network
->routes_by_section
,
58 UINT_TO_PTR(section
));
67 r
= route_new(&route
);
71 route
->protocol
= RTPROT_STATIC
;
72 route
->network
= network
;
74 LIST_PREPEND(routes
, network
->static_routes
, route
);
77 route
->section
= section
;
78 hashmap_put(network
->routes_by_section
,
79 UINT_TO_PTR(route
->section
), route
);
88 void route_free(Route
*route
) {
93 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
96 hashmap_remove(route
->network
->routes_by_section
,
97 UINT_TO_PTR(route
->section
));
101 set_remove(route
->link
->routes
, route
);
102 set_remove(route
->link
->routes_foreign
, route
);
105 sd_event_source_unref(route
->expire
);
110 static void route_hash_func(const void *b
, struct siphash
*state
) {
111 const Route
*route
= b
;
115 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
117 switch (route
->family
) {
120 /* Equality of routes are given by the 4-touple
121 (dst_prefix,dst_prefixlen,tos,priority,table) */
122 siphash24_compress(&route
->dst
, FAMILY_ADDRESS_SIZE(route
->family
), state
);
123 siphash24_compress(&route
->dst_prefixlen
, sizeof(route
->dst_prefixlen
), state
);
124 siphash24_compress(&route
->tos
, sizeof(route
->tos
), state
);
125 siphash24_compress(&route
->priority
, sizeof(route
->priority
), state
);
126 siphash24_compress(&route
->table
, sizeof(route
->table
), state
);
130 /* treat any other address family as AF_UNSPEC */
135 static int route_compare_func(const void *_a
, const void *_b
) {
136 const Route
*a
= _a
, *b
= _b
;
138 if (a
->family
< b
->family
)
140 if (a
->family
> b
->family
)
146 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
148 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
156 if (a
->priority
< b
->priority
)
158 if (a
->priority
> b
->priority
)
161 if (a
->table
< b
->table
)
163 if (a
->table
> b
->table
)
166 return memcmp(&a
->dst
, &b
->dst
, FAMILY_ADDRESS_SIZE(a
->family
));
168 /* treat any other address family as AF_UNSPEC */
173 static const struct hash_ops route_hash_ops
= {
174 .hash
= route_hash_func
,
175 .compare
= route_compare_func
178 int route_get(Link
*link
,
180 union in_addr_union
*dst
,
181 unsigned char dst_prefixlen
,
188 .dst_prefixlen
= dst_prefixlen
,
190 .priority
= priority
,
200 existing
= set_get(link
->routes
, &route
);
205 existing
= set_get(link
->routes_foreign
, &route
);
215 static int route_add_internal(Link
*link
, Set
**routes
,
217 union in_addr_union
*dst
,
218 unsigned char dst_prefixlen
,
221 unsigned char table
, Route
**ret
) {
222 _cleanup_route_free_ Route
*route
= NULL
;
229 r
= route_new(&route
);
233 route
->family
= family
;
235 route
->dst_prefixlen
= dst_prefixlen
;
237 route
->priority
= priority
;
238 route
->table
= table
;
240 r
= set_ensure_allocated(routes
, &route_hash_ops
);
244 r
= set_put(*routes
, route
);
258 int route_add_foreign(Link
*link
,
260 union in_addr_union
*dst
,
261 unsigned char dst_prefixlen
,
264 unsigned char table
, Route
**ret
) {
265 return route_add_internal(link
, &link
->routes_foreign
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, ret
);
268 int route_add(Link
*link
,
270 union in_addr_union
*dst
,
271 unsigned char dst_prefixlen
,
274 unsigned char table
, Route
**ret
) {
278 r
= route_get(link
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
280 /* Route does not exist, create a new one */
281 r
= route_add_internal(link
, &link
->routes
, family
, dst
, dst_prefixlen
, tos
, priority
, table
, &route
);
285 /* Take over a foreign route */
286 r
= set_ensure_allocated(&link
->routes
, &route_hash_ops
);
290 r
= set_put(link
->routes
, route
);
294 set_remove(link
->routes_foreign
, route
);
296 /* Route exists, do nothing */
306 int route_update(Route
*route
,
307 union in_addr_union
*src
,
308 unsigned char src_prefixlen
,
309 union in_addr_union
*gw
,
310 union in_addr_union
*prefsrc
,
312 unsigned char protocol
) {
319 route
->src_prefixlen
= src_prefixlen
;
321 route
->prefsrc
= *prefsrc
;
322 route
->scope
= scope
;
323 route
->protocol
= protocol
;
328 void route_drop(Route
*route
) {
334 int route_remove(Route
*route
, Link
*link
,
335 sd_netlink_message_handler_t callback
) {
336 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
340 assert(link
->manager
);
341 assert(link
->manager
->rtnl
);
342 assert(link
->ifindex
> 0);
343 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
345 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
346 RTM_DELROUTE
, route
->family
,
349 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
351 if (!in_addr_is_null(route
->family
, &route
->gw
)) {
352 if (route
->family
== AF_INET
)
353 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->gw
.in
);
354 else if (route
->family
== AF_INET6
)
355 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->gw
.in6
);
357 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
360 if (route
->dst_prefixlen
) {
361 if (route
->family
== AF_INET
)
362 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst
.in
);
363 else if (route
->family
== AF_INET6
)
364 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst
.in6
);
366 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
368 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
370 return log_error_errno(r
, "Could not set destination prefix length: %m");
373 if (route
->src_prefixlen
) {
374 if (route
->family
== AF_INET
)
375 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src
.in
);
376 else if (route
->family
== AF_INET6
)
377 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src
.in6
);
379 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
381 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
383 return log_error_errno(r
, "Could not set source prefix length: %m");
386 if (!in_addr_is_null(route
->family
, &route
->prefsrc
)) {
387 if (route
->family
== AF_INET
)
388 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in
);
389 else if (route
->family
== AF_INET6
)
390 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in6
);
392 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
395 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
397 return log_error_errno(r
, "Could not set scope: %m");
399 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
401 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
403 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
405 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
407 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
409 return log_error_errno(r
, "Could not send rtnetlink message: %m");
416 int route_expire_handler(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
417 Route
*route
= userdata
;
422 r
= route_remove(route
, route
->link
, NULL
);
424 log_warning_errno(r
, "Could not remove route: %m");
429 int route_configure(Route
*route
, Link
*link
,
430 sd_netlink_message_handler_t callback
) {
431 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*req
= NULL
;
432 _cleanup_(sd_event_source_unrefp
) sd_event_source
*expire
= NULL
;
437 assert(link
->manager
);
438 assert(link
->manager
->rtnl
);
439 assert(link
->ifindex
> 0);
440 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
442 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
443 RTM_NEWROUTE
, route
->family
,
446 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
448 if (!in_addr_is_null(route
->family
, &route
->gw
)) {
449 if (route
->family
== AF_INET
)
450 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->gw
.in
);
451 else if (route
->family
== AF_INET6
)
452 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->gw
.in6
);
454 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
457 if (route
->dst_prefixlen
) {
458 if (route
->family
== AF_INET
)
459 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst
.in
);
460 else if (route
->family
== AF_INET6
)
461 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst
.in6
);
463 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
465 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
467 return log_error_errno(r
, "Could not set destination prefix length: %m");
470 if (route
->src_prefixlen
) {
471 if (route
->family
== AF_INET
)
472 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src
.in
);
473 else if (route
->family
== AF_INET6
)
474 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src
.in6
);
476 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
478 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
480 return log_error_errno(r
, "Could not set source prefix length: %m");
483 if (!in_addr_is_null(route
->family
, &route
->prefsrc
)) {
484 if (route
->family
== AF_INET
)
485 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in
);
486 else if (route
->family
== AF_INET6
)
487 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc
.in6
);
489 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
492 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
494 return log_error_errno(r
, "Could not set scope: %m");
496 r
= sd_rtnl_message_route_set_flags(req
, route
->flags
);
498 return log_error_errno(r
, "Colud not set flags: %m");
500 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->priority
);
502 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
504 r
= sd_netlink_message_append_u8(req
, RTA_PREF
, route
->pref
);
506 return log_error_errno(r
, "Could not append RTA_PREF attribute: %m");
508 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
510 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
512 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
514 return log_error_errno(r
, "Could not send rtnetlink message: %m");
518 lifetime
= route
->lifetime
;
520 r
= route_add(link
, route
->family
, &route
->dst
, route
->dst_prefixlen
, route
->tos
, route
->priority
, route
->table
, &route
);
522 return log_error_errno(r
, "Could not add route: %m");
524 /* TODO: drop expiration handling once it can be pushed into the kernel */
525 route
->lifetime
= lifetime
;
527 if (route
->lifetime
!= USEC_INFINITY
) {
528 r
= sd_event_add_time(link
->manager
->event
, &expire
, clock_boottime_or_monotonic(),
529 route
->lifetime
, 0, route_expire_handler
, route
);
531 return log_error_errno(r
, "Could not arm expiration timer: %m");
534 sd_event_source_unref(route
->expire
);
535 route
->expire
= expire
;
541 int config_parse_gateway(const char *unit
,
542 const char *filename
,
545 unsigned section_line
,
552 Network
*network
= userdata
;
553 _cleanup_route_free_ Route
*n
= NULL
;
554 union in_addr_union buffer
;
563 if (streq(section
, "Network")) {
564 /* we are not in an Route section, so treat
565 * this as the special '0' section */
569 r
= route_new_static(network
, section_line
, &n
);
573 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
575 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
586 int config_parse_preferred_src(const char *unit
,
587 const char *filename
,
590 unsigned section_line
,
597 Network
*network
= userdata
;
598 _cleanup_route_free_ Route
*n
= NULL
;
599 union in_addr_union buffer
;
608 r
= route_new_static(network
, section_line
, &n
);
612 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
614 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
615 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
626 int config_parse_destination(const char *unit
,
627 const char *filename
,
630 unsigned section_line
,
637 Network
*network
= userdata
;
638 _cleanup_route_free_ Route
*n
= NULL
;
639 const char *address
, *e
;
640 union in_addr_union buffer
;
641 unsigned char prefixlen
;
650 r
= route_new_static(network
, section_line
, &n
);
654 /* Destination|Source=address/prefixlen */
657 e
= strchr(rvalue
, '/');
659 address
= strndupa(rvalue
, e
- rvalue
);
663 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
665 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
669 if (f
!= AF_INET
&& f
!= AF_INET6
) {
670 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
676 r
= safe_atou8(e
+ 1, &prefixlen
);
678 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
693 if (streq(lvalue
, "Destination")) {
695 n
->dst_prefixlen
= prefixlen
;
696 } else if (streq(lvalue
, "Source")) {
698 n
->src_prefixlen
= prefixlen
;
700 assert_not_reached(lvalue
);
707 int config_parse_route_priority(const char *unit
,
708 const char *filename
,
711 unsigned section_line
,
717 Network
*network
= userdata
;
718 _cleanup_route_free_ Route
*n
= NULL
;
727 r
= route_new_static(network
, section_line
, &n
);
731 r
= config_parse_uint32(unit
, filename
, line
, section
,
732 section_line
, lvalue
, ltype
,
733 rvalue
, &n
->priority
, userdata
);
742 int config_parse_route_scope(const char *unit
,
743 const char *filename
,
746 unsigned section_line
,
752 Network
*network
= userdata
;
753 _cleanup_route_free_ Route
*n
= NULL
;
762 r
= route_new_static(network
, section_line
, &n
);
766 if (streq(rvalue
, "host"))
767 n
->scope
= RT_SCOPE_HOST
;
768 else if (streq(rvalue
, "link"))
769 n
->scope
= RT_SCOPE_LINK
;
770 else if (streq(rvalue
, "global"))
771 n
->scope
= RT_SCOPE_UNIVERSE
;
773 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);