summary refs log tree commit diff
path: root/src/core/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/devices')
-rw-r--r--src/core/devices/nm-device-bond.c85
-rw-r--r--src/core/devices/nm-device-bond.h3
-rw-r--r--src/core/devices/nm-device.c69
-rw-r--r--src/core/devices/wifi/nm-device-wifi.c4
-rw-r--r--src/core/devices/wwan/nm-modem.c1
5 files changed, 151 insertions, 11 deletions
diff --git a/src/core/devices/nm-device-bond.c b/src/core/devices/nm-device-bond.c
index b60dd3f1..3ab17aff 100644
--- a/src/core/devices/nm-device-bond.c
+++ b/src/core/devices/nm-device-bond.c
@@ -923,6 +923,91 @@ deactivate(NMDevice *device)
 
 /*****************************************************************************/
 
+gboolean
+nm_device_bond_is_slb(NMDevice *device)
+{
+    NMConnection  *connection;
+    NMSettingBond *s_bond;
+
+    connection = nm_device_get_applied_connection(device);
+    if (!connection)
+        return FALSE;
+
+    s_bond = nm_connection_get_setting_bond(connection);
+    if (!s_bond)
+        return FALSE;
+
+    if (!_nm_setting_bond_opt_value_as_intbool(s_bond, NM_SETTING_BOND_OPTION_BALANCE_SLB))
+        return FALSE;
+
+    return TRUE;
+}
+
+gboolean
+nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port)
+{
+    NMDeviceBond      *self               = NM_DEVICE_BOND(controller);
+    int                port_ifindex       = nm_device_get_ifindex(port);
+    int                controller_ifindex = nm_device_get_ifindex(controller);
+    NML3Cfg           *l3cfg              = nm_device_get_l3cfg(controller);
+    NMDevice          *bond_controller    = nm_device_get_controller(controller);
+    NML3Cfg           *bridge_l3cfg;
+    gs_free in_addr_t *addrs_array = NULL;
+    gsize              addrs_len;
+
+    addrs_array = nm_l3cfg_get_configured_ip4_addresses(l3cfg, &addrs_len);
+
+    if (addrs_len > 0) {
+        /* the bond has IPs configured, it is not attached to a
+         * bridge then. */
+        if (!nm_bond_manager_send_arp(controller_ifindex,
+                                      -1,
+                                      nm_device_get_platform(port),
+                                      addrs_array,
+                                      addrs_len)) {
+            _LOGT(LOGD_BOND,
+                  "failed to send gARP on port %s (ifindex %d)",
+                  nm_device_get_iface(port),
+                  port_ifindex);
+            return FALSE;
+        }
+    } else if (bond_controller
+               && nm_device_get_device_type(bond_controller) == NM_DEVICE_TYPE_BRIDGE) {
+        /* the bond is attached to a bridge, firts let's check if the bridge has IP
+         * configuration. */
+        bridge_l3cfg = nm_device_get_l3cfg(bond_controller);
+        addrs_array  = nm_l3cfg_get_configured_ip4_addresses(bridge_l3cfg, &addrs_len);
+        if (addrs_len > 0) {
+            /* the bridge has IPs configured, announcing them on the bond */
+            if (!nm_bond_manager_send_arp(controller_ifindex,
+                                          -1,
+                                          nm_device_get_platform(port),
+                                          addrs_array,
+                                          addrs_len)) {
+                _LOGT(LOGD_BOND,
+                      "failed to send gARP on port %s (ifindex %d) on behalf of bridge",
+                      nm_device_get_iface(port),
+                      port_ifindex);
+                return FALSE;
+            }
+        }
+
+        /* we are going to ARP probe the content of the FDB table */
+        if (!nm_bond_manager_send_arp(controller_ifindex,
+                                      nm_device_get_ifindex(bond_controller),
+                                      nm_device_get_platform(port),
+                                      NULL,
+                                      0)) {
+            _LOGT(LOGD_BOND, "failed to send ARP probing with content of FDB table");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+/*****************************************************************************/
+
 static void
 nm_device_bond_init(NMDeviceBond *self)
 {
diff --git a/src/core/devices/nm-device-bond.h b/src/core/devices/nm-device-bond.h
index 083189bb..2a415843 100644
--- a/src/core/devices/nm-device-bond.h
+++ b/src/core/devices/nm-device-bond.h
@@ -23,4 +23,7 @@ typedef struct _NMDeviceBondClass NMDeviceBondClass;
 
 GType nm_device_bond_get_type(void);
 
+gboolean nm_device_bond_is_slb(NMDevice *device);
+gboolean nm_device_bond_announce_ports_on_slb(NMDevice *controller, NMDevice *port);
+
 #endif /* NM_DEVICE_BOND_H */
diff --git a/src/core/devices/nm-device.c b/src/core/devices/nm-device.c
index 82c2d6b8..516e13df 100644
--- a/src/core/devices/nm-device.c
+++ b/src/core/devices/nm-device.c
@@ -78,6 +78,7 @@
 #include "nm-hostname-manager.h"
 
 #include "nm-device-generic.h"
+#include "nm-device-bond.h"
 #include "nm-device-bridge.h"
 #include "nm-device-loopback.h"
 #include "nm-device-vlan.h"
@@ -5403,6 +5404,7 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
     NMDevicePrivate      *priv     = NM_DEVICE_GET_PRIVATE(self);
     NMPlatform           *platform = nm_device_get_platform(self);
     const NMPlatformLink *pllink;
+    NMPLinkAddress        permanent_hwaddr;
     NMLinkType            link_type;
     const guint8         *hwaddr;
     guint8                pseudo_hwaddr[ETH_ALEN];
@@ -5446,6 +5448,21 @@ get_ip_iface_identifier(NMDevice *self, NMUtilsIPv6IfaceId *out_iid)
             hwaddr_len = G_N_ELEMENTS(pseudo_hwaddr);
             link_type  = NM_LINK_TYPE_ETHERNET;
         }
+    } else if (NM_IN_SET(pllink->type,
+                         NM_LINK_TYPE_VTI6,
+                         NM_LINK_TYPE_IP6TNL,
+                         NM_LINK_TYPE_IP6GRE)) {
+        /* Use the "permanent" 48-bit address to construct a EUI64
+         * according to RFC 4291 Appendix A. */
+        if (!nm_platform_link_get_permanent_address(platform, pllink, &permanent_hwaddr))
+            return FALSE;
+        if (permanent_hwaddr.len < ETH_ALEN)
+            return FALSE;
+
+        memcpy(pseudo_hwaddr, permanent_hwaddr.data, ETH_ALEN);
+        hwaddr     = pseudo_hwaddr;
+        hwaddr_len = ETH_ALEN;
+        link_type  = NM_LINK_TYPE_ETHERNET;
     }
 
     success = nm_utils_get_ipv6_interface_identifier(link_type,
@@ -7339,10 +7356,12 @@ device_link_changed(gpointer user_data)
     NMDevicePrivate                *priv              = NM_DEVICE_GET_PRIVATE(self);
     gboolean                        ip_ifname_changed = FALSE;
     nm_auto_nmpobj const NMPObject *pllink_keep_alive = NULL;
+    NMDevice                       *controller;
     const NMPlatformLink           *pllink;
     const char                     *str;
     int                             ifindex;
     gboolean                        was_up;
+    gboolean                        carrier_was_up;
     gboolean                        update_unmanaged_specs = FALSE;
     gboolean                        got_hw_addr            = FALSE, had_hw_addr;
     gboolean                        seen_down              = priv->device_link_changed_down;
@@ -7425,6 +7444,8 @@ device_link_changed(gpointer user_data)
             _LOGD(LOGD_DEVICE, "IPv6 tokenized identifier present on device %s", priv->iface);
     }
 
+    carrier_was_up = priv->carrier;
+
     /* Update carrier from link event if applicable. */
     if (nm_device_has_capability(self, NM_DEVICE_CAP_CARRIER_DETECT)
         && !nm_device_has_capability(self, NM_DEVICE_CAP_NONSTANDARD_CARRIER))
@@ -7441,6 +7462,35 @@ device_link_changed(gpointer user_data)
     was_up   = priv->up;
     priv->up = NM_FLAGS_HAS(pllink->n_ifi_flags, IFF_UP);
 
+    if ((was_up && !priv->up) || (carrier_was_up && !priv->carrier)) {
+        /* the link was up and now is down, or the carrier was up and now is down. We must
+         * check if this is a port of a bond and if that bond is in balance-slb mode to perform
+         * gARP on the controller's port.
+         */
+        controller = nm_device_get_controller(self);
+        if (controller && nm_device_get_device_type(controller) == NM_DEVICE_TYPE_BOND
+            && nm_device_bond_is_slb(controller)) {
+            NMDevicePrivate *controller_priv = NM_DEVICE_GET_PRIVATE(controller);
+            PortInfo        *info;
+
+            _LOGT(
+                LOGD_CORE,
+                "controller %s is a bond in bonding-slb mode, redirecting traffic to another port",
+                nm_device_get_iface(controller));
+
+            c_list_for_each_entry (info, &controller_priv->ports, lst_port) {
+                if (info->port != self && NM_DEVICE_GET_PRIVATE(info->port)->carrier) {
+                    _LOGT(LOGD_CORE,
+                          "sending gARP on port %s (ifindex %d)",
+                          nm_device_get_iface(info->port),
+                          nm_device_get_ifindex(info->port));
+                    if (nm_device_bond_announce_ports_on_slb(controller, info->port))
+                        break;
+                }
+            }
+        }
+    }
+
     if (pllink->initialized && nm_device_get_unmanaged_flags(self, NM_UNMANAGED_PLATFORM_INIT)) {
         nm_device_set_unmanaged_by_user_udev(self);
         nm_device_set_unmanaged_by_user_conf(self);
@@ -9485,6 +9535,7 @@ check_connection_compatible(NMDevice     *self,
     NMSettingMatch       *s_match;
     const GSList         *specs;
     gboolean              has_match = FALSE;
+    NMSettingSriov       *s_sriov   = NULL;
 
     klass = NM_DEVICE_GET_CLASS(self);
     if (klass->connection_type_check_compatible) {
@@ -9502,12 +9553,14 @@ check_connection_compatible(NMDevice     *self,
         return FALSE;
     }
 
-    if (!nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)
-        && nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV)) {
-        nm_utils_error_set_literal(error,
-                                   NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
-                                   "device does not support SR-IOV");
-        return FALSE;
+    if (!nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) {
+        s_sriov = (NMSettingSriov *) nm_connection_get_setting(connection, NM_TYPE_SETTING_SRIOV);
+        if (s_sriov && nm_setting_sriov_get_total_vfs(s_sriov)) {
+            nm_utils_error_set_literal(error,
+                                       NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
+                                       "device does not support SR-IOV");
+            return FALSE;
+        }
     }
 
     conn_iface = nm_manager_get_connection_iface(NM_MANAGER_GET, connection, NULL, NULL, &local);
@@ -10118,7 +10171,7 @@ activate_stage1_device_prepare(NMDevice *self)
             s_sriov = nm_device_get_applied_setting(self, NM_TYPE_SETTING_SRIOV);
         }
 
-        if (s_sriov) {
+        if (s_sriov && nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV)) {
             nm_auto_freev NMPlatformVF **plat_vfs = NULL;
             gs_free_error GError        *error    = NULL;
             NMSriovVF                   *vf;
@@ -10126,8 +10179,6 @@ activate_stage1_device_prepare(NMDevice *self)
             guint                        num;
             guint                        i;
 
-            nm_assert(nm_device_has_capability(self, NM_DEVICE_CAP_SRIOV));
-
             autoprobe = nm_setting_sriov_get_autoprobe_drivers(s_sriov);
             if (autoprobe == NM_TERNARY_DEFAULT) {
                 autoprobe = nm_config_data_get_connection_default_int64(
diff --git a/src/core/devices/wifi/nm-device-wifi.c b/src/core/devices/wifi/nm-device-wifi.c
index f24b9733..ea65499e 100644
--- a/src/core/devices/wifi/nm-device-wifi.c
+++ b/src/core/devices/wifi/nm-device-wifi.c
@@ -327,7 +327,7 @@ _scan_request_ssids_track(NMDeviceWifiPrivate *priv, const GPtrArray *ssids)
         priv->scan_request_ssids_hash = g_hash_table_new(nm_pg_bytes_hash, nm_pg_bytes_equal);
 
     /* Do a little dance. New elements shall keep their order as in @ssids, but all
-     * new elements should be sorted in the list preexisting elements of the list.
+     * new elements should be sorted before preexisting elements of the list.
      * First move the old elements away, and splice them back afterwards. */
     c_list_init(&old_lst_head);
     c_list_splice(&old_lst_head, &priv->scan_request_ssids_lst_head);
@@ -348,6 +348,8 @@ _scan_request_ssids_track(NMDeviceWifiPrivate *priv, const GPtrArray *ssids)
             g_hash_table_add(priv->scan_request_ssids_hash, d);
         } else
             d->timestamp_msec = now_msec;
+
+        c_list_unlink_stale(&d->lst);
         c_list_link_tail(&priv->scan_request_ssids_lst_head, &d->lst);
     }
 
diff --git a/src/core/devices/wwan/nm-modem.c b/src/core/devices/wwan/nm-modem.c
index ea0fa7aa..23e7de4a 100644
--- a/src/core/devices/wwan/nm-modem.c
+++ b/src/core/devices/wwan/nm-modem.c
@@ -206,7 +206,6 @@ nm_modem_emit_signal_new_config(NMModem                  *self,
     nm_assert(NM_IS_MODEM(self));
     nm_assert_addr_family(addr_family);
     nm_assert(!l3cd || NM_IS_L3_CONFIG_DATA(l3cd));
-    nm_assert(!do_auto || addr_family == AF_INET6);
     nm_assert(!iid || addr_family == AF_INET6);
     nm_assert(!error || (!l3cd && !do_auto && !iid));