]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-manager.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / network / networkd-manager.c
index 7433be96ffb8de36a94f66ed5f7cd12019fe109c..00f6545e24f9e5580d95335d0546c36e14484602 100644 (file)
 #include "fileio.h"
 #include "local-addresses.h"
 #include "netlink-util.h"
+#include "network-internal.h"
 #include "networkd-manager.h"
 #include "ordered-set.h"
 #include "path-util.h"
 #include "set.h"
 #include "strv.h"
+#include "sysctl-util.h"
+#include "tmpfile-util.h"
 #include "virt.h"
 
 /* use 8 MB for receive socket kernel queue. */
 #define RCVBUF_SIZE    (8*1024*1024)
 
-const char* const network_dirs[] = {
-        "/etc/systemd/network",
-        "/run/systemd/network",
-        "/usr/lib/systemd/network",
-#if HAVE_SPLIT_USR
-        "/lib/systemd/network",
-#endif
-        NULL};
-
 static int setup_default_address_pool(Manager *m) {
         AddressPool *p;
         int r;
@@ -201,7 +195,7 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi
         }
 
         if (!STR_IN_SET(action, "add", "change")) {
-                log_device_debug(device, "Ignoring udev %s event for device: %m", action);
+                log_device_debug(device, "Ignoring udev %s event for device.", action);
                 return 0;
         }
 
@@ -240,11 +234,11 @@ static int manager_connect_udev(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Could not add device monitor filter: %m");
 
-        r = sd_device_monitor_attach_event(m->device_monitor, m->event, 0);
+        r = sd_device_monitor_attach_event(m->device_monitor, m->event);
         if (r < 0)
                 return log_error_errno(r, "Failed to attach event to device monitor: %m");
 
-        r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m, "networkd-device-monitor");
+        r = sd_device_monitor_start(m->device_monitor, manager_udev_process_link, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to start device monitor: %m");
 
@@ -421,6 +415,28 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
         (void) route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
 
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL,
+                        *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL;
+
+                if (!in_addr_is_null(family, &dst)) {
+                        (void) in_addr_to_string(family, &dst, &buf_dst);
+                        (void) asprintf(&buf_dst_prefixlen, "/%u", dst_prefixlen);
+                }
+                if (!in_addr_is_null(family, &src))
+                        (void) in_addr_to_string(family, &src, &buf_src);
+                if (!in_addr_is_null(family, &gw))
+                        (void) in_addr_to_string(family, &gw, &buf_gw);
+                if (!in_addr_is_null(family, &prefsrc))
+                        (void) in_addr_to_string(family, &prefsrc, &buf_prefsrc);
+
+                log_link_debug(link,
+                               "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s",
+                               type == RTM_DELROUTE ? "Removing" : route ? "Updating" : "Adding",
+                               strna(buf_dst), strempty(buf_dst_prefixlen),
+                               strna(buf_src), strna(buf_gw), strna(buf_prefsrc));
+        }
+
         switch (type) {
         case RTM_NEWROUTE:
                 if (!route) {
@@ -697,7 +713,8 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
 }
 
 int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
-        uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0;
+        uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0;
+        struct fib_rule_port_range sport = {}, dport = {};
         union in_addr_union to = {}, from = {};
         RoutingPolicyRule *rule = NULL;
         uint32_t fwmark = 0, table = 0;
@@ -829,12 +846,30 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 return 0;
         }
 
-        (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule);
+        r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &protocol);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(sport), (void *) &sport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
+
+        r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(dport), (void *) &dport);
+        if (r < 0 && r != -ENODATA) {
+                log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
+                return 0;
+        }
+
+        (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &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, iif, oif, &rule);
+                        r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
                         if (r < 0) {
                                 log_warning_errno(r, "Could not add rule, ignoring: %m");
                                 return 0;
@@ -1002,14 +1037,19 @@ static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address
         return r;
 }
 
-static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addresses, unsigned n) {
+static int ordered_set_put_in4_addrv(OrderedSet *s,
+                                     const struct in_addr *addresses,
+                                     size_t n,
+                                     bool (*predicate)(const struct in_addr *addr)) {
         int r, c = 0;
-        unsigned i;
+        size_t i;
 
         assert(s);
         assert(n == 0 || addresses);
 
         for (i = 0; i < n; i++) {
+                if (predicate && !predicate(&addresses[i]))
+                        continue;
                 r = ordered_set_put_in4_addr(s, addresses+i);
                 if (r < 0)
                         return r;
@@ -1020,22 +1060,6 @@ static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addres
         return c;
 }
 
-static void print_string_set(FILE *f, const char *field, OrderedSet *s) {
-        bool space = false;
-        Iterator i;
-        char *p;
-
-        if (ordered_set_isempty(s))
-                return;
-
-        fputs(field, f);
-
-        ORDERED_SET_FOREACH(p, s, i)
-                fputs_with_space(f, p, NULL, &space);
-
-        fputc('\n', f);
-}
-
 static int manager_save(Manager *m) {
         _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL;
         Link *link;
@@ -1085,11 +1109,11 @@ static int manager_save(Manager *m) {
                 if (r < 0)
                         return r;
 
-                r = ordered_set_put_strdupv(search_domains, link->network->search_domains);
+                r = ordered_set_put_string_set(search_domains, link->network->search_domains);
                 if (r < 0)
                         return r;
 
-                r = ordered_set_put_strdupv(route_domains, link->network->route_domains);
+                r = ordered_set_put_string_set(route_domains, link->network->route_domains);
                 if (r < 0)
                         return r;
 
@@ -1102,7 +1126,7 @@ static int manager_save(Manager *m) {
 
                         r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
                         if (r > 0) {
-                                r = ordered_set_put_in4_addrv(dns, addresses, r);
+                                r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local);
                                 if (r < 0)
                                         return r;
                         } else if (r < 0 && r != -ENODATA)
@@ -1114,7 +1138,7 @@ static int manager_save(Manager *m) {
 
                         r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
                         if (r > 0) {
-                                r = ordered_set_put_in4_addrv(ntp, addresses, r);
+                                r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local);
                                 if (r < 0)
                                         return r;
                         } else if (r < 0 && r != -ENODATA)
@@ -1158,10 +1182,10 @@ static int manager_save(Manager *m) {
                 "# This is private data. Do not parse.\n"
                 "OPER_STATE=%s\n", operstate_str);
 
-        print_string_set(f, "DNS=", dns);
-        print_string_set(f, "NTP=", ntp);
-        print_string_set(f, "DOMAINS=", search_domains);
-        print_string_set(f, "ROUTE_DOMAINS=", route_domains);
+        ordered_set_print(f, "DNS=", dns);
+        ordered_set_print(f, "NTP=", ntp);
+        ordered_set_print(f, "DOMAINS=", search_domains);
+        ordered_set_print(f, "ROUTE_DOMAINS=", route_domains);
 
         r = routing_policy_serialize_rules(m->rules, f);
         if (r < 0)
@@ -1198,32 +1222,27 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
         Manager *m = userdata;
         Link *link;
         Iterator i;
-        int r;
 
         assert(m);
 
         if (m->dirty)
                 manager_save(m);
 
-        SET_FOREACH(link, m->dirty_links, i) {
-                r = link_save(link);
-                if (r >= 0)
+        SET_FOREACH(link, m->dirty_links, i)
+                if (link_save(link) >= 0)
                         link_clean(link);
-        }
 
         return 1;
 }
 
 Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
         assert_return(m, NULL);
-        assert_return(m->dhcp6_prefixes, NULL);
         assert_return(addr, NULL);
 
         return hashmap_get(m->dhcp6_prefixes, addr);
 }
 
-static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, void *userdata) {
-        Link *link = userdata;
+static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
@@ -1235,13 +1254,24 @@ static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, void *
         return 0;
 }
 
+static void dhcp6_prefixes_hash_func(const struct in6_addr *addr, struct siphash *state) {
+        assert(addr);
+
+        siphash24_compress(addr, sizeof(*addr), state);
+}
+
+static int dhcp6_prefixes_compare_func(const struct in6_addr *a, const struct in6_addr *b) {
+        return memcmp(a, b, sizeof(*a));
+}
+
+DEFINE_PRIVATE_HASH_OPS(dhcp6_prefixes_hash_ops, struct in6_addr, dhcp6_prefixes_hash_func, dhcp6_prefixes_compare_func);
+
 int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
-        int r;
-        Route *route;
         _cleanup_free_ char *buf = NULL;
+        Route *route;
+        int r;
 
         assert_return(m, -EINVAL);
-        assert_return(m->dhcp6_prefixes, -ENODATA);
         assert_return(addr, -EINVAL);
 
         r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
@@ -1256,11 +1286,14 @@ int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
         log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
 
+        r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &dhcp6_prefixes_hash_ops);
+        if (r < 0)
+                return r;
+
         return hashmap_put(m->dhcp6_prefixes, addr, link);
 }
 
-static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, void *userdata) {
-        Link *link = userdata;
+static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
@@ -1273,13 +1306,12 @@ static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, voi
 }
 
 static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+        _cleanup_free_ char *buf = NULL;
+        Route *route;
         Link *l;
         int r;
-        Route *route;
-        _cleanup_free_ char *buf = NULL;
 
         assert_return(m, -EINVAL);
-        assert_return(m->dhcp6_prefixes, -ENODATA);
         assert_return(addr, -EINVAL);
 
         l = hashmap_remove(m->dhcp6_prefixes, addr);
@@ -1303,9 +1335,9 @@ static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
 }
 
 int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
+        struct in6_addr *addr;
         Iterator i;
         Link *l;
-        struct in6_addr *addr;
 
         assert_return(m, -EINVAL);
         assert_return(link, -EINVAL);
@@ -1320,25 +1352,6 @@ int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
         return 0;
 }
 
-static void dhcp6_prefixes_hash_func(const void *p, struct siphash *state) {
-        const struct in6_addr *addr = p;
-
-        assert(p);
-
-        siphash24_compress(addr, sizeof(*addr), state);
-}
-
-static int dhcp6_prefixes_compare_func(const void *_a, const void *_b) {
-        const struct in6_addr *a = _a, *b = _b;
-
-        return memcmp(a, b, sizeof(*a));
-}
-
-static const struct hash_ops dhcp6_prefixes_hash_ops = {
-        .hash = dhcp6_prefixes_hash_func,
-        .compare = dhcp6_prefixes_compare_func,
-};
-
 int manager_new(Manager **ret) {
         _cleanup_(manager_freep) Manager *m = NULL;
         int r;
@@ -1351,6 +1364,8 @@ int manager_new(Manager **ret) {
         if (!m->state_file)
                 return -ENOMEM;
 
+        m->sysctl_ipv6_enabled = -1;
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -1375,10 +1390,6 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        m->netdevs = hashmap_new(&string_hash_ops);
-        if (!m->netdevs)
-                return -ENOMEM;
-
         LIST_HEAD_INIT(m->networks);
 
         r = sd_resolve_default(&m->resolve);
@@ -1393,10 +1404,6 @@ int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        m->dhcp6_prefixes = hashmap_new(&dhcp6_prefixes_hash_ops);
-        if (!m->dhcp6_prefixes)
-                return -ENOMEM;
-
         m->duid.type = DUID_TYPE_EN;
 
         (void) routing_policy_load_rules(m->state_file, &m->rules_saved);
@@ -1407,10 +1414,9 @@ int manager_new(Manager **ret) {
 }
 
 void manager_free(Manager *m) {
+        AddressPool *pool;
         Network *network;
-        NetDev *netdev;
         Link *link;
-        AddressPool *pool;
 
         if (!m)
                 return;
@@ -1419,49 +1425,44 @@ void manager_free(Manager *m) {
 
         sd_netlink_unref(m->rtnl);
         sd_netlink_unref(m->genl);
-
-        while ((network = m->networks))
-                network_free(network);
+        sd_resolve_unref(m->resolve);
 
         while ((link = hashmap_first(m->dhcp6_prefixes)))
                 manager_dhcp6_prefix_remove_all(m, link);
         hashmap_free(m->dhcp6_prefixes);
 
-        while ((link = hashmap_first(m->links))) {
+        while ((link = hashmap_steal_first(m->links))) {
                 if (link->dhcp6_client)
-                        (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client,
-                                                          link);
-
-                hashmap_remove(m->links, INT_TO_PTR(link->ifindex));
-
+                        (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link);
                 link_unref(link);
         }
 
-        set_free_with_destructor(m->dirty_links, link_unref);
-        hashmap_free(m->links);
-        set_free(m->links_requesting_uuid);
+        m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
+        m->links = hashmap_free(m->links);
+        m->links_requesting_uuid = set_free(m->links_requesting_uuid);
         set_free(m->duids_requesting_uuid);
 
+        while ((network = m->networks))
+                network_free(network);
+
         hashmap_free(m->networks_by_name);
 
-        while ((netdev = hashmap_first(m->netdevs)))
-                netdev_unref(netdev);
-        hashmap_free(m->netdevs);
+        m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref);
 
         while ((pool = m->address_pools))
                 address_pool_free(pool);
 
-        set_free_with_destructor(m->rules, routing_policy_rule_free);
-        set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
+        /* routing_policy_rule_free() access m->rules and m->rules_foreign.
+         * So, it is necessary to set NULL after the sets are freed. */
+        m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free);
+        m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
         set_free_with_destructor(m->rules_saved, routing_policy_rule_free);
 
         sd_event_unref(m->event);
 
-        sd_resolve_unref(m->resolve);
-
         sd_device_monitor_unref(m->device_monitor);
 
-        sd_bus_unref(m->bus);
+        sd_bus_flush_close_unref(m->bus);
 
         free(m->dynamic_timezone);
         free(m->dynamic_hostname);
@@ -1490,7 +1491,7 @@ int manager_load_config(Manager *m) {
         int r;
 
         /* update timestamp */
-        paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true);
+        paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, true);
 
         r = netdev_load(m);
         if (r < 0)
@@ -1504,7 +1505,7 @@ int manager_load_config(Manager *m) {
 }
 
 bool manager_should_reload(Manager *m) {
-        return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false);
+        return paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, false);
 }
 
 int manager_rtnl_enumerate_links(Manager *m) {
@@ -1863,3 +1864,18 @@ int manager_request_product_uuid(Manager *m, Link *link) {
 
         return 0;
 }
+
+int manager_sysctl_ipv6_enabled(Manager *manager) {
+        _cleanup_free_ char *value = NULL;
+        int r;
+
+        if (manager->sysctl_ipv6_enabled >= 0)
+                return manager->sysctl_ipv6_enabled;
+
+        r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &value);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to read net.ipv6.conf.all.disable_ipv6 sysctl property: %m");
+
+        manager->sysctl_ipv6_enabled = value[0] == '0';
+        return manager->sysctl_ipv6_enabled;
+}