+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include <sys/socket.h>
#include <linux/if.h>
+#include <linux/fib_rules.h>
#include "sd-daemon.h"
#include "sd-netlink.h"
"/etc/systemd/network",
"/run/systemd/network",
"/usr/lib/systemd/network",
-#ifdef HAVE_SPLIT_USR
+#if HAVE_SPLIT_USR
"/lib/systemd/network",
#endif
NULL};
assert(m);
r = sd_bus_default_system(&m->bus);
- if (r == -ENOENT) {
+ if (r < 0) {
/* We failed to connect? Yuck, we must be in early
- * boot. Let's try in 5s again. As soon as we have
- * kdbus we can stop doing this... */
+ * boot. Let's try in 5s again. */
log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m");
Link *link = NULL;
uint16_t type;
uint32_t ifindex, priority = 0;
- unsigned char protocol, scope, tos, table;
+ unsigned char protocol, scope, tos, table, rt_type;
int family;
unsigned char dst_prefixlen, src_prefixlen;
union in_addr_union dst = {}, gw = {}, src = {}, prefsrc = {};
if (r < 0) {
log_warning_errno(r, "rtnl: could not get message type: %m");
return 0;
- } else if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {
+ } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
log_warning("rtnl: received unexpected message type when processing route");
return 0;
}
break;
default:
- log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+ assert_not_reached("Received unsupported address family");
return 0;
}
return 0;
}
+ r = sd_rtnl_message_route_get_type(message, &rt_type);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received route with invalid type, ignoring: %m");
+ return 0;
+ }
+
r = sd_rtnl_message_route_get_table(message, &table);
if (r < 0) {
log_link_warning_errno(link, r, "rtnl: received route with invalid table, ignoring: %m");
return 0;
}
- route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol);
+ route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, rt_type, protocol);
break;
if (r < 0) {
log_warning_errno(r, "rtnl: could not get message type: %m");
return 0;
- } else if (type != RTM_NEWADDR && type != RTM_DELADDR) {
+ } else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) {
log_warning("rtnl: received unexpected message type when processing address");
return 0;
}
if (r < 0) {
log_warning_errno(r, "rtnl: Could not get message type: %m");
return 0;
- } else if (type != RTM_NEWLINK && type != RTM_DELLINK) {
+ } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) {
log_warning("rtnl: Received unexpected message type when processing link");
return 0;
}
return 1;
}
+int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
+ uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+ RoutingPolicyRule *rule = NULL;
+ union in_addr_union to, from;
+ uint32_t fwmark = 0, table = 0;
+ Manager *m = userdata;
+ uint16_t type;
+ int family;
+ int r;
+
+ assert(rtnl);
+ assert(message);
+ assert(m);
+
+ if (sd_netlink_message_is_error(message)) {
+ r = sd_netlink_message_get_errno(message);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to receive rule: %m");
+
+ return 0;
+ }
+
+ r = sd_netlink_message_get_type(message, &type);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get message type: %m");
+ return 0;
+ } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
+ log_warning("rtnl: received unexpected message type '%u' when processing rule.", type);
+ return 0;
+ }
+
+ r = sd_rtnl_message_get_family(message, &family);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: could not get rule family: %m");
+ return 0;
+ } else if (!IN_SET(family, AF_INET, AF_INET6)) {
+ log_debug("rtnl: received address with invalid family %u, ignoring.", family);
+ return 0;
+ }
+
+ switch (family) {
+ case AF_INET:
+ r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in);
+ if (r >= 0) {
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+ }
+
+ r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in);
+ if (r >= 0) {
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+ }
+
+ break;
+
+ case AF_INET6:
+ r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6);
+ if (r >= 0) {
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m");
+ }
+
+ r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6);
+ if (r >= 0) {
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+ if (r < 0)
+ log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m");
+ }
+
+ break;
+
+ default:
+ assert_not_reached("Received unsupported address family");
+ }
+
+ if (from_prefixlen == 0 && to_prefixlen == 0)
+ return 0;
+
+ (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark);
+ (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table);
+ (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos);
+
+ (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule);
+
+ switch (type) {
+ case RTM_NEWRULE:
+ if(!rule) {
+ r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, &rule);
+ if (r < 0) {
+ log_warning_errno(r, "Could not add rule: %m");
+ return 0;
+ }
+ }
+ break;
+ case RTM_DELRULE:
+ routing_policy_rule_free(rule);
+
+ break;
+
+ default:
+ assert_not_reached("Received invalid RTNL message type");
+ }
+
+ return 1;
+}
+
static int systemd_netlink_fd(void) {
int n, fd, rtnl_fd = -EINVAL;
if (r < 0)
return r;
+ r = sd_netlink_add_match(m->rtnl, RTM_NEWRULE, &manager_rtnl_process_rule, m);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_add_match(m->rtnl, RTM_DELRULE, &manager_rtnl_process_rule, m);
+ if (r < 0)
+ return r;
+
return 0;
}
if (ordered_set_isempty(s))
return;
- fputs(field, f);
+ fputs_unlocked(field, f);
ORDERED_SET_FOREACH(p, s, i)
fputs_with_space(f, p, NULL, &space);
- fputc('\n', f);
+ fputc_unlocked('\n', f);
}
static int manager_save(Manager *m) {
_cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
+ RoutingPolicyRule *rule = NULL;
+ bool space = false;
Link *link;
Iterator i;
_cleanup_free_ char *temp_path = NULL;
print_string_set(f, "DOMAINS=", search_domains);
print_string_set(f, "ROUTE_DOMAINS=", route_domains);
+ SET_FOREACH(rule, m->rules, i) {
+ _cleanup_free_ char *from_str = NULL, *to_str = NULL;
+ fputs("RULE=", f);
+
+ if (!in_addr_is_null(rule->family, &rule->from)) {
+ r = in_addr_to_string(rule->family, &rule->from, &from_str);
+ if (r < 0)
+ goto fail;
+ }
+
+ if (!in_addr_is_null(rule->family, &rule->to)) {
+ r = in_addr_to_string(rule->family, &rule->to, &to_str);
+ if (r < 0)
+ goto fail;
+ }
+
+ fprintf(f, "from=%s%s/%hhu to=%s%s/%hhu tos=%hhu fwmark=%"PRIu32"/%"PRIu32" table=%"PRIu32,
+ space ? " " : "", from_str, rule->from_prefixlen,
+ space ? " " : "", to_str, rule->to_prefixlen,
+ rule->tos,
+ rule->fwmark, rule->fwmask,
+ rule->table);
+
+ fputc('\n', f);
+ }
+
r = fflush_and_check(f);
if (r < 0)
goto fail;
m->duid.type = DUID_TYPE_EN;
+ (void) routing_policy_rule_load(m);
+
*ret = m;
m = NULL;
}
void manager_free(Manager *m) {
+ RoutingPolicyRule *rule;
Network *network;
NetDev *netdev;
Link *link;
free(m->state_file);
+ while ((network = m->networks))
+ network_free(network);
+
while ((link = hashmap_first(m->links)))
link_unref(link);
hashmap_free(m->links);
- while ((network = m->networks))
- network_free(network);
-
hashmap_free(m->networks_by_name);
while ((netdev = hashmap_first(m->netdevs)))
while ((pool = m->address_pools))
address_pool_free(pool);
+ set_free(m->rules);
+ set_free(m->rules_foreign);
+
+ while ((rule = set_steal_first(m->rules_saved)))
+ free(rule);
+
+ set_free(m->rules_saved);
+
sd_netlink_unref(m->rtnl);
sd_event_unref(m->event);
return r;
}
+int manager_rtnl_enumerate_rules(Manager *m) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ sd_netlink_message *rule;
+ int r;
+
+ assert(m);
+ assert(m->rtnl);
+
+ r = sd_rtnl_message_new_routing_policy_rule(m->rtnl, &req, RTM_GETRULE, 0);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_message_request_dump(req, true);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(m->rtnl, req, 0, &reply);
+ if (r < 0) {
+ if (r == -EOPNOTSUPP) {
+ log_debug("FIB Rules are not supported by the kernel. Ignoring.");
+ return 0;
+ }
+
+ return r;
+ }
+
+ for (rule = reply; rule; rule = sd_netlink_message_next(rule)) {
+ int k;
+
+ m->enumerating = true;
+
+ k = manager_rtnl_process_rule(m->rtnl, rule, m);
+ if (k < 0)
+ r = k;
+
+ m->enumerating = false;
+ }
+
+ return r;
+}
+
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found) {
AddressPool *p;
int r;