NetworkManager/src/nm-device.c
Dan Williams cd00315325 2007-08-26 Dan Williams <dcbw@redhat.com>
Convert to using interface indexes as the primary method of identifying
	devices inside NetworkManager.  Indexes are (?) stable, but devices can
	be renamed at any time.  Device object paths now refer to the device
	index rather than the name, and you can map those two manually if you like
	by looking in the /sys/class/net/<name>/ifindex file.  Also moves most
	netlink-related code to nm-netlink.c, and cleans up nm-netlink-monitor.c
	to use interface indexes rather than names.



git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@2731 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
2007-08-26 15:55:27 +00:00

1644 lines
42 KiB
C

/* NetworkManager -- Network link manager
*
* Dan Williams <dcbw@redhat.com>
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* (C) Copyright 2005 Red Hat, Inc.
*/
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
#include <netinet/in.h>
#include <string.h>
#include "nm-device-interface.h"
#include "nm-device.h"
#include "nm-device-private.h"
#include "NetworkManagerDbus.h"
#include "NetworkManagerPolicy.h"
#include "NetworkManagerUtils.h"
#include "NetworkManagerSystem.h"
#include "nm-dhcp-manager.h"
#include "nm-dbus-manager.h"
#include "nm-dbus-nmi.h"
#include "nm-utils.h"
#include "autoip.h"
#include "nm-netlink.h"
#define NM_ACT_REQUEST_IP4_CONFIG "nm-act-request-ip4-config"
static void device_interface_init (NMDeviceInterface *device_interface_class);
G_DEFINE_TYPE_EXTENDED (NMDevice, nm_device, G_TYPE_OBJECT,
G_TYPE_FLAG_ABSTRACT,
G_IMPLEMENT_INTERFACE (NM_TYPE_DEVICE_INTERFACE,
device_interface_init))
#define NM_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE, NMDevicePrivate))
struct _NMDevicePrivate
{
gboolean dispose_has_run;
gboolean initialized;
NMDeviceState state;
char * dbus_path;
char * udi;
int index; /* Should always stay the same over lifetime of device */
char * iface; /* may change, could be renamed by user */
NMDeviceType type;
guint32 capabilities;
char * driver;
gboolean link_active;
guint32 ip4_address;
struct in6_addr ip6_address;
NMData * app_data;
NMActRequest * act_request;
guint act_source_id;
/* IP configuration info */
void * system_config_data; /* Distro-specific config data (parsed config file, etc) */
NMIP4Config * ip4_config; /* Config from DHCP, PPP, or system config files */
NMDHCPManager * dhcp_manager;
gulong dhcp_state_sigid;
gulong dhcp_timeout_sigid;
};
static void nm_device_activate (NMDeviceInterface *device,
NMConnection *connection,
gboolean user_requested);
static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self);
static void nm_device_deactivate (NMDeviceInterface *device);
static void
nm_device_set_address (NMDevice *device)
{
if (NM_DEVICE_GET_CLASS (device)->set_hw_address)
NM_DEVICE_GET_CLASS (device)->set_hw_address (device);
}
static void
device_interface_init (NMDeviceInterface *device_interface_class)
{
/* interface implementation */
device_interface_class->activate = nm_device_activate;
device_interface_class->deactivate = nm_device_deactivate;
}
static void
nm_device_init (NMDevice * self)
{
self->priv = NM_DEVICE_GET_PRIVATE (self);
self->priv->dispose_has_run = FALSE;
self->priv->initialized = FALSE;
self->priv->udi = NULL;
self->priv->iface = NULL;
self->priv->index = G_MAXUINT32;
self->priv->type = DEVICE_TYPE_UNKNOWN;
self->priv->capabilities = NM_DEVICE_CAP_NONE;
self->priv->driver = NULL;
self->priv->link_active = FALSE;
self->priv->ip4_address = 0;
memset (&self->priv->ip6_address, 0, sizeof (struct in6_addr));
self->priv->app_data = NULL;
self->priv->act_source_id = 0;
self->priv->system_config_data = NULL;
self->priv->ip4_config = NULL;
self->priv->state = NM_DEVICE_STATE_DISCONNECTED;
}
static GObject*
constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
{
GObject *object;
NMDevice *dev;
NMDevicePrivate *priv;
NMDBusManager *manager;
object = G_OBJECT_CLASS (nm_device_parent_class)->constructor (type,
n_construct_params,
construct_params);
if (!object)
return NULL;
dev = NM_DEVICE (object);
priv = NM_DEVICE_GET_PRIVATE (dev);
if (priv->index == G_MAXUINT32) {
nm_warning ("Interface index is a required constructor property.");
goto error;
}
priv->iface = nm_netlink_index_to_iface (priv->index);
if (priv->iface == NULL) {
nm_warning ("(%d): Couldn't get interface name for device, ignoring.",
nm_device_get_index (dev));
goto error;
}
priv->capabilities |= NM_DEVICE_GET_CLASS (dev)->get_generic_capabilities (dev);
if (!(priv->capabilities & NM_DEVICE_CAP_NM_SUPPORTED)) {
nm_warning ("(%s): Device unsupported, ignoring.",
nm_device_get_iface (dev));
goto error;
}
/* Grab IP config data for this device from the system configuration files */
priv->system_config_data = nm_system_device_get_system_config (dev, priv->app_data);
/* Allow distributions to flag devices as disabled */
if (nm_system_device_get_disabled (dev)) {
nm_warning ("(%s): Device otherwise managed, ignoring.",
nm_device_get_iface (dev));
goto error;
}
nm_print_device_capabilities (dev);
manager = nm_dbus_manager_get ();
priv->dbus_path = g_strdup_printf ("%s/%d",
NM_DBUS_PATH_DEVICE,
nm_device_get_index (dev));
if (priv->dbus_path == NULL) {
nm_warning ("(%s): Not enough memory to initialize device.",
nm_device_get_iface (dev));
goto error;
}
dbus_g_connection_register_g_object (nm_dbus_manager_get_connection (manager),
nm_device_get_dbus_path (dev),
object);
priv->initialized = TRUE;
return object;
error:
g_object_unref (dev);
return NULL;
}
static gboolean
real_is_up (NMDevice *self)
{
NMSock * sk;
struct ifreq ifr;
int err;
const char *iface;
iface = nm_device_get_iface (self);
if ((sk = nm_dev_sock_open (iface, DEV_GENERAL, __FUNCTION__, NULL)) == NULL)
return FALSE;
/* Get device's flags */
strncpy (ifr.ifr_name, iface, sizeof (ifr.ifr_name) - 1);
nm_ioctl_info ("%s: About to GET IFFLAGS.", iface);
err = ioctl (nm_dev_sock_get_fd (sk), SIOCGIFFLAGS, &ifr);
nm_ioctl_info ("%s: Done with GET IFFLAGS.", iface);
nm_dev_sock_close (sk);
if (!err)
return (!((ifr.ifr_flags^IFF_UP) & IFF_UP));
if (errno != ENODEV) {
nm_warning ("%s: could not get flags for device %s. errno = %d",
__func__, iface, errno);
}
return FALSE;
}
static guint32
real_get_generic_capabilities (NMDevice *dev)
{
return 0;
}
const char *
nm_device_get_dbus_path (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->dbus_path;
}
const char *
nm_device_get_udi (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->udi;
}
guint32
nm_device_get_index (NMDevice *self)
{
g_return_val_if_fail (self != NULL, G_MAXUINT32);
return self->priv->index;
}
/*
* Get/set functions for iface
*/
const char *
nm_device_get_iface (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->iface;
}
/*
* Get/set functions for driver
*/
const char *
nm_device_get_driver (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->driver;
}
/*
* Get/set functions for type
*/
NMDeviceType
nm_device_get_device_type (NMDevice *self)
{
g_return_val_if_fail (NM_IS_DEVICE (self), DEVICE_TYPE_UNKNOWN);
return self->priv->type;
}
void
nm_device_set_device_type (NMDevice *dev, NMDeviceType type)
{
g_return_if_fail (NM_IS_DEVICE (dev));
g_return_if_fail (NM_DEVICE_GET_PRIVATE (dev)->type == DEVICE_TYPE_UNKNOWN);
NM_DEVICE_GET_PRIVATE (dev)->type = type;
}
/*
* Accessor for capabilities
*/
guint32
nm_device_get_capabilities (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE);
return self->priv->capabilities;
}
/*
* Accessor for type-specific capabilities
*/
guint32
nm_device_get_type_capabilities (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NM_DEVICE_CAP_NONE);
return NM_DEVICE_GET_CLASS (self)->get_type_capabilities (self);
}
static guint32
real_get_type_capabilities (NMDevice *self)
{
return NM_DEVICE_CAP_NONE;
}
/*
* nm_device_get_app_data
*
*/
struct NMData *
nm_device_get_app_data (NMDevice *self)
{
g_return_val_if_fail (self != NULL, FALSE);
return self->priv->app_data;
}
/*
* nm_device_get_act_request
*
* Return the devices activation request, if any.
*
*/
NMActRequest *
nm_device_get_act_request (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->act_request;
}
/*
* Get/set functions for link_active
*/
gboolean
nm_device_has_active_link (NMDevice *self)
{
g_return_val_if_fail (self != NULL, FALSE);
return self->priv->link_active;
}
void
nm_device_set_active_link (NMDevice *self,
const gboolean link_active)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
if (priv->link_active != link_active) {
priv->link_active = link_active;
g_signal_emit_by_name (self, "carrier-changed", link_active);
}
}
/*
* nm_device_activate_stage1_device_prepare
*
* Prepare for device activation
*
*/
static gboolean
nm_device_activate_stage1_device_prepare (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
const char * iface;
NMActStageReturn ret;
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE);
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
goto out;
} else if (ret == NM_ACT_STAGE_RETURN_FAILURE) {
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
nm_device_activate_schedule_stage2_device_config (self);
out:
nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage1_device_prepare
*
* Prepare a device for activation
*
*/
void
nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
priv->act_source_id = g_idle_add (nm_device_activate_stage1_device_prepare, self);
nm_info ("Activation (%s) Stage 1 of 5 (Device Prepare) scheduled...",
nm_device_get_iface (self));
}
static NMActStageReturn
real_act_stage1_prepare (NMDevice *dev)
{
/* Nothing to do */
return NM_ACT_STAGE_RETURN_SUCCESS;
}
static NMActStageReturn
real_act_stage2_config (NMDevice *dev)
{
/* Nothing to do */
return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*
* nm_device_activate_stage2_device_config
*
* Determine device parameters and set those on the device, ie
* for wireless devices, set SSID, keys, etc.
*
*/
static gboolean
nm_device_activate_stage2_device_config (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
const char * iface;
NMActStageReturn ret;
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG);
if (!nm_device_bring_up (self, FALSE)) {
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
goto out;
else if (ret == NM_ACT_STAGE_RETURN_FAILURE)
{
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
nm_info ("Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface);
nm_device_activate_schedule_stage3_ip_config_start (self);
out:
nm_info ("Activation (%s) Stage 2 of 5 (Device Configure) complete.", iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage2_device_config
*
* Schedule setup of the hardware device
*
*/
void
nm_device_activate_schedule_stage2_device_config (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
priv->act_source_id = g_idle_add (nm_device_activate_stage2_device_config, self);
nm_info ("Activation (%s) Stage 2 of 5 (Device Configure) scheduled...",
nm_device_get_iface (self));
}
static NMActStageReturn
real_act_stage3_ip_config_start (NMDevice *self)
{
NMSettingIP4Config *setting;
NMActRequest *req;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
req = nm_device_get_act_request (self);
setting = (NMSettingIP4Config *) nm_connection_get_setting (nm_act_request_get_connection (req), "ipv4");
/* If we did not receive IP4 configuration information, default to DHCP */
if (!setting || setting->manual == FALSE) {
/* Begin a DHCP transaction on the interface */
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
gboolean success;
nm_device_set_use_dhcp (self, TRUE);
/* DHCP manager will cancel any transaction already in progress and we do not
want to cancel this activation if we get "down" state from that. */
g_signal_handler_block (priv->dhcp_manager, priv->dhcp_state_sigid);
success = nm_dhcp_manager_begin_transaction (priv->dhcp_manager,
nm_device_get_iface (self),
45);
g_signal_handler_unblock (priv->dhcp_manager, priv->dhcp_state_sigid);
if (success) {
/* DHCP devices will be notified by the DHCP manager when
* stuff happens.
*/
ret = NM_ACT_STAGE_RETURN_POSTPONE;
} else
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
return ret;
}
/*
* nm_device_activate_stage3_ip_config_start
*
* Begin IP configuration with either DHCP or static IP.
*
*/
static gboolean
nm_device_activate_stage3_ip_config_start (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
const char * iface;
NMActStageReturn ret;
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG);
ret = NM_DEVICE_GET_CLASS (self)->act_stage3_ip_config_start (self);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
goto out;
else if (ret == NM_ACT_STAGE_RETURN_FAILURE)
{
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
nm_device_activate_schedule_stage4_ip_config_get (self);
out:
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) complete.", iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage3_ip_config_start
*
* Schedule IP configuration start
*/
void
nm_device_activate_schedule_stage3_ip_config_start (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
self->priv->act_source_id = g_idle_add (nm_device_activate_stage3_ip_config_start, self);
nm_info ("Activation (%s) Stage 3 of 5 (IP Configure Start) scheduled.",
nm_device_get_iface (self));
}
/*
* nm_device_new_ip4_autoip_config
*
* Build up an IP config with a Link Local address
*
*/
NMIP4Config *
nm_device_new_ip4_autoip_config (NMDevice *self)
{
struct in_addr ip;
NMIP4Config * config = NULL;
g_return_val_if_fail (self != NULL, NULL);
if (get_autoip (self, &ip))
{
#define LINKLOCAL_BCAST 0xa9feffff
config = nm_ip4_config_new ();
nm_ip4_config_set_address (config, (guint32)(ip.s_addr));
nm_ip4_config_set_netmask (config, (guint32)(ntohl (0xFFFF0000)));
nm_ip4_config_set_broadcast (config, (guint32)(ntohl (LINKLOCAL_BCAST)));
nm_ip4_config_set_gateway (config, 0);
}
return config;
}
static NMActStageReturn
real_act_stage4_get_ip4_config (NMDevice *self,
NMIP4Config **config)
{
NMActRequest *req;
NMIP4Config * real_config = NULL;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
NMSettingIP4Config *setting;
g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
if (nm_device_get_use_dhcp (self)) {
real_config = nm_dhcp_manager_get_ip4_config (NM_DEVICE_GET_PRIVATE (self)->dhcp_manager,
nm_device_get_iface (self));
if (real_config && nm_ip4_config_get_mtu (real_config) == 0)
/* If the DHCP server doesn't set the MTU, get it from backend. */
nm_ip4_config_set_mtu (real_config, nm_system_get_mtu (self));
} else {
real_config = nm_ip4_config_new ();
}
req = nm_device_get_act_request (self);
setting = (NMSettingIP4Config *) nm_connection_get_setting (nm_act_request_get_connection (req), "ipv4");
if (real_config && setting) {
/* If settings are provided, use them, even if it means overriding the values we got from DHCP */
nm_ip4_config_set_address (real_config, setting->address);
nm_ip4_config_set_netmask (real_config, setting->netmask);
if (setting->gateway)
nm_ip4_config_set_gateway (real_config, setting->gateway);
}
if (real_config) {
*config = real_config;
ret = NM_ACT_STAGE_RETURN_SUCCESS;
} else {
/* Make sure device is up even if config fails */
if (!nm_device_bring_up (self, FALSE))
ret = NM_ACT_STAGE_RETURN_FAILURE;
}
return ret;
}
/*
* nm_device_activate_stage4_ip_config_get
*
* Retrieve the correct IP config.
*
*/
static gboolean
nm_device_activate_stage4_ip_config_get (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMIP4Config * ip4_config = NULL;
NMActStageReturn ret;
const char * iface = NULL;
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Get) started...", iface);
ret = NM_DEVICE_GET_CLASS (self)->act_stage4_get_ip4_config (self, &ip4_config);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE)
goto out;
else if (!ip4_config || (ret == NM_ACT_STAGE_RETURN_FAILURE))
{
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
nm_device_activate_schedule_stage5_ip_config_commit (self);
out:
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Get) complete.", iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage4_ip_config_get
*
* Schedule creation of the IP config
*
*/
void
nm_device_activate_schedule_stage4_ip_config_get (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
priv->act_source_id = g_idle_add (nm_device_activate_stage4_ip_config_get, self);
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Get) scheduled...",
nm_device_get_iface (self));
}
static NMActStageReturn
real_act_stage4_ip_config_timeout (NMDevice *self,
NMIP4Config **config)
{
g_return_val_if_fail (config != NULL, NM_ACT_STAGE_RETURN_FAILURE);
g_return_val_if_fail (*config == NULL, NM_ACT_STAGE_RETURN_FAILURE);
/* Wired network, no DHCP reply. Let's get an IP via Zeroconf. */
nm_info ("No DHCP reply received. Automatically obtaining IP via Zeroconf.");
*config = nm_device_new_ip4_autoip_config (self);
return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*
* nm_device_activate_stage4_ip_config_timeout
*
* Retrieve the correct IP config.
*
*/
static gboolean
nm_device_activate_stage4_ip_config_timeout (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMIP4Config * ip4_config = NULL;
const char * iface;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_FAILURE;
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Timeout) started...", iface);
ret = NM_DEVICE_GET_CLASS (self)->act_stage4_ip_config_timeout (self, &ip4_config);
if (ret == NM_ACT_STAGE_RETURN_POSTPONE) {
goto out;
} else if (!ip4_config || (ret == NM_ACT_STAGE_RETURN_FAILURE)) {
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
g_assert (ip4_config);
g_object_set_data (G_OBJECT (nm_device_get_act_request (self)),
NM_ACT_REQUEST_IP4_CONFIG, ip4_config);
nm_device_activate_schedule_stage5_ip_config_commit (self);
out:
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Timeout) complete.", iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage4_ip_config_timeout
*
* Deal with a timed out DHCP transaction
*
*/
void
nm_device_activate_schedule_stage4_ip_config_timeout (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
priv->act_source_id = g_idle_add (nm_device_activate_stage4_ip_config_timeout, self);
nm_info ("Activation (%s) Stage 4 of 5 (IP Configure Timeout) scheduled...",
nm_device_get_iface (self));
}
/*
* nm_device_activate_stage5_ip_config_commit
*
* Commit the IP config on the device
*
*/
static gboolean
nm_device_activate_stage5_ip_config_commit (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
NMIP4Config * ip4_config = NULL;
const char * iface;
ip4_config = g_object_get_data (G_OBJECT (nm_device_get_act_request (self)),
NM_ACT_REQUEST_IP4_CONFIG);
g_assert (ip4_config);
/* Clear the activation source ID now that this stage has run */
if (self->priv->act_source_id > 0)
self->priv->act_source_id = 0;
iface = nm_device_get_iface (self);
nm_info ("Activation (%s) Stage 5 of 5 (IP Configure Commit) started...",
iface);
nm_device_set_ip4_config (self, ip4_config);
if (nm_system_device_set_from_ip4_config (self)) {
nm_device_update_ip4_address (self);
nm_system_device_add_ip6_link_address (self);
nm_system_restart_mdns_responder ();
nm_system_set_hostname (self->priv->ip4_config);
nm_system_activate_nis (self->priv->ip4_config);
nm_system_set_mtu (self);
if (NM_DEVICE_GET_CLASS (self)->update_link)
NM_DEVICE_GET_CLASS (self)->update_link (self);
nm_device_state_changed (self, NM_DEVICE_STATE_ACTIVATED);
} else {
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED);
}
nm_info ("Activation (%s) Stage 5 of 5 (IP Configure Commit) complete.",
iface);
return FALSE;
}
/*
* nm_device_activate_schedule_stage5_ip_config_commit
*
* Schedule commit of the IP config
*/
static void
nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (priv->act_request);
priv->act_source_id = g_idle_add (nm_device_activate_stage5_ip_config_commit, self);
nm_info ("Activation (%s) Stage 5 of 5 (IP Configure Commit) scheduled...",
nm_device_get_iface (self));
}
static void
real_activation_cancel_handler (NMDevice *self)
{
if (nm_device_get_state (self) == NM_DEVICE_STATE_IP_CONFIG &&
nm_device_get_use_dhcp (self)) {
nm_dhcp_manager_cancel_transaction (NM_DEVICE_GET_PRIVATE (self)->dhcp_manager,
nm_device_get_iface (self));
}
}
/*
* nm_device_activation_cancel
*
* Signal activation worker that it should stop and die.
*
*/
void
nm_device_activation_cancel (NMDevice *self)
{
NMDeviceClass *klass;
g_return_if_fail (self != NULL);
if (!nm_device_is_activating (self))
return;
nm_info ("Activation (%s): cancelling...", nm_device_get_iface (self));
/* Break the activation chain */
if (self->priv->act_source_id) {
g_source_remove (self->priv->act_source_id);
self->priv->act_source_id = 0;
}
klass = NM_DEVICE_CLASS (g_type_class_peek (NM_TYPE_DEVICE));
if (klass->activation_cancel_handler)
klass->activation_cancel_handler (self);
g_object_unref (self->priv->act_request);
self->priv->act_request = NULL;
nm_info ("Activation (%s): cancelled.", nm_device_get_iface (self));
}
/*
* nm_device_deactivate_quickly
*
* Quickly deactivate a device, for things like sleep, etc. Doesn't
* clean much stuff up, and nm_device_deactivate() should be called
* on the device eventually.
*
*/
gboolean
nm_device_deactivate_quickly (NMDevice *self)
{
NMActRequest * act_request;
g_return_val_if_fail (self != NULL, FALSE);
nm_system_shutdown_nis ();
if (nm_device_is_activating (self))
nm_device_activation_cancel (self);
/* Tear down an existing activation request, which may not have happened
* in nm_device_activation_cancel() above, for various reasons.
*/
if ((act_request = nm_device_get_act_request (self)) &&
nm_device_get_use_dhcp (self)) {
nm_dhcp_manager_cancel_transaction (NM_DEVICE_GET_PRIVATE (self)->dhcp_manager,
nm_device_get_iface (self));
g_object_unref (act_request);
self->priv->act_request = NULL;
}
/* Call device type-specific deactivation */
if (NM_DEVICE_GET_CLASS (self)->deactivate_quickly)
NM_DEVICE_GET_CLASS (self)->deactivate_quickly (self);
return TRUE;
}
/*
* nm_device_deactivate
*
* Remove a device's routing table entries and IP address.
*
*/
static void
nm_device_deactivate (NMDeviceInterface *device)
{
NMDevice *self = NM_DEVICE (device);
NMIP4Config * config;
g_return_if_fail (self != NULL);
nm_info ("Deactivating device %s.", nm_device_get_iface (self));
nm_device_deactivate_quickly (self);
/* Remove any device nameservers and domains */
if ((config = nm_device_get_ip4_config (self)))
{
nm_named_manager_remove_ip4_config (self->priv->app_data->named_manager, config);
nm_device_set_ip4_config (self, NULL);
}
/* Take out any entries in the routing table and any IP address the device had. */
nm_system_device_flush_routes (self);
nm_system_device_flush_addresses (self);
nm_device_update_ip4_address (self);
/* Call device type-specific deactivation */
if (NM_DEVICE_GET_CLASS (self)->deactivate)
NM_DEVICE_GET_CLASS (self)->deactivate (self);
nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED);
}
static void
nm_device_activate (NMDeviceInterface *device,
NMConnection *connection,
gboolean user_requested)
{
NMDevice *self = NM_DEVICE (device);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
if (!NM_DEVICE_GET_CLASS (self)->check_connection (self, connection))
/* connection is invalid */
return;
if (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (self)) {
NMConnection *current_connection;
current_connection = nm_act_request_get_connection (nm_device_get_act_request (self));
if (nm_connection_compare (connection, current_connection))
/* Already activating or activated with the same connection */
return;
nm_device_deactivate (device);
}
nm_info ("Activating device %s", nm_device_get_iface (self));
priv->act_request = nm_act_request_new (connection, user_requested);
nm_device_activate_schedule_stage1_device_prepare (self);
}
/*
* nm_device_is_activating
*
* Return whether or not the device is currently activating itself.
*
*/
gboolean
nm_device_is_activating (NMDevice *device)
{
g_return_val_if_fail (NM_IS_DEVICE (device), FALSE);
switch (nm_device_get_state (device)) {
case NM_DEVICE_STATE_PREPARE:
case NM_DEVICE_STATE_CONFIG:
case NM_DEVICE_STATE_NEED_AUTH:
case NM_DEVICE_STATE_IP_CONFIG:
return TRUE;
break;
default:
break;
}
return FALSE;
}
gboolean
nm_device_can_interrupt_activation (NMDevice *self)
{
gboolean interrupt = FALSE;
g_return_val_if_fail (self != NULL, FALSE);
if (NM_DEVICE_GET_CLASS (self)->can_interrupt_activation)
interrupt = NM_DEVICE_GET_CLASS (self)->can_interrupt_activation (self);
return interrupt;
}
/* IP Configuration stuff */
static void
dhcp_state_changed (NMDHCPManager *dhcp_manager,
const char *iface,
NMDHCPState state,
gpointer user_data)
{
NMDevice * device = NM_DEVICE (user_data);
if (strcmp (nm_device_get_iface (device), iface) != 0)
return;
if (!nm_device_get_act_request (device))
return;
switch (state) {
case DHC_BOUND: /* lease obtained */
case DHC_RENEW: /* lease renewed */
case DHC_REBOOT: /* have valid lease, but now obtained a different one */
case DHC_REBIND: /* new, different lease */
if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_activate_schedule_stage4_ip_config_get (device);
break;
case DHC_TIMEOUT: /* timed out contacting DHCP server */
if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_activate_schedule_stage4_ip_config_timeout (device);
break;
case DHC_FAIL: /* all attempts to contact server timed out, sleeping */
case DHC_ABEND: /* dhclient exited abnormally */
case DHC_END: /* dhclient exited normally */
if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG) {
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED);
} else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED) {
if (nm_device_get_use_dhcp (device)) {
/* dhclient quit and therefore can't renew our lease, kill the conneciton */
nm_device_deactivate (NM_DEVICE_INTERFACE (device));
}
}
break;
default:
break;
}
}
static void
dhcp_timeout (NMDHCPManager *dhcp_manager,
const char *iface,
gpointer user_data)
{
NMDevice * device = NM_DEVICE (user_data);
if (strcmp (nm_device_get_iface (device), iface) != 0)
return;
if (nm_device_get_state (device) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_activate_schedule_stage4_ip_config_timeout (device);
}
gboolean
nm_device_get_use_dhcp (NMDevice *self)
{
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
return NM_DEVICE_GET_PRIVATE (self)->dhcp_manager ? TRUE : FALSE;
}
void
nm_device_set_use_dhcp (NMDevice *self,
gboolean use_dhcp)
{
NMDevicePrivate *priv;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
if (use_dhcp) {
if (!priv->dhcp_manager) {
priv->dhcp_manager = nm_dhcp_manager_get ();
priv->dhcp_state_sigid = g_signal_connect (priv->dhcp_manager,
"state-changed",
G_CALLBACK (dhcp_state_changed),
self);
priv->dhcp_timeout_sigid = g_signal_connect (priv->dhcp_manager,
"timeout",
G_CALLBACK (dhcp_timeout),
self);
}
} else if (priv->dhcp_manager) {
g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_state_sigid);
priv->dhcp_state_sigid = 0;
g_signal_handler_disconnect (priv->dhcp_manager, priv->dhcp_timeout_sigid);
priv->dhcp_timeout_sigid = 0;
g_object_unref (priv->dhcp_manager);
priv->dhcp_manager = NULL;
}
}
NMIP4Config *
nm_device_get_ip4_config (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->ip4_config;
}
void
nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
g_return_if_fail (NM_IS_DEVICE (self));
if (priv->ip4_config) {
g_object_unref (priv->ip4_config);
priv->ip4_config = NULL;
}
if (config)
priv->ip4_config = g_object_ref (config);
}
/*
* nm_device_get_ip4_address
*
* Get a device's IPv4 address
*
*/
guint32
nm_device_get_ip4_address (NMDevice *self)
{
g_return_val_if_fail (self != NULL, 0);
return self->priv->ip4_address;
}
void
nm_device_update_ip4_address (NMDevice *self)
{
guint32 new_address;
struct ifreq req;
NMSock * sk;
int err;
const char * iface;
g_return_if_fail (self != NULL);
iface = nm_device_get_iface (self);
g_return_if_fail (iface != NULL);
if ((sk = nm_dev_sock_open (iface, DEV_GENERAL, __func__, NULL)) == NULL)
return;
memset (&req, 0, sizeof (struct ifreq));
strncpy (req.ifr_name, iface, sizeof (req.ifr_name) - 1);
nm_ioctl_info ("%s: About to GET IFADDR.", iface);
err = ioctl (nm_dev_sock_get_fd (sk), SIOCGIFADDR, &req);
nm_ioctl_info ("%s: Done with GET IFADDR.", iface);
nm_dev_sock_close (sk);
if (err != 0)
return;
new_address = ((struct sockaddr_in *)(&req.ifr_addr))->sin_addr.s_addr;
if (new_address != nm_device_get_ip4_address (self))
self->priv->ip4_address = new_address;
}
gboolean
nm_device_is_up (NMDevice *self)
{
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
if (NM_DEVICE_GET_CLASS (self)->is_up)
return NM_DEVICE_GET_CLASS (self)->is_up (self);
return TRUE;
}
/* I really wish nm_v_wait_for_completion_or_timeout could translate these
* to first class args instead of a all this void * arg stuff, so these
* helpers could be nice and _tiny_. */
static gboolean
nm_completion_device_is_up_test (int tries,
nm_completion_args args)
{
NMDevice *self = NM_DEVICE (args[0]);
if (nm_device_is_up (self))
return TRUE;
return FALSE;
}
gboolean
nm_device_bring_up (NMDevice *self, gboolean wait)
{
gboolean success;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
if (nm_device_is_up (self))
return TRUE;
nm_info ("Bringing up device %s", nm_device_get_iface (self));
nm_system_device_set_up_down (self, TRUE);
nm_device_update_ip4_address (self);
nm_device_set_address (self);
if (NM_DEVICE_GET_CLASS (self)->bring_up) {
success = NM_DEVICE_GET_CLASS (self)->bring_up (self);
if (!success)
return FALSE;
}
if (wait) {
nm_completion_args args;
args[0] = self;
nm_wait_for_completion (400, G_USEC_PER_SEC / 200, NULL, nm_completion_device_is_up_test, args);
}
nm_device_state_changed (self, NM_DEVICE_STATE_DISCONNECTED);
return TRUE;
}
void
nm_device_bring_down (NMDevice *self, gboolean wait)
{
g_return_if_fail (NM_IS_DEVICE (self));
if (!nm_device_is_up (self))
return;
nm_info ("Bringing down device %s", nm_device_get_iface (self));
if (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)
nm_device_interface_deactivate (NM_DEVICE_INTERFACE (self));
if (NM_DEVICE_GET_CLASS (self)->bring_down)
NM_DEVICE_GET_CLASS (self)->bring_down (self);
nm_system_device_set_up_down (self, FALSE);
if (wait) {
nm_completion_args args;
args[0] = self;
nm_wait_for_completion (400, G_USEC_PER_SEC / 200, NULL, nm_completion_device_is_up_test, args);
}
nm_device_state_changed (self, NM_DEVICE_STATE_DOWN);
}
/*
* nm_device_get_system_config_data
*
* Return distro-specific system configuration data for this device.
*
*/
void *
nm_device_get_system_config_data (NMDevice *self)
{
g_return_val_if_fail (self != NULL, NULL);
return self->priv->system_config_data;
}
static void
nm_device_dispose (GObject *object)
{
NMDevice *self = NM_DEVICE (object);
if (self->priv->dispose_has_run) {
/* If dispose already ran, return. */
return;
}
if (!self->priv->initialized) {
/* Don't tear down stuff that might not yet be set up */
goto out;
}
/* Make sure dispose does not run twice. */
self->priv->dispose_has_run = TRUE;
/*
* In dispose, you are supposed to free all types referenced from this
* object which might themselves hold a reference to self. Generally,
* the most simple solution is to unref all members on which you own a
* reference.
*/
nm_device_bring_down (self, FALSE);
nm_system_device_free_system_config (self, self->priv->system_config_data);
nm_device_set_ip4_config (self, NULL);
if (self->priv->act_request)
{
g_object_unref (self->priv->act_request);
self->priv->act_request = NULL;
}
if (self->priv->act_source_id) {
g_source_remove (self->priv->act_source_id);
self->priv->act_source_id = 0;
}
nm_device_set_use_dhcp (self, FALSE);
out:
G_OBJECT_CLASS (nm_device_parent_class)->dispose (object);
}
static void
nm_device_finalize (GObject *object)
{
NMDevice *self = NM_DEVICE (object);
g_free (self->priv->udi);
g_free (self->priv->iface);
g_free (self->priv->driver);
G_OBJECT_CLASS (nm_device_parent_class)->finalize (object);
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
switch (prop_id) {
case NM_DEVICE_INTERFACE_PROP_UDI:
/* construct-only */
priv->udi = g_strdup (g_value_get_string (value));
break;
case NM_DEVICE_INTERFACE_PROP_INDEX:
priv->index = g_value_get_uint (value);
break;
case NM_DEVICE_INTERFACE_PROP_DRIVER:
priv->driver = g_strdup (g_value_get_string (value));
break;
case NM_DEVICE_INTERFACE_PROP_APP_DATA:
priv->app_data = g_value_get_pointer (value);
break;
case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
priv->capabilities = g_value_get_uint (value);
break;
case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
priv->ip4_address = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (object);
switch (prop_id) {
case NM_DEVICE_INTERFACE_PROP_UDI:
g_value_set_string (value, priv->udi);
break;
case NM_DEVICE_INTERFACE_PROP_INDEX:
g_value_set_uint (value, priv->index);
break;
case NM_DEVICE_INTERFACE_PROP_IFACE:
g_value_set_string (value, priv->iface);
break;
case NM_DEVICE_INTERFACE_PROP_DRIVER:
g_value_set_string (value, priv->driver);
break;
case NM_DEVICE_INTERFACE_PROP_APP_DATA:
g_value_set_pointer (value, priv->app_data);
break;
case NM_DEVICE_INTERFACE_PROP_CAPABILITIES:
g_value_set_uint (value, priv->capabilities);
break;
case NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS:
g_value_set_uint (value, priv->ip4_address);
break;
case NM_DEVICE_INTERFACE_PROP_IP4_CONFIG:
g_value_set_object (value, priv->ip4_config);
break;
case NM_DEVICE_INTERFACE_PROP_STATE:
g_value_set_uint (value, priv->state);
break;
case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
g_value_set_uint (value, priv->type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
nm_device_class_init (NMDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (NMDevicePrivate));
/* Virtual methods */
object_class->dispose = nm_device_dispose;
object_class->finalize = nm_device_finalize;
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->constructor = constructor;
klass->is_up = real_is_up;
klass->activation_cancel_handler = real_activation_cancel_handler;
klass->get_type_capabilities = real_get_type_capabilities;
klass->get_generic_capabilities = real_get_generic_capabilities;
klass->act_stage1_prepare = real_act_stage1_prepare;
klass->act_stage2_config = real_act_stage2_config;
klass->act_stage3_ip_config_start = real_act_stage3_ip_config_start;
klass->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
klass->act_stage4_ip_config_timeout = real_act_stage4_ip_config_timeout;
/* Properties */
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_UDI,
NM_DEVICE_INTERFACE_UDI);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_INDEX,
NM_DEVICE_INTERFACE_INDEX);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_IFACE,
NM_DEVICE_INTERFACE_IFACE);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_DRIVER,
NM_DEVICE_INTERFACE_DRIVER);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_CAPABILITIES,
NM_DEVICE_INTERFACE_CAPABILITIES);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS,
NM_DEVICE_INTERFACE_IP4_ADDRESS);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_IP4_CONFIG,
NM_DEVICE_INTERFACE_IP4_CONFIG);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_STATE,
NM_DEVICE_INTERFACE_STATE);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_APP_DATA,
NM_DEVICE_INTERFACE_APP_DATA);
g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
NM_DEVICE_INTERFACE_DEVICE_TYPE);
}
void
nm_device_state_changed (NMDevice *device, NMDeviceState state)
{
const char *iface;
NMDeviceState old_state;
g_return_if_fail (NM_IS_DEVICE (device));
iface = nm_device_get_iface (device);
old_state = device->priv->state;
device->priv->state = state;
g_signal_emit_by_name (device, "state-changed", state);
switch (state) {
case NM_DEVICE_STATE_DOWN:
if (old_state == NM_DEVICE_STATE_ACTIVATED)
nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device));
break;
case NM_DEVICE_STATE_ACTIVATED:
nm_info ("Activation (%s) successful, device activated.", iface);
break;
case NM_DEVICE_STATE_FAILED:
nm_info ("Activation (%s) failed.", nm_device_get_iface (device));
nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device));
break;
default:
break;
}
}
NMDeviceState
nm_device_get_state (NMDevice *device)
{
g_return_val_if_fail (NM_IS_DEVICE (device), NM_DEVICE_STATE_UNKNOWN);
return NM_DEVICE_GET_PRIVATE (device)->state;
}