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 "conf-parser.h"
23 #include "in-addr-util.h"
24 #include "netlink-util.h"
25 #include "networkd-route.h"
27 #include "parse-util.h"
28 #include "string-util.h"
31 int route_new(Route
**ret
) {
32 _cleanup_route_free_ Route
*route
= NULL
;
34 route
= new0(Route
, 1);
38 route
->family
= AF_UNSPEC
;
39 route
->scope
= RT_SCOPE_UNIVERSE
;
40 route
->protocol
= RTPROT_UNSPEC
;
41 route
->table
= RT_TABLE_DEFAULT
;
49 int route_new_static(Network
*network
, unsigned section
, Route
**ret
) {
50 _cleanup_route_free_ Route
*route
= NULL
;
54 route
= hashmap_get(network
->routes_by_section
,
55 UINT_TO_PTR(section
));
64 r
= route_new(&route
);
68 route
->protocol
= RTPROT_STATIC
;
69 route
->network
= network
;
71 LIST_PREPEND(routes
, network
->static_routes
, route
);
74 route
->section
= section
;
75 hashmap_put(network
->routes_by_section
,
76 UINT_TO_PTR(route
->section
), route
);
85 void route_free(Route
*route
) {
90 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
93 hashmap_remove(route
->network
->routes_by_section
,
94 UINT_TO_PTR(route
->section
));
100 static void route_hash_func(const void *b
, struct siphash
*state
) {
101 const Route
*route
= b
;
105 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
107 switch (route
->family
) {
110 /* Equality of routes are given by the 4-touple
111 (dst_prefix,dst_prefixlen,tos,priority,table) */
112 siphash24_compress(&route
->dst_addr
, FAMILY_ADDRESS_SIZE(route
->family
), state
);
113 siphash24_compress(&route
->dst_prefixlen
, sizeof(route
->dst_prefixlen
), state
);
114 siphash24_compress(&route
->tos
, sizeof(route
->tos
), state
);
115 siphash24_compress(&route
->priority
, sizeof(route
->priority
), state
);
116 siphash24_compress(&route
->table
, sizeof(route
->table
), state
);
120 /* treat any other address family as AF_UNSPEC */
125 static int route_compare_func(const void *_a
, const void *_b
) {
126 const Route
*a
= _a
, *b
= _b
;
128 if (a
->family
< b
->family
)
130 if (a
->family
> b
->family
)
136 //TODO: check IPv6 routes
137 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
139 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
147 if (a
->priority
< b
->priority
)
149 if (a
->priority
> b
->priority
)
152 if (a
->table
< b
->table
)
154 if (a
->table
> b
->table
)
157 return memcmp(&a
->dst_addr
, &b
->dst_addr
, FAMILY_ADDRESS_SIZE(a
->family
));
159 /* treat any other address family as AF_UNSPEC */
164 static const struct hash_ops route_hash_ops
= {
165 .hash
= route_hash_func
,
166 .compare
= route_compare_func
169 int route_remove(Route
*route
, Link
*link
,
170 sd_netlink_message_handler_t callback
) {
171 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
175 assert(link
->manager
);
176 assert(link
->manager
->rtnl
);
177 assert(link
->ifindex
> 0);
178 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
180 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
181 RTM_DELROUTE
, route
->family
,
184 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
186 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
187 if (route
->family
== AF_INET
)
188 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
189 else if (route
->family
== AF_INET6
)
190 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
192 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
195 if (route
->dst_prefixlen
) {
196 if (route
->family
== AF_INET
)
197 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
198 else if (route
->family
== AF_INET6
)
199 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
201 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
203 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
205 return log_error_errno(r
, "Could not set destination prefix length: %m");
208 if (route
->src_prefixlen
) {
209 if (route
->family
== AF_INET
)
210 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
211 else if (route
->family
== AF_INET6
)
212 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
214 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
216 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
218 return log_error_errno(r
, "Could not set source prefix length: %m");
221 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
222 if (route
->family
== AF_INET
)
223 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
224 else if (route
->family
== AF_INET6
)
225 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
227 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
230 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
232 return log_error_errno(r
, "Could not set scope: %m");
234 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
236 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
238 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
240 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
242 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
244 return log_error_errno(r
, "Could not send rtnetlink message: %m");
251 int route_configure(Route
*route
, Link
*link
,
252 sd_netlink_message_handler_t callback
) {
253 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
257 assert(link
->manager
);
258 assert(link
->manager
->rtnl
);
259 assert(link
->ifindex
> 0);
260 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
262 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
263 RTM_NEWROUTE
, route
->family
,
266 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
268 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
269 if (route
->family
== AF_INET
)
270 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
271 else if (route
->family
== AF_INET6
)
272 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
274 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
277 if (route
->dst_prefixlen
) {
278 if (route
->family
== AF_INET
)
279 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
280 else if (route
->family
== AF_INET6
)
281 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
283 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
285 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
287 return log_error_errno(r
, "Could not set destination prefix length: %m");
290 if (route
->src_prefixlen
) {
291 if (route
->family
== AF_INET
)
292 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
293 else if (route
->family
== AF_INET6
)
294 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
296 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
298 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
300 return log_error_errno(r
, "Could not set source prefix length: %m");
303 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
304 if (route
->family
== AF_INET
)
305 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
306 else if (route
->family
== AF_INET6
)
307 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
309 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
312 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
314 return log_error_errno(r
, "Could not set scope: %m");
316 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
318 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
320 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
322 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
324 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
326 return log_error_errno(r
, "Could not send rtnetlink message: %m");
333 int config_parse_gateway(const char *unit
,
334 const char *filename
,
337 unsigned section_line
,
344 Network
*network
= userdata
;
345 _cleanup_route_free_ Route
*n
= NULL
;
346 union in_addr_union buffer
;
355 if (streq(section
, "Network")) {
356 /* we are not in an Route section, so treat
357 * this as the special '0' section */
361 r
= route_new_static(network
, section_line
, &n
);
365 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
367 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
378 int config_parse_preferred_src(const char *unit
,
379 const char *filename
,
382 unsigned section_line
,
389 Network
*network
= userdata
;
390 _cleanup_route_free_ Route
*n
= NULL
;
391 union in_addr_union buffer
;
400 r
= route_new_static(network
, section_line
, &n
);
404 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
406 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
407 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
412 n
->prefsrc_addr
= buffer
;
418 int config_parse_destination(const char *unit
,
419 const char *filename
,
422 unsigned section_line
,
429 Network
*network
= userdata
;
430 _cleanup_route_free_ Route
*n
= NULL
;
431 const char *address
, *e
;
432 union in_addr_union buffer
;
433 unsigned char prefixlen
;
442 r
= route_new_static(network
, section_line
, &n
);
446 /* Destination|Source=address/prefixlen */
449 e
= strchr(rvalue
, '/');
451 address
= strndupa(rvalue
, e
- rvalue
);
455 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
457 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
461 if (f
!= AF_INET
&& f
!= AF_INET6
) {
462 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
468 r
= safe_atou8(e
+ 1, &prefixlen
);
470 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
485 if (streq(lvalue
, "Destination")) {
486 n
->dst_addr
= buffer
;
487 n
->dst_prefixlen
= prefixlen
;
488 } else if (streq(lvalue
, "Source")) {
489 n
->src_addr
= buffer
;
490 n
->src_prefixlen
= prefixlen
;
492 assert_not_reached(lvalue
);
499 int config_parse_route_priority(const char *unit
,
500 const char *filename
,
503 unsigned section_line
,
509 Network
*network
= userdata
;
510 _cleanup_route_free_ Route
*n
= NULL
;
519 r
= route_new_static(network
, section_line
, &n
);
523 r
= config_parse_unsigned(unit
, filename
, line
, section
,
524 section_line
, lvalue
, ltype
,
525 rvalue
, &n
->metrics
, userdata
);
534 int config_parse_route_scope(const char *unit
,
535 const char *filename
,
538 unsigned section_line
,
544 Network
*network
= userdata
;
545 _cleanup_route_free_ Route
*n
= NULL
;
554 r
= route_new_static(network
, section_line
, &n
);
558 if (streq(rvalue
, "host"))
559 n
->scope
= RT_SCOPE_HOST
;
560 else if (streq(rvalue
, "link"))
561 n
->scope
= RT_SCOPE_LINK
;
562 else if (streq(rvalue
, "global"))
563 n
->scope
= RT_SCOPE_UNIVERSE
;
565 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);