diff options
Diffstat (limited to 'src/devices')
71 files changed, 7409 insertions, 2891 deletions
diff --git a/src/devices/adsl/meson.build b/src/devices/adsl/meson.build index 7ac0e123..f92e809c 100644 --- a/src/devices/adsl/meson.build +++ b/src/devices/adsl/meson.build @@ -1,11 +1,11 @@ sources = files( 'nm-atm-manager.c', - 'nm-device-adsl.c' + 'nm-device-adsl.c', ) deps = [ libudev_dep, - nm_dep + nm_dep, ] libnm_device_plugin_adsl = shared_module( @@ -15,7 +15,7 @@ libnm_device_plugin_adsl = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_adsl diff --git a/src/devices/adsl/nm-atm-manager.c b/src/devices/adsl/nm-atm-manager.c index 0ff4603d..dddb8342 100644 --- a/src/devices/adsl/nm-atm-manager.c +++ b/src/devices/adsl/nm-atm-manager.c @@ -20,7 +20,6 @@ #include "nm-default.h" -#include <string.h> #include <gmodule.h> #include <libudev.h> diff --git a/src/devices/adsl/nm-device-adsl.c b/src/devices/adsl/nm-device-adsl.c index c9984f51..b3b87dc7 100644 --- a/src/devices/adsl/nm-device-adsl.c +++ b/src/devices/adsl/nm-device-adsl.c @@ -25,13 +25,12 @@ #include <sys/socket.h> #include <linux/atmdev.h> #include <linux/atmbr2684.h> -#include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> -#include <string.h> +#include "nm-ip4-config.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" #include "ppp/nm-ppp-manager-call.h" @@ -259,8 +258,9 @@ pppoe_vcc_config (NMDeviceAdsl *self) NMDevice *device = NM_DEVICE (self); NMSettingAdsl *s_adsl; - s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device)); - g_assert (s_adsl); + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + + g_return_val_if_fail (s_adsl, FALSE); /* Set up the VCC */ if (!br2684_assign_vcc (self, s_adsl)) @@ -370,8 +370,8 @@ br2684_create_iface (NMDeviceAdsl *self, priv->nas_update_id = g_timeout_add (100, nas_update_cb, self); return NM_ACT_STAGE_RETURN_POSTPONE; } - if (errno != EEXIST) { - errsv = errno; + errsv = errno; + if (errsv != EEXIST) { _LOGW (LOGD_ADSL, "failed to create br2684 interface (%d)", errsv); break; } @@ -389,7 +389,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMSettingAdsl *s_adsl; const char *protocol; - s_adsl = nm_connection_get_setting_adsl (nm_device_get_applied_connection (device)); + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE); protocol = nm_setting_adsl_get_protocol (s_adsl); @@ -447,9 +448,8 @@ ppp_ip4_config (NMPPPManager *ppp_manager, NMDevice *device = NM_DEVICE (user_data); /* Ignore PPP IP4 events that come in after initial configuration */ - if (nm_device_activate_ip4_state_in_conf (device)) { - nm_device_activate_schedule_ip4_config_result (device, config); - } + if (nm_device_activate_ip4_state_in_conf (device)) + nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config)); } static NMActStageReturn @@ -465,8 +465,11 @@ act_stage3_ip4_config_start (NMDevice *device, const char *ppp_iface; req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_adsl = (NMSettingAdsl *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + + s_adsl = nm_device_get_applied_setting (device, NM_TYPE_SETTING_ADSL); + g_return_val_if_fail (s_adsl, NM_ACT_STAGE_RETURN_FAILURE); /* PPPoE uses the NAS interface, not the ATM interface */ @@ -515,6 +518,18 @@ act_stage3_ip4_config_start (NMDevice *device, return NM_ACT_STAGE_RETURN_POSTPONE; } +static NMActStageReturn +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) +{ + if (addr_family == AF_INET) + return act_stage3_ip4_config_start (device, (NMIP4Config **) out_config, out_failure_reason); + + return NM_DEVICE_CLASS (nm_device_adsl_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); +} + static void adsl_cleanup (NMDeviceAdsl *self) { @@ -523,7 +538,7 @@ adsl_cleanup (NMDeviceAdsl *self) if (priv->ppp_manager) { g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_state_changed), self); g_signal_handlers_disconnect_by_func (priv->ppp_manager, G_CALLBACK (ppp_ip4_config), self); - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -684,7 +699,7 @@ nm_device_adsl_class_init (NMDeviceAdslClass *klass) device_class->complete_connection = complete_connection; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->deactivate = deactivate; obj_properties[PROP_ATM_INDEX] = diff --git a/src/devices/bluetooth/meson.build b/src/devices/bluetooth/meson.build index 628a3bc8..b2f67ceb 100644 --- a/src/devices/bluetooth/meson.build +++ b/src/devices/bluetooth/meson.build @@ -5,12 +5,12 @@ sources = files( 'nm-bluez4-manager.c', 'nm-bluez5-manager.c', 'nm-bt-error.c', - 'nm-device-bt.c' + 'nm-device-bt.c', ) deps = [ libnm_wwan_dep, - nm_dep + nm_dep, ] if enable_bluez5_dun @@ -27,7 +27,7 @@ libnm_device_plugin_bluetooth = shared_module( link_depends: linker_script_devices, install: true, install_dir: nm_plugindir, - install_rpath: nm_plugindir + install_rpath: nm_plugindir, ) core_plugins += libnm_device_plugin_bluetooth diff --git a/src/devices/bluetooth/nm-bluez-device.c b/src/devices/bluetooth/nm-bluez-device.c index b722f692..377ee478 100644 --- a/src/devices/bluetooth/nm-bluez-device.c +++ b/src/devices/bluetooth/nm-bluez-device.c @@ -23,8 +23,6 @@ #include "nm-bluez-device.h" -#include <string.h> - #include "nm-core-internal.h" #include "nm-bt-error.h" #include "nm-bluez-common.h" @@ -451,6 +449,9 @@ nm_bluez_device_disconnect (NMBluezDevice *self) g_return_if_fail (priv->dbus_connection); + /* FIXME: if we are in the process of connecting and cancel the + * connection attempt, we must complete the pending connect request. + * However, we must also ensure that we don't leave a connected device. */ if (priv->connection_bt_type == NM_BT_CAPABILITY_DUN) { if (priv->bluez_version == 4) { /* Can't pass a NULL interface name through dbus to bluez, so just @@ -496,76 +497,109 @@ out: } static void -bluez_connect_cb (GDBusConnection *dbus_connection, +_connect_complete (NMBluezDevice *self, + const char *device, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data, + GError *error) +{ + NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); + + nm_assert ((device || error) && !(device && error)); + + if ( device + && priv->bluez_version == 5) { + priv->connected = TRUE; + _notify (self, PROP_CONNECTED); + } + + if (callback) + callback (self, device, error, callback_user_data); +} + +static void +_connect_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { - GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); - GObject *result_object = g_async_result_get_source_object (G_ASYNC_RESULT (result)); - NMBluezDevice *self = NM_BLUEZ_DEVICE (result_object); - NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - GError *error = NULL; - char *device; - GVariant *variant; + gs_unref_object NMBluezDevice *self = NULL; + NMBluezDevicePrivate *priv; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *error = NULL; + char *device = NULL; + gs_unref_variant GVariant *variant = NULL; - variant = g_dbus_connection_call_finish (dbus_connection, res, &error); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - if (!variant) { - g_simple_async_result_take_error (result, error); - } else { - g_variant_get (variant, "(s)", &device); + priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - g_simple_async_result_set_op_res_gpointer (result, - g_strdup (device), - g_free); + variant = _nm_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), res, G_VARIANT_TYPE ("(s)"), &error); + if (variant) { + g_variant_get (variant, "(s)", &device); priv->b4_iface = device; - g_variant_unref (variant); } - g_simple_async_result_complete (result); - g_object_unref (result); - g_object_unref (result_object); + _connect_complete (self, device, callback, callback_user_data, error); } #if WITH_BLUEZ5_DUN static void -bluez5_dun_connect_cb (NMBluez5DunContext *context, - const char *device, - GError *error, - gpointer user_data) +_connect_cb_bluez5_dun (NMBluez5DunContext *context, + const char *device, + GError *error, + gpointer user_data) { - GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data); + gs_unref_object NMBluezDevice *self = NULL; + gs_unref_object GCancellable *cancellable = NULL; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; - if (error) { - g_simple_async_result_take_error (result, error); - } else { - g_simple_async_result_set_op_res_gpointer (result, - g_strdup (device), - g_free); - } + nm_utils_user_data_unpack (user_data, &self, &cancellable, &callback, &callback_user_data); + + /* FIXME(shutdown): the async operation nm_bluez5_dun_connect() should be cancellable. + * Fake it here. */ + if (g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error)) + error = cancelled_error; - g_simple_async_result_complete (result); - g_object_unref (result); + _connect_complete (self, device, callback, callback_user_data, error); } -#endif +#else /* WITH_BLUEZ5_DUN */ +static void +_connect_cb_bluez5_dun_idle_no_b5 (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMBluezDevice *self = NULL; + NMBluezDeviceConnectCallback callback; + gpointer callback_user_data; + gs_free_error GError *error = NULL; + + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + + if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) { + g_set_error (&error, + NM_BT_ERROR, + NM_BT_ERROR_DUN_CONNECT_FAILED, + "NetworkManager built without support for Bluez 5"); + } + callback (self, NULL, error, callback_user_data); +} +#endif /* WITH_BLUEZ5_DUN */ void nm_bluez_device_connect_async (NMBluezDevice *self, NMBluetoothCapabilities connection_bt_type, - GAsyncReadyCallback callback, - gpointer user_data) + GCancellable *cancellable, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data) { - GSimpleAsyncResult *simple; NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); const char *dbus_iface = NULL; const char *connect_type = NULL; g_return_if_fail (priv->capabilities & connection_bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP)); - simple = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - nm_bluez_device_connect_async); priv->connection_bt_type = connection_bt_type; if (connection_bt_type == NM_BT_CAPABILITY_NAP) { @@ -582,19 +616,29 @@ nm_bluez_device_connect_async (NMBluezDevice *self, #if WITH_BLUEZ5_DUN if (priv->b5_dun_context == NULL) priv->b5_dun_context = nm_bluez5_dun_new (priv->adapter_address, priv->address); - nm_bluez5_dun_connect (priv->b5_dun_context, bluez5_dun_connect_cb, simple); + nm_bluez5_dun_connect (priv->b5_dun_context, + _connect_cb_bluez5_dun, + nm_utils_user_data_pack (g_object_ref (self), + nm_g_object_ref (cancellable), + callback, + callback_user_data)); #else - g_simple_async_result_set_error (simple, - NM_BT_ERROR, - NM_BT_ERROR_DUN_CONNECT_FAILED, - "NetworkManager built without support for Bluez 5"); - g_simple_async_result_complete (simple); + if (callback) { + nm_utils_invoke_on_idle (_connect_cb_bluez5_dun_idle_no_b5, + nm_utils_user_data_pack (g_object_ref (self), + callback, + callback_user_data), + cancellable); + } #endif return; } } else - g_assert_not_reached (); + g_return_if_reached (); + /* FIXME: we need to remember that a connect is in progress. + * So, if the request gets cancelled, that we disconnect the + * connection that was established in the meantime. */ g_dbus_connection_call (priv->dbus_connection, NM_BLUEZ_SERVICE, priv->path, @@ -604,37 +648,11 @@ nm_bluez_device_connect_async (NMBluezDevice *self, NULL, G_DBUS_CALL_FLAGS_NONE, 20000, - NULL, - (GAsyncReadyCallback) bluez_connect_cb, - simple); -} - -const char * -nm_bluez_device_connect_finish (NMBluezDevice *self, - GAsyncResult *result, - GError **error) -{ - NMBluezDevicePrivate *priv = NM_BLUEZ_DEVICE_GET_PRIVATE (self); - GSimpleAsyncResult *simple; - const char *device; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, - G_OBJECT (self), - nm_bluez_device_connect_async), - NULL); - - simple = (GSimpleAsyncResult *) result; - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - device = (const char *) g_simple_async_result_get_op_res_gpointer (simple); - if (device && priv->bluez_version == 5) { - priv->connected = TRUE; - _notify (self, PROP_CONNECTED); - } - - return device; + cancellable, + _connect_cb, + nm_utils_user_data_pack (g_object_ref (self), + callback, + callback_user_data)); } /*****************************************************************************/ diff --git a/src/devices/bluetooth/nm-bluez-device.h b/src/devices/bluetooth/nm-bluez-device.h index f8a1872f..d2d0beb0 100644 --- a/src/devices/bluetooth/nm-bluez-device.h +++ b/src/devices/bluetooth/nm-bluez-device.h @@ -66,16 +66,17 @@ guint32 nm_bluez_device_get_capabilities (NMBluezDevice *self); gboolean nm_bluez_device_get_connected (NMBluezDevice *self); +typedef void (*NMBluezDeviceConnectCallback) (NMBluezDevice *self, + const char *device, + GError *error, + gpointer user_data); + void nm_bluez_device_connect_async (NMBluezDevice *self, NMBluetoothCapabilities connection_bt_type, - GAsyncReadyCallback callback, - gpointer user_data); - -const char * -nm_bluez_device_connect_finish (NMBluezDevice *self, - GAsyncResult *result, - GError **error); + GCancellable *cancellable, + NMBluezDeviceConnectCallback callback, + gpointer callback_user_data); void nm_bluez_device_disconnect (NMBluezDevice *self); diff --git a/src/devices/bluetooth/nm-bluez-manager.c b/src/devices/bluetooth/nm-bluez-manager.c index 711f2e57..dc85a8b2 100644 --- a/src/devices/bluetooth/nm-bluez-manager.c +++ b/src/devices/bluetooth/nm-bluez-manager.c @@ -21,7 +21,6 @@ #include "nm-default.h" #include <signal.h> -#include <string.h> #include <stdlib.h> #include <gmodule.h> diff --git a/src/devices/bluetooth/nm-bluez4-adapter.c b/src/devices/bluetooth/nm-bluez4-adapter.c index c8ef7a27..3a456deb 100644 --- a/src/devices/bluetooth/nm-bluez4-adapter.c +++ b/src/devices/bluetooth/nm-bluez4-adapter.c @@ -22,8 +22,6 @@ #include "nm-bluez4-adapter.h" -#include <string.h> - #include "nm-dbus-interface.h" #include "nm-bluez-device.h" #include "nm-bluez-common.h" diff --git a/src/devices/bluetooth/nm-bluez4-manager.c b/src/devices/bluetooth/nm-bluez4-manager.c index 1fe02f18..82d995be 100644 --- a/src/devices/bluetooth/nm-bluez4-manager.c +++ b/src/devices/bluetooth/nm-bluez4-manager.c @@ -24,7 +24,6 @@ #include "nm-bluez4-manager.h" #include <signal.h> -#include <string.h> #include <stdlib.h> #include "nm-bluez4-adapter.h" diff --git a/src/devices/bluetooth/nm-bluez5-dun.c b/src/devices/bluetooth/nm-bluez5-dun.c index ca09b276..ff3a0da9 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.c +++ b/src/devices/bluetooth/nm-bluez5-dun.c @@ -27,7 +27,6 @@ #include <net/ethernet.h> #include <sys/ioctl.h> #include <unistd.h> -#include <errno.h> #include <fcntl.h> #include "nm-bluez5-dun.h" @@ -57,6 +56,7 @@ dun_connect (NMBluez5DunContext *context) char tty[100]; const int ttylen = sizeof (tty) - 1; GError *error = NULL; + int errsv; struct rfcomm_dev_req req = { .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP), @@ -66,10 +66,10 @@ dun_connect (NMBluez5DunContext *context) context->rfcomm_fd = socket (AF_BLUETOOTH, SOCK_STREAM | SOCK_CLOEXEC, BTPROTO_RFCOMM); if (context->rfcomm_fd < 0) { - int errsv = errno; + errsv = errno; error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to create RFCOMM socket: (%d) %s", - errsv, strerror (errsv)); + errsv, nm_strerror_native (errsv)); goto done; } @@ -78,20 +78,20 @@ dun_connect (NMBluez5DunContext *context) sa.rc_channel = 0; memcpy (&sa.rc_bdaddr, &context->src, ETH_ALEN); if (bind (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof(sa))) { - int errsv = errno; + errsv = errno; error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to bind socket: (%d) %s", - errsv, strerror (errsv)); + errsv, nm_strerror_native (errsv)); goto done; } sa.rc_channel = context->rfcomm_channel; memcpy (&sa.rc_bdaddr, &context->dst, ETH_ALEN); if (connect (context->rfcomm_fd, (struct sockaddr *) &sa, sizeof (sa)) ) { - int errsv = errno; + errsv = errno; error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to connect to remote device: (%d) %s", - errsv, strerror (errsv)); + errsv, nm_strerror_native (errsv)); goto done; } @@ -103,10 +103,10 @@ dun_connect (NMBluez5DunContext *context) memcpy (&req.dst, &context->dst, ETH_ALEN); devid = ioctl (context->rfcomm_fd, RFCOMMCREATEDEV, &req); if (devid < 0) { - int errsv = errno; + errsv = errno; error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to create rfcomm device: (%d) %s", - errsv, strerror (errsv)); + errsv, nm_strerror_native (errsv)); goto done; } context->rfcomm_id = devid; @@ -250,7 +250,8 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da sdp_list_t *search, *attrs; uuid_t svclass; uint16_t attr; - int fd, err, fd_err = 0; + int fd, fd_err = 0; + int err; socklen_t len = sizeof (fd_err); GError *error = NULL; @@ -258,19 +259,19 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da fd = g_io_channel_unix_get_fd (channel); if (getsockopt (fd, SOL_SOCKET, SO_ERROR, &fd_err, &len) < 0) { - nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d", - context->src_str, context->dst_str, errno); err = errno; + nm_log_dbg (LOGD_BT, "(%s -> %s): getsockopt error=%d", + context->src_str, context->dst_str, err); } else { + err = fd_err; nm_log_dbg (LOGD_BT, "(%s -> %s): SO_ERROR error=%d", context->src_str, context->dst_str, fd_err); - err = fd_err; } if (err != 0) { error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Error on Service Discovery socket: (%d) %s", - err, strerror (err)); + err, nm_strerror_native (err)); goto done; } @@ -297,7 +298,7 @@ sdp_connect_watch (GIOChannel *channel, GIOCondition condition, gpointer user_da error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Error starting Service Discovery: (%d) %s", - err, strerror (err)); + err, nm_strerror_native (err)); } sdp_list_free (attrs, NULL); @@ -342,13 +343,14 @@ nm_bluez5_dun_connect (NMBluez5DunContext *context, if (context->rfcomm_channel != -1) { nm_log_dbg (LOGD_BT, "(%s): channel number on device %s cached: %d", - context->src_str, context->dst_str, context->rfcomm_channel); + context->src_str, context->dst_str, context->rfcomm_channel); + /* FIXME: don't invoke the callback synchronously. */ dun_connect (context); return; } nm_log_dbg (LOGD_BT, "(%s): starting channel number discovery for device %s", - context->src_str, context->dst_str); + context->src_str, context->dst_str); context->sdp_session = sdp_connect (&context->src, &context->dst, SDP_NON_BLOCKING); if (!context->sdp_session) { @@ -357,11 +359,13 @@ nm_bluez5_dun_connect (NMBluez5DunContext *context, error = g_error_new (NM_BT_ERROR, NM_BT_ERROR_DUN_CONNECT_FAILED, "Failed to connect to the SDP server: (%d) %s", - err, strerror (err)); + err, nm_strerror_native (err)); + /* FIXME: don't invoke the callback synchronously. */ context->callback (context, NULL, error, context->user_data); return; } + /* FIXME(shutdown): make connect cancellable. */ channel = g_io_channel_unix_new (sdp_get_socket (context->sdp_session)); context->sdp_watch_id = g_io_add_watch (channel, G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, diff --git a/src/devices/bluetooth/nm-bluez5-dun.h b/src/devices/bluetooth/nm-bluez5-dun.h index 124c1a05..b75e4399 100644 --- a/src/devices/bluetooth/nm-bluez5-dun.h +++ b/src/devices/bluetooth/nm-bluez5-dun.h @@ -32,7 +32,8 @@ NMBluez5DunContext *nm_bluez5_dun_new (const char *adapter, const char *remote); void nm_bluez5_dun_connect (NMBluez5DunContext *context, - NMBluez5DunFunc callback, gpointer user_data); + NMBluez5DunFunc callback, + gpointer user_data); /* Clean up connection resources */ void nm_bluez5_dun_cleanup (NMBluez5DunContext *context); diff --git a/src/devices/bluetooth/nm-bluez5-manager.c b/src/devices/bluetooth/nm-bluez5-manager.c index e984212b..be15d824 100644 --- a/src/devices/bluetooth/nm-bluez5-manager.c +++ b/src/devices/bluetooth/nm-bluez5-manager.c @@ -25,7 +25,6 @@ #include "nm-bluez5-manager.h" #include <signal.h> -#include <string.h> #include <stdlib.h> #include "nm-core-internal.h" diff --git a/src/devices/bluetooth/nm-device-bt.c b/src/devices/bluetooth/nm-device-bt.c index f4a1b709..e79251ce 100644 --- a/src/devices/bluetooth/nm-device-bt.c +++ b/src/devices/bluetooth/nm-device-bt.c @@ -23,7 +23,6 @@ #include "nm-device-bt.h" #include <stdio.h> -#include <string.h> #include "nm-bluez-common.h" #include "nm-bluez-device.h" @@ -39,6 +38,7 @@ #include "settings/nm-settings-connection.h" #include "nm-utils.h" #include "nm-bt-error.h" +#include "nm-ip4-config.h" #include "platform/nm-platform.h" #include "devices/wwan/nm-modem-manager.h" @@ -78,7 +78,9 @@ typedef struct { char *rfcomm_iface; NMModem *modem; - guint32 timeout_id; + guint timeout_id; + + GCancellable *cancellable; guint32 bt_type; /* BT type of the current connection */ } NMDeviceBtPrivate; @@ -318,8 +320,6 @@ complete_connection (NMDevice *device, if (s_gsm) { fallback_prefix = _("GSM connection"); - if (!nm_setting_gsm_get_number (s_gsm)) - g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); } else { fallback_prefix = _("CDMA connection"); if (!nm_setting_cdma_get_number (s_cdma)) @@ -398,9 +398,9 @@ ppp_failed (NMModem *modem, case NM_DEVICE_STATE_SECONDARIES: case NM_DEVICE_STATE_ACTIVATED: if (nm_device_activate_ip4_state_in_conf (device)) - nm_device_activate_schedule_ip4_config_timeout (device); + nm_device_activate_schedule_ip_config_timeout (device, AF_INET); else if (nm_device_activate_ip6_state_in_conf (device)) - nm_device_activate_schedule_ip6_config_timeout (device); + nm_device_activate_schedule_ip_config_timeout (device, AF_INET6); else if (nm_device_activate_ip4_state_done (device)) { nm_device_ip_method_failed (device, AF_INET, @@ -542,7 +542,7 @@ modem_ip4_config_result (NMModem *modem, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); } else - nm_device_activate_schedule_ip4_config_result (device, config); + nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config)); } static void @@ -672,6 +672,7 @@ component_added (NMDevice *device, GObject *component) /* Got the modem */ nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); /* Can only accept the modem in stage2, but since the interface matched * what we were expecting, don't let anything else claim the modem either. @@ -715,8 +716,11 @@ static gboolean modem_find_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); + + priv->timeout_id = 0; + nm_clear_g_cancellable (&priv->cancellable); - NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_MODEM_NOT_FOUND); @@ -738,8 +742,8 @@ check_connect_continue (NMDeviceBt *self) "Activation: (bluetooth) Stage 2 of 5 (Device Configure) successful. Will connect via %s.", dun ? "DUN" : (pan ? "PAN" : "unknown")); - /* Kill the connect timeout since we're connected now */ nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); if (pan) { /* Bluez says we're connected now. Start IP config. */ @@ -755,25 +759,25 @@ check_connect_continue (NMDeviceBt *self) } static void -bluez_connect_cb (GObject *object, - GAsyncResult *res, - void *user_data) +bluez_connect_cb (NMBluezDevice *bt_device, + const char *device_name, + GError *error, + gpointer user_data) { - gs_unref_object NMDeviceBt *self = NM_DEVICE_BT (user_data); + gs_unref_object NMDeviceBt *self = user_data; NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); - GError *error = NULL; - const char *device; - device = nm_bluez_device_connect_finish (NM_BLUEZ_DEVICE (object), - res, &error); + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + nm_clear_g_source (&priv->timeout_id); + g_clear_object (&priv->cancellable); if (!nm_device_is_activating (NM_DEVICE (self))) return; - if (!device) { + if (!device_name) { _LOGW (LOGD_BT, "Error connecting with bluez: %s", error->message); - g_clear_error (&error); - nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -782,10 +786,10 @@ bluez_connect_cb (GObject *object, if (priv->bt_type == NM_BT_CAPABILITY_DUN) { g_free (priv->rfcomm_iface); - priv->rfcomm_iface = g_strdup (device); + priv->rfcomm_iface = g_strdup (device_name); } else if (priv->bt_type == NM_BT_CAPABILITY_NAP) { - if (!nm_device_set_ip_iface (NM_DEVICE (self), device)) { - _LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device); + if (!nm_device_set_ip_iface (NM_DEVICE (self), device_name)) { + _LOGW (LOGD_BT, "Error connecting with bluez: cannot find device %s", device_name); nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -843,10 +847,13 @@ static gboolean bt_connect_timeout (gpointer user_data) { NMDeviceBt *self = NM_DEVICE_BT (user_data); + NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE (self); _LOGD (LOGD_BT, "initial connection timed out"); - NM_DEVICE_BT_GET_PRIVATE (self)->timeout_id = 0; + priv->timeout_id = 0; + nm_clear_g_cancellable (&priv->cancellable); + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_BT_FAILED); @@ -876,45 +883,45 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) _LOGD (LOGD_BT, "requesting connection to the device"); - /* Connect to the BT device */ - nm_bluez_device_connect_async (priv->bt_device, - priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP), - bluez_connect_cb, g_object_ref (device)); - nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); + priv->timeout_id = g_timeout_add_seconds (30, bt_connect_timeout, device); + priv->cancellable = g_cancellable_new (); + + nm_bluez_device_connect_async (priv->bt_device, + priv->bt_type & (NM_BT_CAPABILITY_DUN | NM_BT_CAPABILITY_NAP), + priv->cancellable, + bluez_connect_cb, + g_object_ref (self)); return NM_ACT_STAGE_RETURN_POSTPONE; } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device); + nm_assert_addr_family (addr_family); + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { - return nm_modem_stage3_ip4_config_start (priv->modem, - device, - NM_DEVICE_CLASS (nm_device_bt_parent_class), - out_failure_reason); + if (addr_family == AF_INET) { + return nm_modem_stage3_ip4_config_start (priv->modem, + device, + NM_DEVICE_CLASS (nm_device_bt_parent_class), + out_failure_reason); + } else { + return nm_modem_stage3_ip6_config_start (priv->modem, + device, + out_failure_reason); + } } - return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) device); - - if (priv->bt_type == NM_BT_CAPABILITY_DUN) - return nm_modem_stage3_ip6_config_start (priv->modem, device, out_failure_reason); - - return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); + return NM_DEVICE_CLASS (nm_device_bt_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); } static void @@ -925,6 +932,9 @@ deactivate (NMDevice *device) priv->have_iface = FALSE; priv->connected = FALSE; + nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); + if (priv->bt_type == NM_BT_CAPABILITY_DUN) { if (priv->modem) { nm_modem_deactivate (priv->modem, device); @@ -942,8 +952,6 @@ deactivate (NMDevice *device) if (priv->bt_type != NM_BT_CAPABILITY_NONE) nm_bluez_device_disconnect (priv->bt_device); - nm_clear_g_source (&priv->timeout_id); - priv->bt_type = NM_BT_CAPABILITY_NONE; g_free (priv->rfcomm_iface); @@ -1128,6 +1136,7 @@ dispose (GObject *object) NMDeviceBtPrivate *priv = NM_DEVICE_BT_GET_PRIVATE ((NMDeviceBt *) object); nm_clear_g_source (&priv->timeout_id); + nm_clear_g_cancellable (&priv->cancellable); g_signal_handlers_disconnect_matched (priv->bt_device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object); @@ -1191,8 +1200,7 @@ nm_device_bt_class_init (NMDeviceBtClass *klass) device_class->can_auto_connect = can_auto_connect; device_class->deactivate = deactivate; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->check_connection_compatible = check_connection_compatible; device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; diff --git a/src/devices/nm-acd-manager.c b/src/devices/nm-acd-manager.c index 035487a3..a8f7a63a 100644 --- a/src/devices/nm-acd-manager.c +++ b/src/devices/nm-acd-manager.c @@ -39,40 +39,23 @@ typedef enum { typedef struct { in_addr_t address; gboolean duplicate; - NMAcdManager *manager; - NAcd *acd; - GIOChannel *channel; - guint event_id; + NAcdProbe *probe; } AddressInfo; -enum { - PROBE_TERMINATED, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -typedef struct { +struct _NMAcdManager { int ifindex; guint8 hwaddr[ETH_ALEN]; State state; GHashTable *addresses; guint completed; -} NMAcdManagerPrivate; + NAcd *acd; + GIOChannel *channel; + guint event_id; -struct _NMAcdManager { - GObject parent; - NMAcdManagerPrivate _priv; + NMAcdCallbacks callbacks; + gpointer user_data; }; -struct _NMAcdManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT) - -#define NM_ACD_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMAcdManager, NM_IS_ACD_MANAGER) - /*****************************************************************************/ #define _NMLOG_DOMAIN LOGD_IP4 @@ -80,14 +63,13 @@ G_DEFINE_TYPE (NMAcdManager, nm_acd_manager, G_TYPE_OBJECT) #define _NMLOG(level, ...) \ G_STMT_START { \ char _sbuf[64]; \ - int _ifindex = (self) ? NM_ACD_MANAGER_GET_PRIVATE (self)->ifindex : 0; \ \ nm_log ((level), _NMLOG_DOMAIN, \ - nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ + self && self->ifindex > 0 ? nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex) : NULL, \ NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ - self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, _ifindex) : "" \ + self ? nm_sprintf_buf (_sbuf, "[%p,%d]", self, self->ifindex) : "" \ _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ } G_STMT_END @@ -111,32 +93,25 @@ _acd_event_to_string (unsigned int event) return NULL; } -#define acd_event_to_string(event) NM_UTILS_LOOKUP_STR (_acd_event_to_string, event) +#define acd_event_to_string_a(event) NM_UTILS_LOOKUP_STR_A (_acd_event_to_string, event) static const char * -_acd_error_to_string (int error) +acd_error_to_string (int error) { if (error < 0) - return strerror(-error); + return nm_strerror_native (-error); switch (error) { case _N_ACD_E_SUCCESS: return "success"; - case N_ACD_E_DONE: - return "no more events (engine running)"; - case N_ACD_E_STOPPED: - return "no more events (engine stopped)"; case N_ACD_E_PREEMPTED: return "preempted"; case N_ACD_E_INVALID_ARGUMENT: return "invalid argument"; - case N_ACD_E_BUSY: - return "busy"; } - return NULL; -} -#define acd_error_to_string(error) NM_UTILS_LOOKUP_STR (_acd_error_to_string, error) + g_return_val_if_reached (NULL); +} /*****************************************************************************/ @@ -152,21 +127,18 @@ _acd_error_to_string (int error) gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address) { - NMAcdManagerPrivate *priv; AddressInfo *info; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (self->state == STATE_INIT, FALSE); - if (g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address))) + if (g_hash_table_lookup (self->addresses, GUINT_TO_POINTER (address))) return FALSE; info = g_slice_new0 (AddressInfo); info->address = address; - info->manager = self; - g_hash_table_insert (priv->addresses, GUINT_TO_POINTER (address), info); + g_hash_table_insert (self->addresses, GUINT_TO_POINTER (address), info); return TRUE; } @@ -174,115 +146,142 @@ nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address) static gboolean acd_event (GIOChannel *source, GIOCondition condition, gpointer data) { - AddressInfo *info = data; - NMAcdManager *self = info->manager; - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + NMAcdManager *self = data; NAcdEvent *event; + AddressInfo *info; + gboolean emit_probe_terminated = FALSE; char address_str[INET_ADDRSTRLEN]; gs_free char *hwaddr_str = NULL; int r; - if ( n_acd_dispatch (info->acd) - || n_acd_pop_event (info->acd, &event)) + if (n_acd_dispatch (self->acd)) return G_SOURCE_CONTINUE; - switch (event->event) { - case N_ACD_EVENT_READY: - info->duplicate = FALSE; - if (priv->state == STATE_ANNOUNCING) { - r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); - if (r) { - _LOGW ("couldn't announce address %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, address_str), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), - acd_error_to_string (r)); - } else { - _LOGD ("announcing address %s", - nm_utils_inet4_ntop (info->address, address_str)); + while ( !n_acd_pop_event (self->acd, &event) + && event) { + gboolean check_probing_done = FALSE; + + switch (event->event) { + case N_ACD_EVENT_READY: + n_acd_probe_get_userdata (event->ready.probe, (void **) &info); + info->duplicate = FALSE; + if (self->state == STATE_ANNOUNCING) { + /* fake probe ended, start announcing */ + r = n_acd_probe_announce (info->probe, N_ACD_DEFEND_ONCE); + if (r) { + _LOGW ("couldn't announce address %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, address_str), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + } else { + _LOGD ("announcing address %s", + nm_utils_inet4_ntop (info->address, address_str)); + } } + check_probing_done = TRUE; + break; + case N_ACD_EVENT_USED: + n_acd_probe_get_userdata (event->used.probe, (void **) &info); + info->duplicate = TRUE; + check_probing_done = TRUE; + break; + case N_ACD_EVENT_DEFENDED: + n_acd_probe_get_userdata (event->defended.probe, (void **) &info); + _LOGD ("defended address %s from host %s", + nm_utils_inet4_ntop (info->address, address_str), + (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, + event->defended.n_sender))); + break; + case N_ACD_EVENT_CONFLICT: + n_acd_probe_get_userdata (event->conflict.probe, (void **) &info); + _LOGW ("conflict for address %s detected with host %s on interface '%s'", + nm_utils_inet4_ntop (info->address, address_str), + (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, + event->defended.n_sender)), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex)); + break; + default: + _LOGD ("unhandled event '%s'", acd_event_to_string_a (event->event)); + break; + } + + if ( check_probing_done + && self->state == STATE_PROBING + && ++self->completed == g_hash_table_size (self->addresses)) { + self->state = STATE_PROBE_DONE; + emit_probe_terminated = TRUE; } - break; - case N_ACD_EVENT_USED: - info->duplicate = TRUE; - break; - case N_ACD_EVENT_DEFENDED: - _LOGD ("defended address %s from host %s", - nm_utils_inet4_ntop (info->address, address_str), - (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, - event->defended.n_sender))); - break; - case N_ACD_EVENT_CONFLICT: - _LOGW ("conflict for address %s detected with host %s on interface '%s'", - nm_utils_inet4_ntop (info->address, address_str), - (hwaddr_str = nm_utils_hwaddr_ntoa (event->defended.sender, - event->defended.n_sender)), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - break; - default: - _LOGD ("event '%s' for address %s", - acd_event_to_string (event->event), - nm_utils_inet4_ntop (info->address, address_str)); - return G_SOURCE_CONTINUE; } - if ( priv->state == STATE_PROBING - && ++priv->completed == g_hash_table_size (priv->addresses)) { - priv->state = STATE_PROBE_DONE; - g_signal_emit (self, signals[PROBE_TERMINATED], 0); + if (emit_probe_terminated) { + if (self->callbacks.probe_terminated_callback) { + self->callbacks.probe_terminated_callback (self, + self->user_data); + } } return G_SOURCE_CONTINUE; } static gboolean -acd_probe_start (NMAcdManager *self, - AddressInfo *info, - guint64 timeout) +acd_probe_add (NMAcdManager *self, + AddressInfo *info, + guint64 timeout) { - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); - NAcdConfig *config; - int r, fd; + NAcdProbeConfig *probe_config; + int r; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; - r = n_acd_new (&info->acd); + r = n_acd_probe_config_new (&probe_config); if (r) { - _LOGW ("could not create ACD for %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + _LOGW ("could not create probe config for %s on interface '%s': %s", + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); return FALSE; } - n_acd_get_fd (info->acd, &fd); - info->channel = g_io_channel_unix_new (fd); - info->event_id = g_io_add_watch (info->channel, G_IO_IN, acd_event, info); - - config = &(NAcdConfig) { - .ifindex = priv->ifindex, - .mac = priv->hwaddr, - .n_mac = ETH_ALEN, - .ip = info->address, - .timeout_msec = timeout, - .transport = N_ACD_TRANSPORT_ETHERNET, - }; + n_acd_probe_config_set_ip (probe_config, (struct in_addr) { info->address }); + n_acd_probe_config_set_timeout (probe_config, timeout); - r = n_acd_start (info->acd, config); + r = n_acd_probe (self->acd, &info->probe, probe_config); if (r) { _LOGW ("could not start probe for %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); + n_acd_probe_config_free (probe_config); return FALSE; } - if (timeout) { - _LOGD ("started probe for %s with timeout %llu", - nm_utils_inet4_ntop (info->address, NULL), - (unsigned long long) timeout); - } + n_acd_probe_set_userdata (info->probe, info); + n_acd_probe_config_free (probe_config); return TRUE; } +static int +acd_init (NMAcdManager *self) +{ + NAcdConfig *config; + int r; + + if (self->acd) + return 0; + + r = n_acd_config_new (&config); + if (r) + return r; + + n_acd_config_set_ifindex (config, self->ifindex); + n_acd_config_set_transport (config, N_ACD_TRANSPORT_ETHERNET); + n_acd_config_set_mac (config, self->hwaddr, ETH_ALEN); + + r = n_acd_new (&self->acd, config); + n_acd_config_free (config); + return r; +} + /** * nm_acd_manager_start_probe: * @self: a #NMAcdManager @@ -297,59 +296,36 @@ acd_probe_start (NMAcdManager *self, gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout) { - NMAcdManagerPrivate *priv; GHashTableIter iter; AddressInfo *info; gboolean success = FALSE; + int fd, r; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail (priv->state == STATE_INIT, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (self->state == STATE_INIT, FALSE); + + r = acd_init (self); + if (r) { + _LOGW ("couldn't init ACD for probing on interface '%s': %s", + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + return FALSE; + } - priv->completed = 0; + self->completed = 0; - g_hash_table_iter_init (&iter, priv->addresses); + g_hash_table_iter_init (&iter, self->addresses); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) - success |= acd_probe_start (self, info, timeout); + success |= acd_probe_add (self, info, timeout); if (success) - priv->state = STATE_PROBING; + self->state = STATE_PROBING; - return success; -} - -/** - * nm_acd_manager_reset: - * @self: a #NMAcdManager - * - * Stop any operation in progress and reset @self to the initial state. - */ -void -nm_acd_manager_reset (NMAcdManager *self) -{ - NMAcdManagerPrivate *priv; - - g_return_if_fail (NM_IS_ACD_MANAGER (self)); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->addresses); + n_acd_get_fd (self->acd, &fd); + self->channel = g_io_channel_unix_new (fd); + self->event_id = g_io_add_watch (self->channel, G_IO_IN, acd_event, self); - priv->state = STATE_INIT; -} - -/** - * nm_acd_manager_destroy: - * @self: the #NMAcdManager - * - * Calls nm_acd_manager_reset() and unrefs @self. - */ -void -nm_acd_manager_destroy (NMAcdManager *self) -{ - g_return_if_fail (NM_IS_ACD_MANAGER (self)); - - nm_acd_manager_reset (self); - g_object_unref (self); + return success; } /** @@ -365,15 +341,12 @@ nm_acd_manager_destroy (NMAcdManager *self) gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address) { - NMAcdManagerPrivate *priv; AddressInfo *info; - g_return_val_if_fail (NM_IS_ACD_MANAGER (self), FALSE); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - g_return_val_if_fail ( priv->state == STATE_INIT - || priv->state == STATE_PROBE_DONE, FALSE); + g_return_val_if_fail (self, FALSE); + g_return_val_if_fail (NM_IN_SET (self->state, STATE_INIT, STATE_PROBE_DONE), FALSE); - info = g_hash_table_lookup (priv->addresses, GUINT_TO_POINTER (address)); + info = g_hash_table_lookup (self->addresses, GUINT_TO_POINTER (address)); g_return_val_if_fail (info, FALSE); return !info->duplicate; @@ -388,41 +361,43 @@ nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address) void nm_acd_manager_announce_addresses (NMAcdManager *self) { - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); GHashTableIter iter; AddressInfo *info; int r; - if (priv->state == STATE_INIT) { + r = acd_init (self); + if (r) { + _LOGW ("couldn't init ACD for announcing addresses on interface '%s': %s", + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), + acd_error_to_string (r)); + return; + } + + if (self->state == STATE_INIT) { /* n-acd can't announce without probing, therefore let's * start a fake probe with zero timeout and then perform - * the announce. */ - priv->state = STATE_ANNOUNCING; - g_hash_table_iter_init (&iter, priv->addresses); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { - if (!acd_probe_start (self, info, 0)) { - _LOGW ("couldn't announce address %s on interface '%s'", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex)); - } - } - } else if (priv->state == STATE_PROBE_DONE) { - priv->state = STATE_ANNOUNCING; - g_hash_table_iter_init (&iter, priv->addresses); + * the announcement. */ + g_hash_table_iter_init (&iter, self->addresses); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) + acd_probe_add (self, info, 0); + self->state = STATE_ANNOUNCING; + } else if (self->state == STATE_ANNOUNCING) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + + g_hash_table_iter_init (&iter, self->addresses); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &info)) { if (info->duplicate) continue; - r = n_acd_announce (info->acd, N_ACD_DEFEND_ONCE); + r = n_acd_probe_announce (info->probe, N_ACD_DEFEND_ONCE); if (r) { _LOGW ("couldn't announce address %s on interface '%s': %s", - nm_utils_inet4_ntop (info->address, NULL), - nm_platform_link_get_name (NM_PLATFORM_GET, priv->ifindex), + nm_utils_inet4_ntop (info->address, sbuf), + nm_platform_link_get_name (NM_PLATFORM_GET, self->ifindex), acd_error_to_string (r)); } else - _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, NULL)); + _LOGD ("announcing address %s", nm_utils_inet4_ntop (info->address, sbuf)); } - } else - nm_assert_not_reached (); + } } static void @@ -430,64 +405,52 @@ destroy_address_info (gpointer data) { AddressInfo *info = (AddressInfo *) data; - g_clear_pointer (&info->channel, g_io_channel_unref); - g_clear_pointer (&info->acd, n_acd_free); - nm_clear_g_source (&info->event_id); + n_acd_probe_free (info->probe); g_slice_free (AddressInfo, info); } /*****************************************************************************/ -static void -nm_acd_manager_init (NMAcdManager *self) -{ - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); - - priv->addresses = g_hash_table_new_full (nm_direct_hash, NULL, - NULL, destroy_address_info); - priv->state = STATE_INIT; -} - NMAcdManager * -nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len) +nm_acd_manager_new (int ifindex, + const guint8 *hwaddr, + guint hwaddr_len, + const NMAcdCallbacks *callbacks, + gpointer user_data) { NMAcdManager *self; - NMAcdManagerPrivate *priv; + g_return_val_if_fail (ifindex > 0, NULL); g_return_val_if_fail (hwaddr, NULL); g_return_val_if_fail (hwaddr_len == ETH_ALEN, NULL); - self = g_object_new (NM_TYPE_ACD_MANAGER, NULL); - priv = NM_ACD_MANAGER_GET_PRIVATE (self); - priv->ifindex = ifindex; - memcpy (priv->hwaddr, hwaddr, ETH_ALEN); + self = g_slice_new0 (NMAcdManager); + + if (callbacks) + self->callbacks = *callbacks; + self->user_data = user_data; + self->addresses = g_hash_table_new_full (nm_direct_hash, NULL, + NULL, destroy_address_info); + self->state = STATE_INIT; + self->ifindex = ifindex; + memcpy (self->hwaddr, hwaddr, ETH_ALEN); return self; } -static void -dispose (GObject *object) +void +nm_acd_manager_free (NMAcdManager *self) { - NMAcdManager *self = NM_ACD_MANAGER (object); - NMAcdManagerPrivate *priv = NM_ACD_MANAGER_GET_PRIVATE (self); + g_return_if_fail (self); - g_clear_pointer (&priv->addresses, g_hash_table_destroy); - - G_OBJECT_CLASS (nm_acd_manager_parent_class)->dispose (object); -} - -static void -nm_acd_manager_class_init (NMAcdManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); + if (self->callbacks.user_data_destroy) + self->callbacks.user_data_destroy (self->user_data); - object_class->dispose = dispose; + nm_clear_pointer (&self->addresses, g_hash_table_destroy); + nm_clear_pointer (&self->channel, g_io_channel_unref); + nm_clear_g_source (&self->event_id); + nm_clear_pointer (&self->acd, n_acd_unref); - signals[PROBE_TERMINATED] = - g_signal_new (NM_ACD_MANAGER_PROBE_TERMINATED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); + g_slice_free (NMAcdManager, self); } diff --git a/src/devices/nm-acd-manager.h b/src/devices/nm-acd-manager.h index eeede5da..75884846 100644 --- a/src/devices/nm-acd-manager.h +++ b/src/devices/nm-acd-manager.h @@ -19,25 +19,25 @@ #include <netinet/in.h> -#define NM_TYPE_ACD_MANAGER (nm_acd_manager_get_type ()) -#define NM_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ACD_MANAGER, NMAcdManager)) -#define NM_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) -#define NM_IS_ACD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ACD_MANAGER)) -#define NM_IS_ACD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ACD_MANAGER)) -#define NM_ACD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ACD_MANAGER, NMAcdManagerClass)) +typedef struct _NMAcdManager NMAcdManager; -#define NM_ACD_MANAGER_PROBE_TERMINATED "probe-terminated" +typedef struct { + void (*probe_terminated_callback) (NMAcdManager *self, + gpointer user_data); + GDestroyNotify user_data_destroy; +} NMAcdCallbacks; -typedef struct _NMAcdManagerClass NMAcdManagerClass; +NMAcdManager *nm_acd_manager_new (int ifindex, + const guint8 *hwaddr, + guint hwaddr_len, + const NMAcdCallbacks *callbacks, + gpointer user_data); -GType nm_acd_manager_get_type (void); +void nm_acd_manager_free (NMAcdManager *self); -NMAcdManager *nm_acd_manager_new (int ifindex, const guint8 *hwaddr, size_t hwaddr_len); -void nm_acd_manager_destroy (NMAcdManager *self); gboolean nm_acd_manager_add_address (NMAcdManager *self, in_addr_t address); gboolean nm_acd_manager_start_probe (NMAcdManager *self, guint timeout); gboolean nm_acd_manager_check_address (NMAcdManager *self, in_addr_t address); void nm_acd_manager_announce_addresses (NMAcdManager *self); -void nm_acd_manager_reset (NMAcdManager *self); #endif /* __NM_ACD_MANAGER__ */ diff --git a/src/devices/nm-device-6lowpan.c b/src/devices/nm-device-6lowpan.c index b6b9157c..40103747 100644 --- a/src/devices/nm-device-6lowpan.c +++ b/src/devices/nm-device-6lowpan.c @@ -110,9 +110,9 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSetting6Lowpan *s_6lowpan; int parent_ifindex; + int r; s_6lowpan = NM_SETTING_6LOWPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_6LOWPAN)); g_return_val_if_fail (s_6lowpan, FALSE); @@ -126,13 +126,13 @@ create_and_realize (NMDevice *device, return FALSE; } - plerr = nm_platform_link_6lowpan_add (nm_device_get_platform (device), iface, parent_ifindex, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_6lowpan_add (nm_device_get_platform (device), iface, parent_ifindex, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create 6lowpan interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 6e7e6ffc..6dabdfe8 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -22,7 +22,6 @@ #include "nm-device-bond.h" -#include <errno.h> #include <stdlib.h> #include "NetworkManagerUtils.h" @@ -220,7 +219,6 @@ static NMActStageReturn apply_bonding_config (NMDevice *device) { NMDeviceBond *self = NM_DEVICE_BOND (device); - NMConnection *connection; NMSettingBond *s_bond; int ifindex = nm_device_get_ifindex (device); const char *mode_str, *value; @@ -241,10 +239,9 @@ apply_bonding_config (NMDevice *device) * arp_interval doesn't require miimon to be 0 */ - connection = nm_device_get_applied_connection (device); - g_assert (connection); - s_bond = nm_connection_get_setting_bond (connection); - g_assert (s_bond); + s_bond = nm_device_get_applied_setting (device, NM_TYPE_SETTING_BOND); + + g_return_val_if_fail (s_bond, NM_ACT_STAGE_RETURN_FAILURE); mode_str = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE); if (!mode_str) @@ -461,17 +458,17 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; + int r; g_assert (iface); - plerr = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_bond_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bond interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } return TRUE; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 68e1ac28..4c8921c0 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -512,7 +512,7 @@ create_and_realize (NMDevice *device, const char *hwaddr; gs_free char *hwaddr_cloned = NULL; guint8 mac_address[NM_UTILS_HWADDR_LEN_MAX]; - NMPlatformError plerr; + int r; nm_assert (iface); @@ -539,17 +539,17 @@ create_and_realize (NMDevice *device, } } - plerr = nm_platform_link_bridge_add (nm_device_get_platform (device), - iface, - hwaddr ? mac_address : NULL, - hwaddr ? ETH_ALEN : 0, - out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_bridge_add (nm_device_get_platform (device), + iface, + hwaddr ? mac_address : NULL, + hwaddr ? ETH_ALEN : 0, + out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create bridge interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index a9059383..47a45342 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -17,7 +17,6 @@ #include "nm-device-dummy.h" #include <stdlib.h> -#include <string.h> #include <sys/types.h> #include "nm-act-request.h" @@ -98,19 +97,19 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingDummy *s_dummy; + int r; s_dummy = nm_connection_get_setting_dummy (connection); g_assert (s_dummy); - plerr = nm_platform_link_dummy_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_dummy_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create dummy interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index e7262683..24c99f76 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -24,10 +24,8 @@ #include "nm-device-ethernet.h" #include <netinet/in.h> -#include <string.h> #include <stdlib.h> #include <unistd.h> -#include <errno.h> #include <libudev.h> #include "nm-device-private.h" @@ -556,7 +554,9 @@ build_supplicant_config (NMDeviceEthernet *self, guint32 mtu; connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_assert (connection); + + g_return_val_if_fail (connection, NULL); + con_uuid = nm_connection_get_uuid (connection); mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); @@ -790,7 +790,7 @@ link_negotiation_set (NMDevice *device) guint32 speed = 0; guint32 link_speed; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + s_wired = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); if (s_wired) { autoneg = nm_setting_wired_get_auto_negotiate (s_wired); speed = nm_setting_wired_get_speed (s_wired); @@ -880,8 +880,8 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *out_failure_reason) delay); g_assert (!priv->pppoe_wait_id); priv->pppoe_wait_id = g_timeout_add_seconds (delay, - pppoe_reconnect_delay, - self); + pppoe_reconnect_delay, + self); return NM_ACT_STAGE_RETURN_POSTPONE; } priv->last_pppoe_time = 0; @@ -900,6 +900,7 @@ nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *out_failure NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); security = nm_connection_get_setting_802_1x (connection); @@ -981,9 +982,8 @@ ppp_ip4_config (NMPPPManager *ppp_manager, NMDevice *device = NM_DEVICE (user_data); /* Ignore PPP IP4 events that come in after initial configuration */ - if (nm_device_activate_ip4_state_in_conf (device)) { - nm_device_activate_schedule_ip4_config_result (device, config); - } + if (nm_device_activate_ip4_state_in_conf (device)) + nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config)); } static NMActStageReturn @@ -995,10 +995,12 @@ pppoe_stage3_ip4_config_start (NMDeviceEthernet *self, NMDeviceStateReason *out_ NMActRequest *req; GError *err = NULL; - req = nm_device_get_act_request (NM_DEVICE (self)); + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); + s_pppoe = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); priv->ppp_manager = nm_ppp_manager_create (nm_device_get_iface (device), @@ -1069,8 +1071,10 @@ dcb_configure (NMDevice *device) nm_clear_g_source (&priv->dcb_timeout_id); - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); - g_assert (s_dcb); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + + g_return_val_if_fail (s_dcb, FALSE); + if (!nm_dcb_setup (nm_device_get_iface (device), s_dcb, &error)) { _LOGW (LOGD_DCB, "Activation: (ethernet) failed to enable DCB/FCoE: %s", error->message); @@ -1199,7 +1203,8 @@ wake_on_lan_enable (NMDevice *device) NMSettingWired *s_wired; const char *password = NULL; - s_wired = (NMSettingWired *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + s_wired = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRED); + if (s_wired) { wol = nm_setting_wired_get_wake_on_lan (s_wired); password = nm_setting_wired_get_wake_on_lan_password (s_wired); @@ -1208,7 +1213,7 @@ wake_on_lan_enable (NMDevice *device) } wol = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ethernet.wake-on-lan", + NM_CON_DEFAULT ("ethernet.wake-on-lan"), device, NM_SETTING_WIRED_WAKE_ON_LAN_NONE, G_MAXINT32, @@ -1240,8 +1245,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; NMSettingDcb *s_dcb; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device, - NM_TYPE_SETTING_CONNECTION)); + s_con = nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION); + g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); nm_clear_g_source (&priv->dcb_timeout_id); @@ -1254,8 +1259,8 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (!strcmp (connection_type, NM_SETTING_WIRED_SETTING_NAME)) { NMSetting8021x *security; - security = (NMSetting8021x *) nm_device_get_applied_setting (device, - NM_TYPE_SETTING_802_1X); + security = nm_device_get_applied_setting (device, NM_TYPE_SETTING_802_1X); + if (security) { /* FIXME: for now 802.1x is mutually exclusive with DCB */ return nm_8021x_stage2_config (self, out_failure_reason); @@ -1265,7 +1270,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) wake_on_lan_enable (device); /* DCB and FCoE setup */ - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); if (s_dcb) { /* lldpad really really wants the carrier to be up */ if (nm_platform_link_is_connected (nm_device_get_platform (device), nm_device_get_ifindex (device))) { @@ -1288,7 +1293,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NM_SETTING_PPPOE_SETTING_NAME)) { NMSettingPpp *s_ppp; - s_ppp = (NMSettingPpp *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPP); + s_ppp = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPP); if (s_ppp) { guint32 mtu = 0, mru = 0, mxu; @@ -1309,21 +1314,25 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { NMSettingConnection *s_con; const char *connection_type; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION)); - g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); + if (addr_family == AF_INET) { + s_con = nm_device_get_applied_setting (device, NM_TYPE_SETTING_CONNECTION); - connection_type = nm_setting_connection_get_connection_type (s_con); - if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) - return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), out_failure_reason); + g_return_val_if_fail (s_con, NM_ACT_STAGE_RETURN_FAILURE); + + connection_type = nm_setting_connection_get_connection_type (s_con); + if (!strcmp (connection_type, NM_SETTING_PPPOE_SETTING_NAME)) + return pppoe_stage3_ip4_config_start (NM_DEVICE_ETHERNET (device), out_failure_reason); + } - return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); + return NM_DEVICE_CLASS (nm_device_ethernet_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); } static guint32 @@ -1347,7 +1356,7 @@ deactivate (NMDevice *device) nm_clear_g_source (&priv->pppoe_wait_id); if (priv->ppp_manager) { - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -1358,7 +1367,7 @@ deactivate (NMDevice *device) priv->dcb_handle_carrier_changes = FALSE; /* Tear down DCB/FCoE if it was enabled */ - s_dcb = (NMSettingDcb *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); + s_dcb = nm_device_get_applied_setting (device, NM_TYPE_SETTING_DCB); if (s_dcb) { if (!nm_dcb_cleanup (nm_device_get_iface (device), &error)) { _LOGW (LOGD_DEVICE | LOGD_PLATFORM, "failed to disable DCB/FCoE: %s", @@ -1785,7 +1794,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass) device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->get_configured_mtu = get_configured_mtu; device_class->deactivate = deactivate; device_class->get_s390_subchannels = get_s390_subchannels; diff --git a/src/devices/nm-device-factory.c b/src/devices/nm-device-factory.c index a11ead6e..30aca038 100644 --- a/src/devices/nm-device-factory.c +++ b/src/devices/nm-device-factory.c @@ -24,8 +24,6 @@ #include <sys/types.h> #include <sys/stat.h> -#include <errno.h> -#include <string.h> #include <gmodule.h> #include "platform/nm-platform.h" diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 5138b684..4db7d8a7 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -87,7 +87,8 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) if (ret != NM_ACT_STAGE_RETURN_SUCCESS) return ret; - s_infiniband = (NMSettingInfiniband *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND); + s_infiniband = nm_device_get_applied_setting (device, NM_TYPE_SETTING_INFINIBAND); + g_return_val_if_fail (s_infiniband, NM_ACT_STAGE_RETURN_FAILURE); transport_mode = nm_setting_infiniband_get_transport_mode (s_infiniband); @@ -234,7 +235,7 @@ create_and_realize (NMDevice *device, { NMDeviceInfinibandPrivate *priv = NM_DEVICE_INFINIBAND_GET_PRIVATE ((NMDeviceInfiniband *) device); NMSettingInfiniband *s_infiniband; - NMPlatformError plerr; + int r; s_infiniband = nm_connection_get_setting_infiniband (connection); g_assert (s_infiniband); @@ -268,13 +269,13 @@ create_and_realize (NMDevice *device, return FALSE; } - plerr = nm_platform_link_infiniband_add (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_infiniband_add (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", nm_device_get_iface (device), nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -286,7 +287,7 @@ static gboolean unrealize (NMDevice *device, GError **error) { NMDeviceInfinibandPrivate *priv; - NMPlatformError plerr; + int r; g_return_val_if_fail (NM_IS_DEVICE_INFINIBAND (device), FALSE); @@ -298,12 +299,12 @@ unrealize (NMDevice *device, GError **error) return FALSE; } - plerr = nm_platform_link_infiniband_delete (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_infiniband_delete (nm_device_get_platform (device), priv->parent_ifindex, priv->p_key); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to remove InfiniBand P_Key interface '%s': %s", nm_device_get_iface (device), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 1c7e6d51..96275947 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -22,7 +22,6 @@ #include "nm-device-ip-tunnel.h" -#include <string.h> #include <netinet/in.h> #include <linux/if.h> #include <linux/ip.h> @@ -149,9 +148,13 @@ update_properties_from_ifindex (NMDevice *device, int ifindex) NMDeviceIPTunnel *self = NM_DEVICE_IP_TUNNEL (device); NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (self); int parent_ifindex = 0; - in_addr_t local4 = 0, remote4 = 0; - struct in6_addr local6 = { 0 }, remote6 = { 0 }; - guint8 ttl = 0, tos = 0, encap_limit = 0; + in_addr_t local4 = 0; + in_addr_t remote4 = 0; + struct in6_addr local6 = IN6ADDR_ANY_INIT; + struct in6_addr remote6 = IN6ADDR_ANY_INIT; + guint8 ttl = 0; + guint8 tos = 0; + guint8 encap_limit = 0; gboolean pmtud = FALSE; guint32 flow_label = 0; NMIPTunnelFlags flags = NM_IP_TUNNEL_FLAG_NONE; @@ -329,28 +332,28 @@ clear: if (!address_equal_pn (AF_INET, priv->local, &local4)) { g_clear_pointer (&priv->local, g_free); if (local4) - priv->local = g_strdup (nm_utils_inet4_ntop (local4, NULL)); + priv->local = nm_utils_inet4_ntop_dup (local4); _notify (self, PROP_LOCAL); } if (!address_equal_pn (AF_INET, priv->remote, &remote4)) { g_clear_pointer (&priv->remote, g_free); if (remote4) - priv->remote = g_strdup (nm_utils_inet4_ntop (remote4, NULL)); + priv->remote = nm_utils_inet4_ntop_dup (remote4); _notify (self, PROP_REMOTE); } } else { if (!address_equal_pn (AF_INET6, priv->local, &local6)) { g_clear_pointer (&priv->local, g_free); if (memcmp (&local6, &in6addr_any, sizeof (in6addr_any))) - priv->local = g_strdup (nm_utils_inet6_ntop (&local6, NULL)); + priv->local = nm_utils_inet6_ntop_dup (&local6); _notify (self, PROP_LOCAL); } if (!address_equal_pn (AF_INET6, priv->remote, &remote6)) { g_clear_pointer (&priv->remote, g_free); if (memcmp (&remote6, &in6addr_any, sizeof (in6addr_any))) - priv->remote = g_strdup (nm_utils_inet6_ntop (&remote6, NULL)); + priv->remote = nm_utils_inet6_ntop_dup (&remote6); _notify (self, PROP_REMOTE); } } @@ -660,7 +663,6 @@ create_and_realize (NMDevice *device, { const char *iface = nm_device_get_iface (device); NMSettingIPTunnel *s_ip_tunnel; - NMPlatformError plerr; NMPlatformLnkGre lnk_gre = { }; NMPlatformLnkSit lnk_sit = { }; NMPlatformLnkIpIp lnk_ipip = { }; @@ -668,6 +670,7 @@ create_and_realize (NMDevice *device, const char *str; gint64 val; NMIPTunnelMode mode; + int r; s_ip_tunnel = nm_connection_get_setting_ip_tunnel (connection); g_assert (s_ip_tunnel); @@ -713,13 +716,13 @@ create_and_realize (NMDevice *device, lnk_gre.output_flags = NM_GRE_KEY; } - plerr = nm_platform_link_gre_add (nm_device_get_platform (device), iface, &lnk_gre, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_gre_add (nm_device_get_platform (device), iface, &lnk_gre, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create GRE interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -739,13 +742,13 @@ create_and_realize (NMDevice *device, lnk_sit.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel); lnk_sit.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel); - plerr = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create SIT interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -765,13 +768,13 @@ create_and_realize (NMDevice *device, lnk_ipip.tos = nm_setting_ip_tunnel_get_tos (s_ip_tunnel); lnk_ipip.path_mtu_discovery = nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel); - plerr = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create IPIP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; @@ -820,21 +823,21 @@ create_and_realize (NMDevice *device, lnk_ip6tnl.is_gre = TRUE; lnk_ip6tnl.is_tap = (mode == NM_IP_TUNNEL_MODE_IP6GRETAP); - plerr = nm_platform_link_ip6gre_add (nm_device_get_platform (device), - iface, &lnk_ip6tnl, out_plink); + r = nm_platform_link_ip6gre_add (nm_device_get_platform (device), + iface, &lnk_ip6tnl, out_plink); } else { lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6; - plerr = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), - iface, &lnk_ip6tnl, out_plink); + r = nm_platform_link_ip6tnl_add (nm_device_get_platform (device), + iface, &lnk_ip6tnl, out_plink); } - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create IPv6 tunnel interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } break; diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 8ea4c8b5..1a6b64a4 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -210,7 +210,7 @@ update_properties (NMDevice *device) static NMSupplicantConfig * build_supplicant_config (NMDeviceMacsec *self, GError **error) { - NMSupplicantConfig *config = NULL; + gs_unref_object NMSupplicantConfig *config = NULL; NMSettingMacsec *s_macsec; NMSetting8021x *s_8021x; NMConnection *connection; @@ -218,19 +218,21 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) guint32 mtu; connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_assert (connection); + + g_return_val_if_fail (connection, NULL); + con_uuid = nm_connection_get_uuid (connection); mtu = nm_platform_link_get_mtu (nm_device_get_platform (NM_DEVICE (self)), nm_device_get_ifindex (NM_DEVICE (self))); config = nm_supplicant_config_new (FALSE, FALSE); - s_macsec = (NMSettingMacsec *) - nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); + s_macsec = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_MACSEC); + + g_return_val_if_fail (s_macsec, NULL); if (!nm_supplicant_config_add_setting_macsec (config, s_macsec, error)) { g_prefix_error (error, "macsec-setting: "); - g_object_unref (config); return NULL; } @@ -238,11 +240,11 @@ build_supplicant_config (NMDeviceMacsec *self, GError **error) s_8021x = nm_connection_get_setting_802_1x (connection); if (!nm_supplicant_config_add_setting_8021x (config, s_8021x, con_uuid, mtu, TRUE, error)) { g_prefix_error (error, "802-1x-setting: "); - g_clear_object (&config); + return NULL; } } - return config; + return g_steal_pointer (&config); } static void @@ -588,6 +590,7 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) const char *setting_name; connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); if (!priv->supplicant.mgr) @@ -654,7 +657,6 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingMacsec *s_macsec; NMPlatformLnkMacsec lnk = { }; int parent_ifindex; @@ -666,6 +668,7 @@ create_and_realize (NMDevice *device, } s; guint64 u; } sci; + int r; s_macsec = nm_connection_get_setting_macsec (connection); g_assert (s_macsec); @@ -694,13 +697,13 @@ create_and_realize (NMDevice *device, parent_ifindex = nm_device_get_ifindex (parent); g_warn_if_fail (parent_ifindex > 0); - plerr = nm_platform_link_macsec_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_macsec_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create macsec interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 2b212154..aa2a0ac0 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -22,7 +22,6 @@ #include "nm-device-macvlan.h" -#include <string.h> #include <linux/if_link.h> #include "nm-device-private.h" @@ -227,10 +226,10 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMSettingMacvlan *s_macvlan; NMPlatformLnkMacvlan lnk = { }; int parent_ifindex; + int r; s_macvlan = nm_connection_get_setting_macvlan (connection); g_return_val_if_fail (s_macvlan, FALSE); @@ -255,14 +254,14 @@ create_and_realize (NMDevice *device, lnk.no_promisc = !nm_setting_macvlan_get_promiscuous (s_macvlan); lnk.tap = nm_setting_macvlan_get_tap (s_macvlan); - plerr = nm_platform_link_macvlan_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_macvlan_add (nm_device_get_platform (device), iface, parent_ifindex, &lnk, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create %s interface '%s' for '%s': %s", lnk.tap ? "macvtap" : "macvlan", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-ppp.c b/src/devices/nm-device-ppp.c index 74b4d710..3c310146 100644 --- a/src/devices/nm-device-ppp.c +++ b/src/devices/nm-device-ppp.c @@ -16,6 +16,7 @@ #include "nm-device-ppp.h" +#include "nm-ip4-config.h" #include "nm-act-request.h" #include "nm-device-factory.h" #include "nm-device-private.h" @@ -106,7 +107,7 @@ ppp_ip4_config (NMPPPManager *ppp_manager, if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) { if (nm_device_activate_ip4_state_in_conf (device)) { - nm_device_activate_schedule_ip4_config_result (device, config); + nm_device_activate_schedule_ip_config_result (device, AF_INET, NM_IP_CONFIG_CAST (config)); return; } } else { @@ -125,10 +126,12 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActRequest *req; GError *error = NULL; - req = nm_device_get_act_request (NM_DEVICE (self)); + req = nm_device_get_act_request (device); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - s_pppoe = (NMSettingPppoe *) nm_device_get_applied_setting ((NMDevice *) self, NM_TYPE_SETTING_PPPOE); + s_pppoe = nm_device_get_applied_setting (device, NM_TYPE_SETTING_PPPOE); + g_return_val_if_fail (s_pppoe, NM_ACT_STAGE_RETURN_FAILURE); g_clear_object (&priv->ip4_config); @@ -170,23 +173,31 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { - NMDevicePpp *self = NM_DEVICE_PPP (device); - NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + if (addr_family == AF_INET) { + NMDevicePpp *self = NM_DEVICE_PPP (device); + NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); + + if (priv->ip4_config) { + if (out_config) + *out_config = g_steal_pointer (&priv->ip4_config); + else + g_clear_object (&priv->ip4_config); + return NM_ACT_STAGE_RETURN_SUCCESS; + } - if (priv->ip4_config) { - if (out_config) - *out_config = g_steal_pointer (&priv->ip4_config); - else - g_clear_object (&priv->ip4_config); - return NM_ACT_STAGE_RETURN_SUCCESS; + /* Wait IPCP termination */ + return NM_ACT_STAGE_RETURN_POSTPONE; } - /* Wait IPCP termination */ - return NM_ACT_STAGE_RETURN_POSTPONE; + return NM_DEVICE_CLASS (nm_device_ppp_parent_class)->act_stage3_ip_config_start (device, + addr_family, + out_config, + out_failure_reason); } static gboolean @@ -221,7 +232,7 @@ deactivate (NMDevice *device) NMDevicePppPrivate *priv = NM_DEVICE_PPP_GET_PRIVATE (self); if (priv->ppp_manager) { - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } } @@ -268,7 +279,7 @@ nm_device_ppp_class_init (NMDevicePppClass *klass) device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_PPP); device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->create_and_realize = create_and_realize; device_class->deactivate = deactivate; device_class->get_generic_capabilities = get_generic_capabilities; diff --git a/src/devices/nm-device-private.h b/src/devices/nm-device-private.h index 66c715de..6e2372ab 100644 --- a/src/devices/nm-device-private.h +++ b/src/devices/nm-device-private.h @@ -26,6 +26,14 @@ /* This file should only be used by subclasses of NMDevice */ +typedef enum { + NM_DEVICE_IP_STATE_NONE, + NM_DEVICE_IP_STATE_WAIT, + NM_DEVICE_IP_STATE_CONF, + NM_DEVICE_IP_STATE_DONE, + NM_DEVICE_IP_STATE_FAIL, +} NMDeviceIPState; + enum NMActStageReturn { NM_ACT_STAGE_RETURN_FAILURE = 0, /* Hard failure of activation */ NM_ACT_STAGE_RETURN_SUCCESS, /* Activation stage done */ @@ -34,7 +42,7 @@ enum NMActStageReturn { NM_ACT_STAGE_RETURN_IP_DONE, /* IP config stage is done (state IP_DONE), For the ip-config stage, this is similar to NM_ACT_STAGE_RETURN_SUCCESS, except that no - IP config should be commited. */ + IP config should be committed. */ NM_ACT_STAGE_RETURN_IP_FAIL, /* IP config stage failed (state IP_FAIL), activation may proceed */ }; @@ -75,19 +83,51 @@ void nm_device_set_firmware_missing (NMDevice *self, gboolean missing); void nm_device_activate_schedule_stage1_device_prepare (NMDevice *device); void nm_device_activate_schedule_stage2_device_config (NMDevice *device); -void nm_device_activate_schedule_ip4_config_result(NMDevice *device, NMIP4Config *config); -void nm_device_activate_schedule_ip4_config_timeout (NMDevice *device); - -void nm_device_activate_schedule_ip6_config_result (NMDevice *device); -void nm_device_activate_schedule_ip6_config_timeout (NMDevice *device); - -gboolean nm_device_activate_ip4_state_in_conf (NMDevice *device); -gboolean nm_device_activate_ip4_state_in_wait (NMDevice *device); -gboolean nm_device_activate_ip4_state_done (NMDevice *device); - -gboolean nm_device_activate_ip6_state_in_conf (NMDevice *device); -gboolean nm_device_activate_ip6_state_in_wait (NMDevice *device); -gboolean nm_device_activate_ip6_state_done (NMDevice *device); +void nm_device_activate_schedule_ip_config_result (NMDevice *device, + int addr_family, + NMIPConfig *config); + +void nm_device_activate_schedule_ip_config_timeout (NMDevice *device, + int addr_family); + +NMDeviceIPState nm_device_activate_get_ip_state (NMDevice *self, + int addr_family); + +static inline gboolean +nm_device_activate_ip4_state_in_conf (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET) == NM_DEVICE_IP_STATE_CONF; +} + +static inline gboolean +nm_device_activate_ip4_state_in_wait (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET) == NM_DEVICE_IP_STATE_WAIT; +} + +static inline gboolean +nm_device_activate_ip4_state_done (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET) == NM_DEVICE_IP_STATE_DONE; +} + +static inline gboolean +nm_device_activate_ip6_state_in_conf (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET6) == NM_DEVICE_IP_STATE_CONF; +} + +static inline gboolean +nm_device_activate_ip6_state_in_wait (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET6) == NM_DEVICE_IP_STATE_WAIT; +} + +static inline gboolean +nm_device_activate_ip6_state_done (NMDevice *self) +{ + return nm_device_activate_get_ip_state (self, AF_INET6) == NM_DEVICE_IP_STATE_DONE; +} void nm_device_set_dhcp_anycast_address (NMDevice *device, const char *addr); @@ -106,14 +146,24 @@ void nm_device_queue_recheck_available (NMDevice *device, NMDeviceStateReason available_reason, NMDeviceStateReason unavailable_reason); -void nm_device_set_wwan_ip4_config (NMDevice *device, NMIP4Config *config); -void nm_device_set_wwan_ip6_config (NMDevice *device, NMIP6Config *config); +void nm_device_set_dev2_ip_config (NMDevice *device, + int addr_family, + NMIPConfig *config); gboolean nm_device_hw_addr_is_explict (NMDevice *device); -void nm_device_ip_method_failed (NMDevice *self, int family, NMDeviceStateReason reason); +void nm_device_ip_method_failed (NMDevice *self, int addr_family, NMDeviceStateReason reason); + +gboolean nm_device_sysctl_ip_conf_set (NMDevice *self, + int addr_family, + const char *property, + const char *value); + +NMIP4Config *nm_device_ip4_config_new (NMDevice *self); + +NMIP6Config *nm_device_ip6_config_new (NMDevice *self); -gboolean nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value); +NMIPConfig *nm_device_ip_config_new (NMDevice *self, int addr_family); /*****************************************************************************/ @@ -147,9 +197,9 @@ void nm_device_commit_mtu (NMDevice *self); ) gboolean _nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **whitelist); + GError **error, const char *const*whitelist); #define nm_device_hash_check_invalid_keys(hash, setting_name, error, ...) \ - _nm_device_hash_check_invalid_keys (hash, setting_name, error, ((const char *[]) { __VA_ARGS__, NULL })) + _nm_device_hash_check_invalid_keys (hash, setting_name, error, NM_MAKE_STRV (__VA_ARGS__)) gboolean nm_device_match_parent (NMDevice *device, const char *parent); gboolean nm_device_match_parent_hwaddr (NMDevice *device, diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 0f76b23a..90360c9e 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -23,7 +23,6 @@ #include "nm-device-tun.h" #include <stdlib.h> -#include <string.h> #include <sys/types.h> #include <linux/if_tun.h> @@ -231,9 +230,10 @@ create_and_realize (NMDevice *device, { const char *iface = nm_device_get_iface (device); NMPlatformLnkTun props = { }; - NMPlatformError plerr; NMSettingTun *s_tun; - gint64 owner, group; + gint64 owner; + gint64 group; + int r; s_tun = nm_connection_get_setting_tun (connection); g_return_val_if_fail (s_tun, FALSE); @@ -261,17 +261,17 @@ create_and_realize (NMDevice *device, props.multi_queue = nm_setting_tun_get_multi_queue (s_tun); props.persist = TRUE; - plerr = nm_platform_link_tun_add (nm_device_get_platform (device), - iface, - &props, - out_plink, - NULL); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_tun_add (nm_device_get_platform (device), + iface, + &props, + out_plink, + NULL); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create TUN/TAP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index 6f90758c..d7a59bae 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -20,9 +20,7 @@ #include "nm-default.h" -#include <errno.h> #include <stdlib.h> -#include <string.h> #include "nm-device-veth.h" #include "nm-device-private.h" diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index b7f0c4e7..ace6a24b 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -241,7 +241,7 @@ create_and_realize (NMDevice *device, NMSettingVlan *s_vlan; int parent_ifindex; guint vlan_id; - NMPlatformError plerr; + int r; s_vlan = nm_connection_get_setting_vlan (connection); g_assert (s_vlan); @@ -271,18 +271,18 @@ create_and_realize (NMDevice *device, vlan_id = nm_setting_vlan_get_id (s_vlan); - plerr = nm_platform_link_vlan_add (nm_device_get_platform (device), - iface, - parent_ifindex, - vlan_id, - nm_setting_vlan_get_flags (s_vlan), - out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_vlan_add (nm_device_get_platform (device), + iface, + parent_ifindex, + vlan_id, + nm_setting_vlan_get_flags (s_vlan), + out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create VLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -493,7 +493,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) parent_mtu_maybe_changed (parent_device, NULL, device); } - s_vlan = (NMSettingVlan *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_VLAN); + s_vlan = nm_device_get_applied_setting (device, NM_TYPE_SETTING_VLAN); if (s_vlan) { gs_free NMVlanQosMapping *ingress_map = NULL; gs_free NMVlanQosMapping *egress_map = NULL; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index c34f4142..fc73c099 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -22,8 +22,6 @@ #include "nm-device-vxlan.h" -#include <string.h> - #include "nm-device-private.h" #include "nm-manager.h" #include "platform/nm-platform.h" @@ -34,6 +32,7 @@ #include "settings/nm-settings.h" #include "nm-act-request.h" #include "nm-ip4-config.h" +#include "nm-core-internal.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceVxlan); @@ -170,11 +169,11 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; NMPlatformLnkVxlan props = { }; NMSettingVxlan *s_vxlan; const char *str; int ret; + int r; s_vxlan = nm_connection_get_setting_vxlan (connection); g_assert (s_vxlan); @@ -213,13 +212,13 @@ create_and_realize (NMDevice *device, props.l2miss = nm_setting_vxlan_get_l2_miss (s_vxlan); props.l3miss = nm_setting_vxlan_get_l3_miss (s_vxlan); - plerr = nm_platform_link_vxlan_add (nm_device_get_platform (device), iface, &props, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_vxlan_add (nm_device_get_platform (device), iface, &props, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create VXLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } @@ -386,6 +385,7 @@ update_connection (NMDevice *device, NMConnection *connection) { NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE ((NMDeviceVxlan *) device); NMSettingVxlan *s_vxlan = nm_connection_get_setting_vxlan (connection); + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!s_vxlan) { s_vxlan = (NMSettingVxlan *) nm_setting_vxlan_new (); @@ -404,11 +404,11 @@ update_connection (NMDevice *device, NMConnection *connection) if (!address_matches (nm_setting_vxlan_get_remote (s_vxlan), priv->props.group, &priv->props.group6)) { if (priv->props.group) { g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, - nm_utils_inet4_ntop (priv->props.group, NULL), + nm_utils_inet4_ntop (priv->props.group, sbuf), NULL); } else { g_object_set (s_vxlan, NM_SETTING_VXLAN_REMOTE, - nm_utils_inet6_ntop (&priv->props.group6, NULL), + nm_utils_inet6_ntop (&priv->props.group6, sbuf), NULL); } } @@ -416,11 +416,11 @@ update_connection (NMDevice *device, NMConnection *connection) if (!address_matches (nm_setting_vxlan_get_local (s_vxlan), priv->props.local, &priv->props.local6)) { if (priv->props.local) { g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, - nm_utils_inet4_ntop (priv->props.local, NULL), + nm_utils_inet4_ntop (priv->props.local, sbuf), NULL); } else if (memcmp (&priv->props.local6, &in6addr_any, sizeof (in6addr_any))) { g_object_set (s_vxlan, NM_SETTING_VXLAN_LOCAL, - nm_utils_inet6_ntop (&priv->props.local6, NULL), + nm_utils_inet6_ntop (&priv->props.local6, sbuf), NULL); } } @@ -510,15 +510,15 @@ get_property (GObject *object, guint prop_id, break; case PROP_GROUP: if (priv->props.group) - g_value_set_string (value, nm_utils_inet4_ntop (priv->props.group, NULL)); + g_value_take_string (value, nm_utils_inet4_ntop_dup (priv->props.group)); else if (!IN6_IS_ADDR_UNSPECIFIED (&priv->props.group6)) - g_value_set_string (value, nm_utils_inet6_ntop (&priv->props.group6, NULL)); + g_value_take_string (value, nm_utils_inet6_ntop_dup (&priv->props.group6)); break; case PROP_LOCAL: if (priv->props.local) - g_value_set_string (value, nm_utils_inet4_ntop (priv->props.local, NULL)); + g_value_take_string (value, nm_utils_inet4_ntop_dup (priv->props.local)); else if (!IN6_IS_ADDR_UNSPECIFIED (&priv->props.local6)) - g_value_set_string (value, nm_utils_inet6_ntop (&priv->props.local6, NULL)); + g_value_take_string (value, nm_utils_inet6_ntop_dup (&priv->props.local6)); break; case PROP_TOS: g_value_set_uchar (value, priv->props.tos); diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index 62ec0274..a9eb1ab4 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -21,24 +21,128 @@ #include "nm-device-wireguard.h" +#include "nm-setting-wireguard.h" +#include "nm-core-internal.h" +#include "nm-utils/nm-secret-utils.h" #include "nm-device-private.h" #include "platform/nm-platform.h" +#include "platform/nmp-object.h" #include "nm-device-factory.h" +#include "nm-active-connection.h" +#include "nm-act-request.h" +#include "dns/nm-dns-manager.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceWireGuard); /*****************************************************************************/ +/* TODO: activate profile with peer preshared-key-flags=2. On first activation, the secret is + * requested (good). Enter it and connect. Reactivate the profile, now there is no password + * prompt, as the secret is cached (good??). */ + +/* TODO: unlike for other VPNs, we don't inject a direct route to the peers. That means, + * you might get a routing sceneraio where the peer (VPN server) is reachable via the VPN. + * How we handle adding routes to external gateway for other peers, has severe issues + * as well. We may use policy-routing like wg-quick does. See also disussions at + * https://www.wireguard.com/netns/#improving-the-classic-solutions */ + +/* TODO: honor the TTL of DNS to determine when to retry resolving endpoints. */ + +/* TODO: when we get multiple IP addresses when resolving a peer endpoint. We currently + * just take the first from GAI. We should only accept AAAA/IPv6 if we also have a suitable + * IPv6 address. The problem is, that we have to recheck that when IP addressing on other + * interfaces changes. This makes it almost too cumbersome to implement. */ + +/*****************************************************************************/ + +G_STATIC_ASSERT (NM_WIREGUARD_PUBLIC_KEY_LEN == NMP_WIREGUARD_PUBLIC_KEY_LEN); +G_STATIC_ASSERT (NM_WIREGUARD_SYMMETRIC_KEY_LEN == NMP_WIREGUARD_SYMMETRIC_KEY_LEN); + +/*****************************************************************************/ + +#define LINK_CONFIG_RATE_LIMIT_NSEC (50 * NM_UTILS_NS_PER_MSEC) + +/* a special @next_try_at_nsec timestamp indicating that we should try again as soon as possible. */ +#define NEXT_TRY_AT_NSEC_ASAP ((gint64) G_MAXINT64) + +/* a special @next_try_at_nsec timestamp that is + * - positive (indicating resolve-checks are enabled) + * - already in the past (we use the absolute timestamp of 1nsec for that). */ +#define NEXT_TRY_AT_NSEC_PAST ((gint64) 1) + +/* like %NEXT_TRY_AT_NSEC_ASAP, but used for indicating to retry ASAP for a @retry_in_msec value. + * That is a relative time duraction, contrary to @next_try_at_nsec which is an absolute + * timestamp. */ +#define RETRY_IN_MSEC_ASAP ((gint64) G_MAXINT64) + +#define RETRY_IN_MSEC_MAX ((gint64) (30 * 60 * 1000)) + +typedef enum { + LINK_CONFIG_MODE_FULL, + LINK_CONFIG_MODE_REAPPLY, + LINK_CONFIG_MODE_ASSUME, + LINK_CONFIG_MODE_ENDPOINTS, +} LinkConfigMode; + +typedef struct { + GCancellable *cancellable; + + NMSockAddrUnion sockaddr; + + /* the timestamp (in nm_utils_get_monotonic_timestamp_ns() scale) when we want + * to retry resolving the endpoint (again). + * + * It may be set to %NEXT_TRY_AT_NSEC_ASAP to indicate to re-resolve as soon as possible. + * + * A @sockaddr is either fixed or it has + * - @cancellable set to indicate an ongoing request + * - @next_try_at_nsec set to a positive value, indicating when + * we ought to retry. */ + gint64 next_try_at_nsec; + + guint resolv_fail_count; +} PeerEndpointResolveData; + +typedef struct { + NMWireGuardPeer *peer; + + NMDeviceWireGuard *self; + + CList lst_peers; + + PeerEndpointResolveData ep_resolv; + + /* dirty flag used during _peers_update_all(). */ + bool dirty_update_all:1; +} PeerData; + NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWireGuard, PROP_PUBLIC_KEY, PROP_LISTEN_PORT, PROP_FWMARK, ); +typedef struct { + + NMDnsManager *dns_manager; + + NMPlatformLnkWireGuard lnk_curr; + NMActRequestGetSecretsCallId *secrets_call_id; + + CList lst_peers_head; + GHashTable *peers; + + gint64 resolve_next_try_at; + guint resolve_next_try_id; + + gint64 link_config_last_at; + guint link_config_delayed_id; +} NMDeviceWireGuardPrivate; + struct _NMDeviceWireGuard { NMDevice parent; - NMPlatformLnkWireGuard props; + NMDeviceWireGuardPrivate _priv; }; struct _NMDeviceWireGuardClass { @@ -47,25 +151,710 @@ struct _NMDeviceWireGuardClass { G_DEFINE_TYPE (NMDeviceWireGuard, nm_device_wireguard, NM_TYPE_DEVICE) -/******************************************************************/ +#define NM_DEVICE_WIREGUARD_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceWireGuard, NM_IS_DEVICE_WIREGUARD, NMDevice) + +/*****************************************************************************/ + +static void _peers_resolve_start (NMDeviceWireGuard *self, + PeerData *peer_data); + +static void _peers_resolve_retry_reschedule (NMDeviceWireGuard *self, + gint64 new_next_try_at_nsec); + +static gboolean link_config_delayed_resolver_cb (gpointer user_data); + +static gboolean link_config_delayed_ratelimit_cb (gpointer user_data); + +/*****************************************************************************/ + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_link_config_mode_to_string, LinkConfigMode, + NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT (NULL), + NM_UTILS_LOOKUP_ITEM (LINK_CONFIG_MODE_FULL, "full"), + NM_UTILS_LOOKUP_ITEM (LINK_CONFIG_MODE_REAPPLY, "reapply"), + NM_UTILS_LOOKUP_ITEM (LINK_CONFIG_MODE_ASSUME, "assume"), + NM_UTILS_LOOKUP_ITEM (LINK_CONFIG_MODE_ENDPOINTS, "endpoints"), +); + +/*****************************************************************************/ + +static gboolean +_peer_data_equal (gconstpointer ptr_a, gconstpointer ptr_b) +{ + const PeerData *peer_data_a = ptr_a; + const PeerData *peer_data_b = ptr_b; + + return nm_streq (nm_wireguard_peer_get_public_key (peer_data_a->peer), + nm_wireguard_peer_get_public_key (peer_data_b->peer)); +} + +static guint +_peer_data_hash (gconstpointer ptr) +{ + const PeerData *peer_data = ptr; + + return nm_hash_str (nm_wireguard_peer_get_public_key (peer_data->peer)); +} + +static PeerData * +_peers_find (NMDeviceWireGuardPrivate *priv, + NMWireGuardPeer *peer) +{ + nm_assert (peer); + + G_STATIC_ASSERT_EXPR (G_STRUCT_OFFSET (PeerData, peer) == 0); + + return g_hash_table_lookup (priv->peers, &peer); +} + +static void +_peers_remove (NMDeviceWireGuardPrivate *priv, + PeerData *peer_data) +{ + nm_assert (peer_data); + nm_assert (g_hash_table_lookup (priv->peers, peer_data) == peer_data); + + if (!g_hash_table_remove (priv->peers, peer_data)) + nm_assert_not_reached (); + + c_list_unlink_stale (&peer_data->lst_peers); + nm_wireguard_peer_unref (peer_data->peer); + nm_clear_g_cancellable (&peer_data->ep_resolv.cancellable); + g_slice_free (PeerData, peer_data); + + if (c_list_is_empty (&peer_data->lst_peers)) { + nm_clear_g_source (&priv->resolve_next_try_id); + nm_clear_g_source (&priv->link_config_delayed_id); + } +} + +static PeerData * +_peers_add (NMDeviceWireGuard *self, + NMWireGuardPeer *peer) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + PeerData *peer_data; + + nm_assert (peer); + nm_assert (nm_wireguard_peer_is_sealed (peer)); + nm_assert (!_peers_find (priv, peer)); + + peer_data = g_slice_new (PeerData); + *peer_data = (PeerData) { + .self = self, + .peer = nm_wireguard_peer_ref (peer), + .ep_resolv = { + .sockaddr = NM_SOCK_ADDR_UNION_INIT_UNSPEC, + }, + }; + + c_list_link_tail (&priv->lst_peers_head, &peer_data->lst_peers); + if (!nm_g_hash_table_add (priv->peers, peer_data)) + nm_assert_not_reached (); + return peer_data; +} + +static gboolean +_peers_resolve_retry_timeout (gpointer user_data) +{ + NMDeviceWireGuard *self = user_data; + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + PeerData *peer_data; + gint64 now; + gint64 next; + + priv->resolve_next_try_id = 0; + + _LOGT (LOGD_DEVICE, "wireguard-peers: rechecking peer endpoints..."); + + now = nm_utils_get_monotonic_timestamp_ns (); + next = G_MAXINT64; + c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) { + if (peer_data->ep_resolv.next_try_at_nsec <= 0) + continue; + + if (peer_data->ep_resolv.cancellable) { + /* we are currently resolving a name. We don't need the global + * watchdog to guard this peer. No need to adjust @next for + * this one, when the currently ongoing resolving completes, we + * may reschedule. Skip. */ + continue; + } + + if ( peer_data->ep_resolv.next_try_at_nsec == NEXT_TRY_AT_NSEC_ASAP + || now >= peer_data->ep_resolv.next_try_at_nsec) { + _peers_resolve_start (self, peer_data); + /* same here. Now we are resolving. We don't need the global + * watchdog. Skip w.r.t. finding @next. */ + continue; + } + + if (next > peer_data->ep_resolv.next_try_at_nsec) + next = peer_data->ep_resolv.next_try_at_nsec; + } + if (next < G_MAXINT64) + _peers_resolve_retry_reschedule (self, next); + + return G_SOURCE_REMOVE; +} + +static void +_peers_resolve_retry_reschedule (NMDeviceWireGuard *self, + gint64 new_next_try_at_nsec) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + guint32 interval_ms; + gint64 now; + + nm_assert (new_next_try_at_nsec > 0); + nm_assert (new_next_try_at_nsec != NEXT_TRY_AT_NSEC_ASAP); + + if ( priv->resolve_next_try_id + && priv->resolve_next_try_at <= new_next_try_at_nsec) { + /* we already have an earlier timeout scheduled (possibly for + * another peer that expires sooner). Don't reschedule now. + * Even if the scheduled timeout expires too early, we will + * compute the right next-timeout and reschedule then. */ + return; + } + + now = nm_utils_get_monotonic_timestamp_ns (); + + /* schedule at most one day ahead. No problem if we expire earlier + * than expected. Also, rate-limit to 500 msec. */ + interval_ms = NM_CLAMP ((new_next_try_at_nsec - now) / NM_UTILS_NS_PER_MSEC, + (gint64) 500, + (gint64) (24*60*60*1000)); + + _LOGT (LOGD_DEVICE, "wireguard-peers: schedule rechecking peer endpoints in %u msec", + interval_ms); + + nm_clear_g_source (&priv->resolve_next_try_id); + priv->resolve_next_try_at = new_next_try_at_nsec; + priv->resolve_next_try_id = g_timeout_add (interval_ms, + _peers_resolve_retry_timeout, + self); +} + +static void +_peers_resolve_retry_reschedule_for_peer (NMDeviceWireGuard *self, + PeerData *peer_data, + gint64 retry_in_msec) +{ + nm_assert (retry_in_msec >= 0); + + if (retry_in_msec == RETRY_IN_MSEC_ASAP) { + _peers_resolve_start (self, peer_data); + return; + } + + peer_data->ep_resolv.next_try_at_nsec = nm_utils_get_monotonic_timestamp_ns () + + (retry_in_msec * NM_UTILS_NS_PER_MSEC); + _peers_resolve_retry_reschedule (self, peer_data->ep_resolv.next_try_at_nsec); +} + +static gint64 +_peers_retry_in_msec (PeerData *peer_data, + gboolean after_failure) +{ + if (peer_data->ep_resolv.next_try_at_nsec == NEXT_TRY_AT_NSEC_ASAP) { + peer_data->ep_resolv.resolv_fail_count = 0; + return RETRY_IN_MSEC_ASAP; + } + + if (after_failure) { + if (peer_data->ep_resolv.resolv_fail_count < G_MAXUINT) + peer_data->ep_resolv.resolv_fail_count++; + } else + peer_data->ep_resolv.resolv_fail_count = 0; + + if (!after_failure) + return RETRY_IN_MSEC_MAX; -static GVariant * -get_public_key_as_variant (const NMDeviceWireGuard *self) + if (peer_data->ep_resolv.resolv_fail_count > 20) + return RETRY_IN_MSEC_MAX; + + /* double the retry-time, starting with one second. */ + return NM_MIN (RETRY_IN_MSEC_MAX, + (1u << peer_data->ep_resolv.resolv_fail_count) * 500); +} + +static void +_peers_resolve_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NMDeviceWireGuard *self; + PeerData *peer_data; + gs_free_error GError *resolv_error = NULL; + GList *list; + gboolean changed = FALSE; + NMSockAddrUnion sockaddr; + gint64 retry_in_msec; + char s_sockaddr[100]; + char s_retry[100]; + + list = g_resolver_lookup_by_name_finish (G_RESOLVER (source_object), res, &resolv_error); + + if (nm_utils_error_is_cancelled (resolv_error, FALSE)) + return; + + peer_data = user_data; + self = peer_data->self; + + g_clear_object (&peer_data->ep_resolv.cancellable); + + nm_assert ((!resolv_error) != (!list)); + +#define _retry_in_msec_to_string(retry_in_msec, s_retry) \ + ({ \ + gint64 _retry_in_msec = (retry_in_msec); \ + \ + _retry_in_msec == RETRY_IN_MSEC_ASAP \ + ? "right away" \ + : nm_sprintf_buf (s_retry, "in %"G_GINT64_FORMAT" msec", _retry_in_msec); \ + }) + + if ( resolv_error + && !g_error_matches (resolv_error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND)) { + retry_in_msec = _peers_retry_in_msec (peer_data, TRUE); + + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: failure to resolve endpoint \"%s\": %s (retry %s)", + nm_wireguard_peer_get_public_key (peer_data->peer), + nm_wireguard_peer_get_endpoint (peer_data->peer), + resolv_error->message, + _retry_in_msec_to_string (retry_in_msec, s_retry)); + + _peers_resolve_retry_reschedule_for_peer (self, peer_data, retry_in_msec); + return; + } + + sockaddr = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; + + if (!resolv_error) { + GList *iter; + + for (iter = list; iter; iter = iter->next) { + GInetAddress *a = iter->data; + GSocketFamily f = g_inet_address_get_family (a); + + if (f == G_SOCKET_FAMILY_IPV4) { + nm_assert (g_inet_address_get_native_size (a) == sizeof (struct in_addr)); + sockaddr.in = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_port = htons (nm_sock_addr_endpoint_get_port (_nm_wireguard_peer_get_endpoint (peer_data->peer))), + }; + memcpy (&sockaddr.in.sin_addr, g_inet_address_to_bytes (a), sizeof (struct in_addr)); + break; + } + if (f == G_SOCKET_FAMILY_IPV6) { + nm_assert (g_inet_address_get_native_size (a) == sizeof (struct in6_addr)); + sockaddr.in6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_port = htons (nm_sock_addr_endpoint_get_port (_nm_wireguard_peer_get_endpoint (peer_data->peer))), + .sin6_scope_id = 0, + .sin6_flowinfo = 0, + }; + memcpy (&sockaddr.in6.sin6_addr, g_inet_address_to_bytes (a), sizeof (struct in6_addr)); + break; + } + } + + g_list_free_full (list, g_object_unref); + } + + if (sockaddr.sa.sa_family == AF_UNSPEC) { + /* we failed to resolve the name. There is no need to reset the previous + * sockaddr. Either it was already AF_UNSPEC, or we had a good name + * from resolving before. In that case, we don't want to throw away + * a possibly good IP address, since WireGuard supports automatic roaming + * anyway. Either the IP address is still good (and we would wrongly + * reject it), or it isn't -- in which case it does not hurt much. */ + } else { + if (nm_sock_addr_union_cmp (&peer_data->ep_resolv.sockaddr, &sockaddr) != 0) + changed = TRUE; + peer_data->ep_resolv.sockaddr = sockaddr; + } + + if ( resolv_error + || peer_data->ep_resolv.sockaddr.sa.sa_family == AF_UNSPEC) { + /* while it technically did not fail, something is probably odd. Retry frequently to + * resolve the name, like we would do for normal failures. */ + retry_in_msec = _peers_retry_in_msec (peer_data, TRUE); + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: no %sresults for endpoint \"%s\" (retry %s)", + nm_wireguard_peer_get_public_key (peer_data->peer), + resolv_error ? "" : "suitable ", + nm_wireguard_peer_get_endpoint (peer_data->peer), + _retry_in_msec_to_string (retry_in_msec, s_retry)); + } else { + retry_in_msec = _peers_retry_in_msec (peer_data, FALSE); + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: endpoint \"%s\" resolved to %s (retry %s)", + nm_wireguard_peer_get_public_key (peer_data->peer), + nm_wireguard_peer_get_endpoint (peer_data->peer), + nm_sock_addr_union_to_string (&peer_data->ep_resolv.sockaddr, s_sockaddr, sizeof (s_sockaddr)), + _retry_in_msec_to_string (retry_in_msec, s_retry)); + } + + _peers_resolve_retry_reschedule_for_peer (self, peer_data, retry_in_msec); + + if (changed) { + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + /* schedule the job in the background, to give multiple resolve events time + * to complete. */ + nm_clear_g_source (&priv->link_config_delayed_id); + priv->link_config_delayed_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, + link_config_delayed_resolver_cb, + self, + NULL); + } +} + +static void +_peers_resolve_start (NMDeviceWireGuard *self, + PeerData *peer_data) +{ + gs_unref_object GResolver *resolver = NULL; + const char *host; + + resolver = g_resolver_get_default (); + + nm_assert (!peer_data->ep_resolv.cancellable); + + peer_data->ep_resolv.cancellable = g_cancellable_new (); + + /* set a special next-try timestamp. It is positive, and indicates + * that we are in the process of trying. + * This timestamp however already lies in the past, but that is correct, + * because we are currently in the process of trying. We will determine + * a next-try timestamp once the try completes. */ + peer_data->ep_resolv.next_try_at_nsec = NEXT_TRY_AT_NSEC_PAST; + + host = nm_sock_addr_endpoint_get_host (_nm_wireguard_peer_get_endpoint (peer_data->peer)); + + g_resolver_lookup_by_name_async (resolver, + host, + peer_data->ep_resolv.cancellable, + _peers_resolve_cb, + peer_data); + + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: resolving name \"%s\" for endpoint \"%s\"...", + nm_wireguard_peer_get_public_key (peer_data->peer), + host, + nm_wireguard_peer_get_endpoint (peer_data->peer)); +} + +static void +_peers_resolve_reresolve_all (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + PeerData *peer_data; + + c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) { + if (peer_data->ep_resolv.cancellable) { + /* remember to retry when the currently ongoing request completes. */ + peer_data->ep_resolv.next_try_at_nsec = NEXT_TRY_AT_NSEC_ASAP; + } else if (peer_data->ep_resolv.next_try_at_nsec <= 0) { + /* this peer does not require resolving the name. Skip it. */ + } else { + /* we have a next-try scheduled. Restart right away. */ + peer_data->ep_resolv.resolv_fail_count = 0; + _peers_resolve_start (self, peer_data); + } + } +} + +static gboolean +_peers_update (NMDeviceWireGuard *self, + PeerData *peer_data, + NMWireGuardPeer *peer, + gboolean force_update) +{ + nm_auto_unref_wgpeer NMWireGuardPeer *old_peer = NULL; + NMSockAddrEndpoint *old_endpoint; + NMSockAddrEndpoint *endpoint; + gboolean endpoint_changed = FALSE; + gboolean changed; + NMSockAddrUnion sockaddr; + gboolean sockaddr_fixed; + char sockaddr_sbuf[100]; + + nm_assert (peer); + nm_assert (nm_wireguard_peer_is_sealed (peer)); + + if ( peer == peer_data->peer + && !force_update) + return FALSE; + + changed = (nm_wireguard_peer_cmp (peer, + peer_data->peer, + NM_SETTING_COMPARE_FLAG_EXACT) != 0); + + old_peer = peer_data->peer; + peer_data->peer = nm_wireguard_peer_ref (peer); + + old_endpoint = old_peer ? _nm_wireguard_peer_get_endpoint (old_peer) : NULL; + endpoint = peer ? _nm_wireguard_peer_get_endpoint (peer) : NULL; + + endpoint_changed = ( endpoint != old_endpoint + && ( !old_endpoint + || !endpoint + || !nm_streq (nm_sock_addr_endpoint_get_endpoint (old_endpoint), + nm_sock_addr_endpoint_get_endpoint (endpoint)))); + + if ( !force_update + && !endpoint_changed) { + /* nothing to do. */ + return changed; + } + + sockaddr = (NMSockAddrUnion) NM_SOCK_ADDR_UNION_INIT_UNSPEC; + sockaddr_fixed = TRUE; + if ( endpoint + && nm_sock_addr_endpoint_get_host (endpoint)) { + if (!nm_sock_addr_endpoint_get_fixed_sockaddr (endpoint, &sockaddr)) { + /* we have an endpoint, but it's not a static IP address. We need to resolve + * the names. */ + sockaddr_fixed = FALSE; + } + } + + if (nm_sock_addr_union_cmp (&peer_data->ep_resolv.sockaddr, &sockaddr) != 0) + changed = TRUE; + + nm_clear_g_cancellable (&peer_data->ep_resolv.cancellable); + + peer_data->ep_resolv = (PeerEndpointResolveData) { + .sockaddr = sockaddr, + .resolv_fail_count = 0, + .cancellable = NULL, + .next_try_at_nsec = 0, + }; + + if (!endpoint) { + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: no endpoint configured", + nm_wireguard_peer_get_public_key (peer_data->peer)); + } else if (!nm_sock_addr_endpoint_get_host (endpoint)) { + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: invalid endpoint \"%s\"", + nm_wireguard_peer_get_public_key (peer_data->peer), + nm_sock_addr_endpoint_get_endpoint (endpoint)); + } else if (sockaddr_fixed) { + _LOGT (LOGD_DEVICE, "wireguard-peer[%s]: fixed endpoint \"%s\" (%s)", + nm_wireguard_peer_get_public_key (peer_data->peer), + nm_sock_addr_endpoint_get_endpoint (endpoint), + nm_sock_addr_union_to_string (&peer_data->ep_resolv.sockaddr, sockaddr_sbuf, sizeof (sockaddr_sbuf))); + } else + _peers_resolve_start (self, peer_data); + + return changed; +} + +static void +_peers_remove_all (NMDeviceWireGuardPrivate *priv) { - return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, - self->props.public_key, sizeof (self->props.public_key), 1); + PeerData *peer_data; + + while ((peer_data = c_list_first_entry (&priv->lst_peers_head, PeerData, lst_peers))) + _peers_remove (priv, peer_data); } static void +_peers_update_all (NMDeviceWireGuard *self, + NMSettingWireGuard *s_wg, + gboolean *out_peers_removed) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + PeerData *peer_data_safe; + PeerData *peer_data; + guint i, n; + gboolean peers_removed = FALSE; + + c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) + peer_data->dirty_update_all = TRUE; + + n = nm_setting_wireguard_get_peers_len (s_wg); + for (i = 0; i < n; i++) { + NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i); + gboolean added = FALSE; + + peer_data = _peers_find (priv, peer); + if (!peer_data) { + peer_data = _peers_add (self, peer); + added = TRUE; + } + _peers_update (self, peer_data, peer, added); + peer_data->dirty_update_all = FALSE; + } + + c_list_for_each_entry_safe (peer_data, peer_data_safe, &priv->lst_peers_head, lst_peers) { + if (peer_data->dirty_update_all) { + _peers_remove (priv, peer_data); + peers_removed = TRUE; + } + } + + NM_SET_OUT (out_peers_removed, peers_removed); +} + +static void +_peers_get_platform_list (NMDeviceWireGuardPrivate *priv, + LinkConfigMode config_mode, + NMPWireGuardPeer **out_peers, + NMPlatformWireGuardChangePeerFlags **out_peer_flags, + guint *out_len, + GArray **out_allowed_ips_data) +{ + gs_free NMPWireGuardPeer *plpeers = NULL; + gs_free NMPlatformWireGuardChangePeerFlags *plpeer_flags = NULL; + gs_unref_array GArray *allowed_ips = NULL; + PeerData *peer_data; + guint i_good; + guint n_aip; + guint i_aip; + guint len; + guint i; + + nm_assert (out_peers && !*out_peers); + nm_assert (out_peer_flags && !*out_peer_flags); + nm_assert (out_len && *out_len == 0); + nm_assert (out_allowed_ips_data && !*out_allowed_ips_data); + + len = g_hash_table_size (priv->peers); + + nm_assert (len == c_list_length (&priv->lst_peers_head)); + + if (len == 0) + return; + + plpeers = g_new0 (NMPWireGuardPeer, len); + plpeer_flags = g_new0 (NMPlatformWireGuardChangePeerFlags, len); + + i_good = 0; + c_list_for_each_entry (peer_data, &priv->lst_peers_head, lst_peers) { + NMPlatformWireGuardChangePeerFlags *plf = &plpeer_flags[i_good]; + NMPWireGuardPeer *plp = &plpeers[i_good]; + NMSettingSecretFlags psk_secret_flags; + + if (!nm_utils_base64secret_decode (nm_wireguard_peer_get_public_key (peer_data->peer), + sizeof (plp->public_key), + plp->public_key)) + continue; + + *plf = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_NONE; + + plp->persistent_keepalive_interval = nm_wireguard_peer_get_persistent_keepalive (peer_data->peer); + if (NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL, + LINK_CONFIG_MODE_REAPPLY)) + *plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_KEEPALIVE_INTERVAL; + + /* if the peer has an endpoint but it is not yet resolved (not ready), + * we still configure it and leave the endpoint unspecified. Later, + * when we can resolve the endpoint, we will update. */ + plp->endpoint = peer_data->ep_resolv.sockaddr; + if (plp->endpoint.sa.sa_family == AF_UNSPEC) { + /* we don't actually ever clear endpoints, if we don't have better information. */ + } else + *plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ENDPOINT; + + if (NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL, + LINK_CONFIG_MODE_REAPPLY)) { + psk_secret_flags = nm_wireguard_peer_get_preshared_key_flags (peer_data->peer); + if (!NM_FLAGS_HAS (psk_secret_flags, NM_SETTING_SECRET_FLAG_NOT_REQUIRED)) { + if ( !nm_utils_base64secret_decode (nm_wireguard_peer_get_preshared_key (peer_data->peer), + sizeof (plp->preshared_key), + plp->preshared_key) + && config_mode == LINK_CONFIG_MODE_FULL) + goto skip; + } + *plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_PRESHARED_KEY; + } + + if ( NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL, + LINK_CONFIG_MODE_REAPPLY) + && ((n_aip = nm_wireguard_peer_get_allowed_ips_len (peer_data->peer)) > 0)) { + if (!allowed_ips) + allowed_ips = g_array_new (FALSE, FALSE, sizeof (NMPWireGuardAllowedIP)); + + *plf |= NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_HAS_ALLOWEDIPS + | NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_REPLACE_ALLOWEDIPS; + + plp->_construct_idx_start = allowed_ips->len; + for (i_aip = 0; i_aip < n_aip; i_aip++) { + const char *aip; + NMIPAddr addrbin = { }; + int addr_family; + gboolean valid; + int prefix; + + aip = nm_wireguard_peer_get_allowed_ip (peer_data->peer, i_aip, &valid); + if ( !valid + || !nm_utils_parse_inaddr_prefix_bin (AF_UNSPEC, + aip, + &addr_family, + &addrbin, + &prefix)) { + /* the address is really not expected to be invalid, because then + * the connection would not verify. Anyway, silently skip it. */ + continue; + } + + if (prefix == -1) + prefix = addr_family == AF_INET ? 32 : 128; + + g_array_append_val (allowed_ips, + ((NMPWireGuardAllowedIP) { + .family = addr_family, + .mask = prefix, + .addr = addrbin, + })); + } + plp->_construct_idx_end = allowed_ips->len; + } + + i_good++; + continue; + +skip: + memset (plp, 0, sizeof (*plp)); + } + + if (i_good == 0) + return; + + for (i = 0; i < i_good; i++) { + NMPWireGuardPeer *plp = &plpeers[i]; + guint l; + + if (plp->_construct_idx_end == 0) { + nm_assert (plp->_construct_idx_start == 0); + plp->allowed_ips = NULL; + plp->allowed_ips_len = 0; + } else { + nm_assert (plp->_construct_idx_start < plp->_construct_idx_end); + l = plp->_construct_idx_end - plp->_construct_idx_start; + plp->allowed_ips = &g_array_index (allowed_ips, NMPWireGuardAllowedIP, plp->_construct_idx_start); + plp->allowed_ips_len = l; + } + } + *out_peers = g_steal_pointer (&plpeers); + *out_peer_flags = g_steal_pointer (&plpeer_flags);; + *out_len = i_good; + *out_allowed_ips_data = g_steal_pointer (&allowed_ips); +} + +/*****************************************************************************/ + +static void update_properties (NMDevice *device) { NMDeviceWireGuard *self; + NMDeviceWireGuardPrivate *priv; const NMPlatformLink *plink; const NMPlatformLnkWireGuard *props = NULL; int ifindex; g_return_if_fail (NM_IS_DEVICE_WIREGUARD (device)); self = NM_DEVICE_WIREGUARD (device); + priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); ifindex = nm_device_get_ifindex (device); props = nm_platform_link_get_lnk_wireguard (nm_device_get_platform (device), ifindex, &plink); @@ -78,16 +867,16 @@ update_properties (NMDevice *device) #define CHECK_PROPERTY_CHANGED(field, prop) \ G_STMT_START { \ - if (self->props.field != props->field) { \ - self->props.field = props->field; \ + if (priv->lnk_curr.field != props->field) { \ + priv->lnk_curr.field = props->field; \ _notify (self, prop); \ } \ } G_STMT_END #define CHECK_PROPERTY_CHANGED_ARRAY(field, prop) \ G_STMT_START { \ - if (memcmp (&self->props.field, &props->field, sizeof (props->field)) != 0) { \ - memcpy (&self->props.field, &props->field, sizeof (props->field)); \ + if (memcmp (&priv->lnk_curr.field, &props->field, sizeof (priv->lnk_curr.field)) != 0) { \ + memcpy (&priv->lnk_curr.field, &props->field, sizeof (priv->lnk_curr.field)); \ _notify (self, prop); \ } \ } G_STMT_END @@ -107,24 +896,655 @@ link_changed (NMDevice *device, update_properties (device); } +static NMDeviceCapabilities +get_generic_capabilities (NMDevice *dev) +{ + return NM_DEVICE_CAP_IS_SOFTWARE; +} + +/*****************************************************************************/ + +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + const char *iface = nm_device_get_iface (device); + int r; + + g_return_val_if_fail (iface, FALSE); + + r = nm_platform_link_wireguard_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create WireGuard interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_strerror (r)); + return FALSE; + } + + return TRUE; +} + +/*****************************************************************************/ + +static void +_secrets_cancel (NMDeviceWireGuard *self) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + if (priv->secrets_call_id) + nm_act_request_cancel_secrets (NULL, priv->secrets_call_id); + nm_assert (!priv->secrets_call_id); +} + +static void +_secrets_cb (NMActRequest *req, + NMActRequestGetSecretsCallId *call_id, + NMSettingsConnection *connection, + GError *error, + gpointer user_data) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (user_data); + NMDevice *device = NM_DEVICE (self); + NMDeviceWireGuardPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE_WIREGUARD (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + g_return_if_fail (priv->secrets_call_id == call_id); + + priv->secrets_call_id = NULL; + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + g_return_if_fail (req == nm_device_get_act_request (device)); + g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); + g_return_if_fail (nm_act_request_get_settings_connection (req) == connection); + + if (error) { + _LOGW (LOGD_ETHER, "%s", error->message); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); + } else + nm_device_activate_schedule_stage1_device_prepare (device); +} + +static void +_secrets_get_secrets (NMDeviceWireGuard *self, + const char *setting_name, + NMSecretAgentGetSecretsFlags flags, + const char *const*hints) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + NMActRequest *req; + + _secrets_cancel (self); + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + priv->secrets_call_id = nm_act_request_get_secrets (req, + TRUE, + setting_name, + flags, + hints, + _secrets_cb, + self); + g_return_if_fail (priv->secrets_call_id); +} + +static NMActStageReturn +_secrets_handle_auth_or_fail (NMDeviceWireGuard *self, + NMActRequest *req, + gboolean new_secrets) +{ + NMConnection *applied_connection; + const char *setting_name; + gs_unref_ptrarray GPtrArray *hints = NULL; + + if (!nm_device_auth_retries_try_next (NM_DEVICE (self))) + return NM_ACT_STAGE_RETURN_FAILURE; + + nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + + nm_active_connection_clear_secrets (NM_ACTIVE_CONNECTION (req)); + + applied_connection = nm_act_request_get_applied_connection (req); + setting_name = nm_connection_need_secrets (applied_connection, &hints); + if (!setting_name) { + _LOGI (LOGD_DEVICE, "Cleared secrets, but setting didn't need any secrets."); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + if (hints) + g_ptr_array_add (hints, NULL); + + _secrets_get_secrets (self, + setting_name, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION + | (new_secrets ? NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW : 0), + ( hints + ? (const char *const*) hints->pdata + : NULL)); + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static void +_dns_config_changed (NMDnsManager *dns_manager, NMDeviceWireGuard *self) +{ + /* when the DNS configuration changes, we re-resolve the peer addresses. + * + * Possibly, we should also do that when the default-route changes, but it's + * hard to figure out when that happens. */ + _peers_resolve_reresolve_all (self); +} + +/*****************************************************************************/ + +static NMActStageReturn +link_config (NMDeviceWireGuard *self, + const char *reason, + LinkConfigMode config_mode, + NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + nm_auto_bzero_secret_ptr NMSecretPtr wg_lnk_clear_private_key = NM_SECRET_PTR_INIT (); + NMSettingWireGuard *s_wg; + NMConnection *connection; + NMActStageReturn ret; + gs_unref_array GArray *allowed_ips_data = NULL; + NMPlatformLnkWireGuard wg_lnk; + gs_free NMPWireGuardPeer *plpeers = NULL; + gs_free NMPlatformWireGuardChangePeerFlags *plpeer_flags = NULL; + guint plpeers_len = 0; + const char *setting_name; + gboolean peers_removed; + NMPlatformWireGuardChangeFlags wg_change_flags; + int ifindex; + int r; + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NONE); + + connection = nm_device_get_applied_connection (NM_DEVICE (self)); + s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD)); + g_return_val_if_fail (s_wg, NM_ACT_STAGE_RETURN_FAILURE); + + priv->link_config_last_at = nm_utils_get_monotonic_timestamp_ns (); + + _LOGT (LOGD_DEVICE, "wireguard link config (%s, %s)...", + reason, _link_config_mode_to_string (config_mode)); + + if (!priv->dns_manager) { + priv->dns_manager = g_object_ref (nm_dns_manager_get ()); + g_signal_connect (priv->dns_manager, NM_DNS_MANAGER_CONFIG_CHANGED, G_CALLBACK (_dns_config_changed), self); + } + + if ( NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL) + && (setting_name = nm_connection_need_secrets (connection, NULL))) { + NMActRequest *req = nm_device_get_act_request (NM_DEVICE (self)); + + _LOGD (LOGD_DEVICE, + "Activation: connection '%s' has security, but secrets are required.", + nm_connection_get_id (connection)); + + ret = _secrets_handle_auth_or_fail (self, req, FALSE); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) { + if (ret != NM_ACT_STAGE_RETURN_POSTPONE) { + nm_assert (ret == NM_ACT_STAGE_RETURN_FAILURE); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + } + return ret; + } + } + + ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self)); + if (ifindex <= 0) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + _peers_update_all (self, s_wg, &peers_removed); + + wg_lnk = (NMPlatformLnkWireGuard) { }; -/******************************************************************/ + wg_change_flags = NM_PLATFORM_WIREGUARD_CHANGE_FLAG_NONE; + + if ( NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL) + || ( NM_IN_SET (config_mode, LINK_CONFIG_MODE_REAPPLY) + && peers_removed)) + wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_REPLACE_PEERS; + + if (NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL, + LINK_CONFIG_MODE_REAPPLY)) { + + wg_lnk.listen_port = nm_setting_wireguard_get_listen_port (s_wg), + wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_LISTEN_PORT; + + wg_lnk.fwmark = nm_setting_wireguard_get_fwmark (s_wg), + wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_FWMARK; + + if (nm_utils_base64secret_decode (nm_setting_wireguard_get_private_key (s_wg), + sizeof (wg_lnk.private_key), + wg_lnk.private_key)) { + wg_lnk_clear_private_key = NM_SECRET_PTR_ARRAY (wg_lnk.private_key); + wg_change_flags |= NM_PLATFORM_WIREGUARD_CHANGE_FLAG_HAS_PRIVATE_KEY; + } else { + if (NM_IN_SET (config_mode, LINK_CONFIG_MODE_FULL)) { + _LOGD (LOGD_DEVICE, "the provided private-key is invalid"); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + return NM_ACT_STAGE_RETURN_FAILURE; + } + } + } + + _peers_get_platform_list (priv, + config_mode, + &plpeers, + &plpeer_flags, + &plpeers_len, + &allowed_ips_data); + + r = nm_platform_link_wireguard_change (nm_device_get_platform (NM_DEVICE (self)), + ifindex, + &wg_lnk, + plpeers, + plpeer_flags, + plpeers_len, + wg_change_flags); + + nm_explicit_bzero (plpeers, sizeof (plpeers) * plpeers_len); + + if (r < 0) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +link_config_delayed (NMDeviceWireGuard *self, + const char *reason) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + gint64 now; + + priv->link_config_delayed_id = 0; + + if (priv->link_config_last_at != 0) { + now = nm_utils_get_monotonic_timestamp_ns (); + if (now < priv->link_config_last_at + LINK_CONFIG_RATE_LIMIT_NSEC) { + /* we ratelimit calls to link_config(), because we call this whenever a resolver + * completes. */ + _LOGT (LOGD_DEVICE, "wireguard link config (%s) (postponed)", reason); + priv->link_config_delayed_id = g_timeout_add (NM_MAX ((priv->link_config_last_at + LINK_CONFIG_RATE_LIMIT_NSEC - now) / NM_UTILS_NS_PER_MSEC, + (gint64) 1), + link_config_delayed_ratelimit_cb, + self); + return; + } + } + + link_config (self, reason, LINK_CONFIG_MODE_ENDPOINTS, NULL); +} + +static gboolean +link_config_delayed_ratelimit_cb (gpointer user_data) +{ + link_config_delayed (user_data, "after-ratelimiting"); + return G_SOURCE_REMOVE; +} + +static gboolean +link_config_delayed_resolver_cb (gpointer user_data) +{ + link_config_delayed (user_data, "resolver-update"); + return G_SOURCE_REMOVE; +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, + NMDeviceStateReason *out_failure_reason) +{ + NMDeviceSysIfaceState sys_iface_state; + NMDeviceStateReason failure_reason; + NMActStageReturn ret; + + sys_iface_state = nm_device_sys_iface_state_get (device); + + if (sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_EXTERNAL) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NONE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + ret = link_config (NM_DEVICE_WIREGUARD (device), + "configure", + (sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_ASSUME) + ? LINK_CONFIG_MODE_ASSUME + : LINK_CONFIG_MODE_FULL, + &failure_reason); + + if (sys_iface_state == NM_DEVICE_SYS_IFACE_STATE_ASSUME) { + /* this never fails. */ + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NONE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + if (ret != NM_ACT_STAGE_RETURN_FAILURE) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NONE); + return ret; + } + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + failure_reason); + NM_SET_OUT (out_failure_reason, failure_reason); + return NM_ACT_STAGE_RETURN_FAILURE; +} + +static NMIPConfig * +_get_dev2_ip_config (NMDeviceWireGuard *self, + int addr_family) +{ + gs_unref_object NMIPConfig *ip_config = NULL; + NMConnection *connection; + NMSettingWireGuard *s_wg; + guint n_peers; + guint i; + int ip_ifindex; + guint32 route_metric; + guint32 route_table_coerced; + + connection = nm_device_get_applied_connection (NM_DEVICE (self)); + + s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD)); + + /* Differences to `wg-quick`. + * + * `wg-quick` supports the "Table" setting with 3 modes: + * + * a1) "off": this is what we do with "peer-routes" disabled. + * + * a2) an explicit routing table. This is our behavior with "peer-routes" on. In this case + * we honor the "ipv4.route-table" and "ipv6.route-table" settings. One difference is that + * `wg-quick` would resolve table names from /etc/iproute2/rt_tables. Our connection profiles + * only contain table numbers, so that conversion from name to table must have happened + * before already. + * + * a3) "auto" (the default). In this case, `wg-quick` would only add the route to the + * main table, if the AllowedIP range is not yet reachable on the link. With "peer-routes" + * enabled, we don't check for that and always add the routes to the main-table + * (with 'ipv4.route-table' and 'ipv6.route-table' set to zero or RT_TABLE_MAIN (254)). + * + * Also, in "auto" mode, `wg-quick` would add special handling for /0 routes and pick + * an empty table to configure policy routing to avoid routing loops. This handling + * of routing-loops via policy routing is not yet done, and requires a separate solution + * from constructing the peer-routes here. + */ + if (!nm_setting_wireguard_get_peer_routes (s_wg)) + return NULL; + + ip_ifindex = nm_device_get_ip_ifindex (NM_DEVICE (self)); + + if (ip_ifindex <= 0) + return NULL; + + route_metric = nm_device_get_route_metric (NM_DEVICE (self), addr_family); + + route_table_coerced = nm_platform_route_table_coerce (nm_device_get_route_table (NM_DEVICE (self), addr_family, TRUE)); + + n_peers = nm_setting_wireguard_get_peers_len (s_wg); + for (i = 0; i < n_peers; i++) { + NMWireGuardPeer *peer = nm_setting_wireguard_get_peer (s_wg, i); + guint n_aips; + guint j; + + n_aips = nm_wireguard_peer_get_allowed_ips_len (peer); + for (j = 0; j < n_aips; j++) { + NMPlatformIPXRoute rt; + NMIPAddr addrbin; + const char *aip; + gboolean valid; + int prefix; + + aip = nm_wireguard_peer_get_allowed_ip (peer, j, &valid); + + if ( !valid + || !nm_utils_parse_inaddr_prefix_bin (addr_family, + aip, + NULL, + &addrbin, + &prefix)) + continue; + + if (prefix < 0) + prefix = (addr_family == AF_INET) ? 32 : 128; + + if (!ip_config) + ip_config = nm_device_ip_config_new (NM_DEVICE (self), addr_family); + + nm_utils_ipx_address_clear_host_address (addr_family, &addrbin, NULL, prefix); + + if (addr_family == AF_INET) { + rt.r4 = (NMPlatformIP4Route) { + .network = addrbin.addr4, + .plen = prefix, + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .table_coerced = route_table_coerced, + .metric = route_metric, + }; + } else { + rt.r6 = (NMPlatformIP6Route) { + .network = addrbin.addr6, + .plen = prefix, + .ifindex = ip_ifindex, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .table_coerced = route_table_coerced, + .metric = route_metric, + }; + } + + nm_ip_config_add_route (ip_config, &rt.rx, NULL); + } + } + + return g_steal_pointer (&ip_config); +} + +static NMActStageReturn +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) +{ + gs_unref_object NMIPConfig *ip_config = NULL; + + ip_config = _get_dev2_ip_config (NM_DEVICE_WIREGUARD (device), addr_family); + + nm_device_set_dev2_ip_config (device, addr_family, ip_config); + + return NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); +} + +static guint32 +get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source) +{ + /* When "MTU" for `wg-quick up` is unset, it calls `ip route get` for + * each configured endpoint, to determine the suitable MTU how to reach + * each endpoint. + * For `wg-quick` this works very well, because whenever the script runs it + * determines the best setting at that point in time. It's simply not concerned + * with what happens later (and it's not around anyway). + * + * NetworkManager sticks around, so the right MTU would need to be re-determined + * whenever anything relevant changes. Which basically means, to re-evaluate whenever + * something related to addresses or routing changes (which happens all the time). + * + * The correct MTU indeed depends on the MTU setting of other interfaces (or routes). + * But it's still odd, that activating/deactivating a seemingly unrelated interface + * would trigger an MTU change. It's odd to explain/document and odd to implemented + * -- despite this being the reality. + * + * For now, only support configuring an explicit MTU, or leave the setting untouched. + * The same limitation also applies to other "ip-tunnel" types, where we could use + * similar smarts for autodetecting the MTU. + */ + return nm_device_get_configured_mtu_from_connection (device, + NM_TYPE_SETTING_WIREGUARD, + out_source); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + NMDeviceWireGuardPrivate *priv; + + if (new_state <= NM_DEVICE_STATE_ACTIVATED) + return; + + priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device); + + _peers_remove_all (priv); + _secrets_cancel (NM_DEVICE_WIREGUARD (device)); +} + +/*****************************************************************************/ + +static gboolean +can_reapply_change (NMDevice *device, + const char *setting_name, + NMSetting *s_old, + NMSetting *s_new, + GHashTable *diffs, + GError **error) +{ + if (nm_streq (setting_name, NM_SETTING_WIREGUARD_SETTING_NAME)) { + /* Most, but not all WireGuard settings can be reapplied. Whitelist. + * + * MTU cannot be reapplied. */ + return nm_device_hash_check_invalid_keys (diffs, + NM_SETTING_WIREGUARD_SETTING_NAME, + error, + NM_SETTING_WIREGUARD_FWMARK, + NM_SETTING_WIREGUARD_LISTEN_PORT, + NM_SETTING_WIREGUARD_PEERS, + NM_SETTING_WIREGUARD_PEER_ROUTES, + NM_SETTING_WIREGUARD_PRIVATE_KEY, + NM_SETTING_WIREGUARD_PRIVATE_KEY_FLAGS); + } + + return NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->can_reapply_change (device, + setting_name, + s_old, + s_new, + diffs, + error); +} + +static void +reapply_connection (NMDevice *device, + NMConnection *con_old, + NMConnection *con_new) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (device); + gs_unref_object NMIPConfig *ip4_config = NULL; + gs_unref_object NMIPConfig *ip6_config = NULL; + + ip4_config = _get_dev2_ip_config (self, AF_INET); + ip6_config = _get_dev2_ip_config (self, AF_INET6); + + nm_device_set_dev2_ip_config (device, AF_INET, ip4_config); + nm_device_set_dev2_ip_config (device, AF_INET6, ip6_config); + + NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->reapply_connection (device, + con_old, + con_new); + + link_config (NM_DEVICE_WIREGUARD (device), + "reapply", + LINK_CONFIG_MODE_REAPPLY, + NULL); +} + +/*****************************************************************************/ + +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device); + NMSettingWireGuard *s_wg = NM_SETTING_WIREGUARD (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD)); + const NMPObject *obj_wg; + const NMPObjectLnkWireGuard *olnk_wg; + guint i; + + if (!s_wg) { + s_wg = NM_SETTING_WIREGUARD (nm_setting_wireguard_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wg)); + } + + g_object_set (s_wg, + NM_SETTING_WIREGUARD_FWMARK, + (guint) priv->lnk_curr.fwmark, + NM_SETTING_WIREGUARD_LISTEN_PORT, + (guint) priv->lnk_curr.listen_port, + NULL); + + obj_wg = NMP_OBJECT_UP_CAST (nm_platform_link_get_lnk_wireguard (nm_device_get_platform (device), + nm_device_get_ip_ifindex (device), + NULL)); + if (!obj_wg) + return; + + olnk_wg = &obj_wg->_lnk_wireguard; + + for (i = 0; i < olnk_wg->peers_len; i++) { + nm_auto_unref_wgpeer NMWireGuardPeer *peer = NULL; + const NMPWireGuardPeer *ppeer = &olnk_wg->peers[i]; + + peer = nm_wireguard_peer_new (); + + _nm_wireguard_peer_set_public_key_bin (peer, ppeer->public_key); + + nm_setting_wireguard_append_peer (s_wg, peer); + } +} + +/*****************************************************************************/ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); switch (prop_id) { case PROP_PUBLIC_KEY: - g_value_take_variant (value, get_public_key_as_variant (self)); + g_value_take_variant (value, + g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, + priv->lnk_curr.public_key, + sizeof (priv->lnk_curr.public_key), + 1)); break; case PROP_LISTEN_PORT: - g_value_set_uint (value, self->props.listen_port); + g_value_set_uint (value, priv->lnk_curr.listen_port); break; case PROP_FWMARK: - g_value_set_uint (value, self->props.fwmark); + g_value_set_uint (value, priv->lnk_curr.fwmark); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -132,9 +1552,44 @@ get_property (GObject *object, guint prop_id, } } +/*****************************************************************************/ + static void nm_device_wireguard_init (NMDeviceWireGuard *self) { + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + c_list_init (&priv->lst_peers_head); + priv->peers = g_hash_table_new (_peer_data_hash, _peer_data_equal); +} + +static void +dispose (GObject *object) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + _secrets_cancel (self); + + _peers_remove_all (priv); + + G_OBJECT_CLASS (nm_device_wireguard_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceWireGuard *self = NM_DEVICE_WIREGUARD (object); + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); + + nm_explicit_bzero (priv->lnk_curr.private_key, sizeof (priv->lnk_curr.private_key)); + + if (priv->dns_manager) { + g_signal_handlers_disconnect_by_func (priv->dns_manager, _dns_config_changed, self); + g_object_unref (priv->dns_manager); + } + + G_OBJECT_CLASS (nm_device_wireguard_parent_class)->finalize (object); } static const NMDBusInterfaceInfoExtended interface_info_device_wireguard = { @@ -156,13 +1611,26 @@ nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass) NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wireguard); - device_class->connection_type_supported = NULL; + device_class->connection_type_supported = NM_SETTING_WIREGUARD_SETTING_NAME; + device_class->connection_type_check_compatible = NM_SETTING_WIREGUARD_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD); + device_class->state_changed = device_state_changed; + device_class->create_and_realize = create_and_realize; + device_class->act_stage2_config = act_stage2_config; + device_class->act_stage2_config_also_for_external_or_assume = TRUE; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->get_generic_capabilities = get_generic_capabilities; device_class->link_changed = link_changed; + device_class->update_connection = update_connection; + device_class->can_reapply_change = can_reapply_change; + device_class->reapply_connection = reapply_connection; + device_class->get_configured_mtu = get_configured_mtu; obj_properties[PROP_PUBLIC_KEY] = g_param_spec_variant (NM_DEVICE_WIREGUARD_PUBLIC_KEY, @@ -207,6 +1675,7 @@ create_device (NMDeviceFactory *factory, } NM_DEVICE_FACTORY_DEFINE_INTERNAL (WIREGUARD, WireGuard, wireguard, - NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD), + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_WIREGUARD_SETTING_NAME), factory_class->create_device = create_device; ) diff --git a/src/devices/nm-device-wpan.c b/src/devices/nm-device-wpan.c index 05b507d5..cdfd1f70 100644 --- a/src/devices/nm-device-wpan.c +++ b/src/devices/nm-device-wpan.c @@ -23,7 +23,6 @@ #include "nm-device-wpan.h" #include <stdlib.h> -#include <string.h> #include <sys/types.h> #include <linux/if.h> @@ -119,11 +118,11 @@ static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceWpan *self = NM_DEVICE_WPAN (device); - NMConnection *connection; NMSettingWpan *s_wpan; NMPlatform *platform; guint16 pan_id; guint16 short_address; + gint16 page, channel; int ifindex; const guint8 *hwaddr; gsize hwaddr_len = 0; @@ -139,12 +138,11 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) g_return_val_if_fail (platform, NM_ACT_STAGE_RETURN_FAILURE); ifindex = nm_device_get_ifindex (device); + g_return_val_if_fail (ifindex > 0, NM_ACT_STAGE_RETURN_FAILURE); - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_wpan = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WPAN); - s_wpan = NM_SETTING_WPAN (nm_connection_get_setting (connection, NM_TYPE_SETTING_WPAN)); g_return_val_if_fail (s_wpan, NM_ACT_STAGE_RETURN_FAILURE); hwaddr = nm_platform_link_get_address (platform, ifindex, &hwaddr_len); @@ -183,9 +181,18 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) } } + channel = nm_setting_wpan_get_channel (s_wpan); + if (channel != NM_SETTING_WPAN_CHANNEL_DEFAULT) { + page = nm_setting_wpan_get_page (s_wpan); + if (!nm_platform_wpan_set_channel (platform, ifindex, page, channel)) { + _LOGW (LOGD_DEVICE, "unable to set the channel"); + goto out; + } + } + ret = NM_ACT_STAGE_RETURN_SUCCESS; out: - nm_device_bring_up (device, TRUE, NULL); + nm_device_bring_up (device, TRUE, NULL); if (lowpan_device) nm_device_bring_up (lowpan_device, TRUE, NULL); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 89e1cc51..7514fa78 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -24,9 +24,7 @@ #include "nm-device.h" #include <netinet/in.h> -#include <string.h> #include <unistd.h> -#include <errno.h> #include <sys/ioctl.h> #include <signal.h> #include <sys/types.h> @@ -67,6 +65,7 @@ #include "settings/nm-settings.h" #include "nm-setting-ethtool.h" #include "nm-auth-utils.h" +#include "nm-keep-alive.h" #include "nm-netns.h" #include "nm-dispatcher.h" #include "nm-config.h" @@ -114,14 +113,6 @@ typedef enum { CLEANUP_TYPE_DECONFIGURE, } CleanupType; -typedef enum { - IP_NONE = 0, - IP_WAIT, - IP_CONF, - IP_DONE, - IP_FAIL -} IpState; - typedef struct { CList lst_slave; NMDevice *slave; @@ -174,6 +165,7 @@ struct _NMDeviceConnectivityHandle { bool is_periodic:1; bool is_periodic_bump:1; bool is_periodic_bump_on_complete:1; + int addr_family; }; typedef struct { @@ -195,7 +187,6 @@ enum { REMOVED, RECHECK_AUTO_ACTIVATE, RECHECK_ASSUME, - CONNECTIVITY_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -241,7 +232,8 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDevice, PROP_REFRESH_RATE_MS, PROP_TX_BYTES, PROP_RX_BYTES, - PROP_CONNECTIVITY, + PROP_IP4_CONNECTIVITY, + PROP_IP6_CONNECTIVITY, ); typedef struct _NMDevicePrivate { @@ -392,8 +384,11 @@ typedef struct _NMDevicePrivate { bool ipv6ll_handle:1; /* TRUE if NM handles the device's IPv6LL address */ bool ipv6ll_has:1; + bool ndisc_started:1; bool device_link_changed_down:1; + bool concheck_rp_filter_checked:1; + /* Generic DHCP stuff */ char * dhcp_anycast_address; @@ -413,11 +408,8 @@ typedef struct _NMDevicePrivate { NMIPConfig *ip_config_x[2]; }; - union { - const IpState ip4_state; - IpState ip4_state_; - }; - AppliedConfig dev_ip4_config; /* Config from DHCP, PPP, LLv4, etc */ + /* Config from DHCP, PPP, LLv4, etc */ + AppliedConfig dev_ip_config_4; /* config from the setting */ union { @@ -446,18 +438,16 @@ typedef struct _NMDevicePrivate { GSList *vpn_configs_x[2]; }; - /* WWAN configuration */ + /* Extra device configuration, injected by the subclass of NMDevice. + * This is used for example by NMDeviceModem for WWAN configuration. */ union { struct { - AppliedConfig wwan_ip_config_6; - AppliedConfig wwan_ip_config_4; + AppliedConfig dev2_ip_config_6; + AppliedConfig dev2_ip_config_4; }; - AppliedConfig wwan_ip_config_x[2]; + AppliedConfig dev2_ip_config_x[2]; }; - bool v4_has_shadowed_routes; - const char *ip4_rp_filter; - /* DHCPv4 tracking */ struct { NMDhcpClient * client; @@ -500,9 +490,16 @@ typedef struct _NMDevicePrivate { } acd; union { - const IpState ip6_state; - IpState ip6_state_; + struct { + const NMDeviceIPState ip_state_6; + const NMDeviceIPState ip_state_4; + }; + union { + const NMDeviceIPState ip_state_x[2]; + NMDeviceIPState ip_state_x_[2]; + }; }; + AppliedConfig ac_ip6_config; /* config from IPv6 autoconfiguration */ NMIP6Config * ext_ip6_config_captured; /* Configuration captured from platform. */ NMIP6Config * dad6_ip6_config; @@ -555,24 +552,24 @@ typedef struct _NMDevicePrivate { NMLldpListener *lldp_listener; NMConnectivity *concheck_mgr; + CList concheck_lst_head; + struct { + /* if periodic checks are enabled, this is the source id for the next check. */ + guint p_cur_id; - /* if periodic checks are enabled, this is the source id for the next check. */ - guint concheck_p_cur_id; - - /* the currently configured max periodic interval. */ - guint concheck_p_max_interval; - - /* the current interval. If we are probing, the interval might be lower - * then the configured max interval. */ - guint concheck_p_cur_interval; + /* the currently configured max periodic interval. */ + guint p_max_interval; - /* the timestamp, when we last scheduled the timer concheck_p_cur_id with current interval - * concheck_p_cur_interval. */ - gint64 concheck_p_cur_basetime_ns; + /* the current interval. If we are probing, the interval might be lower + * then the configured max interval. */ + guint p_cur_interval; - NMConnectivityState connectivity_state; + /* the timestamp, when we last scheduled the timer p_cur_id with current interval + * p_cur_interval. */ + gint64 p_cur_basetime_ns; - CList concheck_lst_head; + NMConnectivityState state; + } concheck_x[2]; guint check_delete_unrealized_id; @@ -643,7 +640,26 @@ static void _set_mtu (NMDevice *self, guint32 mtu); static void _commit_mtu (NMDevice *self, const NMIP4Config *config); static void _cancel_activation (NMDevice *self); -static void concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean is_periodic); +static void concheck_update_state (NMDevice *self, + int addr_family, + NMConnectivityState state, + gboolean is_periodic); + +static void activate_stage4_ip_config_timeout_4 (NMDevice *self); +static void activate_stage4_ip_config_timeout_6 (NMDevice *self); + +static void (*const activate_stage4_ip_config_timeout_x[2]) (NMDevice *self) = { + activate_stage4_ip_config_timeout_6, + activate_stage4_ip_config_timeout_4, +}; + +static void activate_stage5_ip_config_result_4 (NMDevice *self); +static void activate_stage5_ip_config_result_6 (NMDevice *self); + +static void (*const activate_stage5_ip_config_result_x[2]) (NMDevice *self) = { + activate_stage5_ip_config_result_6, + activate_stage5_ip_config_result_4, +}; /*****************************************************************************/ @@ -739,10 +755,10 @@ NM_UTILS_LOOKUP_STR_DEFINE (nm_device_state_reason_to_str, NMDeviceStateReason, NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE, "ip-address-duplicate"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED, "ip-method-unsupported"), NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_SRIOV_CONFIGURATION_FAILED, "sriov-configuration-failed"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_STATE_REASON_PEER_NOT_FOUND, "peer-not-found"), ); -#define reason_to_string(reason) \ - NM_UTILS_LOOKUP_STR (nm_device_state_reason_to_str, reason) +#define reason_to_string_a(reason) NM_UTILS_LOOKUP_STR_A (nm_device_state_reason_to_str, reason) NM_UTILS_LOOKUP_STR_DEFINE_STATIC (mtu_source_to_str, NMDeviceMtuSource, NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"), @@ -868,28 +884,28 @@ concheck_get_mgr (NMDevice *self) return priv->concheck_mgr; } -static NMIP4Config * -_ip4_config_new (NMDevice *self) +NMIP4Config * +nm_device_ip4_config_new (NMDevice *self) { return nm_ip4_config_new (nm_device_get_multi_index (self), nm_device_get_ip_ifindex (self)); } -static NMIP6Config * -_ip6_config_new (NMDevice *self) +NMIP6Config * +nm_device_ip6_config_new (NMDevice *self) { return nm_ip6_config_new (nm_device_get_multi_index (self), nm_device_get_ip_ifindex (self)); } -static NMIPConfig * -_ip_config_new (NMDevice *self, int addr_family) +NMIPConfig * +nm_device_ip_config_new (NMDevice *self, int addr_family) { nm_assert_addr_family (addr_family); return addr_family == AF_INET - ? (gpointer) _ip4_config_new (self) - : (gpointer) _ip6_config_new (self); + ? (gpointer) nm_device_ip4_config_new (self) + : (gpointer) nm_device_ip6_config_new (self); } static void @@ -902,6 +918,12 @@ applied_config_clear (AppliedConfig *config) static void applied_config_init (AppliedConfig *config, gpointer ip_config) { + nm_assert ( !ip_config + || (!config->orig && !config->current) + || nm_ip_config_get_addr_family (ip_config) == nm_ip_config_get_addr_family (config->orig ?: config->current)); + nm_assert ( !ip_config + || NM_IS_IP_CONFIG (ip_config, AF_UNSPEC)); + nm_g_object_ref (ip_config); applied_config_clear (config); config->orig = ip_config; @@ -910,7 +932,7 @@ applied_config_init (AppliedConfig *config, gpointer ip_config) static void applied_config_init_new (AppliedConfig *config, NMDevice *self, int addr_family) { - gs_unref_object NMIPConfig *c = _ip_config_new (self, addr_family); + gs_unref_object NMIPConfig *c = nm_device_ip_config_new (self, addr_family); applied_config_init (config, c); } @@ -1116,8 +1138,8 @@ init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config) int priority; property = (nm_ip_config_get_addr_family (config) == AF_INET) - ? "ipv4.dns-priority" - : "ipv6.dns-priority"; + ? NM_CON_DEFAULT ("ipv4.dns-priority") + : NM_CON_DEFAULT ("ipv6.dns-priority"); priority = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, @@ -1130,96 +1152,85 @@ init_ip_config_dns_priority (NMDevice *self, NMIPConfig *config) /*****************************************************************************/ -static gboolean -nm_device_ipv4_sysctl_set (NMDevice *self, const char *property, const char *value) +static char * +nm_device_sysctl_ip_conf_get (NMDevice *self, + int addr_family, + const char *property) { - NMPlatform *platform = nm_device_get_platform (self); - gs_free char *value_to_free = NULL; - const char *value_to_set; - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - if (!nm_device_get_ip_ifindex (self)) - return FALSE; + const char *ifname; - if (value) { - value_to_set = value; - } else { - /* Set to a default value when we've got a NULL @value. */ - value_to_free = nm_platform_sysctl_get (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, "default", property))); - value_to_set = value_to_free; - } + nm_assert_addr_family (addr_family); - return nm_platform_sysctl_set (platform, - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, buf, nm_device_get_ip_iface (self), property)), - value_to_set); + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) + return NULL; + return nm_platform_sysctl_ip_conf_get (nm_device_get_platform (self), addr_family, ifname, property); } -static guint32 -nm_device_ipv4_sysctl_get_effective_uint32 (NMDevice *self, const char *property, guint32 fallback) +static gint64 +nm_device_sysctl_ip_conf_get_int_checked (NMDevice *self, + int addr_family, + const char *property, + guint base, + gint64 min, + gint64 max, + gint64 fallback) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - gint64 v, v_all; + const char *ifname; - if (!nm_device_get_ip_ifindex (self)) - return fallback; + nm_assert_addr_family (addr_family); - /* for this kind of sysctl (e.g. "rp_filter"), kernel effectively uses the - * MAX of the per-device value and the "all" value. - * - * Also do that, by reading both sysctls and return the maximum. */ - - v = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, - buf, - nm_device_get_ip_iface (self), - property)), - 10, - 0, - G_MAXUINT32, - -1); - - v_all = nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET, - buf, - "all", - property)), - 10, - 0, - G_MAXUINT32, - -1); - - v = NM_MAX (v, v_all); - return v > -1 ? (guint32) v : fallback; + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) { + errno = EINVAL; + return fallback; + } + return nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), + addr_family, + ifname, + property, + base, + min, + max, + fallback); } gboolean -nm_device_ipv6_sysctl_set (NMDevice *self, const char *property, const char *value) +nm_device_sysctl_ip_conf_set (NMDevice *self, + int addr_family, + const char *property, + const char *value) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - if (!nm_device_get_ip_ifindex (self)) - return FALSE; + NMPlatform *platform = nm_device_get_platform (self); + gs_free char *value_to_free = NULL; + const char *ifname; - return nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), value); -} + nm_assert_addr_family (addr_family); -static guint32 -nm_device_ipv6_sysctl_get_uint32 (NMDevice *self, const char *property, guint32 fallback) -{ - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) + return FALSE; - if (!nm_device_get_ip_ifindex (self)) - return fallback; + if (!value) { + /* Set to a default value when we've got a NULL @value. */ + value_to_free = nm_platform_sysctl_ip_conf_get (platform, + addr_family, + "default", + property); + value = value_to_free; + if (!value) + return FALSE; + } - return nm_platform_sysctl_get_int_checked (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), property)), - 10, - 0, - G_MAXUINT32, - fallback); + return nm_platform_sysctl_ip_conf_set (platform, + addr_family, + ifname, + property, + value); } +/*****************************************************************************/ + gboolean nm_device_has_capability (NMDevice *self, NMDeviceCapabilities caps) { @@ -1275,7 +1286,7 @@ _get_stable_id (NMDevice *self, if (!stable_id) { default_id = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "connection.stable-id", + NM_CON_DEFAULT ("connection.stable-id"), self); stable_id = default_id; } @@ -1283,7 +1294,7 @@ _get_stable_id (NMDevice *self, uuid = nm_connection_get_uuid (connection); /* the cloned-mac-address may be generated based on the stable-id. - * Thus, at this point, we can only use the permanant MAC address + * Thus, at this point, we can only use the permanent MAC address * as seed. */ hwaddr = nm_device_get_permanent_hw_address_full (self, TRUE, &hwaddr_is_fake); @@ -1324,44 +1335,42 @@ _get_stable_id (NMDevice *self, /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_ip_state_to_string, IpState, +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_ip_state_to_string, NMDeviceIPState, NM_UTILS_LOOKUP_DEFAULT_WARN ("unknown"), - NM_UTILS_LOOKUP_STR_ITEM (IP_NONE, "none"), - NM_UTILS_LOOKUP_STR_ITEM (IP_WAIT, "wait"), - NM_UTILS_LOOKUP_STR_ITEM (IP_CONF, "conf"), - NM_UTILS_LOOKUP_STR_ITEM (IP_DONE, "done"), - NM_UTILS_LOOKUP_STR_ITEM (IP_FAIL, "fail"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_IP_STATE_NONE, "none"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_IP_STATE_WAIT, "wait"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_IP_STATE_CONF, "conf"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_IP_STATE_DONE, "done"), + NM_UTILS_LOOKUP_STR_ITEM (NM_DEVICE_IP_STATE_FAIL, "fail"), ); static void -_set_ip_state (NMDevice *self, int addr_family, IpState new_state) +_set_ip_state (NMDevice *self, int addr_family, NMDeviceIPState new_state) { - IpState *p; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); nm_assert_addr_family (addr_family); - p = (addr_family == AF_INET) - ? &priv->ip4_state_ - : &priv->ip6_state_; + if (priv->ip_state_x[IS_IPv4] == new_state) + return; - if (*p != new_state) { - _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", - nm_utils_addr_family_to_char (addr_family), - (int) new_state, - _ip_state_to_string (new_state)); - *p = new_state; + _LOGT (LOGD_DEVICE, "ip%c-state: set to %d (%s)", + nm_utils_addr_family_to_char (addr_family), + (int) new_state, + _ip_state_to_string (new_state)); - if (new_state == IP_DONE) { - /* we only set the IPx_READY flag once we reach IP_DONE state. We don't - * ever clear it, even if we later enter IP_FAIL state. - * - * This is not documented/guaranteed behavior, but seems to make sense for now. */ - _active_connection_set_state_flags (self, - addr_family == AF_INET - ? NM_ACTIVATION_STATE_FLAG_IP4_READY - : NM_ACTIVATION_STATE_FLAG_IP6_READY); - } + priv->ip_state_x_[IS_IPv4] = new_state; + + if (new_state == NM_DEVICE_IP_STATE_DONE) { + /* we only set the IPx_READY flag once we reach NM_DEVICE_IP_STATE_DONE state. We don't + * ever clear it, even if we later enter NM_DEVICE_IP_STATE_FAIL state. + * + * This is not documented/guaranteed behavior, but seems to make sense for now. */ + _active_connection_set_state_flags (self, + addr_family == AF_INET + ? NM_ACTIVATION_STATE_FLAG_IP4_READY + : NM_ACTIVATION_STATE_FLAG_IP6_READY); } } @@ -1473,6 +1482,18 @@ nm_device_get_ip_iface (NMDevice *self) return priv->ip_iface ?: priv->iface; } +const char * +nm_device_get_ip_iface_from_platform (NMDevice *self) +{ + int ifindex; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return NULL; + + return nm_platform_link_get_name (nm_device_get_platform (self), ifindex); +} + int nm_device_get_ip_ifindex (const NMDevice *self) { @@ -1957,9 +1978,10 @@ nm_device_get_ip_iface_identifier (NMDevice *self, NMUtilsIPv6IfaceId *iid, gboo g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); if (!ignore_token) { - s_ip6 = (NMSettingIP6Config *) - nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + g_return_val_if_fail (s_ip6, FALSE); + token = nm_setting_ip6_config_get_token (s_ip6); } if (token) @@ -2037,9 +2059,14 @@ nm_device_get_route_metric_default (NMDeviceType device_type) */ switch (device_type) { - /* 50 is also used for VPN plugins (NM_VPN_ROUTE_METRIC_DEFAULT) */ + + /* 50 is also used for VPN plugins (NM_VPN_ROUTE_METRIC_DEFAULT). + * + * Note that returning 50 from this function means that this device-type is + * in some aspects a VPN. */ case NM_DEVICE_TYPE_WIREGUARD: - return 50; + return NM_VPN_ROUTE_METRIC_DEFAULT; + case NM_DEVICE_TYPE_ETHERNET: case NM_DEVICE_TYPE_VETH: return 100; @@ -2087,6 +2114,7 @@ nm_device_get_route_metric_default (NMDeviceType device_type) return 800; case NM_DEVICE_TYPE_WPAN: return 850; + case NM_DEVICE_TYPE_WIFI_P2P: case NM_DEVICE_TYPE_GENERIC: return 950; case NM_DEVICE_TYPE_UNKNOWN: @@ -2100,13 +2128,14 @@ nm_device_get_route_metric_default (NMDeviceType device_type) } static gboolean -default_route_metric_penalty_detect (NMDevice *self) +default_route_metric_penalty_detect (NMDevice *self, int addr_family) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* currently we don't differentiate between IPv4 and IPv6 when detecting * connectivity. */ - if ( priv->connectivity_state != NM_CONNECTIVITY_FULL + if ( priv->concheck_x[IS_IPv4].state != NM_CONNECTIVITY_FULL && nm_connectivity_check_enabled (concheck_get_mgr (self))) return TRUE; @@ -2141,9 +2170,7 @@ nm_device_get_route_metric (NMDevice *self, connection = nm_device_get_applied_connection (self); if (connection) { - s_ip = addr_family == AF_INET - ? nm_connection_get_setting_ip4_config (connection) - : nm_connection_get_setting_ip6_config (connection); + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); /* Slave interfaces don't have IP settings, but we may get here when * external changes are made or when noticing IP changes when starting @@ -2159,7 +2186,9 @@ nm_device_get_route_metric (NMDevice *self, /* use the current NMConfigData, which makes this configuration reloadable. * Note that that means that the route-metric might change between SIGHUP. * You must cache the returned value if that is a problem. */ - property = addr_family == AF_INET ? "ipv4.route-metric" : "ipv6.route-metric"; + property = addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-metric") + : NM_CON_DEFAULT ("ipv6.route-metric"); route_metric = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, self, @@ -2189,7 +2218,7 @@ _get_mdns (NMDevice *self) return mdns; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.mdns", + NM_CON_DEFAULT ("connection.mdns"), self, NM_SETTING_CONNECTION_MDNS_NO, NM_SETTING_CONNECTION_MDNS_YES, @@ -2211,7 +2240,7 @@ _get_llmnr (NMDevice *self) return llmnr; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.llmnr", + NM_CON_DEFAULT ("connection.llmnr"), self, NM_SETTING_CONNECTION_LLMNR_NO, NM_SETTING_CONNECTION_LLMNR_YES, @@ -2246,11 +2275,7 @@ nm_device_get_route_table (NMDevice *self, connection = nm_device_get_applied_connection (self); if (connection) { - if (addr_family == AF_INET) - s_ip = nm_connection_get_setting_ip4_config (connection); - else - s_ip = nm_connection_get_setting_ip6_config (connection); - + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); if (s_ip) route_table = nm_setting_ip_config_get_route_table (s_ip); @@ -2260,7 +2285,9 @@ nm_device_get_route_table (NMDevice *self, if (route_table == 0) { const char *property; - property = addr_family == AF_INET ? "ipv4.route-table" : "ipv6.route-table"; + property = addr_family == AF_INET + ? NM_CON_DEFAULT ("ipv4.route-table") + : NM_CON_DEFAULT ("ipv6.route-table"); route_table = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, property, self, @@ -2373,10 +2400,27 @@ nm_device_get_act_request (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->act_request.obj; } +NMActivationStateFlags +nm_device_get_activation_state_flags (NMDevice *self) +{ + NMActRequest *ac; + + g_return_val_if_fail (NM_IS_DEVICE (self), NM_ACTIVATION_STATE_FLAG_NONE); + + ac = NM_DEVICE_GET_PRIVATE (self)->act_request.obj; + if (!ac) + return NM_ACTIVATION_STATE_FLAG_NONE; + return nm_active_connection_get_state_flags (NM_ACTIVE_CONNECTION (ac)); +} + NMSettingsConnection * nm_device_get_settings_connection (NMDevice *self) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + + g_return_val_if_fail (NM_IS_DEVICE (self), NULL); + + priv = NM_DEVICE_GET_PRIVATE (self); return priv->act_request.obj ? nm_act_request_get_settings_connection (priv->act_request.obj) : NULL; } @@ -2420,7 +2464,7 @@ nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFla return nm_active_connection_has_unmodified_applied_connection ((NMActiveConnection *) priv->act_request.obj, compare_flags); } -NMSetting * +gpointer nm_device_get_applied_setting (NMDevice *self, GType setting_type) { NMConnection *connection; @@ -2456,24 +2500,37 @@ typedef enum { } ConcheckScheduleMode; static NMDeviceConnectivityHandle *concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic); static void concheck_periodic_schedule_set (NMDevice *self, + int addr_family, ConcheckScheduleMode mode); static gboolean -concheck_periodic_timeout_cb (gpointer user_data) +_concheck_periodic_timeout_cb (NMDevice *self, int addr_family) { - NMDevice *self = user_data; - - _LOGt (LOGD_CONCHECK, "connectivity: periodic timeout"); - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_PERIODIC); + _LOGt (LOGD_CONCHECK, "connectivity: [IPv%c] periodic timeout", + nm_utils_addr_family_to_char (addr_family)); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_PERIODIC); return G_SOURCE_REMOVE; } static gboolean +concheck_ip4_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET); +} + +static gboolean +concheck_ip6_periodic_timeout_cb (gpointer user_data) +{ + return _concheck_periodic_timeout_cb (user_data, AF_INET6); +} + +static gboolean concheck_is_possible (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -2491,17 +2548,18 @@ concheck_is_possible (NMDevice *self) } static gboolean -concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) +concheck_periodic_schedule_do (NMDevice *self, int addr_family, gint64 now_ns) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean periodic_check_disabled = FALSE; gint64 expiry, tdiff; + const gboolean IS_IPv4 = (addr_family == AF_INET); /* we always cancel whatever was pending. */ - if (nm_clear_g_source (&priv->concheck_p_cur_id)) + if (nm_clear_g_source (&priv->concheck_x[IS_IPv4].p_cur_id)) periodic_check_disabled = TRUE; - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic checks are disabled */ goto out; } @@ -2510,46 +2568,50 @@ concheck_periodic_schedule_do (NMDevice *self, gint64 now_ns) goto out; nm_assert (now_ns > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); /* we schedule the timeout based on our current settings cur-interval and cur-basetime. * Before calling concheck_periodic_schedule_do(), make sure that these properties are * correct. */ - expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = expiry - now_ns; - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: %sscheduled in %lld milliseconds (%u seconds interval)", + nm_utils_addr_family_to_char (addr_family), periodic_check_disabled ? "re-" : "", (long long) (tdiff / NM_UTILS_NS_PER_MSEC), - priv->concheck_p_cur_interval); + priv->concheck_x[IS_IPv4].p_cur_interval); - priv->concheck_p_cur_id = g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, - concheck_periodic_timeout_cb, - self); + priv->concheck_x[IS_IPv4].p_cur_id = + g_timeout_add (NM_MAX ((gint64) 0, tdiff) / NM_UTILS_NS_PER_MSEC, + IS_IPv4 ? concheck_ip4_periodic_timeout_cb : concheck_ip6_periodic_timeout_cb, + self); return TRUE; out: - if (periodic_check_disabled) - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: unscheduled"); + if (periodic_check_disabled) { + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: unscheduled", + nm_utils_addr_family_to_char (addr_family)); + } return FALSE; } #define CONCHECK_P_PROBE_INTERVAL 1 static void -concheck_periodic_schedule_set (NMDevice *self, - ConcheckScheduleMode mode) +concheck_periodic_schedule_set (NMDevice *self, int addr_family, ConcheckScheduleMode mode) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gint64 new_expiry, exp_expiry, cur_expiry, tdiff; gint64 now_ns = 0; + const gboolean IS_IPv4 = (addr_family == AF_INET); - if (priv->concheck_p_max_interval == 0) { + if (priv->concheck_x[IS_IPv4].p_max_interval == 0) { /* periodic check is disabled. Nothing to do. */ return; } - if (!priv->concheck_p_cur_id) { + if (!priv->concheck_x[IS_IPv4].p_cur_id) { /* we currently don't have a timeout scheduled. No need to reschedule * another one... */ if (NM_IN_SET (mode, CONCHECK_SCHEDULE_UPDATE_INTERVAL, @@ -2563,19 +2625,19 @@ concheck_periodic_schedule_set (NMDevice *self, switch (mode) { case CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); return; case CONCHECK_SCHEDULE_UPDATE_INTERVAL: - /* called with "UPDATE_INTERVAL" and already have a concheck_p_cur_id scheduled. */ + /* called with "UPDATE_INTERVAL" and already have a p_cur_id scheduled. */ - nm_assert (priv->concheck_p_max_interval > 0); - nm_assert (priv->concheck_p_cur_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_max_interval > 0); + nm_assert (priv->concheck_x[IS_IPv4].p_cur_interval > 0); - if (priv->concheck_p_cur_interval <= priv->concheck_p_max_interval) { + if (priv->concheck_x[IS_IPv4].p_cur_interval <= priv->concheck_x[IS_IPv4].p_max_interval) { /* we currently have a shorter interval set, than what we now have. Either, * because we are probing, or because the previous max interval was shorter. * @@ -2584,17 +2646,17 @@ concheck_periodic_schedule_set (NMDevice *self, return; } - cur_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_max_interval * NM_UTILS_NS_PER_SECOND); + cur_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_max_interval * NM_UTILS_NS_PER_SECOND); nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; if (cur_expiry <= now_ns) { /* Since the last time we scheduled a periodic check, already more than the * new max_interval passed. We need to start a check right away (and * schedule a timeout in cur-interval in the future). */ - priv->concheck_p_cur_basetime_ns = now_ns; - if (concheck_periodic_schedule_do (self, now_ns)) - concheck_start (self, NULL, NULL, TRUE); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns; + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) + concheck_start (self, addr_family, NULL, NULL, TRUE); } else { /* we are reducing the max-interval to a shorter interval that we have currently * scheduled (with cur_interval). @@ -2602,24 +2664,26 @@ concheck_periodic_schedule_set (NMDevice *self, * However, since the last time we scheduled the check, not even the new max-interval * expired. All we need to do, is reschedule the timer to expire sooner. The cur_basetime * is unchanged. */ - concheck_periodic_schedule_do (self, now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); } return; case CONCHECK_SCHEDULE_CHECK_EXTERNAL: /* a external connectivity check delays our periodic check. We reset the counter. */ - priv->concheck_p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); + concheck_periodic_schedule_do (self, addr_family, now_ns); return; case CONCHECK_SCHEDULE_CHECK_PERIODIC: { gboolean any_periodic_pending; NMDeviceConnectivityHandle *handle; - guint old_interval = priv->concheck_p_cur_interval; + guint old_interval = priv->concheck_x[IS_IPv4].p_cur_interval; any_periodic_pending = FALSE; c_list_for_each_entry (handle, &priv->concheck_lst_head, concheck_lst) { + if (handle->addr_family != addr_family) + continue; if (handle->is_periodic_bump) { handle->is_periodic_bump = FALSE; handle->is_periodic_bump_on_complete = FALSE; @@ -2630,7 +2694,7 @@ concheck_periodic_schedule_set (NMDevice *self, /* we reached a timeout to schedule a new periodic request, however we still * have period requests pending that didn't complete yet. We need to bump the * interval already. */ - priv->concheck_p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (old_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); } /* we just reached a timeout. The expected expiry (exp_expiry) should be @@ -2638,13 +2702,13 @@ concheck_periodic_schedule_set (NMDevice *self, * * We want to reschedule the timeout at exp_expiry (aka now) + cur_interval. */ nm_utils_get_monotonic_timestamp_ns_cached (&now_ns); - exp_expiry = priv->concheck_p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); - new_expiry = exp_expiry + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + exp_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (old_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = exp_expiry + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - now_ns, 0); - priv->concheck_p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - if (concheck_periodic_schedule_do (self, now_ns)) { - handle = concheck_start (self, NULL, NULL, TRUE); - if (old_interval != priv->concheck_p_cur_interval) { + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = (now_ns + tdiff) - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + if (concheck_periodic_schedule_do (self, addr_family, now_ns)) { + handle = concheck_start (self, addr_family, NULL, NULL, TRUE); + if (old_interval != priv->concheck_x[IS_IPv4].p_cur_interval) { /* we just bumped the interval already when scheduling this check. * When the handle returns, don't bump a second time. * @@ -2659,54 +2723,57 @@ concheck_periodic_schedule_set (NMDevice *self, /* we just got an event that we lost connectivity (that is, concheck returned). We reset * the interval to min/max or increase the probe interval (bump). */ case CONCHECK_SCHEDULE_RETURNED_MIN: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_max_interval, CONCHECK_P_PROBE_INTERVAL); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_max_interval, CONCHECK_P_PROBE_INTERVAL); break; case CONCHECK_SCHEDULE_RETURNED_MAX: - priv->concheck_p_cur_interval = priv->concheck_p_max_interval; + priv->concheck_x[IS_IPv4].p_cur_interval = priv->concheck_x[IS_IPv4].p_max_interval; break; case CONCHECK_SCHEDULE_RETURNED_BUMP: - priv->concheck_p_cur_interval = NM_MIN (priv->concheck_p_cur_interval * 2, priv->concheck_p_max_interval); + priv->concheck_x[IS_IPv4].p_cur_interval = NM_MIN (priv->concheck_x[IS_IPv4].p_cur_interval * 2, priv->concheck_x[IS_IPv4].p_max_interval); break; } /* we are here, because we returned from a connectivity check and adjust the current interval. * * But note that we calculate the new timeout based on the time when we scheduled the - * last check, instead of counting from now. The reaons is, that we want that the times + * last check, instead of counting from now. The reason is that we want that the times * when we schedule checks be at precise intervals, without including the time it took for * the connectivity check. */ - new_expiry = priv->concheck_p_cur_basetime_ns + (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); + new_expiry = priv->concheck_x[IS_IPv4].p_cur_basetime_ns + (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); tdiff = NM_MAX (new_expiry - nm_utils_get_monotonic_timestamp_ns_cached (&now_ns), 0); - priv->concheck_p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_p_cur_interval * NM_UTILS_NS_PER_SECOND); - concheck_periodic_schedule_do (self, now_ns); + priv->concheck_x[IS_IPv4].p_cur_basetime_ns = now_ns + tdiff - (priv->concheck_x[IS_IPv4].p_cur_interval * NM_UTILS_NS_PER_SECOND); + concheck_periodic_schedule_do (self, addr_family, now_ns); } static void -concheck_update_interval (NMDevice *self, gboolean check_now) +concheck_update_interval (NMDevice *self, int addr_family, gboolean check_now) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); guint new_interval; + const gboolean IS_IPv4 = (addr_family == AF_INET); new_interval = nm_connectivity_get_interval (concheck_get_mgr (self)); new_interval = NM_MIN (new_interval, 7 *24 * 3600); - if (new_interval != priv->concheck_p_max_interval) { - _LOGT (LOGD_CONCHECK, "connectivity: periodic-check: set interval to %u seconds", new_interval); - priv->concheck_p_max_interval = new_interval; + if (new_interval != priv->concheck_x[IS_IPv4].p_max_interval) { + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] periodic-check: set interval to %u seconds", + nm_utils_addr_family_to_char (addr_family), new_interval); + priv->concheck_x[IS_IPv4].p_max_interval = new_interval; } if (!new_interval) { /* this will cancel any potentially pending timeout because max-interval is zero. * But it logs a nice message... */ - concheck_periodic_schedule_do (self, 0); + concheck_periodic_schedule_do (self, addr_family, 0); /* also update the fake connectivity state. */ - concheck_update_state (self, NM_CONNECTIVITY_FAKE, TRUE); + concheck_update_state (self, addr_family, NM_CONNECTIVITY_FAKE, TRUE); return; } concheck_periodic_schedule_set (self, + addr_family, check_now ? CONCHECK_SCHEDULE_UPDATE_INTERVAL_RESTART : CONCHECK_SCHEDULE_UPDATE_INTERVAL); @@ -2715,13 +2782,16 @@ concheck_update_interval (NMDevice *self, gboolean check_now) void nm_device_check_connectivity_update_interval (NMDevice *self) { - concheck_update_interval (self, FALSE); + concheck_update_interval (self, AF_INET, TRUE); + concheck_update_interval (self, AF_INET6, TRUE); } static void -concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow_periodic_bump) +concheck_update_state (NMDevice *self, int addr_family, + NMConnectivityState state, gboolean allow_periodic_bump) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const gboolean IS_IPv4 = (addr_family == AF_INET); /* @state is a result of the connectivity check. We only expect a precise * number of possible values. */ @@ -2734,7 +2804,7 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_ERROR) { /* on error, we don't change the current connectivity state, * except making UNKNOWN to NONE. */ - state = priv->connectivity_state; + state = priv->concheck_x[IS_IPv4].state; if (state == NM_CONNECTIVITY_UNKNOWN) state = NM_CONNECTIVITY_NONE; } else if (state == NM_CONNECTIVITY_FAKE) { @@ -2753,11 +2823,11 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow state = NM_CONNECTIVITY_NONE; } - if (priv->connectivity_state == state) { + if (priv->concheck_x[IS_IPv4].state == state) { /* we got a connectivty update, but the state didn't change. If we were probing, * we bump the probe frequency. */ if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); return; } /* we need to update the probe interval before emitting signals. Emitting @@ -2766,23 +2836,22 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow if (state == NM_CONNECTIVITY_FULL) { /* we reached full connectivity state. Stop probing by setting the * interval to the max. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MAX); - } else if (priv->connectivity_state == NM_CONNECTIVITY_FULL) { + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MAX); + } else if (priv->concheck_x[IS_IPv4].state == NM_CONNECTIVITY_FULL) { /* we are about to loose connectivity. (re)start probing by setting * the timeout interval to the min. */ - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_MIN); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_MIN); } else { if (allow_periodic_bump) - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_RETURNED_BUMP); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_RETURNED_BUMP); } _LOGD (LOGD_CONCHECK, "connectivity state changed from %s to %s", - nm_connectivity_state_to_string (priv->connectivity_state), + nm_connectivity_state_to_string (priv->concheck_x[IS_IPv4].state), nm_connectivity_state_to_string (state)); - priv->connectivity_state = state; + priv->concheck_x[IS_IPv4].state = state; - _notify (self, PROP_CONNECTIVITY); - g_signal_emit (self, signals[CONNECTIVITY_CHANGED], 0); + _notify (self, IS_IPv4 ? PROP_IP4_CONNECTIVITY : PROP_IP6_CONNECTIVITY); if ( priv->state == NM_DEVICE_STATE_ACTIVATED && !nm_device_sys_iface_state_is_external (self)) { @@ -2795,10 +2864,39 @@ concheck_update_state (NMDevice *self, NMConnectivityState state, gboolean allow } } +static const char * +nm_device_get_effective_ip_config_method (NMDevice *self, + int addr_family) +{ + NMDeviceClass *klass; + NMConnection *connection = nm_device_get_applied_connection (self); + const char *method; + + g_return_val_if_fail (NM_IS_CONNECTION (connection), "" /* bogus */); + nm_assert_addr_family (addr_family); + + method = nm_utils_get_ip_config_method (connection, addr_family); + + if ( (addr_family == AF_INET && nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) + || (addr_family == AF_INET6 && nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO))) { + klass = NM_DEVICE_GET_CLASS (self); + if (klass->get_auto_ip_config_method) { + const char *auto_method; + + auto_method = klass->get_auto_ip_config_method (self, addr_family); + if (auto_method) + return auto_method; + } + } + + return method; +} + static void -concheck_handle_complete (NMDeviceConnectivityHandle *handle, - GError *error) +concheck_handle_complete (NMDeviceConnectivityHandle *handle, GError *error) { + const gboolean IS_IPv4 = (handle->addr_family == AF_INET); + /* The moment we invoke the callback, we unlink it. It signals * that @handle is handled -- as far as the callee of callback * is concerned. */ @@ -2810,7 +2908,7 @@ concheck_handle_complete (NMDeviceConnectivityHandle *handle, if (handle->callback) { handle->callback (handle->self, handle, - NM_DEVICE_GET_PRIVATE (handle->self)->connectivity_state, + NM_DEVICE_GET_PRIVATE (handle->self)->concheck_x[IS_IPv4].state, error, handle->user_data); } @@ -2846,7 +2944,8 @@ concheck_cb (NMConnectivity *connectivity, /* the only place where we nm_connectivity_check_cancel(@c_handle), is * from inside concheck_handle_complete(). This is a recursive call, * nothing to do. */ - _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, cancelled)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] complete check (seq:%llu, cancelled)", + nm_utils_addr_family_to_char (handle->addr_family), (long long unsigned) handle->seq); return; } @@ -2856,7 +2955,8 @@ concheck_cb (NMConnectivity *connectivity, self_keep_alive = g_object_ref (self); - _LOGT (LOGD_CONCHECK, "connectivity: complete check (seq:%llu, state:%s)", + _LOGT (LOGD_CONCHECK, "connectivity: [Ipv%c] complete check (seq:%llu, state:%s)", + nm_utils_addr_family_to_char (handle->addr_family), (long long unsigned) handle->seq, nm_connectivity_state_to_string (state)); @@ -2872,6 +2972,8 @@ concheck_cb (NMConnectivity *connectivity, any_periodic_before = FALSE; any_periodic_after = FALSE; c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->is_periodic_bump_on_complete) { if (other_handle->seq < seq) any_periodic_before = TRUE; @@ -2898,7 +3000,7 @@ concheck_cb (NMConnectivity *connectivity, } /* first update the new state, and emit signals. */ - concheck_update_state (self, state, allow_periodic_bump); + concheck_update_state (self, handle->addr_family, state, allow_periodic_bump); handle_is_alive = FALSE; @@ -2910,6 +3012,8 @@ concheck_cb (NMConnectivity *connectivity, * @handle, as they are automatically obsoleted. */ check_handles: c_list_for_each_entry (other_handle, &priv->concheck_lst_head, concheck_lst) { + if (other_handle->addr_family != handle->addr_family) + continue; if (other_handle->seq >= seq) { /* it's not guaranteed that @handle is still in the list. It might already * be canceled while invoking callbacks for a previous other_handle. @@ -2947,6 +3051,7 @@ check_handles: static NMDeviceConnectivityHandle * concheck_start (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data, gboolean is_periodic) @@ -2954,6 +3059,7 @@ concheck_start (NMDevice *self, static guint64 seq_counter = 0; NMDevicePrivate *priv; NMDeviceConnectivityHandle *handle; + const char *ifname; g_return_val_if_fail (NM_IS_DEVICE (self), NULL); @@ -2967,14 +3073,51 @@ concheck_start (NMDevice *self, handle->is_periodic = is_periodic; handle->is_periodic_bump = is_periodic; handle->is_periodic_bump_on_complete = is_periodic; + handle->addr_family = addr_family; c_list_link_tail (&priv->concheck_lst_head, &handle->concheck_lst); - _LOGT (LOGD_CONCHECK, "connectivity: start check (seq:%llu%s)", + _LOGT (LOGD_CONCHECK, "connectivity: [IPv%c] start check (seq:%llu%s)", + nm_utils_addr_family_to_char (addr_family), (long long unsigned) handle->seq, is_periodic ? ", periodic-check" : ""); + if ( addr_family == AF_INET + && !priv->concheck_rp_filter_checked) { + + if ((ifname = nm_device_get_ip_iface_from_platform (self))) { + int val, val_all; + + val = nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), + AF_INET, + ifname, + "rp_filter", + 10, 0, 2, 3); + if (val < 2) { + val_all = nm_platform_sysctl_ip_conf_get_int_checked (nm_device_get_platform (self), + AF_INET, + "all", + "rp_filter", + 10, 0, 2, val); + if (val_all > val) { + val = val_all; + ifname = "all"; + } + } + + if (val == 1) { + _LOGW (LOGD_CONCHECK, "connectivity: \"/proc/sys/net/ipv4/conf/%s/rp_filter\" is set to \"1\". " + "This might break connectivity checking for IPv4 on this device", ifname); + } + } + + /* we only check once per device. It's a warning after all. */ + priv->concheck_rp_filter_checked = TRUE; + } + handle->c_handle = nm_connectivity_check_start (concheck_get_mgr (self), + handle->addr_family, + nm_device_get_ip_ifindex (self), nm_device_get_ip_iface (self), concheck_cb, handle); @@ -2983,6 +3126,7 @@ concheck_start (NMDevice *self, NMDeviceConnectivityHandle * nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data) { @@ -2991,8 +3135,8 @@ nm_device_check_connectivity (NMDevice *self, if (!concheck_is_possible (self)) return NULL; - concheck_periodic_schedule_set (self, CONCHECK_SCHEDULE_CHECK_EXTERNAL); - handle = concheck_start (self, callback, user_data, FALSE); + concheck_periodic_schedule_set (self, addr_family, CONCHECK_SCHEDULE_CHECK_EXTERNAL); + handle = concheck_start (self, addr_family, callback, user_data, FALSE); return handle; } @@ -3014,11 +3158,26 @@ nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle) } NMConnectivityState -nm_device_get_connectivity_state (NMDevice *self) +nm_device_get_connectivity_state (NMDevice *self, int addr_family) { + NMDevicePrivate *priv; + const gboolean IS_IPv4 = (addr_family == AF_INET); + g_return_val_if_fail (NM_IS_DEVICE (self), NM_CONNECTIVITY_UNKNOWN); + nm_assert_addr_family (addr_family); - return NM_DEVICE_GET_PRIVATE (self)->connectivity_state; + priv = NM_DEVICE_GET_PRIVATE (self); + + switch (addr_family) { + case AF_INET: + case AF_INET6: + return priv->concheck_x[IS_IPv4].state; + default: + nm_assert (addr_family == AF_UNSPEC); + return NM_MAX_WITH_CMP (nm_connectivity_state_cmp, + priv->concheck_x[0].state, + priv->concheck_x[1].state); + } } /*****************************************************************************/ @@ -3053,6 +3212,7 @@ find_slave_info (NMDevice *self, NMDevice *slave) static gboolean nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *connection) { + NMDevicePrivate *priv; SlaveInfo *info; gboolean success = FALSE; gboolean configure; @@ -3061,6 +3221,7 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c g_return_val_if_fail (slave != NULL, FALSE); g_return_val_if_fail (NM_DEVICE_GET_CLASS (self)->enslave_slave != NULL, FALSE); + priv = NM_DEVICE_GET_PRIVATE (self); info = find_slave_info (self, slave); if (!info) return FALSE; @@ -3083,15 +3244,20 @@ nm_device_master_enslave_slave (NMDevice *self, NMDevice *slave, NMConnection *c */ nm_device_update_hw_address (self); + /* Send ARP announcements if did not yet and have addresses. */ + if ( priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE + && !priv->acd.announcing) + nm_device_arp_announce (self); + /* Restart IP configuration if we're waiting for slaves. Do this * after updating the hardware address as IP config may need the * new address. */ if (success) { - if (NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT) + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_WAIT) nm_device_activate_stage3_ip4_start (self); - if (NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT) + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT) nm_device_activate_stage3_ip6_start (self); } @@ -3421,11 +3587,14 @@ nm_device_set_carrier (NMDevice *self, gboolean carrier) static void nm_device_set_carrier_from_platform (NMDevice *self) { + int ifindex; + if (nm_device_has_capability (self, NM_DEVICE_CAP_CARRIER_DETECT)) { - if (!nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER)) { + if ( !nm_device_has_capability (self, NM_DEVICE_CAP_NONSTANDARD_CARRIER) + && (ifindex = nm_device_get_ip_ifindex (self)) > 0) { nm_device_set_carrier (self, nm_platform_link_is_connected (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self))); + ifindex)); } } else { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -3599,6 +3768,8 @@ device_link_changed (NMDevice *self) priv->device_link_changed_down = FALSE; ifindex = nm_device_get_ifindex (self); + if (ifindex <= 0) + return G_SOURCE_REMOVE; pllink = nm_platform_link_get (nm_device_get_platform (self), ifindex); if (!pllink) return G_SOURCE_REMOVE; @@ -3709,11 +3880,13 @@ device_link_changed (NMDevice *self) if (priv->up && (!was_up || seen_down)) { /* the link was down and just came up. That happens for example, while changing MTU. * We must restore IP configuration. */ - if (priv->ip4_state == IP_DONE) { + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after link comes up again"); } - if (priv->ip6_state == IP_DONE) { + + priv->linklocal6_dad_counter = 0; + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed applying IP6 config after link comes up again"); } @@ -3798,126 +3971,6 @@ link_changed_cb (NMPlatform *platform, /*****************************************************************************/ -typedef struct { - in_addr_t network; - guint8 plen; -} IP4RPFilterData; - -static guint -_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) -{ - NMHashState h; - - nm_hash_init (&h, 1105201169u); - nm_hash_update_vals (&h, - d->network, - d->plen); - return nm_hash_complete (&h); -} - -static gboolean -_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) -{ - return d1->network == d2->network && d1->plen == d2->plen; -} - -static gboolean -_v4_has_shadowed_routes_detect (NMDevice *self) -{ - NMPlatform *platform; - int ifindex; - NMPLookup lookup; - const NMDedupMultiHeadEntry *head_entry; - NMDedupMultiIter iter; - const NMPObject *o; - guint data_len; - gs_unref_hashtable GHashTable *data_hash = NULL; - gs_free IP4RPFilterData *data_arr = NULL; - - ifindex = nm_device_get_ip_ifindex (self); - if (ifindex <= 0) - return FALSE; - - platform = nm_device_get_platform (self); - - head_entry = nm_platform_lookup (platform, - nmp_lookup_init_object (&lookup, - NMP_OBJECT_TYPE_IP4_ROUTE, - ifindex)); - if (!head_entry) - return FALSE; - - /* first, create a lookup index @data_hash for all network/plen pairs. */ - data_len = 0; - data_arr = g_new (IP4RPFilterData, head_entry->len); - data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, - (GEqualFunc) _v4_has_shadowed_routes_detect_equal); - - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); - IP4RPFilterData *d; - - nm_assert (r->ifindex == ifindex); - - if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) - || r->table_coerced) - continue; - - d = &data_arr[data_len++]; - d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); - d->plen = r->plen; - g_hash_table_add (data_hash, d); - } - - /* then, search if there is any route on another interface with the same - * network/plen destination. If yes, we consider this a multihoming - * setup. */ - head_entry = nm_platform_lookup (platform, - nmp_lookup_init_obj_type (&lookup, - NMP_OBJECT_TYPE_IP4_ROUTE)); - nmp_cache_iter_for_each (&iter, head_entry, &o) { - const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); - IP4RPFilterData d; - - if ( r->ifindex == ifindex - || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) - || r->table_coerced) - continue; - - d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); - d.plen = r->plen; - if (g_hash_table_contains (data_hash, &d)) - return TRUE; - } - - return FALSE; -} - -static void -ip4_rp_filter_update (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ip4_rp_filter; - - if ( priv->v4_has_shadowed_routes - || nm_device_get_best_default_route (self, AF_INET)) { - if (nm_device_ipv4_sysctl_get_effective_uint32 (self, "rp_filter", 0) != 1) { - /* Don't touch the rp_filter if it's not strict. */ - return; - } - /* Loose rp_filter */ - ip4_rp_filter = "2"; - } else { - /* Default rp_filter */ - ip4_rp_filter = NULL; - } - - if (ip4_rp_filter != priv->ip4_rp_filter) { - nm_device_ipv4_sysctl_set (self, "rp_filter", ip4_rp_filter); - priv->ip4_rp_filter = ip4_rp_filter; - } -} - static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { @@ -4331,8 +4384,7 @@ realize_start_setup (NMDevice *self, * NetworkManager might down the interface or remove the 127.0.0.1 address. */ nm_device_set_unmanaged_flags (self, NM_UNMANAGED_BY_TYPE, - is_loopback (self) - || NM_IS_DEVICE_WIREGUARD (self)); + is_loopback (self)); nm_device_set_unmanaged_by_user_udev (self); nm_device_set_unmanaged_by_user_conf (self); @@ -4804,7 +4856,8 @@ nm_device_master_release_slaves (NMDevice *self) if (priv->state == NM_DEVICE_STATE_FAILED) reason = NM_DEVICE_STATE_REASON_DEPENDENCY_FAILED; - if (!nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) + if ( priv->ifindex <= 0 + || !nm_platform_link_get (nm_device_get_platform (self), priv->ifindex)) configure = FALSE; c_list_for_each_safe (iter, safe, &priv->slaves) { @@ -4855,21 +4908,11 @@ static gboolean get_ip_config_may_fail (NMDevice *self, int addr_family) { NMConnection *connection; - NMSettingIPConfig *s_ip = NULL; + NMSettingIPConfig *s_ip; connection = nm_device_get_applied_connection (self); - /* Fail the connection if the failed IP method is required to complete */ - switch (addr_family) { - case AF_INET: - s_ip = nm_connection_get_setting_ip4_config (connection); - break; - case AF_INET6: - s_ip = nm_connection_get_setting_ip6_config (connection); - break; - default: - nm_assert_not_reached (); - } + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); return !s_ip || nm_setting_ip_config_get_may_fail (s_ip); } @@ -4903,32 +4946,32 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) && !priv->is_enslaved) return; - s_ip4 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); + s_ip4 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP4_CONFIG); if (s_ip4 && nm_streq0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) ip4_disabled = TRUE; - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); if (s_ip6 && nm_streq0 (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) ip6_ignore = TRUE; - if ( priv->ip4_state == IP_DONE - && priv->ip6_state == IP_DONE) { + if ( priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE + && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { /* Both method completed (or disabled), proceed with activation */ nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); return; } - if ( (priv->ip4_state == IP_FAIL || (ip4_disabled && priv->ip4_state == IP_DONE)) - && (priv->ip6_state == IP_FAIL || (ip6_ignore && priv->ip6_state == IP_DONE))) { + if ( (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL || (ip4_disabled && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE)) + && (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL || (ip6_ignore && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE))) { /* Either both methods failed, or only one failed and the other is * disabled */ if (nm_device_sys_iface_state_is_external_or_assume (self)) { /* We have assumed configuration, but couldn't redo it. No problem, * move to check state. */ - _set_ip_state (self, AF_INET, IP_DONE); - _set_ip_state (self, AF_INET6, IP_DONE); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_DONE); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_DONE); state = NM_DEVICE_STATE_IP_CHECK; } else if ( may_fail && get_ip_config_may_fail (self, AF_INET) @@ -4951,14 +4994,14 @@ check_ip_state (NMDevice *self, gboolean may_fail, gboolean full_state_update) } /* If a method is still pending but required, wait */ - if (priv->ip4_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET)) + if (priv->ip_state_4 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail (self, AF_INET)) return; - if (priv->ip6_state != IP_DONE && !get_ip_config_may_fail (self, AF_INET6)) + if (priv->ip_state_6 != NM_DEVICE_IP_STATE_DONE && !get_ip_config_may_fail (self, AF_INET6)) return; /* If at least a method has completed, proceed with activation */ - if ( (priv->ip4_state == IP_DONE && !ip4_disabled) - || (priv->ip6_state == IP_DONE && !ip6_ignore)) { + if ( (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && !ip4_disabled) + || (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && !ip6_ignore)) { if (full_state_update) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CHECK, NM_DEVICE_STATE_REASON_NONE); return; @@ -5267,7 +5310,7 @@ nm_device_autoconnect_allowed (NMDevice *self) /* The 'autoconnect-allowed' signal is emitted on a device to allow * other listeners to block autoconnect on the device if they wish. * This is mainly used by the OLPC Mesh devices to block autoconnect - * on their companion WiFi device as they share radio resources and + * on their companion Wi-Fi device as they share radio resources and * cannot be connected at the same time. */ @@ -5486,10 +5529,13 @@ nm_device_generate_connection (NMDevice *self, nm_connection_add_setting (connection, nm_setting_proxy_new ()); pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ifindex); - if (pllink && pllink->inet6_token.id) { + if ( pllink + && pllink->inet6_token.id) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE, NM_IN6_ADDR_GEN_MODE_EUI64, - NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, NULL), + NM_SETTING_IP6_CONFIG_TOKEN, nm_utils_inet6_interface_identifier_to_token (pllink->inet6_token, sbuf), NULL); } } @@ -5507,8 +5553,8 @@ nm_device_generate_connection (NMDevice *self, /* Ignore the connection if it has no IP configuration, * no slave configuration, and is not a master interface. */ - ip4_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - ip6_method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + ip4_method = nm_utils_get_ip_config_method (connection, AF_INET); + ip6_method = nm_utils_get_ip_config_method (connection, AF_INET6); if ( g_strcmp0 (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0 && g_strcmp0 (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0 && !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con)) @@ -5646,7 +5692,6 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError ** NMDeviceClass *klass; const char *const *patterns; NMSettingMatch *s_match; - guint num_patterns; klass = NM_DEVICE_GET_CLASS (self); if (klass->connection_type_check_compatible) { @@ -5687,6 +5732,8 @@ check_connection_compatible (NMDevice *self, NMConnection *connection, GError ** s_match = (NMSettingMatch *) nm_connection_get_setting (connection, NM_TYPE_SETTING_MATCH); if (s_match) { + guint num_patterns = 0; + patterns = nm_setting_match_get_interface_names (s_match, &num_patterns); if (!nm_wildcard_match_check (device_iface, patterns, num_patterns)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -5788,7 +5835,7 @@ unmanaged_on_quit (NMDevice *self) /* the only exception are IPv4 shared connections. We unmanage them on quit. */ connection = nm_device_get_applied_connection (self); if (connection) { - if (NM_IN_STRSET (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG), + if (NM_IN_STRSET (nm_utils_get_ip_config_method (connection, AF_INET), NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { /* shared connections are to be unmangaed. */ return TRUE; @@ -6112,7 +6159,7 @@ lldp_rx_enabled (NMDevice *self) lldp = nm_setting_connection_get_lldp (s_con); if (lldp == NM_SETTING_CONNECTION_LLDP_DEFAULT) { lldp = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.lldp", + NM_CON_DEFAULT ("connection.lldp"), self, NM_SETTING_CONNECTION_LLDP_DEFAULT, NM_SETTING_CONNECTION_LLDP_ENABLE_RX, @@ -6207,7 +6254,7 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) if ( priv->ifindex > 0 && nm_device_has_capability (self, NM_DEVICE_CAP_SRIOV) - && (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { + && (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV))) { nm_auto_freev NMPlatformVF **plat_vfs = NULL; gs_free_error GError *error = NULL; NMSriovVF *vf; @@ -6216,7 +6263,7 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *out_failure_reason) autoprobe = nm_setting_sriov_get_autoprobe_drivers (s_sriov); if (autoprobe == NM_TERNARY_DEFAULT) { autoprobe = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "sriov.autoprobe-drivers", + NM_CON_DEFAULT ("sriov.autoprobe-drivers"), self, NM_TERNARY_FALSE, NM_TERNARY_TRUE, @@ -6271,8 +6318,8 @@ activate_stage1_device_prepare (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; - _set_ip_state (self, AF_INET, IP_NONE); - _set_ip_state (self, AF_INET6, IP_NONE); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_NONE); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_NONE); /* Notify the new ActiveConnection along with the state change */ nm_dbus_track_obj_path_set (&priv->act_request, @@ -6449,23 +6496,25 @@ static void activate_stage2_device_config (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDeviceClass *klass; NMActStageReturn ret; gboolean no_firmware = FALSE; CList *iter; nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); - /* Assumed connections were already set up outside NetworkManager */ - if (!nm_device_sys_iface_state_is_external_or_assume (self)) { - NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - + if (!nm_device_sys_iface_state_is_external_or_assume (self)) _ethtool_state_set (self); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { if (!tc_commit (self)) { _LOGW (LOGD_IP6, "failed applying traffic control rules"); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + return; } + } + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { if (!nm_device_bring_up (self, FALSE, &no_firmware)) { if (no_firmware) nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING); @@ -6473,15 +6522,21 @@ activate_stage2_device_config (NMDevice *self) nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED); return; } + } + + klass = NM_DEVICE_GET_CLASS (self); + if ( klass->act_stage2_config_also_for_external_or_assume + || !nm_device_sys_iface_state_is_external_or_assume (self)) { + NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &failure_reason); + ret = klass->act_stage2_config (self, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) return; - else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) { + nm_assert (ret == NM_ACT_STAGE_RETURN_FAILURE); nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason); return; } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); } /* If we have slaves that aren't yet enslaved, do that now */ @@ -6498,6 +6553,7 @@ activate_stage2_device_config (NMDevice *self) } lldp_init (self, TRUE); + nm_device_activate_schedule_stage3_ip_config_start (self); } @@ -6559,7 +6615,7 @@ nm_device_ip_method_failed (NMDevice *self, g_return_if_fail (NM_IS_DEVICE (self)); g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - _set_ip_state (self, addr_family, IP_FAIL); + _set_ip_state (self, addr_family, NM_DEVICE_IP_STATE_FAIL); if (get_ip_config_may_fail (self, addr_family)) check_ip_state (self, FALSE, (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG)); @@ -6586,7 +6642,7 @@ get_ipv4_dad_timeout (NMDevice *self) return timeout; return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ipv4.dad-timeout", + NM_CON_DEFAULT ("ipv4.dad-timeout"), self, 0, NM_SETTING_IP_CONFIG_DAD_TIMEOUT_MAX, @@ -6594,17 +6650,15 @@ get_ipv4_dad_timeout (NMDevice *self) } static void -acd_data_destroy (gpointer ptr, GClosure *closure) +acd_data_destroy (gpointer ptr) { AcdData *data = ptr; int i; - if (data) { - for (i = 0; data->configs && data->configs[i]; i++) - g_object_unref (data->configs[i]); - g_free (data->configs); - g_slice_free (AcdData, data); - } + for (i = 0; data->configs && data->configs[i]; i++) + g_object_unref (data->configs[i]); + g_free (data->configs); + g_slice_free (AcdData, data); } static void @@ -6612,11 +6666,10 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes { NMConnection *connection; const char *method; - NMIP4Config *empty; connection = nm_device_get_applied_connection (self); nm_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); nm_assert (NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NM_SETTING_IP4_CONFIG_METHOD_AUTO)); @@ -6627,19 +6680,18 @@ ipv4_manual_method_apply (NMDevice *self, NMIP4Config **configs, gboolean succes return; } - if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - empty = _ip4_config_new (self); - nm_device_activate_schedule_ip4_config_result (self, empty); - g_object_unref (empty); - } else { - if (NM_DEVICE_GET_PRIVATE (self)->ip4_state != IP_DONE) + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) + nm_device_activate_schedule_ip_config_result (self, AF_INET, NULL); + else { + if (NM_DEVICE_GET_PRIVATE (self)->ip_state_4 != NM_DEVICE_IP_STATE_DONE) ip_config_merge_and_apply (self, AF_INET, TRUE); } } static void -acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) +acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data) { + AcdData *data = user_data; NMDevice *self; NMDevicePrivate *priv; NMDedupMultiIter ipconf_iter; @@ -6653,13 +6705,15 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) for (i = 0; data->configs && data->configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, data->configs[i], &address) { + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; + result = nm_acd_manager_check_address (acd_manager, address->address); success &= result; _NMLOG (result ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "IPv4 DAD result: address %s is %s", - nm_utils_inet4_ntop (address->address, NULL), + nm_utils_inet4_ntop (address->address, sbuf), result ? "unique" : "duplicate"); } } @@ -6667,7 +6721,7 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) data->callback (self, data->configs, success); priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); - nm_acd_manager_destroy (acd_manager); + nm_acd_manager_free (acd_manager); } /** @@ -6683,6 +6737,10 @@ acd_manager_probe_terminated (NMAcdManager *acd_manager, AcdData *data) static void ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) { + static const NMAcdCallbacks acd_callbacks = { + .probe_terminated_callback = acd_manager_probe_terminated, + .user_data_destroy = acd_data_destroy, + }; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMAcdManager *acd_manager; const NMPlatformIP4Address *address; @@ -6726,26 +6784,23 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) return; } - /* don't take additional references of @acd_manager that outlive @self. - * Otherwise, the callback can be invoked on a dangling pointer as we don't - * disconnect the handler. */ - acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hwaddr_arr, length); - priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager); - data = g_slice_new0 (AcdData); data->configs = configs; data->callback = cb; data->device = self; + acd_manager = nm_acd_manager_new (nm_device_get_ip_ifindex (self), + hwaddr_arr, + length, + &acd_callbacks, + data); + priv->acd.dad_list = g_slist_append (priv->acd.dad_list, acd_manager); + for (i = 0; configs[i]; i++) { nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, configs[i], &address) nm_acd_manager_add_address (acd_manager, address->address); } - g_signal_connect_data (acd_manager, NM_ACD_MANAGER_PROBE_TERMINATED, - G_CALLBACK (acd_manager_probe_terminated), data, - acd_data_destroy, 0); - ret = nm_acd_manager_start_probe (acd_manager, timeout); if (!ret) { @@ -6755,7 +6810,7 @@ ipv4_dad_start (NMDevice *self, NMIP4Config **configs, AcdCallback cb) cb (self, configs, TRUE); priv->acd.dad_list = g_slist_remove (priv->acd.dad_list, acd_manager); - nm_acd_manager_destroy (acd_manager); + nm_acd_manager_free (acd_manager); } } @@ -6783,7 +6838,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) NMPlatformIP4Address address; NMPlatformIP4Route route; - config = _ip4_config_new (self); + config = nm_device_ip4_config_new (self); g_assert (config); memset (&address, 0, sizeof (address)); @@ -6811,8 +6866,6 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) { NMDevice *self = data; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection = NULL; - const char *method; struct in_addr address; NMIP4Config *config; int r; @@ -6820,13 +6873,8 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) if (priv->act_request.obj == NULL) return; - connection = nm_act_request_get_applied_connection (priv->act_request.obj); - g_assert (connection); - - /* Ignore if the connection isn't an AutoIP connection */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL) != 0) - return; + nm_assert (nm_streq (nm_device_get_effective_ip_config_method (self, AF_INET), + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)); switch (event) { case SD_IPV4LL_EVENT_BIND: @@ -6850,11 +6898,11 @@ nm_device_handle_ipv4ll_event (sd_ipv4ll *ll, int event, void *data) return; } - if (priv->ip4_state == IP_CONF) { + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { nm_clear_g_source (&priv->ipv4ll_timeout); - nm_device_activate_schedule_ip4_config_result (self, config); - } else if (priv->ip4_state == IP_DONE) { - applied_config_init (&priv->dev_ip4_config, config); + nm_device_activate_schedule_ip_config_result (self, AF_INET, NM_IP_CONFIG_CAST (config)); + } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { + applied_config_init (&priv->dev_ip_config_4, config); if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) { _LOGE (LOGD_AUTOIP4, "failed to update IP4 config for autoip change."); nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_AUTOIP_FAILED); @@ -6881,8 +6929,8 @@ ipv4ll_timeout_cb (gpointer user_data) priv->ipv4ll_timeout = 0; ipv4ll_cleanup (self); - if (priv->ip4_state == IP_CONF) - nm_device_activate_schedule_ip4_config_timeout (self); + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) + nm_device_activate_schedule_ip_config_timeout (self, AF_INET); } return FALSE; @@ -6965,7 +7013,7 @@ ensure_con_ip_config (NMDevice *self, int addr_family) if (!connection) return; - con_ip_config = _ip_config_new (self, addr_family); + con_ip_config = nm_device_ip_config_new (self, addr_family); if (IS_IPv4) { nm_ip4_config_merge_setting (NM_IP4_CONFIG (con_ip_config), @@ -6998,6 +7046,7 @@ dhcp4_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->dhcp4.was_active = FALSE; nm_clear_g_source (&priv->dhcp4.grace_id); g_clear_pointer (&priv->dhcp4.pac_url, g_free); g_clear_pointer (&priv->dhcp4.root_path, g_free); @@ -7046,10 +7095,9 @@ ip_config_merge_and_apply (NMDevice *self, /* Apply ignore-auto-routes and ignore-auto-dns settings */ if (connection) { - NMSettingIPConfig *s_ip = IS_IPv4 - ? nm_connection_get_setting_ip4_config (connection) - : nm_connection_get_setting_ip6_config (connection); + NMSettingIPConfig *s_ip; + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); if (s_ip) { ignore_auto_routes = nm_setting_ip_config_get_ignore_auto_routes (s_ip); ignore_auto_dns = nm_setting_ip_config_get_ignore_auto_dns (s_ip); @@ -7068,7 +7116,7 @@ ip_config_merge_and_apply (NMDevice *self, } } - composite = _ip_config_new (self, addr_family); + composite = nm_device_ip_config_new (self, addr_family); if (!IS_IPv4) { nm_ip6_config_set_privacy (NM_IP6_CONFIG (composite), @@ -7110,7 +7158,7 @@ ip_config_merge_and_apply (NMDevice *self, if (commit) { gboolean v; - v = default_route_metric_penalty_detect (self); + v = default_route_metric_penalty_detect (self, addr_family); if (IS_IPv4) priv->default_route_metric_penalty_ip4_has = v; else @@ -7120,7 +7168,7 @@ ip_config_merge_and_apply (NMDevice *self, /* Merge all the IP configs into the composite config */ if (IS_IPv4) { - config = applied_config_get_current (&priv->dev_ip4_config); + config = applied_config_get_current (&priv->dev_ip_config_4); if (config) { nm_ip4_config_merge (NM_IP4_CONFIG (composite), NM_IP4_CONFIG (config), (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) @@ -7161,7 +7209,7 @@ ip_config_merge_and_apply (NMDevice *self, /* Merge WWAN config *last* to ensure modem-given settings overwrite * any external stuff set by pppd or other scripts. */ - config = applied_config_get_current (&priv->wwan_ip_config_x[IS_IPv4]); + config = applied_config_get_current (&priv->dev2_ip_config_x[IS_IPv4]); if (config) { nm_ip_config_merge (composite, config, (ignore_auto_routes ? NM_IP_CONFIG_MERGE_NO_ROUTES : 0) @@ -7212,15 +7260,15 @@ ip_config_merge_and_apply (NMDevice *self, } if (!IS_IPv4) { - if (commit) { - NMUtilsIPv6IfaceId iid; + NMUtilsIPv6IfaceId iid; - if ( ip6_addr_gen_token - && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) { - nm_platform_link_set_ipv6_token (nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - iid); - } + if ( commit + && priv->ndisc_started + && ip6_addr_gen_token + && nm_utils_ipv6_interface_identifier_get_from_token (&iid, ip6_addr_gen_token)) { + nm_platform_link_set_ipv6_token (nm_device_get_platform (self), + nm_device_get_ip_ifindex (self), + iid); } } @@ -7242,7 +7290,7 @@ dhcp4_lease_change (NMDevice *self, NMIP4Config *config) g_return_val_if_fail (config, FALSE); - applied_config_init (&priv->dev_ip4_config, config); + applied_config_init (&priv->dev_ip_config_4, config); if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) { _LOGW (LOGD_DHCP4, "failed to update IPv4 config for DHCP change."); @@ -7276,28 +7324,32 @@ dhcp4_grace_period_expired (gpointer user_data) } static void -dhcp4_fail (NMDevice *self) +dhcp4_fail (NMDevice *self, NMDhcpState dhcp_state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - _LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s)", - _ip_state_to_string (priv->ip4_state)); + _LOGD (LOGD_DHCP4, "DHCPv4 failed (ip_state %s, was_active %d)", + _ip_state_to_string (priv->ip_state_4), + priv->dhcp4.was_active); /* Keep client running if there are static addresses configured * on the interface. */ - if ( priv->ip4_state == IP_DONE + if ( priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_4 && nm_ip4_config_get_num_addresses (priv->con_ip_config_4) > 0) goto clear_config; - /* Fail the method in case of timeout or failure during initial - * configuration. + /* Fail the method when one of the following is true: + * 1) the DHCP client terminated: it does not make sense to start a grace + * period without a client running; + * 2) we failed to get an initial lease AND the client was + * not active before. */ - if ( !priv->dhcp4.was_active - && priv->ip4_state == IP_CONF) { + if ( dhcp_state == NM_DHCP_STATE_TERMINATED + || (!priv->dhcp4.was_active && priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF)) { dhcp4_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - nm_device_activate_schedule_ip4_config_timeout (self); + nm_device_activate_schedule_ip_config_timeout (self, AF_INET); return; } @@ -7329,7 +7381,7 @@ static void dhcp4_dad_cb (NMDevice *self, NMIP4Config **configs, gboolean success) { if (success) - nm_device_activate_schedule_ip4_config_result (self, configs[1]); + nm_device_activate_schedule_ip_config_result (self, AF_INET, NM_IP_CONFIG_CAST (configs[1])); else { nm_device_ip_method_failed (self, AF_INET, NM_DEVICE_STATE_REASON_IP_ADDRESS_DUPLICATE); @@ -7358,7 +7410,7 @@ dhcp4_state_changed (NMDhcpClient *client, case NM_DHCP_STATE_BOUND: if (!ip4_config) { _LOGW (LOGD_DHCP4, "failed to get IPv4 config in response to DHCP event."); - dhcp4_fail (self); + dhcp4_fail (self, state); break; } @@ -7367,8 +7419,8 @@ dhcp4_state_changed (NMDhcpClient *client, /* After some failures, we have been able to renew the lease: * update the ip state */ - if (priv->ip4_state == IP_FAIL) - _set_ip_state (self, AF_INET, IP_CONF); + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_FAIL) + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_CONF); g_free (priv->dhcp4.pac_url); priv->dhcp4.pac_url = g_strdup (g_hash_table_lookup (options, "wpad")); @@ -7380,11 +7432,11 @@ dhcp4_state_changed (NMDhcpClient *client, nm_dhcp4_config_set_options (priv->dhcp4.config, options); _notify (self, PROP_DHCP4_CONFIG); - if (priv->ip4_state == IP_CONF) { + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) { connection = nm_device_get_applied_connection (self); g_assert (connection); - manual = _ip4_config_new (self); + manual = nm_device_ip4_config_new (self); nm_ip4_config_merge_setting (manual, nm_connection_get_setting_ip4_config (connection), NM_SETTING_CONNECTION_MDNS_DEFAULT, @@ -7397,24 +7449,25 @@ dhcp4_state_changed (NMDhcpClient *client, configs[1] = g_object_ref (ip4_config); ipv4_dad_start (self, configs, dhcp4_dad_cb); - } else if (priv->ip4_state == IP_DONE) { + } else if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { if (dhcp4_lease_change (self, ip4_config)) nm_device_update_metered (self); else - dhcp4_fail (self); + dhcp4_fail (self, state); } break; case NM_DHCP_STATE_TIMEOUT: - dhcp4_fail (self); + dhcp4_fail (self, state); break; case NM_DHCP_STATE_EXPIRE: /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip4_state == IP_CONF) + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_CONF) break; /* fall through */ case NM_DHCP_STATE_DONE: case NM_DHCP_STATE_FAIL: - dhcp4_fail (self); + case NM_DHCP_STATE_TERMINATED: + dhcp4_fail (self, state); break; default: break; @@ -7434,10 +7487,7 @@ get_dhcp_timeout (NMDevice *self, int addr_family) connection = nm_device_get_applied_connection (self); - if (addr_family == AF_INET) - s_ip = nm_connection_get_setting_ip4_config (connection); - else - s_ip = nm_connection_get_setting_ip6_config (connection); + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); timeout = nm_setting_ip_config_get_dhcp_timeout (s_ip); if (timeout) @@ -7445,8 +7495,8 @@ get_dhcp_timeout (NMDevice *self, int addr_family) timeout = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, addr_family == AF_INET - ? "ipv4.dhcp-timeout" - : "ipv6.dhcp-timeout", + ? NM_CON_DEFAULT ("ipv4.dhcp-timeout") + : NM_CON_DEFAULT ("ipv6.dhcp-timeout"), self, 0, G_MAXINT32, 0); if (timeout) @@ -7481,7 +7531,8 @@ dhcp4_get_client_id (NMDevice *self, if (!client_id) { client_id_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv4.dhcp-client-id", self); + NM_CON_DEFAULT ("ipv4.dhcp-client-id"), + self); if (client_id_default && client_id_default[0]) { /* a non-empty client-id is always valid, see nm_dhcp_utils_client_id_string_to_bytes(). */ client_id = client_id_default; @@ -7490,7 +7541,7 @@ dhcp4_get_client_id (NMDevice *self, if (!client_id) { _LOGD (LOGD_DEVICE | LOGD_DHCP4 | LOGD_IP4, - "ipv4.dhcp-client-id: no explicity client-id configured"); + "ipv4.dhcp-client-id: no explicit client-id configured"); return NULL; } @@ -7723,7 +7774,7 @@ shared4_new_config (NMDevice *self, NMConnection *connection) is_generated = TRUE; } - config = _ip4_config_new (self); + config = nm_device_ip4_config_new (self); nm_ip4_config_add_address (config, &address); if (is_generated) { /* Remove the address lock when the object gets disposed */ @@ -7737,37 +7788,25 @@ shared4_new_config (NMDevice *self, NMConnection *connection) /*****************************************************************************/ static gboolean -connection_ip4_method_requires_carrier (NMConnection *connection, - gboolean *out_ip4_enabled) -{ - const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - static const char *ip4_carrier_methods[] = { - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL, - NULL - }; +connection_ip_method_requires_carrier (NMConnection *connection, + int addr_family, + gboolean *out_ip_enabled) +{ + const char *method; - if (out_ip4_enabled) - *out_ip4_enabled = !!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED); - return g_strv_contains (ip4_carrier_methods, method); -} + method = nm_utils_get_ip_config_method (connection, addr_family); -static gboolean -connection_ip6_method_requires_carrier (NMConnection *connection, - gboolean *out_ip6_enabled) -{ - const char *method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - static const char *ip6_carrier_methods[] = { - NM_SETTING_IP6_CONFIG_METHOD_AUTO, - NM_SETTING_IP6_CONFIG_METHOD_DHCP, - NM_SETTING_IP6_CONFIG_METHOD_SHARED, - NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL, - NULL - }; + if (addr_family == AF_INET) { + NM_SET_OUT (out_ip_enabled, !nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)); + return NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL); + } - if (out_ip6_enabled) - *out_ip6_enabled = !!strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE); - return g_strv_contains (ip6_carrier_methods, method); + NM_SET_OUT (out_ip_enabled, !nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)); + return NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP, + NM_SETTING_IP6_CONFIG_METHOD_SHARED, + NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL); } static gboolean @@ -7784,7 +7823,7 @@ connection_requires_carrier (NMConnection *connection) if (nm_setting_connection_get_master (s_con)) return FALSE; - ip4_carrier_wanted = connection_ip4_method_requires_carrier (connection, &ip4_used); + ip4_carrier_wanted = connection_ip_method_requires_carrier (connection, AF_INET, &ip4_used); if (ip4_carrier_wanted) { /* If IPv4 wants a carrier and cannot fail, the whole connection * requires a carrier regardless of the IPv6 method. @@ -7794,7 +7833,7 @@ connection_requires_carrier (NMConnection *connection) return TRUE; } - ip6_carrier_wanted = connection_ip6_method_requires_carrier (connection, &ip6_used); + ip6_carrier_wanted = connection_ip_method_requires_carrier (connection, AF_INET6, &ip6_used); if (ip6_carrier_wanted) { /* If IPv6 wants a carrier and cannot fail, the whole connection * requires a carrier regardless of the IPv4 method. @@ -7835,107 +7874,6 @@ have_any_ready_slaves (NMDevice *self) return FALSE; } -static gboolean -ip4_requires_slaves (NMConnection *connection) -{ - const char *method; - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - return strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0; -} - -static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *self, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; - const char *method; - - connection = nm_device_get_applied_connection (self); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - if ( connection_ip4_method_requires_carrier (connection, NULL) - && nm_device_is_master (self) - && !priv->carrier) { - _LOGI (LOGD_IP4 | LOGD_DEVICE, - "IPv4 config waiting until carrier is on"); - return NM_ACT_STAGE_RETURN_IP_WAIT; - } - - if (nm_device_is_master (self) && ip4_requires_slaves (connection)) { - /* If the master has no ready slaves, and depends on slaves for - * a successful IPv4 attempt, then postpone IPv4 addressing. - */ - if (!have_any_ready_slaves (self)) { - _LOGI (LOGD_DEVICE | LOGD_IP4, - "IPv4 config waiting until slaves are ready"); - return NM_ACT_STAGE_RETURN_IP_WAIT; - } - } - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (NM_IN_STRSET (method, - NM_SETTING_IP4_CONFIG_METHOD_AUTO, - NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { - NMSettingIPConfig *s_ip4; - NMIP4Config **configs, *config; - guint num_addresses; - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_return_val_if_fail (s_ip4, NM_ACT_STAGE_RETURN_FAILURE); - num_addresses = nm_setting_ip_config_get_num_addresses (s_ip4); - - if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { - ret = dhcp4_start (self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); - return ret; - } - } else { - g_return_val_if_fail (num_addresses != 0, NM_ACT_STAGE_RETURN_FAILURE); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } - - if (num_addresses) { - config = _ip4_config_new (self); - nm_ip4_config_merge_setting (config, - nm_connection_get_setting_ip4_config (connection), - NM_SETTING_CONNECTION_MDNS_DEFAULT, - NM_SETTING_CONNECTION_LLMNR_DEFAULT, - nm_device_get_route_table (self, AF_INET, TRUE), - nm_device_get_route_metric (self, AF_INET)); - configs = g_new0 (NMIP4Config *, 2); - configs[0] = config; - ipv4_dad_start (self, configs, ipv4_manual_method_apply); - } - } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { - ret = ipv4ll_start (self); - if (ret == NM_ACT_STAGE_RETURN_FAILURE) - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); - } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { - if (out_config) { - *out_config = shared4_new_config (self, connection); - if (*out_config) { - priv->dnsmasq_manager = nm_dnsmasq_manager_new (nm_device_get_ip_iface (self)); - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } else - g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE); - } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) - ret = NM_ACT_STAGE_RETURN_SUCCESS; - else - _LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method); - - return ret; -} - /*****************************************************************************/ /* DHCPv6 stuff */ @@ -7944,6 +7882,7 @@ dhcp6_cleanup (NMDevice *self, CleanupType cleanup_type, gboolean release) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->dhcp6.was_active = FALSE; priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; applied_config_clear (&priv->dhcp6.ip6_config); g_clear_pointer (&priv->dhcp6.event_id, g_free); @@ -8017,12 +7956,14 @@ dhcp6_grace_period_expired (gpointer user_data) } static void -dhcp6_fail (NMDevice *self, gboolean timeout) +dhcp6_fail (NMDevice *self, NMDhcpState dhcp_state) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); gboolean is_dhcp_managed; - _LOGD (LOGD_DHCP6, "DHCPv6 failed%s", timeout ? " (timeout)" : ""); + _LOGD (LOGD_DHCP6, "DHCPv6 failed (ip_state %s, was_active %d)", + _ip_state_to_string (priv->ip_state_6), + priv->dhcp6.was_active); is_dhcp_managed = (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED); @@ -8030,18 +7971,21 @@ dhcp6_fail (NMDevice *self, gboolean timeout) /* Keep client running if there are static addresses configured * on the interface. */ - if ( priv->ip6_state == IP_DONE + if ( priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE && priv->con_ip_config_6 && nm_ip6_config_get_num_addresses (priv->con_ip_config_6)) goto clear_config; - /* Fail the method in case of timeout or failure during initial - * configuration. + /* Fail the method when one of the following is true: + * 1) the DHCP client terminated: it does not make sense to start a grace + * period without a client running; + * 2) we failed to get an initial lease AND the client was + * not active before. */ - if ( !priv->dhcp6.was_active - && (timeout || priv->ip6_state == IP_CONF)) { + if ( dhcp_state == NM_DHCP_STATE_TERMINATED + || (!priv->dhcp6.was_active && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF)) { dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - nm_device_activate_schedule_ip6_config_timeout (self); + nm_device_activate_schedule_ip_config_timeout (self, AF_INET6); return; } @@ -8061,8 +8005,8 @@ dhcp6_fail (NMDevice *self, gboolean timeout) } else { /* not a hard failure; just live with the RA info */ dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip6_state == IP_CONF) - nm_device_activate_schedule_ip6_config_result (self); + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); } return; @@ -8076,21 +8020,6 @@ clear_config: } static void -dhcp6_timeout (NMDevice *self, NMDhcpClient *client) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) - dhcp6_fail (self, TRUE); - else { - /* not a hard failure; just live with the RA info */ - dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); - if (priv->ip6_state == IP_CONF) - nm_device_activate_schedule_ip6_config_result (self); - } -} - -static void dhcp6_state_changed (NMDhcpClient *client, NMDhcpState state, NMIP6Config *ip6_config, @@ -8136,28 +8065,35 @@ dhcp6_state_changed (NMDhcpClient *client, /* After long time we have been able to renew the lease: * update the ip state */ - if (priv->ip6_state == IP_FAIL) - _set_ip_state (self, AF_INET6, IP_CONF); + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_CONF); - if (priv->ip6_state == IP_CONF) { + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { if (!applied_config_get_current (&priv->dhcp6.ip6_config)) { nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_DHCP_FAILED); break; } - nm_device_activate_schedule_ip6_config_result (self); - } else if (priv->ip6_state == IP_DONE) + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); + } else if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) if (!dhcp6_lease_change (self)) - dhcp6_fail (self, FALSE); + dhcp6_fail (self, state); break; case NM_DHCP_STATE_TIMEOUT: - dhcp6_timeout (self, client); + if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_MANAGED) + dhcp6_fail (self, state); + else { + /* not a hard failure; just live with the RA info */ + dhcp6_cleanup (self, CLEANUP_TYPE_DECONFIGURE, FALSE); + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); + } break; case NM_DHCP_STATE_EXPIRE: /* Ignore expiry before we even have a lease (NAK, old lease, etc) */ - if (priv->ip6_state != IP_CONF) - dhcp6_fail (self, FALSE); + if (priv->ip_state_6 != NM_DEVICE_IP_STATE_CONF) + dhcp6_fail (self, state); break; - case NM_DHCP_STATE_DONE: + case NM_DHCP_STATE_TERMINATED: /* In IPv6 info-only mode, the client doesn't handle leases so it * may exit right after getting a response from the server. That's * normal. In that case we just ignore the exit. @@ -8165,8 +8101,9 @@ dhcp6_state_changed (NMDhcpClient *client, if (priv->dhcp6.mode == NM_NDISC_DHCP_LEVEL_OTHERCONF) break; /* fall through */ + case NM_DHCP_STATE_DONE: case NM_DHCP_STATE_FAIL: - dhcp6_fail (self, FALSE); + dhcp6_fail (self, state); break; default: break; @@ -8312,7 +8249,8 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole if (!duid) { duid_default = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "ipv6.dhcp-duid", self); + NM_CON_DEFAULT ("ipv6.dhcp-duid"), + self); duid = duid_default; if (!duid) duid = "lease"; @@ -8359,7 +8297,7 @@ dhcp6_get_duid (NMDevice *self, NMConnection *connection, GBytes *hwaddr, gboole /* preferably, we would salt the checksum differently for each @duid type. We missed * to do that initially, so most types use the DEFAULT_SALT. * - * Implemenations that are added later, should use a distinct salt instead, + * Implementations that are added later, should use a distinct salt instead, * like "stable-ll"/"stable-llt" with ARPHRD_INFINIBAND below. */ const guint32 DEFAULT_SALT = 670531087u; nm_auto_free_checksum GChecksum *sum = NULL; @@ -8664,6 +8602,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMPlatformIP6Address address = *subnet; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!applied_config_get_current (&priv->ac_ip6_config)) applied_config_init_new (&priv->ac_ip6_config, self, AF_INET6); @@ -8673,7 +8612,7 @@ nm_device_use_ip6_subnet (NMDevice *self, const NMPlatformIP6Address *subnet) applied_config_add_address (&priv->ac_ip6_config, NM_PLATFORM_IP_ADDRESS_CAST (&address)); _LOGD (LOGD_IP6, "ipv6-pd: using %s address (preferred for %u seconds)", - nm_utils_inet6_ntop (&address.address, NULL), + nm_utils_inet6_ntop (&address.address, sbuf), subnet->preferred); /* This also updates the ndisc if there are actual changes. */ @@ -8727,7 +8666,7 @@ linklocal6_failed (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); nm_clear_g_source (&priv->linklocal6_timeout_id); - nm_device_activate_schedule_ip6_config_timeout (self); + nm_device_activate_schedule_ip_config_timeout (self, AF_INET6); } static gboolean @@ -8765,20 +8704,20 @@ linklocal6_check_complete (NMDevice *self) connection = nm_device_get_applied_connection (self); g_assert (connection); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_device_get_effective_ip_config_method (self, AF_INET6); _LOGD (LOGD_DEVICE, "linklocal6: waiting for link-local addresses successful, continue with method %s", method); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_SHARED)) addrconf6_start_with_link_ready (self); - else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { + else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { if (!dhcp6_start_with_link_ready (self, connection)) { /* Time out IPv6 instead of failing the entire activation */ - nm_device_activate_schedule_ip6_config_timeout (self); + nm_device_activate_schedule_ip_config_timeout (self, AF_INET6); } - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) - nm_device_activate_schedule_ip6_config_result (self); + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); else g_return_if_fail (FALSE); } @@ -8792,6 +8731,7 @@ check_and_add_ipv6ll_addr (NMDevice *self) NMSettingIP6Config *s_ip6 = NULL; GError *error = NULL; const char *addr_type; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; if (!priv->ipv6ll_handle) return; @@ -8852,7 +8792,8 @@ check_and_add_ipv6ll_addr (NMDevice *self) addr_type = "EUI-64"; } - _LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", addr_type, nm_utils_inet6_ntop (&lladdr, NULL)); + _LOGD (LOGD_IP6, "linklocal6: generated %s IPv6LL address %s", + addr_type, nm_utils_inet6_ntop (&lladdr, sbuf)); priv->ipv6ll_has = TRUE; priv->ipv6ll_addr = lladdr; ip_config_merge_and_apply (self, AF_INET6, TRUE); @@ -8862,8 +8803,6 @@ static gboolean linklocal6_start (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMConnection *connection; - const char *method; nm_clear_g_source (&priv->linklocal6_timeout_id); @@ -8873,11 +8812,8 @@ linklocal6_start (NMDevice *self) | NM_PLATFORM_MATCH_WITH_ADDRSTATE_NORMAL)) return TRUE; - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", method); + _LOGD (LOGD_DEVICE, "linklocal6: starting IPv6 with method '%s', but the device has no link-local addresses configured. Wait.", + nm_device_get_effective_ip_config_method (self, AF_INET6)); check_and_add_ipv6ll_addr (self); @@ -8925,19 +8861,23 @@ nm_device_get_configured_mtu_from_connection (NMDevice *self, if (setting_type == NM_TYPE_SETTING_WIRED) { if (setting) mtu = nm_setting_wired_get_mtu (NM_SETTING_WIRED (setting)); - global_property_name = "ethernet.mtu"; + global_property_name = NM_CON_DEFAULT ("ethernet.mtu"); } else if (setting_type == NM_TYPE_SETTING_WIRELESS) { if (setting) mtu = nm_setting_wireless_get_mtu (NM_SETTING_WIRELESS (setting)); - global_property_name = "wifi.mtu"; + global_property_name = NM_CON_DEFAULT ("wifi.mtu"); } else if (setting_type == NM_TYPE_SETTING_INFINIBAND) { if (setting) mtu = nm_setting_infiniband_get_mtu (NM_SETTING_INFINIBAND (setting)); - global_property_name = "infiniband.mtu"; + global_property_name = NM_CON_DEFAULT ("infiniband.mtu"); } else if (setting_type == NM_TYPE_SETTING_IP_TUNNEL) { if (setting) mtu = nm_setting_ip_tunnel_get_mtu (NM_SETTING_IP_TUNNEL (setting)); - global_property_name = "ip-tunnel.mtu"; + global_property_name = NM_CON_DEFAULT ("ip-tunnel.mtu"); + } else if (setting_type == NM_TYPE_SETTING_WIREGUARD) { + if (setting) + mtu = nm_setting_wireguard_get_mtu (NM_SETTING_WIREGUARD (setting)); + global_property_name = NM_CON_DEFAULT ("wireguard.mtu"); } else g_return_val_if_reached (0); @@ -9017,7 +8957,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) { guint32 mtu = 0; - /* preferably, get the MTU from explict user-configuration. + /* preferably, get the MTU from explicit user-configuration. * Only if that fails, look at the current @config (which contains * MTUs from DHCP/PPP) or maybe fallback to a device-specific MTU. */ @@ -9052,7 +8992,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) if (mtu_desired && mtu_desired < 1280) { NMSettingIPConfig *s_ip6; - s_ip6 = (NMSettingIPConfig *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); + s_ip6 = nm_device_get_applied_setting (self, NM_TYPE_SETTING_IP6_CONFIG); if ( s_ip6 && !NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { @@ -9107,7 +9047,7 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) #define _IP6_MTU_SYS() \ ({ \ if (!ip6_mtu_sysctl.initialized) { \ - ip6_mtu_sysctl.value = nm_device_ipv6_sysctl_get_uint32 (self, "mtu", 0); \ + ip6_mtu_sysctl.value = nm_device_sysctl_ip_conf_get_int_checked (self, AF_INET6, "mtu", 10, 0, G_MAXUINT32, 0); \ ip6_mtu_sysctl.initialized = TRUE; \ } \ ip6_mtu_sysctl.value; \ @@ -9124,7 +9064,10 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) } if (mtu_desired && mtu_desired != mtu_plat) { - if (nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired) == NM_PLATFORM_ERROR_CANT_SET_MTU) { + int r; + + r = nm_platform_link_set_mtu (nm_device_get_platform (self), ifindex, mtu_desired); + if (r == -NME_PL_CANT_SET_MTU) { anticipated_failure = TRUE; success = FALSE; _LOGW (LOGD_DEVICE, "mtu: failure to set MTU. %s", @@ -9138,8 +9081,8 @@ _commit_mtu (NMDevice *self, const NMIP4Config *config) } if (ip6_mtu && ip6_mtu != _IP6_MTU_SYS ()) { - if (!nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { + if (!nm_device_sysctl_ip_conf_set (self, AF_INET6, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) ip6_mtu))) { int errsv = errno; _NMLOG (anticipated_failure && errsv == EINVAL ? LOGL_DEBUG : LOGL_WARN, @@ -9281,7 +9224,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_HOP_LIMIT) - nm_platform_sysctl_set_ip6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit); + nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (nm_device_get_platform (self), nm_device_get_ip_iface (self), rdata->hop_limit); if (changed & NM_NDISC_CONFIG_MTU) { if (priv->ip6_mtu != rdata->mtu) { @@ -9290,7 +9233,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } } - nm_device_activate_schedule_ip6_config_result (self); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); } static void @@ -9304,7 +9247,7 @@ ndisc_ra_timeout (NMNDisc *ndisc, NMDevice *self) */ _LOGD (LOGD_IP6, "timed out waiting for IPv6 router advertisement"); - if (priv->ip6_state == IP_CONF) { + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { /* If RA is our only source of addressing information and we don't * ever receive one, then time out IPv6. But if there is other * IPv6 configuration, like manual IPv6 addresses or external IPv6 @@ -9317,9 +9260,9 @@ ndisc_ra_timeout (NMNDisc *ndisc, NMDevice *self) && nm_ip6_config_find_first_address (priv->ip_config_6, NM_PLATFORM_MATCH_WITH_ADDRTYPE_NORMAL | NM_PLATFORM_MATCH_WITH_ADDRSTATE__ANY)) - nm_device_activate_schedule_ip6_config_result (self); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); else - nm_device_activate_schedule_ip6_config_timeout (self); + nm_device_activate_schedule_ip_config_timeout (self, AF_INET6); } } @@ -9337,7 +9280,7 @@ addrconf6_start_with_link_ready (NMDevice *self) } else { /* Don't abort the addrconf at this point -- if ndisc needs the iid * it will notice this itself. */ - _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 adddress creation may fail"); + _LOGI (LOGD_IP6, "addrconf6: no interface identifier; IPv6 address creation may fail"); } /* Apply any manual configuration before starting RA */ @@ -9348,15 +9291,15 @@ addrconf6_start_with_link_ready (NMDevice *self) switch (nm_ndisc_get_node_type (priv->ndisc)) { case NM_NDISC_NODE_TYPE_HOST: /* Accepting prefixes from discovered routers. */ - nm_device_ipv6_sysctl_set (self, "accept_ra", "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "1"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); break; case NM_NDISC_NODE_TYPE_ROUTER: /* We're the router. */ - nm_device_ipv6_sysctl_set (self, "forwarding", "1"); - nm_device_activate_schedule_ip6_config_result (self); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "1"); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); priv->needs_ip6_subnet = TRUE; g_signal_emit (self, signals[IP6_SUBNET_NEEDED], 0); break; @@ -9375,22 +9318,17 @@ addrconf6_start_with_link_ready (NMDevice *self) ndisc_set_router_config (priv->ndisc, self); nm_ndisc_start (priv->ndisc); + priv->ndisc_started = TRUE; return; } static NMNDiscNodeType ndisc_node_type (NMDevice *self) { - NMConnection *connection; - - connection = nm_device_get_applied_connection (self); - g_assert (connection); - - if (strcmp (nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG), - NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) + if (nm_streq (nm_device_get_effective_ip_config_method (self, AF_INET6), + NM_SETTING_IP4_CONFIG_METHOD_SHARED)) return NM_NDISC_NODE_TYPE_ROUTER; - else - return NM_NDISC_NODE_TYPE_HOST; + return NM_NDISC_NODE_TYPE_HOST; } static gboolean @@ -9459,6 +9397,7 @@ addrconf6_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + priv->ndisc_started = FALSE; nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_changed_id); nm_clear_g_signal_handler (priv->ndisc, &priv->ndisc_timeout_id); @@ -9472,34 +9411,36 @@ addrconf6_cleanup (NMDevice *self) /*****************************************************************************/ -static const char *ip6_properties_to_save[] = { - "accept_ra", - "accept_ra_defrtr", - "accept_ra_pinfo", - "accept_ra_rtr_pref", - "forwarding", - "disable_ipv6", - "hop_limit", - "use_tempaddr", -}; - static void save_ip6_properties (NMDevice *self) { + static const char *const ip6_properties_to_save[] = { + "accept_ra", + "accept_ra_defrtr", + "accept_ra_pinfo", + "accept_ra_rtr_pref", + "forwarding", + "disable_ipv6", + "hop_limit", + "use_tempaddr", + }; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *ifname = nm_device_get_ip_iface (self); + NMPlatform *platform = nm_device_get_platform (self); + const char *ifname; char *value; int i; g_hash_table_remove_all (priv->ip6_saved_properties); - if (!nm_device_get_ip_ifindex (self)) + ifname = nm_device_get_ip_iface_from_platform (self); + if (!ifname) return; for (i = 0; i < G_N_ELEMENTS (ip6_properties_to_save); i++) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - - value = nm_platform_sysctl_get (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, ifname, ip6_properties_to_save[i]))); + value = nm_platform_sysctl_ip_conf_get (platform, + AF_INET6, + ifname, + ip6_properties_to_save[i]); if (value) { g_hash_table_insert (priv->ip6_saved_properties, (char *) ip6_properties_to_save[i], @@ -9521,24 +9462,23 @@ restore_ip6_properties (NMDevice *self) if ( priv->ipv6ll_handle && nm_streq (key, "disable_ipv6")) continue; - nm_device_ipv6_sysctl_set (self, key, value); + nm_device_sysctl_ip_conf_set (self, AF_INET6, key, value); } } -static inline void +static void set_disable_ipv6 (NMDevice *self, const char *value) { /* We only touch disable_ipv6 when NM is not managing the IPv6LL address */ if (!NM_DEVICE_GET_PRIVATE (self)->ipv6ll_handle) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", value); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", value); } -static inline void +static void set_nm_ipv6ll (NMDevice *self, gboolean enable) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int ifindex = nm_device_get_ip_ifindex (self); - char *value; if (!nm_platform_check_kernel_support (nm_device_get_platform (self), NM_PLATFORM_KERNEL_SUPPORT_USER_IPV6LL)) @@ -9546,32 +9486,34 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) priv->ipv6ll_handle = enable; if (ifindex > 0) { - NMPlatformError plerr; const char *detail = enable ? "enable" : "disable"; + int r; _LOGD (LOGD_IP6, "will %s userland IPv6LL", detail); - plerr = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { - _NMLOG (( plerr == NM_PLATFORM_ERROR_NOT_FOUND - || plerr == NM_PLATFORM_ERROR_OPNOTSUPP) ? LOGL_DEBUG : LOGL_WARN, + r = nm_platform_link_set_user_ipv6ll_enabled (nm_device_get_platform (self), ifindex, enable); + if (r < 0) { + _NMLOG ( NM_IN_SET (r, -NME_PL_NOT_FOUND, + -NME_PL_OPNOTSUPP) + ? LOGL_DEBUG + : LOGL_WARN, LOGD_IP6, "failed to %s userspace IPv6LL address handling (%s)", detail, - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); } if (enable) { - char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; + gs_free char *value = NULL; /* Bounce IPv6 to ensure the kernel stops IPv6LL address generation */ - value = nm_platform_sysctl_get (nm_device_get_platform (self), - NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (AF_INET6, buf, nm_device_get_ip_iface (self), "disable_ipv6"))); - if (g_strcmp0 (value, "0") == 0) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); - g_free (value); + value = nm_device_sysctl_ip_conf_get (self, + AF_INET6, + "disable_ipv6"); + if (nm_streq0 (value, "0")) + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); /* Ensure IPv6 is enabled */ - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "0"); } } @@ -9616,7 +9558,7 @@ _ip6_privacy_get (NMDevice *self) /* 2.) use the default value from the configuration. */ ip6_privacy = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "ipv6.ip6-privacy", + NM_CON_DEFAULT ("ipv6.ip6-privacy"), self, NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, @@ -9632,150 +9574,228 @@ _ip6_privacy_get (NMDevice *self) * Instead of reading static config files in /etc, just read the current sysctl value. * This works as NM only writes to "/proc/sys/net/ipv6/conf/IFNAME/use_tempaddr", but leaves * the "default" entry untouched. */ - ip6_privacy = nm_platform_sysctl_get_int32 (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + ip6_privacy = nm_platform_sysctl_get_int32 (nm_device_get_platform (self), + NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/default/use_tempaddr"), + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); return _ip6_privacy_clamp (ip6_privacy); } /*****************************************************************************/ static gboolean -ip6_requires_slaves (NMConnection *connection) +ip_requires_slaves (NMDevice *self, int addr_family) { const char *method; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_device_get_effective_ip_config_method (self, addr_family); + + if (addr_family == AF_INET) + return nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); /* SLAAC, DHCP, and Link-Local depend on connectivity (and thus slaves) * to complete addressing. SLAAC and DHCP need a peer to provide a prefix. */ - return strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0; + return NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); } static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *self, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *self, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { + const gboolean IS_IPv4 = (addr_family == AF_INET); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMConnection *connection; + NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; const char *method; - NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; - const char *ip6_privacy_str = "0"; + + nm_assert_addr_family (addr_family); connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - if ( connection_ip6_method_requires_carrier (connection, NULL) + if ( connection_ip_method_requires_carrier (connection, addr_family, NULL) && nm_device_is_master (self) && !priv->carrier) { - _LOGI (LOGD_IP6 | LOGD_DEVICE, - "IPv6 config waiting until carrier is on"); + _LOGI (LOGD_IP | LOGD_DEVICE, + "IPv%c config waiting until carrier is on", + nm_utils_addr_family_to_char (addr_family)); return NM_ACT_STAGE_RETURN_IP_WAIT; } - if (nm_device_is_master (self) && ip6_requires_slaves (connection)) { + if ( nm_device_is_master (self) + && ip_requires_slaves (self, addr_family)) { /* If the master has no ready slaves, and depends on slaves for - * a successful IPv6 attempt, then postpone IPv6 addressing. + * a successful IP configuration attempt, then postpone IP addressing. */ if (!have_any_ready_slaves (self)) { - _LOGI (LOGD_DEVICE | LOGD_IP6, - "IPv6 config waiting until slaves are ready"); + _LOGI (LOGD_DEVICE | LOGD_IP, + "IPv%c config waiting until slaves are ready", + nm_utils_addr_family_to_char (addr_family)); return NM_ACT_STAGE_RETURN_IP_WAIT; } } - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + if (!IS_IPv4) + priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_NONE; - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) { - if ( !priv->master - && !nm_device_sys_iface_state_is_external (self)) { - gboolean ipv6ll_handle_old = priv->ipv6ll_handle; + method = nm_device_get_effective_ip_config_method (self, addr_family); - /* When activating an IPv6 'ignore' connection we need to revert back - * to kernel IPv6LL, but the kernel won't actually assign an address - * to the interface until disable_ipv6 is bounced. - */ - set_nm_ipv6ll (self, FALSE); - if (ipv6ll_handle_old) - nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); - restore_ip6_properties (self); + _LOGD (LOGD_IP | LOGD_DEVICE, "IPv%c config method is %s", + nm_utils_addr_family_to_char (addr_family), method); + + if (IS_IPv4) { + if (NM_IN_STRSET (method, + NM_SETTING_IP4_CONFIG_METHOD_AUTO, + NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) { + NMSettingIPConfig *s_ip4; + NMIP4Config **configs, *config; + guint num_addresses; + + s_ip4 = nm_connection_get_setting_ip4_config (connection); + g_return_val_if_fail (s_ip4, NM_ACT_STAGE_RETURN_FAILURE); + num_addresses = nm_setting_ip_config_get_num_addresses (s_ip4); + + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { + ret = dhcp4_start (self); + if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_DHCP_START_FAILED); + return ret; + } + } else { + g_return_val_if_fail (num_addresses != 0, NM_ACT_STAGE_RETURN_FAILURE); + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } + + if (num_addresses) { + config = nm_device_ip4_config_new (self); + nm_ip4_config_merge_setting (config, + nm_connection_get_setting_ip4_config (connection), + NM_SETTING_CONNECTION_MDNS_DEFAULT, + NM_SETTING_CONNECTION_LLMNR_DEFAULT, + nm_device_get_route_table (self, AF_INET, TRUE), + nm_device_get_route_metric (self, AF_INET)); + configs = g_new0 (NMIP4Config *, 2); + configs[0] = config; + ipv4_dad_start (self, configs, ipv4_manual_method_apply); + } + } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL)) { + ret = ipv4ll_start (self); + if (ret == NM_ACT_STAGE_RETURN_FAILURE) + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_AUTOIP_START_FAILED); + } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { + if (out_config) { + *out_config = shared4_new_config (self, connection); + if (*out_config) { + priv->dnsmasq_manager = nm_dnsmasq_manager_new (nm_device_get_ip_iface (self)); + ret = NM_ACT_STAGE_RETURN_SUCCESS; + } else { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); + ret = NM_ACT_STAGE_RETURN_FAILURE; + } + } else + g_return_val_if_reached (NM_ACT_STAGE_RETURN_FAILURE); + } else if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) + ret = NM_ACT_STAGE_RETURN_SUCCESS; + else + _LOGW (LOGD_IP4, "unhandled IPv4 config method '%s'; will fail", method); + + return ret; + } else { + NMSettingIP6ConfigPrivacy ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN; + const char *ip6_privacy_str = "0"; + + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) { + if ( !priv->master + && !nm_device_sys_iface_state_is_external (self)) { + gboolean ipv6ll_handle_old = priv->ipv6ll_handle; + + /* When activating an IPv6 'ignore' connection we need to revert back + * to kernel IPv6LL, but the kernel won't actually assign an address + * to the interface until disable_ipv6 is bounced. + */ + set_nm_ipv6ll (self, FALSE); + if (ipv6ll_handle_old) + nm_device_sysctl_ip_conf_set (self, AF_INET6, "disable_ipv6", "1"); + restore_ip6_properties (self); + } + return NM_ACT_STAGE_RETURN_IP_DONE; } - return NM_ACT_STAGE_RETURN_IP_DONE; - } - /* Ensure the MTU makes sense. If it was below 1280 the kernel would not - * expose any ipv6 sysctls or allow presence of any addresses on the interface, - * including LL, which * would make it impossible to autoconfigure MTU to a - * correct value. */ - _commit_mtu (self, priv->ip_config_4); + /* Ensure the MTU makes sense. If it was below 1280 the kernel would not + * expose any ipv6 sysctls or allow presence of any addresses on the interface, + * including LL, which * would make it impossible to autoconfigure MTU to a + * correct value. */ + _commit_mtu (self, priv->ip_config_4); - /* Any method past this point requires an IPv6LL address. Use NM-controlled - * IPv6LL if this is not an assumed connection, since assumed connections - * will already have IPv6 set up. - */ - if (!nm_device_sys_iface_state_is_external_or_assume (self)) - set_nm_ipv6ll (self, TRUE); + /* Any method past this point requires an IPv6LL address. Use NM-controlled + * IPv6LL if this is not an assumed connection, since assumed connections + * will already have IPv6 set up. + */ + if (!nm_device_sys_iface_state_is_external_or_assume (self)) + set_nm_ipv6ll (self, TRUE); - /* Re-enable IPv6 on the interface */ - set_disable_ipv6 (self, "0"); + /* Re-enable IPv6 on the interface */ + set_disable_ipv6 (self, "0"); - /* Synchronize external IPv6 configuration with kernel, since - * linklocal6_start() uses the information there to determine if we can - * proceed with the selected method (SLAAC, DHCP, link-local). - */ - nm_platform_process_events (nm_device_get_platform (self)); - g_clear_object (&priv->ext_ip6_config_captured); - priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), - nm_device_get_platform (self), - nm_device_get_ip_ifindex (self), - NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); + /* Synchronize external IPv6 configuration with kernel, since + * linklocal6_start() uses the information there to determine if we can + * proceed with the selected method (SLAAC, DHCP, link-local). + */ + nm_platform_process_events (nm_device_get_platform (self)); + g_clear_object (&priv->ext_ip6_config_captured); + priv->ext_ip6_config_captured = nm_ip6_config_capture (nm_device_get_multi_index (self), + nm_device_get_platform (self), + nm_device_get_ip_ifindex (self), + NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN); - ip6_privacy = _ip6_privacy_get (self); + ip6_privacy = _ip6_privacy_get (self); - if ( strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 - || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { - if (!addrconf6_start (self, ip6_privacy)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0) { - ret = linklocal6_start (self) - ? NM_ACT_STAGE_RETURN_SUCCESS - : NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) { - priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; - if (!dhcp6_start (self, TRUE)) { - /* IPv6 might be disabled; allow IPv4 to proceed */ - ret = NM_ACT_STAGE_RETURN_IP_FAIL; - } else - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL) == 0) { - ret = NM_ACT_STAGE_RETURN_SUCCESS; - } else - _LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); - - if ( ret != NM_ACT_STAGE_RETURN_FAILURE - && !nm_device_sys_iface_state_is_external_or_assume (self)) { - switch (ip6_privacy) { - case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: - case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: - ip6_privacy_str = "0"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: - ip6_privacy_str = "1"; - break; - case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: - ip6_privacy_str = "2"; - break; + if (NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { + if (!addrconf6_start (self, ip6_privacy)) { + /* IPv6 might be disabled; allow IPv4 to proceed */ + ret = NM_ACT_STAGE_RETURN_IP_FAIL; + } else + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL)) { + ret = linklocal6_start (self) + ? NM_ACT_STAGE_RETURN_SUCCESS + : NM_ACT_STAGE_RETURN_POSTPONE; + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP)) { + priv->dhcp6.mode = NM_NDISC_DHCP_LEVEL_MANAGED; + if (!dhcp6_start (self, TRUE)) { + /* IPv6 might be disabled; allow IPv4 to proceed */ + ret = NM_ACT_STAGE_RETURN_IP_FAIL; + } else + ret = NM_ACT_STAGE_RETURN_POSTPONE; + } else if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) + ret = NM_ACT_STAGE_RETURN_SUCCESS; + else + _LOGW (LOGD_IP6, "unhandled IPv6 config method '%s'; will fail", method); + + if ( ret != NM_ACT_STAGE_RETURN_FAILURE + && !nm_device_sys_iface_state_is_external_or_assume (self)) { + switch (ip6_privacy) { + case NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN: + case NM_SETTING_IP6_CONFIG_PRIVACY_DISABLED: + ip6_privacy_str = "0"; + break; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR: + ip6_privacy_str = "1"; + break; + case NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR: + ip6_privacy_str = "2"; + break; + } + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", ip6_privacy_str); } - nm_device_ipv6_sysctl_set (self, "use_tempaddr", ip6_privacy_str); - } - return ret; + return ret; + } } /** @@ -9790,35 +9810,32 @@ nm_device_activate_stage3_ip4_start (NMDevice *self) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActStageReturn ret; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - NMIP4Config *ip4_config = NULL; + gs_unref_object NMIP4Config *ip4_config = NULL; - g_assert (priv->ip4_state == IP_WAIT); + g_assert (priv->ip_state_4 == NM_DEVICE_IP_STATE_WAIT); if (nm_device_sys_iface_state_is_external (self)) { - _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); return TRUE; } - _set_ip_state (self, AF_INET, IP_CONF); - ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip4_config_start (self, &ip4_config, &failure_reason); - if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { - if (!ip4_config) - ip4_config = _ip4_config_new (self); - nm_device_activate_schedule_ip4_config_result (self, ip4_config); - g_object_unref (ip4_config); - } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { - _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_CONF); + ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip_config_start (self, AF_INET, (gpointer *) &ip4_config, &failure_reason); + if (ret == NM_ACT_STAGE_RETURN_SUCCESS) + nm_device_activate_schedule_ip_config_result (self, AF_INET, NM_IP_CONFIG_CAST (ip4_config)); + else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason); return FALSE; } else if (ret == NM_ACT_STAGE_RETURN_IP_FAIL) { /* Activation not wanted */ - _set_ip_state (self, AF_INET, IP_FAIL); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_FAIL); } else if (ret == NM_ACT_STAGE_RETURN_IP_WAIT) { /* Wait for something to try IP config again */ - _set_ip_state (self, AF_INET, IP_WAIT); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_WAIT); } else g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); @@ -9839,37 +9856,37 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; NMIP6Config *ip6_config = NULL; - g_assert (priv->ip6_state == IP_WAIT); + g_assert (priv->ip_state_6 == NM_DEVICE_IP_STATE_WAIT); if (nm_device_sys_iface_state_is_external (self)) { - _set_ip_state (self, AF_INET6, IP_DONE); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); return TRUE; } - _set_ip_state (self, AF_INET6, IP_CONF); - ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip6_config_start (self, &ip6_config, &failure_reason); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_CONF); + ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip_config_start (self, AF_INET6, (gpointer *) &ip6_config, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_SUCCESS) { if (!ip6_config) - ip6_config = _ip6_config_new (self); + ip6_config = nm_device_ip6_config_new (self); /* Here we get a static IPv6 config, like for Shared where it's * autogenerated or from modems where it comes from ModemManager. */ nm_assert (!applied_config_get_current (&priv->ac_ip6_config)); applied_config_init (&priv->ac_ip6_config, ip6_config); - nm_device_activate_schedule_ip6_config_result (self); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); } else if (ret == NM_ACT_STAGE_RETURN_IP_DONE) { - _set_ip_state (self, AF_INET6, IP_DONE); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, failure_reason); return FALSE; } else if (ret == NM_ACT_STAGE_RETURN_IP_FAIL) { /* Activation not wanted */ - _set_ip_state (self, AF_INET6, IP_FAIL); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_FAIL); } else if (ret == NM_ACT_STAGE_RETURN_IP_WAIT) { /* Wait for something to try IP config again */ - _set_ip_state (self, AF_INET6, IP_WAIT); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_WAIT); } else g_assert (ret == NM_ACT_STAGE_RETURN_POSTPONE); @@ -9885,8 +9902,10 @@ nm_device_activate_stage3_ip6_start (NMDevice *self) static void activate_stage3_ip_config_start (NMDevice *self) { - _set_ip_state (self, AF_INET, IP_WAIT); - _set_ip_state (self, AF_INET6, IP_WAIT); + int ifindex; + + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_WAIT); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_WAIT); _active_connection_set_state_flags (self, NM_ACTIVATION_STATE_FLAG_LAYER2_READY); @@ -9894,7 +9913,8 @@ activate_stage3_ip_config_start (NMDevice *self) nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE); /* Device should be up before we can do anything with it */ - if (!nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self))) + if ( (ifindex = nm_device_get_ip_ifindex (self)) > 0 + && !nm_platform_link_is_up (nm_device_get_platform (self), ifindex)) _LOGW (LOGD_DEVICE, "interface %s not up for IP configuration", nm_device_get_ip_iface (self)); /* IPv4 */ @@ -9940,7 +9960,7 @@ fw_change_zone_cb (NMFirewallManager *firewall_manager, break; case FIREWALL_STATE_WAIT_IP_CONFIG: priv->fw_state = FIREWALL_STATE_INITIALIZED; - if (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE) + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) nm_device_start_ip_check (self); break; case FIREWALL_STATE_INITIALIZED: @@ -10017,15 +10037,21 @@ nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self) } static NMActStageReturn -act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *out_failure_reason) +act_stage4_ip_config_timeout (NMDevice *self, + int addr_family, + NMDeviceStateReason *out_failure_reason) { - if (!get_ip_config_may_fail (self, AF_INET)) { + nm_assert_addr_family (addr_family); + + if (!get_ip_config_may_fail (self, addr_family)) { NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); return NM_ACT_STAGE_RETURN_FAILURE; } + return NM_ACT_STAGE_RETURN_SUCCESS; } + /* * nm_device_activate_stage4_ip4_config_timeout * @@ -10033,12 +10059,12 @@ act_stage4_ip4_config_timeout (NMDevice *self, NMDeviceStateReason *out_failure_ * */ static void -activate_stage4_ip4_config_timeout (NMDevice *self) +activate_stage4_ip_config_timeout_4 (NMDevice *self) { NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip4_config_timeout (self, &failure_reason); + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip_config_timeout (self, AF_INET6, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) return; else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { @@ -10047,54 +10073,35 @@ activate_stage4_ip4_config_timeout (NMDevice *self) } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - _set_ip_state (self, AF_INET, IP_FAIL); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_FAIL); check_ip_state (self, FALSE, TRUE); } -/* - * nm_device_activate_schedule_ip4_config_timeout - * - * Deal with a timeout of the IPv4 configuration - * - */ void -nm_device_activate_schedule_ip4_config_timeout (NMDevice *self) +nm_device_activate_schedule_ip_config_timeout (NMDevice *self, + int addr_family) { NMDevicePrivate *priv; + const gboolean IS_IPv4 = (addr_family == AF_INET); g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request.obj); - activation_source_schedule (self, activate_stage4_ip4_config_timeout, AF_INET); -} - -static NMActStageReturn -act_stage4_ip6_config_timeout (NMDevice *self, NMDeviceStateReason *out_failure_reason) -{ - if (!get_ip_config_may_fail (self, AF_INET6)) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); - return NM_ACT_STAGE_RETURN_FAILURE; - } + g_return_if_fail (priv->act_request.obj); - return NM_ACT_STAGE_RETURN_SUCCESS; + activation_source_schedule (self, activate_stage4_ip_config_timeout_x[IS_IPv4], addr_family); } -/* - * activate_stage4_ip6_config_timeout - * - * Time out on retrieving the IPv6 config. - * - */ static void -activate_stage4_ip6_config_timeout (NMDevice *self) +activate_stage4_ip_config_timeout_6 (NMDevice *self) { NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip6_config_timeout (self, &failure_reason); + ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip_config_timeout (self, AF_INET6, &failure_reason); if (ret == NM_ACT_STAGE_RETURN_POSTPONE) return; if (ret == NM_ACT_STAGE_RETURN_FAILURE) { @@ -10103,30 +10110,11 @@ activate_stage4_ip6_config_timeout (NMDevice *self) } g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - _set_ip_state (self, AF_INET6, IP_FAIL); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_FAIL); check_ip_state (self, FALSE, TRUE); } -/* - * nm_device_activate_schedule_ip6_config_timeout - * - * Deal with a timeout of the IPv6 configuration - * - */ -void -nm_device_activate_schedule_ip6_config_timeout (NMDevice *self) -{ - NMDevicePrivate *priv; - - g_return_if_fail (NM_IS_DEVICE (self)); - - priv = NM_DEVICE_GET_PRIVATE (self); - g_return_if_fail (priv->act_request.obj); - - activation_source_schedule (self, activate_stage4_ip6_config_timeout, AF_INET6); -} - static gboolean share_init (NMDevice *self, GError **error) { @@ -10141,9 +10129,9 @@ share_init (NMDevice *self, GError **error) } else if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_forward"), "1")) { errsv = errno; _LOGD (LOGD_SHARING, "share: error enabling IPv4 forwarding: (%d) %s", - errsv, g_strerror (errsv)); + errsv, nm_strerror_native (errsv)); g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "cannot set ipv4/ip_forward: %s", g_strerror (errsv)); + "cannot set ipv4/ip_forward: %s", nm_strerror_native (errsv)); return FALSE; } @@ -10152,7 +10140,7 @@ share_init (NMDevice *self, GError **error) } else if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv4/ip_dynaddr"), "1")) { errsv = errno; _LOGD (LOGD_SHARING, "share: error enabling dynamic addresses: (%d) %s", - errsv, strerror (errsv)); + errsv, nm_strerror_native (errsv)); } for (iter = modules; *iter; iter++) @@ -10179,6 +10167,9 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) const NMPlatformIP4Address *ip4_addr = NULL; const char *ip_iface; GError *local = NULL; + NMConnection *conn; + NMSettingConnection *s_con; + gboolean announce_android_metered; g_return_val_if_fail (config, FALSE); @@ -10200,7 +10191,7 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) return FALSE; req = nm_device_get_act_request (self); - g_assert (req); + g_return_val_if_fail (req, FALSE); netmask = _nm_utils_ip4_prefix_to_netmask (ip4_addr->plen); nm_utils_inet4_ntop (netmask, str_mask); @@ -10221,7 +10212,35 @@ start_sharing (NMDevice *self, NMIP4Config *config, GError **error) nm_act_request_set_shared (req, TRUE); - if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, config, &local)) { + conn = nm_act_request_get_applied_connection (req); + s_con = nm_connection_get_setting_connection (conn); + + switch (nm_setting_connection_get_metered (s_con)) { + case NM_METERED_YES: + /* honor the metered flag. Note that reapply on the device does not affect + * the metered setting. This is different from other profiles, where the + * metered flag of an activated profile can be changed (reapplied). */ + announce_android_metered = TRUE; + break; + case NM_METERED_UNKNOWN: + /* we pick up the current value and announce it. But again, we cannot update + * the announced setting without restarting dnsmasq. That means, if the default + * route changes w.r.t. being metered, then the shared connection does not get + * updated before reactivating. */ + announce_android_metered = NM_IN_SET (nm_manager_get_metered (nm_manager_get ()), + NM_METERED_YES, + NM_METERED_GUESS_YES); + break; + default: + announce_android_metered = FALSE; + break; + } + + + if (!nm_dnsmasq_manager_start (priv->dnsmasq_manager, + config, + announce_android_metered, + &local)) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "could not start dnsmasq due to %s", local->message); g_error_free (local); @@ -10240,10 +10259,7 @@ arp_cleanup (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->acd.announcing) { - nm_acd_manager_destroy (priv->acd.announcing); - priv->acd.announcing = NULL; - } + nm_clear_pointer (&priv->acd.announcing, nm_acd_manager_free); } void @@ -10278,7 +10294,11 @@ nm_device_arp_announce (NMDevice *self) if (num == 0) return; - priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), hw_addr, hw_addr_len); + priv->acd.announcing = nm_acd_manager_new (nm_device_get_ip_ifindex (self), + hw_addr, + hw_addr_len, + NULL, + NULL); for (i = 0; i < num; i++) { NMIPAddress *ip = nm_setting_ip_config_get_address (s_ip4, i); @@ -10294,18 +10314,16 @@ nm_device_arp_announce (NMDevice *self) } static void -activate_stage5_ip4_config_result (NMDevice *self) +activate_stage5_ip_config_result_4 (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; const char *method; - NMConnection *connection; int ip_ifindex; + gboolean do_announce = FALSE; req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_applied_connection (req); - g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); @@ -10322,9 +10340,8 @@ activate_stage5_ip4_config_result (NMDevice *self) } /* Start IPv4 sharing if we need it */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET); + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED)) { gs_free_error GError *error = NULL; if (!start_sharing (self, priv->ip_config_4, &error)) { @@ -10346,45 +10363,77 @@ activate_stage5_ip4_config_result (NMDevice *self) NULL, NULL, NULL); } - nm_device_arp_announce (self); + /* Send ARP announcements */ + + if (nm_device_is_master (self)) { + CList *iter; + SlaveInfo *info; + + /* Skip announcement if there are no device enslaved, for two reasons: + * 1) the master has a temporary MAC address until the first slave comes + * 2) announcements are going to be dropped anyway without slaves + */ + do_announce = FALSE; + + c_list_for_each (iter, &priv->slaves) { + info = c_list_entry (iter, SlaveInfo, lst_slave); + if (info->slave_is_enslaved) { + do_announce = TRUE; + break; + } + } + } else + do_announce = TRUE; + + if (do_announce) + nm_device_arp_announce (self); + nm_device_remove_pending_action (self, NM_PENDING_ACTION_DHCP4, FALSE); /* Enter the IP_CHECK state if this is the first method to complete */ - _set_ip_state (self, AF_INET, IP_DONE); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); } void -nm_device_activate_schedule_ip4_config_result (NMDevice *self, NMIP4Config *config) +nm_device_activate_schedule_ip_config_result (NMDevice *self, + int addr_family, + NMIPConfig *config) { NMDevicePrivate *priv; + const gboolean IS_IPv4 = (addr_family == AF_INET); g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + g_return_if_fail ( !config + || ( addr_family == AF_INET + && nm_ip_config_get_addr_family (config) == AF_INET)); + priv = NM_DEVICE_GET_PRIVATE (self); - applied_config_init (&priv->dev_ip4_config, config); - activation_source_schedule (self, activate_stage5_ip4_config_result, AF_INET); -} + if (IS_IPv4) { + applied_config_init (&priv->dev_ip_config_4, config); + } else { + /* If IP had previously failed, move it back to NM_DEVICE_IP_STATE_CONF since we + * clearly now have configuration. + */ + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_FAIL) + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_CONF); + } -gboolean -nm_device_activate_ip4_state_in_conf (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_CONF; + activation_source_schedule (self, activate_stage5_ip_config_result_x[IS_IPv4], addr_family); } -gboolean -nm_device_activate_ip4_state_in_wait (NMDevice *self) +NMDeviceIPState +nm_device_activate_get_ip_state (NMDevice *self, + int addr_family) { - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_WAIT; -} + const gboolean IS_IPv4 = (addr_family == AF_INET); -gboolean -nm_device_activate_ip4_state_done (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip4_state == IP_DONE; + g_return_val_if_fail (NM_IS_DEVICE (self), NM_DEVICE_IP_STATE_NONE); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6), NM_DEVICE_IP_STATE_NONE); + + return NM_DEVICE_GET_PRIVATE (self)->ip_state_x[IS_IPv4]; } static void @@ -10407,7 +10456,7 @@ dad6_add_pending_address (NMDevice *self, nm_platform_ip6_address_to_string (pl_addr, NULL, 0)); if (!*dad6_config) - *dad6_config = _ip6_config_new (self); + *dad6_config = nm_device_ip6_config_new (self); nm_ip6_config_add_address (*dad6_config, pl_addr); } @@ -10424,7 +10473,7 @@ dad6_get_pending_addresses (NMDevice *self) NMIP6Config *confs[] = { (NMIP6Config *) applied_config_get_current (&priv->ac_ip6_config), (NMIP6Config *) applied_config_get_current (&priv->dhcp6.ip6_config), priv->con_ip_config_6, - (NMIP6Config *) applied_config_get_current (&priv->wwan_ip_config_6) }; + (NMIP6Config *) applied_config_get_current (&priv->dev2_ip_config_6) }; const NMPlatformIP6Address *addr; NMIP6Config *dad6_config = NULL; NMDedupMultiIter ipconf_iter; @@ -10465,19 +10514,16 @@ dad6_get_pending_addresses (NMDevice *self) } static void -activate_stage5_ip6_config_commit (NMDevice *self) +activate_stage5_ip_config_result_6 (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMActRequest *req; const char *method; - NMConnection *connection; int ip_ifindex; int errsv; req = nm_device_get_act_request (self); g_assert (req); - connection = nm_act_request_get_applied_connection (req); - g_assert (connection); /* Interface must be IFF_UP before IP config can be applied */ ip_ifindex = nm_device_get_ip_ifindex (self); @@ -10491,7 +10537,7 @@ activate_stage5_ip6_config_commit (NMDevice *self) if (ip_config_merge_and_apply (self, AF_INET6, TRUE)) { if ( priv->dhcp6.mode != NM_NDISC_DHCP_LEVEL_NONE - && priv->ip6_state == IP_CONF) { + && priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF) { if (applied_config_get_current (&priv->dhcp6.ip6_config)) { /* If IPv6 wasn't the first IP to complete, and DHCP was used, * then ensure dispatcher scripts get the DHCP lease information. @@ -10509,18 +10555,17 @@ activate_stage5_ip6_config_commit (NMDevice *self) nm_device_remove_pending_action (self, NM_PENDING_ACTION_AUTOCONF6, FALSE); /* Start IPv6 forwarding if we need it */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET6); + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_SHARED)) { if (!nm_platform_sysctl_set (nm_device_get_platform (self), NMP_SYSCTL_PATHID_ABSOLUTE ("/proc/sys/net/ipv6/conf/all/forwarding"), "1")) { errsv = errno; - _LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, strerror (errsv)); + _LOGE (LOGD_SHARING, "share: error enabling IPv6 forwarding: (%d) %s", errsv, nm_strerror_native (errsv)); nm_device_ip_method_failed (self, AF_INET6, NM_DEVICE_STATE_REASON_SHARED_START_FAILED); } } /* Check if we have to wait for DAD */ - if (priv->ip6_state == IP_CONF && !priv->dad6_ip6_config) { + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && !priv->dad6_ip6_config) { if (!priv->carrier && priv->ignore_carrier && get_ip_config_may_fail (self, AF_INET6)) _LOGI (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: carrier missing and ignored, not delaying activation"); else @@ -10529,7 +10574,7 @@ activate_stage5_ip6_config_commit (NMDevice *self) if (priv->dad6_ip6_config) { _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD: awaiting termination"); } else { - _set_ip_state (self, AF_INET6, IP_DONE); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); } } @@ -10539,43 +10584,6 @@ activate_stage5_ip6_config_commit (NMDevice *self) } } -void -nm_device_activate_schedule_ip6_config_result (NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - g_return_if_fail (NM_IS_DEVICE (self)); - - /* If IP had previously failed, move it back to IP_CONF since we - * clearly now have configuration. - */ - if (priv->ip6_state == IP_FAIL) - _set_ip_state (self, AF_INET6, IP_CONF); - - activation_source_schedule (self, activate_stage5_ip6_config_commit, AF_INET6); -} - -gboolean -nm_device_activate_ip6_state_in_conf (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_CONF; -} - -gboolean -nm_device_activate_ip6_state_in_wait (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_WAIT; -} - -gboolean -nm_device_activate_ip6_state_done (NMDevice *self) -{ - g_return_val_if_fail (self != NULL, FALSE); - return NM_DEVICE_GET_PRIVATE (self)->ip6_state == IP_DONE; -} - /*****************************************************************************/ static void @@ -10741,7 +10749,7 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const gboolean IS_IPv4 = (addr_family == AF_INET); - _set_ip_state (self, addr_family, IP_NONE); + _set_ip_state (self, addr_family, NM_DEVICE_IP_STATE_NONE); if (nm_clear_g_source (&priv->queued_ip_config_id_x[IS_IPv4])) { _LOGD (LOGD_DEVICE, "clearing queued IP%c config change", @@ -10753,7 +10761,7 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) arp_cleanup (self); dnsmasq_cleanup (self); ipv4ll_cleanup (self); - g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_destroy); + g_slist_free_full (priv->acd.dad_list, (GDestroyNotify) nm_acd_manager_free); priv->acd.dad_list = NULL; } else { g_slist_free_full (priv->dad6_failed_addrs, (GDestroyNotify) nmp_object_unref); @@ -10766,8 +10774,10 @@ _cleanup_ip_pre (NMDevice *self, int addr_family, CleanupType cleanup_type) } gboolean -_nm_device_hash_check_invalid_keys (GHashTable *hash, const char *setting_name, - GError **error, const char **whitelist) +_nm_device_hash_check_invalid_keys (GHashTable *hash, + const char *setting_name, + GError **error, + const char *const*whitelist) { guint found_whitelisted_keys = 0; guint i; @@ -10841,12 +10851,12 @@ nm_device_reactivate_ip4_config (NMDevice *self, g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->ip4_state != IP_NONE) { + if (priv->ip_state_4 != NM_DEVICE_IP_STATE_NONE) { g_clear_object (&priv->con_ip_config_4); g_clear_object (&priv->ext_ip_config_4); - g_clear_object (&priv->dev_ip4_config.current); - g_clear_object (&priv->wwan_ip_config_4.current); - priv->con_ip_config_4 = _ip4_config_new (self); + g_clear_object (&priv->dev_ip_config_4.current); + g_clear_object (&priv->dev2_ip_config_4.current); + priv->con_ip_config_4 = nm_device_ip4_config_new (self); nm_ip4_config_merge_setting (priv->con_ip_config_4, s_ip4_new, _get_mdns (self), @@ -10863,7 +10873,7 @@ nm_device_reactivate_ip4_config (NMDevice *self, if (!nm_streq0 (method_old, method_new)) { _cleanup_ip_pre (self, AF_INET, CLEANUP_TYPE_DECONFIGURE); - _set_ip_state (self, AF_INET, IP_WAIT); + _set_ip_state (self, AF_INET, NM_DEVICE_IP_STATE_WAIT); if (!nm_device_activate_stage3_ip4_start (self)) _LOGW (LOGD_IP4, "Failed to apply IPv4 configuration"); return; @@ -10882,12 +10892,12 @@ nm_device_reactivate_ip4_config (NMDevice *self, metric_new = nm_setting_ip_config_get_route_metric (s_ip4_new); if (metric_old != metric_new) { - if (priv->dev_ip4_config.orig) { - nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->dev_ip4_config.orig, + if (priv->dev_ip_config_4.orig) { + nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->dev_ip_config_4.orig, nm_device_get_route_metric (self, AF_INET)); } - if (priv->wwan_ip_config_4.orig) { - nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->wwan_ip_config_4.orig, + if (priv->dev2_ip_config_4.orig) { + nm_ip4_config_update_routes_metric ((NMIP4Config *) priv->dev2_ip_config_4.orig, nm_device_get_route_metric (self, AF_INET)); } if (priv->dhcp4.client) { @@ -10913,16 +10923,16 @@ nm_device_reactivate_ip6_config (NMDevice *self, g_return_if_fail (NM_IS_DEVICE (self)); priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->ip6_state != IP_NONE) { + if (priv->ip_state_6 != NM_DEVICE_IP_STATE_NONE) { g_clear_object (&priv->con_ip_config_6); g_clear_object (&priv->ext_ip_config_6); g_clear_object (&priv->ac_ip6_config.current); g_clear_object (&priv->dhcp6.ip6_config.current); - g_clear_object (&priv->wwan_ip_config_6.current); + g_clear_object (&priv->dev2_ip_config_6.current); if ( priv->ipv6ll_handle && !IN6_IS_ADDR_UNSPECIFIED (&priv->ipv6ll_addr)) priv->ipv6ll_has = TRUE; - priv->con_ip_config_6 = _ip6_config_new (self); + priv->con_ip_config_6 = nm_device_ip6_config_new (self); nm_ip6_config_merge_setting (priv->con_ip_config_6, s_ip6_new, nm_device_get_route_table (self, AF_INET6, TRUE), @@ -10937,7 +10947,7 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (!nm_streq0 (method_old, method_new)) { _cleanup_ip_pre (self, AF_INET6, CLEANUP_TYPE_DECONFIGURE); - _set_ip_state (self, AF_INET6, IP_WAIT); + _set_ip_state (self, AF_INET6, NM_DEVICE_IP_STATE_WAIT); if (!nm_device_activate_stage3_ip6_start (self)) _LOGW (LOGD_IP6, "Failed to apply IPv6 configuration"); return; @@ -10946,7 +10956,7 @@ nm_device_reactivate_ip6_config (NMDevice *self, if (s_ip6_old && s_ip6_new) { gint64 metric_old, metric_new; - /* See comment in nm_device_reactivate_ip6_config() */ + /* See comment in nm_device_reactivate_ip4_config() */ metric_old = nm_setting_ip_config_get_route_metric (s_ip6_old); metric_new = nm_setting_ip_config_get_route_metric (s_ip6_new); @@ -10959,8 +10969,8 @@ nm_device_reactivate_ip6_config (NMDevice *self, nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->dhcp6.ip6_config.orig, nm_device_get_route_metric (self, AF_INET6)); } - if (priv->wwan_ip_config_6.orig) { - nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->wwan_ip_config_6.orig, + if (priv->dev2_ip_config_6.orig) { + nm_ip6_config_update_routes_metric ((NMIP6Config *) priv->dev2_ip_config_6.orig, nm_device_get_route_metric (self, AF_INET6)); } if (priv->dhcp6.client) { @@ -11070,7 +11080,6 @@ can_reapply_change (NMDevice *self, const char *setting_name, static void reapply_connection (NMDevice *self, NMConnection *con_old, NMConnection *con_new) { - } /* check_and_reapply_connection: @@ -11469,7 +11478,7 @@ _rt6_temporary_not_available_timeout (gpointer user_data) NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); priv->rt6_temporary_not_available_id = 0; - nm_device_activate_schedule_ip6_config_result (self); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); return G_SOURCE_REMOVE; } @@ -11583,14 +11592,15 @@ disconnect_cb (NMDevice *self, } static void -_clear_queued_act_request (NMDevicePrivate *priv) +_clear_queued_act_request (NMDevicePrivate *priv, + NMActiveConnectionStateReason active_reason) { if (priv->queued_act_request) { gs_unref_object NMActRequest *ac = NULL; ac = g_steal_pointer (&priv->queued_act_request); nm_active_connection_set_state_fail ((NMActiveConnection *) ac, - NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED, + active_reason, NULL); } } @@ -11732,7 +11742,8 @@ _carrier_wait_check_queued_act_request (NMDevice *self) priv->queued_act_request_is_waiting_for_carrier = FALSE; if (!priv->carrier) { _LOGD (LOGD_DEVICE, "Cancel queued activation request as we have no carrier after timeout"); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); } else { gs_unref_object NMActRequest *queued_req = NULL; @@ -11787,7 +11798,9 @@ _carrier_wait_check_act_request_must_queue (NMDevice *self, NMActRequest *req) } void -nm_device_disconnect_active_connection (NMActiveConnection *active) +nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason) { NMDevice *self; NMDevicePrivate *priv; @@ -11795,38 +11808,59 @@ nm_device_disconnect_active_connection (NMActiveConnection *active) g_return_if_fail (NM_IS_ACTIVE_CONNECTION (active)); self = nm_active_connection_get_device (active); - if (!self) { /* hm, no device? Just fail the active connection. */ - nm_active_connection_set_state_fail (active, - NM_ACTIVE_CONNECTION_STATE_REASON_UNKNOWN, - NULL); - return; + goto do_fail; } priv = NM_DEVICE_GET_PRIVATE (self); if (NM_ACTIVE_CONNECTION (priv->queued_act_request) == active) { - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, active_reason); return; } + if (NM_ACTIVE_CONNECTION (priv->act_request.obj) == active) { if (priv->state < NM_DEVICE_STATE_DEACTIVATING) { nm_device_state_changed (self, NM_DEVICE_STATE_DEACTIVATING, - NM_DEVICE_STATE_REASON_NEW_ACTIVATION); + device_reason); } else { - /* it's going down already... */ + /* @active is the current ac of @self, but it's going down already. + * Nothing to do. */ } + return; } + + /* the active connection references this device, but it's neither the + * queued_act_request nor the current act_request. Just set it to fail... */ +do_fail: + nm_active_connection_set_state_fail (active, + active_reason, + NULL); } void nm_device_queue_activation (NMDevice *self, NMActRequest *req) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; gboolean must_queue; + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IS_ACT_REQUEST (req)); + + nm_keep_alive_arm (nm_active_connection_get_keep_alive (NM_ACTIVE_CONNECTION (req))); + + if (nm_active_connection_get_state (NM_ACTIVE_CONNECTION (req)) >= NM_ACTIVE_CONNECTION_STATE_DEACTIVATING) { + /* it's already deactivating. Nothing to do. */ + nm_assert (NM_IN_SET (nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req)), NULL, self)); + return; + } + + nm_assert (self == nm_active_connection_get_device (NM_ACTIVE_CONNECTION (req))); + + priv = NM_DEVICE_GET_PRIVATE (self); + must_queue = _carrier_wait_check_act_request_must_queue (self, req); if ( !priv->act_request.obj @@ -11836,8 +11870,9 @@ nm_device_queue_activation (NMDevice *self, NMActRequest *req) return; } - /* supercede any already-queued request */ - _clear_queued_act_request (priv); + /* supersede any already-queued request */ + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); priv->queued_act_request = g_object_ref (req); priv->queued_act_request_is_waiting_for_carrier = must_queue; @@ -12027,16 +12062,11 @@ nm_device_set_ip_config (NMDevice *self, nm_dbus_object_get_path (NM_DBUS_OBJECT (old_config))); if (IS_IPv4) { /* Device config is invalid if combined config is invalid */ - applied_config_clear (&priv->dev_ip4_config); + applied_config_clear (&priv->dev_ip_config_4); } else priv->needs_ip6_subnet = FALSE; } - if (IS_IPv4 && FALSE /* rp_filter handling is disabled */) { - if (!nm_device_sys_iface_state_is_external_or_assume (self)) - ip4_rp_filter_update (self); - } - if (has_changes) { if (IS_IPv4) @@ -12133,13 +12163,25 @@ nm_device_replace_vpn4_config (NMDevice *self, NMIP4Config *old, NMIP4Config *co } void -nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) +nm_device_set_dev2_ip_config (NMDevice *self, + int addr_family, + NMIPConfig *config) { - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + NMDevicePrivate *priv; + const gboolean IS_IPv4 = (addr_family == AF_INET); - applied_config_init (&priv->wwan_ip_config_4, config); - if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) - _LOGW (LOGD_IP4, "failed to set WWAN IPv4 configuration"); + g_return_if_fail (NM_IS_DEVICE (self)); + g_return_if_fail (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + g_return_if_fail ( !config + || nm_ip_config_get_addr_family (config) == addr_family); + + priv = NM_DEVICE_GET_PRIVATE (self); + + applied_config_init (&priv->dev2_ip_config_x[IS_IPv4], config); + if (!ip_config_merge_and_apply (self, addr_family, TRUE)) { + _LOGW (LOGD_IP, "failed to set extra device IPv%c configuration", + nm_utils_addr_family_to_char (addr_family)); + } } void @@ -12160,16 +12202,6 @@ nm_device_replace_vpn6_config (NMDevice *self, NMIP6Config *old, NMIP6Config *co _LOGW (LOGD_IP6, "failed to set VPN routes for device"); } -void -nm_device_set_wwan_ip6_config (NMDevice *self, NMIP6Config *config) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - - applied_config_init (&priv->wwan_ip_config_6, config); - if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) - _LOGW (LOGD_IP6, "failed to set WWAN IPv6 configuration"); -} - NMDhcp6Config * nm_device_get_dhcp6_config (NMDevice *self) { @@ -12404,7 +12436,7 @@ nm_device_start_ip_check (NMDevice *self) g_return_if_fail (!priv->gw_ping.watch); g_return_if_fail (!priv->gw_ping.timeout); g_return_if_fail (!priv->gw_ping.pid); - g_return_if_fail (priv->ip4_state == IP_DONE || priv->ip6_state == IP_DONE); + g_return_if_fail (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE || priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE); connection = nm_device_get_applied_connection (self); g_assert (connection); @@ -12417,14 +12449,14 @@ nm_device_start_ip_check (NMDevice *self) if (timeout) { const NMPObject *gw; - if (priv->ip_config_4 && priv->ip4_state == IP_DONE) { + if (priv->ip_config_4 && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { gw = nm_ip4_config_best_default_route_get (priv->ip_config_4); if (gw) { nm_utils_inet4_ntop (NMP_OBJECT_CAST_IP4_ROUTE (gw)->gateway, buf); ping_binary = nm_utils_find_helper ("ping", "/usr/bin/ping", NULL); log_domain = LOGD_IP4; } - } else if (priv->ip_config_6 && priv->ip6_state == IP_DONE) { + } else if (priv->ip_config_6 && priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { gw = nm_ip6_config_best_default_route_get (priv->ip_config_6); if (gw) { nm_utils_inet6_ntop (&NMP_OBJECT_CAST_IP6_ROUTE (gw)->gateway, buf); @@ -12564,11 +12596,11 @@ nm_device_bring_up (NMDevice *self, gboolean block, gboolean *no_firmware) _update_ip4_address (self); /* when the link comes up, we must restore IP configuration if necessary. */ - if (priv->ip4_state == IP_DONE) { + if (priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE) { if (!ip_config_merge_and_apply (self, AF_INET, TRUE)) _LOGW (LOGD_IP4, "failed applying IP4 config after bringing link up"); } - if (priv->ip6_state == IP_DONE) { + if (priv->ip_state_6 == NM_DEVICE_IP_STATE_DONE) { if (!ip_config_merge_and_apply (self, AF_INET6, TRUE)) _LOGW (LOGD_IP6, "failed applying IP6 config after bringing link up"); } @@ -12637,6 +12669,7 @@ nm_device_get_firmware_missing (NMDevice *self) static void intersect_ext_config (NMDevice *self, AppliedConfig *config, + gboolean intersect_addresses, gboolean intersect_routes) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); @@ -12653,11 +12686,16 @@ intersect_ext_config (NMDevice *self, ? (NMIPConfig *) priv->ext_ip_config_4 : (NMIPConfig *) priv->ext_ip_config_6; - if (config->current) - nm_ip_config_intersect (config->current, ext, intersect_routes, penalty); - else { + if (config->current) { + nm_ip_config_intersect (config->current, + ext, + intersect_addresses, + intersect_routes, + penalty); + } else { config->current = nm_ip_config_intersect_alloc (config->orig, ext, + intersect_addresses, intersect_routes, penalty); } @@ -12693,15 +12731,16 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config * by the user. */ if (priv->con_ip_config_4) { nm_ip4_config_intersect (priv->con_ip_config_4, priv->ext_ip_config_4, + TRUE, is_up, default_route_metric_penalty_get (self, AF_INET)); } - intersect_ext_config (self, &priv->dev_ip4_config, is_up); - intersect_ext_config (self, &priv->wwan_ip_config_4, is_up); + intersect_ext_config (self, &priv->dev_ip_config_4, TRUE, is_up); + intersect_ext_config (self, &priv->dev2_ip_config_4, TRUE, is_up); for (iter = priv->vpn_configs_4; iter; iter = iter->next) - nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, is_up, 0); + nm_ip4_config_intersect (iter->data, priv->ext_ip_config_4, TRUE, is_up, 0); } /* Remove parts from ext_ip_config_4 to only contain the information that @@ -12711,14 +12750,14 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config nm_ip4_config_subtract (priv->ext_ip_config_4, priv->con_ip_config_4, default_route_metric_penalty_get (self, AF_INET)); } - if (applied_config_get_current (&priv->dev_ip4_config)) { + if (applied_config_get_current (&priv->dev_ip_config_4)) { nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current (&priv->dev_ip4_config), + applied_config_get_current (&priv->dev_ip_config_4), default_route_metric_penalty_get (self, AF_INET)); } - if (applied_config_get_current (&priv->wwan_ip_config_4)) { + if (applied_config_get_current (&priv->dev2_ip_config_4)) { nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_4, - applied_config_get_current (&priv->wwan_ip_config_4), + applied_config_get_current (&priv->dev2_ip_config_4), default_route_metric_penalty_get (self, AF_INET)); } for (iter = priv->vpn_configs_4; iter; iter = iter->next) @@ -12746,15 +12785,16 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config if (priv->con_ip_config_6) { nm_ip6_config_intersect (priv->con_ip_config_6, priv->ext_ip_config_6, is_up, + is_up, default_route_metric_penalty_get (self, AF_INET6)); } - intersect_ext_config (self, &priv->ac_ip6_config, is_up); - intersect_ext_config (self, &priv->dhcp6.ip6_config, is_up); - intersect_ext_config (self, &priv->wwan_ip_config_6, is_up); + intersect_ext_config (self, &priv->ac_ip6_config, is_up, is_up); + intersect_ext_config (self, &priv->dhcp6.ip6_config, is_up, is_up); + intersect_ext_config (self, &priv->dev2_ip_config_6, is_up, is_up); for (iter = priv->vpn_configs_6; iter; iter = iter->next) - nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, is_up, 0); + nm_ip6_config_intersect (iter->data, priv->ext_ip_config_6, is_up, is_up, 0); if ( priv->ipv6ll_has && !nm_ip6_config_lookup_address (priv->ext_ip_config_6, &priv->ipv6ll_addr)) @@ -12778,9 +12818,9 @@ update_ext_ip_config (NMDevice *self, int addr_family, gboolean intersect_config applied_config_get_current (&priv->dhcp6.ip6_config), default_route_metric_penalty_get (self, AF_INET6)); } - if (applied_config_get_current (&priv->wwan_ip_config_6)) { + if (applied_config_get_current (&priv->dev2_ip_config_6)) { nm_ip_config_subtract ((NMIPConfig *) priv->ext_ip_config_6, - applied_config_get_current (&priv->wwan_ip_config_6), + applied_config_get_current (&priv->dev2_ip_config_6), default_route_metric_penalty_get (self, AF_INET6)); } for (iter = priv->vpn_configs_6; iter; iter = iter->next) @@ -12844,9 +12884,7 @@ queued_ip_config_change (NMDevice *self, int addr_family) * update in such case. */ if (activation_source_is_scheduled (self, - IS_IPv4 - ? activate_stage5_ip4_config_result - : activate_stage5_ip6_config_commit, + activate_stage5_ip_config_result_x[IS_IPv4], addr_family)) return G_SOURCE_CONTINUE; @@ -12864,14 +12902,17 @@ queued_ip_config_change (NMDevice *self, int addr_family) if (!IS_IPv4) { NMPlatform *platform; GSList *dad6_failed_addrs, *iter; + const NMPlatformLink *pllink; dad6_failed_addrs = g_steal_pointer (&priv->dad6_failed_addrs); if ( priv->state > NM_DEVICE_STATE_DISCONNECTED && priv->state < NM_DEVICE_STATE_DEACTIVATING + && priv->ifindex > 0 && !nm_device_sys_iface_state_is_external (self) && (platform = nm_device_get_platform (self)) - && nm_platform_link_get (platform, priv->ifindex)) { + && (pllink = nm_platform_link_get (platform, priv->ifindex)) + && (pllink->n_ifi_flags & IFF_UP)) { gboolean need_ipv6ll = FALSE; NMNDiscConfigMap ndisc_config_changed = NM_NDISC_CONFIG_NONE; @@ -12914,29 +12955,22 @@ queued_ip_config_change (NMDevice *self, int addr_family) if (!IS_IPv4) { /* Check if DAD is still pending */ - if ( priv->ip6_state == IP_CONF + if ( priv->ip_state_6 == NM_DEVICE_IP_STATE_CONF && priv->dad6_ip6_config && priv->ext_ip6_config_captured && !nm_ip6_config_has_any_dad_pending (priv->ext_ip6_config_captured, priv->dad6_ip6_config)) { _LOGD (LOGD_DEVICE | LOGD_IP6, "IPv6 DAD terminated"); g_clear_object (&priv->dad6_ip6_config); - _set_ip_state (self, addr_family, IP_DONE); + _set_ip_state (self, addr_family, NM_DEVICE_IP_STATE_DONE); check_ip_state (self, FALSE, TRUE); if (priv->rt6_temporary_not_available) - nm_device_activate_schedule_ip6_config_result (self); + nm_device_activate_schedule_ip_config_result (self, AF_INET6, NULL); } } set_unmanaged_external_down (self, TRUE); - if (IS_IPv4 && FALSE /* rp_filter handling is disabled */) { - if (!nm_device_sys_iface_state_is_external_or_assume (self)) { - priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; - ip4_rp_filter_update (self); - } - } - return G_SOURCE_REMOVE; } @@ -13089,10 +13123,10 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo /* @for_user_request can make the result only ~more~ managed. * If the flags already indicate a managed state for a non-user-request, - * then it is also managed for an explict user-request. + * then it is also managed for an explicit user-request. * * Effectively, this check is redundant, as the code below already - * already ensures that. Still, express this invariant explictly here. */ + * already ensures that. Still, express this invariant explicitly here. */ if (_get_managed_by_flags (flags, mask, FALSE)) return TRUE; @@ -13148,7 +13182,7 @@ _get_managed_by_flags(NMUnmanagedFlags flags, NMUnmanagedFlags mask, gboolean fo /** * nm_device_get_managed: * @self: the #NMDevice - * @for_user_request: whether to check the flags for an explict user-request + * @for_user_request: whether to check the flags for an explicit user-request * * Whether the device is unmanaged according to the unmanaged flags. * @@ -13326,7 +13360,7 @@ _set_unmanaged_flags (NMDevice *self, flags, NM_PRINT_FMT_QUOTED (allow_state_transition, ", reason ", - reason_to_string (reason), + reason_to_string_a (reason), transition_state ? ", transition-state" : "", "")); @@ -13603,7 +13637,7 @@ nm_device_update_metered (NMDevice *self) /* Try to guess a value using the metered flag in IP configuration */ if (value == NM_METERED_INVALID) { if ( priv->ip_config_4 - && priv->ip4_state == IP_DONE + && priv->ip_state_4 == NM_DEVICE_IP_STATE_DONE && nm_ip4_config_get_metered (priv->ip_config_4)) value = NM_METERED_GUESS_YES; } @@ -14101,22 +14135,32 @@ nm_device_remove_pending_action (NMDevice *self, const char *action, gboolean as return FALSE; } -gboolean -nm_device_has_pending_action (NMDevice *self) +const char * +nm_device_has_pending_action_reason (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - if (priv->pending_actions) - return TRUE; + if (priv->pending_actions) { + + if ( !priv->pending_actions->next + && nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED + && nm_streq (priv->pending_actions->data, NM_PENDING_ACTION_CARRIER_WAIT)) { + /* if the device is already in activated state, and the only reason + * why it appears still busy is "carrier-wait", then we are already complete. */ + return NULL; + } + + return priv->pending_actions->data; + } if ( nm_device_is_real (self) && nm_device_get_unmanaged_flags (self, NM_UNMANAGED_PLATFORM_INIT)) { /* as long as the platform link is not yet initialized, we have a pending * action. */ - return TRUE; + return NM_PENDING_ACTION_LINK_INIT; } - return FALSE; + return NULL; } /*****************************************************************************/ @@ -14194,15 +14238,15 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) nm_device_set_ip_config (self, AF_INET6, NULL, TRUE, NULL); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip_config_4); - applied_config_clear (&priv->dev_ip4_config); - applied_config_clear (&priv->wwan_ip_config_4); + applied_config_clear (&priv->dev_ip_config_4); + applied_config_clear (&priv->dev2_ip_config_4); g_clear_object (&priv->ext_ip_config_4); g_clear_object (&priv->ip_config_4); g_clear_object (&priv->con_ip_config_6); applied_config_clear (&priv->ac_ip6_config); g_clear_object (&priv->ext_ip_config_6); g_clear_object (&priv->ext_ip6_config_captured); - applied_config_clear (&priv->wwan_ip_config_6); + applied_config_clear (&priv->dev2_ip_config_6); g_clear_object (&priv->ip_config_6); g_clear_object (&priv->dad6_ip6_config); priv->ipv6ll_has = FALSE; @@ -14242,7 +14286,7 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) delete_on_deactivate_check_and_schedule (self, nm_device_get_ip_ifindex (self)); } - /* ip_iface should be cleared after flushing all routes and addreses, since + /* ip_iface should be cleared after flushing all routes and addresses, since * those are identified by ip_iface, not by iface (which might be a tty * or ATM device). */ @@ -14266,7 +14310,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (reason == NM_DEVICE_STATE_REASON_NOW_MANAGED) _LOGD (LOGD_DEVICE, "preparing device"); else - _LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string (reason), reason); + _LOGD (LOGD_DEVICE, "deactivating device (reason '%s') [%d]", reason_to_string_a (reason), reason); /* Save whether or not we tried IPv6 for later */ priv = NM_DEVICE_GET_PRIVATE (self); @@ -14276,8 +14320,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean /* Turn off kernel IPv6 */ if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { set_disable_ipv6 (self, "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra", "0"); - nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); } /* Call device type-specific deactivation */ @@ -14306,6 +14350,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean /* slave: mark no longer enslaved */ if ( priv->master + && priv->ifindex > 0 && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) nm_device_master_release_one_slave (priv->master, self, FALSE, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED); @@ -14342,8 +14387,8 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (priv->ip6_mtu_initial) { char sbuf[64]; - nm_device_ipv6_sysctl_set (self, "mtu", - nm_sprintf_buf (sbuf, "%u", (unsigned) priv->ip6_mtu_initial)); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "mtu", + nm_sprintf_buf (sbuf, "%u", (unsigned) priv->ip6_mtu_initial)); } } priv->mtu_initial = 0; @@ -14373,7 +14418,7 @@ find_dhcp4_address (NMDevice *self) nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, priv->ip_config_4, &a) { if (a->addr_source == NM_IP_CONFIG_SOURCE_DHCP) - return g_strdup (nm_utils_inet4_ntop (a->address, NULL)); + return nm_utils_inet4_ntop_dup (a->address); } return NULL; } @@ -14398,7 +14443,8 @@ nm_device_spawn_iface_helper (NMDevice *self) return; connection = nm_device_get_applied_connection (self); - g_assert (connection); + + g_return_if_fail (connection); argv = g_ptr_array_sized_new (10); g_ptr_array_set_free_func (argv, g_free); @@ -14432,12 +14478,12 @@ nm_device_spawn_iface_helper (NMDevice *self) dhcp4_address = find_dhcp4_address (self); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) { + method = nm_device_get_effective_ip_config_method (self, AF_INET); + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { NMSettingIPConfig *s_ip4; s_ip4 = nm_connection_get_setting_ip4_config (connection); - g_assert (s_ip4); + nm_assert (s_ip4); g_ptr_array_add (argv, g_strdup ("--priority4")); g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_route_metric (self, AF_INET))); @@ -14455,11 +14501,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (client_id) { g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid")); g_ptr_array_add (argv, - _nm_utils_bin2hexstr_full (g_bytes_get_data (client_id, NULL), - g_bytes_get_size (client_id), - ':', - FALSE, - NULL)); + nm_utils_bin2hexstr_full (g_bytes_get_data (client_id, NULL), + g_bytes_get_size (client_id), + ':', + FALSE, + NULL)); } hostname = nm_dhcp_client_get_hostname (priv->dhcp4.client); @@ -14475,8 +14521,8 @@ nm_device_spawn_iface_helper (NMDevice *self) configured = TRUE; } - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); - if (g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) { + method = nm_utils_get_ip_config_method (connection, AF_INET6); + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { NMSettingIPConfig *s_ip6; NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT; @@ -14497,11 +14543,11 @@ nm_device_spawn_iface_helper (NMDevice *self) if (nm_device_get_ip_iface_identifier (self, &iid, FALSE)) { g_ptr_array_add (argv, g_strdup ("--iid")); g_ptr_array_add (argv, - _nm_utils_bin2hexstr_full (iid.id_u8, - sizeof (NMUtilsIPv6IfaceId), - ':', - FALSE, - NULL)); + nm_utils_bin2hexstr_full (iid.id_u8, + sizeof (NMUtilsIPv6IfaceId), + ':', + FALSE, + NULL)); } g_ptr_array_add (argv, g_strdup ("--addr-gen-mode")); @@ -14560,39 +14606,34 @@ ip6_managed_setup (NMDevice *self) { set_nm_ipv6ll (self, TRUE); set_disable_ipv6 (self, "1"); - nm_device_ipv6_sysctl_set (self, "accept_ra_defrtr", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_pinfo", "0"); - nm_device_ipv6_sysctl_set (self, "accept_ra_rtr_pref", "0"); - nm_device_ipv6_sysctl_set (self, "use_tempaddr", "0"); - nm_device_ipv6_sysctl_set (self, "forwarding", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_defrtr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_pinfo", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "accept_ra_rtr_pref", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "use_tempaddr", "0"); + nm_device_sysctl_ip_conf_set (self, AF_INET6, "forwarding", "0"); } static void deactivate_async_ready (NMDevice *self, - GAsyncResult *res, + GError *error, gpointer user_data) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); NMDeviceStateReason reason = GPOINTER_TO_UINT (user_data); - GError *error = NULL; - NM_DEVICE_GET_CLASS (self)->deactivate_async_finish (self, res, &error); - - /* If operation cancelled, just return */ - if ( g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) - || (priv->deactivating_cancellable && g_cancellable_is_cancelled (priv->deactivating_cancellable))) { - _LOGW (LOGD_DEVICE, "Deactivation cancelled"); - } else { - /* In every other case, transition to the DISCONNECTED state */ - if (error) { - _LOGW (LOGD_DEVICE, "Deactivation failed: %s", - error->message); - } - nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + _LOGD (LOGD_DEVICE, "Deactivation cancelled"); + return; } g_clear_object (&priv->deactivating_cancellable); - g_clear_error (&error); + + /* In every other case, transition to the DISCONNECTED state */ + if (error) { + _LOGW (LOGD_DEVICE, "Deactivation failed: %s", + error->message); + } + nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); } static void @@ -14612,14 +14653,13 @@ deactivate_dispatcher_complete (guint call_id, gpointer user_data) priv->dispatcher.post_state_reason = NM_DEVICE_STATE_REASON_NONE; if (nm_clear_g_cancellable (&priv->deactivating_cancellable)) - g_warn_if_reached (); + nm_assert_not_reached (); - if ( NM_DEVICE_GET_CLASS (self)->deactivate_async - && NM_DEVICE_GET_CLASS (self)->deactivate_async_finish) { + if (NM_DEVICE_GET_CLASS (self)->deactivate_async) { priv->deactivating_cancellable = g_cancellable_new (); NM_DEVICE_GET_CLASS (self)->deactivate_async (self, priv->deactivating_cancellable, - (GAsyncReadyCallback) deactivate_async_ready, + deactivate_async_ready, GUINT_TO_POINTER (reason)); } else nm_device_queue_state (self, NM_DEVICE_STATE_DISCONNECTED, reason); @@ -14657,7 +14697,7 @@ _set_state_full (NMDevice *self, _LOGD (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s'%s)", nm_device_state_to_str (old_state), nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), _sys_iface_state_to_str (priv->sys_iface_state), priv->firmware_missing ? ", missing firmware" : ""); return; @@ -14666,7 +14706,7 @@ _set_state_full (NMDevice *self, _LOGI (LOGD_DEVICE, "state change: %s -> %s (reason '%s', sys-iface-state: '%s')", nm_device_state_to_str (old_state), nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), _sys_iface_state_to_str (priv->sys_iface_state)); priv->in_state_changed = TRUE; @@ -14677,8 +14717,8 @@ _set_state_full (NMDevice *self, queued_state_clear (self); dispatcher_cleanup (self); - if (priv->deactivating_cancellable) - g_cancellable_cancel (priv->deactivating_cancellable); + + nm_clear_g_cancellable (&priv->deactivating_cancellable); /* Cache the activation request for the dispatcher */ req = nm_g_object_ref (priv->act_request.obj); @@ -14702,8 +14742,10 @@ _set_state_full (NMDevice *self, if (state <= NM_DEVICE_STATE_UNAVAILABLE) { if (available_connections_del_all (self)) _notify (self, PROP_AVAILABLE_CONNECTIONS); - if (old_state > NM_DEVICE_STATE_UNAVAILABLE) - _clear_queued_act_request (priv); + if (old_state > NM_DEVICE_STATE_UNAVAILABLE) { + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); + } } /* Update the available connections list when a device first becomes available */ @@ -14833,7 +14875,7 @@ _set_state_full (NMDevice *self, } break; case NM_DEVICE_STATE_DEACTIVATING: - if ( (s_sriov = (NMSettingSriov *) nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV)) + if ( (s_sriov = nm_device_get_applied_setting (self, NM_TYPE_SETTING_SRIOV)) && priv->ifindex > 0) { nm_platform_link_set_sriov_params (nm_device_get_platform (self), priv->ifindex, 0, NM_TERNARY_TRUE); @@ -14968,8 +15010,8 @@ _set_state_full (NMDevice *self, if (ip_config_valid (old_state) && !ip_config_valid (state)) notify_ip_properties (self); - concheck_update_interval (self, - state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET, state == NM_DEVICE_STATE_ACTIVATED); + concheck_update_interval (self, AF_INET6, state == NM_DEVICE_STATE_ACTIVATED); /* Dispose of the cached activation request */ if (req) @@ -15001,7 +15043,7 @@ queued_state_set (gpointer user_data) _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "change state"); @@ -15032,23 +15074,23 @@ nm_device_queue_state (NMDevice *self, if (priv->queued_state.id && priv->queued_state.state == state) { _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s%s%s%s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "ignore queuing same state change", NM_PRINT_FMT_QUOTED (priv->queued_state.reason != reason, - " (reason differs: ", reason_to_string (reason), ")", "")); + " (reason differs: ", reason_to_string_a (reason), ")", "")); return; } /* Add pending action for the new state before clearing the queued states, so - * that we don't accidently pop all pending states and reach 'startup complete' */ + * that we don't accidentally pop all pending states and reach 'startup complete' */ nm_device_add_pending_action (self, queued_state_to_string (state), TRUE); /* We should only ever have one delayed state transition at a time */ if (priv->queued_state.id) { _LOGW (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "replace previously queued state change"); nm_clear_g_source (&priv->queued_state.id); @@ -15061,7 +15103,7 @@ nm_device_queue_state (NMDevice *self, _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (state), - reason_to_string (reason), + reason_to_string_a (reason), priv->queued_state.id, "queue state change"); } @@ -15076,7 +15118,7 @@ queued_state_clear (NMDevice *self) _LOGD (LOGD_DEVICE, "queue-state[%s, reason:%s, id:%u]: %s", nm_device_state_to_str (priv->queued_state.state), - reason_to_string (priv->queued_state.reason), + reason_to_string_a (priv->queued_state.reason), priv->queued_state.id, "clear queued state change"); nm_clear_g_source (&priv->queued_state.id); @@ -15166,7 +15208,7 @@ nm_device_update_hw_address (NMDevice *self) && priv->state < NM_DEVICE_STATE_PREPARE && !nm_device_is_activating (self))) { /* when we get a hw_addr the first time or while the device - * is not activated (with no explict hw address set), always + * is not activated (with no explicit hw address set), always * update our initial hw-address as well. */ nm_device_update_initial_hw_address (self); } @@ -15183,7 +15225,7 @@ nm_device_update_initial_hw_address (NMDevice *self) if ( priv->hw_addr_initial && priv->hw_addr_type != HW_ADDR_TYPE_UNSET) { /* once we have the initial hw address set, we only allow - * update if the currenty type is "unset". */ + * update if the currently type is "unset". */ return; } g_free (priv->hw_addr_initial); @@ -15308,7 +15350,9 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool gs_free char *a = NULL; a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_wifi ? "wifi.cloned-mac-address" : "ethernet.cloned-mac-address", + is_wifi + ? NM_CON_DEFAULT ("wifi.cloned-mac-address") + : NM_CON_DEFAULT ("ethernet.cloned-mac-address"), self); addr = NM_CLONED_MAC_PRESERVE; @@ -15319,7 +15363,7 @@ _get_cloned_mac_address_setting (NMDevice *self, NMConnection *connection, gbool /* for backward compatibility, read the deprecated wifi.mac-address-randomization setting. */ a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - "wifi." NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, + NM_CON_DEFAULT ("wifi.mac-address-randomization"), self); v = _nm_utils_ascii_str_to_int64 (a, 10, NM_SETTING_MAC_RANDOMIZATION_DEFAULT, @@ -15356,7 +15400,9 @@ _get_generate_mac_address_mask_setting (NMDevice *self, NMConnection *connection } a = nm_config_data_get_connection_default (NM_CONFIG_GET_DATA, - is_wifi ? "wifi.generate-mac-address-mask" : "ethernet.generate-mac-address-mask", + is_wifi + ? NM_CON_DEFAULT ("wifi.generate-mac-address-mask") + : NM_CON_DEFAULT ("ethernet.generate-mac-address-mask"), self); if (!a) return NULL; @@ -15394,7 +15440,7 @@ _hw_addr_set (NMDevice *self, { NMDevicePrivate *priv; gboolean success = FALSE; - NMPlatformError plerr; + int r; guint8 addr_bytes[NM_UTILS_HWADDR_LEN_MAX]; gsize addr_len; gboolean was_taken_down = FALSE; @@ -15431,21 +15477,21 @@ _hw_addr_set (NMDevice *self, } again: - plerr = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len); - success = (plerr == NM_PLATFORM_ERROR_SUCCESS); + r = nm_platform_link_set_address (nm_device_get_platform (self), nm_device_get_ip_ifindex (self), addr_bytes, addr_len); + success = (r >= 0); if (!success) { retry_down = !was_taken_down - && plerr != NM_PLATFORM_ERROR_NOT_FOUND + && r != -NME_PL_NOT_FOUND && nm_platform_link_is_up (nm_device_get_platform (self), nm_device_get_ip_ifindex (self)); - _NMLOG ( retry_down - || plerr == NM_PLATFORM_ERROR_NOT_FOUND + _NMLOG ( ( retry_down + || r == -NME_PL_NOT_FOUND) ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)%s", operation, addr, detail, - nm_platform_error_to_string_a (plerr), + nm_strerror (r), retry_down ? " (retry with taking down)" : ""); } else { /* MAC address successfully changed; update the current MAC to match */ @@ -15468,7 +15514,7 @@ again: * that is rather complicated and it is not expected that this case * happens for regular drivers. * Note that brcmfmac can block NetworkManager for 500 msec while - * taking down the device. Let's add annother 100 msec to that. + * taking down the device. Let's add another 100 msec to that. * * wait/poll up to 100 msec until it changes. */ @@ -15870,7 +15916,9 @@ nm_device_get_supplicant_timeout (NMDevice *self) g_return_val_if_fail (NM_IS_DEVICE (self), SUPPLICANT_DEFAULT_TIMEOUT); connection = nm_device_get_applied_connection (self); + g_return_val_if_fail (connection, SUPPLICANT_DEFAULT_TIMEOUT); + s_8021x = nm_connection_get_setting_802_1x (connection); if (s_8021x) { timeout = nm_setting_802_1x_get_auth_timeout (s_8021x); @@ -15879,7 +15927,7 @@ nm_device_get_supplicant_timeout (NMDevice *self) } return nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "802-1x.auth-timeout", + NM_CON_DEFAULT ("802-1x.auth-timeout"), self, 1, G_MAXINT32, @@ -15901,13 +15949,13 @@ nm_device_auth_retries_try_next (NMDevice *self) if (G_UNLIKELY (auth_retries == NM_DEVICE_AUTH_RETRIES_UNSET)) { auth_retries = -1; - s_con = NM_SETTING_CONNECTION (nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION)); + s_con = nm_device_get_applied_setting (self, NM_TYPE_SETTING_CONNECTION); if (s_con) auth_retries = nm_setting_connection_get_auth_retries (s_con); if (auth_retries == -1) { auth_retries = nm_config_data_get_connection_default_int64 (NM_CONFIG_GET_DATA, - "connection.auth-retries", + NM_CON_DEFAULT ("connection.auth-retries"), self, -1, G_MAXINT32, -1); } @@ -15945,10 +15993,10 @@ _activation_func_to_string (ActivationHandleFunc func) FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage1_device_prepare); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage2_device_config); FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage3_ip_config_start); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip4_config_timeout); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip6_config_timeout); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip4_config_result); - FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip6_config_commit); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip_config_timeout_4); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage4_ip_config_timeout_6); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip_config_result_4); + FUNC_TO_STRING_CHECK_AND_RETURN (func, activate_stage5_ip_config_result_6); g_return_val_if_reached ("unknown"); } @@ -15967,7 +16015,8 @@ nm_device_init (NMDevice *self) c_list_init (&self->devices_lst); c_list_init (&priv->slaves); - priv->connectivity_state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[0].state = NM_CONNECTIVITY_UNKNOWN; + priv->concheck_x[1].state = NM_CONNECTIVITY_UNKNOWN; nm_dbus_track_obj_path_init (&priv->parent_device, G_OBJECT (self), obj_properties[PROP_PARENT]); nm_dbus_track_obj_path_init (&priv->act_request, G_OBJECT (self), obj_properties[PROP_ACTIVE_CONNECTION]); @@ -16155,7 +16204,8 @@ dispose (GObject *object) if (nm_clear_g_source (&priv->carrier_wait_id)) nm_device_remove_pending_action (self, NM_PENDING_ACTION_CARRIER_WAIT, FALSE); - _clear_queued_act_request (priv); + _clear_queued_act_request (priv, + NM_ACTIVE_CONNECTION_STATE_REASON_DEVICE_DISCONNECTED); nm_clear_g_source (&priv->device_link_changed_id); nm_clear_g_source (&priv->device_ip_link_changed_id); @@ -16168,12 +16218,13 @@ dispose (GObject *object) g_clear_object (&priv->lldp_listener); } - nm_clear_g_source (&priv->concheck_p_cur_id); + nm_clear_g_source (&priv->concheck_x[0].p_cur_id); + nm_clear_g_source (&priv->concheck_x[1].p_cur_id); G_OBJECT_CLASS (nm_device_parent_class)->dispose (object); if (nm_clear_g_source (&priv->queued_state.id)) { - /* FIXME: we'd expect the queud_state to be alredy cleared and this statement + /* FIXME: we'd expect the queud_state to be already cleared and this statement * not being necessary. Add this check here to hopefully investigate crash * rh#1270247. */ g_return_if_reached (); @@ -16293,6 +16344,8 @@ set_property (GObject *object, guint prop_id, /* construct-only */ nm_assert (priv->type == NM_DEVICE_TYPE_UNKNOWN); priv->type = g_value_get_uint (value); + nm_assert (priv->type > NM_DEVICE_TYPE_UNKNOWN); + nm_assert (priv->type <= NM_DEVICE_TYPE_WIFI_P2P); break; case PROP_LINK_TYPE: /* construct-only */ @@ -16505,8 +16558,11 @@ get_property (GObject *object, guint prop_id, case PROP_RX_BYTES: g_value_set_uint64 (value, priv->stats.rx_bytes); break; - case PROP_CONNECTIVITY: - g_value_set_uint (value, priv->connectivity_state); + case PROP_IP4_CONNECTIVITY: + g_value_set_uint (value, priv->concheck_x[1].state); + break; + case PROP_IP6_CONNECTIVITY: + g_value_set_uint (value, priv->concheck_x[0].state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -16594,6 +16650,8 @@ static const NMDBusInterfaceInfoExtended interface_info_device = { NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Metered", "u", NM_DEVICE_METERED), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("LldpNeighbors", "aa{sv}", NM_DEVICE_LLDP_NEIGHBORS), NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE_L ("Real", "b", NM_DEVICE_REAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip4Connectivity", "u", NM_DEVICE_IP4_CONNECTIVITY), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Ip6Connectivity", "u", NM_DEVICE_IP6_CONNECTIVITY), ), ), }; @@ -16636,10 +16694,8 @@ nm_device_class_init (NMDeviceClass *klass) klass->is_available = is_available; klass->act_stage1_prepare = act_stage1_prepare; klass->act_stage2_config = act_stage2_config; - klass->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - klass->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - klass->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - klass->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; + klass->act_stage3_ip_config_start = act_stage3_ip_config_start; + klass->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; klass->get_type_description = get_type_description; klass->can_auto_connect = can_auto_connect; @@ -16870,8 +16926,13 @@ nm_device_class_init (NMDeviceClass *klass) G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - obj_properties[PROP_CONNECTIVITY] = - g_param_spec_uint (NM_DEVICE_CONNECTIVITY, "", "", + obj_properties[PROP_IP4_CONNECTIVITY] = + g_param_spec_uint (NM_DEVICE_IP4_CONNECTIVITY, "", "", + NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + obj_properties[PROP_IP6_CONNECTIVITY] = + g_param_spec_uint (NM_DEVICE_IP6_CONNECTIVITY, "", "", NM_CONNECTIVITY_UNKNOWN, NM_CONNECTIVITY_FULL, NM_CONNECTIVITY_UNKNOWN, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); @@ -16951,12 +17012,12 @@ nm_device_class_init (NMDeviceClass *klass) G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); - - signals[CONNECTIVITY_CHANGED] = - g_signal_new (NM_DEVICE_CONNECTIVITY_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); } + +/* Connection defaults from plugins */ +NM_CON_DEFAULT_NOP ("cdma.mtu"); +NM_CON_DEFAULT_NOP ("gsm.mtu"); +NM_CON_DEFAULT_NOP ("wifi.powersave"); +NM_CON_DEFAULT_NOP ("wifi.wake-on-wlan"); +NM_CON_DEFAULT_NOP ("wifi-sec.pmf"); +NM_CON_DEFAULT_NOP ("wifi-sec.fils"); diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 3c703564..45c9dda0 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -54,7 +54,7 @@ typedef enum { static inline NMDeviceStateReason nm_device_state_reason_check (NMDeviceStateReason reason) { - /* the device-state-reason serves mostly informational purpse during a state + /* the device-state-reason serves mostly informational purpose during a state * change. In some cases however, decisions are made based on the reason. * I tend to think that interpreting the state reason to derive some behaviors * is confusing, because the cause and effect are so far apart. @@ -76,6 +76,7 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT "waiting-for-supplicant" #define NM_PENDING_ACTION_WIFI_SCAN "wifi-scan" #define NM_PENDING_ACTION_WAITING_FOR_COMPANION "waiting-for-companion" +#define NM_PENDING_ACTION_LINK_INIT "link-init" #define NM_PENDING_ACTIONPREFIX_QUEUED_STATE_CHANGE "queued-state-change-" #define NM_PENDING_ACTIONPREFIX_ACTIVATION "activation-" @@ -142,13 +143,13 @@ nm_device_state_reason_check (NMDeviceStateReason reason) #define NM_DEVICE_STATE_CHANGED "state-changed" #define NM_DEVICE_LINK_INITIALIZED "link-initialized" #define NM_DEVICE_AUTOCONNECT_ALLOWED "autoconnect-allowed" -#define NM_DEVICE_CONNECTIVITY_CHANGED "connectivity-changed" #define NM_DEVICE_STATISTICS_REFRESH_RATE_MS "refresh-rate-ms" #define NM_DEVICE_STATISTICS_TX_BYTES "tx-bytes" #define NM_DEVICE_STATISTICS_RX_BYTES "rx-bytes" -#define NM_DEVICE_CONNECTIVITY "connectivity" +#define NM_DEVICE_IP4_CONNECTIVITY "ip4-connectivity" +#define NM_DEVICE_IP6_CONNECTIVITY "ip6-connectivity" #define NM_TYPE_DEVICE (nm_device_get_type ()) #define NM_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE, NMDevice)) @@ -182,13 +183,13 @@ typedef enum { /*< skip >*/ _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP = (1L << 2), /* a device can be marked as unmanaged for various reasons. Some of these reasons - * are authorative, others not. Non-authoritative reasons can be overruled by + * are authoritative, others not. Non-authoritative reasons can be overruled by * `nmcli device set $DEVICE managed yes`. Also, for an explicit user activation * request we may want to consider the device as managed. This flag makes devices * that are unmanaged appear available. */ _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_OVERRULE_UNMANAGED = (1L << 3), - /* a collection of flags, that are commonly set for an explict user-request. */ + /* a collection of flags, that are commonly set for an explicit user-request. */ NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST = _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_WAITING_CARRIER | _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP @@ -221,6 +222,10 @@ typedef enum { /*< skip >*/ NM_DEVICE_CHECK_DEV_AVAILABLE_ALL = (1L << 1) - 1, } NMDeviceCheckDevAvailableFlags; +typedef void (*NMDeviceDeactivateCallback) (NMDevice *self, + GError *error, + gpointer user_data); + typedef struct _NMDeviceClass { NMDBusObjectClass parent; @@ -278,7 +283,7 @@ typedef struct _NMDeviceClass { * Hook for derived classes to be notfied during realize_start_setup() * and perform additional setup. * - * The default implemention of NMDevice calls link_changed(). + * The default implementation of NMDevice calls link_changed(). */ void (*realize_start_notify) (NMDevice *self, const NMPlatformLink *pllink); @@ -327,9 +332,11 @@ typedef struct _NMDeviceClass { guint32 (*get_configured_mtu) (NMDevice *self, NMDeviceMtuSource *out_source); + const char *(*get_auto_ip_config_method) (NMDevice *self, int addr_family); + /* Checks whether the connection is compatible with the device using * only the devices type and characteristics. Does not use any live - * network information like WiFi scan lists etc. + * network information like Wi-Fi scan lists etc. */ gboolean (* check_connection_compatible) (NMDevice *self, NMConnection *connection, @@ -362,27 +369,21 @@ typedef struct _NMDeviceClass { NMDeviceStateReason *out_failure_reason); NMActStageReturn (* act_stage2_config) (NMDevice *self, NMDeviceStateReason *out_failure_reason); - NMActStageReturn (* act_stage3_ip4_config_start) (NMDevice *self, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason); - NMActStageReturn (* act_stage3_ip6_config_start) (NMDevice *self, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason); - NMActStageReturn (* act_stage4_ip4_config_timeout) (NMDevice *self, - NMDeviceStateReason *out_failure_reason); - NMActStageReturn (* act_stage4_ip6_config_timeout) (NMDevice *self, - NMDeviceStateReason *out_failure_reason); + NMActStageReturn (* act_stage3_ip_config_start) (NMDevice *self, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason); + NMActStageReturn (* act_stage4_ip_config_timeout) (NMDevice *self, + int addr_family, + NMDeviceStateReason *out_failure_reason); void (* ip4_config_pre_commit) (NMDevice *self, NMIP4Config *config); /* Async deactivating (in the DEACTIVATING phase) */ void (* deactivate_async) (NMDevice *self, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMDeviceDeactivateCallback callback, gpointer user_data); - gboolean (* deactivate_async_finish) (NMDevice *self, - GAsyncResult *res, - GError **error); void (* deactivate_reset_hw_addr) (NMDevice *self); @@ -452,6 +453,11 @@ typedef struct _NMDeviceClass { guint32 (* get_dhcp_timeout) (NMDevice *self, int addr_family); + + /* Controls, whether to call act_stage2_config() callback also for assuming + * a device or for external activations. In this case, act_stage2_config() must + * take care not to touch the device's configuration. */ + bool act_stage2_config_also_for_external_or_assume:1; } NMDeviceClass; typedef void (*NMDeviceAuthRequestFunc) (NMDevice *device, @@ -481,6 +487,7 @@ int nm_device_get_ifindex (NMDevice *dev); gboolean nm_device_is_software (NMDevice *dev); gboolean nm_device_is_real (NMDevice *dev); const char * nm_device_get_ip_iface (NMDevice *dev); +const char * nm_device_get_ip_iface_from_platform (NMDevice *dev); int nm_device_get_ip_ifindex (const NMDevice *dev); const char * nm_device_get_driver (NMDevice *dev); const char * nm_device_get_driver_version (NMDevice *dev); @@ -541,7 +548,10 @@ NMConnection * nm_device_get_settings_connection_get_connection (NMDevice *self NMConnection * nm_device_get_applied_connection (NMDevice *dev); gboolean nm_device_has_unmodified_applied_connection (NMDevice *self, NMSettingCompareFlags compare_flags); -NMSetting * nm_device_get_applied_setting (NMDevice *dev, GType setting_type); +NMActivationStateFlags nm_device_get_activation_state_flags (NMDevice *self); + +gpointer /* (NMSetting *) */ nm_device_get_applied_setting (NMDevice *dev, + GType setting_type); void nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config); @@ -617,7 +627,7 @@ void nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device); * the settings plugin (for example keyfile.unmanaged-devices or ifcfg-rh's * NM_CONTROLLED=no). Although this is user-configuration (provided from * the settings plugins, such as NM_CONTROLLED=no in ifcfg-rh), it cannot - * be overruled and is authorative. That is because users may depend on + * be overruled and is authoritative. That is because users may depend on * dropping a ifcfg-rh file to ensure the device is unmanaged. * @NM_UNMANAGED_USER_CONF: %TRUE when unmanaged by user decision via * the NetworkManager.conf ("unmanaged" in the [device] section). @@ -634,7 +644,7 @@ void nm_device_copy_ip6_dns_config (NMDevice *self, NMDevice *from_device); typedef enum { /*< skip >*/ NM_UNMANAGED_NONE = 0, - /* these flags are authorative. If one of them is set, + /* these flags are authoritative. If one of them is set, * the device cannot be managed. */ NM_UNMANAGED_SLEEPING = (1LL << 0), NM_UNMANAGED_QUITTING = (1LL << 1), @@ -767,7 +777,9 @@ void nm_device_queue_state (NMDevice *self, gboolean nm_device_get_firmware_missing (NMDevice *self); -void nm_device_disconnect_active_connection (NMActiveConnection *active); +void nm_device_disconnect_active_connection (NMActiveConnection *active, + NMDeviceStateReason device_reason, + NMActiveConnectionStateReason active_reason); void nm_device_queue_activation (NMDevice *device, NMActRequest *req); @@ -775,7 +787,13 @@ gboolean nm_device_supports_vlans (NMDevice *device); gboolean nm_device_add_pending_action (NMDevice *device, const char *action, gboolean assert_not_yet_pending); gboolean nm_device_remove_pending_action (NMDevice *device, const char *action, gboolean assert_is_pending); -gboolean nm_device_has_pending_action (NMDevice *device); +const char *nm_device_has_pending_action_reason (NMDevice *device); + +static inline gboolean +nm_device_has_pending_action (NMDevice *device) +{ + return !!nm_device_has_pending_action_reason (device); +} NMSettingsConnection *nm_device_get_best_connection (NMDevice *device, const char *specific_object, @@ -838,12 +856,13 @@ typedef void (*NMDeviceConnectivityCallback) (NMDevice *self, void nm_device_check_connectivity_update_interval (NMDevice *self); NMDeviceConnectivityHandle *nm_device_check_connectivity (NMDevice *self, + int addr_family, NMDeviceConnectivityCallback callback, gpointer user_data); void nm_device_check_connectivity_cancel (NMDeviceConnectivityHandle *handle); -NMConnectivityState nm_device_get_connectivity_state (NMDevice *self); +NMConnectivityState nm_device_get_connectivity_state (NMDevice *self, int addr_family); typedef struct _NMBtVTableNetworkServer NMBtVTableNetworkServer; struct _NMBtVTableNetworkServer { diff --git a/src/devices/nm-lldp-listener.c b/src/devices/nm-lldp-listener.c index c0484ed3..cb3b5907 100644 --- a/src/devices/nm-lldp-listener.c +++ b/src/devices/nm-lldp-listener.c @@ -23,7 +23,6 @@ #include "nm-lldp-listener.h" #include <net/ethernet.h> -#include <errno.h> #include "platform/nm-platform.h" #include "nm-utils.h" @@ -128,7 +127,7 @@ typedef struct { int _ifindex = (self) ? NM_LLDP_LISTENER_GET_PRIVATE (self)->ifindex : 0; \ \ _nm_log (_level, _NMLOG_DOMAIN, 0, \ - nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex), \ + _ifindex > 0 ? nm_platform_link_get_name (NM_PLATFORM_GET, _ifindex) : NULL, \ NULL, \ "%s%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ _NMLOG_PREFIX_NAME, \ @@ -384,7 +383,7 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) &chassis_id, &chassis_id_len); if (r < 0) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "failed reading chassis-id: %s", g_strerror (-r)); + "failed reading chassis-id: %s", nm_strerror_native (-r)); return NULL; } if (chassis_id_len < 1) { @@ -397,7 +396,7 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) &port_id, &port_id_len); if (r < 0) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "failed reading port-id: %s", g_strerror (-r)); + "failed reading port-id: %s", nm_strerror_native (-r)); return NULL; } if (port_id_len < 1) { @@ -413,7 +412,7 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) r = sd_lldp_neighbor_get_destination_address (neighbor_sd, &neigh->destination_address); if (r < 0) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "failed getting destination address: %s", g_strerror (-r)); + "failed getting destination address: %s", nm_strerror_native (-r)); goto out; } @@ -464,7 +463,7 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) r = sd_lldp_neighbor_tlv_rewind (neighbor_sd); if (r < 0) { g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "failed reading tlv (rewind): %s", g_strerror (-r)); + "failed reading tlv (rewind): %s", nm_strerror_native (-r)); goto out; } do { @@ -476,7 +475,7 @@ lldp_neighbor_new (sd_lldp_neighbor *neighbor_sd, GError **error) if (r == -ENXIO) continue; g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "failed reading tlv: %s", g_strerror (-r)); + "failed reading tlv: %s", nm_strerror_native (-r)); goto out; } diff --git a/src/devices/ovs/meson.build b/src/devices/ovs/meson.build index 9d637fe9..834b27b0 100644 --- a/src/devices/ovs/meson.build +++ b/src/devices/ovs/meson.build @@ -3,12 +3,12 @@ sources = files( 'nm-device-ovs-interface.c', 'nm-device-ovs-port.c', 'nm-ovsdb.c', - 'nm-ovs-factory.c' + 'nm-ovs-factory.c', ) deps = [ jansson_dep, - nm_dep + nm_dep, ] libnm_device_plugin_ovs = shared_module( @@ -18,7 +18,7 @@ libnm_device_plugin_ovs = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_ovs diff --git a/src/devices/ovs/nm-device-ovs-bridge.c b/src/devices/ovs/nm-device-ovs-bridge.c index eff355a3..be707e7a 100644 --- a/src/devices/ovs/nm-device-ovs-bridge.c +++ b/src/devices/ovs/nm-device-ovs-bridge.c @@ -77,17 +77,10 @@ get_generic_capabilities (NMDevice *device) } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - return NM_ACT_STAGE_RETURN_IP_FAIL; -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { return NM_ACT_STAGE_RETURN_IP_FAIL; } @@ -146,8 +139,7 @@ nm_device_ovs_bridge_class_init (NMDeviceOvsBridgeClass *klass) device_class->create_and_realize = create_and_realize; device_class->unrealize = unrealize; device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; } diff --git a/src/devices/ovs/nm-device-ovs-interface.c b/src/devices/ovs/nm-device-ovs-interface.c index 2b48fae6..e3d3f9ee 100644 --- a/src/devices/ovs/nm-device-ovs-interface.c +++ b/src/devices/ovs/nm-device-ovs-interface.c @@ -121,48 +121,32 @@ link_changed (NMDevice *device, static gboolean _is_internal_interface (NMDevice *device) { - NMConnection *connection = nm_device_get_applied_connection (device); - NMSettingOvsInterface *s_ovs_iface = nm_connection_get_setting_ovs_interface (connection); - - g_return_val_if_fail (s_ovs_iface, FALSE); - - return strcmp (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal") == 0; -} - -static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device); + NMSettingOvsInterface *s_ovs_iface; - if (!_is_internal_interface (device)) - return NM_ACT_STAGE_RETURN_IP_FAIL; + s_ovs_iface = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OVS_INTERFACE); - if (!nm_device_get_ip_ifindex (device)) { - priv->waiting_for_interface = TRUE; - return NM_ACT_STAGE_RETURN_POSTPONE; - } + g_return_val_if_fail (s_ovs_iface, FALSE); - return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); + return nm_streq (nm_setting_ovs_interface_get_interface_type (s_ovs_iface), "internal"); } static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { NMDeviceOvsInterfacePrivate *priv = NM_DEVICE_OVS_INTERFACE_GET_PRIVATE (device); if (!_is_internal_interface (device)) return NM_ACT_STAGE_RETURN_IP_FAIL; - if (!nm_device_get_ip_ifindex (device)) { + if (nm_device_get_ip_ifindex (device) <= 0) { priv->waiting_for_interface = TRUE; return NM_ACT_STAGE_RETURN_POSTPONE; } - return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); + return NM_DEVICE_CLASS (nm_device_ovs_interface_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); } static gboolean @@ -206,7 +190,6 @@ nm_device_ovs_interface_class_init (NMDeviceOvsInterfaceClass *klass) device_class->is_available = is_available; device_class->check_connection_compatible = check_connection_compatible; device_class->link_changed = link_changed; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->can_unmanaged_external_down = can_unmanaged_external_down; } diff --git a/src/devices/ovs/nm-device-ovs-port.c b/src/devices/ovs/nm-device-ovs-port.c index 1f9afbab..b96eba68 100644 --- a/src/devices/ovs/nm-device-ovs-port.c +++ b/src/devices/ovs/nm-device-ovs-port.c @@ -71,17 +71,10 @@ get_generic_capabilities (NMDevice *device) } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - return NM_ACT_STAGE_RETURN_IP_FAIL; -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { return NM_ACT_STAGE_RETURN_IP_FAIL; } @@ -186,8 +179,7 @@ nm_device_ovs_port_class_init (NMDeviceOvsPortClass *klass) device_class->get_type_description = get_type_description; device_class->create_and_realize = create_and_realize; device_class->get_generic_capabilities = get_generic_capabilities; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->enslave_slave = enslave_slave; device_class->release_slave = release_slave; } diff --git a/src/devices/ovs/nm-ovsdb.c b/src/devices/ovs/nm-ovsdb.c index 494e2ea0..9d73c3ac 100644 --- a/src/devices/ovs/nm-ovsdb.c +++ b/src/devices/ovs/nm-ovsdb.c @@ -21,7 +21,6 @@ #include "nm-ovsdb.h" -#include <string.h> #include <gmodule.h> #include <gio/gunixsocketaddress.h> @@ -1118,7 +1117,7 @@ ovsdb_got_msg (NMOvsdb *self, json_t *msg) ovsdb_disconnect (self, FALSE); return; } - /* Cool, we found a corresponsing call. Finish it. */ + /* Cool, we found a corresponding call. Finish it. */ _call_trace ("response", call, msg); diff --git a/src/devices/team/meson.build b/src/devices/team/meson.build index 0f0763bd..3f755012 100644 --- a/src/devices/team/meson.build +++ b/src/devices/team/meson.build @@ -1,12 +1,12 @@ sources = files( 'nm-device-team.c', - 'nm-team-factory.c' + 'nm-team-factory.c', ) deps = [ jansson_dep, libteamdctl_dep, - nm_dep + nm_dep, ] libnm_device_plugin_team = shared_module( @@ -16,7 +16,7 @@ libnm_device_plugin_team = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_team diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index 899932fd..38a6dd8c 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -34,6 +34,7 @@ #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" #include "platform/nm-platform.h" +#include "nm-config.h" #include "nm-core-internal.h" #include "nm-ip4-config.h" #include "nm-dbus-compat.h" @@ -479,11 +480,25 @@ teamd_child_setup (gpointer user_data) signal (SIGPIPE, SIG_IGN); } +static const char ** +teamd_env (void) +{ + const char **env = g_new0 (const char *, 2); + + if (nm_config_get_is_debug (nm_config_get ())) + env[0] = "TEAM_LOG_OUTPUT=stderr"; + else + env[0] = "TEAM_LOG_OUTPUT=syslog"; + + return env; +} + static gboolean teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) { gs_unref_ptrarray GPtrArray *argv = NULL; gs_free char *tmp_str = NULL; + gs_free const char **envp = NULL; if (!teamd_binary) { teamd_binary = nm_utils_find_helper ("teamd", NULL, error); @@ -500,8 +515,11 @@ teamd_kill (NMDeviceTeam *self, const char *teamd_binary, GError **error) g_ptr_array_add (argv, (gpointer) nm_device_get_iface (NM_DEVICE (self))); g_ptr_array_add (argv, NULL); + envp = teamd_env (); + _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata))); - return g_spawn_sync ("/", (char **) argv->pdata, NULL, 0, teamd_child_setup, NULL, NULL, NULL, NULL, error); + return g_spawn_sync ("/", (char **) argv->pdata, (char **) envp, 0, + teamd_child_setup, NULL, NULL, NULL, NULL, error); } static gboolean @@ -518,6 +536,7 @@ teamd_start (NMDevice *device, NMConnection *connection) nm_auto_free const char *config_free = NULL; NMSettingTeam *s_team; gs_free char *cloned_mac = NULL; + gs_free const char **envp = NULL; s_team = nm_connection_get_setting_team (connection); g_return_val_if_fail (s_team, FALSE); @@ -588,8 +607,10 @@ teamd_start (NMDevice *device, NMConnection *connection) g_ptr_array_add (argv, (gpointer) "-gg"); g_ptr_array_add (argv, NULL); + envp = teamd_env (); + _LOGD (LOGD_TEAM, "running: %s", (tmp_str = g_strjoinv (" ", (char **) argv->pdata))); - if (!g_spawn_async ("/", (char **) argv->pdata, NULL, G_SPAWN_DO_NOT_REAP_CHILD, + if (!g_spawn_async ("/", (char **) argv->pdata, (char **) envp, G_SPAWN_DO_NOT_REAP_CHILD, teamd_child_setup, NULL, &priv->teamd_pid, &error)) { _LOGW (LOGD_TEAM, "Activation: (team) failed to start teamd: %s", error->message); teamd_cleanup (device, TRUE); @@ -626,6 +647,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) connection = nm_device_get_applied_connection (device); g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_team = nm_connection_get_setting_team (connection); g_return_val_if_fail (s_team, NM_ACT_STAGE_RETURN_FAILURE); @@ -782,15 +804,15 @@ create_and_realize (NMDevice *device, GError **error) { const char *iface = nm_device_get_iface (device); - NMPlatformError plerr; + int r; - plerr = nm_platform_link_team_add (nm_device_get_platform (device), iface, out_plink); - if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + r = nm_platform_link_team_add (nm_device_get_platform (device), iface, out_plink); + if (r < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to create team master interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string_a (plerr)); + nm_strerror (r)); return FALSE; } diff --git a/src/devices/team/nm-team-factory.c b/src/devices/team/nm-team-factory.c index d062429f..f18b943d 100644 --- a/src/devices/team/nm-team-factory.c +++ b/src/devices/team/nm-team-factory.c @@ -20,7 +20,6 @@ #include "nm-default.h" -#include <string.h> #include <gmodule.h> #include "nm-manager.h" diff --git a/src/devices/tests/meson.build b/src/devices/tests/meson.build index 02c61ced..4702c656 100644 --- a/src/devices/tests/meson.build +++ b/src/devices/tests/meson.build @@ -1,18 +1,19 @@ test_units = [ 'test-acd', - 'test-lldp' + 'test-lldp', ] foreach test_unit: test_units exe = executable( test_unit, test_unit + '.c', - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'devices/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], + timeout: default_test_timeout, ) endforeach diff --git a/src/devices/tests/test-acd.c b/src/devices/tests/test-acd.c index 8a2852a2..aff71825 100644 --- a/src/devices/tests/test-acd.c +++ b/src/devices/tests/test-acd.c @@ -20,6 +20,8 @@ #include "nm-default.h" +#include "n-acd/src/n-acd.h" + #include "devices/nm-acd-manager.h" #include "platform/tests/test-common.h" @@ -31,6 +33,46 @@ #define ADDR3 0x03030303 #define ADDR4 0x04040404 +/*****************************************************************************/ + +static gboolean +_skip_acd_test_check (void) +{ + NAcd *acd; + NAcdConfig *config; + const guint8 hwaddr[ETH_ALEN] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }; + int r; + static int skip = -1; + + if (skip == -1) { + r = n_acd_config_new (&config); + g_assert (r == 0); + + n_acd_config_set_ifindex (config, 1); + n_acd_config_set_transport (config, N_ACD_TRANSPORT_ETHERNET); + n_acd_config_set_mac (config, hwaddr, sizeof (hwaddr)); + + r = n_acd_new (&acd, config); + n_acd_config_free (config); + if (r == 0) + n_acd_unref (acd); + + skip = (r != 0); + } + return skip; +} + +#define _skip_acd_test() \ + ({ \ + gboolean _skip = _skip_acd_test_check (); \ + \ + if (_skip) \ + g_test_skip ("Cannot create NAcd. Running under valgind?"); \ + _skip; \ + }) + +/*****************************************************************************/ + typedef struct { int ifindex0; int ifindex1; @@ -61,20 +103,26 @@ typedef struct { } TestInfo; static void -acd_manager_probe_terminated (NMAcdManager *acd_manager, GMainLoop *loop) +acd_manager_probe_terminated (NMAcdManager *acd_manager, gpointer user_data) { - g_main_loop_quit (loop); + g_main_loop_quit (user_data); } static void test_acd_common (test_fixture *fixture, TestInfo *info) { - gs_unref_object NMAcdManager *manager = NULL; + NMAcdManager *manager; GMainLoop *loop; int i; const guint WAIT_TIME_OPTIMISTIC = 50; guint wait_time; - gulong signal_id; + static const NMAcdCallbacks callbacks = { + .probe_terminated_callback = acd_manager_probe_terminated, + .user_data_destroy = (GDestroyNotify) g_main_loop_unref, + }; + + if (_skip_acd_test ()) + return; /* first, try with a short waittime. We hope that this is long enough * to successfully complete the test. Only if that's not the case, we @@ -83,7 +131,13 @@ test_acd_common (test_fixture *fixture, TestInfo *info) wait_time = WAIT_TIME_OPTIMISTIC; again: - manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + loop = g_main_loop_new (NULL, FALSE); + + manager = nm_acd_manager_new (fixture->ifindex0, + fixture->hwaddr0, + fixture->hwaddr0_len, + &callbacks, + g_main_loop_ref (loop)); g_assert (manager != NULL); for (i = 0; info->addresses[i]; i++) @@ -94,16 +148,13 @@ again: 24, 0, 3600, 1800, 0, NULL); } - loop = g_main_loop_new (NULL, FALSE); - signal_id = g_signal_connect (manager, NM_ACD_MANAGER_PROBE_TERMINATED, - G_CALLBACK (acd_manager_probe_terminated), loop); g_assert (nm_acd_manager_start_probe (manager, wait_time)); g_assert (nmtst_main_loop_run (loop, 2000)); - g_signal_handler_disconnect (manager, signal_id); g_main_loop_unref (loop); for (i = 0; info->addresses[i]; i++) { gboolean val; + char sbuf[NM_UTILS_INET_ADDRSTRLEN]; val = nm_acd_manager_check_address (manager, info->addresses[i]); if (val == info->expected_result[i]) @@ -113,14 +164,16 @@ again: /* probably we just had a glitch and the system took longer than * expected. Re-verify with a large timeout this time. */ wait_time = 1000; - g_clear_object (&manager); + nm_clear_pointer (&manager, nm_acd_manager_free); goto again; } g_error ("expected check for address #%d (%s) to %s, but it didn't", - i, nm_utils_inet4_ntop (info->addresses[i], NULL), + i, nm_utils_inet4_ntop (info->addresses[i], sbuf), info->expected_result[i] ? "detect no duplicated" : "detect a duplicate"); } + + nm_acd_manager_free (manager); } static void @@ -146,10 +199,17 @@ test_acd_probe_2 (test_fixture *fixture, gconstpointer user_data) static void test_acd_announce (test_fixture *fixture, gconstpointer user_data) { - gs_unref_object NMAcdManager *manager = NULL; + NMAcdManager *manager; GMainLoop *loop; - manager = nm_acd_manager_new (fixture->ifindex0, fixture->hwaddr0, fixture->hwaddr0_len); + if (_skip_acd_test ()) + return; + + manager = nm_acd_manager_new (fixture->ifindex0, + fixture->hwaddr0, + fixture->hwaddr0_len, + NULL, + NULL); g_assert (manager != NULL); g_assert (nm_acd_manager_add_address (manager, ADDR1)); @@ -159,6 +219,8 @@ test_acd_announce (test_fixture *fixture, gconstpointer user_data) nm_acd_manager_announce_addresses (manager); g_assert (!nmtst_main_loop_run (loop, 200)); g_main_loop_unref (loop); + + nm_acd_manager_free (manager); } static void diff --git a/src/devices/wifi/meson.build b/src/devices/wifi/meson.build index 2745040b..4dfbe4c8 100644 --- a/src/devices/wifi/meson.build +++ b/src/devices/wifi/meson.build @@ -1,13 +1,15 @@ common_sources = files( 'nm-wifi-ap.c', - 'nm-wifi-utils.c' + 'nm-wifi-p2p-peer.c', + 'nm-wifi-utils.c', ) sources = common_sources + files( - 'nm-wifi-factory.c', - 'nm-wifi-common.c', + 'nm-device-olpc-mesh.c', + 'nm-device-wifi-p2p.c', 'nm-device-wifi.c', - 'nm-device-olpc-mesh.c' + 'nm-wifi-common.c', + 'nm-wifi-factory.c', ) if enable_iwd @@ -18,7 +20,7 @@ if enable_iwd endif deps = [ - nm_dep + nm_dep, ] libnm_device_plugin_wifi = shared_module( @@ -28,7 +30,7 @@ libnm_device_plugin_wifi = shared_module( link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) core_plugins += libnm_device_plugin_wifi diff --git a/src/devices/wifi/nm-device-iwd.c b/src/devices/wifi/nm-device-iwd.c index 1d1be742..dcc161d2 100644 --- a/src/devices/wifi/nm-device-iwd.c +++ b/src/devices/wifi/nm-device-iwd.c @@ -22,8 +22,6 @@ #include "nm-device-iwd.h" -#include <string.h> - #include "nm-common-macros.h" #include "devices/nm-device.h" #include "devices/nm-device-private.h" @@ -41,6 +39,7 @@ #include "nm-config.h" #include "nm-iwd-manager.h" #include "nm-dbus-manager.h" +#include "nm-dbus-compat.h" #include "devices/nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceIwd); @@ -69,6 +68,8 @@ typedef struct { GDBusObject * dbus_obj; GDBusProxy * dbus_device_proxy; GDBusProxy * dbus_station_proxy; + GDBusProxy * dbus_ap_proxy; + GDBusProxy * dbus_adhoc_proxy; CList aps_lst_head; NMWifiAP * current_ap; GCancellable * cancellable; @@ -80,6 +81,7 @@ typedef struct { bool can_connect:1; bool scanning:1; bool scan_requested:1; + bool act_mode_switch:1; gint64 last_scan; } NMDeviceIwdPrivate; @@ -104,7 +106,7 @@ G_DEFINE_TYPE (NMDeviceIwd, nm_device_iwd, NM_TYPE_DEVICE) /*****************************************************************************/ static void schedule_periodic_scan (NMDeviceIwd *self, - NMDeviceState current_state); + gboolean initial_scan); /*****************************************************************************/ @@ -229,7 +231,11 @@ vardict_from_network_type (const char *type) } static void -insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint32_t ap_id) +insert_ap_from_network (NMDeviceIwd *self, + GHashTable *aps, + const char *path, + int16_t signal, + uint32_t ap_id) { gs_unref_object GDBusProxy *network_proxy = NULL; gs_unref_variant GVariant *name_value = NULL, *type_value = NULL; @@ -240,6 +246,11 @@ insert_ap_from_network (GHashTable *aps, const char *path, int16_t signal, uint3 uint8_t bssid[6]; NMWifiAP *ap; + if (g_hash_table_lookup (aps, path)) { + _LOGD (LOGD_WIFI, "Duplicate network at %s", path); + return; + } + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), path, NM_IWD_NETWORK_INTERFACE); @@ -304,7 +315,7 @@ static void get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDeviceIwdPrivate *priv; gs_free_error GError *error = NULL; gs_unref_variant GVariant *variant = NULL; GVariantIter *networks; @@ -314,33 +325,42 @@ get_ordered_networks_cb (GObject *source, GAsyncResult *res, gpointer user_data) gboolean changed = FALSE; GHashTableIter ap_iter; gs_unref_hashtable GHashTable *new_aps = NULL; - /* Depending on whether we're using the Station interface or the Device - * interface for compatibility with IWD <= 0.7, the return signature of - * GetOrderedNetworks will be different. - */ - gboolean compat = priv->dbus_station_proxy == priv->dbus_device_proxy; - const char *return_sig = compat ? "(a(osns))" : "(a(on))"; + gboolean compat; + const char *return_sig; static uint32_t ap_id = 0; - variant = _nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE (return_sig), - &error); + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); if (!variant) { _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks failed: %s", error->message); return; } + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + /* Depending on whether we're using the Station interface or the Device + * interface for compatibility with IWD <= 0.7, the return signature of + * GetOrderedNetworks will be different. + */ + compat = priv->dbus_station_proxy == priv->dbus_device_proxy; + return_sig = compat ? "(a(osns))" : "(a(on))"; + + if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (return_sig))) { + _LOGE (LOGD_WIFI, "Station.GetOrderedNetworks returned type %s instead of %s", + g_variant_get_type_string (variant), return_sig); + return; + } + new_aps = g_hash_table_new_full (nm_str_hash, g_str_equal, NULL, g_object_unref); g_variant_get (variant, return_sig, &networks); if (compat) { while (g_variant_iter_next (networks, "(&o&sn&s)", &path, &name, &signal, &type)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } else { while (g_variant_iter_next (networks, "(&on)", &path, &signal)) - insert_ap_from_network (new_aps, path, signal, ap_id++); + insert_ap_from_network (self, new_aps, path, signal, ap_id++); } g_variant_iter_free (networks); @@ -393,7 +413,7 @@ update_aps (NMDeviceIwd *self) priv->cancellable = g_cancellable_new (); g_dbus_proxy_call (priv->dbus_station_proxy, "GetOrderedNetworks", - g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, + NULL, G_DBUS_CALL_FLAGS_NONE, 2000, priv->cancellable, get_ordered_networks_cb, self); } @@ -403,8 +423,8 @@ send_disconnect (NMDeviceIwd *self) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); } static void @@ -431,55 +451,104 @@ cleanup_association_attempt (NMDeviceIwd *self, gboolean disconnect) } static void +reset_mode (NMDeviceIwd *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new_string ("station")), + G_DBUS_CALL_FLAGS_NONE, 2000, + cancellable, + callback, + user_data); +} + +static void deactivate (NMDevice *device) { - cleanup_association_attempt (NM_DEVICE_IWD (device), TRUE); + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (!priv->dbus_obj) + return; + + cleanup_association_attempt (self, TRUE); + priv->act_mode_switch = FALSE; + + if (!priv->dbus_station_proxy) + reset_mode (self, NULL, NULL, NULL); } -static gboolean -deactivate_async_finish (NMDevice *device, GAsyncResult *res, GError **error) +static void +disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; - variant = g_dbus_proxy_call_finish (priv->dbus_station_proxy, res, error); - return variant != NULL; -} + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); -typedef struct { - NMDeviceIwd *self; - GAsyncReadyCallback callback; - gpointer user_data; -} DeactivateContext; + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + callback (NM_DEVICE (self), error, callback_user_data); +} static void -disconnect_cb (GObject *source, GAsyncResult *res, gpointer user_data) +disconnect_cb_on_idle (gpointer user_data, + GCancellable *cancellable) { - DeactivateContext *ctx = user_data; + gs_unref_object NMDeviceIwd *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; + gs_free_error GError *cancelled_error = NULL; - ctx->callback (G_OBJECT (ctx->self), res, ctx->user_data); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); - g_object_unref (ctx->self); - g_slice_free (DeactivateContext, ctx); + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + callback (NM_DEVICE (self), cancelled_error, callback_user_data); } static void deactivate_async (NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) + NMDeviceDeactivateCallback callback, + gpointer callback_user_data) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - DeactivateContext *ctx; + gpointer user_data; - ctx = g_slice_new0 (DeactivateContext); - ctx->self = g_object_ref (self); - ctx->callback = callback; - ctx->user_data = user_data; + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); - g_dbus_proxy_call (priv->dbus_station_proxy, "Disconnect", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, cancellable, disconnect_cb, ctx); + user_data = nm_utils_user_data_pack (g_object_ref (self), callback, callback_user_data); + + if (!priv->dbus_obj) { + nm_utils_invoke_on_idle (disconnect_cb_on_idle, user_data, cancellable); + return; + } + + cleanup_association_attempt (self, FALSE); + priv->act_mode_switch = FALSE; + + if (priv->dbus_station_proxy) { + g_dbus_proxy_call (priv->dbus_station_proxy, + "Disconnect", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + cancellable, + disconnect_cb, + user_data); + } else + reset_mode (self, cancellable, disconnect_cb, user_data); } static gboolean @@ -510,13 +579,37 @@ is_connection_known_network (NMConnection *connection) } static gboolean +is_ap_known_network (NMWifiAP *ap) +{ + GDBusProxy *network_proxy; + gs_unref_variant GVariant *known_network = NULL; + + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) + return FALSE; + + known_network = g_dbus_proxy_get_cached_property (network_proxy, "KnownNetwork"); + g_object_unref (network_proxy); + + return known_network + && g_variant_is_of_type (known_network, G_VARIANT_TYPE_OBJECT_PATH); +} + +static gboolean check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) { + NMDeviceIwd *self = NM_DEVICE_IWD (device); + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wireless; const char *mac; const char * const *mac_blacklist; int i; const char *perm_hw_addr; + const char *mode; + NMIwdNetworkSecurity security; + gboolean mapped; if (!NM_DEVICE_CLASS (nm_device_iwd_parent_class)->check_connection_compatible (device, connection, error)) return FALSE; @@ -549,23 +642,65 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError return FALSE; } - if (!NM_IN_STRSET (nm_setting_wireless_get_mode (s_wireless), - NULL, - NM_SETTING_WIRELESS_MODE_INFRA)) { + /* Hidden SSIDs not supported in any mode (client or AP) */ + if (nm_setting_wireless_get_hidden (s_wireless)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "IWD only support infrastructure type profiles"); + "hidden networks not supported by the IWD backend"); return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { + security = nm_wifi_connection_get_iwd_security (connection, &mapped); + if (!mapped) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "connection authentication type not supported by IWD backend"); + return FALSE; + } + + mode = nm_setting_wireless_get_mode (s_wireless); + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (security == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_connection_known_network (connection)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x connections must have IWD provisioning files"); + return FALSE; + } + } else if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports Open, PSK and 802.1x network " + "authentication in Infrastructure mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_AP)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Access Point mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_PSK)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "IWD backend only supports PSK authentication in AP mode"); + return FALSE; + } + } else if (nm_streq (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + if (!(priv->capabilities & NM_WIFI_DEVICE_CAP_ADHOC)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "device does not support Ad-Hoc mode"); + return FALSE; + } + + if (!NM_IN_SET (security, NM_IWD_NETWORK_SECURITY_NONE, NM_IWD_NETWORK_SECURITY_PSK)) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "802.1x profile is not a known network"); + "IWD backend only supports Open and PSK authentication in Ad-Hoc mode"); return FALSE; } + } else { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "%s type profiles not supported by IWD backend"); + return FALSE; } return TRUE; @@ -582,42 +717,15 @@ check_connection_available (NMDevice *device, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMSettingWireless *s_wifi; const char *mode; + NMWifiAP *ap = NULL; s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode at this time */ - mode = nm_setting_wireless_get_mode (s_wifi); - if (!NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "iwd only supports infrastructure mode connections"); - return FALSE; - } - - /* Hidden SSIDs not supported yet */ - if (nm_setting_wireless_get_hidden (s_wifi)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "hidden networks not supported by iwd"); - return FALSE; - } - - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "network is not known to iwd"); - return FALSE; - } - } - /* a connection that is available for a certain @specific_object, MUST * also be available in general (without @specific_object). */ if (specific_object) { - NMWifiAP *ap; - ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, @@ -629,18 +737,36 @@ check_connection_available (NMDevice *device, "requested access point is not compatible with profile"); return FALSE; } - return TRUE; } + /* AP and Ad-Hoc connections can be activated independent of the scan list */ + mode = nm_setting_wireless_get_mode (s_wifi); + if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) + return TRUE; + if (NM_FLAGS_HAS (flags, _NM_DEVICE_CHECK_CON_AVAILABLE_FOR_USER_REQUEST_IGNORE_AP)) return TRUE; - if (!nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection)) { + if (!ap) + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + + if (!ap) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, "no compatible access point found"); return FALSE; } + /* 8021x networks can only be used if they've been provisioned on the IWD side and + * thus are Known Networks. + */ + if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { + if (!is_ap_known_network (ap)) { + nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, + "802.1x network is not an IWD Known Network (missing provisioning file?)"); + return FALSE; + } + } + return TRUE; } @@ -666,15 +792,11 @@ complete_connection (NMDevice *device, mode = s_wifi ? nm_setting_wireless_get_mode (s_wifi) : NULL; - if (mode && !nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "Only Infrastructure mode is supported."); - return FALSE; - } - - if (!specific_object) { + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) { + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; + ap = NULL; + } else if (!specific_object) { /* If not given a specific object, we need at minimum an SSID */ if (!s_wifi) { g_set_error_literal (error, @@ -696,11 +818,16 @@ complete_connection (NMDevice *device, /* Find a compatible AP in the scan list */ ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (!ap) { - g_set_error_literal (error, - NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "No compatible AP in the scan list and hidden SSIDs not supported."); - return FALSE; + if (!nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) { + g_set_error_literal (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "No compatible AP in the scan list and hidden SSIDs not supported."); + return FALSE; + } + + if (!nm_setting_verify (NM_SETTING (s_wifi), connection, error)) + return FALSE; } } else { ap = nm_wifi_ap_lookup_for_device (NM_DEVICE (self), specific_object); @@ -720,7 +847,10 @@ complete_connection (NMDevice *device, nm_connection_add_setting (connection, NM_SETTING (s_wifi)); } - ssid = nm_wifi_ap_get_ssid (ap); + ssid = nm_setting_wireless_get_ssid (s_wifi); + if (!ssid && ap) + ssid = nm_wifi_ap_get_ssid (ap); + if (!ssid) { g_set_error_literal (error, NM_DEVICE_ERROR, @@ -729,11 +859,13 @@ complete_connection (NMDevice *device, return FALSE; } - if (!nm_wifi_ap_complete_connection (ap, - connection, - nm_wifi_utils_is_manf_default_ssid (ssid), - error)) - return FALSE; + if (ap) { + if (!nm_wifi_ap_complete_connection (ap, + connection, + nm_wifi_utils_is_manf_default_ssid (ssid), + error)) + return FALSE; + } ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); nm_utils_complete_generic (nm_device_get_platform (device), @@ -745,19 +877,6 @@ complete_connection (NMDevice *device, NULL, TRUE); - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "This 8021x network has not been provisioned on this machine"); - return FALSE; - } - } - perm_hw_addr = nm_device_get_permanent_hw_address (device); if (perm_hw_addr) { setting_mac = nm_setting_wireless_get_mac_address (s_wifi); @@ -820,8 +939,21 @@ is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) { NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - - return priv->enabled && priv->dbus_station_proxy; + NMDeviceState state = nm_device_get_state (device); + + /* Available if either the device is UP and in station mode + * or in AP/Ad-Hoc modes while activating or activated. Device + * may be temporarily DOWN while activating or deactivating and + * we don't want it to be marked unavailable because of this. + * + * For reference: + * We call nm_device_queue_recheck_available whenever + * priv->enabled changes or priv->dbus_station_proxy changes. + */ + return priv->dbus_obj + && priv->enabled + && ( priv->dbus_station_proxy + || (state >= NM_DEVICE_STATE_CONFIG && state <= NM_DEVICE_STATE_DEACTIVATING)); } static gboolean @@ -829,8 +961,7 @@ get_autoconnect_allowed (NMDevice *device) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (NM_DEVICE_IWD (device)); - return is_available (device, NM_DEVICE_CHECK_DEV_AVAILABLE_NONE) - && priv->can_connect; + return priv->can_connect; } static gboolean @@ -856,7 +987,9 @@ can_auto_connect (NMDevice *device, s_wifi = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wifi, FALSE); - /* Only Infrastrusture mode */ + /* Don't auto-activate AP or Ad-Hoc connections. + * Note the wpa_supplicant backend has the opposite policy. + */ mode = nm_setting_wireless_get_mode (s_wifi); if (mode && g_strcmp0 (mode, NM_SETTING_WIRELESS_MODE_INFRA) != 0) return FALSE; @@ -870,14 +1003,6 @@ can_auto_connect (NMDevice *device, return FALSE; } - /* 8021x networks can only be used if they've been provisioned on the IWD side and - * thus are Known Networks. - */ - if (nm_wifi_connection_get_iwd_security (connection, NULL) == NM_IWD_NETWORK_SECURITY_8021X) { - if (!is_connection_known_network (connection)) - return FALSE; - } - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); if (ap) { /* All good; connection is usable */ @@ -908,11 +1033,11 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) { NMDeviceIwd *self = user_data; NMDeviceIwdPrivate *priv; + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; - if ( !_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), &error) - && g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) return; priv = NM_DEVICE_IWD_GET_PRIVATE (self); @@ -925,11 +1050,8 @@ scan_cb (GObject *source, GAsyncResult *res, gpointer user_data) * scheduled when priv->scanning goes back to false. On error, * schedule a retry now. */ - if (error && !priv->scanning) { - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); - - schedule_periodic_scan (self, state); - } + if (error && !priv->scanning) + schedule_periodic_scan (self, FALSE); } static void @@ -958,9 +1080,7 @@ dbus_request_scan_cb (NMDevice *device, priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (context, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -982,8 +1102,7 @@ dbus_request_scan_cb (NMDevice *device, if (!priv->scanning && !priv->scan_requested) { g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; } @@ -999,9 +1118,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - if ( !priv->can_scan - || nm_device_get_state (device) < NM_DEVICE_STATE_DISCONNECTED - || nm_device_is_activating (device)) { + if (!priv->can_scan) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_NOT_ALLOWED, @@ -1013,7 +1130,7 @@ _nm_device_iwd_request_scan (NMDeviceIwd *self, NM_DEVICE_AUTH_REQUEST, invocation, NULL, - NM_AUTH_PERMISSION_NETWORK_CONTROL, + NM_AUTH_PERMISSION_WIFI_SCAN, TRUE, dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); @@ -1041,8 +1158,6 @@ scanning_prohibited (NMDeviceIwd *self, gboolean periodic) return TRUE; case NM_DEVICE_STATE_DISCONNECTED: case NM_DEVICE_STATE_FAILED: - /* Can always scan when disconnected */ - return FALSE; case NM_DEVICE_STATE_ACTIVATED: break; } @@ -1199,7 +1314,7 @@ wifi_secrets_cb (NMActRequest *req, priv->wifi_secrets_id = NULL; - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + if (nm_utils_error_is_cancelled (error, FALSE)) { g_dbus_method_invocation_return_error_literal (invocation, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "NM secrets request cancelled"); @@ -1263,7 +1378,7 @@ wifi_secrets_get_one (NMDeviceIwd *self, TRUE, setting_name, flags, - setting_key, + NM_MAKE_STRV (setting_key), wifi_secrets_cb, nm_utils_user_data_pack (self, invocation)); } @@ -1274,6 +1389,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceIwd *self = user_data; NMDevice *device = NM_DEVICE (self); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + gs_unref_variant GVariant *variant = NULL; gs_free_error GError *error = NULL; NMConnection *connection; NMSettingWireless *s_wifi; @@ -1282,9 +1398,8 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED; GVariant *value; - if (!_nm_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, - G_VARIANT_TYPE ("()"), - &error)) { + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { gs_free char *dbus_error = NULL; /* Connection failed; radio problems or if the network wasn't @@ -1295,7 +1410,7 @@ network_connect_cb (GObject *source, GAsyncResult *res, gpointer user_data) "Activation: (wifi) Network.Connect failed: %s", error->message); - if (nm_utils_error_is_cancelled (error, TRUE)) + if (nm_utils_error_is_cancelled (error, FALSE)) return; if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) @@ -1359,12 +1474,262 @@ failed: } static void +act_failed_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant && nm_utils_error_is_cancelled (error, FALSE)) + return; + + /* Change state to FAILED unless already done by state_changed + * which may have been triggered by the station interface + * appearing on DBus. + */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG) + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); +} + +static void +act_start_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + NMSettingWireless *s_wireless; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Network.Connect failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + goto error; + } + + nm_assert (nm_device_get_state (device) == NM_DEVICE_STATE_CONFIG); + + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + if (!s_wireless) + goto error; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto error; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Stage 2 of 5 (Device Configure) successful. Started '%s'.", + ssid_utf8); + + nm_device_activate_schedule_stage3_ip_config_start (device); + return; + +error: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +/* Check if we're activating an AP/AdHoc connection and if the target + * DBus interface has appeared already. If so proceed to call Start or + * StartOpen on that interface. + */ +static void +act_check_interface (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wireless_sec; + GDBusProxy *proxy = NULL; + GBytes *ssid; + gs_free char *ssid_utf8 = NULL; + const char *mode; + + if (!priv->act_mode_switch) + return; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + proxy = priv->dbus_ap_proxy; + else if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_ADHOC)) + proxy = priv->dbus_adhoc_proxy; + + if (!proxy) + return; + + priv->act_mode_switch = FALSE; + + if (!NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG)) + return; + + ssid = nm_setting_wireless_get_ssid (s_wireless); + if (!ssid) + goto failed; + + ssid_utf8 = _nm_utils_ssid_to_utf8 (ssid); + + s_wireless_sec = (NMSettingWirelessSecurity *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS_SECURITY); + + if (!s_wireless_sec) { + g_dbus_proxy_call (proxy, "StartOpen", + g_variant_new ("(s)", ssid_utf8), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } else { + const char *psk = nm_setting_wireless_security_get_psk (s_wireless_sec); + + if (!psk) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) No PSK for '%s'.", + ssid_utf8); + goto failed; + } + + g_dbus_proxy_call (proxy, "Start", + g_variant_new ("(ss)", ssid_utf8, psk), + G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, act_start_cb, self); + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Called Start('%s').", + ssid_utf8); + return; + +failed: + reset_mode (self, priv->cancellable, act_failed_cb, self); +} + +static void +act_set_mode_cb (GObject *source, GAsyncResult *res, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + gs_unref_variant GVariant *variant = NULL; + gs_free_error GError *error = NULL; + + variant = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); + if (!variant) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) Setting Device.Mode failed: %s", + error->message); + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + if ( !NM_IN_SET (nm_device_get_state (device), NM_DEVICE_STATE_CONFIG) + || !priv->act_mode_switch) + return; + + priv->act_mode_switch = FALSE; + nm_device_queue_state (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) IWD Device.Mode set successfully"); + + act_check_interface (self); +} + +static void +act_set_mode (NMDeviceIwd *self) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + const char *iwd_mode; + const char *mode; + NMSettingWireless *s_wireless; + + s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + mode = nm_setting_wireless_get_mode (s_wireless); + + /* We need to first set interface mode (Device.Mode) to ap or ad-hoc. + * We can't directly queue a call to the Start/StartOpen method on + * the DBus interface that's going to be created after the property + * set call returns. + */ + iwd_mode = nm_streq (mode, NM_SETTING_WIRELESS_MODE_AP) ? "ap" : "ad-hoc"; + + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + g_dbus_proxy_call (priv->dbus_device_proxy, + DBUS_INTERFACE_PROPERTIES ".Set", + g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, + "Mode", + g_variant_new ("s", iwd_mode)), + G_DBUS_CALL_FLAGS_NONE, 2000, + priv->cancellable, act_set_mode_cb, self); + priv->act_mode_switch = TRUE; +} + +static void +act_psk_cb (NMActRequest *req, + NMActRequestGetSecretsCallId *call_id, + NMSettingsConnection *s_connection, + GError *error, + gpointer user_data) +{ + NMDeviceIwd *self = user_data; + NMDeviceIwdPrivate *priv; + NMDevice *device; + + if (nm_utils_error_is_cancelled (error, FALSE)) + return; + + priv = NM_DEVICE_IWD_GET_PRIVATE (self); + device = NM_DEVICE (self); + + g_return_if_fail (priv->wifi_secrets_id == call_id); + priv->wifi_secrets_id = NULL; + + g_return_if_fail (req == nm_device_get_act_request (device)); + g_return_if_fail (nm_act_request_get_settings_connection (req) == s_connection); + + if (nm_device_get_state (device) != NM_DEVICE_STATE_NEED_AUTH) + goto secrets_error; + + if (error) { + _LOGW (LOGD_WIFI, "%s", error->message); + goto secrets_error; + } + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "Activation: (wifi) missing PSK request completed"); + + /* Change state back to what it was before NEED_AUTH */ + nm_device_state_changed (device, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE); + act_set_mode (self); + return; + +secrets_error: + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); + cleanup_association_attempt (self, FALSE); +} + +static void set_powered (NMDeviceIwd *self, gboolean powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); g_dbus_proxy_call (priv->dbus_device_proxy, - "org.freedesktop.DBus.Properties.Set", + DBUS_INTERFACE_PROPERTIES ".Set", g_variant_new ("(ssv)", NM_IWD_DEVICE_INTERFACE, "Powered", g_variant_new ("b", powered)), @@ -1384,6 +1749,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMActRequest *req; NMConnection *connection; NMSettingWireless *s_wireless; + const char *mode; const char *ap_path; ret = NM_DEVICE_CLASS (nm_device_iwd_parent_class)->act_stage1_prepare (device, out_failure_reason); @@ -1399,20 +1765,51 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) s_wireless = nm_connection_get_setting_wireless (connection); g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); + /* AP mode never uses a specific object or existing scanned AP */ + mode = nm_setting_wireless_get_mode (s_wireless); + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) + goto add_new; + ap_path = nm_active_connection_get_specific_object (NM_ACTIVE_CONNECTION (req)); ap = ap_path ? nm_wifi_ap_lookup_for_device (NM_DEVICE (self), ap_path) : NULL; - if (!ap) { - ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - return NM_ACT_STAGE_RETURN_FAILURE; - } + if (ap) { + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + ap = nm_wifi_aps_find_first_compatible (&priv->aps_lst_head, connection); + if (ap) { nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + set_current_ap (self, ap, TRUE); + return NM_ACT_STAGE_RETURN_SUCCESS; + } + + if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_INFRA)) { + /* Hidden networks not supported at this time */ + return NM_ACT_STAGE_RETURN_FAILURE; } +add_new: + /* If the user is trying to connect to an AP that NM doesn't yet know about + * (hidden network or something) or starting a Hotspot, create an fake AP + * from the security settings in the connection. This "fake" AP gets used + * until the real one is found in the scan list (Ad-Hoc or Hidden), or until + * the device is deactivated (Ad-Hoc or Hotspot). + */ + ap = nm_wifi_ap_new_fake_from_connection (connection); + g_return_val_if_fail (ap != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + if (nm_wifi_ap_is_hotspot (ap)) + nm_wifi_ap_set_address (ap, nm_device_get_hw_address (device)); + + g_object_freeze_notify (G_OBJECT (self)); + ap_add_remove (self, TRUE, ap, FALSE); + g_object_thaw_notify (G_OBJECT (self)); set_current_ap (self, ap, FALSE); + nm_active_connection_set_specific_object (NM_ACTIVE_CONNECTION (req), + nm_dbus_object_get_path (NM_DBUS_OBJECT (ap))); + g_object_unref (ap); return NM_ACT_STAGE_RETURN_SUCCESS; } @@ -1423,60 +1820,85 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; NMActRequest *req; - NMWifiAP *ap; NMConnection *connection; - GDBusProxy *network_proxy; + NMSettingWireless *s_wireless; + const char *mode; req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - ap = priv->current_ap; - if (!ap) { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } - connection = nm_act_request_get_applied_connection (req); g_assert (connection); - /* 802.1x networks that are not IWD Known Networks will definitely - * fail, for other combinations we will let the Connect call fail - * or ask us for any missing secrets through the Agent. - */ - if ( !is_connection_known_network (connection) - && nm_connection_get_setting_802_1x (connection)) { - _LOGI (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) access point '%s' has 802.1x security, but is not configured.", - nm_connection_get_id (connection)); + s_wireless = nm_connection_get_setting_wireless (connection); + g_return_val_if_fail (s_wireless, NM_ACT_STAGE_RETURN_FAILURE); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - ret = NM_ACT_STAGE_RETURN_FAILURE; - goto out; - } + mode = nm_setting_wireless_get_mode (s_wireless); + if (NM_IN_STRSET (mode, NULL, NM_SETTING_WIRELESS_MODE_INFRA)) { + GDBusProxy *network_proxy; + NMWifiAP *ap = priv->current_ap; - network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), - nm_wifi_ap_get_supplicant_path (ap), - NM_IWD_NETWORK_INTERFACE); - if (!network_proxy) { - _LOGE (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get Network interface proxy for %s", - nm_wifi_ap_get_supplicant_path (ap)); - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - goto out; - } + if (!ap) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - if (!priv->cancellable) - priv->cancellable = g_cancellable_new (); + /* 802.1x networks that are not IWD Known Networks will definitely + * 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)) { + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) access point '%s' has 802.1x security but is not configured in IWD.", + nm_connection_get_id (connection)); + + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + goto out; + } - /* Call Network.Connect. No timeout because IWD already handles - * timeouts. - */ - g_dbus_proxy_call (network_proxy, "Connect", - g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, G_MAXINT, - priv->cancellable, network_connect_cb, self); + network_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + nm_wifi_ap_get_supplicant_path (ap), + NM_IWD_NETWORK_INTERFACE); + if (!network_proxy) { + _LOGE (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get Network interface proxy for %s", + nm_wifi_ap_get_supplicant_path (ap)); + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + goto out; + } - g_object_unref (network_proxy); + if (!priv->cancellable) + priv->cancellable = g_cancellable_new (); + + /* Call Network.Connect. No timeout because IWD already handles + * timeouts. + */ + g_dbus_proxy_call (network_proxy, "Connect", + NULL, G_DBUS_CALL_FLAGS_NONE, G_MAXINT, + priv->cancellable, network_connect_cb, self); + + g_object_unref (network_proxy); + } else if (NM_IN_STRSET (mode, NM_SETTING_WIRELESS_MODE_AP, NM_SETTING_WIRELESS_MODE_ADHOC)) { + NMSettingWirelessSecurity *s_wireless_sec; + + s_wireless_sec = nm_connection_get_setting_wireless_security (connection); + if (s_wireless_sec && !nm_setting_wireless_security_get_psk (s_wireless_sec)) { + /* PSK is missing from the settings, have to request it */ + + wifi_secrets_cancel (self); + + priv->wifi_secrets_id = nm_act_request_get_secrets (req, + TRUE, + NM_SETTING_WIRELESS_SECURITY_SETTING_NAME, + NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION, + NM_MAKE_STRV (NM_SETTING_WIRELESS_SECURITY_PSK), + act_psk_cb, + self); + nm_device_state_changed (device, NM_DEVICE_STATE_NEED_AUTH, NM_DEVICE_STATE_REASON_NONE); + } else + act_set_mode (self); + } /* We'll get stage3 started when the supplicant connects */ ret = NM_ACT_STAGE_RETURN_POSTPONE; @@ -1507,8 +1929,8 @@ periodic_scan_timeout_cb (gpointer user_data) if (priv->scanning || priv->scan_requested) return FALSE; - g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", g_variant_new ("()"), - G_DBUS_CALL_FLAGS_NONE, -1, + g_dbus_proxy_call (priv->dbus_station_proxy, "Scan", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, priv->cancellable, scan_cb, self); priv->scan_requested = TRUE; @@ -1516,18 +1938,37 @@ periodic_scan_timeout_cb (gpointer user_data) } static void -schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) +schedule_periodic_scan (NMDeviceIwd *self, gboolean initial_scan) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GVariant *value; + gboolean disconnected; guint interval; - if (current_state <= NM_DEVICE_STATE_UNAVAILABLE) + if (!priv->can_scan || priv->scan_requested) return; - if (current_state == NM_DEVICE_STATE_DISCONNECTED) - interval = 10; + value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "State"); + disconnected = nm_streq0 (get_variant_state (value), "disconnected"); + g_variant_unref (value); + + /* Start scan immediately after a disconnect, mode change or + * device UP, otherwise wait a period dependent on the current + * state. + * + * (initial_scan && disconnected) override priv->scanning below + * because of an IWD quirk where a device will often be in the + * autoconnect state and scanning at the time of our initial_scan, + * but our logic will the send it a Disconnect() causeing IWD to + * exit autoconnect and interrupt the ongoing scan, meaning that + * we still want a new scan ASAP. + */ + if (initial_scan && disconnected) + interval = 0; + else if (!priv->periodic_scan_id && !priv->scanning) + interval = disconnected ? 10 : 20; else - interval = 20; + return; nm_clear_g_source (&priv->periodic_scan_id); priv->periodic_scan_id = g_timeout_add_seconds (interval, @@ -1536,6 +1977,19 @@ schedule_periodic_scan (NMDeviceIwd *self, NMDeviceState current_state) } static void +set_can_scan (NMDeviceIwd *self, gboolean can_scan) +{ + NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + + if (priv->can_scan == can_scan) + return; + + priv->can_scan = can_scan; + + schedule_periodic_scan (self, TRUE); +} + +static void device_state_changed (NMDevice *device, NMDeviceState new_state, NMDeviceState old_state, @@ -1544,14 +1998,6 @@ device_state_changed (NMDevice *device, NMDeviceIwd *self = NM_DEVICE_IWD (device); NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { - remove_all_aps (self); - nm_clear_g_source (&priv->periodic_scan_id); - } else if (old_state <= NM_DEVICE_STATE_UNAVAILABLE) { - update_aps (self); - schedule_periodic_scan (self, new_state); - } - switch (new_state) { case NM_DEVICE_STATE_UNMANAGED: break; @@ -1669,10 +2115,13 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_MODE: - if (priv->current_ap) - g_value_set_uint (value, NM_802_11_MODE_INFRA); - else + if (!priv->current_ap) g_value_set_uint (value, NM_802_11_MODE_UNKNOWN); + else if (nm_wifi_ap_is_hotspot (priv->current_ap)) + g_value_set_uint (value, NM_802_11_MODE_AP); + else + g_value_set_uint (value, nm_wifi_ap_get_mode (priv->current_ap)); + break; case PROP_BITRATE: g_value_set_uint (value, 65000); @@ -1702,24 +2151,6 @@ get_property (GObject *object, guint prop_id, } } -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMDeviceIwd *device = NM_DEVICE_IWD (object); - NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (device); - - switch (prop_id) { - case PROP_CAPABILITIES: - /* construct-only */ - priv->capabilities = g_value_get_uint (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - /*****************************************************************************/ static void @@ -1729,7 +2160,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) NMDevice *device = NM_DEVICE (self); NMDeviceState dev_state = nm_device_get_state (device); gboolean iwd_connection = FALSE; - gboolean can_connect; + gboolean can_connect = priv->can_connect; _LOGI (LOGD_DEVICE | LOGD_WIFI, "new IWD device state is %s", new_state); @@ -1738,7 +2169,9 @@ state_changed (NMDeviceIwd *self, const char *new_state) iwd_connection = TRUE; /* Don't allow scanning while connecting, disconnecting or roaming */ - priv->can_scan = NM_IN_STRSET (new_state, "connected", "disconnected"); + set_can_scan (self, NM_IN_STRSET (new_state, "connected", "disconnected")); + + priv->can_connect = FALSE; if (NM_IN_STRSET (new_state, "connecting", "connected", "roaming")) { /* If we were connecting, do nothing, the confirmation of @@ -1766,7 +2199,7 @@ state_changed (NMDeviceIwd *self, const char *new_state) * callback will have more information on the specific failure * reason. */ - if (dev_state == NM_DEVICE_STATE_CONFIG) + if (NM_IN_SET (dev_state, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_NEED_AUTH)) return; if (iwd_connection) @@ -1781,10 +2214,10 @@ state_changed (NMDeviceIwd *self, const char *new_state) /* Don't allow new connection until iwd exits disconnecting and no * Connect callback is pending. */ - can_connect = NM_IN_STRSET (new_state, "disconnected"); - if (can_connect != priv->can_connect) { - priv->can_connect = can_connect; - nm_device_emit_recheck_auto_activate (device); + if (NM_IN_STRSET (new_state, "disconnected")) { + priv->can_connect = TRUE; + if (!can_connect) + nm_device_emit_recheck_auto_activate (device); } } @@ -1792,7 +2225,6 @@ static void scanning_changed (NMDeviceIwd *self, gboolean new_scanning) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); - NMDeviceState state = nm_device_get_state (NM_DEVICE (self)); if (new_scanning == priv->scanning) return; @@ -1805,7 +2237,7 @@ scanning_changed (NMDeviceIwd *self, gboolean new_scanning) update_aps (self); if (!priv->scan_requested) - schedule_periodic_scan (self, state); + schedule_periodic_scan (self, FALSE); } } @@ -1814,40 +2246,81 @@ station_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; + const char *new_str; + gboolean new_bool; - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "State")) - state_changed (self, get_variant_state (value)); + if (g_variant_lookup (changed_properties, "State", "&s", &new_str)) + state_changed (self, new_str); - if (!strcmp (key, "Scanning")) - scanning_changed (self, get_variant_boolean (value, "Scanning")); + if (g_variant_lookup (changed_properties, "Scanning", "b", &new_bool)) + scanning_changed (self, new_bool); +} - g_variant_unref (value); - } +static void +ap_adhoc_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, + GStrv invalidate_properties, gpointer user_data) +{ + NMDeviceIwd *self = user_data; + gboolean new_bool; - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Started", "b", &new_bool)) + _LOGI (LOGD_DEVICE | LOGD_WIFI, "IWD AP/AdHoc state is now %s", new_bool ? "Started" : "Stopped"); } static void powered_changed (NMDeviceIwd *self, gboolean new_powered) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); + GDBusInterface *interface; + GVariant *value; nm_device_queue_recheck_available (NM_DEVICE (self), NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - if (new_powered) { - GDBusInterface *interface; - GVariant *value; + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_AP_INTERFACE) : NULL; - if (priv->dbus_station_proxy) - return; + if (priv->dbus_ap_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_ap_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_ap_proxy); + } + + if (interface) { + priv->dbus_ap_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_ap_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + interface = new_powered ? g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_ADHOC_INTERFACE) : NULL; + + if (priv->dbus_adhoc_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_adhoc_proxy, + ap_adhoc_properties_changed, self); + g_clear_object (&priv->dbus_adhoc_proxy); + } + + if (interface) { + priv->dbus_adhoc_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_adhoc_proxy, "g-properties-changed", + G_CALLBACK (ap_adhoc_properties_changed), self); + if (priv->act_mode_switch) + act_check_interface (self); + else + reset_mode (self, NULL, NULL, NULL); + } + + /* We expect one of the three interfaces to always be present when + * device is Powered so if AP and AdHoc are not present we should + * be in station mode. + */ + if (new_powered && !priv->dbus_ap_proxy && !priv->dbus_adhoc_proxy) { interface = g_dbus_object_get_interface (priv->dbus_obj, NM_IWD_STATION_INTERFACE); if (!interface) { /* No Station interface on the device object. Check if the @@ -1858,18 +2331,30 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) * priv->dbus_station_proxy both point at the Device interface. */ value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "State"); - if (!value) { + if (value) { + g_variant_unref (value); + interface = g_object_ref (G_DBUS_INTERFACE (priv->dbus_device_proxy)); + } else { _LOGE (LOGD_WIFI, "Interface %s not found on obj %s", NM_IWD_STATION_INTERFACE, g_dbus_object_get_object_path (priv->dbus_obj)); - return; + interface = NULL; } - g_variant_unref (value); - interface = g_object_ref (priv->dbus_device_proxy); } + } else + interface = NULL; + + if (priv->dbus_station_proxy) { + g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, + station_properties_changed, self); + g_clear_object (&priv->dbus_station_proxy); + } + if (interface) { priv->dbus_station_proxy = G_DBUS_PROXY (interface); + g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", + G_CALLBACK (station_properties_changed), self); value = g_dbus_proxy_get_cached_property (priv->dbus_station_proxy, "Scanning"); priv->scanning = get_variant_boolean (value, "Scanning"); @@ -1879,27 +2364,15 @@ powered_changed (NMDeviceIwd *self, gboolean new_powered) state_changed (self, get_variant_state (value)); g_variant_unref (value); - g_signal_connect (priv->dbus_station_proxy, "g-properties-changed", - G_CALLBACK (station_properties_changed), self); - - /* Call Disconnect to make sure IWD's autoconnect is disabled. - * Autoconnect is the default state after device is brought UP. - */ - if (priv->enabled) - send_disconnect (self); + update_aps (self); } else { - if (!priv->dbus_station_proxy) - return; - - g_signal_handlers_disconnect_by_func (priv->dbus_station_proxy, - station_properties_changed, self); - g_clear_object (&priv->dbus_station_proxy); - - priv->can_scan = FALSE; + set_can_scan (self, FALSE); + nm_clear_g_source (&priv->periodic_scan_id); priv->scanning = FALSE; priv->scan_requested = FALSE; priv->can_connect = FALSE; cleanup_association_attempt (self, FALSE); + remove_all_aps (self); } } @@ -1908,19 +2381,10 @@ device_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidate_properties, gpointer user_data) { NMDeviceIwd *self = user_data; - GVariantIter *iter; - const char *key; - GVariant *value; - - g_variant_get (changed_properties, "a{sv}", &iter); - while (g_variant_iter_next (iter, "{&sv}", &key, &value)) { - if (!strcmp (key, "Powered")) - powered_changed (self, get_variant_boolean (value, "Powered")); + gboolean new_bool; - g_variant_unref (value); - } - - g_variant_iter_free (iter); + if (g_variant_lookup (changed_properties, "Powered", "b", &new_bool)) + powered_changed (self, new_bool); } void @@ -1928,24 +2392,24 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) { NMDeviceIwdPrivate *priv = NM_DEVICE_IWD_GET_PRIVATE (self); GDBusInterface *interface; - GVariant *value; + gs_unref_variant GVariant *value = NULL; + gs_unref_object GDBusProxy *adapter_proxy = NULL; + GVariantIter *iter; + const char *mode; gboolean powered; + NMDeviceWifiCapabilities capabilities; - if (!nm_g_object_ref_set ((GObject **) &priv->dbus_obj, (GObject *) object)) + if (!nm_g_object_ref_set (&priv->dbus_obj, object)) return; - if (priv->enabled && priv->dbus_station_proxy) { - nm_device_queue_recheck_available (NM_DEVICE (self), - NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, - NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); - } - if (priv->dbus_device_proxy) { g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, device_properties_changed, self); g_clear_object (&priv->dbus_device_proxy); powered_changed (self, FALSE); + + priv->act_mode_switch = FALSE; } if (!object) @@ -1965,14 +2429,63 @@ nm_device_iwd_set_dbus_object (NMDeviceIwd *self, GDBusObject *object) g_signal_connect (priv->dbus_device_proxy, "g-properties-changed", G_CALLBACK (device_properties_changed), self); + /* Parse list of interface modes supported by adapter (wiphy) */ + + value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Adapter"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_OBJECT_PATH)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Adapter property not cached or not an object path"); + goto error; + } + + adapter_proxy = nm_iwd_manager_get_dbus_interface (nm_iwd_manager_get (), + g_variant_get_string (value, NULL), + NM_IWD_WIPHY_INTERFACE); + if (!adapter_proxy) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "Can't get DBus proxy for IWD Adapter for IWD Device"); + goto error; + } + + g_variant_unref (value); + value = g_dbus_proxy_get_cached_property (adapter_proxy, "SupportedModes"); + if (!value || !g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY)) { + nm_log_warn (LOGD_DEVICE | LOGD_WIFI, + "SupportedModes property not cached or not a string array"); + goto error; + } + + capabilities = NM_WIFI_DEVICE_CAP_CIPHER_CCMP | NM_WIFI_DEVICE_CAP_RSN; + + g_variant_get (value, "as", &iter); + while (g_variant_iter_next (iter, "&s", &mode)) { + if (nm_streq (mode, "ap")) + capabilities |= NM_WIFI_DEVICE_CAP_AP; + else if (nm_streq (mode, "ad-hoc")) + capabilities |= NM_WIFI_DEVICE_CAP_ADHOC; + } + g_variant_iter_free (iter); + + if (priv->capabilities != capabilities) { + priv->capabilities = capabilities; + _notify (self, PROP_CAPABILITIES); + } + + g_variant_unref (value); value = g_dbus_proxy_get_cached_property (priv->dbus_device_proxy, "Powered"); powered = get_variant_boolean (value, "Powered"); - g_variant_unref (value); if (powered != priv->enabled) set_powered (self, priv->enabled); else if (powered) powered_changed (self, TRUE); + + return; + +error: + g_signal_handlers_disconnect_by_func (priv->dbus_device_proxy, + device_properties_changed, self); + g_clear_object (&priv->dbus_device_proxy); } gboolean @@ -2041,15 +2554,14 @@ nm_device_iwd_init (NMDeviceIwd *self) } NMDevice * -nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities) +nm_device_iwd_new (const char *iface) { return g_object_new (NM_TYPE_DEVICE_IWD, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, - NM_DEVICE_IWD_CAPABILITIES, (guint) capabilities, NULL); } @@ -2067,6 +2579,8 @@ dispose (GObject *object) g_clear_object (&priv->dbus_device_proxy); g_clear_object (&priv->dbus_station_proxy); + g_clear_object (&priv->dbus_ap_proxy); + g_clear_object (&priv->dbus_adhoc_proxy); g_clear_object (&priv->dbus_obj); remove_all_aps (self); @@ -2084,7 +2598,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); object_class->get_property = get_property; - object_class->set_property = set_property; object_class->dispose = dispose; dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&nm_interface_info_device_wireless); @@ -2108,7 +2621,6 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) device_class->get_configured_mtu = get_configured_mtu; device_class->deactivate = deactivate; device_class->deactivate_async = deactivate_async; - device_class->deactivate_async_finish = deactivate_async_finish; device_class->can_reapply_change = can_reapply_change; device_class->state_changed = device_state_changed; @@ -2144,8 +2656,7 @@ nm_device_iwd_class_init (NMDeviceIwdClass *klass) obj_properties[PROP_CAPABILITIES] = g_param_spec_uint (NM_DEVICE_IWD_CAPABILITIES, "", "", 0, G_MAXUINT32, NM_WIFI_DEVICE_CAP_NONE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_properties[PROP_SCANNING] = diff --git a/src/devices/wifi/nm-device-iwd.h b/src/devices/wifi/nm-device-iwd.h index 825123b1..4a2bd31e 100644 --- a/src/devices/wifi/nm-device-iwd.h +++ b/src/devices/wifi/nm-device-iwd.h @@ -47,7 +47,7 @@ typedef struct _NMDeviceIwdClass NMDeviceIwdClass; GType nm_device_iwd_get_type (void); -NMDevice *nm_device_iwd_new (const char *iface, NMDeviceWifiCapabilities capabilities); +NMDevice *nm_device_iwd_new (const char *iface); void nm_device_iwd_set_dbus_object (NMDeviceIwd *device, GDBusObject *object); diff --git a/src/devices/wifi/nm-device-olpc-mesh.c b/src/devices/wifi/nm-device-olpc-mesh.c index 4c42e7d3..1172a613 100644 --- a/src/devices/wifi/nm-device-olpc-mesh.c +++ b/src/devices/wifi/nm-device-olpc-mesh.c @@ -29,13 +29,11 @@ #include "nm-device-olpc-mesh.h" #include <netinet/in.h> -#include <string.h> #include <sys/stat.h> #include <sys/wait.h> #include <signal.h> #include <unistd.h> #include <sys/ioctl.h> -#include <errno.h> #include "devices/nm-device.h" #include "nm-device-wifi.h" @@ -166,7 +164,7 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) nm_device_get_iface (priv->companion)); } - /* wait with continuing configuration untill the companion device is done scanning */ + /* wait with continuing configuration until the companion device is done scanning */ g_object_get (priv->companion, NM_DEVICE_WIFI_SCANNING, &scanning, NULL); if (scanning) { priv->stage1_waiting = TRUE; @@ -193,16 +191,13 @@ static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { NMDeviceOlpcMesh *self = NM_DEVICE_OLPC_MESH (device); - NMConnection *connection; NMSettingOlpcMesh *s_mesh; guint32 channel; GBytes *ssid; const char *anycast_addr; - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + s_mesh = nm_device_get_applied_setting (device, NM_TYPE_SETTING_OLPC_MESH); - s_mesh = nm_connection_get_setting_olpc_mesh (connection); g_return_val_if_fail (s_mesh, NM_ACT_STAGE_RETURN_FAILURE); channel = nm_setting_olpc_mesh_get_channel (s_mesh); @@ -333,7 +328,7 @@ check_companion (NMDeviceOlpcMesh *self, NMDevice *other) g_assert (priv->companion == NULL); priv->companion = g_object_ref (other); - _LOGI (LOGD_OLPC, "found companion WiFi device %s", + _LOGI (LOGD_OLPC, "found companion Wi-Fi device %s", nm_device_get_iface (other)); g_signal_connect (G_OBJECT (other), NM_DEVICE_STATE_CHANGED, diff --git a/src/devices/wifi/nm-device-wifi-p2p.c b/src/devices/wifi/nm-device-wifi-p2p.c new file mode 100644 index 00000000..8381ebc7 --- /dev/null +++ b/src/devices/wifi/nm-device-wifi-p2p.c @@ -0,0 +1,1316 @@ +/* NetworkManager -- Wi-Fi P2P Device + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-device-wifi-p2p.h" + +#include <sys/socket.h> + +#include "supplicant/nm-supplicant-manager.h" +#include "supplicant/nm-supplicant-interface.h" + +#include "nm-manager.h" +#include "nm-utils.h" +#include "nm-wifi-p2p-peer.h" +#include "NetworkManagerUtils.h" +#include "devices/nm-device-private.h" +#include "settings/nm-settings.h" +#include "nm-setting-wifi-p2p.h" +#include "nm-act-request.h" +#include "nm-ip4-config.h" +#include "platform/nm-platform.h" +#include "nm-manager.h" +#include "nm-core-internal.h" +#include "platform/nmp-object.h" + +#include "devices/nm-device-logging.h" +_LOG_DECLARE_SELF(NMDeviceWifiP2P); + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifiP2P, + PROP_PEERS, +); + +typedef struct { + NMSupplicantManager *sup_mgr; + + /* NOTE: In theory management and group ifaces could be identical. However, + * in practice, this cannot happen currently as NMDeviceWifiP2P is only + * created for existing non-P2P interfaces. + * (i.e. a single standalone P2P interface is not supported at this point) + */ + NMSupplicantInterface *mgmt_iface; + NMSupplicantInterface *group_iface; + + CList peers_lst_head; + + guint sup_timeout_id; + guint peer_dump_id; + guint peer_missing_id; + + bool is_waiting_for_supplicant:1; +} NMDeviceWifiP2PPrivate; + +struct _NMDeviceWifiP2P { + NMDevice parent; + NMDeviceWifiP2PPrivate _priv; +}; + +struct _NMDeviceWifiP2PClass { + NMDeviceClass parent; +}; + +G_DEFINE_TYPE (NMDeviceWifiP2P, nm_device_wifi_p2p, NM_TYPE_DEVICE) + +#define NM_DEVICE_WIFI_P2P_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceWifiP2P, NM_IS_DEVICE_WIFI_P2P, NMDevice) + +/*****************************************************************************/ + +static const NMDBusInterfaceInfoExtended interface_info_device_wifi_p2p; +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_added; +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_removed; + +static void supplicant_group_interface_release (NMDeviceWifiP2P *self); +static void supplicant_interfaces_release (NMDeviceWifiP2P *self, gboolean set_is_waiting); + +/*****************************************************************************/ + +static void +_peer_dump (NMDeviceWifiP2P *self, + NMLogLevel log_level, + const NMWifiP2PPeer *peer, + const char *prefix, + gint32 now_s) +{ + char buf[1024]; + + _NMLOG (log_level, LOGD_WIFI_SCAN, "wifi-peer: %-7s %s", + prefix, + nm_wifi_p2p_peer_to_string (peer, buf, sizeof (buf), now_s)); +} + +static gboolean +peer_list_dump (gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->peer_dump_id = 0; + + if (_LOGD_ENABLED (LOGD_WIFI_SCAN)) { + NMWifiP2PPeer *peer; + gint32 now_s = nm_utils_get_monotonic_timestamp_s (); + + _LOGD (LOGD_WIFI_SCAN, "P2P Peers: [now:%u]", now_s); + c_list_for_each_entry (peer, &priv->peers_lst_head, peers_lst) + _peer_dump (self, LOGL_DEBUG, peer, "dump", now_s); + } + return G_SOURCE_REMOVE; +} + +static void +schedule_peer_list_dump (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if ( !priv->peer_dump_id + && _LOGD_ENABLED (LOGD_WIFI_SCAN)) + priv->peer_dump_id = g_timeout_add_seconds (1, peer_list_dump, self); +} + +/*****************************************************************************/ + +static void +_set_is_waiting_for_supplicant (NMDeviceWifiP2P *self, gboolean is_waiting) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (priv->is_waiting_for_supplicant == (!!is_waiting)) + return; + + priv->is_waiting_for_supplicant = is_waiting; + + if (is_waiting) + nm_device_add_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); + else + nm_device_remove_pending_action (NM_DEVICE (self), NM_PENDING_ACTION_WAITING_FOR_SUPPLICANT, TRUE); +} + +/*****************************************************************************/ + +static gboolean +check_connection_peer_joined (NMDeviceWifiP2P *device) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (device); + NMConnection *conn = nm_device_get_applied_connection (NM_DEVICE (device)); + NMWifiP2PPeer *peer; + const char* group; + const char * const * groups; + + if (!conn || !priv->group_iface) + return FALSE; + + /* Comparing the object path found on the group_iface with the peers + * found on the mgmt_iface is legal. */ + group = nm_supplicant_interface_get_p2p_group_path (priv->group_iface); + if (!group) + return FALSE; + + /* NOTE: We currently only support connections to a specific peer */ + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, conn); + if (!peer) + return FALSE; + + groups = nm_wifi_p2p_peer_get_groups (peer); + if ( !groups + || !g_strv_contains (groups, group)) + return FALSE; + + return TRUE; +} + +static gboolean +disconnect_on_connection_peer_missing_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + _LOGW (LOGD_WIFI, "Peer requested in connection is missing for too long, failing connection."); + + priv->peer_missing_id = 0; + + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + return FALSE; +} + +static void +update_disconnect_on_connection_peer_missing (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMDeviceState state; + + state = nm_device_get_state (NM_DEVICE (self)); + if ( state < NM_DEVICE_STATE_IP_CONFIG + || state > NM_DEVICE_STATE_ACTIVATED) { + nm_clear_g_source (&priv->peer_missing_id); + return; + } + + if (check_connection_peer_joined (self)) { + if (nm_clear_g_source (&priv->peer_missing_id)) + _LOGD (LOGD_WIFI, "Peer requested in connection is joined, removing timeout"); + return; + } + + if (priv->peer_missing_id == 0) { + _LOGD (LOGD_WIFI, "Peer requested in connection is missing, adding timeout"); + priv->peer_missing_id = g_timeout_add_seconds (5, disconnect_on_connection_peer_missing_cb, self); + } +} + +static gboolean +is_available (NMDevice *device, NMDeviceCheckDevAvailableFlags flags) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMSupplicantInterfaceState supplicant_state; + + if (!priv->mgmt_iface) + return FALSE; + + supplicant_state = nm_supplicant_interface_get_state (priv->mgmt_iface); + if ( supplicant_state < NM_SUPPLICANT_INTERFACE_STATE_READY + || supplicant_state > NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) + return FALSE; + + return TRUE; +} + +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection, GError **error) +{ + if (!NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->check_connection_compatible (device, connection, error)) + return FALSE; + + /* TODO: Allow limitting the interface using the HW-address? */ + + /* We don't need to check anything else here. The P2P device will only + * exists if we are able to establish a P2P connection, and there should + * be no further restrictions necessary. + */ + + return TRUE; +} + +static gboolean +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + NMConnection *const*existing_connections, + GError **error) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + gs_free char *setting_name = NULL; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + const char *setting_peer; + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + + if (!specific_object) { + /* If not given a specific object, we need at minimum a peer address */ + if (!s_wifi_p2p) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A '%s' setting is required if no Peer path was given", + NM_SETTING_WIFI_P2P_SETTING_NAME); + return FALSE; + } + + setting_peer = nm_setting_wifi_p2p_get_peer (s_wifi_p2p); + if (!setting_peer) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "A '%s' setting with a valid Peer is required if no Peer path was given", + NM_SETTING_WIFI_P2P_SETTING_NAME); + return FALSE; + } + + } else { + peer = nm_wifi_p2p_peer_lookup_for_device (NM_DEVICE (self), specific_object); + if (!peer) { + g_set_error (error, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_SPECIFIC_OBJECT_NOT_FOUND, + "The P2P peer %s is unknown", + specific_object); + return FALSE; + } + + setting_peer = nm_wifi_p2p_peer_get_address (peer); + g_return_val_if_fail (setting_peer, FALSE); + } + + /* Add a Wi-Fi P2P setting if one doesn't exist yet */ + if (!s_wifi_p2p) { + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_setting_wifi_p2p_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_wifi_p2p)); + } + + g_object_set (G_OBJECT (s_wifi_p2p), NM_SETTING_WIFI_P2P_PEER, setting_peer, NULL); + + setting_name = g_strdup_printf ("Wi-Fi P2P Peer %s", setting_peer); + nm_utils_complete_generic (nm_device_get_platform (device), + connection, + NM_SETTING_WIFI_P2P_SETTING_NAME, + existing_connections, + setting_name, + setting_name, + NULL, + TRUE); + + return TRUE; +} + +/* + * supplicant_find_timeout_cb + * + * Called when the supplicant has been unable to find the peer we want to connect to. + */ +static gboolean +supplicant_find_timeout_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->sup_timeout_id = 0; + + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (nm_device_is_activating (device)) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi-p2p) could not find peer, failing activation"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + } + + return G_SOURCE_REMOVE; +} + +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMActStageReturn ret; + NMActRequest *req; + NMConnection *connection; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + + nm_clear_g_source (&priv->sup_timeout_id); + + ret = NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->act_stage1_prepare (device, out_failure_reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + if (!priv->mgmt_iface) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + req = nm_device_get_act_request (NM_DEVICE (self)); + g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_applied_connection (req); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + g_return_val_if_fail (s_wifi_p2p, NM_ACT_STAGE_RETURN_FAILURE); + + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (!peer) { + /* Set up a timeout on the find attempt and run a find for the same period of time */ + priv->sup_timeout_id = g_timeout_add_seconds (10, + supplicant_find_timeout_cb, + self); + + nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, 10); + + return NM_ACT_STAGE_RETURN_POSTPONE; + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +static void +cleanup_p2p_connect_attempt (NMDeviceWifiP2P *self, gboolean disconnect) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + nm_clear_g_source (&priv->sup_timeout_id); + nm_clear_g_source (&priv->peer_missing_id); + + if (priv->mgmt_iface) + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (disconnect && priv->group_iface) + nm_supplicant_interface_p2p_disconnect (priv->group_iface); +} + +/* + * supplicant_connection_timeout_cb + * + * Called when the supplicant has been unable to connect to a peer + * within a specified period of time. + */ +static gboolean +supplicant_connection_timeout_cb (gpointer user_data) +{ + NMDevice *device = NM_DEVICE (user_data); + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + priv->sup_timeout_id = 0; + + nm_supplicant_interface_p2p_cancel_connect (priv->mgmt_iface); + + if (nm_device_is_activating (device)) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi-p2p) connecting took too long, failing activation"); + nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT); + } + + return G_SOURCE_REMOVE; +} + +static NMActStageReturn +act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMConnection *connection; + NMSettingWifiP2P *s_wifi_p2p; + NMWifiP2PPeer *peer; + GBytes *wfd_ies; + + nm_clear_g_source (&priv->sup_timeout_id); + + if (!priv->mgmt_iface) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + connection = nm_device_get_applied_connection (device); + g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); + + nm_assert (NM_IS_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P))); + + /* The prepare stage ensures that the peer has been found */ + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (!peer) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_PEER_NOT_FOUND); + return NM_ACT_STAGE_RETURN_FAILURE; + } + + /* Set the WFD IEs before trying to establish the connection. */ + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + wfd_ies = nm_setting_wifi_p2p_get_wfd_ies (s_wifi_p2p); + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, wfd_ies); + + /* TODO: Grab secrets if we don't have them yet! */ + + /* TODO: Fix "pbc" being hardcoded here! */ + nm_supplicant_interface_p2p_connect (priv->mgmt_iface, + nm_wifi_p2p_peer_get_supplicant_path (peer), + "pbc", NULL); + + /* Set up a timeout on the connect attempt */ + priv->sup_timeout_id = g_timeout_add_seconds (45, + supplicant_connection_timeout_cb, + self); + + /* We'll get stage3 started when the P2P group has been started */ + return NM_ACT_STAGE_RETURN_POSTPONE; +} + +/*****************************************************************************/ + +static void +emit_signal_p2p_peer_add_remove (NMDeviceWifiP2P *device, + NMWifiP2PPeer *peer, + gboolean is_added /* or else is_removed */) +{ + nm_dbus_object_emit_signal (NM_DBUS_OBJECT (device), + &interface_info_device_wifi_p2p, + is_added + ? &nm_signal_info_wifi_p2p_peer_added + : &nm_signal_info_wifi_p2p_peer_removed, + "(o)", + nm_dbus_object_get_path (NM_DBUS_OBJECT (peer))); +} + +static void +peer_add_remove (NMDeviceWifiP2P *self, + gboolean is_adding, /* or else removing */ + NMWifiP2PPeer *peer, + gboolean recheck_available_connections) +{ + NMDevice *device = NM_DEVICE (self); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (is_adding) { + g_object_ref (peer); + peer->wifi_device = device; + c_list_link_tail (&priv->peers_lst_head, &peer->peers_lst); + nm_dbus_object_export (NM_DBUS_OBJECT (peer)); + _peer_dump (self, LOGL_DEBUG, peer, "added", 0); + + emit_signal_p2p_peer_add_remove (self, peer, TRUE); + } else { + peer->wifi_device = NULL; + c_list_unlink (&peer->peers_lst); + _peer_dump (self, LOGL_DEBUG, peer, "removed", 0); + } + + _notify (self, PROP_PEERS); + + if (!is_adding) { + emit_signal_p2p_peer_add_remove (self, peer, FALSE); + nm_dbus_object_clear_and_unexport (&peer); + } + + if (is_adding) { + /* If we are in prepare state, then we are currently runnign a find + * to search for the requested peer. */ + if (nm_device_get_state (device) == NM_DEVICE_STATE_PREPARE) { + NMConnection *connection; + + connection = nm_device_get_applied_connection (device); + g_assert (connection); + + peer = nm_wifi_p2p_peers_find_first_compatible (&priv->peers_lst_head, connection); + if (peer) { + /* A peer for the connection was found, cancel the timeout and go to configure state. */ + nm_clear_g_source (&priv->sup_timeout_id); + nm_device_activate_schedule_stage2_device_config (device); + } + } + + /* TODO: We may want to re-check auto-activation here, otherwise it will never work. */ + } + + update_disconnect_on_connection_peer_missing (self); +} + +static void +remove_all_peers (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMWifiP2PPeer *peer; + + if (c_list_is_empty (&priv->peers_lst_head)) + return; + + while ((peer = c_list_first_entry (&priv->peers_lst_head, NMWifiP2PPeer, peers_lst))) + peer_add_remove (self, FALSE, peer, FALSE); + + nm_device_recheck_available_connections (NM_DEVICE (self)); +} + +/*****************************************************************************/ + + +static NMActStageReturn +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) +{ + gboolean indicate_addressing_running; + NMConnection *connection; + const char *method; + + connection = nm_device_get_applied_connection (device); + + method = nm_utils_get_ip_config_method (connection, addr_family); + + if (addr_family == AF_INET) + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + else { + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); + } + + if (indicate_addressing_running) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), TRUE); + + return NM_DEVICE_CLASS (nm_device_wifi_p2p_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); +} + +static void +deactivate (NMDevice *device) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + int ifindex = nm_device_get_ip_ifindex (device); + + cleanup_p2p_connect_attempt (self, TRUE); + + /* Clear any critical protocol notification in the Wi-Fi stack */ + if (ifindex > 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), ifindex, FALSE); +} + +static guint32 +get_configured_mtu (NMDevice *device, NMDeviceMtuSource *out_source) +{ + *out_source = NM_DEVICE_MTU_SOURCE_NONE; + return 0; +} + +static const char * +get_auto_ip_config_method (NMDevice *device, int addr_family) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + /* Override the AUTO method to mean shared if we are group owner. */ + if ( priv->group_iface + && nm_supplicant_interface_get_p2p_group_owner (priv->group_iface)) { + if (addr_family == AF_INET) + return NM_SETTING_IP4_CONFIG_METHOD_SHARED; + + if (addr_family == AF_INET6) + return NM_SETTING_IP6_CONFIG_METHOD_SHARED; + } + + return NULL; +} + +static gboolean +unmanaged_on_quit (NMDevice *self) +{ + return TRUE; +} + +static void +supplicant_iface_state_cb (NMSupplicantInterface *iface, + int new_state_i, + int old_state_i, + int disconnect_reason, + gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDevice *device = NM_DEVICE (self); + NMSupplicantInterfaceState new_state = new_state_i; + NMSupplicantInterfaceState old_state = old_state_i; + + if (new_state == old_state) + return; + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "supplicant management interface state: %s -> %s", + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + switch (new_state) { + case NM_SUPPLICANT_INTERFACE_STATE_READY: + _LOGD (LOGD_WIFI, "supplicant ready"); + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + + if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, FALSE); + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + supplicant_interfaces_release (self, TRUE); + nm_device_queue_recheck_available (device, + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + break; + default: + break; + } +} + +static void +supplicant_iface_peer_updated_cb (NMSupplicantInterface *iface, + const char *object_path, + GVariant *properties, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + NMWifiP2PPeer *found_peer; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + found_peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); + if (found_peer) { + if (!nm_wifi_p2p_peer_update_from_properties (found_peer, object_path, properties)) + return; + + update_disconnect_on_connection_peer_missing (self); + _peer_dump (self, LOGL_DEBUG, found_peer, "updated", 0); + } else { + gs_unref_object NMWifiP2PPeer *peer = NULL; + + peer = nm_wifi_p2p_peer_new_from_properties (object_path, properties); + if (!peer) { + _LOGD (LOGD_WIFI, "invalid P2P peer properties received for %s", object_path); + return; + } + + peer_add_remove (self, TRUE, peer, TRUE); + } + + schedule_peer_list_dump (self); +} + +static void +supplicant_iface_peer_removed_cb (NMSupplicantInterface *iface, + const char *object_path, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + NMWifiP2PPeer *peer; + + g_return_if_fail (self != NULL); + g_return_if_fail (object_path != NULL); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + peer = nm_wifi_p2p_peers_find_by_supplicant_path (&priv->peers_lst_head, object_path); + if (!peer) + return; + + peer_add_remove (self, FALSE, peer, TRUE); + schedule_peer_list_dump (self); +} + +static void +check_group_iface_ready (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self);; + + if (!priv->group_iface) + return; + + if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + return; + + if (!nm_supplicant_interface_get_p2p_group_joined (priv->group_iface)) + return; + + nm_clear_g_source (&priv->sup_timeout_id); + update_disconnect_on_connection_peer_missing (self); + + nm_device_activate_schedule_stage3_ip_config_start (NM_DEVICE (self)); +} + +static void +supplicant_group_iface_state_cb (NMSupplicantInterface *iface, + int new_state_i, + int old_state_i, + int disconnect_reason, + gpointer user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + NMDevice *device = NM_DEVICE (self); + NMSupplicantInterfaceState new_state = new_state_i; + NMSupplicantInterfaceState old_state = old_state_i; + + if (new_state == old_state) + return; + + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "P2P Group supplicant interface state: %s -> %s", + nm_supplicant_interface_state_to_string (old_state), + nm_supplicant_interface_state_to_string (new_state)); + + switch (new_state) { + case NM_SUPPLICANT_INTERFACE_STATE_READY: + _LOGD (LOGD_WIFI, "P2P Group supplicant ready"); + + if (!nm_device_set_ip_iface (device, nm_supplicant_interface_get_ifname (priv->group_iface))) { + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + break; + } + + if (old_state < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, FALSE); + + check_group_iface_ready (self); + break; + case NM_SUPPLICANT_INTERFACE_STATE_DOWN: + supplicant_group_interface_release (self); + + nm_device_state_changed (device, + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); + break; + default: + break; + } +} + +static void +supplicant_group_iface_group_finished_cb (NMSupplicantInterface *iface, + void *user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + + supplicant_group_interface_release (self); + + nm_device_state_changed (NM_DEVICE (self), + NM_DEVICE_STATE_DISCONNECTED, + NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT); +} + +static void +supplicant_iface_group_joined_updated_cb (NMSupplicantInterface *iface, + GParamSpec *pspec, + void *user_data) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (user_data); + + check_group_iface_ready (self); +} + +static void +supplicant_iface_group_started_cb (NMSupplicantInterface *iface, + NMSupplicantInterface *group_iface, + NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv; + + g_return_if_fail (self != NULL); + + if (!nm_device_is_activating (NM_DEVICE (self))) { + _LOGW (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant notified a group start but we are not trying to connect! Ignoring the event."); + return; + } + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + supplicant_group_interface_release (self); + priv->group_iface = g_object_ref (group_iface); + + /* We need to wait for the interface to be ready and the group + * information to be resolved. */ + g_signal_connect (priv->group_iface, + "notify::" NM_SUPPLICANT_INTERFACE_P2P_GROUP_JOINED, + G_CALLBACK (supplicant_iface_group_joined_updated_cb), + self); + + g_signal_connect (priv->group_iface, + NM_SUPPLICANT_INTERFACE_STATE, + G_CALLBACK (supplicant_group_iface_state_cb), + self); + + g_signal_connect (priv->group_iface, NM_SUPPLICANT_INTERFACE_GROUP_FINISHED, + G_CALLBACK (supplicant_group_iface_group_finished_cb), + self); + + if (nm_supplicant_interface_get_state (priv->group_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, TRUE); + + check_group_iface_ready (self); +} + +static void +supplicant_group_interface_release (NMDeviceWifiP2P *self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (!priv->group_iface) + return; + + g_signal_handlers_disconnect_by_data (priv->group_iface, self); + + nm_supplicant_interface_p2p_disconnect (priv->group_iface); + + g_clear_object (&priv->group_iface); +} + +static void +supplicant_interfaces_release (NMDeviceWifiP2P *self, gboolean set_is_waiting) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + nm_clear_g_source (&priv->peer_dump_id); + + remove_all_peers (self); + + if (priv->mgmt_iface) { + _LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: Releasing WPA supplicant interface."); + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + g_signal_handlers_disconnect_by_data (priv->mgmt_iface, self); + g_clear_object (&priv->mgmt_iface); + nm_clear_g_source (&priv->sup_timeout_id); + } + + supplicant_group_interface_release (self); + + if (set_is_waiting) + _set_is_waiting_for_supplicant (self, TRUE); +} + +static void +device_state_changed (NMDevice *device, + NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (device); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + update_disconnect_on_connection_peer_missing (self); + + if (new_state <= NM_DEVICE_STATE_UNAVAILABLE) { + /* Clean up the supplicant interface because in these states the + * device cannot be used. + * Do not clean up for the UNMANAGED to UNAVAILABLE transition which + * will happen during initialization. + */ + if (priv->mgmt_iface && old_state > new_state) + supplicant_interfaces_release (self, TRUE); + + /* TODO: More cleanup needed? */ + } + + switch (new_state) { + case NM_DEVICE_STATE_UNMANAGED: + break; + case NM_DEVICE_STATE_UNAVAILABLE: + if ( !priv->mgmt_iface + || nm_supplicant_interface_get_state (priv->mgmt_iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + _set_is_waiting_for_supplicant (self, TRUE); + + break; + case NM_DEVICE_STATE_NEED_AUTH: + /* Disconnect? */ + break; + case NM_DEVICE_STATE_IP_CHECK: + /* Clear any critical protocol notification in the wifi stack */ + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), FALSE); + break; + case NM_DEVICE_STATE_ACTIVATED: + //activation_success_handler (device); + break; + case NM_DEVICE_STATE_FAILED: + /* Clear any critical protocol notification in the wifi stack. + * At this point the IP device may have been removed already. */ + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + if (nm_device_get_ip_ifindex (device) > 0) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), FALSE); + break; + case NM_DEVICE_STATE_DISCONNECTED: + nm_supplicant_manager_set_wfd_ies (priv->sup_mgr, NULL); + break; + default: + break; + } +} + +static void +impl_device_wifi_p2p_start_find (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (obj); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + gs_unref_variant GVariant *options = NULL; + const char *opts_key; + GVariant *opts_val; + GVariantIter iter; + gint32 timeout = 30; + + g_variant_get (parameters, "(@a{sv})", &options); + + g_variant_iter_init (&iter, options); + while (g_variant_iter_next (&iter, "{&sv}", &opts_key, &opts_val)) { + _nm_unused gs_unref_variant GVariant *opts_val_free = opts_val; + + if (nm_streq (opts_key, "timeout")) { + if (!g_variant_is_of_type (opts_val, G_VARIANT_TYPE_INT32)) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_ARGUMENT, + "\"timeout\" must be an integer \"i\""); + return; + } + + timeout = g_variant_get_int32 (opts_val); + if (timeout <= 0 || timeout > 600) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ALLOWED, + "The timeout for a find operation needs to be in the range of 1-600s."); + return; + } + + continue; + } + + g_dbus_method_invocation_return_error (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_ARGUMENT, + "Unsupported options key \"%s\"", + opts_key); + return; + } + + if (!priv->mgmt_iface) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "WPA Supplicant management interface is currently unavailable."); + return; + } + + nm_supplicant_interface_p2p_start_find (priv->mgmt_iface, timeout); + + g_dbus_method_invocation_return_value (invocation, NULL); +} + +static void +impl_device_wifi_p2p_stop_find (NMDBusObject *obj, + const NMDBusInterfaceInfoExtended *interface_info, + const NMDBusMethodInfoExtended *method_info, + GDBusConnection *connection, + const char *sender, + GDBusMethodInvocation *invocation, + GVariant *parameters) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (obj); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (!priv->mgmt_iface) { + g_dbus_method_invocation_return_error_literal (invocation, + NM_DEVICE_ERROR, + NM_DEVICE_ERROR_NOT_ACTIVE, + "WPA Supplicant management interface is currently unavailable."); + return; + } + + nm_supplicant_interface_p2p_stop_find (priv->mgmt_iface); + + g_dbus_method_invocation_return_value (invocation, NULL); +} + +/*****************************************************************************/ + +NMSupplicantInterface * +nm_device_wifi_p2p_get_mgmt_iface (NMDeviceWifiP2P *self) +{ + g_return_val_if_fail (NM_IS_DEVICE_WIFI_P2P (self), NULL); + + return NM_DEVICE_WIFI_P2P_GET_PRIVATE (self)->mgmt_iface; +} + +void +nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self, + NMSupplicantInterface *iface) +{ + NMDeviceWifiP2PPrivate *priv; + + g_return_if_fail (NM_IS_DEVICE_WIFI_P2P (self)); + g_return_if_fail (!iface || NM_IS_SUPPLICANT_INTERFACE (iface)); + + priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + if (priv->mgmt_iface == iface) + goto done; + + supplicant_interfaces_release (self, FALSE); + + if (!iface) + goto done; + + _LOGD (LOGD_DEVICE | LOGD_WIFI, "P2P: WPA supplicant management interface changed to %s.", + nm_supplicant_interface_get_object_path (iface)); + + priv->mgmt_iface = g_object_ref (iface); + + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_STATE, + G_CALLBACK (supplicant_iface_state_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_UPDATED, + G_CALLBACK (supplicant_iface_peer_updated_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_PEER_REMOVED, + G_CALLBACK (supplicant_iface_peer_removed_cb), + self); + g_signal_connect (priv->mgmt_iface, NM_SUPPLICANT_INTERFACE_GROUP_STARTED, + G_CALLBACK (supplicant_iface_group_started_cb), + self); +done: + nm_device_queue_recheck_available (NM_DEVICE (self), + NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE, + NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED); + _set_is_waiting_for_supplicant (self, + !priv->mgmt_iface + || ( nm_supplicant_interface_get_state (priv->mgmt_iface) + < NM_SUPPLICANT_INTERFACE_STATE_READY)); +} + +void +nm_device_wifi_p2p_remove (NMDeviceWifiP2P* self) +{ + g_signal_emit_by_name (self, NM_DEVICE_REMOVED); +} + +/*****************************************************************************/ + +static const char * +get_type_description (NMDevice *device) +{ + return "wifi-p2p"; +} + +/*****************************************************************************/ + +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_added = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "PeerAdded", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("peer", "o"), + ), +); + +static const GDBusSignalInfo nm_signal_info_wifi_p2p_peer_removed = NM_DEFINE_GDBUS_SIGNAL_INFO_INIT ( + "PeerRemoved", + .args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("peer", "o"), + ), +); + +static const NMDBusInterfaceInfoExtended interface_info_device_wifi_p2p = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_DEVICE_WIFI_P2P, + .methods = NM_DEFINE_GDBUS_METHOD_INFOS ( + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "StartFind", + .in_args = NM_DEFINE_GDBUS_ARG_INFOS ( + NM_DEFINE_GDBUS_ARG_INFO ("options", "a{sv}"), + ), + ), + .handle = impl_device_wifi_p2p_start_find, + ), + NM_DEFINE_DBUS_METHOD_INFO_EXTENDED ( + NM_DEFINE_GDBUS_METHOD_INFO_INIT ( + "StopFind", + ), + .handle = impl_device_wifi_p2p_stop_find, + ), + ), + .signals = NM_DEFINE_GDBUS_SIGNAL_INFOS ( + &nm_signal_info_wifi_p2p_peer_added, + &nm_signal_info_wifi_p2p_peer_removed, + ), + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_DEVICE_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Peers", "ao", NM_DEVICE_WIFI_P2P_PEERS), + ), + ), + .legacy_property_changed = FALSE, +}; + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + const char **list; + + switch (prop_id) { + case PROP_PEERS: + list = nm_wifi_p2p_peers_get_paths (&priv->peers_lst_head); + g_value_take_boxed (value, nm_utils_strv_make_deep_copied (list)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_device_wifi_p2p_init (NMDeviceWifiP2P * self) +{ + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (self); + + c_list_init (&priv->peers_lst_head); + + priv->sup_mgr = g_object_ref (nm_supplicant_manager_get ()); +} + +static void +constructed (GObject *object) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->constructed (object); + + _set_is_waiting_for_supplicant (self, TRUE); +} + +NMDeviceWifiP2P * +nm_device_wifi_p2p_new (const char *iface) +{ + return g_object_new (NM_TYPE_DEVICE_WIFI_P2P, + NM_DEVICE_IFACE, iface, + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi P2P", + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI_P2P, + NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, + NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, + NULL); +} + +static void +dispose (GObject *object) +{ + NMDeviceWifiP2P *self = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (object); + + g_clear_object (&priv->sup_mgr); + + supplicant_interfaces_release (self, FALSE); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->dispose (object); +} + +static void +finalize (GObject *object) +{ + NMDeviceWifiP2P *peer = NM_DEVICE_WIFI_P2P (object); + NMDeviceWifiP2PPrivate *priv = NM_DEVICE_WIFI_P2P_GET_PRIVATE (peer); + + nm_assert (c_list_is_empty (&priv->peers_lst_head)); + + G_OBJECT_CLASS (nm_device_wifi_p2p_parent_class)->finalize (object); +} + +static void +nm_device_wifi_p2p_class_init (NMDeviceWifiP2PClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + NMDeviceClass *device_class = NM_DEVICE_CLASS (klass); + + object_class->constructed = constructed; + object_class->get_property = get_property; + object_class->dispose = dispose; + object_class->finalize = finalize; + + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_device_wifi_p2p); + + device_class->connection_type_supported = NM_SETTING_WIFI_P2P_SETTING_NAME; + device_class->connection_type_check_compatible = NM_SETTING_WIFI_P2P_SETTING_NAME; + device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIFI_P2P); + device_class->get_type_description = get_type_description; + + /* Do we need compatibility checking or is the default good enough? */ + device_class->is_available = is_available; + device_class->check_connection_compatible = check_connection_compatible; + device_class->complete_connection = complete_connection; + + 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->get_auto_ip_config_method = get_auto_ip_config_method; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + + device_class->deactivate = deactivate; + device_class->unmanaged_on_quit = unmanaged_on_quit; + + device_class->state_changed = device_state_changed; + + obj_properties[PROP_PEERS] = + g_param_spec_boxed (NM_DEVICE_WIFI_P2P_PEERS, "", "", + G_TYPE_STRV, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/devices/wifi/nm-device-wifi-p2p.h b/src/devices/wifi/nm-device-wifi-p2p.h new file mode 100644 index 00000000..a13eef15 --- /dev/null +++ b/src/devices/wifi/nm-device-wifi-p2p.h @@ -0,0 +1,50 @@ +/* NetworkManager -- Wi-Fi P2P Device + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2018 Red Hat, Inc. + */ + +#ifndef __NM_DEVICE_WIFI_P2P_H__ +#define __NM_DEVICE_WIFI_P2P_H__ + +#include "devices/nm-device.h" +#include "supplicant/nm-supplicant-interface.h" + +#define NM_TYPE_DEVICE_WIFI_P2P (nm_device_wifi_p2p_get_type ()) +#define NM_DEVICE_WIFI_P2P(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2P)) +#define NM_DEVICE_WIFI_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2PClass)) +#define NM_IS_DEVICE_WIFI_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DEVICE_WIFI_P2P)) +#define NM_IS_DEVICE_WIFI_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_DEVICE_WIFI_P2P)) +#define NM_DEVICE_WIFI_P2P_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_WIFI_P2P, NMDeviceWifiP2PClass)) + +#define NM_DEVICE_WIFI_P2P_PEERS "peers" +#define NM_DEVICE_WIFI_P2P_GROUPS "groups" + +typedef struct _NMDeviceWifiP2P NMDeviceWifiP2P; +typedef struct _NMDeviceWifiP2PClass NMDeviceWifiP2PClass; + +GType nm_device_wifi_p2p_get_type (void); + +NMDeviceWifiP2P *nm_device_wifi_p2p_new (const char *iface); + +NMSupplicantInterface * nm_device_wifi_p2p_get_mgmt_iface (NMDeviceWifiP2P *self); +void nm_device_wifi_p2p_set_mgmt_iface (NMDeviceWifiP2P *self, + NMSupplicantInterface *iface); + +void nm_device_wifi_p2p_remove (NMDeviceWifiP2P *self); + +#endif /* __NM_DEVICE_WIFI_P2P_H__ */ diff --git a/src/devices/wifi/nm-device-wifi.c b/src/devices/wifi/nm-device-wifi.c index 2ce84618..64869672 100644 --- a/src/devices/wifi/nm-device-wifi.c +++ b/src/devices/wifi/nm-device-wifi.c @@ -24,10 +24,9 @@ #include "nm-device-wifi.h" #include <netinet/in.h> -#include <string.h> #include <unistd.h> -#include <errno.h> +#include "nm-device-wifi-p2p.h" #include "nm-wifi-ap.h" #include "nm-common-macros.h" #include "devices/nm-device.h" @@ -79,6 +78,7 @@ NM_GOBJECT_PROPERTIES_DEFINE (NMDeviceWifi, enum { SCANNING_PROHIBITED, + P2P_DEVICE_CREATED, LAST_SIGNAL }; @@ -124,6 +124,8 @@ typedef struct { guint wps_timeout_id; NMSettingWirelessWakeOnWLan wowlan_restore; + + NMDeviceWifiP2P *p2p_device; } NMDeviceWifiPrivate; struct _NMDeviceWifi @@ -186,6 +188,10 @@ static void supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, GParamSpec *pspec, NMDeviceWifi *self); +static void supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self); + static void request_wireless_scan (NMDeviceWifi *self, gboolean periodic, gboolean force_if_scanning, @@ -198,6 +204,8 @@ static void ap_add_remove (NMDeviceWifi *self, static void _hw_addr_set_scanning (NMDeviceWifi *self, gboolean do_reset); +static void recheck_p2p_availability (NMDeviceWifi *self); + /*****************************************************************************/ static void @@ -291,6 +299,10 @@ supplicant_interface_acquire (NMDeviceWifi *self) "notify::" NM_SUPPLICANT_INTERFACE_CURRENT_BSS, G_CALLBACK (supplicant_iface_notify_current_bss), self); + g_signal_connect (priv->sup_iface, + "notify::" NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, + G_CALLBACK (supplicant_iface_notify_p2p_available), + self); _notify_scanning (self); @@ -347,6 +359,11 @@ supplicant_interface_release (NMDeviceWifi *self) g_clear_object (&priv->sup_iface); } + if (priv->p2p_device) { + /* Signal to P2P device to also release its reference */ + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, NULL); + } + _notify_scanning (self); } @@ -732,7 +749,7 @@ check_connection_available (NMDevice *device, /* Hidden SSIDs obviously don't always appear in the scan list either. * - * For an explict user-activation-request, a connection is considered + * For an explicit user-activation-request, a connection is considered * available because for hidden Wi-Fi, clients didn't consistently * set the 'hidden' property to indicate hidden SSID networks. If * activating but the network isn't available let the device recheck @@ -838,7 +855,7 @@ complete_connection (NMDevice *device, ssid = nm_wifi_ap_get_ssid (ap); if (ssid == NULL) { - /* The AP must be hidden. Connecting to a WiFi AP requires the SSID + /* The AP must be hidden. Connecting to a Wi-Fi AP requires the SSID * as part of the initial handshake, so check the connection details * for the SSID. The AP object will still be used for encryption * settings and such. @@ -909,18 +926,6 @@ complete_connection (NMDevice *device, g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS); return FALSE; } - } else { - guint8 tmp[ETH_ALEN]; - - /* Lock the connection to this device by default if it uses a - * permanent MAC address (ie not a 'locally administered' one) - */ - nm_utils_hwaddr_aton (perm_hw_addr, tmp, ETH_ALEN); - if (!(tmp[0] & 0x02)) { - g_object_set (G_OBJECT (s_wifi), - NM_SETTING_WIRELESS_MAC_ADDRESS, perm_hw_addr, - NULL); - } } } @@ -981,7 +986,7 @@ can_auto_connect (NMDevice *device, g_return_val_if_fail (s_wifi, FALSE); /* Always allow autoconnect for AP and non-autoconf Ad-Hoc */ - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); mode = nm_setting_wireless_get_mode (s_wifi); if (nm_streq0 (mode, NM_SETTING_WIRELESS_MODE_AP)) return TRUE; @@ -1197,7 +1202,7 @@ _nm_device_wifi_request_scan (NMDeviceWifi *self, NM_DEVICE_AUTH_REQUEST, invocation, NULL, - NM_AUTH_PERMISSION_NETWORK_CONTROL, + NM_AUTH_PERMISSION_WIFI_SCAN, TRUE, dbus_request_scan_cb, options ? g_variant_ref (options) : NULL); @@ -1236,7 +1241,7 @@ scanning_prohibited (NMDeviceWifi *self, gboolean periodic) return FALSE; case NM_DEVICE_STATE_ACTIVATED: /* Prohibit periodic scans when connected; we ask the supplicant to - * background scan for us, unless the connection is locked to a specifc + * background scan for us, unless the connection is locked to a specific * BSSID. */ if (periodic) @@ -1685,20 +1690,37 @@ wifi_secrets_cb (NMActRequest *req, g_return_if_fail (nm_act_request_get_settings_connection (req) == connection); if (error) { - _LOGW (LOGD_WIFI, "%s", error->message); - - if (g_error_matches (error, NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_USER_CANCELED)) { - /* Don't wait for WPS timeout on an explicit cancel. */ - nm_clear_g_source (&priv->wps_timeout_id); - } + _LOGW (LOGD_WIFI, "no secrets: %s", error->message); - if (!priv->wps_timeout_id) { - /* Fail the device only if the WPS period is over too. */ - nm_device_state_changed (device, - NM_DEVICE_STATE_FAILED, - NM_DEVICE_STATE_REASON_NO_SECRETS); - } + /* Even if WPS is still pending, let's abort the activation when the secret + * request returns. + * + * This means, a user can only effectively use WPS when also running a secret + * agent, and pressing the push button while being prompted for the password. + * Note, that in the secret prompt the user can see that WPS is in progress + * (via the NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE flag). + * + * Previously, WPS was not cancelled when the secret request returns. + * Note that in common use-cases WPS is enabled in the connection profile + * but it won't succeed (because it's disabled in the AP or because the + * user is not prepared to press the push button). + * That means for example, during boot we would try to autoconnect with WPS. + * At that point, there is no secret-agent running, and WPS is pending for + * full 30 seconds. If in the meantime a secret agent registers (because + * of logging into the DE), the profile is still busy waiting for WPS to time + * out. Only after that delay, autoconnect starts again (note that autoconnect gets + * not blocked in this case, because a secret agent registered in the meantime). + * + * It seems wrong to continue doing WPS if the user is not aware + * that WPS is ongoing. The user is required to perform an action (push button), + * and must be told via the secret prompt. + * If no secret-agent is running, if the user cancels the secret-request, or any + * other error to obtain secrets, the user apparently does not want WPS either. + */ + nm_clear_g_source (&priv->wps_timeout_id); + nm_device_state_changed (device, + NM_DEVICE_STATE_FAILED, + NM_DEVICE_STATE_REASON_NO_SECRETS); } else nm_device_activate_schedule_stage1_device_prepare (device); } @@ -1851,9 +1873,10 @@ need_new_8021x_secrets (NMDeviceWifi *self, NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; NMConnection *connection; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); + g_return_val_if_fail (connection != NULL, FALSE); /* 802.1x stuff only happens in the supplicant's ASSOCIATED state when it's @@ -1905,10 +1928,11 @@ need_new_wpa_psk (NMDeviceWifi *self, NMConnection *connection; const char *key_mgmt = NULL; - g_assert (setting_name != NULL); + g_return_val_if_fail (setting_name, FALSE); connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_val_if_fail (connection != NULL, FALSE); + + g_return_val_if_fail (connection, FALSE); /* A bad PSK will cause the supplicant to disconnect during the 4-way handshake */ if (old_state != NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE) @@ -2021,6 +2045,10 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, && new_state <= NM_SUPPLICANT_INTERFACE_STATE_COMPLETED) priv->ssid_found = TRUE; + if ( old_state < NM_SUPPLICANT_INTERFACE_STATE_READY + && new_state >= NM_SUPPLICANT_INTERFACE_STATE_READY) + recheck_p2p_availability (self); + switch (new_state) { case NM_SUPPLICANT_INTERFACE_STATE_READY: _LOGD (LOGD_WIFI, "supplicant ready"); @@ -2040,15 +2068,12 @@ supplicant_iface_state_cb (NMSupplicantInterface *iface, * schedule the next activation stage. */ if (devstate == NM_DEVICE_STATE_CONFIG) { - NMConnection *connection; NMSettingWireless *s_wifi; GBytes *ssid; gs_free char *ssid_str = NULL; - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - g_return_if_fail (connection); + s_wifi = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); - s_wifi = nm_connection_get_setting_wireless (connection); g_return_if_fail (s_wifi); ssid = nm_setting_wireless_get_ssid (s_wifi); @@ -2200,6 +2225,67 @@ supplicant_iface_notify_current_bss (NMSupplicantInterface *iface, } } +/* We bind the existence of the P2P device to a wifi device that is being + * managed by NetworkManager and is capable of P2P operation. + * Note that some care must be taken here, because we don't want to re-create + * the device every time the supplicant interface is destroyed (e.g. due to + * a suspend/resume cycle). + * Therefore, this function will be called when a change in the P2P capability + * is detected and the supplicant interface has been initialised. + */ +static void +recheck_p2p_availability (NMDeviceWifi *self) +{ + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + gboolean p2p_available; + + g_object_get (priv->sup_iface, + NM_SUPPLICANT_INTERFACE_P2P_AVAILABLE, &p2p_available, + NULL); + + if (p2p_available && !priv->p2p_device) { + gs_free char *iface_name = NULL; + + /* Create a P2P device. "p2p-dev-" is the same prefix as chosen by + * wpa_supplicant internally. + */ + iface_name = g_strconcat ("p2p-dev-", nm_device_get_iface (NM_DEVICE (self)), NULL); + + priv->p2p_device = nm_device_wifi_p2p_new (iface_name); + + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, priv->sup_iface); + + g_signal_emit (self, signals[P2P_DEVICE_CREATED], 0, priv->p2p_device); + g_object_add_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + g_object_unref (priv->p2p_device); + return; + } + + if (p2p_available && priv->p2p_device) { + nm_device_wifi_p2p_set_mgmt_iface (priv->p2p_device, priv->sup_iface); + return; + } + + if (!p2p_available && priv->p2p_device) { + /* Destroy the P2P device. */ + g_object_remove_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + nm_device_wifi_p2p_remove (g_steal_pointer (&priv->p2p_device)); + return; + } +} + +static void +supplicant_iface_notify_p2p_available (NMSupplicantInterface *iface, + GParamSpec *pspec, + NMDeviceWifi *self) +{ + /* Do not update when the interface is still initializing. */ + if (nm_supplicant_interface_get_state (iface) < NM_SUPPLICANT_INTERFACE_STATE_READY) + return; + + recheck_p2p_availability (self); +} + static gboolean handle_auth_or_fail (NMDeviceWifi *self, NMActRequest *req, @@ -2469,7 +2555,7 @@ wake_on_wlan_enable (NMDeviceWifi *self) NMSettingWirelessWakeOnWLan wowl; NMSettingWireless *s_wireless; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (NM_DEVICE (self), NM_TYPE_SETTING_WIRELESS); if (s_wireless) { wowl = nm_setting_wireless_get_wake_on_wlan (s_wireless); if (wowl != NM_SETTING_WIRELESS_WAKE_ON_WLAN_DEFAULT) @@ -2646,7 +2732,8 @@ set_powersave (NMDevice *device) NMSettingWireless *s_wireless; NMSettingWirelessPowersave val; - s_wireless = (NMSettingWireless *) nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + s_wireless = nm_device_get_applied_setting (device, NM_TYPE_SETTING_WIRELESS); + g_return_if_fail (s_wireless); val = nm_setting_wireless_get_powersave (s_wireless); @@ -2787,50 +2874,29 @@ out: } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip4; - const char *method = NM_SETTING_IP4_CONFIG_METHOD_AUTO; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - if (s_ip4) - method = nm_setting_ip_config_get_method (s_ip4); - - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE); - - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip4_config_start (device, out_config, out_failure_reason); -} - -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { + gboolean indicate_addressing_running; NMConnection *connection; - NMSettingIPConfig *s_ip6; - const char *method = NM_SETTING_IP6_CONFIG_METHOD_AUTO; + const char *method; connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - s_ip6 = nm_connection_get_setting_ip6_config (connection); - if (s_ip6) - method = nm_setting_ip_config_get_method (s_ip6); + method = nm_utils_get_ip_config_method (connection, addr_family); + if (addr_family == AF_INET) + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO); + else { + indicate_addressing_running = NM_IN_STRSET (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO, + NM_SETTING_IP6_CONFIG_METHOD_DHCP); + } - /* Indicate that a critical protocol is about to start */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0 || - strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_DHCP) == 0) - nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ifindex (device), TRUE); + if (indicate_addressing_running) + nm_platform_wifi_indicate_addressing_running (nm_device_get_platform (device), nm_device_get_ip_ifindex (device), TRUE); - return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip6_config_start (device, out_config, out_failure_reason); + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage3_ip_config_start (device, addr_family, out_config, out_failure_reason); } static guint32 @@ -2865,90 +2931,52 @@ is_static_wep (NMConnection *connection) } static NMActStageReturn -handle_ip_config_timeout (NMDeviceWifi *self, - NMConnection *connection, - gboolean may_fail, - gboolean *chain_up, - NMDeviceStateReason *out_failure_reason) +act_stage4_ip_config_timeout (NMDevice *device, + int addr_family, + NMDeviceStateReason *out_failure_reason) { - NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE; + NMDeviceWifi *self = NM_DEVICE_WIFI (device); + NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self); + NMConnection *connection; + NMSettingIPConfig *s_ip; + gboolean may_fail; + + connection = nm_device_get_applied_connection (device); + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + may_fail = nm_setting_ip_config_get_may_fail (s_ip); - g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + if (priv->mode == NM_802_11_MODE_AP) + goto call_parent; - if (NM_DEVICE_WIFI_GET_PRIVATE (self)->mode == NM_802_11_MODE_AP) { - *chain_up = TRUE; - return NM_ACT_STAGE_RETURN_FAILURE; + if ( may_fail + || !is_static_wep (connection)) { + /* Not static WEP or failure allowed; let superclass handle it */ + goto call_parent; } /* If IP configuration times out and it's a static WEP connection, that * usually means the WEP key is wrong. WEP's Open System auth mode has * no provision for figuring out if the WEP key is wrong, so you just have - * to wait for DHCP to fail to figure it out. For all other WiFi security + * to wait for DHCP to fail to figure it out. For all other Wi-Fi security * types (open, WPA, 802.1x, etc) if the secrets/certs were wrong the * connection would have failed before IP configuration. - */ - if (!may_fail && is_static_wep (connection)) { - /* Activation failed, we must have bad encryption key */ - _LOGW (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) could not get IP configuration for connection '%s'.", - nm_connection_get_id (connection)); + * + * Activation failed, we must have bad encryption key */ + _LOGW (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) could not get IP configuration for connection '%s'.", + nm_connection_get_id (connection)); - if (handle_auth_or_fail (self, NULL, TRUE)) { - _LOGI (LOGD_DEVICE | LOGD_WIFI, - "Activation: (wifi) asking for new secrets"); - ret = NM_ACT_STAGE_RETURN_POSTPONE; - } else { - NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); - ret = NM_ACT_STAGE_RETURN_FAILURE; - } - } else { - /* Not static WEP or failure allowed; let superclass handle it */ - *chain_up = TRUE; + if (!handle_auth_or_fail (self, NULL, TRUE)) { + NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_NO_SECRETS); + return NM_ACT_STAGE_RETURN_FAILURE; } - return ret; -} - -static NMActStageReturn -act_stage4_ip4_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip4; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip4 = nm_connection_get_setting_ip4_config (connection); - may_fail = nm_setting_ip_config_get_may_fail (s_ip4); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip4_config_timeout (device, out_failure_reason); - - return ret; -} - -static NMActStageReturn -act_stage4_ip6_config_timeout (NMDevice *device, NMDeviceStateReason *out_failure_reason) -{ - NMConnection *connection; - NMSettingIPConfig *s_ip6; - gboolean may_fail = FALSE, chain_up = FALSE; - NMActStageReturn ret; - - connection = nm_device_get_applied_connection (device); - g_return_val_if_fail (connection, NM_ACT_STAGE_RETURN_FAILURE); - - s_ip6 = nm_connection_get_setting_ip6_config (connection); - may_fail = nm_setting_ip_config_get_may_fail (s_ip6); - - ret = handle_ip_config_timeout (NM_DEVICE_WIFI (device), connection, may_fail, &chain_up, out_failure_reason); - if (chain_up) - ret = NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip6_config_timeout (device, out_failure_reason); + _LOGI (LOGD_DEVICE | LOGD_WIFI, + "Activation: (wifi) asking for new secrets"); + return NM_ACT_STAGE_RETURN_POSTPONE; - return ret; +call_parent: + return NM_DEVICE_CLASS (nm_device_wifi_parent_class)->act_stage4_ip_config_timeout (device, addr_family, out_failure_reason); } static void @@ -3279,7 +3307,7 @@ nm_device_wifi_new (const char *iface, NMDeviceWifiCapabilities capabilities) { return g_object_new (NM_TYPE_DEVICE_WIFI, NM_DEVICE_IFACE, iface, - NM_DEVICE_TYPE_DESC, "802.11 WiFi", + NM_DEVICE_TYPE_DESC, "802.11 Wi-Fi", NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_WIFI, NM_DEVICE_LINK_TYPE, NM_LINK_TYPE_WIFI, NM_DEVICE_RFKILL_TYPE, RFKILL_TYPE_WLAN, @@ -3305,6 +3333,12 @@ dispose (GObject *object) remove_all_aps (self); + if (priv->p2p_device) { + /* Destroy the P2P device. */ + g_object_remove_weak_pointer (G_OBJECT (priv->p2p_device), (gpointer*) &priv->p2p_device); + nm_device_wifi_p2p_remove (g_steal_pointer (&priv->p2p_device)); + } + G_OBJECT_CLASS (nm_device_wifi_parent_class)->dispose (object); } @@ -3350,10 +3384,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) 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->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; - device_class->act_stage4_ip4_config_timeout = act_stage4_ip4_config_timeout; - device_class->act_stage4_ip6_config_timeout = act_stage4_ip6_config_timeout; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; + device_class->act_stage4_ip_config_timeout = act_stage4_ip_config_timeout; device_class->deactivate = deactivate; device_class->deactivate_reset_hw_addr = deactivate_reset_hw_addr; device_class->unmanaged_on_quit = unmanaged_on_quit; @@ -3417,4 +3449,12 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass) G_STRUCT_OFFSET (NMDeviceWifiClass, scanning_prohibited), NULL, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); + + signals[P2P_DEVICE_CREATED] = + g_signal_new (NM_DEVICE_WIFI_P2P_DEVICE_CREATED, + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, NM_TYPE_DEVICE); } diff --git a/src/devices/wifi/nm-device-wifi.h b/src/devices/wifi/nm-device-wifi.h index 1c555f5f..116ad11e 100644 --- a/src/devices/wifi/nm-device-wifi.h +++ b/src/devices/wifi/nm-device-wifi.h @@ -40,6 +40,7 @@ #define NM_DEVICE_WIFI_LAST_SCAN "last-scan" #define NM_DEVICE_WIFI_SCANNING_PROHIBITED "scanning-prohibited" +#define NM_DEVICE_WIFI_P2P_DEVICE_CREATED "p2p-device-created" typedef struct _NMDeviceWifi NMDeviceWifi; typedef struct _NMDeviceWifiClass NMDeviceWifiClass; diff --git a/src/devices/wifi/nm-iwd-manager.c b/src/devices/wifi/nm-iwd-manager.c index a3da9791..d668f0d8 100644 --- a/src/devices/wifi/nm-iwd-manager.c +++ b/src/devices/wifi/nm-iwd-manager.c @@ -22,7 +22,6 @@ #include "nm-iwd-manager.h" -#include <string.h> #include <net/if.h> #include "nm-logging.h" @@ -48,6 +47,7 @@ typedef struct { typedef struct { NMManager *manager; + NMSettings *settings; GCancellable *cancellable; gboolean running; GDBusObjectManager *object_manager; @@ -136,6 +136,7 @@ agent_dbus_method_cb (GDBusConnection *connection, int ifindex; NMDevice *device; gs_free char *name_owner = NULL; + int errsv; /* Be paranoid and check the sender address */ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (priv->object_manager)); @@ -171,8 +172,9 @@ agent_dbus_method_cb (GDBusConnection *connection, ifindex = if_nametoindex (ifname); if (!ifindex) { + errsv = errno; _LOGD ("agent-request: if_nametoindex failed for Name %s for Device at %s: %i", - ifname, device_path, errno); + ifname, device_path, errsv); goto return_error; } @@ -338,6 +340,7 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, const char *ifname; int ifindex; NMDevice *device; + int errsv; ifname = get_property_string_or_null (proxy, "Name"); if (!ifname) { @@ -349,8 +352,9 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, ifindex = if_nametoindex (ifname); if (!ifindex) { + errsv = errno; _LOGE ("if_nametoindex failed for Name %s for Device at %s: %i", - ifname, g_dbus_proxy_get_object_path (proxy), errno); + ifname, g_dbus_proxy_get_object_path (proxy), errsv); return; } @@ -363,31 +367,35 @@ set_device_dbus_object (NMIwdManager *self, GDBusProxy *proxy, nm_device_iwd_set_dbus_object (NM_DEVICE_IWD (device), object); } -/* Create an in-memory NMConnection for a WPA2-Enterprise network that - * has been preprovisioned with an IWD config file so that NM autoconnect - * mechanism and the clients know this networks needs no additional EAP - * configuration from the user. Only do this if no existing connection - * SSID and security type match that network yet. +/* Look up an existing NMSettingsConnection for a WPA2-Enterprise network + * that has been preprovisioned with an IWD config file, or create a new + * in-memory connection object so that NM autoconnect mechanism and the + * clients know this networks needs no additional EAP configuration from + * the user. */ static NMSettingsConnection * mirror_8021x_connection (NMIwdManager *self, - const char *name) + const char *name, + gboolean create_new) { - NMSettings *settings = NM_SETTINGS_GET; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); NMSettingsConnection *const*iter; gs_unref_object NMConnection *connection = NULL; - NMSettingsConnection *settings_connection; + NMSettingsConnection *settings_connection = NULL; char uuid[37]; NMSetting *setting; GError *error = NULL; gs_unref_bytes GBytes *new_ssid = NULL; - for (iter = nm_settings_get_connections (settings, NULL); *iter; iter++) { + for (iter = nm_settings_get_connections (priv->settings, NULL); *iter; iter++) { NMSettingsConnection *sett_conn = *iter; NMConnection *conn = nm_settings_connection_get_connection (sett_conn); NMIwdNetworkSecurity security; gs_free char *ssid_name = NULL; NMSettingWireless *s_wifi; + NMSetting8021x *s_8021x; + gboolean external = FALSE; + guint i; security = nm_wifi_connection_get_iwd_security (conn, NULL); if (security != NM_IWD_NETWORK_SECURITY_8021X) @@ -399,14 +407,30 @@ mirror_8021x_connection (NMIwdManager *self, ssid_name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wifi)); - /* We already have an NMSettingsConnection matching this - * KnownNetwork, whether it's saved or an in-memory connection - * potentially created by ourselves. Nothing to do here. - */ - if (nm_streq (ssid_name, name)) - return NULL; + if (!nm_streq (ssid_name, name)) + continue; + + s_8021x = nm_connection_get_setting_802_1x (conn); + for (i = 0; i < nm_setting_802_1x_get_num_eap_methods (s_8021x); i++) { + if (nm_streq (nm_setting_802_1x_get_eap_method (s_8021x, i), "external")) { + external = TRUE; + break; + } + } + + /* Prefer returning connections for EAP method "external" */ + if (!settings_connection || external) + settings_connection = sett_conn; } + /* If we already have an NMSettingsConnection matching this + * KnownNetwork, whether it's saved or an in-memory connection + * potentially created by ourselves then we have nothing left to + * do here. + */ + if (settings_connection || !create_new) + return settings_connection; + connection = nm_simple_connection_new (); setting = NM_SETTING (g_object_new (NM_TYPE_SETTING_CONNECTION, @@ -446,7 +470,7 @@ mirror_8021x_connection (NMIwdManager *self, if (!nm_connection_normalize (connection, NULL, NULL, NULL)) return NULL; - settings_connection = nm_settings_add_connection (settings, connection, + settings_connection = nm_settings_add_connection (priv->settings, connection, FALSE, &error); if (!settings_connection) { _LOGW ("failed to add a mirror NMConnection for IWD's Known Network '%s': %s", @@ -526,16 +550,19 @@ interface_added (GDBusObjectManager *object_manager, GDBusObject *object, id = known_network_id_new (name, security); data = g_hash_table_lookup (priv->known_networks, id); - if (data) + if (data) { + _LOGW ("DBus error: KnownNetwork already exists ('%s', %s)", + name, type_str); g_free (id); - else { + nm_g_object_ref_set (&data->known_network, proxy); + } else { data = g_slice_new0 (KnownNetworkData); data->known_network = g_object_ref (proxy); g_hash_table_insert (priv->known_networks, id, data); } if (security == NM_IWD_NETWORK_SECURITY_8021X) { - sett_conn = mirror_8021x_connection (self, name); + sett_conn = mirror_8021x_connection (self, name, TRUE); if ( sett_conn && sett_conn != data->mirror_connection) { @@ -593,6 +620,55 @@ interface_removed (GDBusObjectManager *object_manager, GDBusObject *object, } } +static void +connection_removed (NMSettings *settings, + NMSettingsConnection *sett_conn, + gpointer user_data) +{ + NMIwdManager *self = user_data; + NMIwdManagerPrivate *priv = NM_IWD_MANAGER_GET_PRIVATE (self); + NMConnection *conn = nm_settings_connection_get_connection (sett_conn); + NMSettingWireless *s_wireless; + gboolean mapped; + KnownNetworkData *data; + KnownNetworkId id; + + id.security = nm_wifi_connection_get_iwd_security (conn, &mapped); + if (!mapped) + return; + + s_wireless = nm_connection_get_setting_wireless (conn); + id.name = _nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); + data = g_hash_table_lookup (priv->known_networks, &id); + g_free ((char *) id.name); + if (!data) + return; + + if (id.security == NM_IWD_NETWORK_SECURITY_8021X) { + NMSettingsConnection *new_mirror_conn; + + if (data->mirror_connection != sett_conn) + return; + + g_clear_object (&data->mirror_connection); + + /* Don't call Forget for an 8021x network until there's no + * longer *any* matching NMSettingsConnection (debatable) + */ + new_mirror_conn = mirror_8021x_connection (self, id.name, FALSE); + if (new_mirror_conn) { + data->mirror_connection = g_object_ref (new_mirror_conn); + return; + } + } + + if (!priv->running) + return; + + g_dbus_proxy_call (data->known_network, "Forget", + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); +} + static gboolean _om_has_name_owner (GDBusObjectManager *object_manager) { @@ -744,7 +820,7 @@ got_object_manager (GObject *object, GAsyncResult *result, gpointer user_data) &priv->agent_path, &error); if (!priv->agent_id) { - _LOGE ("failed to export the IWD Agent: PSK/8021x WiFi networks may not work: %s", + _LOGE ("failed to export the IWD Agent: PSK/8021x Wi-Fi networks may not work: %s", error->message); g_clear_error (&error); } @@ -824,6 +900,10 @@ nm_iwd_manager_init (NMIwdManager *self) g_signal_connect (priv->manager, NM_MANAGER_DEVICE_ADDED, G_CALLBACK (device_added), self); + priv->settings = g_object_ref (nm_settings_get ()); + g_signal_connect (priv->settings, NM_SETTINGS_SIGNAL_CONNECTION_REMOVED, + G_CALLBACK (connection_removed), self); + priv->cancellable = g_cancellable_new (); priv->known_networks = g_hash_table_new_full ((GHashFunc) known_network_id_hash, @@ -844,6 +924,14 @@ dispose (GObject *object) nm_clear_g_cancellable (&priv->cancellable); + if (priv->settings) { + g_signal_handlers_disconnect_by_data (priv->settings, self); + g_clear_object (&priv->settings); + } + + /* This may trigger mirror connection removals so it happens + * after the g_signal_handlers_disconnect_by_data above. + */ nm_clear_pointer (&priv->known_networks, g_hash_table_destroy); if (priv->manager) { diff --git a/src/devices/wifi/nm-wifi-ap.c b/src/devices/wifi/nm-wifi-ap.c index e5573383..87daff59 100644 --- a/src/devices/wifi/nm-wifi-ap.c +++ b/src/devices/wifi/nm-wifi-ap.c @@ -23,7 +23,6 @@ #include "nm-wifi-ap.h" -#include <string.h> #include <stdlib.h> #include "nm-setting-wireless.h" @@ -425,6 +424,8 @@ security_from_vardict (GVariant *security) g_strv_contains (array, "wpa-fils-sha256") || g_strv_contains (array, "wpa-fils-sha384")) flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X; + if (g_strv_contains (array, "sae")) + flags |= NM_802_11_AP_SEC_KEY_MGMT_SAE; g_free (array); } @@ -1390,7 +1391,8 @@ nm_wifi_ap_class_init (NMWifiAPClass *ap_class) | NM_802_11_AP_SEC_GROUP_TKIP \ | NM_802_11_AP_SEC_GROUP_CCMP \ | NM_802_11_AP_SEC_KEY_MGMT_PSK \ - | NM_802_11_AP_SEC_KEY_MGMT_802_1X ) + | NM_802_11_AP_SEC_KEY_MGMT_802_1X \ + | NM_802_11_AP_SEC_KEY_MGMT_SAE ) GObjectClass *object_class = G_OBJECT_CLASS (ap_class); NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (ap_class); diff --git a/src/devices/wifi/nm-wifi-factory.c b/src/devices/wifi/nm-wifi-factory.c index 6b8e5fb8..9a89bbe9 100644 --- a/src/devices/wifi/nm-wifi-factory.c +++ b/src/devices/wifi/nm-wifi-factory.c @@ -26,6 +26,7 @@ #include "nm-setting-wireless.h" #include "nm-setting-olpc-mesh.h" #include "nm-device-wifi.h" +#include "nm-device-wifi-p2p.h" #include "nm-device-olpc-mesh.h" #include "nm-device-iwd.h" #include "settings/nm-settings-connection.h" @@ -68,6 +69,18 @@ nm_device_factory_create (GError **error) /*****************************************************************************/ +static void +p2p_device_created (NMDeviceWifi *device, + NMDeviceWifiP2P *p2p_device, + NMDeviceFactory *self) +{ + nm_log_info (LOGD_PLATFORM | LOGD_WIFI, + "Wi-Fi P2P device controlled by interface %s created", + nm_device_get_iface (NM_DEVICE (device))); + + g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, p2p_device); +} + static NMDevice * create_device (NMDeviceFactory *factory, const char *iface, @@ -75,8 +88,6 @@ create_device (NMDeviceFactory *factory, NMConnection *connection, gboolean *out_ignore) { - NMDeviceWifiCapabilities capabilities; - NM80211Mode mode; gs_free char *backend = NULL; g_return_val_if_fail (iface != NULL, NULL); @@ -84,23 +95,6 @@ create_device (NMDeviceFactory *factory, g_return_val_if_fail (g_strcmp0 (iface, plink->name) == 0, NULL); g_return_val_if_fail (NM_IN_SET (plink->type, NM_LINK_TYPE_WIFI, NM_LINK_TYPE_OLPC_MESH), NULL); - if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, - plink->ifindex, - &capabilities)) { - nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) failed to initialize Wi-Fi driver for ifindex %d", iface, plink->ifindex); - return NULL; - } - - /* Ignore monitor-mode and other unhandled interface types. - * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage - * them if/when they change to a handled type. - */ - mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); - if (mode == NM_802_11_MODE_UNKNOWN) { - *out_ignore = TRUE; - return NULL; - } - if (plink->type != NM_LINK_TYPE_WIFI) return nm_device_olpc_mesh_new (iface); @@ -116,11 +110,42 @@ create_device (NMDeviceFactory *factory, iface, NM_PRINT_FMT_QUOTE_STRING (backend), WITH_IWD ? " (iwd support enabled)" : ""); - if (!backend || !strcasecmp (backend, "wpa_supplicant")) - return nm_device_wifi_new (iface, capabilities); + if (!backend || !strcasecmp (backend, "wpa_supplicant")) { + NMDevice *device; + NMDeviceWifiCapabilities capabilities; + NM80211Mode mode; + + if (!nm_platform_wifi_get_capabilities (NM_PLATFORM_GET, + plink->ifindex, + &capabilities)) { + nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, + "(%s) failed to initialize Wi-Fi driver for ifindex %d", + iface, plink->ifindex); + return NULL; + } + + /* Ignore monitor-mode and other unhandled interface types. + * FIXME: keep TYPE_MONITOR devices in UNAVAILABLE state and manage + * them if/when they change to a handled type. + */ + mode = nm_platform_wifi_get_mode (NM_PLATFORM_GET, plink->ifindex); + if (mode == NM_802_11_MODE_UNKNOWN) { + *out_ignore = TRUE; + return NULL; + } + + device = nm_device_wifi_new (iface, capabilities); + + g_signal_connect_object (device, NM_DEVICE_WIFI_P2P_DEVICE_CREATED, + G_CALLBACK (p2p_device_created), + factory, + 0); + + return device; + } #if WITH_IWD else if (!strcasecmp (backend, "iwd")) - return nm_device_iwd_new (iface, capabilities); + return nm_device_iwd_new (iface); #endif nm_log_warn (LOGD_PLATFORM | LOGD_WIFI, "(%s) config: unknown or unsupported wifi-backend %s", iface, backend); diff --git a/src/devices/wifi/nm-wifi-p2p-peer.c b/src/devices/wifi/nm-wifi-p2p-peer.c new file mode 100644 index 00000000..4b524623 --- /dev/null +++ b/src/devices/wifi/nm-wifi-p2p-peer.c @@ -0,0 +1,796 @@ +/* NetworkManager -- Wi-Fi P2P Peer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#include "nm-default.h" + +#include "nm-wifi-p2p-peer.h" + +#include <stdlib.h> + +#include "nm-setting-wireless.h" + +#include "nm-wifi-utils.h" +#include "NetworkManagerUtils.h" +#include "nm-utils.h" +#include "nm-core-internal.h" +#include "platform/nm-platform.h" +#include "devices/nm-device.h" +#include "nm-dbus-manager.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NMWifiP2PPeer, + PROP_NAME, + PROP_MANUFACTURER, + PROP_MODEL, + PROP_MODEL_NUMBER, + PROP_SERIAL, + PROP_WFD_IES, + PROP_GROUPS, + PROP_HW_ADDRESS, + PROP_STRENGTH, + PROP_LAST_SEEN, + PROP_FLAGS, +); + +struct _NMWifiP2PPeerPrivate { + char *supplicant_path; /* D-Bus object path of this Peer from wpa_supplicant */ + + /* Scanned or cached values */ + char * name; + char * manufacturer; + char * model; + char * model_number; + char * serial; + + char * address; + + GBytes * wfd_ies; + char ** groups; + + guint8 strength; + + NM80211ApFlags flags; /* General flags */ + + /* Non-scanned attributes */ + gint32 last_seen; /* Timestamp when the Peer was seen lastly (obtained via nm_utils_get_monotonic_timestamp_s()) */ +}; + +typedef struct _NMWifiP2PPeerPrivate NMWifiP2PPeerPrivate; + +struct _NMWifiP2PPeerClass { + NMDBusObjectClass parent; +}; + +G_DEFINE_TYPE (NMWifiP2PPeer, nm_wifi_p2p_peer, NM_TYPE_DBUS_OBJECT) + +#define NM_WIFI_P2P_PEER_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR(self, NMWifiP2PPeer, NM_IS_WIFI_P2P_PEER) + +/*****************************************************************************/ + +const char ** +nm_wifi_p2p_peers_get_paths (const CList *peers_lst_head) +{ + NMWifiP2PPeer *peer; + const char **list; + const char *path; + gsize i, n; + + n = c_list_length (peers_lst_head); + list = g_new (const char *, n + 1); + + i = 0; + if (n > 0) { + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + nm_assert (i < n); + path = nm_dbus_object_get_path (NM_DBUS_OBJECT (peer)); + nm_assert (path); + + list[i++] = path; + } + nm_assert (i <= n); + } + list[i] = NULL; + return list; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peers_find_first_compatible (const CList *peers_lst_head, + NMConnection *connection) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (connection, NULL); + + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + if (nm_wifi_p2p_peer_check_compatible (peer, connection)) + return peer; + } + return NULL; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peers_find_by_supplicant_path (const CList *peers_lst_head, const char *path) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (path != NULL, NULL); + + c_list_for_each_entry (peer, peers_lst_head, peers_lst) { + if (nm_streq0 (path, nm_wifi_p2p_peer_get_supplicant_path (peer))) + return peer; + } + return NULL; +} + +/*****************************************************************************/ + +NMWifiP2PPeer * +nm_wifi_p2p_peer_lookup_for_device (NMDevice *device, const char *exported_path) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (NM_IS_DEVICE (device), NULL); + + peer = (NMWifiP2PPeer *) nm_dbus_manager_lookup_object (nm_dbus_object_get_manager (NM_DBUS_OBJECT (device)), + exported_path); + if ( !peer + || !NM_IS_WIFI_P2P_PEER (peer) + || peer->wifi_device != device) + return NULL; + + return peer; +} + +/*****************************************************************************/ + +const char * +nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->supplicant_path; +} + +const char * +nm_wifi_p2p_peer_get_name (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->name; +} + +gboolean +nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, const char *name) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (name, priv->name) == 0) + return FALSE; + + g_clear_pointer (&priv->name, g_free); + if (name) + priv->name = g_strdup (name); + + _notify (peer, PROP_NAME); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_manufacturer (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->manufacturer; +} + +gboolean +nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, const char *manufacturer) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (manufacturer, priv->manufacturer) == 0) + return FALSE; + + g_clear_pointer (&priv->manufacturer, g_free); + if (manufacturer) + priv->manufacturer = g_strdup (manufacturer); + + _notify (peer, PROP_MANUFACTURER); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_model (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->model; +} + +gboolean +nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, const char *model) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (model, priv->model) == 0) + return FALSE; + + g_clear_pointer (&priv->model, g_free); + if (model) + priv->model = g_strdup (model); + + _notify (peer, PROP_MODEL); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_model_number (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->model_number; +} + +gboolean +nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, const char *model_number) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (model_number, priv->model_number) == 0) + return FALSE; + + g_clear_pointer (&priv->model_number, g_free); + if (model_number) + priv->model_number = g_strdup (model_number); + + _notify (peer, PROP_MODEL_NUMBER); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_serial (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->serial; +} + +gboolean +nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, const char *serial) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if (g_strcmp0 (serial, priv->serial) == 0) + return FALSE; + + g_clear_pointer (&priv->serial, g_free); + if (serial) + priv->serial = g_strdup (serial); + + _notify (peer, PROP_SERIAL); + return TRUE; +} + +GBytes * +nm_wifi_p2p_peer_get_wfd_ies (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->wfd_ies; +} + +gboolean +nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, GBytes *wfd_ies) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (nm_gbytes_equal0 (priv->wfd_ies, wfd_ies)) + return FALSE; + + g_bytes_unref (priv->wfd_ies); + priv->wfd_ies = wfd_ies ? g_bytes_ref (wfd_ies) : NULL; + + _notify (peer, PROP_WFD_IES); + return TRUE; +} + +const char *const* +nm_wifi_p2p_peer_get_groups (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return (const char * const*) NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->groups; +} + +static gboolean +nm_wifi_p2p_peer_set_groups (NMWifiP2PPeer *peer, const char** groups) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + g_return_val_if_fail (groups != NULL, FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (_nm_utils_strv_equal (priv->groups, (char **) groups)) + return FALSE; + + g_strfreev (priv->groups); + priv->groups = g_strdupv ((char**) groups); + + _notify (peer, PROP_GROUPS); + return TRUE; +} + +const char * +nm_wifi_p2p_peer_get_address (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NULL); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->address; +} + +static gboolean +nm_wifi_p2p_peer_set_address_bin (NMWifiP2PPeer *peer, const guint8 addr[static ETH_ALEN]) +{ + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if ( priv->address + && nm_utils_hwaddr_matches (addr, ETH_ALEN, priv->address, -1)) + return FALSE; + + g_free (priv->address); + priv->address = nm_utils_hwaddr_ntoa (addr, ETH_ALEN); + _notify (peer, PROP_HW_ADDRESS); + return TRUE; +} + +gboolean +nm_wifi_p2p_peer_set_address (NMWifiP2PPeer *peer, const char *addr) +{ + guint8 addr_buf[ETH_ALEN]; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + if ( !addr + || !nm_utils_hwaddr_aton (addr, addr_buf, sizeof (addr_buf))) + g_return_val_if_reached (FALSE); + + return nm_wifi_p2p_peer_set_address_bin (peer, addr_buf); +} + +gint8 +nm_wifi_p2p_peer_get_strength (NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), 0); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->strength; +} + +gboolean +nm_wifi_p2p_peer_set_strength (NMWifiP2PPeer *peer, const gint8 strength) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (priv->strength != strength) { + priv->strength = strength; + _notify (peer, PROP_STRENGTH); + return TRUE; + } + return FALSE; +} + +NM80211ApFlags +nm_wifi_p2p_peer_get_flags (const NMWifiP2PPeer *peer) +{ + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), NM_802_11_AP_FLAGS_NONE); + + return NM_WIFI_P2P_PEER_GET_PRIVATE (peer)->flags; +} + +static gboolean +nm_wifi_p2p_peer_set_last_seen (NMWifiP2PPeer *peer, gint32 last_seen) +{ + NMWifiP2PPeerPrivate *priv; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + if (priv->last_seen != last_seen) { + priv->last_seen = last_seen; + _notify (peer, PROP_LAST_SEEN); + return TRUE; + } + return FALSE; +} + + +/*****************************************************************************/ + +gboolean +nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, + const char *supplicant_path, + GVariant *properties) +{ + NMWifiP2PPeerPrivate *priv; + const guint8 *bytes; + GVariant *v; + gsize len; + const char *s; + const char **sv; + gint32 i32; + gboolean changed = FALSE; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (peer), FALSE); + g_return_val_if_fail (properties, FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (peer); + + g_object_freeze_notify (G_OBJECT (peer)); + + if (g_variant_lookup (properties, "level", "i", &i32)) + changed |= nm_wifi_p2p_peer_set_strength (peer, nm_wifi_utils_level_to_quality (i32)); + + if (g_variant_lookup (properties, "DeviceName", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_name (peer, s); + + if (g_variant_lookup (properties, "Manufacturer", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_manufacturer (peer, s); + + if (g_variant_lookup (properties, "Model", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_model (peer, s); + + if (g_variant_lookup (properties, "ModelNumber", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_model_number (peer, s); + + if (g_variant_lookup (properties, "Serial", "&s", &s)) + changed |= nm_wifi_p2p_peer_set_serial (peer, s); + + v = g_variant_lookup_value (properties, "DeviceAddress", G_VARIANT_TYPE_BYTESTRING); + if (v) { + bytes = g_variant_get_fixed_array (v, &len, 1); + if ( len == ETH_ALEN + && memcmp (bytes, nm_ip_addr_zero.addr_eth, ETH_ALEN) != 0 + && memcmp (bytes, (char[ETH_ALEN]) { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ETH_ALEN) != 0) + changed |= nm_wifi_p2p_peer_set_address_bin (peer, bytes); + g_variant_unref (v); + } + + /* The IEs property contains the WFD R1 subelements */ + v = g_variant_lookup_value (properties, "IEs", G_VARIANT_TYPE_BYTESTRING); + if (v) { + gs_unref_bytes GBytes *b = NULL; + + bytes = g_variant_get_fixed_array (v, &len, 1); + b = g_bytes_new (bytes, len); + changed |= nm_wifi_p2p_peer_set_wfd_ies (peer, b); + g_variant_unref (v); + } + + v = g_variant_lookup_value (properties, "Groups", G_VARIANT_TYPE_OBJECT_PATH_ARRAY); + if (v) { + sv = g_variant_get_objv (v, NULL); + changed |= nm_wifi_p2p_peer_set_groups (peer, sv); + g_free (sv); + } + + /*if (max_rate) + changed |= nm_wifi_p2p_peer_set_max_bitrate (peer, max_rate / 1000);*/ + + if (!priv->supplicant_path) { + priv->supplicant_path = g_strdup (supplicant_path); + changed = TRUE; + } + + changed |= nm_wifi_p2p_peer_set_last_seen (peer, nm_utils_get_monotonic_timestamp_s ()); + + g_object_thaw_notify (G_OBJECT (peer)); + + return changed; +} + +const char * +nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self, + char *str_buf, + gsize buf_len, + gint32 now_s) +{ + const NMWifiP2PPeerPrivate *priv; + const char *supplicant_id = "-"; + const char* export_path; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (self), NULL); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + if (priv->supplicant_path) + supplicant_id = strrchr (priv->supplicant_path, '/') ?: supplicant_id; + + export_path = nm_dbus_object_get_path (NM_DBUS_OBJECT (self)); + if (export_path) + export_path = strrchr (export_path, '/') ?: export_path; + else + export_path = "/"; + + g_snprintf (str_buf, buf_len, + "%17s [n:%s, m:%s, mod:%s, mod_num:%s, ser:%s] %3us sup:%s [nm:%s]", + priv->address ?: "(none)", + priv->name, + priv->manufacturer, + priv->model, + priv->model_number, + priv->serial, + priv->last_seen > 0 ? ((now_s > 0 ? now_s : nm_utils_get_monotonic_timestamp_s ()) - priv->last_seen) : -1, + supplicant_id, + export_path); + + return str_buf; +} + +gboolean +nm_wifi_p2p_peer_check_compatible (NMWifiP2PPeer *self, + NMConnection *connection) +{ + NMWifiP2PPeerPrivate *priv; + NMSettingWifiP2P *s_wifi_p2p; + const char *hwaddr; + + g_return_val_if_fail (NM_IS_WIFI_P2P_PEER (self), FALSE); + g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE); + + priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + s_wifi_p2p = NM_SETTING_WIFI_P2P (nm_connection_get_setting (connection, NM_TYPE_SETTING_WIFI_P2P)); + if (s_wifi_p2p == NULL) + return FALSE; + + hwaddr = nm_setting_wifi_p2p_get_peer (s_wifi_p2p); + if ( hwaddr + && ( !priv->address + || !nm_utils_hwaddr_matches (hwaddr, -1, priv->address, -1))) + return FALSE; + + return TRUE; +} + +/*****************************************************************************/ + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + NMWifiP2PPeer *self = NM_WIFI_P2P_PEER (object); + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + switch (prop_id) { + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + case PROP_MANUFACTURER: + g_value_set_string (value, priv->manufacturer); + break; + case PROP_MODEL: + g_value_set_string (value, priv->model); + break; + case PROP_MODEL_NUMBER: + g_value_set_string (value, priv->model_number); + break; + case PROP_SERIAL: + g_value_set_string (value, priv->serial); + break; + case PROP_WFD_IES: + g_value_take_variant (value, nm_utils_gbytes_to_variant_ay (priv->wfd_ies)); + break; + case PROP_GROUPS: + g_value_set_variant (value, + g_variant_new_strv ( (const char*const*) priv->groups + ?: NM_PTRARRAY_EMPTY (const char *), + -1)); + break; + case PROP_HW_ADDRESS: + g_value_set_string (value, priv->address); + break; + case PROP_STRENGTH: + g_value_set_uchar (value, priv->strength); + break; + case PROP_LAST_SEEN: + g_value_set_int (value, + priv->last_seen > 0 + ? (int) nm_utils_monotonic_timestamp_as_boottime (priv->last_seen, NM_UTILS_NS_PER_SECOND) + : -1); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_wifi_p2p_peer_init (NMWifiP2PPeer *self) +{ + NMWifiP2PPeerPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerPrivate); + + self->_priv = priv; + + c_list_init (&self->peers_lst); + + priv->flags = NM_802_11_AP_FLAGS_NONE; + priv->last_seen = -1; +} + +NMWifiP2PPeer * +nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, GVariant *properties) +{ + NMWifiP2PPeer *peer; + + g_return_val_if_fail (supplicant_path != NULL, NULL); + g_return_val_if_fail (properties != NULL, NULL); + + peer = (NMWifiP2PPeer *) g_object_new (NM_TYPE_WIFI_P2P_PEER, NULL); + nm_wifi_p2p_peer_update_from_properties (peer, supplicant_path, properties); + + /* ignore peers with invalid or missing address */ + if (!nm_wifi_p2p_peer_get_address (peer)) { + g_object_unref (peer); + return NULL; + } + + return peer; +} + +static void +finalize (GObject *object) +{ + NMWifiP2PPeer *self = NM_WIFI_P2P_PEER (object); + NMWifiP2PPeerPrivate *priv = NM_WIFI_P2P_PEER_GET_PRIVATE (self); + + nm_assert (!self->wifi_device); + nm_assert (c_list_is_empty (&self->peers_lst)); + + g_free (priv->supplicant_path); + g_free (priv->name); + g_free (priv->manufacturer); + g_free (priv->model); + g_free (priv->model_number); + g_free (priv->serial); + g_free (priv->address); + g_bytes_unref (priv->wfd_ies); + g_strfreev (priv->groups); + + G_OBJECT_CLASS (nm_wifi_p2p_peer_parent_class)->finalize (object); +} + +static const NMDBusInterfaceInfoExtended interface_info_p2p_peer = { + .parent = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT ( + NM_DBUS_INTERFACE_WIFI_P2P_PEER, + .properties = NM_DEFINE_GDBUS_PROPERTY_INFOS ( + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Flags", "u", NM_WIFI_P2P_PEER_FLAGS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Name", "s", NM_WIFI_P2P_PEER_NAME), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Manufacturer", "s", NM_WIFI_P2P_PEER_MANUFACTURER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Model", "s", NM_WIFI_P2P_PEER_MODEL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("ModelNumber", "s", NM_WIFI_P2P_PEER_MODEL_NUMBER), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Serial", "s", NM_WIFI_P2P_PEER_SERIAL), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("WfdIEs", "ay", NM_WIFI_P2P_PEER_WFD_IES), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Groups", "as", NM_WIFI_P2P_PEER_GROUPS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("HwAddress", "s", NM_WIFI_P2P_PEER_HW_ADDRESS), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("Strength", "y", NM_WIFI_P2P_PEER_STRENGTH), + NM_DEFINE_DBUS_PROPERTY_INFO_EXTENDED_READABLE ("LastSeen", "i", NM_WIFI_P2P_PEER_LAST_SEEN), + ), + ), + .legacy_property_changed = FALSE, +}; + +static void +nm_wifi_p2p_peer_class_init (NMWifiP2PPeerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + NMDBusObjectClass *dbus_object_class = NM_DBUS_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (NMWifiP2PPeerPrivate)); + + dbus_object_class->export_path = NM_DBUS_EXPORT_PATH_NUMBERED (NM_DBUS_PATH_WIFI_P2P_PEER); + dbus_object_class->interface_infos = NM_DBUS_INTERFACE_INFOS (&interface_info_p2p_peer); + + object_class->get_property = get_property; + object_class->finalize = finalize; + + obj_properties[PROP_FLAGS] = + g_param_spec_uint (NM_WIFI_P2P_PEER_FLAGS, "", "", + NM_802_11_AP_FLAGS_NONE, + NM_802_11_AP_FLAGS_PRIVACY, + NM_802_11_AP_FLAGS_NONE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_NAME] = + g_param_spec_string (NM_WIFI_P2P_PEER_NAME, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MANUFACTURER] = + g_param_spec_string (NM_WIFI_P2P_PEER_MANUFACTURER, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MODEL] = + g_param_spec_string (NM_WIFI_P2P_PEER_MODEL, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_MODEL_NUMBER] = + g_param_spec_string (NM_WIFI_P2P_PEER_MODEL_NUMBER, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_SERIAL] = + g_param_spec_string (NM_WIFI_P2P_PEER_SERIAL, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_WFD_IES] = + g_param_spec_variant (NM_WIFI_P2P_PEER_WFD_IES, "", "", + G_VARIANT_TYPE ("ay"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_GROUPS] = + g_param_spec_variant (NM_WIFI_P2P_PEER_GROUPS, "", "", + G_VARIANT_TYPE ("as"), + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_HW_ADDRESS] = + g_param_spec_string (NM_WIFI_P2P_PEER_HW_ADDRESS, "", "", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_STRENGTH] = + g_param_spec_uchar (NM_WIFI_P2P_PEER_STRENGTH, "", "", + 0, G_MAXINT8, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_LAST_SEEN] = + g_param_spec_int (NM_WIFI_P2P_PEER_LAST_SEEN, "", "", + -1, G_MAXINT, -1, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/devices/wifi/nm-wifi-p2p-peer.h b/src/devices/wifi/nm-wifi-p2p-peer.h new file mode 100644 index 00000000..d6ff7abc --- /dev/null +++ b/src/devices/wifi/nm-wifi-p2p-peer.h @@ -0,0 +1,114 @@ +/* NetworkManager -- Wi-Fi P2P Peer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright (C) 2018 Red Hat, Inc. + */ + +#ifndef __NM_WIFI_P2P_PEER_H__ +#define __NM_WIFI_P2P_PEER_H__ + +#include "nm-dbus-object.h" +#include "nm-dbus-interface.h" +#include "nm-connection.h" + +#define NM_TYPE_WIFI_P2P_PEER (nm_wifi_p2p_peer_get_type ()) +#define NM_WIFI_P2P_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeer)) +#define NM_WIFI_P2P_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerClass)) +#define NM_IS_WIFI_P2P_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_WIFI_P2P_PEER)) +#define NM_IS_WIFI_P2P_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_WIFI_P2P_PEER)) +#define NM_WIFI_P2P_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_WIFI_P2P_PEER, NMWifiP2PPeerClass)) + +#define NM_WIFI_P2P_PEER_FLAGS "flags" +#define NM_WIFI_P2P_PEER_NAME "name" +#define NM_WIFI_P2P_PEER_MANUFACTURER "manufacturer" +#define NM_WIFI_P2P_PEER_MODEL "model" +#define NM_WIFI_P2P_PEER_MODEL_NUMBER "model-number" +#define NM_WIFI_P2P_PEER_SERIAL "serial" +#define NM_WIFI_P2P_PEER_WFD_IES "wfd-ies" +#define NM_WIFI_P2P_PEER_GROUPS "groups" +#define NM_WIFI_P2P_PEER_HW_ADDRESS "hw-address" +#define NM_WIFI_P2P_PEER_STRENGTH "strength" +#define NM_WIFI_P2P_PEER_LAST_SEEN "last-seen" + +typedef struct { + NMDBusObject parent; + NMDevice *wifi_device; + CList peers_lst; + struct _NMWifiP2PPeerPrivate *_priv; +} NMWifiP2PPeer; + +typedef struct _NMWifiP2PPeerClass NMWifiP2PPeerClass; + +GType nm_wifi_p2p_peer_get_type (void); + +NMWifiP2PPeer * nm_wifi_p2p_peer_new_from_properties (const char *supplicant_path, + GVariant *properties); + +gboolean nm_wifi_p2p_peer_update_from_properties (NMWifiP2PPeer *peer, + const char *supplicant_path, + GVariant *properties); + +gboolean nm_wifi_p2p_peer_check_compatible (NMWifiP2PPeer *self, + NMConnection *connection); + +const char * nm_wifi_p2p_peer_get_supplicant_path (NMWifiP2PPeer *peer); + +const char * nm_wifi_p2p_peer_get_name (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_name (NMWifiP2PPeer *peer, + const char *name); +const char * nm_wifi_p2p_peer_get_manufacturer (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_manufacturer (NMWifiP2PPeer *peer, + const char *manufacturer); +const char * nm_wifi_p2p_peer_get_model (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_model (NMWifiP2PPeer *peer, + const char *model); +const char * nm_wifi_p2p_peer_get_model_number (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_model_number (NMWifiP2PPeer *peer, + const char *number); +const char * nm_wifi_p2p_peer_get_serial (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_serial (NMWifiP2PPeer *peer, + const char *serial); + +GBytes * nm_wifi_p2p_peer_get_wfd_ies (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_wfd_ies (NMWifiP2PPeer *peer, + GBytes *bytes); + +const char *const*nm_wifi_p2p_peer_get_groups (const NMWifiP2PPeer *peer); + +const char * nm_wifi_p2p_peer_get_address (const NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_address (NMWifiP2PPeer *peer, + const char *addr); +gint8 nm_wifi_p2p_peer_get_strength (NMWifiP2PPeer *peer); +gboolean nm_wifi_p2p_peer_set_strength (NMWifiP2PPeer *peer, + gint8 strength); +NM80211ApFlags nm_wifi_p2p_peer_get_flags (const NMWifiP2PPeer *self); + +const char *nm_wifi_p2p_peer_to_string (const NMWifiP2PPeer *self, + char *str_buf, + gsize buf_len, + gint32 now_s); + +const char **nm_wifi_p2p_peers_get_paths (const CList *peers_lst_head); + +NMWifiP2PPeer *nm_wifi_p2p_peers_find_first_compatible (const CList *peers_lst_head, + NMConnection *connection); + +NMWifiP2PPeer *nm_wifi_p2p_peers_find_by_supplicant_path (const CList *peers_lst_head, const char *path); + +NMWifiP2PPeer *nm_wifi_p2p_peer_lookup_for_device (NMDevice *device, const char *exported_path); + +#endif /* __NM_WIFI_P2P_PEER_H__ */ diff --git a/src/devices/wifi/nm-wifi-utils.c b/src/devices/wifi/nm-wifi-utils.c index 0f7836be..c6e8b3e0 100644 --- a/src/devices/wifi/nm-wifi-utils.c +++ b/src/devices/wifi/nm-wifi-utils.c @@ -22,7 +22,6 @@ #include "nm-wifi-utils.h" -#include <string.h> #include <stdlib.h> #include "nm-utils.h" @@ -739,6 +738,12 @@ nm_wifi_utils_complete_connection (GBytes *ap_ssid, * setting. Since there's so much configuration required for it, there's * no way it can be automatically completed. */ + } else if ( (key_mgmt && !strcmp (key_mgmt, "sae")) + || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) { + g_object_set (s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "sae", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NULL); } else if ( (key_mgmt && !strcmp (key_mgmt, "wpa-psk")) || (ap_wpa_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK) || (ap_rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_PSK)) { diff --git a/src/devices/wifi/tests/meson.build b/src/devices/wifi/tests/meson.build index bb8f7c27..895853c4 100644 --- a/src/devices/wifi/tests/meson.build +++ b/src/devices/wifi/tests/meson.build @@ -3,11 +3,12 @@ test_unit = 'test-general' exe = executable( 'wifi-' + test_unit, [test_unit + '.c'] + common_sources, - dependencies: test_nm_dep + dependencies: test_nm_dep, ) test( 'devices/wifi/' + test_unit, test_script, - args: test_args + [exe.full_path()] + args: test_args + [exe.full_path()], + timeout: default_test_timeout, ) diff --git a/src/devices/wifi/tests/test-general.c b/src/devices/wifi/tests/test-general.c index f752bbfc..e0899837 100644 --- a/src/devices/wifi/tests/test-general.c +++ b/src/devices/wifi/tests/test-general.c @@ -20,10 +20,7 @@ #include "nm-default.h" -#include <string.h> - #include "devices/wifi/nm-wifi-utils.h" - #include "nm-core-internal.h" #include "nm-test-utils-core.h" diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index 70b954c5..ea966afe 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -6,7 +6,6 @@ global: nm_modem_complete_connection; nm_modem_deactivate; nm_modem_deactivate_async; - nm_modem_deactivate_async_finish; nm_modem_device_state_changed; nm_modem_get_capabilities; nm_modem_get_configured_mtu; diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build index 5fe6e433..482dc205 100644 --- a/src/devices/wwan/meson.build +++ b/src/devices/wwan/meson.build @@ -1,13 +1,13 @@ sources = files( 'nm-modem-broadband.c', 'nm-modem.c', - 'nm-modem-manager.c' + 'nm-modem-manager.c', ) deps = [ libsystemd_dep, mm_glib_dep, - nm_dep + nm_dep, ] if enable_ofono @@ -25,12 +25,12 @@ libnm_wwan = shared_module( ], link_depends: linker_script, install: true, - install_dir: nm_plugindir + install_dir: nm_plugindir, ) libnm_wwan_dep = declare_dependency( include_directories: include_directories('.'), - link_with: libnm_wwan + link_with: libnm_wwan, ) core_plugins += libnm_wwan @@ -43,7 +43,7 @@ test( sources = files( 'nm-device-modem.c', - 'nm-wwan-factory.c' + 'nm-wwan-factory.c', ) libnm_device_plugin_wwan = shared_module( @@ -55,7 +55,7 @@ libnm_device_plugin_wwan = shared_module( link_depends: linker_script_devices, install: true, install_dir: nm_plugindir, - install_rpath: nm_plugindir + install_rpath: nm_plugindir, ) core_plugins += libnm_device_plugin_wwan @@ -63,7 +63,7 @@ core_plugins += libnm_device_plugin_wwan run_target( 'check-local-devices-wwan', command: [check_exports, libnm_device_plugin_wwan.full_path(), linker_script_devices], - depends: libnm_device_plugin_wwan + depends: libnm_device_plugin_wwan, ) # FIXME: check_so_symbols replacement diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 8ff931aa..1e316280 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -22,9 +22,8 @@ #include "nm-device-modem.h" -#include <string.h> - #include "nm-modem.h" +#include "nm-ip4-config.h" #include "devices/nm-device-private.h" #include "nm-rfkill-manager.h" #include "settings/nm-settings-connection.h" @@ -61,7 +60,7 @@ struct _NMDeviceModemClass { G_DEFINE_TYPE (NMDeviceModem, nm_device_modem, NM_TYPE_DEVICE) -#define NM_DEVICE_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceModem, NM_IS_DEVICE_MODEM) +#define NM_DEVICE_MODEM_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMDeviceModem, NM_IS_DEVICE_MODEM, NMDevice) /*****************************************************************************/ @@ -85,9 +84,9 @@ ppp_failed (NMModem *modem, case NM_DEVICE_STATE_SECONDARIES: case NM_DEVICE_STATE_ACTIVATED: if (nm_device_activate_ip4_state_in_conf (device)) - nm_device_activate_schedule_ip4_config_timeout (device); + nm_device_activate_schedule_ip_config_timeout (device, AF_INET); else if (nm_device_activate_ip6_state_in_conf (device)) - nm_device_activate_schedule_ip6_config_timeout (device); + nm_device_activate_schedule_ip_config_timeout (device, AF_INET6); else if (nm_device_activate_ip4_state_done (device)) { nm_device_ip_method_failed (device, AF_INET, @@ -213,8 +212,8 @@ modem_ip4_config_result (NMModem *modem, AF_INET, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); } else { - nm_device_set_wwan_ip4_config (device, config); - nm_device_activate_schedule_ip4_config_result (device, NULL); + nm_device_set_dev2_ip_config (device, AF_INET, NM_IP_CONFIG_CAST (config)); + nm_device_activate_schedule_ip_config_result (device, AF_INET, NULL); } } @@ -229,7 +228,7 @@ modem_ip6_config_result (NMModem *modem, NMDevice *device = NM_DEVICE (self); NMActStageReturn ret; NMDeviceStateReason failure_reason = NM_DEVICE_STATE_REASON_NONE; - NMIP6Config *ignored = NULL; + gs_unref_object NMIP6Config *ignored = NULL; gboolean got_config = !!config; g_return_if_fail (nm_device_activate_ip6_state_in_conf (device) == TRUE); @@ -244,14 +243,14 @@ modem_ip6_config_result (NMModem *modem, } /* Re-enable IPv6 on the interface */ - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "0"); + nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "0"); if (config) - nm_device_set_wwan_ip6_config (device, config); + nm_device_set_dev2_ip_config (device, AF_INET6, NM_IP_CONFIG_CAST (config)); if (do_slaac == FALSE) { if (got_config) - nm_device_activate_schedule_ip6_config_result (device); + nm_device_activate_schedule_ip_config_result (device, AF_INET6, NULL); else { _LOGW (LOGD_MB | LOGD_IP6, "retrieving IPv6 configuration failed: SLAAC not requested and no addresses"); nm_device_ip_method_failed (device, @@ -262,15 +261,17 @@ modem_ip6_config_result (NMModem *modem, } /* Start SLAAC now that we have a link-local address from the modem */ - ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip6_config_start (device, &ignored, &failure_reason); - g_assert (ignored == NULL); + ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage3_ip_config_start (device, AF_INET6, (gpointer *) &ignored, &failure_reason); + + nm_assert (ignored == NULL); + switch (ret) { case NM_ACT_STAGE_RETURN_FAILURE: nm_device_ip_method_failed (device, AF_INET6, failure_reason); break; case NM_ACT_STAGE_RETURN_IP_FAIL: /* all done */ - nm_device_activate_schedule_ip6_config_result (device); + nm_device_activate_schedule_ip_config_result (device, AF_INET6, NULL); break; case NM_ACT_STAGE_RETURN_POSTPONE: /* let SLAAC run */ @@ -279,7 +280,7 @@ modem_ip6_config_result (NMModem *modem, /* Should never get here since we've assured that the IPv6 method * will either be "auto" or "ignored" when starting IPv6 configuration. */ - g_assert_not_reached (); + nm_assert_not_reached (); } } @@ -303,7 +304,7 @@ ip_ifindex_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) * internally, and leaving it enabled could allow the kernel's IPv6 * RA handling code to run before NM is ready. */ - nm_device_ipv6_sysctl_set (device, "disable_ipv6", "1"); + nm_device_sysctl_ip_conf_set (device, AF_INET6, "disable_ipv6", "1"); } static void @@ -474,7 +475,7 @@ check_connection_available (NMDevice *device, state = nm_modem_get_state (priv->modem); if (state <= NM_MODEM_STATE_INITIALIZING) { nm_utils_error_set_literal (error, NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY, - "modem not initalized"); + "modem not initialized"); return FALSE; } @@ -509,44 +510,35 @@ deactivate (NMDevice *device) /*****************************************************************************/ -static gboolean -deactivate_async_finish (NMDevice *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - static void -modem_deactivate_async_ready (NMModem *modem, - GAsyncResult *res, - GSimpleAsyncResult *simple) +modem_deactivate_async_cb (NMModem *modem, + GError *error, + gpointer user_data) { - GError *error = NULL; + gs_unref_object NMDevice *self = NULL; + NMDeviceDeactivateCallback callback; + gpointer callback_user_data; - if (!nm_modem_deactivate_async_finish (modem, res, &error)) - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete (simple); - g_object_unref (simple); + nm_utils_user_data_unpack (user_data, &self, &callback, &callback_user_data); + callback (self, error, callback_user_data); } static void deactivate_async (NMDevice *self, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMDeviceDeactivateCallback callback, gpointer user_data) { - GSimpleAsyncResult *simple; + nm_assert (G_IS_CANCELLABLE (cancellable)); + nm_assert (callback); - simple = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - deactivate_async); - nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) self)->modem, + nm_modem_deactivate_async (NM_DEVICE_MODEM_GET_PRIVATE (self)->modem, self, cancellable, - (GAsyncReadyCallback) modem_deactivate_async_ready, - simple); + modem_deactivate_async_cb, + nm_utils_user_data_pack (g_object_ref (self), + callback, + user_data)); } /*****************************************************************************/ @@ -579,14 +571,25 @@ act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) } static NMActStageReturn -act_stage3_ip4_config_start (NMDevice *device, - NMIP4Config **out_config, - NMDeviceStateReason *out_failure_reason) +act_stage3_ip_config_start (NMDevice *device, + int addr_family, + gpointer *out_config, + NMDeviceStateReason *out_failure_reason) { - return nm_modem_stage3_ip4_config_start (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, - device, - NM_DEVICE_CLASS (nm_device_modem_parent_class), - out_failure_reason); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + nm_assert_addr_family (addr_family); + + if (addr_family == AF_INET) { + return nm_modem_stage3_ip4_config_start (priv->modem, + device, + NM_DEVICE_CLASS (nm_device_modem_parent_class), + out_failure_reason); + } else { + return nm_modem_stage3_ip6_config_start (priv->modem, + device, + out_failure_reason); + } } static void @@ -595,16 +598,6 @@ ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, device, config); } -static NMActStageReturn -act_stage3_ip6_config_start (NMDevice *device, - NMIP6Config **out_config, - NMDeviceStateReason *out_failure_reason) -{ - return nm_modem_stage3_ip6_config_start (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, - device, - out_failure_reason); -} - static gboolean get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid) { @@ -831,12 +824,10 @@ nm_device_modem_class_init (NMDeviceModemClass *klass) device_class->check_connection_available = check_connection_available; device_class->complete_connection = complete_connection; device_class->deactivate_async = deactivate_async; - device_class->deactivate_async_finish = deactivate_async_finish; device_class->deactivate = deactivate; device_class->act_stage1_prepare = act_stage1_prepare; device_class->act_stage2_config = act_stage2_config; - device_class->act_stage3_ip4_config_start = act_stage3_ip4_config_start; - device_class->act_stage3_ip6_config_start = act_stage3_ip6_config_start; + device_class->act_stage3_ip_config_start = act_stage3_ip_config_start; device_class->ip4_config_pre_commit = ip4_config_pre_commit; device_class->get_enabled = get_enabled; device_class->set_enabled = set_enabled; diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 04cb8599..1cb549b0 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -22,7 +22,6 @@ #include "nm-modem-broadband.h" -#include <string.h> #include <arpa/inet.h> #include <libmm-glib.h> @@ -254,16 +253,21 @@ get_bearer_ip_method (MMBearerIpConfig *config) static MMSimpleConnectProperties * create_cdma_connect_properties (NMConnection *connection) { - NMSettingCdma *setting; MMSimpleConnectProperties *properties; - const char *str; - setting = nm_connection_get_setting_cdma (connection); properties = mm_simple_connect_properties_new (); - str = nm_setting_cdma_get_number (setting); - if (str) - mm_simple_connect_properties_set_number (properties, str); +#if !MM_CHECK_VERSION (1, 9, 1) + { + NMSettingCdma *setting; + const char *str; + + setting = nm_connection_get_setting_cdma (connection); + str = nm_setting_cdma_get_number (setting); + if (str) + mm_simple_connect_properties_set_number (properties, str); + } +#endif return properties; } @@ -279,11 +283,6 @@ create_gsm_connect_properties (NMConnection *connection) setting = nm_connection_get_setting_gsm (connection); properties = mm_simple_connect_properties_new (); - /* TODO: not needed */ - str = nm_setting_gsm_get_number (setting); - if (str) - mm_simple_connect_properties_set_number (properties, str); - /* Blank APN ("") means the default subscription APN */ str = nm_setting_gsm_get_apn (setting); mm_simple_connect_properties_set_apn (properties, str ?: ""); @@ -693,10 +692,6 @@ complete_connection (NMModem *_self, return FALSE; } - /* TODO: This is not needed */ - if (!nm_setting_gsm_get_number (s_gsm)) - g_object_set (G_OBJECT (s_gsm), NM_SETTING_GSM_NUMBER, "*99#", NULL); - nm_utils_complete_generic (NM_PLATFORM_GET, connection, NM_SETTING_GSM_SETTING_NAME, @@ -884,7 +879,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Fully fail if invalid IP address retrieved */ address_string = mm_bearer_ip_config_get_address (self->_priv.ipv4_config); if ( !address_string - || !nm_utils_parse_inaddr_bin (AF_INET, address_string, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, address_string, NULL, &address_network)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid address given %s%s%s", @@ -896,7 +891,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* Missing gateway not a hard failure */ gw_string = mm_bearer_ip_config_get_gateway (self->_priv.ipv4_config); if ( gw_string - && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, &gw)) { + && !nm_utils_parse_inaddr_bin (AF_INET, gw_string, NULL, &gw)) { error = g_error_new (NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, "(%s) retrieving IP4 configuration failed: invalid gateway address \"%s\"", @@ -937,7 +932,7 @@ static_stage3_ip4_done (NMModemBroadband *self) /* DNS servers */ dns = mm_bearer_ip_config_get_dns (self->_priv.ipv4_config); for (i = 0; dns && dns[i]; i++) { - if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], &address_network) + if ( nm_utils_parse_inaddr_bin (AF_INET, dns[i], NULL, &address_network) && address_network > 0) { nm_ip4_config_add_nameserver (config, address_network); _LOGI (" DNS %s", dns[i]); @@ -1098,100 +1093,101 @@ stage3_ip6_config_request (NMModem *modem, NMDeviceStateReason *out_failure_reas typedef struct { NMModemBroadband *self; - GSimpleAsyncResult *result; + _NMModemDisconnectCallback callback; + gpointer callback_user_data; GCancellable *cancellable; gboolean warn; } DisconnectContext; static void -disconnect_context_complete (DisconnectContext *ctx) +disconnect_context_complete (DisconnectContext *ctx, GError *error) { - g_simple_async_result_complete_in_idle (ctx->result); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_object_unref (ctx->result); + if (ctx->callback) + ctx->callback (NM_MODEM (ctx->self), error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } -static gboolean -disconnect_finish (NMModem *self, - GAsyncResult *res, - GError **error) +static void +disconnect_context_complete_on_idle (gpointer user_data, + GCancellable *cancellable) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); + DisconnectContext *ctx = user_data; + gs_free_error GError *cancelled_error = NULL; + + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + disconnect_context_complete (ctx, cancelled_error); } static void -simple_disconnect_ready (MMModemSimple *modem_iface, +simple_disconnect_ready (GObject *source_object, GAsyncResult *res, - DisconnectContext *ctx) + gpointer user_data) { + MMModemSimple *modem_iface = MM_MODEM_SIMPLE (source_object); + DisconnectContext *ctx = user_data; GError *error = NULL; if (!mm_modem_simple_disconnect_finish (modem_iface, res, &error)) { - if (ctx->warn && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { + if ( ctx->warn + && !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) { NMModemBroadband *self = ctx->self; _LOGW ("failed to disconnect modem: %s", error->message); } - g_simple_async_result_take_error (ctx->result, error); } - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); } static void disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data) { NMModemBroadband *self = NM_MODEM_BROADBAND (modem); DisconnectContext *ctx; - GError *error = NULL; connect_context_clear (self); - ctx = g_slice_new (DisconnectContext); - ctx->cancellable = NULL; + + ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - disconnect); + ctx->cancellable = nm_g_object_ref (cancellable); + ctx->callback = callback; + ctx->callback_user_data = user_data; + /* Don't bother warning on FAILED since the modem is already gone */ ctx->warn = warn; - /* Already cancelled? */ - if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - disconnect_context_complete (ctx); - return; - } - - /* If no simple iface, we're done */ - if (!ctx->self->_priv.simple_iface) { - disconnect_context_complete (ctx); + /* Already cancelled or no simple-iface? We are done. */ + if ( !ctx->self->_priv.simple_iface + || g_cancellable_is_cancelled (cancellable)) { + nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, + ctx, + cancellable); return; } _LOGD ("notifying ModemManager about the modem disconnection"); - ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL; - mm_modem_simple_disconnect (ctx->self->_priv.simple_iface, + mm_modem_simple_disconnect (self->_priv.simple_iface, NULL, /* bearer path; if NULL given ALL get disconnected */ cancellable, - (GAsyncReadyCallback) simple_disconnect_ready, + simple_disconnect_ready, ctx); } /*****************************************************************************/ static void -deactivate_cleanup (NMModem *_self, NMDevice *device) +deactivate_cleanup (NMModem *modem, + NMDevice *device, + gboolean stop_ppp_manager) { - NMModemBroadband *self = NM_MODEM_BROADBAND (_self); + NMModemBroadband *self = NM_MODEM_BROADBAND (modem); /* TODO: cancel SimpleConnect() if any */ @@ -1202,8 +1198,9 @@ deactivate_cleanup (NMModem *_self, NMDevice *device) self->_priv.pin_tries = 0; - /* Chain up parent's */ - NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (_self, device); + NM_MODEM_CLASS (nm_modem_broadband_parent_class)->deactivate_cleanup (modem, + device, + stop_ppp_manager); } /*****************************************************************************/ @@ -1468,7 +1465,6 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass) modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; modem_class->stage3_ip6_config_request = stage3_ip6_config_request; modem_class->disconnect = disconnect; - modem_class->disconnect_finish = disconnect_finish; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->set_mm_enabled = set_mm_enabled; modem_class->get_user_pass = get_user_pass; diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c index dfc102f3..fac14d69 100644 --- a/src/devices/wwan/nm-modem-manager.c +++ b/src/devices/wwan/nm-modem-manager.c @@ -24,7 +24,6 @@ #include "nm-modem-manager.h" -#include <string.h> #include <libmm-glib.h> #if HAVE_LIBSYSTEMD diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index ea668590..78d9a9f0 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -22,8 +22,6 @@ #include "nm-modem-ofono.h" -#include <string.h> - #include "nm-core-internal.h" #include "devices/nm-device-private.h" #include "nm-modem.h" @@ -146,30 +144,36 @@ update_modem_state (NMModemOfono *self) /* Disconnect */ typedef struct { NMModemOfono *self; - GSimpleAsyncResult *result; + _NMModemDisconnectCallback callback; + gpointer callback_user_data; GCancellable *cancellable; gboolean warn; } DisconnectContext; static void -disconnect_context_complete (DisconnectContext *ctx) +disconnect_context_complete (DisconnectContext *ctx, GError *error) { - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - if (ctx->result) { - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); - } + if (ctx->callback) + ctx->callback (NM_MODEM (ctx->self), error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->self); g_slice_free (DisconnectContext, ctx); } -static gboolean -disconnect_finish (NMModem *self, - GAsyncResult *result, - GError **error) +static void +disconnect_context_complete_on_idle (gpointer user_data, + GCancellable *cancellable) { - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); + DisconnectContext *ctx = user_data; + gs_free_error GError *error = NULL; + + if (!g_cancellable_set_error_if_cancelled (cancellable, &error)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + ("modem is currently not connected")); + } + disconnect_context_complete (ctx, error); } static void @@ -177,16 +181,14 @@ disconnect_done (GObject *source, GAsyncResult *result, gpointer user_data) { - DisconnectContext *ctx = (DisconnectContext*) user_data; + DisconnectContext *ctx = user_data; NMModemOfono *self = ctx->self; gs_free_error GError *error = NULL; gs_unref_variant GVariant *v = NULL; v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), result, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - if (ctx->result) - g_simple_async_result_take_error (ctx->result, g_steal_pointer (&error)); - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); return; } @@ -196,21 +198,20 @@ disconnect_done (GObject *source, _LOGD ("modem disconnected"); update_modem_state (self); - disconnect_context_complete (ctx); + disconnect_context_complete (ctx, error); } static void disconnect (NMModem *modem, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data) { NMModemOfono *self = NM_MODEM_OFONO (modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); DisconnectContext *ctx; NMModemState state = nm_modem_get_state (NM_MODEM (self)); - GError *error = NULL; _LOGD ("warn: %s modem_state: %s", warn ? "TRUE" : "FALSE", @@ -218,37 +219,19 @@ disconnect (NMModem *modem, ctx = g_slice_new0 (DisconnectContext); ctx->self = g_object_ref (self); + ctx->cancellable = nm_g_object_ref (cancellable); ctx->warn = warn; - if (callback) { - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - disconnect); - } - - if (state != NM_MODEM_STATE_CONNECTED) { - if (ctx->result) { - g_set_error_literal (&error, - NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - ("modem is currently not connected")); - g_simple_async_result_take_error (ctx->result, error); - } - disconnect_context_complete (ctx); + ctx->callback = callback; + ctx->callback_user_data = user_data; + + if ( state != NM_MODEM_STATE_CONNECTED + || g_cancellable_is_cancelled (cancellable)) { + nm_utils_invoke_on_idle (disconnect_context_complete_on_idle, + ctx, + cancellable); return; } - if (g_cancellable_set_error_if_cancelled (cancellable, &error)) { - if (ctx->result) - g_simple_async_result_take_error (ctx->result, error); - else - g_clear_error (&error); - disconnect_context_complete (ctx); - return; - } - - ctx->cancellable = nm_g_object_ref (cancellable); - nm_modem_set_state (NM_MODEM (self), NM_MODEM_STATE_DISCONNECTING, nm_modem_state_to_string (NM_MODEM_STATE_DISCONNECTING)); @@ -266,7 +249,9 @@ disconnect (NMModem *modem, } static void -deactivate_cleanup (NMModem *modem, NMDevice *device) +deactivate_cleanup (NMModem *modem, + NMDevice *device, + gboolean stop_ppp_manager) { NMModemOfono *self = NM_MODEM_OFONO (modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); @@ -275,7 +260,9 @@ deactivate_cleanup (NMModem *modem, NMDevice *device) g_clear_object (&priv->ip4_config); - NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (modem, device); + NM_MODEM_CLASS (nm_modem_ofono_parent_class)->deactivate_cleanup (modem, + device, + stop_ppp_manager); } static gboolean @@ -664,7 +651,7 @@ handle_connman_iface (NMModemOfono *self, gboolean found) OFONO_DBUS_INTERFACE_CONNECTION_MANAGER, priv->connman_proxy_cancellable, _connman_proxy_new_cb, - NULL); + self); } } @@ -883,7 +870,7 @@ context_property_changed (GDBusProxy *proxy, goto out; } if ( !s - || !nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { _LOGW ("can't convert 'Address' %s to addr", s ?: ""); goto out; } @@ -897,7 +884,7 @@ context_property_changed (GDBusProxy *proxy, goto out; } if ( !s - || !nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + || !nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { _LOGW ("invalid 'Netmask': %s", s ?: ""); goto out; } @@ -911,7 +898,7 @@ context_property_changed (GDBusProxy *proxy, _LOGW ("Settings 'Gateway' missing"); goto out; } - if (!nm_utils_parse_inaddr_bin (AF_INET, s, &gateway_network)) { + if (!nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &gateway_network)) { _LOGW ("invalid 'Gateway': %s", s); goto out; } @@ -938,7 +925,7 @@ context_property_changed (GDBusProxy *proxy, } if (array) { for (iter = array; *iter; iter++) { - if ( nm_utils_parse_inaddr_bin (AF_INET, *iter, &address_network) + if ( nm_utils_parse_inaddr_bin (AF_INET, *iter, NULL, &address_network) && address_network) { _LOGI ("DNS: %s", *iter); nm_ip4_config_add_nameserver (priv->ip4_config, address_network); @@ -958,7 +945,7 @@ context_property_changed (GDBusProxy *proxy, if (g_variant_lookup (v_dict, "MessageProxy", "&s", &s)) { _LOGI ("MessageProxy: %s", s); if ( s - && nm_utils_parse_inaddr_bin (AF_INET, s, &address_network)) { + && nm_utils_parse_inaddr_bin (AF_INET, s, NULL, &address_network)) { nm_modem_get_route_parameters (NM_MODEM (self), &ip4_route_table, &ip4_route_metric, @@ -1319,7 +1306,6 @@ nm_modem_ofono_class_init (NMModemOfonoClass *klass) modem_class->get_capabilities = get_capabilities; modem_class->disconnect = disconnect; - modem_class->disconnect_finish = disconnect_finish; modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 59b081e9..2217f2a2 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -24,7 +24,6 @@ #include "nm-modem.h" #include <fcntl.h> -#include <string.h> #include <termios.h> #include <linux/rtnetlink.h> @@ -706,13 +705,13 @@ nm_modem_stage3_ip4_config_start (NMModem *self, nm_modem_set_route_parameters_from_device (self, device); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET); /* Only Disabled and Auto methods make sense for WWAN */ - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0) + if (nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) return NM_ACT_STAGE_RETURN_SUCCESS; - if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) != 0) { + if (!nm_streq (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) { _LOGE ("unhandled WWAN IPv4 method '%s'; will fail", method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_METHOD_UNSUPPORTED); return NM_ACT_STAGE_RETURN_FAILURE; @@ -729,7 +728,7 @@ nm_modem_stage3_ip4_config_start (NMModem *self, break; case NM_MODEM_IP_METHOD_AUTO: _LOGD ("MODEM_IP_METHOD_AUTO"); - ret = device_class->act_stage3_ip4_config_start (device, NULL, out_failure_reason); + ret = device_class->act_stage3_ip_config_start (device, AF_INET, NULL, out_failure_reason); break; default: _LOGI ("IPv4 configuration disabled"); @@ -823,13 +822,13 @@ nm_modem_stage3_ip6_config_start (NMModem *self, nm_modem_set_route_parameters_from_device (self, device); - method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG); + method = nm_utils_get_ip_config_method (connection, AF_INET6); /* Only Ignore and Auto methods make sense for WWAN */ - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) + if (nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE)) return NM_ACT_STAGE_RETURN_IP_DONE; - if (strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) != 0) { + if (!nm_streq (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO)) { _LOGW ("unhandled WWAN IPv6 method '%s'; will fail", method); NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_IP_CONFIG_UNAVAILABLE); @@ -948,7 +947,7 @@ nm_modem_get_secrets (NMModem *self, FALSE, setting_name, flags, - hint, + NM_MAKE_STRV (hint), modem_secrets_cb, self); g_return_if_fail (priv->secrets_id); @@ -986,8 +985,7 @@ nm_modem_act_stage1_prepare (NMModem *self, setting_name = nm_connection_need_secrets (connection, &hints); if (!setting_name) { - /* Ready to connect */ - g_assert (!hints); + nm_assert (!hints); return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, out_failure_reason); } @@ -995,11 +993,14 @@ nm_modem_act_stage1_prepare (NMModem *self, if (priv->secrets_tries++) flags |= NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW; + if (hints) + g_ptr_array_add (hints, NULL); + priv->secrets_id = nm_act_request_get_secrets (req, FALSE, setting_name, flags, - hints ? g_ptr_array_index (hints, 0) : NULL, + hints ? (const char *const*) hints->pdata : NULL, modem_secrets_cb, self); g_return_val_if_fail (priv->secrets_id, NM_ACT_STAGE_RETURN_FAILURE); @@ -1105,7 +1106,9 @@ nm_modem_complete_connection (NMModem *self, /*****************************************************************************/ static void -deactivate_cleanup (NMModem *self, NMDevice *device) +deactivate_cleanup (NMModem *self, + NMDevice *device, + gboolean stop_ppp_manager) { NMModemPrivate *priv; int ifindex; @@ -1126,7 +1129,8 @@ deactivate_cleanup (NMModem *self, NMDevice *device) if (priv->ppp_manager) { g_signal_handlers_disconnect_by_data (priv->ppp_manager, self); - nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL); + if (stop_ppp_manager) + nm_ppp_manager_stop (priv->ppp_manager, NULL, NULL, NULL); g_clear_object (&priv->ppp_manager); } @@ -1157,189 +1161,109 @@ deactivate_cleanup (NMModem *self, NMDevice *device) /*****************************************************************************/ -typedef enum { - DEACTIVATE_CONTEXT_STEP_FIRST, - DEACTIVATE_CONTEXT_STEP_CLEANUP, - DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP, - DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT, - DEACTIVATE_CONTEXT_STEP_LAST -} DeactivateContextStep; - typedef struct { NMModem *self; NMDevice *device; GCancellable *cancellable; - GSimpleAsyncResult *result; - DeactivateContextStep step; - NMPPPManager *ppp_manager; - NMPPPManagerStopHandle *ppp_stop_handle; - gulong ppp_stop_cancellable_id; + NMModemDeactivateCallback callback; + gpointer callback_user_data; } DeactivateContext; static void -deactivate_context_complete (DeactivateContext *ctx) +deactivate_context_complete (DeactivateContext *ctx, GError *error) { - if (ctx->ppp_stop_handle) - nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); + NMModem *self = ctx->self; - nm_assert (!ctx->ppp_stop_handle); - nm_assert (ctx->ppp_stop_cancellable_id == 0); + _LOGD ("modem deactivation finished %s%s%s", + NM_PRINT_FMT_QUOTED (error, "with failure: ", error->message, "", "successfully")); - if (ctx->ppp_manager) - g_object_unref (ctx->ppp_manager); - if (ctx->cancellable) - g_object_unref (ctx->cancellable); - g_simple_async_result_complete_in_idle (ctx->result); - g_object_unref (ctx->result); + if (ctx->callback) + ctx->callback (ctx->self, error, ctx->callback_user_data); + nm_g_object_unref (ctx->cancellable); g_object_unref (ctx->device); g_object_unref (ctx->self); g_slice_free (DeactivateContext, ctx); } -gboolean -nm_modem_deactivate_async_finish (NMModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void deactivate_step (DeactivateContext *ctx); - static void -disconnect_ready (NMModem *self, - GAsyncResult *res, - DeactivateContext *ctx) +_deactivate_call_disconnect_cb (NMModem *self, + GError *error, + gpointer user_data) { - GError *error = NULL; - - if (!NM_MODEM_GET_CLASS (self)->disconnect_finish (self, res, &error)) { - g_simple_async_result_take_error (ctx->result, error); - deactivate_context_complete (ctx); - return; - } - - /* Go on */ - ctx->step++; - deactivate_step (ctx); + deactivate_context_complete (user_data, error); } static void -ppp_manager_stop_ready (NMPPPManager *ppp_manager, - NMPPPManagerStopHandle *handle, - gboolean was_cancelled, - gpointer user_data) +_deactivate_call_disconnect (DeactivateContext *ctx) { - DeactivateContext *ctx = user_data; - - nm_assert (ctx->ppp_stop_handle == handle); - ctx->ppp_stop_handle = NULL; - - if (ctx->ppp_stop_cancellable_id) { - g_cancellable_disconnect (ctx->cancellable, - nm_steal_int (&ctx->ppp_stop_cancellable_id)); - } - - if (was_cancelled) - return; - - ctx->step++; - deactivate_step (ctx); + NM_MODEM_GET_CLASS (ctx->self)->disconnect (ctx->self, + FALSE, + ctx->cancellable, + _deactivate_call_disconnect_cb, + ctx); } static void -ppp_manager_stop_cancelled (GCancellable *cancellable, - gpointer user_data) +_deactivate_ppp_manager_stop_cb (NMPPPManager *ppp_manager, + NMPPPManagerStopHandle *handle, + gboolean was_cancelled, + gpointer user_data) { DeactivateContext *ctx = user_data; - nm_ppp_manager_stop_cancel (ctx->ppp_stop_handle); -} + g_object_unref (ppp_manager); -static void -deactivate_step (DeactivateContext *ctx) -{ - NMModem *self = ctx->self; - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); - GError *error = NULL; - - /* Check cancellable in each step */ - if (g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) { - g_simple_async_result_take_error (ctx->result, error); - deactivate_context_complete (ctx); - return; - } - - switch (ctx->step) { - case DEACTIVATE_CONTEXT_STEP_FIRST: - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_CLEANUP: - /* Make sure we keep a ref to the PPP manager if there is one */ - if (priv->ppp_manager) - ctx->ppp_manager = g_object_ref (priv->ppp_manager); - /* Run cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device); - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_PPP_MANAGER_STOP: - /* If we have a PPP manager, stop it */ - if (ctx->ppp_manager) { - nm_assert (!ctx->ppp_stop_handle); - if (ctx->cancellable) { - ctx->ppp_stop_cancellable_id = g_cancellable_connect (ctx->cancellable, - G_CALLBACK (ppp_manager_stop_cancelled), - ctx, - NULL); - } - ctx->ppp_stop_handle = nm_ppp_manager_stop (ctx->ppp_manager, - ppp_manager_stop_ready, - ctx); - return; - } - ctx->step++; - /* fall through */ - case DEACTIVATE_CONTEXT_STEP_MM_DISCONNECT: - /* Disconnect asynchronously */ - NM_MODEM_GET_CLASS (self)->disconnect (self, - FALSE, - ctx->cancellable, - (GAsyncReadyCallback) disconnect_ready, - ctx); - return; + if (was_cancelled) { + gs_free_error GError *error = NULL; - case DEACTIVATE_CONTEXT_STEP_LAST: - _LOGD ("modem deactivation finished"); - deactivate_context_complete (ctx); + if (!g_cancellable_set_error_if_cancelled (ctx->cancellable, &error)) + nm_assert_not_reached (); + deactivate_context_complete (ctx, error); return; } - g_assert_not_reached (); + nm_assert (!g_cancellable_is_cancelled (ctx->cancellable)); + _deactivate_call_disconnect (ctx); } void nm_modem_deactivate_async (NMModem *self, NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMModemDeactivateCallback callback, gpointer user_data) { + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); DeactivateContext *ctx; + NMPPPManager *ppp_manager; + + g_return_if_fail (NM_IS_MODEM (self)); + g_return_if_fail (NM_IS_DEVICE (device)); + g_return_if_fail (G_IS_CANCELLABLE (cancellable)); - ctx = g_slice_new0 (DeactivateContext); + ctx = g_slice_new (DeactivateContext); ctx->self = g_object_ref (self); ctx->device = g_object_ref (device); - ctx->result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - nm_modem_deactivate_async); - /* FIXME(shutdown): we always require a cancellable, otherwise we cannot - * do a coordinated shutdown. */ - ctx->cancellable = nm_g_object_ref (cancellable); + ctx->cancellable = g_object_ref (cancellable); + ctx->callback = callback; + ctx->callback_user_data = user_data; + + ppp_manager = nm_g_object_ref (priv->ppp_manager); + + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, ctx->device, FALSE); + + if (ppp_manager) { + /* If we have a PPP manager, stop it. + * + * Pass on the reference in @ppp_manager. */ + nm_ppp_manager_stop (ppp_manager, + ctx->cancellable, + _deactivate_ppp_manager_stop_cb, + ctx); + return; + } - /* Start */ - ctx->step = DEACTIVATE_CONTEXT_STEP_FIRST; - deactivate_step (ctx); + _deactivate_call_disconnect (ctx); } /*****************************************************************************/ @@ -1348,7 +1272,7 @@ void nm_modem_deactivate (NMModem *self, NMDevice *device) { /* First cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device); + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, device, TRUE); /* Then disconnect without waiting */ NM_MODEM_GET_CLASS (self)->disconnect (self, FALSE, NULL, NULL, NULL); } @@ -1387,7 +1311,7 @@ nm_modem_device_state_changed (NMModem *self, if (new_state == NM_DEVICE_STATE_FAILED || new_state == NM_DEVICE_STATE_DISCONNECTED) warn = FALSE; /* First cleanup */ - NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL); + NM_MODEM_GET_CLASS (self)->deactivate_cleanup (self, NULL, TRUE); NM_MODEM_GET_CLASS (self)->disconnect (self, warn, NULL, NULL, NULL); } break; diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index c73745ce..f7b6bfe9 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -109,6 +109,10 @@ struct _NMModem { typedef struct _NMModem NMModem; +typedef void (*_NMModemDisconnectCallback) (NMModem *modem, + GError *error, + gpointer user_data); + typedef struct { GObjectClass parent; @@ -149,13 +153,12 @@ typedef struct { void (*disconnect) (NMModem *self, gboolean warn, GCancellable *cancellable, - GAsyncReadyCallback callback, + _NMModemDisconnectCallback callback, gpointer user_data); - gboolean (*disconnect_finish) (NMModem *self, - GAsyncResult *res, - GError **error); - void (*deactivate_cleanup) (NMModem *self, NMDevice *device); + void (*deactivate_cleanup) (NMModem *self, + NMDevice *device, + gboolean stop_ppp_manager); gboolean (*owns_port) (NMModem *self, const char *iface); } NMModemClass; @@ -236,14 +239,15 @@ void nm_modem_get_secrets (NMModem *modem, void nm_modem_deactivate (NMModem *modem, NMDevice *device); +typedef void (*NMModemDeactivateCallback) (NMModem *self, + GError *error, + gpointer user_data); + void nm_modem_deactivate_async (NMModem *self, NMDevice *device, GCancellable *cancellable, - GAsyncReadyCallback callback, + NMModemDeactivateCallback callback, gpointer user_data); -gboolean nm_modem_deactivate_async_finish (NMModem *self, - GAsyncResult *res, - GError **error); void nm_modem_device_state_changed (NMModem *modem, NMDeviceState new_state, diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index a561b58a..c9ee27ff 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -20,7 +20,6 @@ #include "nm-default.h" -#include <string.h> #include <gmodule.h> #include "devices/nm-device-factory.h" |