wifi/iwd: merge branch 'balrog-kun/NetworkManager:iwd-fixes'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1338
This commit is contained in:
Thomas Haller
2022-08-24 20:46:36 +02:00
3 changed files with 137 additions and 18 deletions

View File

@@ -70,6 +70,7 @@ typedef struct {
bool secrets_failed : 1;
bool networks_requested : 1;
bool networks_changed : 1;
bool assuming : 1;
gint64 last_scan;
uint32_t ap_id;
guint32 rate;
@@ -77,6 +78,7 @@ typedef struct {
GDBusMethodInvocation *pending_agent_request;
NMActiveConnection *assumed_ac;
guint assumed_ac_timeout;
NMIwdManager *manager;
} NMDeviceIwdPrivate;
struct _NMDeviceIwd {
@@ -289,6 +291,7 @@ insert_ap_from_network(NMDeviceIwd *self,
gint64 last_seen_msec,
int16_t signal)
{
NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self);
gs_unref_object GDBusProxy *network_proxy = NULL;
nm_auto_ref_string NMRefString *bss_path = nm_ref_string_new(path);
NMWifiAP *ap;
@@ -299,7 +302,7 @@ insert_ap_from_network(NMDeviceIwd *self,
}
network_proxy =
nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(), path, NM_IWD_NETWORK_INTERFACE);
nm_iwd_manager_get_dbus_interface(priv->manager, path, NM_IWD_NETWORK_INTERFACE);
ap = ap_from_network(self, network_proxy, bss_path, last_seen_msec, signal);
if (!ap)
@@ -581,6 +584,10 @@ deactivate(NMDevice *device)
if (!priv->dbus_obj)
return;
/* Don't cause IWD to break the connection being assumed */
if (priv->assuming)
return;
if (priv->dbus_station_proxy) {
gs_unref_variant GVariant *value =
g_dbus_proxy_get_cached_property(priv->dbus_station_proxy, "State");
@@ -673,7 +680,7 @@ deactivate_async(NMDevice *device,
}
static gboolean
is_connection_known_network(NMConnection *connection)
is_connection_known_network(NMIwdManager *manager, NMConnection *connection)
{
NMIwdNetworkSecurity security;
gs_free char *ssid = NULL;
@@ -681,17 +688,17 @@ is_connection_known_network(NMConnection *connection)
if (!nm_wifi_connection_get_iwd_ssid_and_security(connection, &ssid, &security))
return FALSE;
return nm_iwd_manager_is_known_network(nm_iwd_manager_get(), ssid, security);
return nm_iwd_manager_is_known_network(manager, ssid, security);
}
static gboolean
is_ap_known_network(NMWifiAP *ap)
is_ap_known_network(NMIwdManager *manager, NMWifiAP *ap)
{
gs_unref_object GDBusProxy *network_proxy = NULL;
gs_unref_variant GVariant *known_network = NULL;
network_proxy =
nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(),
nm_iwd_manager_get_dbus_interface(manager,
nm_ref_string_get_str(nm_wifi_ap_get_supplicant_path(ap)),
NM_IWD_NETWORK_INTERFACE);
if (!network_proxy)
@@ -794,7 +801,8 @@ check_connection_compatible(NMDevice *device, NMConnection *connection, GError *
* thus are Known Networks.
*/
if (security == NM_IWD_NETWORK_SECURITY_8021X) {
if (!is_connection_known_network(connection)) {
if (!is_connection_known_network(priv->manager, connection)
&& !nm_iwd_manager_is_recently_mirrored(priv->manager, ssid)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
"802.1x connections must have IWD provisioning files");
@@ -927,7 +935,9 @@ check_connection_available(NMDevice *device,
*/
if (nm_wifi_connection_get_iwd_ssid_and_security(connection, NULL, &security)
&& security == NM_IWD_NETWORK_SECURITY_8021X) {
if (!is_ap_known_network(ap)) {
if (!is_ap_known_network(priv->manager, ap)
&& !nm_iwd_manager_is_recently_mirrored(priv->manager,
nm_setting_wireless_get_ssid(s_wifi))) {
nm_utils_error_set_literal(
error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
@@ -2044,7 +2054,7 @@ assume_connection(NMDeviceIwd *self, NMWifiAP *ap)
* becomes "managed" only when ACTIVATED but for IWD it's really
* managed when IP_CONFIG starts.
*/
sett_conn = nm_iwd_manager_get_ap_mirror_connection(nm_iwd_manager_get(), ap);
sett_conn = nm_iwd_manager_get_ap_mirror_connection(priv->manager, ap);
if (!sett_conn)
goto error;
@@ -2217,7 +2227,8 @@ act_stage1_prepare(NMDevice *device, NMDeviceStateReason *out_failure_reason)
* for a first-time connection to a hidden network. If a hidden network is
* a Known Network it should still have been in the AP list.
*/
if (!nm_setting_wireless_get_hidden(s_wireless) || is_connection_known_network(connection))
if (!nm_setting_wireless_get_hidden(s_wireless)
|| is_connection_known_network(priv->manager, connection))
return NM_ACT_STAGE_RETURN_FAILURE;
add_new:
@@ -2270,6 +2281,18 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
goto out_fail;
}
/* With priv->iwd_autoconnect we have to let IWD handle retries for
* infrastructure networks. IWD will not necessarily retry the same
* network after a failure but it will likely go into an autoconnect
* mode and we don't want to try to override the logic. We don't need
* to reset the retry count so we set no timeout.
*/
if (priv->iwd_autoconnect) {
NMSettingsConnection *sett_conn = nm_act_request_get_settings_connection(req);
nm_settings_connection_autoconnect_retries_set(sett_conn, 0);
}
/* With priv->iwd_autoconnect, if we're assuming a connection because
* of a state change to "connecting", signal stage 2 is still running.
* If "connected" or "roaming", we can go right to the IP_CONFIG state
@@ -2310,7 +2333,9 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
* fail, for other combinations we will let the Connect call fail
* or ask us for any missing secrets through the Agent.
*/
if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(ap)) {
if (nm_connection_get_setting_802_1x(connection) && !is_ap_known_network(priv->manager, ap)
&& !nm_iwd_manager_is_recently_mirrored(priv->manager,
nm_setting_wireless_get_ssid(s_wireless))) {
_LOGI(LOGD_DEVICE | LOGD_WIFI,
"Activation: (wifi) access point '%s' has 802.1x security but is not configured "
"in IWD.",
@@ -2351,7 +2376,7 @@ act_stage2_config(NMDevice *device, NMDeviceStateReason *out_failure_reason)
}
network_proxy = nm_iwd_manager_get_dbus_interface(
nm_iwd_manager_get(),
priv->manager,
nm_ref_string_get_str(nm_wifi_ap_get_supplicant_path(ap)),
NM_IWD_NETWORK_INTERFACE);
if (!network_proxy) {
@@ -2719,12 +2744,20 @@ state_changed(NMDeviceIwd *self, const char *new_state)
"IWD is connecting to the wrong AP, %s activation",
switch_ap ? "replacing" : "aborting");
cleanup_association_attempt(self, !switch_ap);
nm_device_state_changed(device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
if (switch_ap)
assume_connection(self, ap);
if (!switch_ap) {
nm_device_state_changed(device,
NM_DEVICE_STATE_FAILED,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
return;
}
priv->assuming = TRUE; /* Don't send Station.Disconnect() */
nm_device_state_changed(device,
NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT);
priv->assuming = FALSE;
assume_connection(self, ap);
return;
}
@@ -3101,7 +3134,7 @@ nm_device_iwd_set_dbus_object(NMDeviceIwd *self, GDBusObject *object)
goto error;
}
adapter_proxy = nm_iwd_manager_get_dbus_interface(nm_iwd_manager_get(),
adapter_proxy = nm_iwd_manager_get_dbus_interface(priv->manager,
g_variant_get_string(value, NULL),
NM_IWD_WIPHY_INTERFACE);
if (!adapter_proxy) {
@@ -3411,7 +3444,7 @@ nm_device_iwd_init(NMDeviceIwd *self)
g_signal_connect(self, "notify::" NM_DEVICE_AUTOCONNECT, G_CALLBACK(autoconnect_changed), self);
/* Make sure the manager is running */
(void) nm_iwd_manager_get();
priv->manager = g_object_ref(nm_iwd_manager_get());
}
NMDevice *
@@ -3443,6 +3476,8 @@ dispose(GObject *object)
G_OBJECT_CLASS(nm_device_iwd_parent_class)->dispose(object);
nm_assert(c_list_is_empty(&priv->aps_lst_head));
g_clear_object(&priv->manager);
}
static void

View File

@@ -46,6 +46,11 @@ typedef struct {
const KnownNetworkId *id;
} KnownNetworkData;
typedef struct {
GBytes *ssid;
gint64 timestamp;
} RecentlyMirroredData;
typedef struct {
NMManager *manager;
NMSettings *settings;
@@ -62,6 +67,7 @@ typedef struct {
GHashTable *p2p_devices;
NMIwdWfdInfo wfd_info;
guint wfd_use_count;
GSList *recently_mirrored;
} NMIwdManagerPrivate;
struct _NMIwdManager {
@@ -353,6 +359,70 @@ register_agent(NMIwdManager *self)
/*****************************************************************************/
static void
recently_mirrored_data_free(void *data)
{
RecentlyMirroredData *rmd = data;
g_bytes_unref(rmd->ssid);
g_free(rmd);
}
/* When we mirror an 802.1x connection to an IWD config file, and there's an
* AP in range with matching SSID, that connection should become available
* for activation. In IWD terms when an 802.1x network becomes a Known
* Network, it can be connected to using the .Connect D-Bus method.
*
* However there's a delay between writing the IWD config file and receiving
* the InterfaceAdded event for the Known Network so we don't immediately
* find out that the network can now be used. If an NM client creates a
* new connection for an 802.1x AP and tries to activate it immediately,
* NMDeviceIWD will not allow it to because it doesn't know the network is
* known yet. To work around this, we save the SSIDs of 802.1x connections
* we recently mirrored to IWD config files, for 2 seconds, and we treat
* them as Known Networks in that period since in theory activations should
* succeed.
*/
bool
nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
gint64 now = nm_utils_get_monotonic_timestamp_nsec();
GSList *iter;
RecentlyMirroredData *rmd;
/* Drop entries older than 2 seconds */
while (priv->recently_mirrored) {
rmd = priv->recently_mirrored->data;
if (now < rmd->timestamp + 2000000000)
break;
priv->recently_mirrored = g_slist_remove(priv->recently_mirrored, rmd);
recently_mirrored_data_free(rmd);
}
for (iter = priv->recently_mirrored; iter; iter = iter->next) {
rmd = iter->data;
if (g_bytes_equal(ssid, rmd->ssid))
return TRUE;
}
return FALSE;
}
static void
save_mirrored(NMIwdManager *self, GBytes *ssid)
{
NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE(self);
RecentlyMirroredData *rmd = g_malloc(sizeof(RecentlyMirroredData));
rmd->ssid = g_bytes_ref(ssid);
rmd->timestamp = nm_utils_get_monotonic_timestamp_nsec();
priv->recently_mirrored = g_slist_append(priv->recently_mirrored, rmd);
}
/*****************************************************************************/
static KnownNetworkId *
known_network_id_new(const char *name, NMIwdNetworkSecurity security)
{
@@ -721,6 +791,9 @@ sett_conn_changed(NMSettingsConnection *sett_conn,
"iwd: changed Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
if (security == NM_IWD_NETWORK_SECURITY_8021X)
save_mirrored(nm_iwd_manager_get(), ssid);
}
/* Look up an existing NMSettingsConnection for a network that has been
@@ -1283,6 +1356,7 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer
gs_free_error GError *error = NULL;
nm_auto_unref_keyfile GKeyFile *iwd_config = NULL;
NMSettingsConnectionIntFlags flags;
NMIwdNetworkSecurity security;
if (!nm_streq(nm_settings_connection_get_connection_type(sett_conn), "802-11-wireless"))
return;
@@ -1338,6 +1412,12 @@ connection_added(NMSettings *settings, NMSettingsConnection *sett_conn, gpointer
_LOGD("New Wi-Fi connection %s mirrored as IWD profile %s",
nm_settings_connection_get_id(sett_conn),
full_path);
if (nm_wifi_connection_get_iwd_ssid_and_security(conn, NULL, &security)
&& security == NM_IWD_NETWORK_SECURITY_8021X) {
NMSettingWireless *s_wifi = nm_connection_get_setting_wireless(conn);
save_mirrored(nm_iwd_manager_get(), nm_setting_wireless_get_ssid(s_wifi));
}
}
static gboolean
@@ -1952,6 +2032,8 @@ dispose(GObject *object)
g_hash_table_unref(nm_steal_pointer(&priv->p2p_devices));
g_slist_free_full(nm_steal_pointer(&priv->recently_mirrored), recently_mirrored_data_free);
G_OBJECT_CLASS(nm_iwd_manager_parent_class)->dispose(object);
}

View File

@@ -63,4 +63,6 @@ gboolean nm_iwd_manager_check_wfd_info_compatible(NMIwdManager *self, const NMIw
gboolean nm_iwd_manager_register_wfd(NMIwdManager *self, const NMIwdWfdInfo *wfd_info);
void nm_iwd_manager_unregister_wfd(NMIwdManager *self);
bool nm_iwd_manager_is_recently_mirrored(NMIwdManager *self, const GBytes *ssid);
#endif /* __NETWORKMANAGER_IWD_MANAGER_H__ */