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"
26 #include "string-util.h"
28 #include "networkd-route.h"
30 int route_new(Route
**ret
) {
31 _cleanup_route_free_ Route
*route
= NULL
;
33 route
= new0(Route
, 1);
37 route
->family
= AF_UNSPEC
;
38 route
->scope
= RT_SCOPE_UNIVERSE
;
39 route
->protocol
= RTPROT_UNSPEC
;
40 route
->table
= RT_TABLE_DEFAULT
;
48 int route_new_static(Network
*network
, unsigned section
, Route
**ret
) {
49 _cleanup_route_free_ Route
*route
= NULL
;
53 route
= hashmap_get(network
->routes_by_section
,
54 UINT_TO_PTR(section
));
63 r
= route_new(&route
);
67 route
->protocol
= RTPROT_STATIC
;
68 route
->network
= network
;
70 LIST_PREPEND(routes
, network
->static_routes
, route
);
73 route
->section
= section
;
74 hashmap_put(network
->routes_by_section
,
75 UINT_TO_PTR(route
->section
), route
);
84 void route_free(Route
*route
) {
89 LIST_REMOVE(routes
, route
->network
->static_routes
, route
);
92 hashmap_remove(route
->network
->routes_by_section
,
93 UINT_TO_PTR(route
->section
));
99 static void route_hash_func(const void *b
, struct siphash
*state
) {
100 const Route
*route
= b
;
104 siphash24_compress(&route
->family
, sizeof(route
->family
), state
);
106 switch (route
->family
) {
109 /* Equality of routes are given by the 4-touple
110 (dst_prefix,dst_prefixlen,tos,priority,table) */
111 siphash24_compress(&route
->dst_addr
, FAMILY_ADDRESS_SIZE(route
->family
), state
);
112 siphash24_compress(&route
->dst_prefixlen
, sizeof(route
->dst_prefixlen
), state
);
113 siphash24_compress(&route
->tos
, sizeof(route
->tos
), state
);
114 siphash24_compress(&route
->priority
, sizeof(route
->priority
), state
);
115 siphash24_compress(&route
->table
, sizeof(route
->table
), state
);
119 /* treat any other address family as AF_UNSPEC */
124 static int route_compare_func(const void *_a
, const void *_b
) {
125 const Route
*a
= _a
, *b
= _b
;
127 if (a
->family
< b
->family
)
129 if (a
->family
> b
->family
)
135 //TODO: check IPv6 routes
136 if (a
->dst_prefixlen
< b
->dst_prefixlen
)
138 if (a
->dst_prefixlen
> b
->dst_prefixlen
)
146 if (a
->priority
< b
->priority
)
148 if (a
->priority
> b
->priority
)
151 if (a
->table
< b
->table
)
153 if (a
->table
> b
->table
)
156 return memcmp(&a
->dst_addr
, &b
->dst_addr
, FAMILY_ADDRESS_SIZE(a
->family
));
158 /* treat any other address family as AF_UNSPEC */
163 static const struct hash_ops route_hash_ops
= {
164 .hash
= route_hash_func
,
165 .compare
= route_compare_func
168 int route_remove(Route
*route
, Link
*link
,
169 sd_netlink_message_handler_t callback
) {
170 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
174 assert(link
->manager
);
175 assert(link
->manager
->rtnl
);
176 assert(link
->ifindex
> 0);
177 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
179 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
180 RTM_DELROUTE
, route
->family
,
183 return log_error_errno(r
, "Could not create RTM_DELROUTE message: %m");
185 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
186 if (route
->family
== AF_INET
)
187 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
188 else if (route
->family
== AF_INET6
)
189 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
191 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
194 if (route
->dst_prefixlen
) {
195 if (route
->family
== AF_INET
)
196 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
197 else if (route
->family
== AF_INET6
)
198 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
200 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
202 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
204 return log_error_errno(r
, "Could not set destination prefix length: %m");
207 if (route
->src_prefixlen
) {
208 if (route
->family
== AF_INET
)
209 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
210 else if (route
->family
== AF_INET6
)
211 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
213 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
215 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
217 return log_error_errno(r
, "Could not set source prefix length: %m");
220 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
221 if (route
->family
== AF_INET
)
222 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
223 else if (route
->family
== AF_INET6
)
224 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
226 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
229 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
231 return log_error_errno(r
, "Could not set scope: %m");
233 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
235 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
237 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
239 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
241 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
243 return log_error_errno(r
, "Could not send rtnetlink message: %m");
250 int route_configure(Route
*route
, Link
*link
,
251 sd_netlink_message_handler_t callback
) {
252 _cleanup_netlink_message_unref_ sd_netlink_message
*req
= NULL
;
256 assert(link
->manager
);
257 assert(link
->manager
->rtnl
);
258 assert(link
->ifindex
> 0);
259 assert(route
->family
== AF_INET
|| route
->family
== AF_INET6
);
261 r
= sd_rtnl_message_new_route(link
->manager
->rtnl
, &req
,
262 RTM_NEWROUTE
, route
->family
,
265 return log_error_errno(r
, "Could not create RTM_NEWROUTE message: %m");
267 if (!in_addr_is_null(route
->family
, &route
->in_addr
)) {
268 if (route
->family
== AF_INET
)
269 r
= sd_netlink_message_append_in_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in
);
270 else if (route
->family
== AF_INET6
)
271 r
= sd_netlink_message_append_in6_addr(req
, RTA_GATEWAY
, &route
->in_addr
.in6
);
273 return log_error_errno(r
, "Could not append RTA_GATEWAY attribute: %m");
276 if (route
->dst_prefixlen
) {
277 if (route
->family
== AF_INET
)
278 r
= sd_netlink_message_append_in_addr(req
, RTA_DST
, &route
->dst_addr
.in
);
279 else if (route
->family
== AF_INET6
)
280 r
= sd_netlink_message_append_in6_addr(req
, RTA_DST
, &route
->dst_addr
.in6
);
282 return log_error_errno(r
, "Could not append RTA_DST attribute: %m");
284 r
= sd_rtnl_message_route_set_dst_prefixlen(req
, route
->dst_prefixlen
);
286 return log_error_errno(r
, "Could not set destination prefix length: %m");
289 if (route
->src_prefixlen
) {
290 if (route
->family
== AF_INET
)
291 r
= sd_netlink_message_append_in_addr(req
, RTA_SRC
, &route
->src_addr
.in
);
292 else if (route
->family
== AF_INET6
)
293 r
= sd_netlink_message_append_in6_addr(req
, RTA_SRC
, &route
->src_addr
.in6
);
295 return log_error_errno(r
, "Could not append RTA_SRC attribute: %m");
297 r
= sd_rtnl_message_route_set_src_prefixlen(req
, route
->src_prefixlen
);
299 return log_error_errno(r
, "Could not set source prefix length: %m");
302 if (!in_addr_is_null(route
->family
, &route
->prefsrc_addr
)) {
303 if (route
->family
== AF_INET
)
304 r
= sd_netlink_message_append_in_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in
);
305 else if (route
->family
== AF_INET6
)
306 r
= sd_netlink_message_append_in6_addr(req
, RTA_PREFSRC
, &route
->prefsrc_addr
.in6
);
308 return log_error_errno(r
, "Could not append RTA_PREFSRC attribute: %m");
311 r
= sd_rtnl_message_route_set_scope(req
, route
->scope
);
313 return log_error_errno(r
, "Could not set scope: %m");
315 r
= sd_netlink_message_append_u32(req
, RTA_PRIORITY
, route
->metrics
);
317 return log_error_errno(r
, "Could not append RTA_PRIORITY attribute: %m");
319 r
= sd_netlink_message_append_u32(req
, RTA_OIF
, link
->ifindex
);
321 return log_error_errno(r
, "Could not append RTA_OIF attribute: %m");
323 r
= sd_netlink_call_async(link
->manager
->rtnl
, req
, callback
, link
, 0, NULL
);
325 return log_error_errno(r
, "Could not send rtnetlink message: %m");
332 int config_parse_gateway(const char *unit
,
333 const char *filename
,
336 unsigned section_line
,
343 Network
*network
= userdata
;
344 _cleanup_route_free_ Route
*n
= NULL
;
345 union in_addr_union buffer
;
354 if (streq(section
, "Network")) {
355 /* we are not in an Route section, so treat
356 * this as the special '0' section */
360 r
= route_new_static(network
, section_line
, &n
);
364 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
366 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route is invalid, ignoring assignment: %s", rvalue
);
377 int config_parse_preferred_src(const char *unit
,
378 const char *filename
,
381 unsigned section_line
,
388 Network
*network
= userdata
;
389 _cleanup_route_free_ Route
*n
= NULL
;
390 union in_addr_union buffer
;
399 r
= route_new_static(network
, section_line
, &n
);
403 r
= in_addr_from_string_auto(rvalue
, &f
, &buffer
);
405 log_syntax(unit
, LOG_ERR
, filename
, line
, EINVAL
,
406 "Preferred source is invalid, ignoring assignment: %s", rvalue
);
411 n
->prefsrc_addr
= buffer
;
417 int config_parse_destination(const char *unit
,
418 const char *filename
,
421 unsigned section_line
,
428 Network
*network
= userdata
;
429 _cleanup_route_free_ Route
*n
= NULL
;
430 const char *address
, *e
;
431 union in_addr_union buffer
;
432 unsigned char prefixlen
;
441 r
= route_new_static(network
, section_line
, &n
);
445 /* Destination|Source=address/prefixlen */
448 e
= strchr(rvalue
, '/');
450 address
= strndupa(rvalue
, e
- rvalue
);
454 r
= in_addr_from_string_auto(address
, &f
, &buffer
);
456 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Destination is invalid, ignoring assignment: %s", address
);
460 if (f
!= AF_INET
&& f
!= AF_INET6
) {
461 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown address family, ignoring assignment: %s", address
);
467 r
= safe_atou8(e
+ 1, &prefixlen
);
469 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Route destination prefix length is invalid, ignoring assignment: %s", e
+ 1);
484 if (streq(lvalue
, "Destination")) {
485 n
->dst_addr
= buffer
;
486 n
->dst_prefixlen
= prefixlen
;
487 } else if (streq(lvalue
, "Source")) {
488 n
->src_addr
= buffer
;
489 n
->src_prefixlen
= prefixlen
;
491 assert_not_reached(lvalue
);
498 int config_parse_route_priority(const char *unit
,
499 const char *filename
,
502 unsigned section_line
,
508 Network
*network
= userdata
;
509 _cleanup_route_free_ Route
*n
= NULL
;
518 r
= route_new_static(network
, section_line
, &n
);
522 r
= config_parse_unsigned(unit
, filename
, line
, section
,
523 section_line
, lvalue
, ltype
,
524 rvalue
, &n
->metrics
, userdata
);
533 int config_parse_route_scope(const char *unit
,
534 const char *filename
,
537 unsigned section_line
,
543 Network
*network
= userdata
;
544 _cleanup_route_free_ Route
*n
= NULL
;
553 r
= route_new_static(network
, section_line
, &n
);
557 if (streq(rvalue
, "host"))
558 n
->scope
= RT_SCOPE_HOST
;
559 else if (streq(rvalue
, "link"))
560 n
->scope
= RT_SCOPE_LINK
;
561 else if (streq(rvalue
, "global"))
562 n
->scope
= RT_SCOPE_UNIVERSE
;
564 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Unknown route scope: %s", rvalue
);