From 4b412218e64d57be64a61bdbdf47d1ea9ac7d007 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 1 Oct 2015 12:44:28 -0500 Subject: [PATCH 1/4] libnm/wwan: add GSM setting device-id, sim-id, and sim-operator-id properties These properties limit whether the connection applies to a certain WWAN modem based on the modem's device ID or SIM ID (as reported by the WWAN management service), or through the MCC/MNC ID of the operator that issued the SIM card. --- libnm-core/nm-setting-gsm.c | 171 ++++++++++++++++++ libnm-core/nm-setting-gsm.h | 44 +++-- libnm-core/tests/test-general.c | 30 +++ libnm/libnm.ver | 3 + src/devices/wwan/nm-device-modem.c | 10 + src/devices/wwan/nm-modem-broadband.c | 6 +- src/devices/wwan/nm-modem.c | 101 +++++++++++ src/devices/wwan/nm-modem.h | 40 ++-- .../tests/keyfiles/ATT_Data_Connect_Plain | 3 + .../plugins/keyfile/tests/test-keyfile.c | 171 ++++-------------- 10 files changed, 403 insertions(+), 176 deletions(-) diff --git a/libnm-core/nm-setting-gsm.c b/libnm-core/nm-setting-gsm.c index 4d5d7d43b3..f5e60e789f 100644 --- a/libnm-core/nm-setting-gsm.c +++ b/libnm-core/nm-setting-gsm.c @@ -50,6 +50,11 @@ typedef struct { char *password; NMSettingSecretFlags password_flags; + /* Restrict connection to certain devices or SIMs */ + char *device_id; + char *sim_id; + char *sim_operator_id; + char *apn; /* NULL for dynamic */ char *network_id; /* for manual registration or NULL for automatic */ @@ -70,6 +75,9 @@ enum { PROP_PIN, PROP_PIN_FLAGS, PROP_HOME_ONLY, + PROP_DEVICE_ID, + PROP_SIM_ID, + PROP_SIM_OPERATOR_ID, LAST_PROP }; @@ -213,6 +221,54 @@ nm_setting_gsm_get_home_only (NMSettingGsm *setting) return NM_SETTING_GSM_GET_PRIVATE (setting)->home_only; } +/** + * nm_setting_gsm_get_device_id: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:device-id property of the setting + * + * Since: 1.2 + **/ +const char * +nm_setting_gsm_get_device_id (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->device_id; +} + +/** + * nm_setting_gsm_get_sim_id: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:sim-id property of the setting + * + * Since: 1.2 + **/ +const char * +nm_setting_gsm_get_sim_id (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->sim_id; +} + +/** + * nm_setting_gsm_get_sim_operator_id: + * @setting: the #NMSettingGsm + * + * Returns: the #NMSettingGsm:sim-operator-id property of the setting + * + * Since: 1.2 + **/ +const char * +nm_setting_gsm_get_sim_operator_id (NMSettingGsm *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_GSM (setting), NULL); + + return NM_SETTING_GSM_GET_PRIVATE (setting)->sim_operator_id; +} + static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { @@ -312,6 +368,49 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (priv->device_id && !priv->device_id[0]) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_DEVICE_ID); + return FALSE; + } + + if (priv->sim_id && !priv->sim_id[0]) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_SIM_ID); + return FALSE; + } + + if (priv->sim_operator_id) { + size_t len = strlen (priv->sim_operator_id); + const char *p = priv->sim_operator_id; + + if (len == 0 || (len != 5 && len != 6)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property is empty or wrong size")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_SIM_OPERATOR_ID); + return FALSE; + } + + while (p && *p) { + if (!g_ascii_isdigit (*p++)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("property must contain only digits")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_GSM_SETTING_NAME, NM_SETTING_GSM_SIM_OPERATOR_ID); + return FALSE; + } + } + } + return TRUE; } @@ -401,6 +500,18 @@ set_property (GObject *object, guint prop_id, case PROP_HOME_ONLY: priv->home_only = g_value_get_boolean (value); break; + case PROP_DEVICE_ID: + g_free (priv->device_id); + priv->device_id = g_value_dup_string (value); + break; + case PROP_SIM_ID: + g_free (priv->sim_id); + priv->sim_id = g_value_dup_string (value); + break; + case PROP_SIM_OPERATOR_ID: + g_free (priv->sim_operator_id); + priv->sim_operator_id = g_value_dup_string (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -441,6 +552,15 @@ get_property (GObject *object, guint prop_id, case PROP_HOME_ONLY: g_value_set_boolean (value, nm_setting_gsm_get_home_only (setting)); break; + case PROP_DEVICE_ID: + g_value_set_string (value, nm_setting_gsm_get_device_id (setting)); + break; + case PROP_SIM_ID: + g_value_set_string (value, nm_setting_gsm_get_sim_id (setting)); + break; + case PROP_SIM_OPERATOR_ID: + g_value_set_string (value, nm_setting_gsm_get_sim_operator_id (setting)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -596,6 +716,57 @@ nm_setting_gsm_class_init (NMSettingGsmClass *setting_class) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NMSettingGsm:device-id: + * + * The device unique identifier (as given by the WWAN management service) + * which this connection applies to. If given, the connection will only + * apply to the specified device. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_DEVICE_ID, + g_param_spec_string (NM_SETTING_GSM_DEVICE_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:sim-id: + * + * The SIM card unique identifier (as given by the WWAN management service) + * which this connection applies to. If given, the connection will apply + * to any device also allowed by #NMSettingGsm:device-id which contains a + * SIM card matching the given identifier. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_SIM_ID, + g_param_spec_string (NM_SETTING_GSM_SIM_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSettingGsm:sim-operator-id: + * + * A MCC/MNC string like "310260" or "21601" identifying the specific + * mobile network operator which this connection applies to. If given, + * the connection will apply to any device also allowed by + * #NMSettingGsm:device-id and #NMSettingGsm:sim-id which contains a SIM + * card provisioined by the given operator. + * + * Since: 1.2 + **/ + g_object_class_install_property + (object_class, PROP_SIM_OPERATOR_ID, + g_param_spec_string (NM_SETTING_GSM_SIM_OPERATOR_ID, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /* Ignore incoming deprecated properties */ _nm_setting_class_add_dbus_only_property (parent_class, "allowed-bands", G_VARIANT_TYPE_UINT32, diff --git a/libnm-core/nm-setting-gsm.h b/libnm-core/nm-setting-gsm.h index 907567381a..2dfe73fae2 100644 --- a/libnm-core/nm-setting-gsm.h +++ b/libnm-core/nm-setting-gsm.h @@ -40,15 +40,18 @@ G_BEGIN_DECLS #define NM_SETTING_GSM_SETTING_NAME "gsm" -#define NM_SETTING_GSM_NUMBER "number" -#define NM_SETTING_GSM_USERNAME "username" -#define NM_SETTING_GSM_PASSWORD "password" -#define NM_SETTING_GSM_PASSWORD_FLAGS "password-flags" -#define NM_SETTING_GSM_APN "apn" -#define NM_SETTING_GSM_NETWORK_ID "network-id" -#define NM_SETTING_GSM_PIN "pin" -#define NM_SETTING_GSM_PIN_FLAGS "pin-flags" -#define NM_SETTING_GSM_HOME_ONLY "home-only" +#define NM_SETTING_GSM_NUMBER "number" +#define NM_SETTING_GSM_USERNAME "username" +#define NM_SETTING_GSM_PASSWORD "password" +#define NM_SETTING_GSM_PASSWORD_FLAGS "password-flags" +#define NM_SETTING_GSM_APN "apn" +#define NM_SETTING_GSM_NETWORK_ID "network-id" +#define NM_SETTING_GSM_PIN "pin" +#define NM_SETTING_GSM_PIN_FLAGS "pin-flags" +#define NM_SETTING_GSM_HOME_ONLY "home-only" +#define NM_SETTING_GSM_DEVICE_ID "device-id" +#define NM_SETTING_GSM_SIM_ID "sim-id" +#define NM_SETTING_GSM_SIM_OPERATOR_ID "sim-operator-id" struct _NMSettingGsm { NMSetting parent; @@ -63,14 +66,21 @@ typedef struct { GType nm_setting_gsm_get_type (void); -NMSetting *nm_setting_gsm_new (void); -const char *nm_setting_gsm_get_number (NMSettingGsm *setting); -const char *nm_setting_gsm_get_username (NMSettingGsm *setting); -const char *nm_setting_gsm_get_password (NMSettingGsm *setting); -const char *nm_setting_gsm_get_apn (NMSettingGsm *setting); -const char *nm_setting_gsm_get_network_id (NMSettingGsm *setting); -const char *nm_setting_gsm_get_pin (NMSettingGsm *setting); -gboolean nm_setting_gsm_get_home_only (NMSettingGsm *setting); +NMSetting *nm_setting_gsm_new (void); +const char *nm_setting_gsm_get_number (NMSettingGsm *setting); +const char *nm_setting_gsm_get_username (NMSettingGsm *setting); +const char *nm_setting_gsm_get_password (NMSettingGsm *setting); +const char *nm_setting_gsm_get_apn (NMSettingGsm *setting); +const char *nm_setting_gsm_get_network_id (NMSettingGsm *setting); +const char *nm_setting_gsm_get_pin (NMSettingGsm *setting); +gboolean nm_setting_gsm_get_home_only (NMSettingGsm *setting); + +NM_AVAILABLE_IN_1_2 +const char *nm_setting_gsm_get_device_id (NMSettingGsm *setting); +NM_AVAILABLE_IN_1_2 +const char *nm_setting_gsm_get_sim_id (NMSettingGsm *setting); +NM_AVAILABLE_IN_1_2 +const char *nm_setting_gsm_get_sim_operator_id (NMSettingGsm *setting); NMSettingSecretFlags nm_setting_gsm_get_pin_flags (NMSettingGsm *setting); NMSettingSecretFlags nm_setting_gsm_get_password_flags (NMSettingGsm *setting); diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 5d95f26aed..2111e59574 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -756,6 +756,35 @@ test_setting_gsm_without_number (void) NM_CONNECTION_ERROR_INVALID_PROPERTY); } +static void +test_setting_gsm_sim_operator_id (void) +{ + gs_unref_object NMSettingGsm *s_gsm = NULL; + + s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); + g_assert (s_gsm); + + /* Valid */ + g_object_set (s_gsm, NM_SETTING_GSM_SIM_OPERATOR_ID, "12345", NULL); + nmtst_assert_setting_verifies (NM_SETTING (s_gsm)); + + g_object_set (s_gsm, NM_SETTING_GSM_SIM_OPERATOR_ID, "123456", NULL); + nmtst_assert_setting_verifies (NM_SETTING (s_gsm)); + + /* Invalid */ + g_object_set (s_gsm, NM_SETTING_GSM_SIM_OPERATOR_ID, "", NULL); + nmtst_assert_setting_verify_fails (NM_SETTING (s_gsm), NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY); + + g_object_set (s_gsm, NM_SETTING_GSM_SIM_OPERATOR_ID, " ", NULL); + nmtst_assert_setting_verify_fails (NM_SETTING (s_gsm), NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY); + + g_object_set (s_gsm, NM_SETTING_GSM_SIM_OPERATOR_ID, "abcdef", NULL); + nmtst_assert_setting_verify_fails (NM_SETTING (s_gsm), NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY); +} + static NMSettingWirelessSecurity * make_test_wsec_setting (const char *detail) { @@ -4861,6 +4890,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_gsm_apn_bad_chars", test_setting_gsm_apn_bad_chars); g_test_add_func ("/core/general/test_setting_gsm_apn_underscore", test_setting_gsm_apn_underscore); g_test_add_func ("/core/general/test_setting_gsm_without_number", test_setting_gsm_without_number); + g_test_add_func ("/core/general/test_setting_gsm_sim_operator_id", test_setting_gsm_sim_operator_id); g_test_add_func ("/core/general/test_setting_to_dbus_all", test_setting_to_dbus_all); g_test_add_func ("/core/general/test_setting_to_dbus_no_secrets", test_setting_to_dbus_no_secrets); g_test_add_func ("/core/general/test_setting_to_dbus_only_secrets", test_setting_to_dbus_only_secrets); diff --git a/libnm/libnm.ver b/libnm/libnm.ver index eff17b3dae..0a73b27233 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -881,6 +881,9 @@ global: nm_setting_connection_get_lldp; nm_setting_connection_get_metered; nm_setting_connection_lldp_get_type; + nm_setting_gsm_get_device_id; + nm_setting_gsm_get_sim_id; + nm_setting_gsm_get_sim_operator_id; nm_setting_ip4_config_get_dhcp_timeout; nm_setting_ip6_config_addr_gen_mode_get_type; nm_setting_ip6_config_get_addr_gen_mode; diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 3a85d58fa9..04d869052d 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -253,6 +253,12 @@ data_port_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) nm_device_ipv6_sysctl_set (self, "disable_ipv6", "1"); } +static void +ids_changed_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data) +{ + nm_device_recheck_available_connections (NM_DEVICE (user_data)); +} + static void modem_state_cb (NMModem *modem, NMModemState new_state, @@ -677,6 +683,10 @@ set_modem (NMDeviceModem *self, NMModem *modem) * while in the new ModemManager the data port is set afterwards when the bearer gets * created */ g_signal_connect (modem, "notify::" NM_MODEM_DATA_PORT, G_CALLBACK (data_port_changed_cb), self); + + g_signal_connect (modem, "notify::" NM_MODEM_DEVICE_ID, G_CALLBACK (ids_changed_cb), self); + g_signal_connect (modem, "notify::" NM_MODEM_SIM_ID, G_CALLBACK (ids_changed_cb), self); + g_signal_connect (modem, "notify::" NM_MODEM_SIM_OPERATOR_ID, G_CALLBACK (ids_changed_cb), self); } static void diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 2236559477..51e396537f 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -1126,6 +1126,7 @@ get_sim_ready (MMModem *modem, if (new_sim) { g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, mm_sim_get_identifier (new_sim), + NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (new_sim), NULL); g_object_unref (new_sim); } else { @@ -1150,7 +1151,10 @@ sim_changed (MMModem *modem, GParamSpec *pspec, gpointer user_data) (GAsyncReadyCallback) get_sim_ready, g_object_ref (self)); } else - g_object_set (G_OBJECT (self), NM_MODEM_SIM_ID, NULL, NULL); + g_object_set (G_OBJECT (self), + NM_MODEM_SIM_ID, NULL, + NM_MODEM_SIM_OPERATOR_ID, NULL, + NULL); } static void diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 5889aa7805..86a0281b01 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -22,7 +22,9 @@ #include "config.h" #include + #include "nm-modem.h" +#include "nm-core-internal.h" #include "nm-platform.h" #include "nm-setting-connection.h" #include "nm-default.h" @@ -50,6 +52,7 @@ enum { PROP_DEVICE_ID, PROP_SIM_ID, PROP_IP_TYPES, + PROP_SIM_OPERATOR_ID, LAST_PROP }; @@ -69,6 +72,7 @@ typedef struct { char *device_id; char *sim_id; NMModemIPType ip_types; + char *sim_operator_id; NMPPPManager *ppp_manager; @@ -348,6 +352,24 @@ nm_modem_get_connection_ip_type (NMModem *self, return NULL; } +const char * +nm_modem_get_device_id (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->device_id; +} + +const char * +nm_modem_get_sim_id (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->sim_id; +} + +const char * +nm_modem_get_sim_operator_id (NMModem *self) +{ + return NM_MODEM_GET_PRIVATE (self)->sim_operator_id; +} + /*****************************************************************************/ /* IP method PPP */ @@ -844,6 +866,67 @@ nm_modem_act_stage2_config (NMModem *self, gboolean nm_modem_check_connection_compatible (NMModem *self, NMConnection *connection) { + NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + g_assert (s_con); + + if (g_str_equal (nm_setting_connection_get_connection_type (s_con), + NM_SETTING_GSM_SETTING_NAME)) { + NMSettingGsm *s_gsm; + const char *str; + + s_gsm = nm_connection_get_setting_gsm (connection); + if (!s_gsm) + return FALSE; + + str = nm_setting_gsm_get_device_id (s_gsm); + if (str) { + if (!priv->device_id) { + nm_log_dbg (LOGD_MB, "(%s): %s/%s has device-id, device does not", + priv->uid, + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); + return FALSE; + } + if (strcmp (str, priv->device_id)) { + nm_log_dbg (LOGD_MB, "(%s): %s/%s device-id mismatch", + priv->uid, + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); + return FALSE; + } + } + + /* SIM properties may not be available before the SIM is unlocked, so + * to ensure that autoconnect works, the connection's SIM properties + * are only compared if present on the device. + */ + + str = nm_setting_gsm_get_sim_id (s_gsm); + if (str && priv->sim_id) { + if (strcmp (str, priv->sim_id)) { + nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-id mismatch", + priv->uid, + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); + return FALSE; + } + } + + str = nm_setting_gsm_get_sim_operator_id (s_gsm); + if (str && priv->sim_operator_id) { + if (strcmp (str, priv->sim_operator_id)) { + nm_log_dbg (LOGD_MB, "(%s): %s/%s sim-operator-id mismatch", + priv->uid, + nm_connection_get_uuid (connection), + nm_connection_get_id (connection)); + return FALSE; + } + } + } + if (NM_MODEM_GET_CLASS (self)->check_connection_compatible) return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection); return FALSE; @@ -1297,6 +1380,9 @@ get_property (GObject *object, guint prop_id, case PROP_IP_TYPES: g_value_set_uint (value, priv->ip_types); break; + case PROP_SIM_OPERATOR_ID: + g_value_set_string (value, priv->sim_operator_id); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1308,6 +1394,7 @@ set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object); + const char *s; switch (prop_id) { case PROP_PATH: @@ -1351,6 +1438,12 @@ set_property (GObject *object, guint prop_id, case PROP_IP_TYPES: priv->ip_types = g_value_get_uint (value); break; + case PROP_SIM_OPERATOR_ID: + g_clear_pointer (&priv->sim_operator_id, g_free); + s = g_value_get_string (value); + if (s && s[0]) + priv->sim_operator_id = g_strdup (s); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1382,6 +1475,7 @@ finalize (GObject *object) g_free (priv->data_port); g_free (priv->device_id); g_free (priv->sim_id); + g_free (priv->sim_operator_id); G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object); } @@ -1496,6 +1590,13 @@ nm_modem_class_init (NMModemClass *klass) 0, G_MAXUINT32, NM_MODEM_IP_TYPE_IPV4, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property + (object_class, PROP_SIM_OPERATOR_ID, + g_param_spec_string (NM_MODEM_SIM_OPERATOR_ID, "", "", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + /* Signals */ signals[PPP_STATS] = diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index fffbeb3213..60e5589b55 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -36,18 +36,19 @@ G_BEGIN_DECLS #define NM_MODEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MODEM, NMModemClass)) /* Properties */ -#define NM_MODEM_UID "uid" -#define NM_MODEM_PATH "path" -#define NM_MODEM_DRIVER "driver" -#define NM_MODEM_CONTROL_PORT "control-port" -#define NM_MODEM_DATA_PORT "data-port" -#define NM_MODEM_IP4_METHOD "ip4-method" -#define NM_MODEM_IP6_METHOD "ip6-method" -#define NM_MODEM_IP_TIMEOUT "ip-timeout" -#define NM_MODEM_STATE "state" -#define NM_MODEM_DEVICE_ID "device-id" -#define NM_MODEM_SIM_ID "sim-id" -#define NM_MODEM_IP_TYPES "ip-types" /* Supported IP types */ +#define NM_MODEM_UID "uid" +#define NM_MODEM_PATH "path" +#define NM_MODEM_DRIVER "driver" +#define NM_MODEM_CONTROL_PORT "control-port" +#define NM_MODEM_DATA_PORT "data-port" +#define NM_MODEM_IP4_METHOD "ip4-method" +#define NM_MODEM_IP6_METHOD "ip6-method" +#define NM_MODEM_IP_TIMEOUT "ip-timeout" +#define NM_MODEM_STATE "state" +#define NM_MODEM_DEVICE_ID "device-id" +#define NM_MODEM_SIM_ID "sim-id" +#define NM_MODEM_IP_TYPES "ip-types" /* Supported IP types */ +#define NM_MODEM_SIM_OPERATOR_ID "sim-operator-id" /* Signals */ #define NM_MODEM_PPP_STATS "ppp-stats" @@ -179,12 +180,15 @@ typedef struct { GType nm_modem_get_type (void); -const char *nm_modem_get_path (NMModem *modem); -const char *nm_modem_get_uid (NMModem *modem); -const char *nm_modem_get_control_port (NMModem *modem); -const char *nm_modem_get_data_port (NMModem *modem); -const char *nm_modem_get_driver (NMModem *modem); -gboolean nm_modem_get_iid (NMModem *modem, NMUtilsIPv6IfaceId *out_iid); +const char *nm_modem_get_path (NMModem *modem); +const char *nm_modem_get_uid (NMModem *modem); +const char *nm_modem_get_control_port (NMModem *modem); +const char *nm_modem_get_data_port (NMModem *modem); +const char *nm_modem_get_driver (NMModem *modem); +const char *nm_modem_get_device_id (NMModem *modem); +const char *nm_modem_get_sim_id (NMModem *modem); +const char *nm_modem_get_sim_operator_id (NMModem *modem); +gboolean nm_modem_get_iid (NMModem *modem, NMUtilsIPv6IfaceId *out_iid); gboolean nm_modem_owns_port (NMModem *modem, const char *iface); diff --git a/src/settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain b/src/settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain index 902b842797..11e1ec6f63 100644 --- a/src/settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain +++ b/src/settings/plugins/keyfile/tests/keyfiles/ATT_Data_Connect_Plain @@ -15,6 +15,9 @@ password=CINGULAR1 apn=ISP.CINGULAR network-id=24005 pin=2345 +device-id=da812de91eec16620b06cd0ca5cbc7ea25245222 +sim-id=89148000000060671234 +sim-operator-id=310260 [serial] baud=115200 diff --git a/src/settings/plugins/keyfile/tests/test-keyfile.c b/src/settings/plugins/keyfile/tests/test-keyfile.c index ede40ee1cc..250bad36aa 100644 --- a/src/settings/plugins/keyfile/tests/test-keyfile.c +++ b/src/settings/plugins/keyfile/tests/test-keyfile.c @@ -1882,153 +1882,43 @@ test_read_gsm_connection (void) NMSettingConnection *s_con; NMSettingSerial *s_serial; NMSettingGsm *s_gsm; - NMSettingBluetooth *s_bluetooth; GError *error = NULL; - const char *tmp; - NMSettingSerialParity parity; - const char *expected_id = "AT&T Data Connect"; - const char *expected_apn = "ISP.CINGULAR"; - const char *expected_username = "ISP@CINGULARGPRS.COM"; - const char *expected_password = "CINGULAR1"; - const char *expected_network_id = "24005"; - const char *expected_pin = "2345"; + gboolean success; - connection = nm_keyfile_plugin_connection_from_file (TEST_GSM_FILE, NULL); - ASSERT (connection != NULL, - "connection-read", "failed to read %s", TEST_GSM_FILE); + connection = nm_keyfile_plugin_connection_from_file (TEST_GSM_FILE, &error); + g_assert_no_error (error); + g_assert (connection); - ASSERT (nm_connection_verify (connection, &error), - "connection-verify", "failed to verify %s: %s", TEST_GSM_FILE, error->message); + success = nm_connection_verify (connection, &error); + g_assert_no_error (error); + g_assert (success); /* ===== CONNECTION SETTING ===== */ - s_con = nm_connection_get_setting_connection (connection); - ASSERT (s_con != NULL, - "connection-verify-connection", "failed to verify %s: missing %s setting", - TEST_GSM_FILE, - NM_SETTING_CONNECTION_SETTING_NAME); - - /* ID */ - tmp = nm_setting_connection_get_id (s_con); - ASSERT (tmp != NULL, - "connection-verify-connection", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_ID); - ASSERT (strcmp (tmp, expected_id) == 0, - "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_ID); - - tmp = nm_setting_connection_get_connection_type (s_con); - ASSERT (tmp != NULL, - "connection-verify-connection", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_ID); - ASSERT (strcmp (tmp, NM_SETTING_GSM_SETTING_NAME) == 0, - "connection-verify-connection", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_CONNECTION_SETTING_NAME, - NM_SETTING_CONNECTION_TYPE); + g_assert (s_con); + g_assert_cmpstr (nm_setting_connection_get_id (s_con), ==, "AT&T Data Connect"); + g_assert_cmpstr (nm_setting_connection_get_connection_type (s_con), ==, NM_SETTING_GSM_SETTING_NAME); /* ===== BLUETOOTH SETTING ===== */ - /* Plain GSM, so no BT setting expected */ - s_bluetooth = nm_connection_get_setting_bluetooth (connection); - ASSERT (s_bluetooth == NULL, - "connection-verify-bt", "unexpected %s setting", - TEST_GSM_FILE, - NM_SETTING_BLUETOOTH_SETTING_NAME); + g_assert (nm_connection_get_setting_bluetooth (connection) == NULL); /* ===== GSM SETTING ===== */ - s_gsm = nm_connection_get_setting_gsm (connection); - ASSERT (s_gsm != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s setting", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME); - - /* APN */ - tmp = nm_setting_gsm_get_apn (s_gsm); - ASSERT (tmp != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_APN); - ASSERT (strcmp (tmp, expected_apn) == 0, - "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_APN); - - /* Username */ - tmp = nm_setting_gsm_get_username (s_gsm); - ASSERT (tmp != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_USERNAME); - ASSERT (strcmp (tmp, expected_username) == 0, - "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_USERNAME); - - /* Password */ - tmp = nm_setting_gsm_get_password (s_gsm); - ASSERT (tmp != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_PASSWORD); - ASSERT (strcmp (tmp, expected_password) == 0, - "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_PASSWORD); - - /* Network ID */ - tmp = nm_setting_gsm_get_network_id (s_gsm); - ASSERT (tmp != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_NETWORK_ID); - ASSERT (strcmp (tmp, expected_network_id) == 0, - "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_NETWORK_ID); - - /* PIN */ - tmp = nm_setting_gsm_get_pin (s_gsm); - ASSERT (tmp != NULL, - "connection-verify-gsm", "failed to verify %s: missing %s / %s key", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_PIN); - ASSERT (strcmp (tmp, expected_pin) == 0, - "connection-verify-gsm", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_GSM_SETTING_NAME, - NM_SETTING_GSM_PIN); + g_assert (s_gsm); + g_assert_cmpstr (nm_setting_gsm_get_apn (s_gsm), ==, "ISP.CINGULAR"); + g_assert_cmpstr (nm_setting_gsm_get_username (s_gsm), ==, "ISP@CINGULARGPRS.COM"); + g_assert_cmpstr (nm_setting_gsm_get_password (s_gsm), ==, "CINGULAR1"); + g_assert_cmpstr (nm_setting_gsm_get_network_id (s_gsm), ==, "24005"); + g_assert_cmpstr (nm_setting_gsm_get_pin (s_gsm), ==, "2345"); + g_assert_cmpstr (nm_setting_gsm_get_device_id (s_gsm), ==, "da812de91eec16620b06cd0ca5cbc7ea25245222"); + g_assert_cmpstr (nm_setting_gsm_get_sim_id (s_gsm), ==, "89148000000060671234"); + g_assert_cmpstr (nm_setting_gsm_get_sim_operator_id (s_gsm), ==, "310260"); /* ===== SERIAL SETTING ===== */ - s_serial = nm_connection_get_setting_serial (connection); - ASSERT (s_serial != NULL, - "connection-verify-serial", "failed to verify %s: missing %s setting", - TEST_GSM_FILE, - NM_SETTING_SERIAL_SETTING_NAME); - - parity = nm_setting_serial_get_parity (s_serial); - ASSERT (parity == NM_SETTING_SERIAL_PARITY_ODD, - "connection-verify-serial", "failed to verify %s: unexpected %s / %s key value", - TEST_GSM_FILE, - NM_SETTING_SERIAL_SETTING_NAME, - NM_SETTING_SERIAL_PARITY); + g_assert (s_serial); + g_assert_cmpint (nm_setting_serial_get_parity (s_serial), ==, NM_SETTING_SERIAL_PARITY_ODD); g_object_unref (connection); } @@ -2087,22 +1977,23 @@ test_write_gsm_connection (void) NM_SETTING_GSM_PIN, "123456", NM_SETTING_GSM_NETWORK_ID, "254098", NM_SETTING_GSM_HOME_ONLY, TRUE, + NM_SETTING_GSM_DEVICE_ID, "da812de91eec16620b06cd0ca5cbc7ea25245222", + NM_SETTING_GSM_SIM_ID, "89148000000060671234", + NM_SETTING_GSM_SIM_OPERATOR_ID, "310260", NULL); /* Write out the connection */ owner_uid = geteuid (); owner_grp = getegid (); success = nm_keyfile_plugin_write_test_connection (connection, TEST_SCRATCH_DIR, owner_uid, owner_grp, &testfile, &error); - ASSERT (success == TRUE, - "connection-write", "failed to write keyfile: %s", - error ? error->message : "(none)"); - - ASSERT (testfile != NULL, - "connection-write", "didn't get keyfile name back after writing connection"); + g_assert_no_error (error); + g_assert (success); + g_assert (testfile != NULL); /* Read the connection back in and compare it to the one we just wrote out */ - reread = nm_keyfile_plugin_connection_from_file (testfile, NULL); - ASSERT (reread != NULL, "connection-write", "failed to re-read test connection"); + reread = nm_keyfile_plugin_connection_from_file (testfile, &error); + g_assert_no_error (error); + g_assert (reread); nmtst_assert_connection_equals (connection, TRUE, reread, FALSE); From 06442276c93733b1e6c271d7e34c615cd135331f Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 20 Oct 2015 18:35:16 -0500 Subject: [PATCH 2/4] cli: add support for GSM setting device-id, sim-id, and sim-operator-id properties --- clients/cli/settings.c | 61 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/clients/cli/settings.c b/clients/cli/settings.c index 4e63e24e67..88704155da 100644 --- a/clients/cli/settings.c +++ b/clients/cli/settings.c @@ -460,6 +460,9 @@ NmcOutputField nmc_fields_setting_gsm[] = { SETTING_FIELD (NM_SETTING_GSM_PIN), /* 7 */ SETTING_FIELD (NM_SETTING_GSM_PIN_FLAGS), /* 8 */ SETTING_FIELD (NM_SETTING_GSM_HOME_ONLY), /* 9 */ + SETTING_FIELD (NM_SETTING_GSM_DEVICE_ID), /* 10 */ + SETTING_FIELD (NM_SETTING_GSM_SIM_ID), /* 11 */ + SETTING_FIELD (NM_SETTING_GSM_SIM_OPERATOR_ID), /* 12 */ {NULL, NULL, 0, NULL, FALSE, FALSE, 0} }; #define NMC_FIELDS_SETTING_GSM_ALL "name"","\ @@ -471,7 +474,10 @@ NmcOutputField nmc_fields_setting_gsm[] = { NM_SETTING_GSM_NETWORK_ID","\ NM_SETTING_GSM_PIN","\ NM_SETTING_GSM_PIN_FLAGS","\ - NM_SETTING_GSM_HOME_ONLY + NM_SETTING_GSM_HOME_ONLY","\ + NM_SETTING_GSM_DEVICE_ID","\ + NM_SETTING_GSM_SIM_ID","\ + NM_SETTING_GSM_SIM_OPERATOR_ID #define NMC_FIELDS_SETTING_GSM_COMMON NMC_FIELDS_SETTING_GSM_ALL /* Available fields for NM_SETTING_CDMA_SETTING_NAME */ @@ -1268,6 +1274,9 @@ DEFINE_GETTER (nmc_property_gsm_get_network_id, NM_SETTING_GSM_NETWORK_ID) DEFINE_GETTER (nmc_property_gsm_get_pin, NM_SETTING_GSM_PIN) DEFINE_SECRET_FLAGS_GETTER (nmc_property_gsm_get_pin_flags, NM_SETTING_GSM_PIN_FLAGS) DEFINE_GETTER (nmc_property_gsm_get_home_only, NM_SETTING_GSM_HOME_ONLY) +DEFINE_GETTER (nmc_property_gsm_get_device_id, NM_SETTING_GSM_DEVICE_ID) +DEFINE_GETTER (nmc_property_gsm_get_sim_id, NM_SETTING_GSM_SIM_ID) +DEFINE_GETTER (nmc_property_gsm_get_sim_operator_id, NM_SETTING_GSM_SIM_OPERATOR_ID) /* --- NM_SETTING_INFINIBAND_SETTING_NAME property get functions --- */ DEFINE_GETTER (nmc_property_ib_get_mac_address, NM_SETTING_INFINIBAND_MAC_ADDRESS) @@ -5196,6 +5205,32 @@ nmc_property_dcb_set_app_fcoe_mode (NMSetting *setting, const char *prop, const DEFINE_ALLOWED_VAL_FUNC (nmc_property_dcb_allowed_app_fcoe_modes, _dcb_valid_fcoe_modes) +static gboolean +nmc_property_gsm_set_sim_operator_id (NMSetting *setting, const char *prop, const char *val, GError **error) +{ + const char *p = val; + + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (strlen (val) != 5 && strlen (val) != 6) { + g_set_error_literal (error, 1, 0, _("SIM operator ID must be a 5 or 6 number MCCMNC code")); + return FALSE; + } + + while (p && *p) { + if (!g_ascii_isdigit (*p++)) { + g_set_error_literal (error, 1, 0, _("SIM operator ID must be a 5 or 6 number MCCMNC code")); + return FALSE; + } + } + + g_object_set (G_OBJECT (setting), + NM_SETTING_GSM_SIM_OPERATOR_ID, + val, + NULL); + return TRUE; +} + /*----------------------------------------------------------------------------*/ static inline void @@ -5944,6 +5979,27 @@ nmc_properties_init (void) NULL, NULL, NULL); + nmc_add_prop_funcs (GLUE (GSM, DEVICE_ID), + nmc_property_gsm_get_device_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, SIM_ID), + nmc_property_gsm_get_sim_id, + nmc_property_set_string, + NULL, + NULL, + NULL, + NULL); + nmc_add_prop_funcs (GLUE (GSM, SIM_OPERATOR_ID), + nmc_property_gsm_get_sim_operator_id, + nmc_property_gsm_set_sim_operator_id, + NULL, + NULL, + NULL, + NULL); /* Add editable properties for NM_SETTING_INFINIBAND_SETTING_NAME */ nmc_add_prop_funcs (GLUE (INFINIBAND, MAC_ADDRESS), @@ -7593,6 +7649,9 @@ setting_gsm_details (NMSetting *setting, NmCli *nmc, const char *one_prop, gboo set_val_str (arr, 7, GET_SECRET (secrets, setting, nmc_property_gsm_get_pin)); set_val_str (arr, 8, nmc_property_gsm_get_pin_flags (setting, NMC_PROPERTY_GET_PRETTY)); set_val_str (arr, 9, nmc_property_gsm_get_home_only (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 10, nmc_property_gsm_get_device_id (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 11, nmc_property_gsm_get_sim_id (setting, NMC_PROPERTY_GET_PRETTY)); + set_val_str (arr, 12, nmc_property_gsm_get_sim_operator_id (setting, NMC_PROPERTY_GET_PRETTY)); g_ptr_array_add (nmc->output_data, arr); print_data (nmc); /* Print all data */ From d9c6b9f3dd8463dc636115c20e2d84a1a07b4fbe Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 Oct 2015 13:42:42 -0500 Subject: [PATCH 3/4] core: only run availability recheck transition if required Device subclasses can call nm_device_recheck_available() at any time, and the function would change the device's state to UNKNOWN in cases where the device was available already. For WWAN devices, availability is rechecked every time the modem state changes, resulting in: NetworkManager[28919]: (ttyUSB4): modem state changed, 'disabled' --> 'enabling' (reason: user-requested) NetworkManager[28919]: [1445538582.116727] [devices/nm-device.c:2769] recheck_available(): [0x23bd710] (ttyUSB4): device is available, will transition to unknown NetworkManager[28919]: (ttyUSB4): modem state changed, 'enabling' --> 'searching' (reason: user-requested) NetworkManager[28919]: [1445538582.776317] [devices/nm-device.c:2769] recheck_available(): [0x23bd710] (ttyUSB4): device is available, will transition to unknown --- src/devices/nm-device.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 1d28dd4835..0f7f25793e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2771,13 +2771,17 @@ recheck_available (gpointer user_data) new_state = NM_DEVICE_STATE_UNAVAILABLE; nm_device_queue_state (self, new_state, priv->recheck_available.unavailable_reason); } - _LOGD (LOGD_DEVICE, "device is %savailable, %s %s", - now_available ? "" : "not ", - new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to", - state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state)); - priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE; - priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE; + if (new_state > NM_DEVICE_STATE_UNKNOWN) { + _LOGD (LOGD_DEVICE, "device is %savailable, %s %s", + now_available ? "" : "not ", + new_state == NM_DEVICE_STATE_UNAVAILABLE ? "no change required for" : "will transition to", + state_to_string (new_state == NM_DEVICE_STATE_UNAVAILABLE ? state : new_state)); + + priv->recheck_available.available_reason = NM_DEVICE_STATE_REASON_NONE; + priv->recheck_available.unavailable_reason = NM_DEVICE_STATE_REASON_NONE; + } + return G_SOURCE_REMOVE; } From cb751012a2f4b8ef236eab2a7c65687c99205806 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 22 Oct 2015 11:08:40 -0500 Subject: [PATCH 4/4] wwan: rework connection flow to send PIN earlier and fix autoconnect Modems often don't expose all the required properties until they have been unlocked, and that includes the IP types supported by the modem. With an autoconnect WWAN connection where the SIM requires a PIN, there were two problems: 1) the PIN is a secret and we don't have it until it's explicitly requested during the activation process, so we cannot gate GSM connection availability on whether a PIN is present since this happens long before we request secrets 2) when the modem is locked it may not report the supported IP types, which caused an auto-activation to fail early becuase IP compatibility is checked before the PIN is sent to the modem Rework connection activation flow into a series of concrete steps, where the PIN is sent to the modem if required, and only after the modem is actually unlocked does the connection proceed. This does mean that any connection marked 'autoconnect' can theoretically enable a PIN-locked modem even if the connection has no PIN defined, but there's no good way around that. NetworkManager would activate the connection --- src/devices/wwan/nm-device-modem.c | 5 +- src/devices/wwan/nm-modem-broadband.c | 342 +++++++++++++++++--------- 2 files changed, 233 insertions(+), 114 deletions(-) diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 04d869052d..6fc827e00b 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -415,10 +415,7 @@ check_connection_available (NMDevice *device, return FALSE; if (state == NM_MODEM_STATE_LOCKED) { - NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (connection); - - /* Can't use a connection without a PIN if the modem is locked */ - if (!s_gsm || !nm_setting_gsm_get_pin (s_gsm)) + if (!nm_connection_get_setting_gsm (connection)) return FALSE; } diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 51e396537f..427f3ed695 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -33,14 +33,38 @@ G_DEFINE_TYPE (NMModemBroadband, nm_modem_broadband, NM_TYPE_MODEM) +typedef enum { + CONNECT_STEP_FIRST, + CONNECT_STEP_WAIT_FOR_SIM, + CONNECT_STEP_UNLOCK, + CONNECT_STEP_WAIT_FOR_READY, + CONNECT_STEP_CONNECT, + CONNECT_STEP_LAST +} ConnectStep; + +typedef struct { + NMModemBroadband *self; + ConnectStep step; + + MMModemCapability caps; + NMConnection *connection; + GCancellable *cancellable; + MMSimpleConnectProperties *connect_properties; + GArray *ip_types; + guint ip_types_i; + GError *first_error; +} ConnectContext; + struct _NMModemBroadbandPrivate { /* The modem object from dbus */ MMObject *modem_object; /* Per-interface objects */ MMModem *modem_iface; MMModemSimple *simple_iface; + MMSim *sim_iface; /* Connection setup */ + ConnectContext *ctx; MMBearer *bearer; MMBearerIpConfig *ipv4_config; @@ -256,45 +280,45 @@ create_gsm_connect_properties (NMConnection *connection) return properties; } -typedef struct { - NMModemBroadband *self; - MMModemCapability caps; - MMSimpleConnectProperties *connect_properties; - GArray *ip_types; - guint ip_types_i; - GError *first_error; -} ActStageContext; - static void -act_stage_context_free (ActStageContext *ctx) +connect_context_clear (NMModemBroadband *self) { - g_clear_error (&ctx->first_error); - g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref); - g_clear_object (&ctx->connect_properties); - g_object_unref (ctx->self); - g_slice_free (ActStageContext, ctx); + if (self->priv->ctx) { + ConnectContext *ctx = self->priv->ctx; + + g_clear_error (&ctx->first_error); + g_clear_pointer (&ctx->ip_types, (GDestroyNotify) g_array_unref); + g_clear_object (&ctx->cancellable); + g_clear_object (&ctx->connection); + g_clear_object (&ctx->connect_properties); + g_clear_object (&ctx->self); + g_slice_free (ConnectContext, ctx); + self->priv->ctx = NULL; + } } -static void act_stage_context_step (ActStageContext *ctx); +static void connect_context_step (NMModemBroadband *self); static void connect_ready (MMModemSimple *simple_iface, GAsyncResult *res, - ActStageContext *ctx) + NMModemBroadband *self) { + ConnectContext *ctx = self->priv->ctx; GError *error = NULL; NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; - ctx->self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); - if (!ctx->self->priv->bearer) { + self->priv->bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); + if (!self->priv->bearer) { if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && - mm_modem_get_unlock_required (ctx->self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { - /* Request PIN */ - ask_for_pin (ctx->self); + mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { g_error_free (error); - act_stage_context_free (ctx); + + /* Request PIN */ + ask_for_pin (self); + connect_context_clear (self); return; } @@ -311,79 +335,190 @@ connect_ready (MMModemSimple *simple_iface, * retry with the next one, if any. */ ctx->ip_types_i++; - act_stage_context_step (ctx); + connect_context_clear (self); return; } /* Grab IP configurations */ - ctx->self->priv->ipv4_config = mm_bearer_get_ipv4_config (ctx->self->priv->bearer); - if (ctx->self->priv->ipv4_config) - ip4_method = get_bearer_ip_method (ctx->self->priv->ipv4_config); + self->priv->ipv4_config = mm_bearer_get_ipv4_config (self->priv->bearer); + if (self->priv->ipv4_config) + ip4_method = get_bearer_ip_method (self->priv->ipv4_config); - ctx->self->priv->ipv6_config = mm_bearer_get_ipv6_config (ctx->self->priv->bearer); - if (ctx->self->priv->ipv6_config) - ip6_method = get_bearer_ip_method (ctx->self->priv->ipv6_config); + self->priv->ipv6_config = mm_bearer_get_ipv6_config (self->priv->bearer); + if (self->priv->ipv6_config) + ip6_method = get_bearer_ip_method (self->priv->ipv6_config); if (ip4_method == NM_MODEM_IP_METHOD_UNKNOWN && ip6_method == NM_MODEM_IP_METHOD_UNKNOWN) { nm_log_warn (LOGD_MB, "(%s): failed to connect modem: invalid bearer IP configuration", - nm_modem_get_uid (NM_MODEM (ctx->self))); - g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED); - act_stage_context_free (ctx); + nm_modem_get_uid (NM_MODEM (self))); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_CONFIG_FAILED); + connect_context_clear (self); return; } - g_object_set (ctx->self, - NM_MODEM_DATA_PORT, mm_bearer_get_interface (ctx->self->priv->bearer), + g_object_set (self, + NM_MODEM_DATA_PORT, mm_bearer_get_interface (self->priv->bearer), NM_MODEM_IP4_METHOD, ip4_method, NM_MODEM_IP6_METHOD, ip6_method, - NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (ctx->self->priv->bearer), + NM_MODEM_IP_TIMEOUT, mm_bearer_get_ip_timeout (self->priv->bearer), NULL); - g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); - act_stage_context_free (ctx); + ctx->step++; + connect_context_step (self); } static void -act_stage_context_step (ActStageContext *ctx) +send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) { - if (ctx->ip_types_i < ctx->ip_types->len) { - NMModemIPType current; + GError *error = NULL; - current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i); - - if (current == NM_MODEM_IP_TYPE_IPV4) - mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4); - else if (current == NM_MODEM_IP_TYPE_IPV6) - mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6); - else if (current == NM_MODEM_IP_TYPE_IPV4V6) - mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6); - else - g_assert_not_reached (); - - nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'", - nm_modem_get_uid (NM_MODEM (ctx->self)), - nm_modem_ip_type_to_string (current)); - - mm_modem_simple_connect (ctx->self->priv->simple_iface, - ctx->connect_properties, - NULL, - (GAsyncReadyCallback)connect_ready, - ctx); + if (!mm_sim_send_pin_finish (sim, result, &error)) { + if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN) || + (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED) && + mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN)) { + ask_for_pin (self); + } else { + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (error)); + } + g_error_free (error); return; + } + + self->priv->ctx->step++; + connect_context_step (self); +} + +static void +connect_context_step (NMModemBroadband *self) +{ + ConnectContext *ctx = self->priv->ctx; + + switch (ctx->step) { + case CONNECT_STEP_FIRST: + ctx->step++; + /* fall through */ + + case CONNECT_STEP_WAIT_FOR_SIM: + if (MODEM_CAPS_3GPP (ctx->caps) && !self->priv->sim_iface) { + /* Have to wait for the SIM to show up */ + break; + } + ctx->step++; + /* fall through */ + + case CONNECT_STEP_UNLOCK: + if ( MODEM_CAPS_3GPP (ctx->caps) + && mm_modem_get_unlock_required (self->priv->modem_iface) == MM_MODEM_LOCK_SIM_PIN) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection); + const char *pin = nm_setting_gsm_get_pin (s_gsm); + + /* If we have a PIN already, send it. If we don't, get it. */ + if (pin) { + mm_sim_send_pin (self->priv->sim_iface, + pin, + ctx->cancellable, + (GAsyncReadyCallback) send_pin_ready, + self); + } else { + ask_for_pin (self); + } + break; + } + ctx->step++; + /* fall through */ + + case CONNECT_STEP_WAIT_FOR_READY: { + GError *error = NULL; + + if (mm_modem_get_state (self->priv->modem_iface) <= MM_MODEM_STATE_LOCKED) + break; + + /* Create core connect properties based on the modem capabilities */ + g_assert (!ctx->connect_properties); + + if (MODEM_CAPS_3GPP (ctx->caps)) + ctx->connect_properties = create_gsm_connect_properties (ctx->connection); + else if (MODEM_CAPS_3GPP2 (ctx->caps)) + ctx->connect_properties = create_cdma_connect_properties (ctx->connection); + else { + nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem", + nm_modem_get_uid (NM_MODEM (self)), + nm_connection_get_id (ctx->connection)); + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + connect_context_clear (self); + break; + } + g_assert (ctx->connect_properties); + + /* Build up list of IP types that we need to use in the retries */ + ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), ctx->connection, &error); + if (!ctx->ip_types) { + nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s", + nm_modem_get_uid (NM_MODEM (self)), + nm_connection_get_id (ctx->connection), + error ? error->message : "unknown error"); + g_clear_error (&error); + + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + connect_context_clear (self); + break; + } + + ctx->step++; + /* fall through */ } - /* If we have a saved error from a previous attempt, use it */ - if (!ctx->first_error) - ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR, - NM_DEVICE_ERROR_INVALID_CONNECTION, - "invalid bearer IP configuration"); + case CONNECT_STEP_CONNECT: + if (ctx->ip_types_i < ctx->ip_types->len) { + NMModemIPType current; - nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s", - nm_modem_get_uid (NM_MODEM (ctx->self)), - ctx->first_error->message); - g_signal_emit_by_name (ctx->self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error)); - act_stage_context_free (ctx); + current = g_array_index (ctx->ip_types, NMModemIPType, ctx->ip_types_i); + + if (current == NM_MODEM_IP_TYPE_IPV4) + mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4); + else if (current == NM_MODEM_IP_TYPE_IPV6) + mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV6); + else if (current == NM_MODEM_IP_TYPE_IPV4V6) + mm_simple_connect_properties_set_ip_type (ctx->connect_properties, MM_BEARER_IP_FAMILY_IPV4V6); + else + g_assert_not_reached (); + + nm_log_dbg (LOGD_MB, "(%s): launching connection with ip type '%s'", + nm_modem_get_uid (NM_MODEM (self)), + nm_modem_ip_type_to_string (current)); + + mm_modem_simple_connect (self->priv->simple_iface, + ctx->connect_properties, + NULL, + (GAsyncReadyCallback) connect_ready, + self); + break; + } + + ctx->step++; + /* fall through */ + + case CONNECT_STEP_LAST: + if (self->priv->ipv4_config || self->priv->ipv6_config) { + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, TRUE, NM_DEVICE_STATE_REASON_NONE); + } else { + /* If we have a saved error from a previous attempt, use it */ + if (!ctx->first_error) + ctx->first_error = g_error_new_literal (NM_DEVICE_ERROR, + NM_DEVICE_ERROR_INVALID_CONNECTION, + "invalid bearer IP configuration"); + + nm_log_warn (LOGD_MB, "(%s): failed to connect modem: %s", + nm_modem_get_uid (NM_MODEM (self)), + ctx->first_error->message); + g_signal_emit_by_name (self, NM_MODEM_PREPARE_RESULT, FALSE, translate_mm_error (ctx->first_error)); + } + + connect_context_clear (self); + break; + } } static NMActStageReturn @@ -392,8 +527,6 @@ act_stage1_prepare (NMModem *_self, NMDeviceStateReason *reason) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); - ActStageContext *ctx; - GError *error = NULL; /* Make sure we can get the Simple interface from the modem */ if (!self->priv->simple_iface) { @@ -406,41 +539,16 @@ act_stage1_prepare (NMModem *_self, } } - /* Allocate new context for this activation stage attempt */ - ctx = g_slice_new0 (ActStageContext); - ctx->self = NM_MODEM_BROADBAND (g_object_ref (self)); - ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface); + connect_context_clear (self); - /* Create core connect properties based on the modem capabilities */ - if (MODEM_CAPS_3GPP (ctx->caps)) - ctx->connect_properties = create_gsm_connect_properties (connection); - else if (MODEM_CAPS_3GPP2 (ctx->caps)) - ctx->connect_properties = create_cdma_connect_properties (connection); - else { - nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': not a mobile broadband modem", - nm_modem_get_uid (NM_MODEM (self)), - nm_connection_get_id (connection)); - act_stage_context_free (ctx); - *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; - } - g_assert (ctx->connect_properties); - - /* Checkout list of IP types that we need to use in the retries */ - ctx->ip_types = nm_modem_get_connection_ip_type (NM_MODEM (self), connection, &error); - if (!ctx->ip_types) { - nm_log_warn (LOGD_MB, "(%s): Failed to connect '%s': %s", - nm_modem_get_uid (NM_MODEM (self)), - nm_connection_get_id (connection), - error ? error->message : "unknown error"); - g_clear_error (&error); - act_stage_context_free (ctx); - *reason = NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED; - return NM_ACT_STAGE_RETURN_FAILURE; - } + /* Allocate new context for this connect stage attempt */ + self->priv->ctx = g_slice_new0 (ConnectContext); + self->priv->ctx->caps = mm_modem_get_current_capabilities (self->priv->modem_iface); + self->priv->ctx->cancellable = g_cancellable_new (); + self->priv->ctx->connection = g_object_ref (connection); g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (self->priv->simple_iface), MODEM_CONNECT_TIMEOUT_SECS * 1000); - act_stage_context_step (ctx); + connect_context_step (self); return NM_ACT_STAGE_RETURN_POSTPONE; } @@ -1050,7 +1158,6 @@ modem_state_changed (MMModem *modem, MMModemStateChangeReason reason, NMModemBroadband *self) { - /* After the SIM is unlocked MM1 will move the device to INITIALIZING which * is an unavailable state. That makes state handling confusing here, so * suppress this state change and let the modem move from LOCKED to DISABLED. @@ -1061,6 +1168,9 @@ modem_state_changed (MMModem *modem, nm_modem_set_state (NM_MODEM (self), mm_state_to_nm (new_state), mm_modem_state_change_reason_get_string (reason)); + + if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_READY) + connect_context_step (self); } /*****************************************************************************/ @@ -1122,13 +1232,23 @@ get_sim_ready (MMModem *modem, GError *error = NULL; MMSim *new_sim; + new_sim = mm_modem_get_sim_finish (modem, res, &error); - if (new_sim) { + if (new_sim != self->priv->sim_iface) { + g_clear_object (&self->priv->sim_iface); + self->priv->sim_iface = new_sim; + } else + g_clear_object (&new_sim); + + if (self->priv->sim_iface) { g_object_set (G_OBJECT (self), - NM_MODEM_SIM_ID, mm_sim_get_identifier (new_sim), - NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (new_sim), + NM_MODEM_SIM_ID, mm_sim_get_identifier (self->priv->sim_iface), + NM_MODEM_SIM_OPERATOR_ID, mm_sim_get_operator_identifier (self->priv->sim_iface), NULL); - g_object_unref (new_sim); + + /* If we're waiting for the SIM during a connect, proceed with the connect */ + if (self->priv->ctx && self->priv->ctx->step == CONNECT_STEP_WAIT_FOR_SIM) + connect_context_step (self); } else { nm_log_warn (LOGD_MB, "(%s): failed to retrieve SIM object: %s", nm_modem_get_uid (NM_MODEM (self)), @@ -1238,11 +1358,13 @@ dispose (GObject *object) { NMModemBroadband *self = NM_MODEM_BROADBAND (object); + connect_context_clear (self); g_clear_object (&self->priv->ipv4_config); g_clear_object (&self->priv->ipv6_config); g_clear_object (&self->priv->bearer); g_clear_object (&self->priv->modem_iface); g_clear_object (&self->priv->simple_iface); + g_clear_object (&self->priv->sim_iface); g_clear_object (&self->priv->modem_object); G_OBJECT_CLASS (nm_modem_broadband_parent_class)->dispose (object);