diff options
| author | Michael Biebl <biebl@debian.org> | 2019-12-18 18:29:24 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2019-12-18 18:29:24 +0100 |
| commit | 28028b26b3371756811e95d894f709f4b1207c00 (patch) | |
| tree | 6fe7316fd743b51042db47601a8ef8814b3134ac /src/devices/wwan | |
| parent | e22609983008e1a669196ad64ba3a59ae8c76e0d (diff) | |
New upstream version 1.22.0 upstream/1.22.0
Diffstat (limited to 'src/devices/wwan')
| -rw-r--r-- | src/devices/wwan/libnm-wwan.ver | 6 | ||||
| -rw-r--r-- | src/devices/wwan/meson.build | 42 | ||||
| -rw-r--r-- | src/devices/wwan/nm-device-modem.c | 104 | ||||
| -rw-r--r-- | src/devices/wwan/nm-device-modem.h | 18 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-broadband.c | 193 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-broadband.h | 18 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-manager.c | 32 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-manager.h | 21 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-ofono.c | 26 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem-ofono.h | 18 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem.c | 92 | ||||
| -rw-r--r-- | src/devices/wwan/nm-modem.h | 32 | ||||
| -rw-r--r-- | src/devices/wwan/nm-service-providers.c | 459 | ||||
| -rw-r--r-- | src/devices/wwan/nm-service-providers.h | 24 | ||||
| -rw-r--r-- | src/devices/wwan/nm-wwan-factory.c | 32 | ||||
| -rw-r--r-- | src/devices/wwan/tests/test-service-providers.c | 124 | ||||
| -rw-r--r-- | src/devices/wwan/tests/test-service-providers.xml | 73 |
17 files changed, 1018 insertions, 296 deletions
diff --git a/src/devices/wwan/libnm-wwan.ver b/src/devices/wwan/libnm-wwan.ver index 7ccebcb5..c368a590 100644 --- a/src/devices/wwan/libnm-wwan.ver +++ b/src/devices/wwan/libnm-wwan.ver @@ -3,6 +3,7 @@ global: nm_modem_act_stage1_prepare; nm_modem_act_stage2_config; nm_modem_check_connection_compatible; + nm_modem_claim; nm_modem_complete_connection; nm_modem_deactivate; nm_modem_deactivate_async; @@ -14,15 +15,17 @@ global: nm_modem_get_device_id; nm_modem_get_driver; nm_modem_get_iid; - nm_modem_get_path; nm_modem_get_ip_ifindex; nm_modem_get_operator_code; + nm_modem_get_path; nm_modem_get_secrets; nm_modem_get_state; nm_modem_get_type; nm_modem_get_uid; nm_modem_ip4_pre_commit; + nm_modem_is_claimed; nm_modem_manager_get; + nm_modem_manager_get_modems; nm_modem_manager_get_type; nm_modem_manager_name_owner_get; nm_modem_manager_name_owner_ref; @@ -32,6 +35,7 @@ global: nm_modem_stage3_ip4_config_start; nm_modem_stage3_ip6_config_start; nm_modem_state_to_string; + nm_modem_unclaim; local: *; }; diff --git a/src/devices/wwan/meson.build b/src/devices/wwan/meson.build index 482dc205..ed6f8010 100644 --- a/src/devices/wwan/meson.build +++ b/src/devices/wwan/meson.build @@ -1,13 +1,15 @@ -sources = files( +nm_service_providers_source = files('nm-service-providers.c') + +sources = nm_service_providers_source + files( 'nm-modem-broadband.c', 'nm-modem.c', 'nm-modem-manager.c', ) deps = [ + daemon_nm_default_dep, libsystemd_dep, mm_glib_dep, - nm_dep, ] if enable_ofono @@ -20,16 +22,17 @@ libnm_wwan = shared_module( 'nm-wwan', sources: sources, dependencies: deps, - link_args: [ - '-Wl,--version-script,@0@'.format(linker_script), - ], + c_args: daemon_c_flags, + link_args: '-Wl,--version-script,@0@'.format(linker_script), link_depends: linker_script, install: true, install_dir: nm_plugindir, ) +wwan_inc = include_directories('.') + libnm_wwan_dep = declare_dependency( - include_directories: include_directories('.'), + include_directories: wwan_inc, link_with: libnm_wwan, ) @@ -50,6 +53,7 @@ libnm_device_plugin_wwan = shared_module( 'nm-device-plugin-wwan', sources: sources, dependencies: deps, + c_args: daemon_c_flags, link_with: libnm_wwan, link_args: ldflags_linker_script_devices, link_depends: linker_script_devices, @@ -66,11 +70,21 @@ run_target( depends: libnm_device_plugin_wwan, ) -# FIXME: check_so_symbols replacement -''' -check-local-devices-wwan: src/devices/wwan/libnm-device-plugin-wwan.la src/devices/wwan/libnm-wwan.la - $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so "$(srcdir)/linker-script-devices.ver" - $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-device-plugin-wwan.so) - $(srcdir)/tools/check-exports.sh $(builddir)/src/devices/wwan/.libs/libnm-wwan.so "$(srcdir)/src/devices/wwan/libnm-wwan.ver" - $(call check_so_symbols,$(builddir)/src/devices/wwan/.libs/libnm-wwan.so) -''' +if enable_tests + test_unit = 'test-service-providers' + + exe = executable( + test_unit, + ['tests/' + test_unit + '.c'] + nm_service_providers_source, + include_directories: wwan_inc, + dependencies: libnetwork_manager_test_dep, + c_args: test_c_flags, + ) + + test( + 'wwan/' + test_unit, + test_script, + timeout: default_test_timeout, + args: test_args + [exe.full_path()], + ) +endif diff --git a/src/devices/wwan/nm-device-modem.c b/src/devices/wwan/nm-device-modem.c index 042a6ca4..3e6ccc8f 100644 --- a/src/devices/wwan/nm-device-modem.c +++ b/src/devices/wwan/nm-device-modem.c @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2009 - 2019 Red Hat, Inc. */ @@ -48,10 +34,11 @@ typedef struct { NMModem *modem; NMDeviceModemCapabilities caps; NMDeviceModemCapabilities current_caps; - gboolean rf_enabled; char *device_id; char *operator_code; char *apn; + bool rf_enabled:1; + NMDeviceStageState stage1_state:3; } NMDeviceModemPrivate; struct _NMDeviceModem { @@ -119,16 +106,17 @@ modem_prepare_result (NMModem *modem, gpointer user_data) { NMDeviceModem *self = NM_DEVICE_MODEM (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self); - NMDeviceState state; NMDeviceStateReason reason = i_reason; - state = nm_device_get_state (device); - g_return_if_fail (state == NM_DEVICE_STATE_PREPARE); + if ( nm_device_get_state (device) != NM_DEVICE_STATE_PREPARE + || priv->stage1_state != NM_DEVICE_STAGE_STATE_PENDING) { + nm_assert_not_reached (); + success = FALSE; + } - if (success) - nm_device_activate_schedule_stage2_device_config (device); - else { + if (!success) { /* There are several reasons to block autoconnection at device level: * * - Wrong SIM-PIN: The device won't autoconnect because it doesn't make sense @@ -164,7 +152,11 @@ modem_prepare_result (NMModem *modem, break; } nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, reason); + return; } + + priv->stage1_state = NM_DEVICE_STAGE_STATE_COMPLETED; + nm_device_activate_schedule_stage1_device_prepare (device); } static void @@ -187,16 +179,19 @@ static void modem_auth_result (NMModem *modem, GError *error, gpointer user_data) { NMDevice *device = NM_DEVICE (user_data); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); if (error) { nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NO_SECRETS); - } else { - /* Otherwise, on success for modem secrets we need to schedule stage1 again */ - g_return_if_fail (nm_device_get_state (device) == NM_DEVICE_STATE_NEED_AUTH); - nm_device_activate_schedule_stage1_device_prepare (device); + return; } + + priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT; + nm_device_activate_schedule_stage1_device_prepare (device); } static void @@ -355,7 +350,7 @@ modem_state_cb (NMModem *modem, NMModemState new_state = new_state_i; NMModemState old_state = old_state_i; NMDevice *device = NM_DEVICE (user_data); - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); NMDeviceState dev_state = nm_device_get_state (device); if (new_state <= NM_MODEM_STATE_DISABLING && @@ -409,7 +404,7 @@ modem_removed_cb (NMModem *modem, gpointer user_data) static gboolean owns_iface (NMDevice *device, const char *iface) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); g_return_val_if_fail (priv->modem, FALSE); @@ -447,7 +442,7 @@ get_generic_capabilities (NMDevice *device) static const char * get_type_description (NMDevice *device) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); if (NM_FLAGS_HAS (priv->current_caps, NM_DEVICE_MODEM_CAPABILITY_GSM_UMTS)) return "gsm"; @@ -464,7 +459,7 @@ check_connection_compatible (NMDevice *device, NMConnection *connection, GError if (!NM_DEVICE_CLASS (nm_device_modem_parent_class)->check_connection_compatible (device, connection, error)) return FALSE; - if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, + if (!nm_modem_check_connection_compatible (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, connection, error ? &local : NULL)) { if (error) { @@ -530,7 +525,7 @@ complete_connection (NMDevice *device, NMConnection *const*existing_connections, GError **error) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); return nm_modem_complete_connection (priv->modem, nm_device_get_iface (device), @@ -542,7 +537,10 @@ complete_connection (NMDevice *device, static void deactivate (NMDevice *device) { - nm_modem_deactivate (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); + + nm_modem_deactivate (priv->modem, device); + priv->stage1_state = NM_DEVICE_STAGE_STATE_INIT; } /*****************************************************************************/ @@ -583,28 +581,32 @@ deactivate_async (NMDevice *self, static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMActStageReturn ret; + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); NMActRequest *req; - ret = NM_DEVICE_CLASS (nm_device_modem_parent_class)->act_stage1_prepare (device, out_failure_reason); - if (ret != NM_ACT_STAGE_RETURN_SUCCESS) - return ret; - req = nm_device_get_act_request (device); g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); - return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason); + if (priv->stage1_state == NM_DEVICE_STAGE_STATE_INIT) { + priv->stage1_state = NM_DEVICE_STAGE_STATE_PENDING; + return nm_modem_act_stage1_prepare (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, + req, + out_failure_reason); + } + + if (priv->stage1_state == NM_DEVICE_STAGE_STATE_PENDING) + return NM_ACT_STAGE_RETURN_POSTPONE; + + nm_assert (priv->stage1_state == NM_DEVICE_STAGE_STATE_COMPLETED); + return NM_ACT_STAGE_RETURN_SUCCESS; } static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) { - NMActRequest *req; - - req = nm_device_get_act_request (device); - g_return_val_if_fail (req, NM_ACT_STAGE_RETURN_FAILURE); + nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem); - return nm_modem_act_stage2_config (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, req, out_failure_reason); + return NM_ACT_STAGE_RETURN_SUCCESS; } static NMActStageReturn @@ -632,7 +634,7 @@ act_stage3_ip_config_start (NMDevice *device, static void ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) { - nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device)->modem, device, config); + nm_modem_ip4_pre_commit (NM_DEVICE_MODEM_GET_PRIVATE (device)->modem, device, config); } static gboolean @@ -654,7 +656,7 @@ get_ip_iface_identifier (NMDevice *device, NMUtilsIPv6IfaceId *out_iid) static gboolean get_enabled (NMDevice *device) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) device); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (device); NMModemState modem_state = nm_modem_get_state (priv->modem); return priv->rf_enabled && (modem_state >= NM_MODEM_STATE_LOCKED); @@ -710,7 +712,7 @@ set_modem (NMDeviceModem *self, NMModem *modem) g_return_if_fail (modem != NULL); - priv->modem = g_object_ref (modem); + priv->modem = nm_modem_claim (modem); g_signal_connect (modem, NM_MODEM_PPP_FAILED, G_CALLBACK (ppp_failed), self); g_signal_connect (modem, NM_MODEM_PREPARE_RESULT, G_CALLBACK (modem_prepare_result), self); @@ -745,7 +747,7 @@ static void get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); switch (prop_id) { case PROP_MODEM: @@ -776,7 +778,7 @@ static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); switch (prop_id) { case PROP_MODEM: @@ -834,11 +836,11 @@ nm_device_modem_new (NMModem *modem) static void dispose (GObject *object) { - NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE ((NMDeviceModem *) object); + NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (object); if (priv->modem) { g_signal_handlers_disconnect_by_data (priv->modem, NM_DEVICE_MODEM (object)); - g_clear_object (&priv->modem); + nm_clear_pointer (&priv->modem, nm_modem_unclaim); } g_clear_pointer (&priv->device_id, g_free); diff --git a/src/devices/wwan/nm-device-modem.h b/src/devices/wwan/nm-device-modem.h index 0a557f57..842096e2 100644 --- a/src/devices/wwan/nm-device-modem.h +++ b/src/devices/wwan/nm-device-modem.h @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2011 Red Hat, Inc. */ diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 216fedfe..a1ae8671 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -1,25 +1,12 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org> */ #include "nm-default.h" #include "nm-modem-broadband.h" +#include "nm-service-providers.h" #include <arpa/inet.h> #include <libmm-glib.h> @@ -274,7 +261,10 @@ create_cdma_connect_properties (NMConnection *connection) } static MMSimpleConnectProperties * -create_gsm_connect_properties (NMConnection *connection) +create_gsm_connect_properties (NMConnection *connection, + const char *apn, + const char *username, + const char *password) { NMSettingGsm *setting; NMSettingPpp *s_ppp; @@ -282,11 +272,14 @@ create_gsm_connect_properties (NMConnection *connection) const char *str; setting = nm_connection_get_setting_gsm (connection); + properties = mm_simple_connect_properties_new (); - /* Blank APN ("") means the default subscription APN */ - str = nm_setting_gsm_get_apn (setting); - mm_simple_connect_properties_set_apn (properties, str ?: ""); + mm_simple_connect_properties_set_apn (properties, apn ?: ""); + if (username) + mm_simple_connect_properties_set_user (properties, username); + if (password) + mm_simple_connect_properties_set_password (properties, password); str = nm_setting_gsm_get_network_id (setting); if (str) @@ -296,14 +289,6 @@ create_gsm_connect_properties (NMConnection *connection) if (str) mm_simple_connect_properties_set_pin (properties, str); - str = nm_setting_gsm_get_username (setting); - if (str) - mm_simple_connect_properties_set_user (properties, str); - - str = nm_setting_gsm_get_password (setting); - if (str) - mm_simple_connect_properties_set_password (properties, str); - /* Roaming */ if (nm_setting_gsm_get_home_only (setting)) mm_simple_connect_properties_set_allow_roaming (properties, FALSE); @@ -356,16 +341,26 @@ connect_ready (MMModemSimple *simple_iface, GAsyncResult *res, NMModemBroadband *self) { - ConnectContext *ctx = self->_priv.ctx; + ConnectContext *ctx; GError *error = NULL; NMModemIPMethod ip4_method = NM_MODEM_IP_METHOD_UNKNOWN; NMModemIPMethod ip6_method = NM_MODEM_IP_METHOD_UNKNOWN; + MMBearer *bearer; - self->_priv.bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); + bearer = mm_modem_simple_connect_finish (simple_iface, res, &error); + + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_error_free (error); + return; + } + + ctx = self->_priv.ctx; if (!ctx) return; + self->_priv.bearer = bearer; + 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) && @@ -457,6 +452,95 @@ send_pin_ready (MMSim *sim, GAsyncResult *result, NMModemBroadband *self) } static void +find_gsm_apn_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + NMModemBroadband *self = user_data; + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); + ConnectContext *ctx = priv->ctx; + + if (error) { + _LOGW ("failed to connect '%s': APN not found: %s", + nm_connection_get_id (ctx->connection), error->message); + + nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_GSM_APN_FAILED); + connect_context_clear (self); + return; + } + + /* Blank APN ("") means the default subscription APN */ + ctx->connect_properties = create_gsm_connect_properties (ctx->connection, + apn, + username, + password); + g_return_if_fail (ctx->connect_properties); + connect_context_step (self); +} + +static gboolean +try_create_connect_properties (NMModemBroadband *self) +{ + NMModemBroadbandPrivate *priv = NM_MODEM_BROADBAND_GET_PRIVATE (self); + ConnectContext *ctx = priv->ctx; + + if (MODEM_CAPS_3GPP (ctx->caps)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (ctx->connection); + + if (!s_gsm || nm_setting_gsm_get_auto_config (s_gsm)) { + gs_unref_object MMModem3gpp *modem_3gpp = NULL; + const char *network_id = NULL; + + s_gsm = nm_connection_get_setting_gsm (ctx->connection); + if (s_gsm) + network_id = nm_setting_gsm_get_network_id (s_gsm); + if (!network_id) { + if (mm_modem_get_state (self->_priv.modem_iface) < MM_MODEM_STATE_REGISTERED) + return FALSE; + modem_3gpp = mm_object_get_modem_3gpp (priv->modem_object); + network_id = mm_modem_3gpp_get_operator_code (modem_3gpp); + } + if (!network_id) { + _LOGW ("failed to connect '%s': unable to determine the network id", + nm_connection_get_id (ctx->connection)); + goto out; + } + + nm_service_providers_find_gsm_apn (MOBILE_BROADBAND_PROVIDER_INFO_DATABASE, + network_id, + ctx->cancellable, + find_gsm_apn_cb, + self); + } else { + ctx->connect_properties = create_gsm_connect_properties (ctx->connection, + nm_setting_gsm_get_apn (s_gsm), + nm_setting_gsm_get_username (s_gsm), + nm_setting_gsm_get_password (s_gsm)); + g_return_val_if_fail (ctx->connect_properties, TRUE); + } + + return TRUE; + } else if (MODEM_CAPS_3GPP2 (ctx->caps)) { + ctx->connect_properties = create_cdma_connect_properties (ctx->connection); + g_return_val_if_fail (ctx->connect_properties, FALSE); + return TRUE; + } else { + _LOGW ("failed to connect '%s': not a mobile broadband modem", + nm_connection_get_id (ctx->connection)); + } + +out: + nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); + connect_context_clear (self); + return TRUE; +} + +static void connect_context_step (NMModemBroadband *self) { ConnectContext *ctx = self->_priv.ctx; @@ -500,22 +584,11 @@ connect_context_step (NMModemBroadband *self) 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 { - _LOGW ("failed to connect '%s': not a mobile broadband modem", - nm_connection_get_id (ctx->connection)); + if (!try_create_connect_properties (self)) + break; - nm_modem_emit_prepare_result (NM_MODEM (self), FALSE, NM_DEVICE_STATE_REASON_MODEM_INIT_FAILED); - connect_context_clear (self); + if (!self->_priv.ctx) 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); @@ -534,6 +607,9 @@ connect_context_step (NMModemBroadband *self) } /* fall through */ case CONNECT_STEP_CONNECT: + if (!ctx->connect_properties) + break; + if (ctx->ip_types_i < ctx->ip_types->len) { NMModemIPType current; @@ -546,7 +622,7 @@ connect_context_step (NMModemBroadband *self) 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 (); + g_return_if_reached (); _nm_modem_set_apn (NM_MODEM (self), mm_simple_connect_properties_get_apn (ctx->connect_properties)); @@ -556,7 +632,7 @@ connect_context_step (NMModemBroadband *self) mm_modem_simple_connect (self->_priv.simple_iface, ctx->connect_properties, - NULL, + ctx->cancellable, (GAsyncReadyCallback) connect_ready, self); break; @@ -586,9 +662,9 @@ connect_context_step (NMModemBroadband *self) } static NMActStageReturn -act_stage1_prepare (NMModem *_self, - NMConnection *connection, - NMDeviceStateReason *out_failure_reason) +modem_act_stage1_prepare (NMModem *_self, + NMConnection *connection, + NMDeviceStateReason *out_failure_reason) { NMModemBroadband *self = NM_MODEM_BROADBAND (_self); @@ -689,6 +765,9 @@ complete_connection (NMModem *modem, if (!s_gsm) { s_gsm = (NMSettingGsm *) nm_setting_gsm_new (); nm_connection_add_setting (connection, NM_SETTING (s_gsm)); + g_object_set (G_OBJECT (s_gsm), + NM_SETTING_GSM_AUTO_CONFIG, TRUE, + NULL); } if (!nm_setting_gsm_get_device_id (s_gsm)) { @@ -876,8 +955,8 @@ static_stage3_ip4_done (NMModemBroadband *self) guint32 ip4_route_table, ip4_route_metric; NMPlatformIP4Route *r; - g_assert (self->_priv.ipv4_config); - g_assert (self->_priv.bearer); + g_return_val_if_fail (self->_priv.ipv4_config, FALSE); + g_return_val_if_fail (self->_priv.bearer, FALSE); self->_priv.idle_id_ip4 = 0; @@ -908,7 +987,7 @@ static_stage3_ip4_done (NMModemBroadband *self) } data_port = mm_bearer_get_interface (self->_priv.bearer); - g_assert (data_port); + g_return_val_if_fail (data_port, FALSE); config = nm_ip4_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); @@ -983,7 +1062,7 @@ stage3_ip6_done (NMModemBroadband *self) const char **dns; guint i; - g_assert (self->_priv.ipv6_config); + g_return_val_if_fail (self->_priv.ipv6_config, FALSE); self->_priv.idle_id_ip6 = 0; memset (&address, 0, sizeof (address)); @@ -1015,7 +1094,8 @@ stage3_ip6_done (NMModemBroadband *self) _LOGI ("IPv6 base configuration:"); data_port = mm_bearer_get_interface (self->_priv.bearer); - g_assert (data_port); + g_return_val_if_fail (data_port, FALSE); + config = nm_ip6_config_new (nm_platform_get_multi_idx (NM_PLATFORM_GET), nm_platform_link_get_ifindex (NM_PLATFORM_GET, data_port)); @@ -1383,8 +1463,9 @@ set_property (GObject *object, /* construct-only */ self->_priv.modem_object = g_value_dup_object (value); self->_priv.modem_iface = mm_object_get_modem (self->_priv.modem_object); + g_return_if_fail (self->_priv.modem_iface); self->_priv.modem_3gpp_iface = mm_object_get_modem_3gpp (self->_priv.modem_object); - g_assert (self->_priv.modem_iface != NULL); + g_signal_connect (self->_priv.modem_iface, "state-changed", G_CALLBACK (modem_state_changed), @@ -1512,7 +1593,7 @@ nm_modem_broadband_class_init (NMModemBroadbandClass *klass) modem_class->get_user_pass = get_user_pass; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; modem_class->complete_connection = complete_connection; - modem_class->act_stage1_prepare = act_stage1_prepare; + modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare; modem_class->owns_port = owns_port; obj_properties[PROP_MODEM] = diff --git a/src/devices/wwan/nm-modem-broadband.h b/src/devices/wwan/nm-modem-broadband.h index 9404f0b9..43041e2b 100644 --- a/src/devices/wwan/nm-modem-broadband.h +++ b/src/devices/wwan/nm-modem-broadband.h @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2012 - Aleksander Morgado <aleksander@gnu.org> */ diff --git a/src/devices/wwan/nm-modem-manager.c b/src/devices/wwan/nm-modem-manager.c index db2c0192..daccf68a 100644 --- a/src/devices/wwan/nm-modem-manager.c +++ b/src/devices/wwan/nm-modem-manager.c @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2009 - 2014 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. * Copyright (C) 2009 - 2013 Canonical Ltd. @@ -149,6 +135,20 @@ remove_one_modem (gpointer key, gpointer value, gpointer user_data) /*****************************************************************************/ +NMModem ** +nm_modem_manager_get_modems (NMModemManager *self, + guint *out_len) +{ + g_return_val_if_fail (NM_IS_MODEM_MANAGER (self), NULL); + + return (NMModem **) nm_utils_hash_values_to_array (NM_MODEM_MANAGER_GET_PRIVATE (self)->modems, + NULL, + NULL, + out_len); +} + +/*****************************************************************************/ + static void modm_clear_manager (NMModemManager *self) { diff --git a/src/devices/wwan/nm-modem-manager.h b/src/devices/wwan/nm-modem-manager.h index 1a26fd9f..97a2126a 100644 --- a/src/devices/wwan/nm-modem-manager.h +++ b/src/devices/wwan/nm-modem-manager.h @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2009 - 2014 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. * Copyright (C) 2009 Canonical Ltd. @@ -51,4 +37,7 @@ void nm_modem_manager_name_owner_unref (NMModemManager *self); const char *nm_modem_manager_name_owner_get (NMModemManager *self); +NMModem **nm_modem_manager_get_modems (NMModemManager *self, + guint *out_len); + #endif /* __NETWORKMANAGER_MODEM_MANAGER_H__ */ diff --git a/src/devices/wwan/nm-modem-ofono.c b/src/devices/wwan/nm-modem-ofono.c index 31111b62..b68cd7e8 100644 --- a/src/devices/wwan/nm-modem-ofono.c +++ b/src/devices/wwan/nm-modem-ofono.c @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2013 - 2016 Canonical Ltd. */ @@ -1114,9 +1100,9 @@ create_connect_properties (NMConnection *connection) } static NMActStageReturn -act_stage1_prepare (NMModem *modem, - NMConnection *connection, - NMDeviceStateReason *out_failure_reason) +modem_act_stage1_prepare (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *out_failure_reason) { NMModemOfono *self = NM_MODEM_OFONO (modem); NMModemOfonoPrivate *priv = NM_MODEM_OFONO_GET_PRIVATE (self); @@ -1308,6 +1294,6 @@ nm_modem_ofono_class_init (NMModemOfonoClass *klass) modem_class->deactivate_cleanup = deactivate_cleanup; modem_class->check_connection_compatible_with_modem = check_connection_compatible_with_modem; - modem_class->act_stage1_prepare = act_stage1_prepare; + modem_class->modem_act_stage1_prepare = modem_act_stage1_prepare; modem_class->static_stage3_ip4_config_start = static_stage3_ip4_config_start; } diff --git a/src/devices/wwan/nm-modem-ofono.h b/src/devices/wwan/nm-modem-ofono.h index 1dcd79b0..4ff2fbdd 100644 --- a/src/devices/wwan/nm-modem-ofono.h +++ b/src/devices/wwan/nm-modem-ofono.h @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2013 - Canonical Ltd. */ diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index fb2316d5..ad6449b1 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2009 - 2014 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. */ @@ -110,6 +96,8 @@ typedef struct _NMModemPrivate { /* PPP stats */ guint32 in_bytes; guint32 out_bytes; + + bool claimed:1; } NMModemPrivate; G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT) @@ -187,6 +175,53 @@ nm_modem_state_to_string (NMModemState state) return NULL; } +/*****************************************************************************/ + +gboolean +nm_modem_is_claimed (NMModem *self) +{ + g_return_val_if_fail (NM_IS_MODEM (self), FALSE); + + return NM_MODEM_GET_PRIVATE (self)->claimed; +} + +NMModem * +nm_modem_claim (NMModem *self) +{ + NMModemPrivate *priv; + + g_return_val_if_fail (NM_IS_MODEM (self), NULL); + + priv = NM_MODEM_GET_PRIVATE (self); + + g_return_val_if_fail (!priv->claimed, NULL); + + priv->claimed = TRUE; + return g_object_ref (self); +} + +void +nm_modem_unclaim (NMModem *self) +{ + NMModemPrivate *priv; + + g_return_if_fail (NM_IS_MODEM (self)); + + priv = NM_MODEM_GET_PRIVATE (self); + + g_return_if_fail (priv->claimed); + + /* we don't actually unclaim the instance. This instance should not be re-used + * by another owner, that is because we only claim modems as we receive them. + * There is no mechanism that somebody else would later re-use them again. + * + * // priv->claimed = FALSE; */ + + g_object_unref (self); +} + +/*****************************************************************************/ + NMModemState nm_modem_get_state (NMModem *self) { @@ -976,9 +1011,9 @@ nm_modem_get_secrets (NMModem *self, /*****************************************************************************/ static NMActStageReturn -act_stage1_prepare (NMModem *modem, - NMConnection *connection, - NMDeviceStateReason *out_failure_reason) +modem_act_stage1_prepare (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *out_failure_reason) { NM_SET_OUT (out_failure_reason, NM_DEVICE_STATE_REASON_UNKNOWN); return NM_ACT_STAGE_RETURN_FAILURE; @@ -995,6 +1030,8 @@ nm_modem_act_stage1_prepare (NMModem *self, NMSecretAgentGetSecretsFlags flags = NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION; NMConnection *connection; + g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE); + if (priv->act_request) g_object_unref (priv->act_request); priv->act_request = g_object_ref (req); @@ -1005,7 +1042,7 @@ nm_modem_act_stage1_prepare (NMModem *self, setting_name = nm_connection_need_secrets (connection, &hints); if (!setting_name) { nm_assert (!hints); - return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, out_failure_reason); + return NM_MODEM_GET_CLASS (self)->modem_act_stage1_prepare (self, connection, out_failure_reason); } /* Secrets required... */ @@ -1029,19 +1066,18 @@ nm_modem_act_stage1_prepare (NMModem *self, /*****************************************************************************/ -NMActStageReturn -nm_modem_act_stage2_config (NMModem *self, - NMActRequest *req, - NMDeviceStateReason *out_failure_reason) +void +nm_modem_act_stage2_config (NMModem *self) { - NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self); + NMModemPrivate *priv; + + g_return_if_fail (NM_IS_MODEM (self)); + priv = NM_MODEM_GET_PRIVATE (self); /* Clear secrets tries counter since secrets were successfully used * already if we get here. */ priv->secrets_tries = 0; - - return NM_ACT_STAGE_RETURN_SUCCESS; } /*****************************************************************************/ @@ -1800,7 +1836,7 @@ nm_modem_class_init (NMModemClass *klass) object_class->dispose = dispose; object_class->finalize = finalize; - klass->act_stage1_prepare = act_stage1_prepare; + klass->modem_act_stage1_prepare = modem_act_stage1_prepare; klass->stage3_ip6_config_request = stage3_ip6_config_request; klass->deactivate_cleanup = deactivate_cleanup; diff --git a/src/devices/wwan/nm-modem.h b/src/devices/wwan/nm-modem.h index fccb4fec..f2de990b 100644 --- a/src/devices/wwan/nm-modem.h +++ b/src/devices/wwan/nm-modem.h @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2009 - 2011 Red Hat, Inc. * Copyright (C) 2009 Novell, Inc. */ @@ -136,9 +122,9 @@ typedef struct { NMConnection *const*existing_connections, GError **error); - NMActStageReturn (*act_stage1_prepare) (NMModem *modem, - NMConnection *connection, - NMDeviceStateReason *out_failure_reason); + NMActStageReturn (*modem_act_stage1_prepare) (NMModem *modem, + NMConnection *connection, + NMDeviceStateReason *out_failure_reason); NMActStageReturn (*static_stage3_ip4_config_start) (NMModem *self, NMActRequest *req, @@ -167,6 +153,10 @@ typedef struct { GType nm_modem_get_type (void); +gboolean nm_modem_is_claimed (NMModem *modem); +NMModem *nm_modem_claim (NMModem *modem); +void nm_modem_unclaim (NMModem *modem); + 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); @@ -222,9 +212,7 @@ NMActStageReturn nm_modem_act_stage1_prepare (NMModem *modem, NMActRequest *req, NMDeviceStateReason *out_failure_reason); -NMActStageReturn nm_modem_act_stage2_config (NMModem *modem, - NMActRequest *req, - NMDeviceStateReason *out_failure_reason); +void nm_modem_act_stage2_config (NMModem *modem); NMActStageReturn nm_modem_stage3_ip4_config_start (NMModem *modem, NMDevice *device, diff --git a/src/devices/wwan/nm-service-providers.c b/src/devices/wwan/nm-service-providers.c new file mode 100644 index 00000000..5140f7ed --- /dev/null +++ b/src/devices/wwan/nm-service-providers.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2009 Novell, Inc. + * Author: Tambet Ingo (tambet@gmail.com). + * Copyright (C) 2009 - 2019 Red Hat, Inc. + * Copyright (C) 2012 Lanedo GmbH + */ + +#include "nm-default.h" + +#include "nm-service-providers.h" + +typedef enum { + PARSER_TOPLEVEL = 0, + PARSER_COUNTRY, + PARSER_PROVIDER, + PARSER_METHOD_GSM, + PARSER_METHOD_GSM_APN, + PARSER_METHOD_CDMA, + PARSER_DONE, + PARSER_ERROR +} ParseContextState; + +typedef struct { + char *mccmnc; + NMServiceProvidersGsmApnCallback callback; + gpointer user_data; + GCancellable *cancellable; + GMarkupParseContext *ctx; + char buffer[4096]; + + char *text_buffer; + ParseContextState state; + + gboolean mccmnc_matched; + gboolean found_internet_apn; + char *apn; + char *username; + char *password; + char *gateway; + char *auth_method; + GSList *dns; +} ParseContext; + +/*****************************************************************************/ + +static void +parser_toplevel_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "serviceproviders") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "format") == 0) { + if (strcmp (attribute_values[i], "2.0")) { + g_warning ("%s: mobile broadband provider database format '%s'" + " not supported.", __func__, attribute_values[i]); + parse_context->state = PARSER_ERROR; + break; + } + } + } + } else if (strcmp (name, "country") == 0) { + parse_context->state = PARSER_COUNTRY; + } +} + +static void +parser_country_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + if (strcmp (name, "provider") == 0) + parse_context->state = PARSER_PROVIDER; +} + +static void +parser_provider_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + parse_context->mccmnc_matched = FALSE; + if (strcmp (name, "gsm") == 0) + parse_context->state = PARSER_METHOD_GSM; + else if (strcmp (name, "cdma") == 0) + parse_context->state = PARSER_METHOD_CDMA; +} + +static void +parser_gsm_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "network-id") == 0) { + const char *mcc = NULL, *mnc = NULL; + + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "mcc") == 0) + mcc = attribute_values[i]; + else if (strcmp (attribute_names[i], "mnc") == 0) + mnc = attribute_values[i]; + if (mcc && strlen (mcc) && mnc && strlen (mnc)) { + char *mccmnc = g_strdup_printf ("%s%s", mcc, mnc); + + if (strcmp (mccmnc, parse_context->mccmnc) == 0) + parse_context->mccmnc_matched = TRUE; + g_free (mccmnc); + break; + } + } + } else if (strcmp (name, "apn") == 0) { + parse_context->found_internet_apn = FALSE; + g_clear_pointer (&parse_context->apn, g_free); + g_clear_pointer (&parse_context->username, g_free); + g_clear_pointer (&parse_context->password, g_free); + g_clear_pointer (&parse_context->gateway, g_free); + g_clear_pointer (&parse_context->auth_method, g_free); + g_slist_free_full (parse_context->dns, g_free); + parse_context->dns = NULL; + + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "value") == 0) { + parse_context->state = PARSER_METHOD_GSM_APN; + parse_context->apn = g_strstrip (g_strdup (attribute_values[i])); + break; + } + } + } +} + +static void +parser_gsm_apn_start (ParseContext *parse_context, + const char *name, + const char **attribute_names, + const char **attribute_values) +{ + int i; + + if (strcmp (name, "usage") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if ( (strcmp (attribute_names[i], "type") == 0) + && (strcmp (attribute_values[i], "internet") == 0)) { + parse_context->found_internet_apn = TRUE; + break; + } + } + } else if (strcmp (name, "authentication") == 0) { + for (i = 0; attribute_names && attribute_names[i]; i++) { + if (strcmp (attribute_names[i], "method") == 0) { + g_clear_pointer (&parse_context->auth_method, g_free); + parse_context->auth_method = g_strstrip (g_strdup (attribute_values[i])); + break; + } + } + } +} + +static void +parser_start_element (GMarkupParseContext *context, + const char *element_name, + const char **attribute_names, + const char **attribute_values, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + g_clear_pointer (&parse_context->text_buffer, g_free); + + switch (parse_context->state) { + case PARSER_TOPLEVEL: + parser_toplevel_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_COUNTRY: + parser_country_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_PROVIDER: + parser_provider_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_GSM: + parser_gsm_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_GSM_APN: + parser_gsm_apn_start (parse_context, element_name, attribute_names, attribute_values); + break; + case PARSER_METHOD_CDMA: + break; + case PARSER_ERROR: + break; + case PARSER_DONE: + break; + } +} + +static void +parser_country_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "country") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_TOPLEVEL; + } +} + +static void +parser_provider_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "provider") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_COUNTRY; + } +} + +static void +parser_gsm_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "gsm") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_PROVIDER; + } +} + +static void +parser_gsm_apn_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "username") == 0) { + g_clear_pointer (&parse_context->username, g_free); + parse_context->username = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "password") == 0) { + g_clear_pointer (&parse_context->password, g_free); + parse_context->password = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "dns") == 0) { + parse_context->dns = g_slist_prepend (parse_context->dns, + g_steal_pointer (&parse_context->text_buffer)); + } else if (strcmp (name, "gateway") == 0) { + g_clear_pointer (&parse_context->gateway, g_free); + parse_context->gateway = g_steal_pointer (&parse_context->text_buffer); + } else if (strcmp (name, "apn") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + + if (parse_context->mccmnc_matched && parse_context->found_internet_apn) + parse_context->state = PARSER_DONE; + else + parse_context->state = PARSER_METHOD_GSM; + + } +} + +static void +parser_cdma_end (ParseContext *parse_context, + const char *name) +{ + if (strcmp (name, "cdma") == 0) { + g_clear_pointer (&parse_context->text_buffer, g_free); + parse_context->state = PARSER_PROVIDER; + } +} + +static void +parser_end_element (GMarkupParseContext *context, + const char *element_name, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + switch (parse_context->state) { + case PARSER_TOPLEVEL: + break; + case PARSER_COUNTRY: + parser_country_end (parse_context, element_name); + break; + case PARSER_PROVIDER: + parser_provider_end (parse_context, element_name); + break; + case PARSER_METHOD_GSM: + parser_gsm_end (parse_context, element_name); + break; + case PARSER_METHOD_GSM_APN: + parser_gsm_apn_end (parse_context, element_name); + break; + case PARSER_METHOD_CDMA: + parser_cdma_end (parse_context, element_name); + break; + case PARSER_ERROR: + break; + case PARSER_DONE: + break; + } +} + +static void +parser_text (GMarkupParseContext *context, + const char *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParseContext *parse_context = user_data; + + g_free (parse_context->text_buffer); + parse_context->text_buffer = g_strdup (text); +} + +static const GMarkupParser parser = { + .start_element = parser_start_element, + .end_element = parser_end_element, + .text = parser_text, + .passthrough = NULL, + .error = NULL, +}; + +/*****************************************************************************/ + +static void +finish_parse_context (ParseContext *parse_context, GError *error) +{ + if (parse_context->callback) { + if (error) { + parse_context->callback (NULL, NULL, NULL, NULL, NULL, + NULL, error, + parse_context->user_data); + } else { + parse_context->callback (parse_context->apn, + parse_context->username, + parse_context->password, + parse_context->gateway, + parse_context->auth_method, + parse_context->dns, + error, + parse_context->user_data); + } + } + + g_free (parse_context->mccmnc); + g_markup_parse_context_free (parse_context->ctx); + + g_free (parse_context->text_buffer); + g_free (parse_context->apn); + g_free (parse_context->username); + g_free (parse_context->password); + g_free (parse_context->gateway); + g_free (parse_context->auth_method); + g_slist_free_full (parse_context->dns, g_free); + + g_slice_free (ParseContext, parse_context); +} + +static void +read_next_chunk (GInputStream *stream, ParseContext *parse_context); + +static void +stream_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GInputStream *stream = G_INPUT_STREAM (source_object); + ParseContext *parse_context = user_data; + gssize len; + GError *error = NULL; + + len = g_input_stream_read_finish (stream, res, &error); + if (len == -1) { + g_prefix_error (&error, "Error reading service provider database: "); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (len == 0) { + g_set_error (&error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + "Operator ID '%s' not found in service provider database", + parse_context->mccmnc); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (!g_markup_parse_context_parse (parse_context->ctx, parse_context->buffer, len, &error)) { + g_prefix_error (&error, "Error parsing service provider database: "); + finish_parse_context (parse_context, error); + g_clear_error (&error); + return; + } + + if (parse_context->state == PARSER_DONE) { + finish_parse_context (parse_context, NULL); + return; + } + + read_next_chunk (stream, parse_context); +} + +static void +read_next_chunk (GInputStream *stream, ParseContext *parse_context) +{ + g_input_stream_read_async (stream, + parse_context->buffer, + sizeof (parse_context->buffer), + G_PRIORITY_DEFAULT, + parse_context->cancellable, + stream_read_cb, + parse_context); +} + +static void +file_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GFile *file = G_FILE (source_object); + ParseContext *parse_context = user_data; + GFileInputStream *stream; + gs_free_error GError *error = NULL; + + stream = g_file_read_finish (file, res, &error); + if (!stream) { + g_prefix_error (&error, "Error opening service provider database: "); + finish_parse_context (parse_context, error); + return; + } + + read_next_chunk (G_INPUT_STREAM (stream), parse_context); + + g_object_unref (stream); +} + +/*****************************************************************************/ + +void +nm_service_providers_find_gsm_apn (const char *service_providers, + const char *mccmnc, + GCancellable *cancellable, + NMServiceProvidersGsmApnCallback callback, + gpointer user_data) +{ + GFile *file; + ParseContext *parse_context; + + parse_context = g_slice_new0 (ParseContext); + parse_context->mccmnc = g_strdup (mccmnc); + parse_context->cancellable = cancellable; + parse_context->callback = callback; + parse_context->user_data = user_data; + parse_context->ctx = g_markup_parse_context_new (&parser, 0, parse_context, NULL); + + file = g_file_new_for_path (service_providers); + + g_file_read_async (file, G_PRIORITY_DEFAULT, cancellable, file_read_cb, parse_context); + + g_object_unref (file); +} diff --git a/src/devices/wwan/nm-service-providers.h b/src/devices/wwan/nm-service-providers.h new file mode 100644 index 00000000..35ad2fc1 --- /dev/null +++ b/src/devices/wwan/nm-service-providers.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2019 Red Hat, Inc. + */ + +#ifndef __NETWORKMANAGER_SERVICE_PROVIDERS_H__ +#define __NETWORKMANAGER_SERVICE_PROVIDERS_H__ + +typedef void (*NMServiceProvidersGsmApnCallback) (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data); + +void nm_service_providers_find_gsm_apn (const char *service_providers, + const char *mccmnc, + GCancellable *cancellable, + NMServiceProvidersGsmApnCallback callback, + gpointer user_data); + +#endif /* __NETWORKMANAGER_SERVICE_PROVIDERS_H__ */ diff --git a/src/devices/wwan/nm-wwan-factory.c b/src/devices/wwan/nm-wwan-factory.c index a0e5c160..9a85f936 100644 --- a/src/devices/wwan/nm-wwan-factory.c +++ b/src/devices/wwan/nm-wwan-factory.c @@ -1,19 +1,5 @@ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * +// SPDX-License-Identifier: GPL-2.0+ +/* * Copyright (C) 2014 Red Hat, Inc. */ @@ -77,11 +63,10 @@ modem_added_cb (NMModemManager *manager, gpointer user_data) { NMWwanFactory *self = NM_WWAN_FACTORY (user_data); - NMDevice *device; + gs_unref_object NMDevice *device = NULL; const char *driver; - /* Do nothing if the modem was consumed by some other plugin */ - if (nm_device_factory_emit_component_added (NM_DEVICE_FACTORY (self), G_OBJECT (modem))) + if (nm_modem_is_claimed (modem)) return; driver = nm_modem_get_driver (modem); @@ -90,17 +75,16 @@ modem_added_cb (NMModemManager *manager, * it. The rfcomm port (and thus the modem) gets created automatically * by the Bluetooth code during the connection process. */ - if (driver && strstr (driver, "bluetooth")) { - nm_log_info (LOGD_MB, "ignoring modem '%s' (no associated Bluetooth device)", - nm_modem_get_control_port (modem)); + if ( driver + && strstr (driver, "bluetooth")) { + nm_log_dbg (LOGD_MB, "WWAN factory ignores bluetooth modem '%s' which should be handled by bluetooth plugin", + nm_modem_get_control_port (modem)); return; } /* Make the new modem device */ device = nm_device_modem_new (modem); - g_assert (device); g_signal_emit_by_name (self, NM_DEVICE_FACTORY_DEVICE_ADDED, device); - g_object_unref (device); } static NMDevice * diff --git a/src/devices/wwan/tests/test-service-providers.c b/src/devices/wwan/tests/test-service-providers.c new file mode 100644 index 00000000..33402cd5 --- /dev/null +++ b/src/devices/wwan/tests/test-service-providers.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: LGPL-2.1+ +/* + * Copyright (C) 2019 Red Hat + */ + +#include "nm-default.h" + +#include "nm-service-providers.h" + +#include "nm-test-utils-core.h" + +static void +test_positive_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_no_error (error); + g_assert_cmpstr (apn, ==, "gprs.example.com"); + g_assert_cmpstr (username, ==, "praise"); + g_assert_cmpstr (password, ==, "santa"); + g_assert_cmpstr (gateway, ==, "192.0.2.3"); + g_assert_cmpstr (auth_method, ==, "pap"); + + g_assert_nonnull (dns); + g_assert_cmpstr (dns->data, ==, "192.0.2.2"); + dns = dns->next; + g_assert_nonnull (dns); + g_assert_cmpstr (dns->data, ==, "192.0.2.1"); + g_assert_null (dns->next); +} + +static void +test_positive (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml", + "13337", NULL, test_positive_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +static void +test_negative_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN); +} + +static void +test_negative (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn (NM_BUILD_SRCDIR"/src/devices/wwan/tests/test-service-providers.xml", + "78130", NULL, test_negative_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +static void +test_nonexistent_cb (const char *apn, + const char *username, + const char *password, + const char *gateway, + const char *auth_method, + const GSList *dns, + GError *error, + gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_AGAIN); +} + +static void +test_nonexistent (void) +{ + GMainLoop *loop = g_main_loop_new (NULL, FALSE); + + nm_service_providers_find_gsm_apn ("nonexistent.xml", "13337", NULL, + test_nonexistent_cb, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); +} + +/*****************************************************************************/ + +NMTST_DEFINE (); + +int +main (int argc, char **argv) +{ + nmtst_init_assert_logging (&argc, &argv, "INFO", "DEFAULT"); + + g_test_add_func ("/service-providers/positive", test_positive); + g_test_add_func ("/service-providers/negative", test_negative); + g_test_add_func ("/service-providers/nonexistent", test_nonexistent); + + return g_test_run (); +} + diff --git a/src/devices/wwan/tests/test-service-providers.xml b/src/devices/wwan/tests/test-service-providers.xml new file mode 100644 index 00000000..f0ca2deb --- /dev/null +++ b/src/devices/wwan/tests/test-service-providers.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding='utf-8'?> +<!DOCTYPE serviceproviders SYSTEM "serviceproviders.2.dtd"> + +<serviceproviders format="2.0"> + +<country code="feh"> + <provider> + <name>Sophia</name> + <gsm> + <network-id mcc="666" mnc="999"/> + <apn value="access.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>APN</name> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + </apn> + </gsm> + </provider> +</country> + +<country code="meh"> + <provider> + <name>Demiurge</name> + <gsm> + <network-id mcc="133" mnc="37"/> + <network-id mcc="133" mnc="666"/> + <apn value="mms"> + <usage type="mms"/> + <name>Unsolicited Nudes MMS</name> + <username>mms</username> + <password>mms</password> + <mmsc>http://mms.example.com/</mmsc> + <mmsproxy>192.0.2.1:8080</mmsproxy> + </apn> + <apn value="gprs.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>GPRS</name> + <username>praise</username> + <password>santa</password> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + <gateway>192.0.2.3</gateway> + <authentication method="pap"/> + </apn> + <apn value="second.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>Second</name> + <username>worship</username> + <password>doom</password> + </apn> + </gsm> + </provider> + + <provider> + <name>Personal</name> + <gsm> + <network-id mcc="666" mnc="999"/> + <apn value="access.example.com"> + <plan type="postpaid"/> + <usage type="internet"/> + <name>APN</name> + <dns>192.0.2.1</dns> + <dns>192.0.2.2</dns> + </apn> + </gsm> + </provider> +</country> + +</serviceproviders> + |