diff options
Diffstat (limited to 'src/core/devices/wifi/nm-device-iwd.c')
| -rw-r--r-- | src/core/devices/wifi/nm-device-iwd.c | 466 |
1 files changed, 442 insertions, 24 deletions
diff --git a/src/core/devices/wifi/nm-device-iwd.c b/src/core/devices/wifi/nm-device-iwd.c index ab37cbec..e03227cd 100644 --- a/src/core/devices/wifi/nm-device-iwd.c +++ b/src/core/devices/wifi/nm-device-iwd.c @@ -8,6 +8,7 @@ #include "nm-device-iwd.h" #include <linux/if_ether.h> +#include <linux/rtnetlink.h> #include "devices/nm-device-private.h" #include "devices/nm-device.h" @@ -32,6 +33,7 @@ #include "supplicant/nm-supplicant-types.h" #include "nm-auth-utils.h" #include "nm-manager.h" +#include "nm-l3-config-data.h" #define _NMLOG_DEVICE_TYPE NMDeviceIwd #include "devices/nm-device-logging.h" @@ -70,6 +72,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 +80,16 @@ typedef struct { GDBusMethodInvocation *pending_agent_request; NMActiveConnection *assumed_ac; guint assumed_ac_timeout; + + union { + struct { + const NML3ConfigData *pending_l3cd_6; + const NML3ConfigData *pending_l3cd_4; + }; + const NML3ConfigData *pending_l3cd_x[2]; + }; + + NMIwdManager *manager; } NMDeviceIwdPrivate; struct _NMDeviceIwd { @@ -289,6 +302,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 +313,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) @@ -525,6 +539,9 @@ cleanup_association_attempt(NMDeviceIwd *self, gboolean disconnect) if (disconnect && priv->dbus_station_proxy) send_disconnect(self); + + nm_clear_l3cd(&priv->pending_l3cd_6); + nm_clear_l3cd(&priv->pending_l3cd_4); } static void @@ -581,6 +598,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 +694,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 +702,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 +815,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 +949,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 +2068,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 +2241,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 +2295,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 +2347,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 +2390,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) { @@ -2421,6 +2460,38 @@ out_fail: return NM_ACT_STAGE_RETURN_FAILURE; } +static void +act_stage3_ip_config(NMDevice *device, int addr_family) +{ + NMDeviceIwd *self = NM_DEVICE_IWD(device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); + const int IS_IPv4 = NM_IS_IPv4(addr_family); + + if (!nm_iwd_manager_get_netconfig_enabled(nm_iwd_manager_get())) { + NMDeviceClass *device_class = NM_DEVICE_CLASS(nm_device_iwd_parent_class); + + if (device_class->act_stage3_ip_config) + device_class->act_stage3_ip_config(device, addr_family); + + return; + } + + if (!priv->pending_l3cd_x[IS_IPv4]) + return; + + nm_device_devip_set_state(device, + addr_family, + NM_DEVICE_IP_STATE_READY, + priv->pending_l3cd_x[IS_IPv4]); + nm_clear_l3cd(&priv->pending_l3cd_x[IS_IPv4]); +} + +static gboolean +ready_for_ip_config(NMDevice *device, gboolean is_manual) +{ + return !nm_iwd_manager_get_netconfig_enabled(nm_iwd_manager_get()); +} + static guint32 get_configured_mtu(NMDevice *device, NMDeviceMtuSource *out_source, gboolean *out_force) { @@ -2719,12 +2790,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); + + 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_FAILED, + NM_DEVICE_STATE_DISCONNECTED, NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); - - if (switch_ap) - assume_connection(self, ap); + priv->assuming = FALSE; + assume_connection(self, ap); return; } @@ -3101,7 +3180,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) { @@ -3278,7 +3357,7 @@ nm_device_iwd_agent_query(NMDeviceIwd *self, GDBusMethodInvocation *invocation) * it. IWD only queries us if it has not saved the PSK (e.g. by policy) * or a previous attempt has failed with current secrets so it wants a * fresh value. It doesn't know about agent-owned secrets so whenever - * possible and the PSK is saved and not asked from NM. However if this + * possible, the PSK is saved and not asked from NM. However if this * is a new connection it may include all of the needed settings already * so allow using these, too. Connection timestamp is set after * activation or after first activation failure (to 0). @@ -3368,6 +3447,341 @@ nm_device_iwd_network_add_remove(NMDeviceIwd *self, GDBusProxy *network, bool ad } } +static const NML3ConfigData * +nm_device_iwd_parse_netconfig(NMDeviceIwd *self, int addr_family, GVariantIter *config_iter) +{ + NMDevice *device = NM_DEVICE(self); + nm_auto_unref_l3cd_init NML3ConfigData *l3cd = NULL; + const char *key; + GVariant *value; + NMIPConfigSource source = NM_IP_CONFIG_SOURCE_UNKNOWN; + nm_auto_free_variant_iter GVariantIter *address_iter = NULL; + nm_auto_free_variant_iter GVariantIter *route_iter = NULL; + nm_auto_free_variant_iter GVariantIter *dns_iter = NULL; + nm_auto_free_variant_iter GVariantIter *domain_iter = NULL; + NMSettingConnectionMdns mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT; + const char *str_value; + GVariantIter *entry_iter; + uint32_t timestamp = nm_utils_get_monotonic_timestamp_sec(); + + while (g_variant_iter_next(config_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Method")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) + || source != NM_IP_CONFIG_SOURCE_UNKNOWN) + goto param_error; + + str_value = g_variant_get_string(value, NULL); + if (nm_streq(str_value, "static")) + source = NM_IP_CONFIG_SOURCE_USER; + else if (nm_streq(str_value, "auto")) + /* Use SOURCE_DHCP as shorthand for the various autoconfiguration protocols */ + source = NM_IP_CONFIG_SOURCE_DHCP; + else + _LOGW(LOGD_WIFI, "iwd_parse_netconfig: Uknown Method value \"%s\"", str_value); + } + + if (nm_streq(key, "Addresses")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")) || address_iter) + goto param_error; + + g_variant_get(value, "aa{sv}", &address_iter); + } + + if (nm_streq(key, "Routes")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("aa{sv}")) || route_iter) + goto param_error; + + g_variant_get(value, "aa{sv}", &route_iter); + } + + if (nm_streq(key, "DomainNameServers")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY) || dns_iter) + goto param_error; + + g_variant_get(value, "as", &dns_iter); + } + + if (nm_streq(key, "DomainNames")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY) || domain_iter) + goto param_error; + + g_variant_get(value, "as", &domain_iter); + } + + if (nm_streq(key, "MDNS")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) + || mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) + goto param_error; + + str_value = g_variant_get_string(value, NULL); + if (nm_streq(str_value, "yes")) + mdns = NM_SETTING_CONNECTION_MDNS_YES; + else if (nm_streq(str_value, "no")) + mdns = NM_SETTING_CONNECTION_MDNS_NO; + else if (nm_streq(str_value, "resolve")) + mdns = NM_SETTING_CONNECTION_MDNS_RESOLVE; + else + _LOGW(LOGD_WIFI, "iwd_parse_netconfig: Uknown MDNS value \"%s\"", str_value); + } + } + + if (!address_iter || !route_iter) + goto param_error; + + l3cd = nm_l3_config_data_new(nm_device_get_multi_index(device), + nm_device_get_ip_ifindex(device), + source); + + while (g_variant_iter_next(address_iter, "a{sv}", &entry_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *entry_iter_free = entry_iter; + const char *addr_str = NULL; + NMIPAddr addr_bin; + uint8_t plen = 0; + const char *bcast_str = NULL; + NMIPAddr bcast_bin = {}; + uint32_t valid_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + uint32_t preferred_lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + NMPlatformIPXAddress a; + + while (g_variant_iter_next(entry_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Address")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || addr_str) + goto param_error; + + addr_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, addr_str, &addr_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "PrefixLength")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTE)) + goto param_error; + + plen = g_variant_get_byte(value); + } + + if (nm_streq(key, "Broadcast")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || bcast_str) + goto param_error; + + bcast_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, bcast_str, &bcast_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "ValidLifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + valid_lifetime = g_variant_get_uint32(value); + } + + if (nm_streq(key, "PreferredLifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || preferred_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + preferred_lifetime = g_variant_get_uint32(value); + } + } + + if (addr_str == NULL) + goto param_error; + + if (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT + && preferred_lifetime == NM_PLATFORM_LIFETIME_PERMANENT) + preferred_lifetime = valid_lifetime; + + if (addr_family == AF_INET) { + a.a4 = (NMPlatformIP4Address){ + .address = addr_bin.addr4, + .peer_address = addr_bin.addr4, + .plen = plen, + .timestamp = (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) ? timestamp : 0, + .lifetime = valid_lifetime, + .preferred = preferred_lifetime, + .addr_source = source, + .use_ip4_broadcast_address = (bcast_str != NULL), + .broadcast_address = bcast_bin.addr4, + }; + } else { + a.a6 = (NMPlatformIP6Address){ + .address = addr_bin.addr6, + .plen = 128, + .timestamp = (valid_lifetime != NM_PLATFORM_LIFETIME_PERMANENT) ? timestamp : 0, + .lifetime = valid_lifetime, + .preferred = preferred_lifetime, + .addr_source = source, + }; + } + + nm_l3_config_data_add_address(l3cd, addr_family, NULL, &a.ax); + } + + while (g_variant_iter_next(route_iter, "a{sv}", &entry_iter)) { + _nm_unused nm_auto_free_variant_iter GVariantIter *entry_iter_free = entry_iter; + const char *dst_addr_str = NULL; + NMIPAddr dst_addr_bin = {}; + uint8_t dst_plen = 0; + const char *router_str = NULL; + NMIPAddr router_bin = {}; + const char *pref_src_str = NULL; + NMIPAddr pref_src_bin = {}; + _nm_unused uint32_t lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + _nm_unused uint32_t priority = 0; + uint8_t preference = 0; + uint32_t mtu = 0; + NMPlatformIPXRoute r; + + while (g_variant_iter_next(entry_iter, "{&sv}", &key, &value)) { + _nm_unused gs_unref_variant GVariant *value_free = value; + + if (nm_streq(key, "Destination")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE("(sy)")) || dst_addr_str) + goto param_error; + + g_variant_get(value, "(&sy)", &dst_addr_str, &dst_plen); + if (inet_pton(addr_family, dst_addr_str, &dst_addr_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "Router")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || router_str) + goto param_error; + + router_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, router_str, &router_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "PreferredSource")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) || pref_src_str) + goto param_error; + + pref_src_str = g_variant_get_string(value, NULL); + if (inet_pton(addr_family, pref_src_str, &pref_src_bin) != 1) + goto param_error; + } + + if (nm_streq(key, "Lifetime")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32) + || lifetime != NM_PLATFORM_LIFETIME_PERMANENT) + goto param_error; + + lifetime = g_variant_get_uint32(value); + } + + if (nm_streq(key, "Priority")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) + goto param_error; + + priority = g_variant_get_uint32(value); + } + + if (nm_streq(key, "Preference")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BYTE)) + goto param_error; + + preference = g_variant_get_byte(value); + } + + if (nm_streq(key, "MTU")) { + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_UINT32)) + goto param_error; + + mtu = g_variant_get_uint32(value); + } + } + + if (addr_family == AF_INET) { + r.r4 = (NMPlatformIP4Route){ + .network = dst_addr_str ? dst_addr_bin.addr4 : 0, + .plen = dst_addr_str ? dst_plen : 0, + .gateway = router_str ? router_bin.addr4 : 0, + .pref_src = pref_src_str ? pref_src_bin.addr4 : 0, + .scope_inv = + nm_platform_route_scope_inv(router_str ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK), + }; + } else { + r.r6 = (NMPlatformIP6Route){ + .network = dst_addr_str ? dst_addr_bin.addr6 : nm_ip_addr_zero.addr6, + .plen = dst_addr_str ? dst_plen : 0, + .gateway = router_str ? router_bin.addr6 : nm_ip_addr_zero.addr6, + .pref_src = pref_src_str ? pref_src_bin.addr6 : nm_ip_addr_zero.addr6, + .rt_pref = preference, + .r_rtm_flags = RTNH_F_ONLINK, + }; + } + + r.rx.metric_any = TRUE; + r.rx.mtu = mtu; + r.rx.rt_source = source; + r.rx.type_coerced = nm_platform_route_type_coerce(RTN_UNICAST); + r.rx.table_any = TRUE; + + /* TODO: set the lifetime */ + nm_l3_config_data_add_route(l3cd, addr_family, NULL, &r.rx); + } + + if (dns_iter) { + while (g_variant_iter_next(dns_iter, "&s", &str_value)) { + NMIPAddr dns_bin; + + if (inet_pton(addr_family, str_value, &dns_bin) != 1) + goto param_error; + + nm_l3_config_data_add_nameserver_detail(l3cd, addr_family, &dns_bin, NULL); + nm_l3_config_data_set_dns_priority(l3cd, addr_family, NM_DNS_PRIORITY_DEFAULT_NORMAL); + } + } + + if (domain_iter) { + while (g_variant_iter_next(domain_iter, "&s", &str_value)) + nm_l3_config_data_add_search(l3cd, addr_family, str_value); + } + + if (mdns != NM_SETTING_CONNECTION_MDNS_DEFAULT) + nm_l3_config_data_set_mdns(l3cd, mdns); + + return nm_l3_config_data_ref_and_seal(l3cd); + +param_error: + return NULL; +} + +bool +nm_device_iwd_set_netconfig(NMDeviceIwd *self, int addr_family, GVariantIter *config_iter) +{ + NMDevice *device = NM_DEVICE(self); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE(self); + const NML3ConfigData *l3cd; + NMDeviceState state = nm_device_get_state(device); + + if (state < NM_DEVICE_STATE_CONFIG || state > NM_DEVICE_STATE_ACTIVATED) + return FALSE; + + l3cd = nm_device_iwd_parse_netconfig(self, addr_family, config_iter); + if (!l3cd) { + _LOGE(LOGD_WIFI, "Malformed netconfig DBus structure"); + return FALSE; + } + + if (state == NM_DEVICE_STATE_CONFIG) { + nm_l3_config_data_unref(priv->pending_l3cd_x[NM_IS_IPv4(addr_family)]); + priv->pending_l3cd_x[NM_IS_IPv4(addr_family)] = l3cd; + } else { + nm_device_devip_set_state(device, addr_family, NM_DEVICE_IP_STATE_READY, l3cd); + nm_l3_config_data_unref(l3cd); + } + + return TRUE; +} + static void autoconnect_changed(NMDevice *device, GParamSpec *pspec, NMDeviceIwd *self) { @@ -3411,7 +3825,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 +3857,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 @@ -3472,12 +3888,14 @@ nm_device_iwd_class_init(NMDeviceIwdClass *klass) device_class->set_enabled = set_enabled; device_class->get_type_description = get_type_description; - device_class->act_stage1_prepare = act_stage1_prepare; - device_class->act_stage2_config = act_stage2_config; - device_class->get_configured_mtu = get_configured_mtu; - device_class->deactivate = deactivate; - device_class->deactivate_async = deactivate_async; - device_class->can_reapply_change = can_reapply_change; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->act_stage2_config = act_stage2_config; + device_class->act_stage3_ip_config = act_stage3_ip_config; + device_class->ready_for_ip_config = ready_for_ip_config; + device_class->get_configured_mtu = get_configured_mtu; + device_class->deactivate = deactivate; + device_class->deactivate_async = deactivate_async; + device_class->can_reapply_change = can_reapply_change; /* Stage 1 needed only for the set_current_ap() call. Stage 2 is * needed if we're assuming a connection still in the "connecting" |