]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-manager.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / network / networkd-manager.c
index 45f9b3d359b3cd406e0974ca0c3a4389729b6cba..fc9ef74016368900eca5a528a4637ec6c16ff331 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
@@ -19,6 +20,7 @@
 
 #include <sys/socket.h>
 #include <linux/if.h>
+#include <linux/fib_rules.h>
 
 #include "sd-daemon.h"
 #include "sd-netlink.h"
@@ -47,7 +49,7 @@ const char* const network_dirs[] = {
         "/etc/systemd/network",
         "/run/systemd/network",
         "/usr/lib/systemd/network",
-#ifdef HAVE_SPLIT_USR
+#if HAVE_SPLIT_USR
         "/lib/systemd/network",
 #endif
         NULL};
@@ -295,7 +297,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         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 = {};
@@ -318,7 +320,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         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;
         }
@@ -412,7 +414,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 break;
 
         default:
-                log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family);
+                assert_not_reached("Received unsupported address family");
                 return 0;
         }
 
@@ -440,6 +442,12 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 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");
@@ -463,7 +471,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                                 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;
 
@@ -509,7 +517,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         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;
         }
@@ -656,7 +664,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
         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;
         }
@@ -718,6 +726,116 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
         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;
 
@@ -782,6 +900,14 @@ static int manager_connect_rtnl(Manager *m) {
         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;
 }
 
@@ -875,6 +1001,8 @@ static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
 
 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;
@@ -999,6 +1127,32 @@ static int manager_save(Manager *m) {
         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;
@@ -1084,6 +1238,8 @@ int manager_new(Manager **ret, sd_event *event) {
 
         m->duid.type = DUID_TYPE_EN;
 
+        (void) routing_policy_rule_load(m);
+
         *ret = m;
         m = NULL;
 
@@ -1091,6 +1247,7 @@ int manager_new(Manager **ret, sd_event *event) {
 }
 
 void manager_free(Manager *m) {
+        RoutingPolicyRule *rule;
         Network *network;
         NetDev *netdev;
         Link *link;
@@ -1101,13 +1258,13 @@ void manager_free(Manager *m) {
 
         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)))
@@ -1117,6 +1274,14 @@ void manager_free(Manager *m) {
         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);
 
@@ -1277,6 +1442,47 @@ int manager_rtnl_enumerate_routes(Manager *m) {
         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;