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"
29 #include "string-util.h"
32 int route_new(Route
**ret
) {
33 _cleanup_route_free_ Route
*route
= NULL
;
35 route
= new0(Route
, 1);
39 route
->family
= AF_UNSPEC
;
40 route
->scope
= RT_SCOPE_UNIVERSE
;
41 route
->protocol
= RTPROT_UNSPEC
;
42 route
->table
= RT_TABLE_DEFAULT
;
50 int route_new_static(Network
*network
, unsigned section
, Route
**ret
) {
51 _cleanup_route_free_ Route
*route
= NULL
;
55 route
= hashmap_get(network
->routes_by_section
,
56 UINT_TO_PTR(section
));
65 r
= route_new(&route
);
69 route
->protocol
= RTPROT_STATIC
;
70 route
->network
= network
;
72 LIST_PREPEND(routes
, network
->static_routes
, route
);
75 route
->section
= section
;
76 hashmap_put(network
->routes_by_section
,
77 UINT_TO_PTR(route
->section
), route
);
86 void route_free(Route
*route
) {
91 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
94 hashmap_remove(route
->network
->routes_by_section
,
95 UINT_TO_PTR(route
->section
));
101 static void route_hash_func(const void *b
, struct siphash
*state
) {
102 const Route
*route
= b
;
106 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
108 switch (route
->family
) {
111 /* Equality of routes are given by the 4-touple
112 (dst_prefix,dst_prefixlen,tos,priority,table) */
113 siphash24_compress(&route
->dst_addr
, FAMILY_ADDRESS_SIZE(route
->family
), state
);
114 siphash24_compress(&route
->dst_prefixlen
, sizeof(route
->dst_prefixlen
), state
);
115 siphash24_compress(&route
->tos
, sizeof(route
->tos
), state
);
116 siphash24_compress(&route
->priority
, sizeof(route
->priority
), state
);
117 siphash24_compress(&route
->table
, sizeof(route
->table
), state
);
121 /* treat any other address family as AF_UNSPEC */
126 static int route_compare_func(const void *_a
, const void *_b
) {
127 const Route
*a
= _a
, *b
= _b
;
129 if (a
->family
< b
->family
)
131 if (a
->family
> b
->family
)
137 //TODO: check IPv6 routes
138 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
140 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
148 if (a
->priority
< b
->priority
)
150 if (a
->priority
> b
->priority
)
153 if (a
->table
< b
->table
)
155 if (a
->table
> b
->table
)
158 return memcmp(&a
->dst_addr
, &b
->dst_addr
, FAMILY_ADDRESS_SIZE(a
->family
));
160 /* treat any other address family as AF_UNSPEC */
165 static const struct hash_ops route_hash_ops
= {
166 .hash
= route_hash_func
,
167 .compare
= route_compare_func
170 int route_remove(Route
*route
, Link
*link
,
171 sd_netlink_message_handler_t callback
) {
172 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
176 assert(link
->manager
);
177 assert(link
->manager
->rtnl
);
178 assert(link
->ifindex
> 0);
179 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
181 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
182 RTM_DELROUTE
, route
->family
,
185 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
187 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
188 if (route
->family
== AF_INET
)
189 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
190 else if (route
->family
== AF_INET6
)
191 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
193 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
196 if (route
->dst_prefixlen
) {
197 if (route
->family
== AF_INET
)
198 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
199 else if (route
->family
== AF_INET6
)
200 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
202 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
204 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
206 return log_error_errno(r
, "Could not set destination prefix length: %m");
209 if (route
->src_prefixlen
) {
210 if (route
->family
== AF_INET
)
211 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
212 else if (route
->family
== AF_INET6
)
213 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
215 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
217 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
219 return log_error_errno(r
, "Could not set source prefix length: %m");
222 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
223 if (route
->family
== AF_INET
)
224 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
225 else if (route
->family
== AF_INET6
)
226 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
228 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
231 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
233 return log_error_errno(r
, "Could not set scope: %m");
235 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
237 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
239 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
241 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
243 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
245 return log_error_errno(r
, "Could not send rtnetlink message: %m");
252 int route_configure(Route
*route
, Link
*link
,
253 sd_netlink_message_handler_t callback
) {
254 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
258 assert(link
->manager
);
259 assert(link
->manager
->rtnl
);
260 assert(link
->ifindex
> 0);
261 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
263 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
264 RTM_NEWROUTE
, route
->family
,
267 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
269 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
270 if (route
->family
== AF_INET
)
271 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
272 else if (route
->family
== AF_INET6
)
273 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
275 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
278 if (route
->dst_prefixlen
) {
279 if (route
->family
== AF_INET
)
280 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
281 else if (route
->family
== AF_INET6
)
282 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
284 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
286 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
288 return log_error_errno(r
, "Could not set destination prefix length: %m");
291 if (route
->src_prefixlen
) {
292 if (route
->family
== AF_INET
)
293 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
294 else if (route
->family
== AF_INET6
)
295 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
297 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
299 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
301 return log_error_errno(r
, "Could not set source prefix length: %m");
304 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
305 if (route
->family
== AF_INET
)
306 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
307 else if (route
->family
== AF_INET6
)
308 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
310 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
313 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
315 return log_error_errno(r
, "Could not set scope: %m");
317 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
319 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
321 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
323 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
325 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
327 return log_error_errno(r
, "Could not send rtnetlink message: %m");
334 int config_parse_gateway(const char *unit
,
335 const char *filename
,
338 unsigned section_line
,
345 Network
*network
= userdata
;
346 _cleanup_route_free_ Route
*n
= NULL
;
347 union in_addr_union buffer
;
356 if (streq(section
, "Network")) {
357 /* we are not in an Route section, so treat
358 * this as the special '0' section */
362 r
= route_new_static(network
, section_line
, &n
);
366 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
368 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
379 int config_parse_preferred_src(const char *unit
,
380 const char *filename
,
383 unsigned section_line
,
390 Network
*network
= userdata
;
391 _cleanup_route_free_ Route
*n
= NULL
;
392 union in_addr_union buffer
;
401 r
= route_new_static(network
, section_line
, &n
);
405 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
407 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
408 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
413 n
->prefsrc_addr
= buffer
;
419 int config_parse_destination(const char *unit
,
420 const char *filename
,
423 unsigned section_line
,
430 Network
*network
= userdata
;
431 _cleanup_route_free_ Route
*n
= NULL
;
432 const char *address
, *e
;
433 union in_addr_union buffer
;
434 unsigned char prefixlen
;
443 r
= route_new_static(network
, section_line
, &n
);
447 /* Destination|Source=address/prefixlen */
450 e
= strchr(rvalue
, '/');
452 address
= strndupa(rvalue
, e
- rvalue
);
456 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
458 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
462 if (f
!= AF_INET
&& f
!= AF_INET6
) {
463 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
469 r
= safe_atou8(e
+ 1, &prefixlen
);
471 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
486 if (streq(lvalue
, "Destination")) {
487 n
->dst_addr
= buffer
;
488 n
->dst_prefixlen
= prefixlen
;
489 } else if (streq(lvalue
, "Source")) {
490 n
->src_addr
= buffer
;
491 n
->src_prefixlen
= prefixlen
;
493 assert_not_reached(lvalue
);
500 int config_parse_route_priority(const char *unit
,
501 const char *filename
,
504 unsigned section_line
,
510 Network
*network
= userdata
;
511 _cleanup_route_free_ Route
*n
= NULL
;
520 r
= route_new_static(network
, section_line
, &n
);
524 r
= config_parse_unsigned(unit
, filename
, line
, section
,
525 section_line
, lvalue
, ltype
,
526 rvalue
, &n
->metrics
, userdata
);
535 int config_parse_route_scope(const char *unit
,
536 const char *filename
,
539 unsigned section_line
,
545 Network
*network
= userdata
;
546 _cleanup_route_free_ Route
*n
= NULL
;
555 r
= route_new_static(network
, section_line
, &n
);
559 if (streq(rvalue
, "host"))
560 n
->scope
= RT_SCOPE_HOST
;
561 else if (streq(rvalue
, "link"))
562 n
->scope
= RT_SCOPE_LINK
;
563 else if (streq(rvalue
, "global"))
564 n
->scope
= RT_SCOPE_UNIVERSE
;
566 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);