]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-link.c
Merge pull request #12501 from keszybz/silence-strncpy-warnings
[thirdparty/systemd.git] / src / network / networkd-link.c
index 7983f448a106473986922dcb1d97b6747b1ea64e..3c8b5c5cb436e76d2eda1f258572fb63853579ad 100644 (file)
@@ -4,7 +4,6 @@
 #include <linux/if.h>
 #include <linux/can/netlink.h>
 #include <unistd.h>
-#include <stdio_ext.h>
 
 #include "alloc-util.h"
 #include "bus-util.h"
@@ -111,7 +110,7 @@ static bool link_dhcp4_server_enabled(Link *link) {
         return link->network->dhcp_server;
 }
 
-static bool link_ipv4ll_enabled(Link *link) {
+bool link_ipv4ll_enabled(Link *link) {
         assert(link);
 
         if (link->flags & IFF_LOOPBACK)
@@ -129,6 +128,24 @@ static bool link_ipv4ll_enabled(Link *link) {
         return link->network->link_local & ADDRESS_FAMILY_IPV4;
 }
 
+bool link_ipv4ll_fallback_enabled(Link *link) {
+        assert(link);
+
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        if (STRPTR_IN_SET(link->kind, "vrf", "wireguard"))
+                return false;
+
+        if (link->network->bond)
+                return false;
+
+        return link->network->link_local & ADDRESS_FAMILY_FALLBACK_IPV4;
+}
+
 static bool link_ipv6ll_enabled(Link *link) {
         assert(link);
 
@@ -321,8 +338,7 @@ static int link_enable_ipv6(Link *link) {
 
         r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled);
         if (r < 0)
-                log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m",
-                                       enable_disable(!disabled), link->ifname);
+                log_link_warning_errno(link, r, "Cannot %s IPv6: %m", enable_disable(!disabled));
         else
                 log_link_info(link, "IPv6 successfully %sd", enable_disable(!disabled));
 
@@ -410,7 +426,7 @@ void link_update_operstate(Link *link, bool also_update_master) {
         if (operstate >= LINK_OPERSTATE_CARRIER) {
                 Link *slave;
 
-                HASHMAP_FOREACH(slave, link->slaves, i) {
+                SET_FOREACH(slave, link->slaves, i) {
                         link_update_operstate(slave, false);
 
                         if (slave->operstate < LINK_OPERSTATE_CARRIER)
@@ -507,8 +523,6 @@ static int link_update_flags(Link *link, sd_netlink_message *m) {
         return 0;
 }
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
-
 static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         _cleanup_(link_unrefp) Link *link = NULL;
         uint16_t type;
@@ -606,40 +620,16 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
         return 0;
 }
 
-static void link_detach_from_manager(Link *link) {
-        if (!link || !link->manager)
-                return;
-
-        hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex));
-        set_remove(link->manager->links_requesting_uuid, link);
-        link_clean(link);
-}
-
 static Link *link_free(Link *link) {
-        Link *carrier, *master;
         Address *address;
-        Route *route;
-        Iterator i;
 
         assert(link);
 
-        while ((route = set_first(link->routes)))
-                route_free(route);
-
-        while ((route = set_first(link->routes_foreign)))
-                route_free(route);
+        link->routes = set_free_with_destructor(link->routes, route_free);
+        link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
 
-        link->routes = set_free(link->routes);
-        link->routes_foreign = set_free(link->routes_foreign);
-
-        while ((address = set_first(link->addresses)))
-                address_free(address);
-
-        while ((address = set_first(link->addresses_foreign)))
-                address_free(address);
-
-        link->addresses = set_free(link->addresses);
-        link->addresses_foreign = set_free(link->addresses_foreign);
+        link->addresses = set_free_with_destructor(link->addresses, address_free);
+        link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
 
         while ((address = link->pool_addresses)) {
                 LIST_REMOVE(addresses, link->pool_addresses, address);
@@ -664,10 +654,7 @@ static Link *link_free(Link *link) {
         sd_ndisc_unref(link->ndisc);
         sd_radv_unref(link->radv);
 
-        link_detach_from_manager(link);
-
         free(link->ifname);
-
         free(link->kind);
 
         (void) unlink(link->state_file);
@@ -675,25 +662,12 @@ static Link *link_free(Link *link) {
 
         sd_device_unref(link->sd_device);
 
-        HASHMAP_FOREACH (carrier, link->bound_to_links, i)
-                hashmap_remove(link->bound_to_links, INT_TO_PTR(carrier->ifindex));
         hashmap_free(link->bound_to_links);
-
-        HASHMAP_FOREACH (carrier, link->bound_by_links, i)
-                hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex));
         hashmap_free(link->bound_by_links);
 
-        hashmap_free(link->slaves);
+        set_free_with_destructor(link->slaves, link_unref);
 
-        if (link->network) {
-                if (link->network->bond &&
-                    link_get(link->manager, link->network->bond->ifindex, &master) >= 0)
-                        (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
-
-                if (link->network->bridge &&
-                    link_get(link->manager, link->network->bridge->ifindex, &master) >= 0)
-                        (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex));
-        }
+        network_unref(link->network);
 
         return mfree(link);
 }
@@ -722,6 +696,10 @@ static void link_set_state(Link *link, LinkState state) {
         if (link->state == state)
                 return;
 
+        log_link_debug(link, "State changed: %s -> %s",
+                       link_state_to_string(link->state),
+                       link_state_to_string(state));
+
         link->state = state;
 
         link_send_changed(link, "AdministrativeState", NULL);
@@ -737,7 +715,7 @@ static void link_enter_unmanaged(Link *link) {
         link_dirty(link);
 }
 
-static int link_stop_clients(Link *link) {
+int link_stop_clients(Link *link) {
         int r = 0, k;
 
         assert(link);
@@ -822,6 +800,35 @@ static Address* link_find_dhcp_server_address(Link *link) {
         return NULL;
 }
 
+static int link_join_netdevs_after_configured(Link *link) {
+        NetDev *netdev;
+        Iterator i;
+        int r;
+
+        HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
+                if (netdev->ifindex > 0)
+                        /* Assume already enslaved. */
+                        continue;
+
+                if (netdev_get_create_type(netdev) != NETDEV_CREATE_AFTER_CONFIGURED)
+                        continue;
+
+                log_struct(LOG_DEBUG,
+                           LOG_LINK_INTERFACE(link),
+                           LOG_NETDEV_INTERFACE(netdev),
+                           LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname));
+
+                r = netdev_join(netdev, link, NULL);
+                if (r < 0)
+                        return log_struct_errno(LOG_WARNING, r,
+                                                LOG_LINK_INTERFACE(link),
+                                                LOG_NETDEV_INTERFACE(netdev),
+                                                LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname));
+        }
+
+        return 0;
+}
+
 static void link_enter_configured(Link *link) {
         assert(link);
         assert(link->network);
@@ -833,6 +840,8 @@ static void link_enter_configured(Link *link) {
 
         link_set_state(link, LINK_STATE_CONFIGURED);
 
+        (void) link_join_netdevs_after_configured(link);
+
         link_dirty(link);
 }
 
@@ -901,7 +910,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         return 1;
 }
 
-static int link_request_set_routes(Link *link) {
+int link_request_set_routes(Link *link) {
         enum {
                 PHASE_NON_GATEWAY, /* First phase: Routes without a gateway */
                 PHASE_GATEWAY,     /* Second phase: Routes with a gateway */
@@ -982,21 +991,18 @@ void link_check_ready(Link *link) {
         if (!link->routing_policy_rules_configured)
                 return;
 
-        if (link_ipv4ll_enabled(link))
-                if (!link->ipv4ll_address ||
-                    !link->ipv4ll_route)
-                        return;
+        if (link_ipv4ll_enabled(link) && !(link->ipv4ll_address && link->ipv4ll_route))
+                return;
 
         if (link_ipv6ll_enabled(link) &&
             in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
                 return;
 
-        if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) &&
-             !link->dhcp4_configured) ||
-            (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) &&
-             !link->dhcp6_configured) ||
-            (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) &&
-             !link->dhcp4_configured && !link->dhcp6_configured))
+        if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+            !(link->dhcp4_configured || link->dhcp6_configured) &&
+            !(link_ipv4ll_fallback_enabled(link) && link->ipv4ll_address && link->ipv4ll_route))
+                /* When DHCP is enabled, at least one protocol must provide an address, or
+                 * an IPv4ll fallback address must be configured. */
                 return;
 
         if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
@@ -1421,7 +1427,7 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
 
         log_link_debug(link, "Setting MTU done.");
 
-        if (link->state == LINK_STATE_PENDING)
+        if (link->state == LINK_STATE_INITIALIZED)
                 (void) link_configure_after_setting_mtu(link);
 
         return 1;
@@ -1717,28 +1723,6 @@ static int link_set_bond(Link *link) {
         return r;
 }
 
-static int link_append_to_master(Link *link, NetDev *netdev) {
-        Link *master;
-        int r;
-
-        assert(link);
-        assert(netdev);
-
-        r = link_get(link->manager, netdev->ifindex, &master);
-        if (r < 0)
-                return r;
-
-        r = hashmap_ensure_allocated(&master->slaves, NULL);
-        if (r < 0)
-                return r;
-
-        r = hashmap_put(master->slaves, INT_TO_PTR(link->ifindex), link);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int link_lldp_save(Link *link) {
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
@@ -1864,7 +1848,6 @@ static int link_acquire_ipv4_conf(Link *link) {
         int r;
 
         assert(link);
-        assert(link->network);
         assert(link->manager);
         assert(link->manager->event);
 
@@ -2501,6 +2484,55 @@ static void link_free_carrier_maps(Link *link) {
         return;
 }
 
+static int link_append_to_master(Link *link, NetDev *netdev) {
+        Link *master;
+        int r;
+
+        assert(link);
+        assert(netdev);
+
+        r = link_get(link->manager, netdev->ifindex, &master);
+        if (r < 0)
+                return r;
+
+        r = set_ensure_allocated(&master->slaves, NULL);
+        if (r < 0)
+                return r;
+
+        r = set_put(master->slaves, link);
+        if (r < 0)
+                return r;
+
+        link_ref(link);
+        return 0;
+}
+
+static void link_drop_from_master(Link *link, NetDev *netdev) {
+        Link *master;
+
+        assert(link);
+
+        if (!link->manager || !netdev)
+                return;
+
+        if (link_get(link->manager, netdev->ifindex, &master) < 0)
+                return;
+
+        link_unref(set_remove(master->slaves, link));
+}
+
+static void link_detach_from_manager(Link *link) {
+        if (!link || !link->manager)
+                return;
+
+        link_unref(set_remove(link->manager->links_requesting_uuid, link));
+        link_clean(link);
+
+        /* The following must be called at last. */
+        assert_se(hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)) == link);
+        link_unref(link);
+}
+
 void link_drop(Link *link) {
         if (!link || link->state == LINK_STATE_LINGER)
                 return;
@@ -2509,15 +2541,15 @@ void link_drop(Link *link) {
 
         link_free_carrier_maps(link);
 
+        if (link->network) {
+                link_drop_from_master(link, link->network->bridge);
+                link_drop_from_master(link, link->network->bond);
+        }
+
         log_link_debug(link, "Link removed");
 
         (void) unlink(link->state_file);
-
         link_detach_from_manager(link);
-
-        link_unref(link);
-
-        return;
 }
 
 static int link_joined(Link *link) {
@@ -2610,7 +2642,7 @@ static int link_enter_join_netdev(Link *link) {
 
         assert(link);
         assert(link->network);
-        assert(link->state == LINK_STATE_PENDING);
+        assert(link->state == LINK_STATE_INITIALIZED);
 
         link_set_state(link, LINK_STATE_CONFIGURING);
 
@@ -2681,10 +2713,12 @@ static int link_enter_join_netdev(Link *link) {
 
         HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) {
 
-                if (netdev->ifindex > 0) {
-                        link_joined(link);
+                if (netdev->ifindex > 0)
+                        /* Assume already enslaved. */
+                        continue;
+
+                if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED)
                         continue;
-                }
 
                 log_struct(LOG_DEBUG,
                            LOG_LINK_INTERFACE(link),
@@ -3021,7 +3055,7 @@ static int link_configure(Link *link) {
 
         assert(link);
         assert(link->network);
-        assert(link->state == LINK_STATE_PENDING);
+        assert(link->state == LINK_STATE_INITIALIZED);
 
         if (STRPTR_IN_SET(link->kind, "can", "vcan"))
                 return link_configure_can(link);
@@ -3074,7 +3108,7 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        if (link_ipv4ll_enabled(link)) {
+        if (link_ipv4ll_enabled(link) || link_ipv4ll_fallback_enabled(link)) {
                 r = ipv4ll_configure(link);
                 if (r < 0)
                         return r;
@@ -3170,7 +3204,7 @@ static int link_configure_after_setting_mtu(Link *link) {
 
         assert(link);
         assert(link->network);
-        assert(link->state == LINK_STATE_PENDING);
+        assert(link->state == LINK_STATE_INITIALIZED);
 
         if (link->setting_mtu)
                 return 0;
@@ -3321,10 +3355,13 @@ static int link_initialized_and_synced(Link *link) {
         assert(link->ifname);
         assert(link->manager);
 
-        if (link->state != LINK_STATE_PENDING)
-                return 1;
+        /* We may get called either from the asynchronous netlink callback,
+         * or directly for link_add() if running in a container. See link_add(). */
+        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED))
+                return 0;
 
         log_link_debug(link, "Link state is up-to-date");
+        link_set_state(link, LINK_STATE_INITIALIZED);
 
         r = link_new_bound_by_list(link);
         if (r < 0)
@@ -3339,7 +3376,7 @@ static int link_initialized_and_synced(Link *link) {
                                 &link->mac, &network);
                 if (r == -ENOENT) {
                         link_enter_unmanaged(link);
-                        return 1;
+                        return 0;
                 } else if (r == 0 && network->unmanaged) {
                         link_enter_unmanaged(link);
                         return 0;
@@ -3376,7 +3413,7 @@ static int link_initialized_and_synced(Link *link) {
         if (r < 0)
                 return r;
 
-        return 1;
+        return 0;
 }
 
 static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -3400,6 +3437,7 @@ int link_initialized(Link *link, sd_device *device) {
                 return 0;
 
         log_link_debug(link, "udev initialized link");
+        link_set_state(link, LINK_STATE_INITIALIZED);
 
         link->sd_device = sd_device_ref(device);
 
@@ -3690,7 +3728,7 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address) {
         link->ipv6ll_address = *address;
         link_check_ready(link);
 
-        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+        if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
                 r = link_acquire_ipv6_conf(link);
                 if (r < 0) {
                         link_enter_failed(link);
@@ -3706,7 +3744,7 @@ static int link_carrier_gained(Link *link) {
 
         assert(link);
 
-        if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_UNMANAGED, LINK_STATE_FAILED)) {
+        if (IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) {
                 r = link_acquire_conf(link);
                 if (r < 0) {
                         link_enter_failed(link);
@@ -3798,7 +3836,7 @@ int link_update(Link *link, sd_netlink_message *m) {
         assert(m);
 
         if (link->state == LINK_STATE_LINGER) {
-                log_link_info(link, "Link readded");
+                log_link_info(link, "Link re-added");
                 link_set_state(link, LINK_STATE_CONFIGURING);
 
                 r = link_new_carrier_maps(link);
@@ -3985,7 +4023,7 @@ int link_save(Link *link) {
         assert(link->manager);
 
         if (link->state == LINK_STATE_LINGER) {
-                unlink(link->state_file);
+                (void) unlink(link->state_file);
                 return 0;
         }
 
@@ -4001,7 +4039,6 @@ int link_save(Link *link) {
         if (r < 0)
                 goto fail;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
         (void) fchmod(fileno(f), 0644);
 
         fprintf(f,
@@ -4254,7 +4291,7 @@ int link_save(Link *link) {
                         "DHCP_LEASE=%s\n",
                         link->lease_file);
         } else
-                unlink(link->lease_file);
+                (void) unlink(link->lease_file);
 
         if (link->ipv4ll) {
                 struct in_addr address;
@@ -4318,6 +4355,7 @@ void link_clean(Link *link) {
 
 static const char* const link_state_table[_LINK_STATE_MAX] = {
         [LINK_STATE_PENDING] = "pending",
+        [LINK_STATE_INITIALIZED] = "initialized",
         [LINK_STATE_CONFIGURING] = "configuring",
         [LINK_STATE_CONFIGURED] = "configured",
         [LINK_STATE_UNMANAGED] = "unmanaged",