1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2017 Susant Sahani
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <linux/fib_rules.h>
24 #include "alloc-util.h"
25 #include "conf-parser.h"
27 #include "networkd-routing-policy-rule.h"
28 #include "netlink-util.h"
29 #include "networkd-manager.h"
30 #include "parse-util.h"
31 #include "socket-util.h"
32 #include "string-util.h"
34 int routing_policy_rule_new(RoutingPolicyRule
**ret
) {
35 RoutingPolicyRule
*rule
;
37 rule
= new0(RoutingPolicyRule
, 1);
41 rule
->family
= AF_INET
;
42 rule
->table
= RT_TABLE_MAIN
;
48 void routing_policy_rule_free(RoutingPolicyRule
*rule
) {
54 LIST_REMOVE(rules
, rule
->network
->rules
, rule
);
55 assert(rule
->network
->n_rules
> 0);
56 rule
->network
->n_rules
--;
59 hashmap_remove(rule
->network
->rules_by_section
, rule
->section
);
60 network_config_section_free(rule
->section
);
63 if (rule
->network
->manager
) {
64 set_remove(rule
->network
->manager
->rules
, rule
);
65 set_remove(rule
->network
->manager
->rules_foreign
, rule
);
74 static void routing_policy_rule_hash_func(const void *b
, struct siphash
*state
) {
75 const RoutingPolicyRule
*rule
= b
;
79 siphash24_compress(&rule
->family
, sizeof(rule
->family
), state
);
81 switch (rule
->family
) {
85 siphash24_compress(&rule
->from
, FAMILY_ADDRESS_SIZE(rule
->family
), state
);
86 siphash24_compress(&rule
->from_prefixlen
, sizeof(rule
->from_prefixlen
), state
);
88 siphash24_compress(&rule
->to
, FAMILY_ADDRESS_SIZE(rule
->family
), state
);
89 siphash24_compress(&rule
->to_prefixlen
, sizeof(rule
->to_prefixlen
), state
);
91 siphash24_compress(&rule
->tos
, sizeof(rule
->tos
), state
);
92 siphash24_compress(&rule
->fwmark
, sizeof(rule
->fwmark
), state
);
93 siphash24_compress(&rule
->table
, sizeof(rule
->table
), state
);
96 siphash24_compress(&rule
->iif
, strlen(rule
->iif
), state
);
99 siphash24_compress(&rule
->oif
, strlen(rule
->oif
), state
);
103 /* treat any other address family as AF_UNSPEC */
108 static int routing_policy_rule_compare_func(const void *_a
, const void *_b
) {
109 const RoutingPolicyRule
*a
= _a
, *b
= _b
;
112 if (a
->family
< b
->family
)
114 if (a
->family
> b
->family
)
120 if (a
->from_prefixlen
< b
->from_prefixlen
)
122 if (a
->from_prefixlen
> b
->from_prefixlen
)
125 if (a
->to_prefixlen
< b
->to_prefixlen
)
127 if (a
->to_prefixlen
> b
->to_prefixlen
)
135 if (a
->fwmask
< b
->fwmark
)
137 if (a
->fwmask
> b
->fwmark
)
140 if (a
->table
< b
->table
)
142 if (a
->table
> b
->table
)
145 r
= strcmp_ptr(a
->iif
, b
->iif
);
149 r
= strcmp_ptr(a
->oif
, b
->oif
);
153 r
= memcmp(&a
->from
, &b
->from
, FAMILY_ADDRESS_SIZE(a
->family
));
157 return memcmp(&a
->to
, &b
->to
, FAMILY_ADDRESS_SIZE(a
->family
));
160 /* treat any other address family as AF_UNSPEC */
165 const struct hash_ops routing_policy_rule_hash_ops
= {
166 .hash
= routing_policy_rule_hash_func
,
167 .compare
= routing_policy_rule_compare_func
170 int routing_policy_rule_get(Manager
*m
,
172 const union in_addr_union
*from
,
173 uint8_t from_prefixlen
,
174 const union in_addr_union
*to
,
175 uint8_t to_prefixlen
,
181 RoutingPolicyRule
**ret
) {
183 RoutingPolicyRule rule
, *existing
;
185 assert_return(m
, -1);
187 rule
= (RoutingPolicyRule
) {
190 .from_prefixlen
= from_prefixlen
,
192 .to_prefixlen
= to_prefixlen
,
201 existing
= set_get(m
->rules
, &rule
);
209 if (m
->rules_foreign
) {
210 existing
= set_get(m
->rules_foreign
, &rule
);
221 int routing_policy_rule_make_local(Manager
*m
, RoutingPolicyRule
*rule
) {
226 if (set_contains(m
->rules_foreign
, rule
)) {
227 set_remove(m
->rules_foreign
, rule
);
229 r
= set_ensure_allocated(&m
->rules
, &routing_policy_rule_hash_ops
);
233 return set_put(m
->rules
, rule
);
239 static int routing_policy_rule_add_internal(Set
**rules
,
241 const union in_addr_union
*from
,
242 uint8_t from_prefixlen
,
243 const union in_addr_union
*to
,
244 uint8_t to_prefixlen
,
250 RoutingPolicyRule
**ret
) {
252 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*rule
= NULL
;
255 assert_return(rules
, -EINVAL
);
257 r
= routing_policy_rule_new(&rule
);
261 rule
->family
= family
;
263 rule
->from_prefixlen
= from_prefixlen
;
265 rule
->to_prefixlen
= to_prefixlen
;
267 rule
->fwmark
= fwmark
;
272 r
= set_ensure_allocated(rules
, &routing_policy_rule_hash_ops
);
276 r
= set_put(*rules
, rule
);
288 int routing_policy_rule_add(Manager
*m
,
290 const union in_addr_union
*from
,
291 uint8_t from_prefixlen
,
292 const union in_addr_union
*to
,
293 uint8_t to_prefixlen
,
299 RoutingPolicyRule
**ret
) {
301 return routing_policy_rule_add_internal(&m
->rules
, family
, from
, from_prefixlen
, to
, to_prefixlen
, tos
, fwmark
, table
, iif
, oif
, ret
);
304 int routing_policy_rule_add_foreign(Manager
*m
,
306 const union in_addr_union
*from
,
307 uint8_t from_prefixlen
,
308 const union in_addr_union
*to
,
309 uint8_t to_prefixlen
,
315 RoutingPolicyRule
**ret
) {
316 return routing_policy_rule_add_internal(&m
->rules_foreign
, family
, from
, from_prefixlen
, to
, to_prefixlen
, tos
, fwmark
, table
, iif
, oif
, ret
);
319 static int routing_policy_rule_remove_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
320 _cleanup_link_unref_ Link
*link
= userdata
;
325 assert(link
->ifname
);
327 link
->link_messages
--;
329 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
332 r
= sd_netlink_message_get_errno(m
);
334 log_link_warning_errno(link
, r
, "Could not drop routing policy rule: %m");
339 int routing_policy_rule_remove(RoutingPolicyRule
*routing_policy_rule
, Link
*link
, sd_netlink_message_handler_t callback
) {
340 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
343 assert(routing_policy_rule
);
345 assert(link
->manager
);
346 assert(link
->manager
->rtnl
);
347 assert(link
->ifindex
> 0);
348 assert(IN_SET(routing_policy_rule
->family
, AF_INET
, AF_INET6
));
350 r
= sd_rtnl_message_new_routing_policy_rule(link
->manager
->rtnl
, &m
, RTM_DELRULE
, routing_policy_rule
->family
);
352 return log_error_errno(r
, "Could not allocate RTM_DELRULE message: %m");
354 if (!in_addr_is_null(routing_policy_rule
->family
, &routing_policy_rule
->from
)) {
355 if (routing_policy_rule
->family
== AF_INET
)
356 r
= sd_netlink_message_append_in_addr(m
, FRA_SRC
, &routing_policy_rule
->from
.in
);
358 r
= sd_netlink_message_append_in6_addr(m
, FRA_SRC
, &routing_policy_rule
->from
.in6
);
361 return log_error_errno(r
, "Could not append FRA_SRC attribute: %m");
363 r
= sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m
, routing_policy_rule
->from_prefixlen
);
365 return log_error_errno(r
, "Could not set source prefix length: %m");
368 if (!in_addr_is_null(routing_policy_rule
->family
, &routing_policy_rule
->to
)) {
369 if (routing_policy_rule
->family
== AF_INET
)
370 r
= sd_netlink_message_append_in_addr(m
, FRA_DST
, &routing_policy_rule
->to
.in
);
372 r
= sd_netlink_message_append_in6_addr(m
, FRA_DST
, &routing_policy_rule
->to
.in6
);
375 return log_error_errno(r
, "Could not append FRA_DST attribute: %m");
377 r
= sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m
, routing_policy_rule
->to_prefixlen
);
379 return log_error_errno(r
, "Could not set destination prefix length: %m");
382 r
= sd_netlink_call_async(link
->manager
->rtnl
, m
, callback
, link
, 0, NULL
);
384 return log_error_errno(r
, "Could not send rtnetlink message: %m");
391 static int routing_policy_rule_new_static(Network
*network
, const char *filename
, unsigned section_line
, RoutingPolicyRule
**ret
) {
392 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*rule
= NULL
;
393 _cleanup_network_config_section_free_ NetworkConfigSection
*n
= NULL
;
398 assert(!!filename
== (section_line
> 0));
400 r
= network_config_section_new(filename
, section_line
, &n
);
404 rule
= hashmap_get(network
->rules_by_section
, n
);
412 r
= routing_policy_rule_new(&rule
);
417 rule
->network
= network
;
420 r
= hashmap_put(network
->rules_by_section
, rule
->section
, rule
);
424 LIST_APPEND(rules
, network
->rules
, rule
);
433 int link_routing_policy_rule_handler(sd_netlink
*rtnl
, sd_netlink_message
*m
, void *userdata
) {
434 _cleanup_link_unref_ Link
*link
= userdata
;
440 assert(link
->ifname
);
441 assert(link
->link_messages
> 0);
443 link
->link_messages
--;
445 if (IN_SET(link
->state
, LINK_STATE_FAILED
, LINK_STATE_LINGER
))
448 r
= sd_netlink_message_get_errno(m
);
449 if (r
< 0 && r
!= -EEXIST
)
450 log_link_warning_errno(link
, r
, "Could not add routing policy rule: %m");
452 if (link
->link_messages
== 0)
453 log_link_debug(link
, "Routing policy rule configured");
458 int routing_policy_rule_configure(RoutingPolicyRule
*rule
, Link
*link
, sd_netlink_message_handler_t callback
, bool update
) {
459 _cleanup_(sd_netlink_message_unrefp
) sd_netlink_message
*m
= NULL
;
464 assert(link
->ifindex
> 0);
465 assert(link
->manager
);
466 assert(link
->manager
->rtnl
);
468 r
= sd_rtnl_message_new_routing_policy_rule(link
->manager
->rtnl
, &m
, RTM_NEWRULE
, rule
->family
);
470 return log_error_errno(r
, "Could not allocate RTM_NEWRULE message: %m");
472 if (!in_addr_is_null(rule
->family
, &rule
->from
)) {
473 if (rule
->family
== AF_INET
)
474 r
= sd_netlink_message_append_in_addr(m
, FRA_SRC
, &rule
->from
.in
);
476 r
= sd_netlink_message_append_in6_addr(m
, FRA_SRC
, &rule
->from
.in6
);
479 return log_error_errno(r
, "Could not append FRA_SRC attribute: %m");
481 r
= sd_rtnl_message_routing_policy_rule_set_rtm_src_prefixlen(m
, rule
->from_prefixlen
);
483 return log_error_errno(r
, "Could not set source prefix length: %m");
486 if (!in_addr_is_null(rule
->family
, &rule
->to
)) {
487 if (rule
->family
== AF_INET
)
488 r
= sd_netlink_message_append_in_addr(m
, FRA_DST
, &rule
->to
.in
);
490 r
= sd_netlink_message_append_in6_addr(m
, FRA_DST
, &rule
->to
.in6
);
493 return log_error_errno(r
, "Could not append FRA_DST attribute: %m");
495 r
= sd_rtnl_message_routing_policy_rule_set_rtm_dst_prefixlen(m
, rule
->to_prefixlen
);
497 return log_error_errno(r
, "Could not set destination prefix length: %m");
500 r
= sd_netlink_message_append_u32(m
, FRA_PRIORITY
, rule
->priority
);
502 return log_error_errno(r
, "Could not append FRA_PRIORITY attribute: %m");
505 r
= sd_rtnl_message_routing_policy_rule_set_tos(m
, rule
->tos
);
507 return log_error_errno(r
, "Could not set ip rule tos: %m");
510 if (rule
->table
< 256) {
511 r
= sd_rtnl_message_routing_policy_rule_set_table(m
, rule
->table
);
513 return log_error_errno(r
, "Could not set ip rule table: %m");
515 r
= sd_rtnl_message_routing_policy_rule_set_table(m
, RT_TABLE_UNSPEC
);
517 return log_error_errno(r
, "Could not set ip rule table: %m");
519 r
= sd_netlink_message_append_u32(m
, FRA_TABLE
, rule
->table
);
521 return log_error_errno(r
, "Could not append FRA_TABLE attribute: %m");
524 if (rule
->fwmark
> 0) {
525 r
= sd_netlink_message_append_u32(m
, FRA_FWMARK
, rule
->fwmark
);
527 return log_error_errno(r
, "Could not append FRA_FWMARK attribute: %m");
530 if (rule
->fwmask
> 0) {
531 r
= sd_netlink_message_append_u32(m
, FRA_FWMASK
, rule
->fwmask
);
533 return log_error_errno(r
, "Could not append FRA_FWMASK attribute: %m");
537 r
= sd_netlink_message_append_string(m
, FRA_IFNAME
, rule
->iif
);
539 return log_error_errno(r
, "Could not append FRA_IFNAME attribute: %m");
543 r
= sd_netlink_message_append_string(m
, FRA_OIFNAME
, rule
->oif
);
545 return log_error_errno(r
, "Could not append FRA_OIFNAME attribute: %m");
550 r
= sd_netlink_call_async(link
->manager
->rtnl
, m
, callback
, link
, 0, NULL
);
552 return log_error_errno(r
, "Could not send rtnetlink message: %m");
556 r
= routing_policy_rule_add(link
->manager
, rule
->family
, &rule
->from
, rule
->from_prefixlen
, &rule
->to
,
557 rule
->to_prefixlen
, rule
->tos
, rule
->fwmark
, rule
->table
, rule
->iif
, rule
->oif
, NULL
);
559 return log_error_errno(r
, "Could not add rule : %m");
564 static int parse_fwmark_fwmask(const char *s
, uint32_t *fwmark
, uint32_t *fwmask
) {
565 _cleanup_free_
char *f
= NULL
;
579 r
= safe_atou32(f
, fwmark
);
581 return log_error_errno(r
, "Failed to parse RPDB rule firewall mark, ignoring: %s", f
);
584 r
= safe_atou32(p
, fwmask
);
586 return log_error_errno(r
, "Failed to parse RPDB rule mask, ignoring: %s", f
);
592 int config_parse_routing_policy_rule_tos(
594 const char *filename
,
597 unsigned section_line
,
604 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
605 Network
*network
= userdata
;
614 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
618 r
= safe_atou8(rvalue
, &n
->tos
);
620 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse RPDB rule tos, ignoring: %s", rvalue
);
629 int config_parse_routing_policy_rule_priority(
631 const char *filename
,
634 unsigned section_line
,
641 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
642 Network
*network
= userdata
;
651 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
655 r
= safe_atou32(rvalue
, &n
->priority
);
657 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse RPDB rule priority, ignoring: %s", rvalue
);
666 int config_parse_routing_policy_rule_table(
668 const char *filename
,
671 unsigned section_line
,
678 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
679 Network
*network
= userdata
;
688 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
692 r
= safe_atou32(rvalue
, &n
->table
);
694 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse RPDB rule table, ignoring: %s", rvalue
);
703 int config_parse_routing_policy_rule_fwmark_mask(
705 const char *filename
,
708 unsigned section_line
,
715 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
716 Network
*network
= userdata
;
725 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
729 r
= parse_fwmark_fwmask(rvalue
, &n
->fwmark
, &n
->fwmask
);
731 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue
);
740 int config_parse_routing_policy_rule_prefix(
742 const char *filename
,
745 unsigned section_line
,
752 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
753 Network
*network
= userdata
;
754 union in_addr_union buffer
;
764 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
768 r
= in_addr_prefix_from_string(rvalue
, AF_INET
, &buffer
, &prefixlen
);
770 r
= in_addr_prefix_from_string(rvalue
, AF_INET6
, &buffer
, &prefixlen
);
772 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue
);
776 n
->family
= AF_INET6
;
780 if (streq(lvalue
, "To")) {
782 n
->to_prefixlen
= prefixlen
;
785 n
->from_prefixlen
= prefixlen
;
793 int config_parse_routing_policy_rule_device(
795 const char *filename
,
798 unsigned section_line
,
805 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*n
= NULL
;
806 Network
*network
= userdata
;
815 r
= routing_policy_rule_new_static(network
, filename
, section_line
, &n
);
819 if (!ifname_valid(rvalue
)) {
820 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse '%s' interface name, ignoring: %s", lvalue
, rvalue
);
824 if (streq(lvalue
, "IncomingInterface")) {
825 r
= free_and_strdup(&n
->iif
, rvalue
);
829 r
= free_and_strdup(&n
->oif
, rvalue
);
839 static int routing_policy_rule_read_full_file(const char *state_file
, char **ret
) {
840 _cleanup_free_
char *s
= NULL
;
846 r
= read_full_file(state_file
, &s
, &size
);
860 int routing_policy_serialize_rules(Set
*rules
, FILE *f
) {
861 RoutingPolicyRule
*rule
= NULL
;
867 SET_FOREACH(rule
, rules
, i
) {
868 _cleanup_free_
char *from_str
= NULL
, *to_str
= NULL
;
873 if (!in_addr_is_null(rule
->family
, &rule
->from
)) {
874 r
= in_addr_to_string(rule
->family
, &rule
->from
, &from_str
);
878 fprintf(f
, "from=%s/%hhu",
879 from_str
, rule
->from_prefixlen
);
883 if (!in_addr_is_null(rule
->family
, &rule
->to
)) {
884 r
= in_addr_to_string(rule
->family
, &rule
->to
, &to_str
);
888 fprintf(f
, "%sto=%s/%hhu",
890 to_str
, rule
->to_prefixlen
);
894 if (rule
->tos
!= 0) {
895 fprintf(f
, "%stos=%hhu",
901 if (rule
->fwmark
!= 0) {
902 fprintf(f
, "%sfwmark=%"PRIu32
"/%"PRIu32
,
904 rule
->fwmark
, rule
->fwmask
);
909 fprintf(f
, "%siif=%s",
916 fprintf(f
, "%soif=%s",
922 fprintf(f
, "%stable=%"PRIu32
"\n",
930 int routing_policy_load_rules(const char *state_file
, Set
**rules
) {
931 _cleanup_strv_free_
char **l
= NULL
;
932 _cleanup_free_
char *data
= NULL
;
940 r
= routing_policy_rule_read_full_file(state_file
, &data
);
944 l
= strv_split_newlines(data
);
948 r
= set_ensure_allocated(rules
, &routing_policy_rule_hash_ops
);
953 _cleanup_routing_policy_rule_free_ RoutingPolicyRule
*rule
= NULL
;
955 p
= startswith(*i
, "RULE=");
959 r
= routing_policy_rule_new(&rule
);
964 _cleanup_free_
char *word
= NULL
, *a
= NULL
, *b
= NULL
;
965 union in_addr_union buffer
;
968 r
= extract_first_word(&p
, &word
, NULL
, 0);
974 r
= split_pair(word
, "=", &a
, &b
);
978 if (STR_IN_SET(a
, "from", "to")) {
980 r
= in_addr_prefix_from_string(b
, AF_INET
, &buffer
, &prefixlen
);
982 r
= in_addr_prefix_from_string(b
, AF_INET6
, &buffer
, &prefixlen
);
984 log_error_errno(r
, "RPDB rule prefix is invalid, ignoring assignment: %s", b
);
988 rule
->family
= AF_INET6
;
990 rule
->family
= AF_INET
;
992 if (streq(a
, "to")) {
994 rule
->to_prefixlen
= prefixlen
;
997 rule
->from_prefixlen
= prefixlen
;
999 } else if (streq(a
, "tos")) {
1000 r
= safe_atou8(b
, &rule
->tos
);
1002 log_error_errno(r
, "Failed to parse RPDB rule tos, ignoring: %s", b
);
1005 } else if (streq(a
, "table")) {
1006 r
= safe_atou32(b
, &rule
->table
);
1008 log_error_errno(r
, "Failed to parse RPDB rule table, ignoring: %s", b
);
1011 } else if (streq(a
, "fwmark")) {
1013 r
= parse_fwmark_fwmask(b
, &rule
->fwmark
, &rule
->fwmask
);
1015 log_error_errno(r
, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", a
);
1018 } else if (streq(a
, "iif")) {
1020 if (free_and_strdup(&rule
->iif
, b
) < 0)
1023 } else if (streq(a
, "oif")) {
1025 if (free_and_strdup(&rule
->oif
, b
) < 0)
1030 r
= set_put(*rules
, rule
);
1032 log_warning_errno(r
, "Failed to add RPDB rule to saved DB, ignoring: %s", p
);
1042 void routing_policy_rule_purge(Manager
*m
, Link
*link
) {
1043 RoutingPolicyRule
*rule
, *existing
;
1050 SET_FOREACH(rule
, m
->rules_saved
, i
) {
1051 existing
= set_get(m
->rules_foreign
, rule
);
1054 r
= routing_policy_rule_remove(rule
, link
, routing_policy_rule_remove_handler
);
1056 log_warning_errno(r
, "Could not remove routing policy rules: %m");
1060 link
->link_messages
++;