]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-link.c
Merge pull request #30967 from yuwata/network-can-required-operstate-for-online
[thirdparty/systemd.git] / src / network / networkd-link.c
index c237295606cdb80d77333bb827d9788f28396453..49ed8f59bb1804c67d1eb46203e3edd8135fe3a7 100644 (file)
@@ -17,7 +17,6 @@
 #include "bus-util.h"
 #include "device-private.h"
 #include "device-util.h"
-#include "dhcp-identifier.h"
 #include "dhcp-lease-internal.h"
 #include "env-file.h"
 #include "ethtool-util.h"
@@ -35,6 +34,7 @@
 #include "networkd-address.h"
 #include "networkd-bridge-fdb.h"
 #include "networkd-bridge-mdb.h"
+#include "networkd-bridge-vlan.h"
 #include "networkd-can.h"
 #include "networkd-dhcp-prefix-delegation.h"
 #include "networkd-dhcp-server.h"
@@ -52,6 +52,7 @@
 #include "networkd-nexthop.h"
 #include "networkd-queue.h"
 #include "networkd-radv.h"
+#include "networkd-route-util.h"
 #include "networkd-route.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-setlink.h"
 #include "udev-util.h"
 #include "vrf.h"
 
+void link_required_operstate_for_online(Link *link, LinkOperationalStateRange *ret) {
+        assert(link);
+        assert(ret);
+
+        if (link->network && operational_state_range_is_valid(&link->network->required_operstate_for_online))
+                *ret = link->network->required_operstate_for_online;
+        else if (link->iftype == ARPHRD_CAN)
+                *ret = (const LinkOperationalStateRange) {
+                        .min = LINK_OPERSTATE_CARRIER,
+                        .max = LINK_OPERSTATE_CARRIER,
+                };
+        else
+                *ret = LINK_OPERSTATE_RANGE_DEFAULT;
+}
+
 bool link_ipv6_enabled(Link *link) {
         assert(link);
 
@@ -94,6 +110,32 @@ bool link_ipv6_enabled(Link *link) {
         return false;
 }
 
+bool link_has_ipv6_connectivity(Link *link) {
+        LinkAddressState ipv6_address_state;
+
+        assert(link);
+
+        link_get_address_states(link, NULL, &ipv6_address_state, NULL);
+
+        switch (ipv6_address_state) {
+        case LINK_ADDRESS_STATE_ROUTABLE:
+                /* If the interface has a routable IPv6 address, then we assume yes. */
+                return true;
+
+        case LINK_ADDRESS_STATE_DEGRADED:
+                /* If the interface has only degraded IPv6 address (mostly, link-local address), then let's check
+                 * there is an IPv6 default gateway. */
+                return link_has_default_gateway(link, AF_INET6);
+
+        case LINK_ADDRESS_STATE_OFF:
+                /* No IPv6 address. */
+                return false;
+
+        default:
+                assert_not_reached();
+        }
+}
+
 static bool link_is_ready_to_configure_one(Link *link, bool allow_unmanaged) {
         assert(link);
 
@@ -160,6 +202,8 @@ static void link_free_engines(Link *link) {
         link->lldp_rx = sd_lldp_rx_unref(link->lldp_rx);
         link->lldp_tx = sd_lldp_tx_unref(link->lldp_tx);
 
+        link->ipv4acd_by_address = hashmap_free(link->ipv4acd_by_address);
+
         link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
 
         link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
@@ -179,7 +223,6 @@ static Link *link_free(Link *link) {
         link_dns_settings_clear(link);
 
         link->routes = set_free(link->routes);
-        link->nexthops = set_free(link->nexthops);
         link->neighbors = set_free(link->neighbors);
         link->addresses = set_free(link->addresses);
         link->qdiscs = set_free(link->qdiscs);
@@ -222,7 +265,9 @@ int link_get_by_index(Manager *m, int ifindex, Link **ret) {
         Link *link;
 
         assert(m);
-        assert(ifindex > 0);
+
+        if (ifindex <= 0)
+                return -EINVAL;
 
         link = hashmap_get(m->links_by_index, INT_TO_PTR(ifindex));
         if (!link)
@@ -291,7 +336,7 @@ void link_set_state(Link *link, LinkState state) {
 }
 
 int link_stop_engines(Link *link, bool may_keep_dhcp) {
-        int r = 0, k;
+        int r, ret = 0;
 
         assert(link);
         assert(link->manager);
@@ -304,53 +349,55 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) {
                           FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
 
         if (!keep_dhcp) {
-                k = sd_dhcp_client_stop(link->dhcp_client);
-                if (k < 0)
-                        r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
+                r = sd_dhcp_client_stop(link->dhcp_client);
+                if (r < 0)
+                        RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"));
         }
 
-        k = sd_dhcp_server_stop(link->dhcp_server);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop DHCPv4 server: %m");
+        r = sd_dhcp_server_stop(link->dhcp_server);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv4 server: %m"));
 
-        k = sd_lldp_rx_stop(link->lldp_rx);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop LLDP Rx: %m");
+        r = sd_lldp_rx_stop(link->lldp_rx);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Rx: %m"));
 
-        k = sd_lldp_tx_stop(link->lldp_tx);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop LLDP Tx: %m");
+        r = sd_lldp_tx_stop(link->lldp_tx);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop LLDP Tx: %m"));
 
-        k = sd_ipv4ll_stop(link->ipv4ll);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m");
+        r = sd_ipv4ll_stop(link->ipv4ll);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"));
 
-        k = ipv4acd_stop(link);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop IPv4 ACD client: %m");
+        r = ipv4acd_stop(link);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv4 ACD client: %m"));
 
-        k = sd_dhcp6_client_stop(link->dhcp6_client);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
+        r = sd_dhcp6_client_stop(link->dhcp6_client);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"));
 
-        k = dhcp_pd_remove(link, /* only_marked = */ false);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
+        r = dhcp_pd_remove(link, /* only_marked = */ false);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not remove DHCPv6 PD addresses and routes: %m"));
 
-        k = ndisc_stop(link);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m");
+        r = ndisc_stop(link);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"));
 
         ndisc_flush(link);
 
-        k = sd_radv_stop(link->radv);
-        if (k < 0)
-                r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m");
+        r = sd_radv_stop(link->radv);
+        if (r < 0)
+                RET_GATHER(ret, log_link_warning_errno(link, r, "Could not stop IPv6 Router Advertisement: %m"));
 
-        return r;
+        return ret;
 }
 
 void link_enter_failed(Link *link) {
+        int r;
+
         assert(link);
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
@@ -360,7 +407,22 @@ void link_enter_failed(Link *link) {
 
         link_set_state(link, LINK_STATE_FAILED);
 
-        (void) link_stop_engines(link, false);
+        if (!ratelimit_below(&link->automatic_reconfigure_ratelimit)) {
+                log_link_warning(link, "The interface entered the failed state frequently, refusing to reconfigure it automatically.");
+                goto stop;
+        }
+
+        log_link_info(link, "Trying to reconfigure the interface.");
+        r = link_reconfigure(link, /* force = */ true);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "Failed to reconfigure interface: %m");
+                goto stop;
+        }
+
+        return;
+
+stop:
+        (void) link_stop_engines(link, /* may_keep_dhcp = */ false);
 }
 
 void link_check_ready(Link *link) {
@@ -386,11 +448,9 @@ void link_check_ready(Link *link) {
         if (!link->activated)
                 return (void) log_link_debug(link, "%s(): link is not activated.", __func__);
 
-        if (link->iftype == ARPHRD_CAN) {
+        if (link->iftype == ARPHRD_CAN)
                 /* let's shortcut things for CAN which doesn't need most of checks below. */
-                link_set_state(link, LINK_STATE_CONFIGURED);
-                return;
-        }
+                goto ready;
 
         if (!link->stacked_netdevs_created)
                 return (void) log_link_debug(link, "%s(): stacked netdevs are not created.", __func__);
@@ -475,7 +535,7 @@ void link_check_ready(Link *link) {
         /* If the uplink for PD is self, then request the corresponding DHCP protocol is also ready. */
         if (dhcp_pd_is_uplink(link, link, /* accept_auto = */ false)) {
                 if (link_dhcp4_enabled(link) && link->network->dhcp_use_6rd &&
-                    link->dhcp_lease && dhcp4_lease_has_pd_prefix(link->dhcp_lease)) {
+                    sd_dhcp_lease_has_6rd(link->dhcp_lease)) {
                         if (!dhcp4_ready)
                                 return (void) log_link_debug(link, "%s(): DHCPv4 6rd prefix is assigned, but DHCPv4 protocol is not finished yet.", __func__);
                         if (!dhcp_pd_ready)
@@ -483,7 +543,7 @@ void link_check_ready(Link *link) {
                 }
 
                 if (link_dhcp6_enabled(link) && link->network->dhcp6_use_pd_prefix &&
-                    link->dhcp6_lease && dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) {
+                    sd_dhcp6_lease_has_pd_prefix(link->dhcp6_lease)) {
                         if (!dhcp6_ready)
                                 return (void) log_link_debug(link, "%s(): DHCPv6 IA_PD prefix is assigned, but DHCPv6 protocol is not finished yet.", __func__);
                         if (!dhcp_pd_ready)
@@ -901,15 +961,51 @@ static void link_drop_from_master(Link *link) {
         link_unref(set_remove(master->slaves, link));
 }
 
-static void link_drop_requests(Link *link) {
+static int link_drop_requests(Link *link) {
         Request *req;
+        int ret = 0;
 
         assert(link);
         assert(link->manager);
 
-        ORDERED_SET_FOREACH(req, link->manager->request_queue)
-                if (req->link == link)
-                        request_detach(link->manager, req);
+        ORDERED_SET_FOREACH(req, link->manager->request_queue) {
+                if (req->link != link)
+                        continue;
+
+                /* If the request is already called, but its reply is not received, then we need to
+                 * drop the configuration (e.g. address) here. Note, if the configuration is known,
+                 * it will be handled later by link_drop_foreign_addresses() or so. */
+                if (req->waiting_reply && link->state != LINK_STATE_LINGER)
+                        switch (req->type) {
+                        case REQUEST_TYPE_ADDRESS: {
+                                Address *address = ASSERT_PTR(req->userdata);
+
+                                if (address_get(link, address, NULL) < 0)
+                                        RET_GATHER(ret, address_remove(address, link));
+                                break;
+                        }
+                        case REQUEST_TYPE_NEIGHBOR: {
+                                Neighbor *neighbor = ASSERT_PTR(req->userdata);
+
+                                if (neighbor_get(link, neighbor, NULL) < 0)
+                                        RET_GATHER(ret, neighbor_remove(neighbor, link));
+                                break;
+                        }
+                        case REQUEST_TYPE_NEXTHOP: {
+                                NextHop *nexthop = ASSERT_PTR(req->userdata);
+
+                                if (nexthop_get_by_id(link->manager, nexthop->id, NULL) < 0)
+                                        RET_GATHER(ret, nexthop_remove(nexthop, link->manager));
+                                break;
+                        }
+                        default:
+                                ;
+                        }
+
+                request_detach(req);
+        }
+
+        return ret;
 }
 
 static Link *link_drop(Link *link) {
@@ -923,7 +1019,7 @@ static Link *link_drop(Link *link) {
         /* Drop all references from other links and manager. Note that async netlink calls may have
          * references to the link, and they will be dropped when we receive replies. */
 
-        link_drop_requests(link);
+        (void) link_drop_requests(link);
 
         link_free_bound_to_list(link);
         link_free_bound_by_list(link);
@@ -1019,6 +1115,10 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_configure_mtu(link);
+        if (r < 0)
+                return r;
+
         if (link->iftype == ARPHRD_CAN) {
                 /* let's shortcut things for CAN which doesn't need most of what's done below. */
                 r = link_request_to_set_can(link);
@@ -1052,10 +1152,6 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
-        r = link_configure_mtu(link);
-        if (r < 0)
-                return r;
-
         r = link_request_to_set_addrgen_mode(link);
         if (r < 0)
                 return r;
@@ -1210,10 +1306,20 @@ int link_reconfigure_impl(Link *link, bool force) {
                 return 0;
 
         if (network) {
+                _cleanup_free_ char *joined = strv_join(network->dropins, ", ");
+
                 if (link->state == LINK_STATE_INITIALIZED)
-                        log_link_info(link, "Configuring with %s.", network->filename);
+                        log_link_info(link, "Configuring with %s%s%s%s.",
+                                      network->filename,
+                                      isempty(joined) ? "" : " (dropins: ",
+                                      joined,
+                                      isempty(joined) ? "" : ")");
                 else
-                        log_link_info(link, "Reconfiguring with %s.", network->filename);
+                        log_link_info(link, "Reconfiguring with %s%s%s%s.",
+                                      network->filename,
+                                      isempty(joined) ? "" : " (dropins: ",
+                                      joined,
+                                      isempty(joined) ? "" : ")");
         } else
                 log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO,
                               "Unmanaging interface.");
@@ -1223,7 +1329,9 @@ int link_reconfigure_impl(Link *link, bool force) {
         if (r < 0)
                 return r;
 
-        link_drop_requests(link);
+        r = link_drop_requests(link);
+        if (r < 0)
+                return r;
 
         if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES)
                 /* When a new/updated .network file is assigned, first make all configs (addresses,
@@ -1436,6 +1544,7 @@ static int link_check_initialized(Link *link) {
 
 int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t action) {
         int r, ifindex;
+        const char *s;
         Link *link;
 
         assert(m);
@@ -1470,6 +1579,15 @@ int manager_udev_process_link(Manager *m, sd_device *device, sd_device_action_t
                 return 0;
         }
 
+        r = sd_device_get_property_value(device, "ID_NET_MANAGED_BY", &s);
+        if (r < 0 && r != -ENOENT)
+                log_device_debug_errno(device, r, "Failed to get ID_NET_MANAGED_BY udev property, ignoring: %m");
+        if (r >= 0 && !streq(s, "io.systemd.Network")) {
+                log_device_debug(device, "Interface is requested to be managed by '%s', not managing the interface.", s);
+                link_set_state(link, LINK_STATE_UNMANAGED);
+                return 0;
+        }
+
         r = link_initialized(link, device);
         if (r < 0)
                 link_enter_failed(link);
@@ -1615,7 +1733,7 @@ static int link_carrier_lost(Link *link) {
                 usec = 5 * USEC_PER_SEC;
 
         else
-                /* Otherwise, use the currently set value. */
+                /* Otherwise, use the implied default value. */
                 usec = link->network->ignore_carrier_loss_usec;
 
         if (usec == USEC_INFINITY)
@@ -1685,28 +1803,13 @@ static bool link_is_enslaved(Link *link) {
         return false;
 }
 
-static LinkAddressState address_state_from_scope(uint8_t scope) {
-        if (scope < RT_SCOPE_SITE)
-                /* universally accessible addresses found */
-                return LINK_ADDRESS_STATE_ROUTABLE;
-
-        if (scope < RT_SCOPE_HOST)
-                /* only link or site local addresses found */
-                return LINK_ADDRESS_STATE_DEGRADED;
-
-        /* no useful addresses found */
-        return LINK_ADDRESS_STATE_OFF;
-}
-
 void link_update_operstate(Link *link, bool also_update_master) {
         LinkOperationalState operstate;
         LinkCarrierState carrier_state;
         LinkAddressState ipv4_address_state, ipv6_address_state, address_state;
         LinkOnlineState online_state;
         _cleanup_strv_free_ char **p = NULL;
-        uint8_t ipv4_scope = RT_SCOPE_NOWHERE, ipv6_scope = RT_SCOPE_NOWHERE;
         bool changed = false;
-        Address *address;
 
         assert(link);
 
@@ -1733,20 +1836,7 @@ void link_update_operstate(Link *link, bool also_update_master) {
                 }
         }
 
-        SET_FOREACH(address, link->addresses) {
-                if (!address_is_ready(address))
-                        continue;
-
-                if (address->family == AF_INET)
-                        ipv4_scope = MIN(ipv4_scope, address->scope);
-
-                if (address->family == AF_INET6)
-                        ipv6_scope = MIN(ipv6_scope, address->scope);
-        }
-
-        ipv4_address_state = address_state_from_scope(ipv4_scope);
-        ipv6_address_state = address_state_from_scope(ipv6_scope);
-        address_state = address_state_from_scope(MIN(ipv4_scope, ipv6_scope));
+        link_get_address_states(link, &ipv4_address_state, &ipv6_address_state, &address_state);
 
         /* Mapping of address and carrier state vs operational state
          *                                                     carrier state
@@ -1770,12 +1860,16 @@ void link_update_operstate(Link *link, bool also_update_master) {
         else
                 operstate = LINK_OPERSTATE_ENSLAVED;
 
+        LinkOperationalStateRange req;
+        link_required_operstate_for_online(link, &req);
+
         /* Only determine online state for managed links with RequiredForOnline=yes */
         if (!link->network || !link->network->required_for_online)
                 online_state = _LINK_ONLINE_STATE_INVALID;
-        else if (operstate < link->network->required_operstate_for_online.min ||
-                 operstate > link->network->required_operstate_for_online.max)
+
+        else if (!operational_state_is_in_range(operstate, &req))
                 online_state = LINK_ONLINE_STATE_OFFLINE;
+
         else {
                 AddressFamily required_family = link->network->required_family_for_online;
                 bool needs_ipv4 = required_family & ADDRESS_FAMILY_IPV4;
@@ -1786,14 +1880,14 @@ void link_update_operstate(Link *link, bool also_update_master) {
                  * to offline in the blocks below. */
                 online_state = LINK_ONLINE_STATE_ONLINE;
 
-                if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_DEGRADED) {
+                if (req.min >= LINK_OPERSTATE_DEGRADED) {
                         if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_DEGRADED)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                         if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_DEGRADED)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                 }
 
-                if (link->network->required_operstate_for_online.min >= LINK_OPERSTATE_ROUTABLE) {
+                if (req.min >= LINK_OPERSTATE_ROUTABLE) {
                         if (needs_ipv4 && ipv4_address_state < LINK_ADDRESS_STATE_ROUTABLE)
                                 online_state = LINK_ONLINE_STATE_OFFLINE;
                         if (needs_ipv6 && ipv6_address_state < LINK_ADDRESS_STATE_ROUTABLE)
@@ -1978,20 +2072,18 @@ static int link_update_master(Link *link, sd_netlink_message *message) {
         if (master_ifindex == link->ifindex)
                 master_ifindex = 0;
 
-        if (master_ifindex == link->master_ifindex)
-                return 0;
-
-        if (link->master_ifindex == 0)
-                log_link_debug(link, "Attached to master interface: %i", master_ifindex);
-        else if (master_ifindex == 0)
-                log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
-        else
-                log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
-                               special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
-
-        link_drop_from_master(link);
+        if (master_ifindex != link->master_ifindex) {
+                if (link->master_ifindex == 0)
+                        log_link_debug(link, "Attached to master interface: %i", master_ifindex);
+                else if (master_ifindex == 0)
+                        log_link_debug(link, "Detached from master interface: %i", link->master_ifindex);
+                else
+                        log_link_debug(link, "Master interface changed: %i %s %i", link->master_ifindex,
+                                       special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), master_ifindex);
 
-        link->master_ifindex = master_ifindex;
+                link_drop_from_master(link);
+                link->master_ifindex = master_ifindex;
+        }
 
         r = link_append_to_master(link);
         if (r < 0)
@@ -2424,6 +2516,10 @@ static int link_update(Link *link, sd_netlink_message *message) {
         if (r < 0)
                 return r;
 
+        r = link_update_bridge_vlan(link, message);
+        if (r < 0)
+                return r;
+
         return needs_reconfigure;
 }
 
@@ -2492,11 +2588,14 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
                 .n_ref = 1,
                 .state = LINK_STATE_PENDING,
                 .online_state = _LINK_ONLINE_STATE_INVALID,
+                .automatic_reconfigure_ratelimit = (const RateLimit) { .interval = 10 * USEC_PER_SEC, .burst = 5 },
                 .ifindex = ifindex,
                 .iftype = iftype,
                 .ifname = TAKE_PTR(ifname),
                 .kind = TAKE_PTR(kind),
 
+                .bridge_vlan_pvid = UINT16_MAX,
+
                 .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
 
                 .state_file = TAKE_PTR(state_file),