mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-03-30 16:30:45 +02:00
If the kernel doesn't tag a modem's ethernet interface with DEVTYPE=wwan then NetworkManager has no idea that's a modem (and cannot be used until connected via the control port). Since DEVTYPE=wwan devices get ignored by NM, so should these interfaces when NM knows they are modems. That got broken at some point for ModemManager1, because the data port isn't read until the modem is connected. NM only looked for and removed the data-port-as-ethernet-device when the modem was added, long before the MM1 data port was found. ModemManager does provide a list of ports owned by the modem though, which we can use at modem addition time to remove an ethernet device that is controled by the modem.
994 lines
28 KiB
C
994 lines
28 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* 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.
|
|
*
|
|
* Copyright (C) 2009 - 2011 Red Hat, Inc.
|
|
* Copyright (C) 2009 Novell, Inc.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "nm-modem.h"
|
|
#include "nm-platform.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "nm-setting-connection.h"
|
|
#include "nm-properties-changed-signal.h"
|
|
#include "nm-logging.h"
|
|
#include "NetworkManagerUtils.h"
|
|
#include "nm-device-private.h"
|
|
#include "nm-dbus-glib-types.h"
|
|
#include "nm-enum-types.h"
|
|
|
|
G_DEFINE_TYPE (NMModem, nm_modem, G_TYPE_OBJECT)
|
|
|
|
#define NM_MODEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_MODEM, NMModemPrivate))
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CONTROL_PORT,
|
|
PROP_DATA_PORT,
|
|
PROP_PATH,
|
|
PROP_UID,
|
|
PROP_DRIVER,
|
|
PROP_IP_METHOD,
|
|
PROP_IP_TIMEOUT,
|
|
PROP_ENABLED,
|
|
PROP_CONNECTED,
|
|
|
|
LAST_PROP
|
|
};
|
|
|
|
typedef struct {
|
|
char *uid;
|
|
char *path;
|
|
char *driver;
|
|
char *control_port;
|
|
char *data_port;
|
|
char *ppp_iface;
|
|
guint32 ip_method;
|
|
|
|
NMPPPManager *ppp_manager;
|
|
|
|
NMActRequest *act_request;
|
|
guint32 secrets_tries;
|
|
guint32 secrets_id;
|
|
|
|
gboolean mm_enabled;
|
|
guint32 mm_ip_timeout;
|
|
gboolean mm_connected;
|
|
|
|
/* PPP stats */
|
|
guint32 in_bytes;
|
|
guint32 out_bytes;
|
|
} NMModemPrivate;
|
|
|
|
enum {
|
|
PPP_STATS,
|
|
PPP_FAILED,
|
|
PREPARE_RESULT,
|
|
IP4_CONFIG_RESULT,
|
|
AUTH_REQUESTED,
|
|
AUTH_RESULT,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
GQuark
|
|
nm_modem_error_quark (void)
|
|
{
|
|
static GQuark quark = 0;
|
|
if (!quark)
|
|
quark = g_quark_from_static_string ("nm-modem-error");
|
|
return quark;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Get/Set enabled/connected */
|
|
|
|
gboolean
|
|
nm_modem_get_mm_enabled (NMModem *self)
|
|
{
|
|
return NM_MODEM_GET_PRIVATE (self)->mm_enabled;
|
|
}
|
|
|
|
void
|
|
nm_modem_set_mm_enabled (NMModem *self,
|
|
gboolean enabled)
|
|
{
|
|
NMModemPrivate *priv;
|
|
|
|
priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
if (priv->mm_enabled != enabled)
|
|
NM_MODEM_GET_CLASS (self)->set_mm_enabled (self, enabled);
|
|
}
|
|
|
|
gboolean
|
|
nm_modem_get_mm_connected (NMModem *self)
|
|
{
|
|
return NM_MODEM_GET_PRIVATE (self)->mm_connected;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* IP method PPP */
|
|
|
|
static void
|
|
ppp_state_changed (NMPPPManager *ppp_manager, NMPPPStatus status, gpointer user_data)
|
|
{
|
|
switch (status) {
|
|
case NM_PPP_STATUS_DISCONNECT:
|
|
g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_DISCONNECT);
|
|
break;
|
|
case NM_PPP_STATUS_DEAD:
|
|
g_signal_emit (NM_MODEM (user_data), signals[PPP_FAILED], 0, NM_DEVICE_STATE_REASON_PPP_FAILED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ppp_ip4_config (NMPPPManager *ppp_manager,
|
|
const char *iface,
|
|
NMIP4Config *config,
|
|
gpointer user_data)
|
|
{
|
|
NMModem *self = NM_MODEM (user_data);
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
guint32 i, num;
|
|
guint32 bad_dns1 = htonl (0x0A0B0C0D);
|
|
guint32 good_dns1 = htonl (0x04020201); /* GTE nameserver */
|
|
guint32 bad_dns2 = htonl (0x0A0B0C0E);
|
|
guint32 good_dns2 = htonl (0x04020202); /* GTE nameserver */
|
|
gboolean dns_workaround = FALSE;
|
|
|
|
/* Notify about the new data port to use */
|
|
g_free (priv->ppp_iface);
|
|
priv->ppp_iface = g_strdup (iface);
|
|
g_object_notify (G_OBJECT (self), NM_MODEM_DATA_PORT);
|
|
|
|
/* Work around a PPP bug (#1732) which causes many mobile broadband
|
|
* providers to return 10.11.12.13 and 10.11.12.14 for the DNS servers.
|
|
* Apparently fixed in ppp-2.4.5 but we've had some reports that this is
|
|
* not the case.
|
|
*
|
|
* http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=2e09ef6886bbf00bc5a9a641110f801e372ffde6
|
|
* http://git.ozlabs.org/?p=ppp.git;a=commitdiff_plain;h=f8191bf07df374f119a07910a79217c7618f113e
|
|
*/
|
|
|
|
num = nm_ip4_config_get_num_nameservers (config);
|
|
if (num == 2) {
|
|
gboolean found1 = FALSE, found2 = FALSE;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
guint32 ns = nm_ip4_config_get_nameserver (config, i);
|
|
|
|
if (ns == bad_dns1)
|
|
found1 = TRUE;
|
|
else if (ns == bad_dns2)
|
|
found2 = TRUE;
|
|
}
|
|
|
|
/* Be somewhat conservative about substitutions; the "bad" nameservers
|
|
* could actually be valid in some cases, so only substitute if ppp
|
|
* returns *only* the two bad nameservers.
|
|
*/
|
|
dns_workaround = (found1 && found2);
|
|
}
|
|
|
|
if (!num || dns_workaround) {
|
|
nm_log_warn (LOGD_PPP, "compensating for invalid PPP-provided nameservers");
|
|
nm_ip4_config_reset_nameservers (config);
|
|
nm_ip4_config_add_nameserver (config, good_dns1);
|
|
nm_ip4_config_add_nameserver (config, good_dns2);
|
|
}
|
|
|
|
g_signal_emit (self, signals[IP4_CONFIG_RESULT], 0, config, NULL);
|
|
}
|
|
|
|
static void
|
|
ppp_stats (NMPPPManager *ppp_manager,
|
|
guint32 in_bytes,
|
|
guint32 out_bytes,
|
|
gpointer user_data)
|
|
{
|
|
NMModem *self = NM_MODEM (user_data);
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
if (priv->in_bytes != in_bytes || priv->out_bytes != out_bytes) {
|
|
priv->in_bytes = in_bytes;
|
|
priv->out_bytes = out_bytes;
|
|
|
|
g_signal_emit (self, signals[PPP_STATS], 0, in_bytes, out_bytes);
|
|
}
|
|
}
|
|
|
|
static NMActStageReturn
|
|
ppp_stage3_ip4_config_start (NMModem *self,
|
|
NMActRequest *req,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
const char *ppp_name = NULL;
|
|
GError *error = NULL;
|
|
NMActStageReturn ret;
|
|
guint ip_timeout = 20;
|
|
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
|
|
g_return_val_if_fail (NM_IS_ACT_REQUEST (req), NM_ACT_STAGE_RETURN_FAILURE);
|
|
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
|
|
if (NM_MODEM_GET_CLASS (self)->get_user_pass) {
|
|
NMConnection *connection = nm_act_request_get_connection (req);
|
|
|
|
g_assert (connection);
|
|
if (!NM_MODEM_GET_CLASS (self)->get_user_pass (self, connection, &ppp_name, NULL))
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
|
}
|
|
|
|
/* Check if ModemManager requested a specific IP timeout to be used. If 0 reported,
|
|
* use the default one (20s) */
|
|
if (priv->mm_ip_timeout > 0) {
|
|
nm_log_info (LOGD_PPP, "using modem-specified IP timeout: %u seconds",
|
|
priv->mm_ip_timeout);
|
|
ip_timeout = priv->mm_ip_timeout;
|
|
}
|
|
|
|
priv->ppp_manager = nm_ppp_manager_new (priv->data_port);
|
|
if (nm_ppp_manager_start (priv->ppp_manager, req, ppp_name, ip_timeout, &error)) {
|
|
g_signal_connect (priv->ppp_manager, "state-changed",
|
|
G_CALLBACK (ppp_state_changed),
|
|
self);
|
|
g_signal_connect (priv->ppp_manager, "ip4-config",
|
|
G_CALLBACK (ppp_ip4_config),
|
|
self);
|
|
g_signal_connect (priv->ppp_manager, "stats",
|
|
G_CALLBACK (ppp_stats),
|
|
self);
|
|
|
|
ret = NM_ACT_STAGE_RETURN_POSTPONE;
|
|
} else {
|
|
nm_log_err (LOGD_PPP, "error starting PPP: (%d) %s",
|
|
error ? error->code : -1,
|
|
error && error->message ? error->message : "(unknown)");
|
|
g_error_free (error);
|
|
|
|
g_object_unref (priv->ppp_manager);
|
|
priv->ppp_manager = NULL;
|
|
|
|
*reason = NM_DEVICE_STATE_REASON_PPP_START_FAILED;
|
|
ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMActStageReturn
|
|
nm_modem_stage3_ip4_config_start (NMModem *self,
|
|
NMDevice *device,
|
|
NMDeviceClass *device_class,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
NMModemPrivate *priv;
|
|
NMActRequest *req;
|
|
NMActStageReturn ret;
|
|
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NM_ACT_STAGE_RETURN_FAILURE);
|
|
g_return_val_if_fail (NM_IS_DEVICE (device), NM_ACT_STAGE_RETURN_FAILURE);
|
|
g_return_val_if_fail (NM_IS_DEVICE_CLASS (device_class), NM_ACT_STAGE_RETURN_FAILURE);
|
|
g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
|
|
|
|
req = nm_device_get_act_request (device);
|
|
g_assert (req);
|
|
|
|
priv = NM_MODEM_GET_PRIVATE (self);
|
|
switch (priv->ip_method) {
|
|
case MM_MODEM_IP_METHOD_PPP:
|
|
ret = ppp_stage3_ip4_config_start (self, req, reason);
|
|
break;
|
|
case MM_MODEM_IP_METHOD_STATIC:
|
|
ret = NM_MODEM_GET_CLASS (self)->static_stage3_ip4_config_start (self, req, reason);
|
|
break;
|
|
case MM_MODEM_IP_METHOD_DHCP:
|
|
ret = device_class->act_stage3_ip4_config_start (device, NULL, reason);
|
|
break;
|
|
default:
|
|
nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
|
|
ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
nm_modem_ip4_pre_commit (NMModem *modem,
|
|
NMDevice *device,
|
|
NMIP4Config *config)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (modem);
|
|
|
|
/* If the modem has an ethernet-type data interface (ie, not PPP and thus
|
|
* not point-to-point) and IP config has a /32 prefix, then we assume that
|
|
* ARP will be pointless and we turn it off.
|
|
*/
|
|
if ( priv->ip_method == MM_MODEM_IP_METHOD_STATIC
|
|
|| priv->ip_method == MM_MODEM_IP_METHOD_DHCP) {
|
|
const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, 0);
|
|
|
|
g_assert (address);
|
|
if (address->plen == 32)
|
|
nm_platform_link_set_noarp (nm_device_get_ip_ifindex (device));
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMActStageReturn
|
|
nm_modem_stage3_ip6_config_start (NMModem *self,
|
|
NMDevice *device,
|
|
NMDeviceClass *device_class,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
/* FIXME: We don't support IPv6 on modems quite yet... */
|
|
nm_device_activate_schedule_ip6_config_timeout (device);
|
|
return NM_ACT_STAGE_RETURN_POSTPONE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
cancel_get_secrets (NMModem *self)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
if (priv->secrets_id) {
|
|
nm_act_request_cancel_secrets (priv->act_request, priv->secrets_id);
|
|
priv->secrets_id = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
modem_secrets_cb (NMActRequest *req,
|
|
guint32 call_id,
|
|
NMConnection *connection,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
NMModem *self = NM_MODEM (user_data);
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
g_return_if_fail (call_id == priv->secrets_id);
|
|
|
|
priv->secrets_id = 0;
|
|
|
|
if (error)
|
|
nm_log_warn (LOGD_MB, "%s", error->message);
|
|
|
|
g_signal_emit (self, signals[AUTH_RESULT], 0, error);
|
|
}
|
|
|
|
gboolean
|
|
nm_modem_get_secrets (NMModem *self,
|
|
const char *setting_name,
|
|
gboolean request_new,
|
|
const char *hint)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
|
|
|
|
cancel_get_secrets (self);
|
|
|
|
if (request_new)
|
|
flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
|
|
priv->secrets_id = nm_act_request_get_secrets (priv->act_request,
|
|
setting_name,
|
|
flags,
|
|
hint,
|
|
modem_secrets_cb,
|
|
self);
|
|
if (priv->secrets_id)
|
|
g_signal_emit (self, signals[AUTH_REQUESTED], 0);
|
|
|
|
return !!(priv->secrets_id);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static NMActStageReturn
|
|
act_stage1_prepare (NMModem *modem,
|
|
NMConnection *connection,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
*reason = NM_DEVICE_STATE_REASON_UNKNOWN;
|
|
return NM_ACT_STAGE_RETURN_FAILURE;
|
|
}
|
|
|
|
NMActStageReturn
|
|
nm_modem_act_stage1_prepare (NMModem *self,
|
|
NMActRequest *req,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
NMActStageReturn ret;
|
|
GPtrArray *hints = NULL;
|
|
const char *setting_name = NULL;
|
|
NMSettingsGetSecretsFlags flags = NM_SETTINGS_GET_SECRETS_FLAG_ALLOW_INTERACTION;
|
|
NMConnection *connection;
|
|
|
|
if (priv->act_request)
|
|
g_object_unref (priv->act_request);
|
|
priv->act_request = g_object_ref (req);
|
|
|
|
connection = nm_act_request_get_connection (req);
|
|
g_assert (connection);
|
|
|
|
setting_name = nm_connection_need_secrets (connection, &hints);
|
|
if (!setting_name) {
|
|
/* Ready to connect */
|
|
g_assert (!hints);
|
|
return NM_MODEM_GET_CLASS (self)->act_stage1_prepare (self, connection, reason);
|
|
}
|
|
|
|
/* Secrets required... */
|
|
if (priv->secrets_tries++)
|
|
flags |= NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW;
|
|
|
|
priv->secrets_id = nm_act_request_get_secrets (req,
|
|
setting_name,
|
|
flags,
|
|
hints ? g_ptr_array_index (hints, 0) : NULL,
|
|
modem_secrets_cb,
|
|
self);
|
|
if (priv->secrets_id) {
|
|
g_signal_emit (self, signals[AUTH_REQUESTED], 0);
|
|
ret = NM_ACT_STAGE_RETURN_POSTPONE;
|
|
} else {
|
|
*reason = NM_DEVICE_STATE_REASON_NO_SECRETS;
|
|
ret = NM_ACT_STAGE_RETURN_FAILURE;
|
|
}
|
|
|
|
if (hints)
|
|
g_ptr_array_free (hints, TRUE);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMActStageReturn
|
|
nm_modem_act_stage2_config (NMModem *self,
|
|
NMActRequest *req,
|
|
NMDeviceStateReason *reason)
|
|
{
|
|
NMModemPrivate *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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_modem_check_connection_compatible (NMModem *self,
|
|
NMConnection *connection,
|
|
GError **error)
|
|
{
|
|
if (NM_MODEM_GET_CLASS (self)->check_connection_compatible)
|
|
return NM_MODEM_GET_CLASS (self)->check_connection_compatible (self, connection, error);
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_modem_complete_connection (NMModem *self,
|
|
NMConnection *connection,
|
|
const GSList *existing_connections,
|
|
GError **error)
|
|
{
|
|
if (NM_MODEM_GET_CLASS (self)->complete_connection)
|
|
return NM_MODEM_GET_CLASS (self)->complete_connection (self, connection, existing_connections, error);
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
deactivate (NMModem *self, NMDevice *device)
|
|
{
|
|
NMModemPrivate *priv;
|
|
int ifindex;
|
|
|
|
g_return_if_fail (NM_IS_MODEM (self));
|
|
g_return_if_fail (NM_IS_DEVICE (device));
|
|
|
|
priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
priv->secrets_tries = 0;
|
|
|
|
if (priv->act_request) {
|
|
cancel_get_secrets (self);
|
|
g_object_unref (priv->act_request);
|
|
priv->act_request = NULL;
|
|
}
|
|
|
|
priv->in_bytes = priv->out_bytes = 0;
|
|
|
|
if (priv->ppp_manager) {
|
|
g_object_unref (priv->ppp_manager);
|
|
priv->ppp_manager = NULL;
|
|
}
|
|
|
|
switch (priv->ip_method) {
|
|
case MM_MODEM_IP_METHOD_PPP:
|
|
break;
|
|
case MM_MODEM_IP_METHOD_STATIC:
|
|
case MM_MODEM_IP_METHOD_DHCP:
|
|
ifindex = nm_device_get_ip_ifindex (device);
|
|
if (ifindex > 0) {
|
|
nm_platform_route_flush (ifindex);
|
|
nm_platform_address_flush (ifindex);
|
|
nm_platform_link_set_down (ifindex);
|
|
}
|
|
break;
|
|
default:
|
|
nm_log_err (LOGD_MB, "unknown IP method %d", priv->ip_method);
|
|
break;
|
|
}
|
|
|
|
g_free (priv->ppp_iface);
|
|
priv->ppp_iface = NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_modem_deactivate (NMModem *self, NMDevice *device)
|
|
{
|
|
NM_MODEM_GET_CLASS (self)->deactivate (self, device);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_modem_device_state_changed (NMModem *self,
|
|
NMDeviceState new_state,
|
|
NMDeviceState old_state,
|
|
NMDeviceStateReason reason)
|
|
{
|
|
gboolean was_connected = FALSE, warn = TRUE;
|
|
NMModemPrivate *priv;
|
|
|
|
g_return_if_fail (NM_IS_MODEM (self));
|
|
|
|
if (old_state >= NM_DEVICE_STATE_PREPARE && old_state <= NM_DEVICE_STATE_DEACTIVATING)
|
|
was_connected = TRUE;
|
|
|
|
priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
/* Make sure we don't leave the serial device open */
|
|
switch (new_state) {
|
|
case NM_DEVICE_STATE_UNMANAGED:
|
|
case NM_DEVICE_STATE_UNAVAILABLE:
|
|
case NM_DEVICE_STATE_DISCONNECTED:
|
|
case NM_DEVICE_STATE_FAILED:
|
|
if (priv->act_request) {
|
|
cancel_get_secrets (self);
|
|
g_object_unref (priv->act_request);
|
|
priv->act_request = NULL;
|
|
}
|
|
|
|
if (was_connected) {
|
|
/* Don't bother warning on FAILED since the modem is already gone */
|
|
if (new_state == NM_DEVICE_STATE_FAILED)
|
|
warn = FALSE;
|
|
NM_MODEM_GET_CLASS (self)->disconnect (self, warn);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nm_modem_get_uid (NMModem *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NULL);
|
|
|
|
return NM_MODEM_GET_PRIVATE (self)->uid;
|
|
}
|
|
|
|
const char *
|
|
nm_modem_get_path (NMModem *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NULL);
|
|
|
|
return NM_MODEM_GET_PRIVATE (self)->path;
|
|
}
|
|
|
|
const char *
|
|
nm_modem_get_driver (NMModem *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NULL);
|
|
|
|
return NM_MODEM_GET_PRIVATE (self)->driver;
|
|
}
|
|
|
|
const char *
|
|
nm_modem_get_control_port (NMModem *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NULL);
|
|
|
|
return NM_MODEM_GET_PRIVATE (self)->control_port;
|
|
}
|
|
|
|
const char *
|
|
nm_modem_get_data_port (NMModem *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_MODEM (self), NULL);
|
|
|
|
/* The ppp_iface takes precedence over the data interface when PPP is used,
|
|
* since data_iface is the TTY over which PPP is run, and that TTY can't
|
|
* do IP. The caller really wants the thing that's doing IP.
|
|
*/
|
|
return NM_MODEM_GET_PRIVATE (self)->ppp_iface ?
|
|
NM_MODEM_GET_PRIVATE (self)->ppp_iface : NM_MODEM_GET_PRIVATE (self)->data_port;
|
|
}
|
|
|
|
gboolean
|
|
nm_modem_owns_port (NMModem *self, const char *iface)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (self);
|
|
|
|
g_return_val_if_fail (iface != NULL, FALSE);
|
|
|
|
if (NM_MODEM_GET_CLASS (self)->owns_port)
|
|
return NM_MODEM_GET_CLASS (self)->owns_port (self, iface);
|
|
|
|
/* Fall back to data/control ports */
|
|
if (priv->ppp_iface && (strcmp (priv->ppp_iface, iface) == 0))
|
|
return TRUE;
|
|
if (priv->data_port && (strcmp (priv->data_port, iface) == 0))
|
|
return TRUE;
|
|
if (priv->control_port && (strcmp (priv->control_port, iface) == 0))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_modem_get_capabilities (NMModem *self,
|
|
NMDeviceModemCapabilities *modem_caps,
|
|
NMDeviceModemCapabilities *current_caps)
|
|
{
|
|
g_return_if_fail (NM_IS_MODEM (self));
|
|
|
|
NM_MODEM_GET_CLASS (self)->get_capabilities (self, modem_caps, current_caps);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_modem_init (NMModem *self)
|
|
{
|
|
}
|
|
|
|
static GObject*
|
|
constructor (GType type,
|
|
guint n_construct_params,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
GObject *object;
|
|
NMModemPrivate *priv;
|
|
|
|
object = G_OBJECT_CLASS (nm_modem_parent_class)->constructor (type,
|
|
n_construct_params,
|
|
construct_params);
|
|
if (!object)
|
|
return NULL;
|
|
|
|
priv = NM_MODEM_GET_PRIVATE (object);
|
|
|
|
if (!priv->data_port && !priv->control_port) {
|
|
nm_log_err (LOGD_HW, "neither modem command nor data interface provided");
|
|
goto err;
|
|
}
|
|
|
|
if (!priv->path) {
|
|
nm_log_err (LOGD_HW, "D-Bus path not provided");
|
|
goto err;
|
|
}
|
|
|
|
return object;
|
|
|
|
err:
|
|
g_object_unref (object);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
g_value_set_string (value, priv->path);
|
|
break;
|
|
case PROP_DRIVER:
|
|
g_value_set_string (value, priv->driver);
|
|
break;
|
|
case PROP_CONTROL_PORT:
|
|
g_value_set_string (value, priv->control_port);
|
|
break;
|
|
case PROP_DATA_PORT:
|
|
g_value_set_string (value, nm_modem_get_data_port (NM_MODEM (object)));
|
|
break;
|
|
case PROP_UID:
|
|
g_value_set_string (value, priv->uid);
|
|
break;
|
|
case PROP_IP_METHOD:
|
|
g_value_set_uint (value, priv->ip_method);
|
|
break;
|
|
case PROP_IP_TIMEOUT:
|
|
g_value_set_uint (value, priv->mm_ip_timeout);
|
|
break;
|
|
case PROP_ENABLED:
|
|
g_value_set_boolean (value, priv->mm_enabled);
|
|
break;
|
|
case PROP_CONNECTED:
|
|
g_value_set_boolean (value, priv->mm_connected);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_property (GObject *object, guint prop_id,
|
|
const GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_PATH:
|
|
/* Construct only */
|
|
priv->path = g_value_dup_string (value);
|
|
break;
|
|
case PROP_DRIVER:
|
|
/* Construct only */
|
|
priv->driver = g_value_dup_string (value);
|
|
break;
|
|
case PROP_CONTROL_PORT:
|
|
priv->control_port = g_value_dup_string (value);
|
|
break;
|
|
case PROP_DATA_PORT:
|
|
priv->data_port = g_value_dup_string (value);
|
|
break;
|
|
case PROP_UID:
|
|
/* Construct only */
|
|
priv->uid = g_value_dup_string (value);
|
|
break;
|
|
case PROP_IP_METHOD:
|
|
priv->ip_method = g_value_get_uint (value);
|
|
break;
|
|
case PROP_IP_TIMEOUT:
|
|
priv->mm_ip_timeout = g_value_get_uint (value);
|
|
break;
|
|
case PROP_ENABLED:
|
|
priv->mm_enabled = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_CONNECTED:
|
|
priv->mm_connected = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
|
|
|
|
if (priv->act_request) {
|
|
g_object_unref (priv->act_request);
|
|
priv->act_request = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (nm_modem_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
NMModemPrivate *priv = NM_MODEM_GET_PRIVATE (object);
|
|
|
|
g_free (priv->uid);
|
|
g_free (priv->path);
|
|
g_free (priv->driver);
|
|
g_free (priv->control_port);
|
|
g_free (priv->data_port);
|
|
|
|
G_OBJECT_CLASS (nm_modem_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
nm_modem_class_init (NMModemClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
g_type_class_add_private (object_class, sizeof (NMModemPrivate));
|
|
|
|
/* Virtual methods */
|
|
object_class->constructor = constructor;
|
|
object_class->set_property = set_property;
|
|
object_class->get_property = get_property;
|
|
object_class->dispose = dispose;
|
|
object_class->finalize = finalize;
|
|
|
|
klass->act_stage1_prepare = act_stage1_prepare;
|
|
klass->deactivate = deactivate;
|
|
|
|
/* Properties */
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_UID,
|
|
g_param_spec_string (NM_MODEM_UID,
|
|
"UID",
|
|
"Modem unique ID",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_PATH,
|
|
g_param_spec_string (NM_MODEM_PATH,
|
|
"DBus path",
|
|
"DBus path",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_DRIVER,
|
|
g_param_spec_string (NM_MODEM_DRIVER,
|
|
"Driver",
|
|
"Driver",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_CONTROL_PORT,
|
|
g_param_spec_string (NM_MODEM_CONTROL_PORT,
|
|
"Control port",
|
|
"The port controlling the modem",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_DATA_PORT,
|
|
g_param_spec_string (NM_MODEM_DATA_PORT,
|
|
"Data port",
|
|
"The port to connect to",
|
|
NULL,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_IP_METHOD,
|
|
g_param_spec_uint (NM_MODEM_IP_METHOD,
|
|
"IP method",
|
|
"IP method",
|
|
MM_MODEM_IP_METHOD_PPP,
|
|
MM_MODEM_IP_METHOD_DHCP,
|
|
MM_MODEM_IP_METHOD_PPP,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_IP_TIMEOUT,
|
|
g_param_spec_uint (NM_MODEM_IP_TIMEOUT,
|
|
"IP timeout",
|
|
"IP timeout",
|
|
0, 360, 20,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_ENABLED,
|
|
g_param_spec_boolean (NM_MODEM_ENABLED,
|
|
"Enabled",
|
|
"Enabled",
|
|
TRUE,
|
|
G_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_CONNECTED,
|
|
g_param_spec_boolean (NM_MODEM_CONNECTED,
|
|
"Connected",
|
|
"Connected",
|
|
TRUE,
|
|
G_PARAM_READWRITE));
|
|
|
|
/* Signals */
|
|
|
|
signals[PPP_STATS] =
|
|
g_signal_new ("ppp-stats",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, ppp_stats),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2,
|
|
G_TYPE_UINT, G_TYPE_UINT);
|
|
|
|
signals[PPP_FAILED] =
|
|
g_signal_new ("ppp-failed",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, ppp_failed),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_UINT);
|
|
|
|
signals[IP4_CONFIG_RESULT] =
|
|
g_signal_new (NM_MODEM_IP4_CONFIG_RESULT,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, ip4_config_result),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_POINTER);
|
|
|
|
signals[PREPARE_RESULT] =
|
|
g_signal_new (NM_MODEM_PREPARE_RESULT,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, prepare_result),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT);
|
|
|
|
signals[AUTH_REQUESTED] =
|
|
g_signal_new (NM_MODEM_AUTH_REQUESTED,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, auth_requested),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
signals[AUTH_RESULT] =
|
|
g_signal_new (NM_MODEM_AUTH_RESULT,
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMModemClass, auth_result),
|
|
NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
|
|
|
dbus_g_error_domain_register (NM_MODEM_ERROR,
|
|
NM_DBUS_INTERFACE_DEVICE_MODEM,
|
|
NM_TYPE_MODEM_ERROR);
|
|
}
|