merge: better take-over of existing connections on NM startup (bgo #702488)

Read more state of connections that exist before NM starts, and take those
connections over more effectively.

https://bugzilla.gnome.org/show_bug.cgi?id=702488
This commit is contained in:
Dan Williams 2013-11-08 16:52:33 -06:00
commit 465458a206
41 changed files with 1593 additions and 844 deletions

View file

@ -548,6 +548,9 @@ typedef enum {
/* DCB or FCoE setup failed */
NM_DEVICE_STATE_REASON_DCB_FCOE_FAILED = 55,
/* teamd control failed */
NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED = 56,
/* Unused */
NM_DEVICE_STATE_REASON_LAST = 0xFFFF
} NMDeviceStateReason;

View file

@ -592,6 +592,11 @@
DCB or FCoE setup failed.
</tp:docstring>
</tp:enumvalue>
<tp:enumvalue suffix="TEAMD_CONTROL_FAILED" value="56">
<tp:docstring>
teamd control failed.
</tp:docstring>
</tp:enumvalue>
</tp:enum>
<tp:struct name="NM_DEVICE_STATE_REASON_STRUCT">

View file

@ -184,13 +184,6 @@ complete_connection (NMDevice *device,
return TRUE;
}
static gboolean
match_l2_config (NMDevice *self, NMConnection *connection)
{
/* FIXME */
return TRUE;
}
/******************************************************************/
static gboolean
@ -207,6 +200,57 @@ set_bond_attr (NMDevice *device, const char *attr, const char *value)
return ret;
}
/* Ignore certain bond options if they are zero (off/disabled) */
static gboolean
ignore_if_zero (const char *option, const char *value)
{
if (strcmp (option, "arp_interval") &&
strcmp (option, "miimon") &&
strcmp (option, "downdelay") &&
strcmp (option, "updelay"))
return FALSE;
return g_strcmp0 (value, "0") == 0 ? TRUE : FALSE;
}
static void
update_connection (NMDevice *device, NMConnection *connection)
{
NMSettingBond *s_bond = nm_connection_get_setting_bond (connection);
const char *ifname = nm_device_get_iface (device);
int ifindex = nm_device_get_ifindex (device);
const char **options;
if (!s_bond) {
s_bond = (NMSettingBond *) nm_setting_bond_new ();
nm_connection_add_setting (connection, (NMSetting *) s_bond);
g_object_set (s_bond, NM_SETTING_BOND_INTERFACE_NAME, ifname, NULL);
}
/* Read bond options from sysfs and update the Bond setting to match */
options = nm_setting_bond_get_valid_options (s_bond);
while (options && *options) {
gs_free char *value = nm_platform_master_get_option (ifindex, *options);
const char *defvalue = nm_setting_bond_get_option_default (s_bond, *options);
if (value && !ignore_if_zero (*options, value) && (g_strcmp0 (value, defvalue) != 0)) {
/* Replace " " with "," for arp_ip_targets from the kernel */
if (strcmp (*options, "arp_ip_target") == 0) {
char *p = value;
while (p && *p) {
if (*p == ' ')
*p = ',';
p++;
}
}
nm_setting_bond_add_option (s_bond, *options, value);
}
options++;
}
}
static void
set_arp_targets (NMDevice *device,
const char *value,
@ -369,20 +413,23 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
enslave_slave (NMDevice *device,
NMDevice *slave,
NMConnection *connection,
gboolean configure)
{
gboolean success, no_firmware = FALSE;
gboolean success = TRUE, no_firmware = FALSE;
const char *iface = nm_device_get_ip_iface (device);
const char *slave_iface = nm_device_get_ip_iface (slave);
nm_device_master_check_slave_physical_port (device, slave, LOGD_BOND);
nm_device_take_down (slave, TRUE);
success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_ifindex (slave));
nm_device_bring_up (slave, TRUE, &no_firmware);
if (configure) {
nm_device_take_down (slave, TRUE);
success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_ifindex (slave));
nm_device_bring_up (slave, TRUE, &no_firmware);
}
if (success) {
nm_log_info (LOGD_BOND, "(%s): enslaved bond slave %s", iface, slave_iface);
@ -518,6 +565,8 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
g_type_class_add_private (object_class, sizeof (NMDeviceBondPrivate));
parent_class->connection_type = NM_SETTING_BOND_SETTING_NAME;
/* virtual methods */
object_class->constructed = constructed;
object_class->get_property = get_property;
@ -529,7 +578,7 @@ nm_device_bond_class_init (NMDeviceBondClass *klass)
parent_class->check_connection_available = check_connection_available;
parent_class->complete_connection = complete_connection;
parent_class->match_l2_config = match_l2_config;
parent_class->update_connection = update_connection;
parent_class->act_stage1_prepare = act_stage1_prepare;
parent_class->enslave_slave = enslave_slave;

View file

@ -24,6 +24,7 @@
#include <glib/gi18n.h>
#include <netinet/ether.h>
#include <stdlib.h>
#include "gsystem-local-alloc.h"
#include "nm-device-bridge.h"
@ -180,13 +181,6 @@ complete_connection (NMDevice *device,
return TRUE;
}
static gboolean
match_l2_config (NMDevice *self, NMConnection *connection)
{
/* FIXME */
return TRUE;
}
/******************************************************************/
typedef struct {
@ -290,6 +284,80 @@ commit_slave_options (NMDevice *device, NMSettingBridgePort *setting)
g_clear_object (&s_clear);
}
static void
update_connection (NMDevice *device, NMConnection *connection)
{
NMSettingBridge *s_bridge = nm_connection_get_setting_bridge (connection);
const char *ifname = nm_device_get_iface (device);
int ifindex = nm_device_get_ifindex (device);
const Option *option;
if (!s_bridge) {
s_bridge = (NMSettingBridge *) nm_setting_bridge_new ();
nm_connection_add_setting (connection, (NMSetting *) s_bridge);
g_object_set (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, ifname, NULL);
}
for (option = master_options; option->name; option++) {
gs_free char *str = nm_platform_master_get_option (ifindex, option->sysname);
int value = strtol (str, NULL, 10);
/* See comments in set_sysfs_uint() about centiseconds. */
if (option->user_hz_compensate)
value /= 100;
g_object_set (s_bridge, option->name, value, NULL);
}
}
/**
* nm_bridge_update_slave_connection:
* @slave: the slave #NMDevice, is *not* necessarily a bridge interface
* @connection: the #NMConnection to update with the bridge port settings
*
* Reads bridge port configuration and updates @connection with those
* properties.
*
* Returns: %TRUE if the port configuration was read and @connection updated,
* %FALSE if not.
*/
gboolean
nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection)
{
NMSettingBridgePort *s_port;
int ifindex = nm_device_get_ifindex (slave);
const Option *option;
g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE);
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
s_port = nm_connection_get_setting_bridge_port (connection);
if (!s_port) {
s_port = (NMSettingBridgePort *) nm_setting_bridge_port_new ();
nm_connection_add_setting (connection, NM_SETTING (s_port));
}
for (option = slave_options; option->name; option++) {
gs_free char *str = nm_platform_slave_get_option (ifindex, option->sysname);
int value;
if (str) {
value = strtol (str, NULL, 10);
/* See comments in set_sysfs_uint() about centiseconds. */
if (option->user_hz_compensate)
value /= 100;
g_object_set (s_port, option->name, value, NULL);
} else {
nm_log_warn (LOGD_BRIDGE, "(%s): failed to read bridge port setting '%s'",
nm_device_get_iface (slave), option->sysname);
}
}
return TRUE;
}
static NMActStageReturn
act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
{
@ -308,12 +376,17 @@ act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason)
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
enslave_slave (NMDevice *device,
NMDevice *slave,
NMConnection *connection,
gboolean configure)
{
if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
return FALSE;
if (configure) {
if (!nm_platform_link_enslave (nm_device_get_ip_ifindex (device), nm_device_get_ip_ifindex (slave)))
return FALSE;
commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
commit_slave_options (slave, nm_connection_get_setting_bridge_port (connection));
}
nm_log_info (LOGD_BRIDGE, "(%s): attached bridge port %s",
nm_device_get_ip_iface (device),
@ -439,6 +512,8 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
g_type_class_add_private (object_class, sizeof (NMDeviceBridgePrivate));
parent_class->connection_type = NM_SETTING_BRIDGE_SETTING_NAME;
/* virtual methods */
object_class->constructed = constructed;
object_class->get_property = get_property;
@ -450,7 +525,7 @@ nm_device_bridge_class_init (NMDeviceBridgeClass *klass)
parent_class->check_connection_available = check_connection_available;
parent_class->complete_connection = complete_connection;
parent_class->match_l2_config = match_l2_config;
parent_class->update_connection = update_connection;
parent_class->act_stage1_prepare = act_stage1_prepare;
parent_class->enslave_slave = enslave_slave;

View file

@ -57,6 +57,8 @@ GType nm_device_bridge_get_type (void);
NMDevice *nm_device_bridge_new (NMPlatformLink *platform_device);
NMDevice *nm_device_bridge_new_for_connection (NMConnection *connection);
gboolean nm_bridge_update_slave_connection (NMDevice *slave, NMConnection *connection);
G_END_DECLS
#endif /* NM_DEVICE_BRIDGE_H */

View file

@ -1238,19 +1238,38 @@ spec_match_list (NMDevice *device, const GSList *specs)
static void
update_connection (NMDevice *device, NMConnection *connection)
{
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
NMSettingWired *s_wired = nm_connection_get_setting_wired (connection);
guint maclen;
gconstpointer mac = nm_device_get_hw_address (device, &maclen);
static const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
GByteArray *array;
if (!s_wired) {
s_wired = (NMSettingWired *) nm_setting_wired_new ();
nm_connection_add_setting (connection, (NMSetting *) s_wired);
}
if (mac && maclen == 6) {
GBytes *address = g_bytes_new (mac, maclen);
/* If the device reports a permanent address, use that for the MAC address
* and the current MAC, if different, is the cloned MAC.
*/
if (priv->perm_hw_addr && memcmp (priv->perm_hw_addr, null_mac, ETH_ALEN)) {
array = g_byte_array_sized_new (ETH_ALEN);
g_byte_array_append (array, priv->perm_hw_addr, ETH_ALEN);
g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, array, NULL);
g_byte_array_unref (array);
g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, address, NULL);
mac_prop = NULL;
if (mac && memcmp (priv->perm_hw_addr, mac, ETH_ALEN))
mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS;
}
if (mac_prop && mac && maclen == ETH_ALEN) {
array = g_byte_array_sized_new (ETH_ALEN);
g_byte_array_append (array, (guint8 *) mac, maclen);
g_object_set (s_wired, mac_prop, array, NULL);
g_byte_array_unref (array);
}
/* We don't set the MTU as we don't know whether it was set explicitly */

View file

@ -95,6 +95,13 @@ check_connection_compatible (NMDevice *device,
return TRUE;
}
static void
update_connection (NMDevice *device, NMConnection *connection)
{
if (!nm_connection_get_setting_generic (connection))
nm_connection_add_setting (connection, nm_setting_generic_new ());
}
/**************************************************************/
NMDevice *
@ -184,6 +191,8 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
g_type_class_add_private (klass, sizeof (NMDeviceGenericPrivate));
parent_class->connection_type = NM_SETTING_GENERIC_SETTING_NAME;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->get_property = get_property;
@ -191,6 +200,7 @@ nm_device_generic_class_init (NMDeviceGenericClass *klass)
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->update_connection = update_connection;
/* properties */
g_object_class_install_property

View file

@ -307,11 +307,39 @@ complete_connection (NMDevice *device,
return TRUE;
}
static gboolean
match_l2_config (NMDevice *self, NMConnection *connection)
static void
update_connection (NMDevice *device, NMConnection *connection)
{
/* FIXME */
return TRUE;
NMSettingInfiniband *s_infiniband = nm_connection_get_setting_infiniband (connection);
guint maclen;
gconstpointer mac = nm_device_get_hw_address (device, &maclen);
static const guint8 null_mac[INFINIBAND_ALEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
GByteArray *array;
char *mode_path, *contents;
const char *transport_mode = "datagram";
if (!s_infiniband) {
s_infiniband = (NMSettingInfiniband *) nm_setting_infiniband_new ();
nm_connection_add_setting (connection, (NMSetting *) s_infiniband);
}
if (mac && (maclen == INFINIBAND_ALEN) && (memcmp (mac, null_mac, maclen) != 0)) {
array = g_byte_array_sized_new (maclen);
g_byte_array_append (array, (guint8 *) mac, maclen);
g_object_set (s_infiniband, NM_SETTING_INFINIBAND_MAC_ADDRESS, array, NULL);
g_byte_array_unref (array);
}
mode_path = g_strdup_printf ("/sys/class/net/%s/mode", nm_device_get_iface (device));
if (g_file_get_contents (mode_path, &contents, NULL, NULL)) {
if (strstr (contents, "datagram"))
transport_mode = "datagram";
else if (strstr (contents, "connected"))
transport_mode = "connected";
}
g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL);
g_free (mode_path);
g_free (contents);
}
static gboolean
@ -384,11 +412,11 @@ nm_device_infiniband_class_init (NMDeviceInfinibandClass *klass)
parent_class->get_generic_capabilities = get_generic_capabilities;
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->complete_connection = complete_connection;
parent_class->update_connection = update_connection;
parent_class->spec_match_list = spec_match_list;
parent_class->act_stage1_prepare = act_stage1_prepare;
parent_class->ip4_config_pre_commit = ip4_config_pre_commit;
parent_class->match_l2_config = match_l2_config;
/* properties */

View file

@ -31,6 +31,7 @@
#if WITH_TEAMDCTL
#include <teamdctl.h>
#endif
#include <stdlib.h>
#include "nm-device-team.h"
#include "nm-logging.h"
@ -195,11 +196,108 @@ complete_connection (NMDevice *device,
return TRUE;
}
#if WITH_TEAMDCTL
static gboolean
match_l2_config (NMDevice *self, NMConnection *connection)
ensure_teamd_connection (NMDevice *self)
{
/* FIXME */
return TRUE;
NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (self);
int err;
if (priv->tdc)
return TRUE;
priv->tdc = teamdctl_alloc ();
g_assert (priv->tdc);
err = teamdctl_connect (priv->tdc, nm_device_get_iface (self), NULL, NULL);
if (err) {
nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd", nm_device_get_iface (self));
teamdctl_free (priv->tdc);
priv->tdc = NULL;
}
return !!priv->tdc;
}
#endif
static void
update_connection (NMDevice *device, NMConnection *connection)
{
NMSettingTeam *s_team = nm_connection_get_setting_team (connection);
const char *iface = nm_device_get_iface (device);
if (!s_team) {
s_team = (NMSettingTeam *) nm_setting_team_new ();
nm_connection_add_setting (connection, (NMSetting *) s_team);
g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_INTERFACE_NAME, iface, NULL);
}
#if WITH_TEAMDCTL
if (ensure_teamd_connection (device)) {
char *config;
config = teamdctl_config_get_raw (NM_DEVICE_TEAM_GET_PRIVATE (device)->tdc);
if (config)
g_object_set (G_OBJECT (s_team), NM_SETTING_TEAM_CONFIG, config, NULL);
else
nm_log_err (LOGD_TEAM, "(%s): failed to read teamd config", iface);
g_free (config);
}
#endif
}
/******************************************************************/
gboolean
nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection)
{
NMSettingTeamPort *s_port;
const char *iface = nm_device_get_iface (slave);
char *port_config = NULL;
gboolean success = FALSE;
#if WITH_TEAMDCTL
const char *master_iface;
int master_ifindex;
struct teamdctl *tdc;
int err;
#endif
g_return_val_if_fail (NM_IS_DEVICE (slave), FALSE);
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
#if WITH_TEAMDCTL
master_ifindex = nm_platform_link_get_master (nm_device_get_ifindex (slave));
g_assert (master_ifindex > 0);
master_iface = nm_platform_link_get_name (master_ifindex);
g_assert (master_iface);
tdc = teamdctl_alloc ();
g_assert (tdc);
err = teamdctl_connect (tdc, master_iface, NULL, NULL);
if (err) {
nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd for master %s",
iface, master_iface);
teamdctl_free (tdc);
return FALSE;
}
/* FIXME: wait for libteamd to implement getting port config */
/* port_config = teamdctl_port_config_get_raw (tdc, iface); */
teamdctl_free (tdc);
#endif
s_port = nm_connection_get_setting_team_port (connection);
if (!s_port) {
s_port = (NMSettingTeamPort *) nm_setting_team_port_new ();
nm_connection_add_setting (connection, NM_SETTING (s_port));
}
if (port_config) {
g_object_set (G_OBJECT (s_port), NM_SETTING_TEAM_PORT_CONFIG, port_config, NULL);
free (port_config);
success = TRUE;
} else
nm_log_err (LOGD_TEAM, "(%s): failed to read teamd port configuration", iface);
return success;
}
/******************************************************************/
@ -279,7 +377,7 @@ teamd_cleanup (NMDevice *dev, gboolean device_state_failed)
if (device_state_failed) {
if (nm_device_is_activating (dev) ||
(nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED))
nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_NONE);
nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
}
}
@ -311,17 +409,9 @@ teamd_dbus_appeared (GDBusConnection *connection,
nm_log_info (LOGD_TEAM, "(%s): teamd appeared on D-Bus", nm_device_get_iface (dev));
teamd_timeout_remove (dev);
#if WITH_TEAMDCTL
if (!priv->tdc) {
int err;
priv->tdc = teamdctl_alloc ();
g_assert (priv->tdc);
err = teamdctl_connect (priv->tdc, nm_device_get_iface (dev), NULL, NULL);
if (err) {
nm_log_err (LOGD_TEAM, "(%s): failed to connect to teamd", nm_device_get_iface (dev));
teamdctl_free (priv->tdc);
priv->tdc = NULL;
}
if (!ensure_teamd_connection (dev)) {
nm_device_state_changed (dev, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_TEAMD_CONTROL_FAILED);
return;
}
#endif
nm_device_activate_schedule_stage2_device_config (dev);
@ -548,48 +638,52 @@ deactivate (NMDevice *dev)
}
static gboolean
enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection)
enslave_slave (NMDevice *device,
NMDevice *slave,
NMConnection *connection,
gboolean configure)
{
#if WITH_TEAMDCTL
NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device);
#endif
gboolean success, no_firmware = FALSE;
gboolean success = TRUE, no_firmware = FALSE;
const char *iface = nm_device_get_ip_iface (device);
const char *slave_iface = nm_device_get_ip_iface (slave);
NMSettingTeamPort *s_team_port;
nm_device_master_check_slave_physical_port (device, slave, LOGD_TEAM);
nm_device_take_down (slave, TRUE);
if (configure) {
nm_device_take_down (slave, TRUE);
s_team_port = nm_connection_get_setting_team_port (connection);
if (s_team_port) {
const char *config = nm_setting_team_port_get_config (s_team_port);
s_team_port = nm_connection_get_setting_team_port (connection);
if (s_team_port) {
const char *config = nm_setting_team_port_get_config (s_team_port);
if (config) {
if (config) {
#if WITH_TEAMDCTL
if (!priv->tdc) {
nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd",
iface, slave_iface);
} else {
int err;
if (!priv->tdc) {
nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd",
iface, slave_iface);
} else {
int err;
err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config);
if (err) {
nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface);
return FALSE;
err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config);
if (err) {
nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface);
return FALSE;
}
}
}
#else
nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support",
iface, slave_iface);
nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support",
iface, slave_iface);
#endif
}
}
success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_ifindex (slave));
nm_device_bring_up (slave, TRUE, &no_firmware);
}
success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device),
nm_device_get_ip_ifindex (slave));
nm_device_bring_up (slave, TRUE, &no_firmware);
if (success) {
nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface);
@ -744,8 +838,7 @@ nm_device_team_class_init (NMDeviceTeamClass *klass)
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->check_connection_available = check_connection_available;
parent_class->complete_connection = complete_connection;
parent_class->match_l2_config = match_l2_config;
parent_class->update_connection = update_connection;
parent_class->act_stage1_prepare = act_stage1_prepare;
parent_class->deactivate = deactivate;

View file

@ -57,6 +57,8 @@ GType nm_device_team_get_type (void);
NMDevice *nm_device_team_new (NMPlatformLink *platform_device);
NMDevice *nm_device_team_new_for_connection (NMConnection *connection);
gboolean nm_team_update_slave_connection (NMDevice *slave, NMConnection *connection);
G_END_DECLS
#endif /* NM_DEVICE_TEAM_H */

View file

@ -28,6 +28,7 @@
#include <netinet/ether.h>
#include "nm-device-vlan.h"
#include "nm-manager.h"
#include "nm-logging.h"
#include "nm-utils.h"
#include "NetworkManagerUtils.h"
@ -35,6 +36,7 @@
#include "nm-enum-types.h"
#include "nm-dbus-manager.h"
#include "nm-platform.h"
#include "nm-utils.h"
#include "nm-device-vlan-glue.h"
@ -54,7 +56,7 @@ typedef struct {
NMDevice *parent;
guint parent_state_id;
guint vlan_id;
int vlan_id;
} NMDeviceVlanPrivate;
enum {
@ -274,35 +276,76 @@ complete_connection (NMDevice *device,
return TRUE;
}
static gboolean
match_l2_config (NMDevice *device, NMConnection *connection)
static void parent_state_changed (NMDevice *parent, NMDeviceState new_state,
NMDeviceState old_state,
NMDeviceStateReason reason,
gpointer user_data);
static void
nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent)
{
NMSettingVlan *s_vlan;
gboolean fail_if_no_hwaddr = FALSE;
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
s_vlan = nm_connection_get_setting_vlan (connection);
g_assert (s_vlan);
if (priv->parent_state_id) {
g_signal_handler_disconnect (priv->parent, priv->parent_state_id);
priv->parent_state_id = 0;
}
g_clear_object (&priv->parent);
if ( !nm_setting_vlan_get_parent (s_vlan)
&& !nm_setting_vlan_get_interface_name (s_vlan)) {
/* If there's no parent and no interface name given, then the only way
* we have to identify the VLAN interface the connection matches is
* a hardware-specific setting's hardware address property, so we want
* to fail the match below if we there is none.
*/
fail_if_no_hwaddr = TRUE;
if (parent) {
priv->parent = g_object_ref (parent);
priv->parent_state_id = g_signal_connect (priv->parent,
"state-changed",
G_CALLBACK (parent_state_changed),
device);
}
g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT);
}
static void
update_connection (NMDevice *device, NMConnection *connection)
{
NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device);
NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection);
int ifindex = nm_device_get_ifindex (device);
int parent_ifindex = -1, vlan_id = -1;
NMDevice *parent;
const char *setting_parent, *new_parent;
if (!s_vlan) {
s_vlan = (NMSettingVlan *) nm_setting_vlan_new ();
nm_connection_add_setting (connection, (NMSetting *) s_vlan);
g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, nm_device_get_iface (device), NULL);
}
/* MAC address check; we ask the parent to check our own MAC address,
* because only the parent knows what kind of NMSetting the MAC
* address will be in. The VLAN device shouldn't have to know what kind
* of interface the parent is.
*/
if (!match_hwaddr (device, connection, fail_if_no_hwaddr))
return FALSE;
nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id);
if (priv->vlan_id != vlan_id) {
priv->vlan_id = vlan_id;
g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID);
}
/* FIXME: any more L2 checks? */
return TRUE;
if (vlan_id != nm_setting_vlan_get_id (s_vlan))
g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL);
parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex);
g_assert (parent);
if (priv->parent != parent)
nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent);
/* Update parent in the connection; default to parent's interface name */
new_parent = nm_device_get_iface (parent);
setting_parent = nm_setting_vlan_get_parent (s_vlan);
if (setting_parent && nm_utils_is_uuid (setting_parent)) {
NMConnectionProvider *cp = nm_device_get_connection_provider (device);
NMConnection *parent_connection;
/* Don't change a parent specified by UUID if it's still valid */
parent_connection = nm_connection_provider_get_connection_by_uuid (cp, setting_parent);
if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection, NULL))
new_parent = NULL;
}
if (new_parent)
g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL);
}
static NMActStageReturn
@ -558,11 +601,7 @@ set_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_PARENT:
priv->parent = g_value_dup_object (value);
priv->parent_state_id = g_signal_connect (priv->parent,
"state-changed",
G_CALLBACK (parent_state_changed),
object);
nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value));
break;
case PROP_VLAN_ID:
priv->vlan_id = g_value_get_uint (value);
@ -585,8 +624,7 @@ dispose (GObject *object)
}
priv->disposed = TRUE;
g_signal_handler_disconnect (priv->parent, priv->parent_state_id);
g_object_unref (priv->parent);
nm_device_vlan_set_parent (self, NULL);
G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object);
}
@ -597,6 +635,8 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass);
parent_class->connection_type = NM_SETTING_VLAN_SETTING_NAME;
g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate));
/* virtual methods */
@ -614,7 +654,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass)
parent_class->check_connection_compatible = check_connection_compatible;
parent_class->complete_connection = complete_connection;
parent_class->match_l2_config = match_l2_config;
parent_class->update_connection = update_connection;
/* properties */
g_object_class_install_property

View file

@ -69,6 +69,10 @@
#include "nm-config.h"
#include "nm-platform.h"
#include "nm-device-bridge.h"
#include "nm-device-bond.h"
#include "nm-device-team.h"
static void impl_device_disconnect (NMDevice *device, DBusGMethodInvocation *context);
#include "nm-device-glue.h"
@ -164,6 +168,7 @@ typedef struct {
typedef struct {
NMDevice *slave;
gboolean enslaved;
gboolean configure;
guint watch_id;
} SlaveInfo;
@ -349,7 +354,7 @@ static void link_changed_cb (NMPlatform *platform, int ifindex, NMPlatformLink *
static void check_carrier (NMDevice *device);
static void nm_device_queued_ip_config_change_clear (NMDevice *self);
static void update_ip_config (NMDevice *self);
static void update_ip_config (NMDevice *self, gboolean capture_dhcp);
static void device_ip_changed (NMPlatform *platform, int ifindex, gpointer platform_object, NMPlatformReason reason, gpointer user_data);
static void nm_device_slave_notify_enslave (NMDevice *dev, gboolean success);
@ -545,7 +550,6 @@ constructor (GType type,
update_accept_ra_save (dev);
update_ip6_privacy_save (dev);
update_ip_config (dev);
/* Watch for external IP config changes */
platform = nm_platform_get ();
@ -717,7 +721,10 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
priv->ip_iface = g_strdup (iface);
if (priv->ip_iface) {
priv->ip_ifindex = nm_platform_link_get_ifindex (priv->ip_iface);
if (priv->ip_ifindex <= 0) {
if (priv->ip_ifindex > 0) {
if (!nm_platform_link_is_up (priv->ip_ifindex))
nm_platform_link_set_up (priv->ip_ifindex);
} else {
/* Device IP interface must always be a kernel network interface */
nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
}
@ -955,7 +962,7 @@ nm_device_enslave_slave (NMDevice *dev, NMDevice *slave, NMConnection *connectio
return FALSE;
g_warn_if_fail (info->enslaved == FALSE);
success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection);
success = NM_DEVICE_GET_CLASS (dev)->enslave_slave (dev, slave, connection, info->configure);
info->enslaved = success;
nm_device_slave_notify_enslave (info->slave, success);
@ -1242,6 +1249,8 @@ slave_state_changed (NMDevice *slave,
* nm_device_master_add_slave:
* @dev: the master device
* @slave: the slave device to enslave
* @configure: pass %TRUE if the slave should be configured by the master, or
* %FALSE if it is already configured outside NetworkManager
*
* If @dev is capable of enslaving other devices (ie it's a bridge, bond, team,
* etc) then this function adds @slave to the slave list for later enslavement.
@ -1249,7 +1258,7 @@ slave_state_changed (NMDevice *slave,
* Returns: %TRUE on success, %FALSE on failure
*/
gboolean
nm_device_master_add_slave (NMDevice *dev, NMDevice *slave)
nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
SlaveInfo *info;
@ -1262,6 +1271,7 @@ nm_device_master_add_slave (NMDevice *dev, NMDevice *slave)
if (!find_slave_info (dev, slave)) {
info = g_malloc0 (sizeof (SlaveInfo));
info->slave = g_object_ref (slave);
info->configure = configure;
info->watch_id = g_signal_connect (slave, "state-changed",
G_CALLBACK (slave_state_changed), dev);
priv->slaves = g_slist_prepend (priv->slaves, info);
@ -1625,6 +1635,10 @@ device_has_config (NMDevice *device)
if (nm_device_is_software (device))
return TRUE;
/* Slaves are also configured by definition */
if (nm_platform_link_get_master (priv->ifindex) > 0)
return TRUE;
return FALSE;
}
@ -1634,12 +1648,15 @@ nm_device_generate_connection (NMDevice *device)
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
const char *ifname = nm_device_get_iface (device);
int ifindex = nm_device_get_ifindex (device);
NMConnection *connection;
NMSetting *s_con;
NMSetting *s_ip4;
NMSetting *s_ip6;
gs_free char *uuid = NULL;
gs_free char *name = NULL;
int master_ifindex = 0;
const char *ip4_method, *ip6_method;
/* If update_connection() is not implemented, just fail. */
if (!klass->update_connection)
@ -1649,12 +1666,8 @@ nm_device_generate_connection (NMDevice *device)
if (!device_has_config (device))
return NULL;
nm_log_info (LOGD_DEVICE, "(%s): Generating connection from current device status.", ifname);
connection = nm_connection_new ();
s_con = nm_setting_connection_new ();
s_ip4 = nm_setting_ip4_config_new ();
s_ip6 = nm_setting_ip6_config_new ();
uuid = nm_utils_uuid_generate ();
name = g_strdup_printf ("%s", ifname);
@ -1666,18 +1679,75 @@ nm_device_generate_connection (NMDevice *device)
NULL);
if (klass->connection_type)
g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type, NULL);
nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4);
nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6);
nm_connection_add_setting (connection, s_con);
/* If the device is a slave, update various slave settings */
if (ifindex)
master_ifindex = nm_platform_link_get_master (ifindex);
if (master_ifindex) {
const char *master_iface = nm_platform_link_get_name (master_ifindex);
const char *slave_type = NULL;
gboolean success = FALSE;
switch (nm_platform_link_get_type (master_ifindex)) {
case NM_LINK_TYPE_BRIDGE:
slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
success = nm_bridge_update_slave_connection (device, connection);
break;
case NM_LINK_TYPE_BOND:
slave_type = NM_SETTING_BOND_SETTING_NAME;
success = TRUE;
break;
case NM_LINK_TYPE_TEAM:
slave_type = NM_SETTING_TEAM_SETTING_NAME;
success = nm_team_update_slave_connection (device, connection);
break;
default:
g_warn_if_reached ();
break;
}
if (!success)
nm_log_err (LOGD_DEVICE, "(%s): failed to read slave configuration", ifname);
g_object_set (s_con,
NM_SETTING_CONNECTION_MASTER, master_iface,
NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type,
NULL);
}
s_ip4 = nm_setting_ip4_config_new ();
nm_connection_add_setting (connection, s_ip4);
if (priv->ip4_config)
nm_ip4_config_update_setting (priv->ip4_config, (NMSettingIP4Config *) s_ip4);
else
g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_DISABLED, NULL);
s_ip6 = nm_setting_ip6_config_new ();
nm_connection_add_setting (connection, s_ip6);
if (priv->ip6_config)
nm_ip6_config_update_setting (priv->ip6_config, (NMSettingIP6Config *) s_ip6);
else
g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE, NULL);
klass->update_connection (device, connection);
/* Check the connection in case of update_connection() bug. */
g_return_val_if_fail (nm_connection_verify (connection, NULL), NULL);
/* Ignore the connection if it has no IP configuration,
* no slave configuration, and is not a master interface.
*/
ip4_method = nm_setting_ip4_config_get_method (NM_SETTING_IP4_CONFIG (s_ip4));
ip6_method = nm_setting_ip6_config_get_method (NM_SETTING_IP6_CONFIG (s_ip6));
if ( strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
&& strcmp (ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0
&& !nm_setting_connection_get_master (NM_SETTING_CONNECTION (s_con))
&& !nm_platform_link_supports_slaves (priv->ifindex)) {
g_object_unref (connection);
connection = NULL;
}
return connection;
}
@ -1806,7 +1876,6 @@ nm_device_check_connection_compatible (NMDevice *device,
* @device: #NMDevice instance
*
* This is a convenience function to determine whether connection assumption
* via old match_l2_config() or new update_connection() virtual functions
* is available for this device.
*
* Use this function when you need to determine whether full cleanup should
@ -1821,9 +1890,7 @@ nm_device_check_connection_compatible (NMDevice *device,
gboolean
nm_device_can_assume_connections (NMDevice *device)
{
NMDeviceClass *klass = NM_DEVICE_GET_CLASS (device);
return klass->match_l2_config || klass->update_connection;
return !!NM_DEVICE_GET_CLASS (device)->update_connection;
}
static void
@ -1935,7 +2002,9 @@ master_ready_cb (NMActiveConnection *active,
master = nm_active_connection_get_master (active);
priv->master = g_object_ref (nm_active_connection_get_device (master));
nm_device_master_add_slave (priv->master, self);
nm_device_master_add_slave (priv->master,
self,
nm_active_connection_get_assumed (active) ? FALSE : TRUE);
nm_log_dbg (LOGD_DEVICE, "(%s): master connection ready; master device %s",
nm_device_get_iface (self),
@ -1952,10 +2021,46 @@ master_ready_cb (NMActiveConnection *active,
static NMActStageReturn
act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
{
return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*
* 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);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
priv->ip4_state = priv->ip6_state = IP_NONE;
iface = nm_device_get_iface (self);
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
/* Assumed connections were already set up outside NetworkManager */
if (!nm_active_connection_get_assumed (active)) {
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
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, reason);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
}
if (nm_active_connection_get_master (active)) {
/* If the master connection is ready for slaves, attach ourselves */
if (nm_active_connection_get_master_ready (active))
@ -1970,47 +2075,10 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
"notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
(GCallback) master_ready_cb,
self);
ret = NM_ACT_STAGE_RETURN_POSTPONE;
/* Postpone */
}
}
return ret;
}
/*
* 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);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
NMActStageReturn ret;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
priv->ip4_state = priv->ip6_state = IP_NONE;
iface = nm_device_get_iface (self);
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason);
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, reason);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
nm_device_activate_schedule_stage2_device_config (self);
} else
nm_device_activate_schedule_stage2_device_config (self);
out:
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface);
@ -2043,17 +2111,6 @@ nm_device_activate_schedule_stage1_device_prepare (NMDevice *self)
static NMActStageReturn
act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (dev);
GSList *iter;
/* If we have slaves that aren't yet enslaved, do that now */
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
SlaveInfo *info = iter->data;
if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_enslave_slave (dev, info->slave, nm_device_get_connection (info->slave));
}
/* Nothing to do */
return NM_ACT_STAGE_RETURN_SUCCESS;
}
@ -2069,10 +2126,13 @@ static gboolean
nm_device_activate_stage2_device_config (gpointer user_data)
{
NMDevice *self = NM_DEVICE (user_data);
const char * iface;
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
NMActStageReturn ret;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
gboolean no_firmware = FALSE;
NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
GSList *iter;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, 0);
@ -2081,23 +2141,33 @@ nm_device_activate_stage2_device_config (gpointer user_data)
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) starting...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_CONFIG, NM_DEVICE_STATE_REASON_NONE);
if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
if (no_firmware)
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING);
else
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
goto out;
/* Assumed connections were already set up outside NetworkManager */
if (!nm_active_connection_get_assumed (active)) {
if (!nm_device_bring_up (self, FALSE, &no_firmware)) {
if (no_firmware)
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_FIRMWARE_MISSING);
else
nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_CONFIG_FAILED);
goto out;
}
ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason);
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, reason);
goto out;
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
}
ret = NM_DEVICE_GET_CLASS (self)->act_stage2_config (self, &reason);
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, reason);
goto out;
/* If we have slaves that aren't yet enslaved, do that now */
for (iter = priv->slaves; iter; iter = g_slist_next (iter)) {
SlaveInfo *info = iter->data;
if (nm_device_get_state (info->slave) == NM_DEVICE_STATE_IP_CONFIG)
nm_device_enslave_slave (self, info->slave, nm_device_get_connection (info->slave));
}
g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 2 of 5 (Device Configure) successful.", iface);
@ -3602,7 +3672,6 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
NMDevice *self = NM_DEVICE (user_data);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
const char *iface;
int ifindex;
NMActiveConnection *master;
NMDevice *master_device;
@ -3615,10 +3684,11 @@ nm_device_activate_stage3_ip_config_start (gpointer user_data)
nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 3 of 5 (IP Configure Start) started...", iface);
nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_NONE);
/* Make sure the interface is up before trying to do anything with it */
ifindex = nm_device_get_ip_ifindex (self);
if (ifindex && !nm_platform_link_is_up (ifindex))
nm_platform_link_set_up (ifindex);
/* Device should be up before we can do anything with it */
if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) {
nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration",
iface, nm_device_get_ip_iface (self));
}
/* If the device is a slave, then we don't do any IP configuration but we
* use the IP config stage to indicate to the master we're ready for
@ -4009,7 +4079,6 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
const char *iface, *method;
NMConnection *connection;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
int ifindex;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, AF_INET);
@ -4023,10 +4092,11 @@ nm_device_activate_ip4_config_commit (gpointer user_data)
connection = nm_act_request_get_connection (req);
g_assert (connection);
/* Make sure the interface is up again just because */
ifindex = nm_device_get_ip_ifindex (self);
if (ifindex && !nm_platform_link_is_up (ifindex))
nm_platform_link_set_up (ifindex);
/* Device should be up before we can do anything with it */
if (!nm_platform_link_is_up (nm_device_get_ip_ifindex (self))) {
nm_log_warn (LOGD_DEVICE, "(%s): interface %s not up for IP configuration",
iface, nm_device_get_ip_iface (self));
}
/* NULL to use the existing priv->dev_ip4_config */
if (!ip4_config_merge_and_apply (self, NULL, TRUE, &reason)) {
@ -4102,7 +4172,6 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
const char *iface;
NMConnection *connection;
NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE;
int ifindex;
/* Clear the activation source ID now that this stage has run */
activation_source_clear (self, FALSE, AF_INET6);
@ -4116,10 +4185,8 @@ nm_device_activate_ip6_config_commit (gpointer user_data)
connection = nm_act_request_get_connection (req);
g_assert (connection);
/* Make sure the interface is up again just because */
ifindex = nm_device_get_ip_ifindex (self);
if (ifindex && !nm_platform_link_is_up (ifindex))
nm_platform_link_set_up (ifindex);
/* Device should be up before we can do anything with it */
g_warn_if_fail (nm_platform_link_is_up (nm_device_get_ip_ifindex (self)));
/* Allow setting MTU etc */
if (NM_DEVICE_GET_CLASS (self)->ip6_config_pre_commit)
@ -4571,28 +4638,17 @@ nm_device_activate (NMDevice *self, NMActRequest *req)
NM_DEVICE_STATE_REASON_NOW_MANAGED);
}
g_assert (nm_device_connection_is_available (self, connection));
priv->act_request = g_object_ref (req);
g_object_notify (G_OBJECT (self), NM_DEVICE_ACTIVE_CONNECTION);
if (priv->state_reason == NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED) {
/* If it's an assumed connection, let the device subclass short-circuit
* the normal connection process and just copy its IP configs from the
* interface.
*/
nm_device_state_changed (self, NM_DEVICE_STATE_IP_CONFIG, NM_DEVICE_STATE_REASON_CONNECTION_ASSUMED);
nm_device_activate_schedule_stage3_ip_config_start (self);
} else {
/* HACK: update the state a bit early to avoid a race between the
* scheduled stage1 handler and nm_policy_device_change_check() thinking
* that the activation request isn't deferred because the deferred bit
* gets cleared a bit too early, when the connection becomes valid.
*/
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
/* HACK: update the state a bit early to avoid a race between the
* scheduled stage1 handler and nm_policy_device_change_check() thinking
* that the activation request isn't deferred because the deferred bit
* gets cleared a bit too early, when the connection becomes valid.
*/
nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE);
nm_device_activate_schedule_stage1_device_prepare (self);
}
nm_device_activate_schedule_stage1_device_prepare (self);
}
/*
@ -6339,8 +6395,107 @@ nm_device_get_state (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->state;
}
static NMIP4Config *
find_ip4_lease_config (NMDevice *device,
NMConnection *connection,
NMIP4Config *ext_ip4_config)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
const char *ip_iface = nm_device_get_ip_iface (device);
GSList *leases, *liter;
NMIP4Config *found = NULL;
g_return_val_if_fail (NM_IS_IP4_CONFIG (ext_ip4_config), NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
leases = nm_dhcp_manager_get_lease_ip_configs (priv->dhcp_manager,
ip_iface,
nm_connection_get_uuid (connection),
FALSE);
for (liter = leases; liter && !found; liter = liter->next) {
NMIP4Config *lease_config = liter->data;
const NMPlatformIP4Address *address = nm_ip4_config_get_address (lease_config, 0);
guint32 gateway = nm_ip4_config_get_gateway (lease_config);
g_assert (address);
if (!nm_ip4_config_address_exists (ext_ip4_config, address))
continue;
if (gateway != nm_ip4_config_get_gateway (ext_ip4_config))
continue;
found = g_object_ref (lease_config);
}
g_slist_free_full (leases, g_object_unref);
return found;
}
static void
update_ip_config (NMDevice *self)
capture_lease_config (NMDevice *device,
NMIP4Config *ext_ip4_config,
NMIP4Config **out_ip4_config,
NMIP6Config *ext_ip6_config,
NMIP6Config **out_ip6_config)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (device);
const GSList *connections, *citer;
guint i;
gboolean dhcp_used = FALSE;
/* Ensure at least one address on the device has a non-infinite lifetime,
* otherwise DHCP cannot possibly be active on the device right now.
*/
if (ext_ip4_config && out_ip4_config) {
for (i = 0; i < nm_ip4_config_get_num_addresses (ext_ip4_config); i++) {
const NMPlatformIP4Address *addr = nm_ip4_config_get_address (ext_ip4_config, i);
if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
dhcp_used = TRUE;
break;
}
}
} else if (ext_ip6_config && out_ip6_config) {
for (i = 0; i < nm_ip6_config_get_num_addresses (ext_ip6_config); i++) {
const NMPlatformIP6Address *addr = nm_ip6_config_get_address (ext_ip6_config, i);
if (addr->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
dhcp_used = TRUE;
break;
}
}
} else {
g_return_if_fail ( (ext_ip6_config && out_ip6_config)
|| (ext_ip4_config && out_ip4_config));
}
if (!dhcp_used)
return;
connections = nm_connection_provider_get_connections (priv->con_provider);
for (citer = connections; citer; citer = citer->next) {
NMConnection *candidate = citer->data;
const char *method;
if (!nm_device_check_connection_compatible (device, candidate, NULL))
continue;
/* IPv4 leases */
method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
if (out_ip4_config && strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0) {
*out_ip4_config = find_ip4_lease_config (device, candidate, ext_ip4_config);
if (*out_ip4_config)
return;
}
/* IPv6 leases */
method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
if (out_ip6_config && strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0) {
/* FIXME: implement find_ip6_lease_config() */
}
}
}
static void
update_ip_config (NMDevice *self, gboolean capture_dhcp)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
int ifindex;
@ -6353,7 +6508,12 @@ update_ip_config (NMDevice *self)
/* IPv4 */
g_clear_object (&priv->ext_ip4_config);
priv->ext_ip4_config = nm_ip4_config_capture (ifindex);
if (priv->ext_ip4_config) {
if (capture_dhcp) {
g_clear_object (&priv->dev_ip4_config);
capture_lease_config (self, priv->ext_ip4_config, &priv->dev_ip4_config, NULL, NULL);
}
if (priv->dev_ip4_config)
nm_ip4_config_subtract (priv->ext_ip4_config, priv->dev_ip4_config);
if (priv->vpn4_config)
@ -6389,6 +6549,12 @@ update_ip_config (NMDevice *self)
}
}
void
nm_device_capture_initial_config (NMDevice *dev)
{
update_ip_config (dev, TRUE);
}
static gboolean
queued_ip_config_change (gpointer user_data)
{
@ -6400,7 +6566,7 @@ queued_ip_config_change (gpointer user_data)
return TRUE;
priv->queued_ip_config_id = 0;
update_ip_config (self);
update_ip_config (self, FALSE);
return FALSE;
}
@ -6562,125 +6728,6 @@ spec_match_list (NMDevice *device, const GSList *specs)
return matched;
}
static gboolean
ip4_match_config (NMDevice *self, NMConnection *connection)
{
NMSettingIP4Config *s_ip4;
int i, num;
GSList *leases, *iter;
NMDHCPManager *dhcp_mgr;
const char *method;
/* Get any saved leases that apply to this connection */
dhcp_mgr = nm_dhcp_manager_get ();
leases = nm_dhcp_manager_get_lease_config (dhcp_mgr,
nm_device_get_iface (self),
nm_connection_get_uuid (connection),
FALSE);
g_object_unref (dhcp_mgr);
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO)) {
gboolean found = FALSE;
/* Find at least one lease's address on the device */
for (iter = leases; iter; iter = g_slist_next (iter)) {
NMIP4Config *ip4_config = iter->data;
const NMPlatformIP4Address *address = nm_ip4_config_get_address (ip4_config, 0);
if (address && nm_platform_ip4_address_exists (nm_device_get_ip_ifindex (self),
address->address,
address->plen)) {
found = TRUE; /* Yay, device has same address as a lease */
break;
}
}
g_slist_free_full (leases, g_object_unref);
return found;
} else {
/* Maybe the connection used to be DHCP and there are stale leases; ignore them */
g_slist_free_full (leases, g_object_unref);
}
if (!strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED)) {
// FIXME: Enforce no ipv4 addresses?
return TRUE;
}
/* 'shared' and 'link-local' aren't supported methods because 'shared'
* requires too much iptables and dnsmasq state to be reclaimed, and
* avahi-autoipd isn't smart enough to allow the link-local address to be
* determined at any point other than when it was first assigned.
*/
if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
return FALSE;
/* Everything below for static addressing */
/* Find all IP4 addresses of this connection on the device */
s_ip4 = nm_connection_get_setting_ip4_config (connection);
if (s_ip4) {
num = nm_setting_ip4_config_get_num_addresses (s_ip4);
for (i = 0; i < num; i++) {
NMIP4Address *addr = nm_setting_ip4_config_get_address (s_ip4, i);
if (!nm_platform_ip4_address_exists (nm_device_get_ip_ifindex (self),
nm_ip4_address_get_address (addr),
nm_ip4_address_get_prefix (addr)))
return FALSE;
}
}
/* Success; all the connection's static IP addresses are assigned to the device */
return TRUE;
}
/**
* nm_device_find_assumable_connection:
* @device: an #NMDevice
* @connections: (element-type NMConnection): a list of connections
*
* Searches @connections for one that matches the currently-configured
* state of @device (in both L2 and L3 configuration). That is, it
* looks for the connection such that if you activated that connection
* on @device, it would result in @device having the configuration
* that it has now. This is used at startup to attempt to match
* already-active devices with corresponding #NMConnections.
*
* Some device types (eg, Wi-Fi) and subtypes (eg, PPPoE) can't be
* matched reliably, so this will always fail for those devices.
*
* Returns: (transfer none): an #NMConnection that matches @device's
* current state, or %NULL if none match.
*/
NMConnection *
nm_device_find_assumable_connection (NMDevice *device, const GSList *connections)
{
const GSList *iter;
g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
if (!NM_DEVICE_GET_CLASS (device)->match_l2_config)
return NULL;
for (iter = connections; iter; iter = iter->next) {
NMConnection *candidate = NM_CONNECTION (iter->data);
if (!nm_device_check_connection_compatible (device, candidate, NULL))
continue;
if (!ip4_match_config (device, candidate))
continue;
/* FIXME: match IPv6 config */
if (NM_DEVICE_GET_CLASS (device)->match_l2_config (device, candidate))
return candidate;
}
return NULL;
}
void
nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
{

View file

@ -180,28 +180,13 @@ typedef struct {
gboolean (* spec_match_list) (NMDevice *self, const GSList *specs);
/* FIXME: We currently support match_l2_config() virtual function for
* compatibility. When match_l2_config() is not present, we use the
* new update_connection() virtual function which should first call
* NMDevice's implementation and then perform type-specific adjustments.
*
* Therefore subclasses that implement the new API *must* leave
* match_l2_config set to NULL and implement update_connection, while
* subclasses that implement the old API *must* set match_l2_config
* (update_connection is ignored).
*
* Subclasses which don't implement any of the APIs for connection assumption
* *should* leave generate_connection NULL.
*
* The update_connection() virtual function is also used for live
* reconfiguration of the connection according to link level changes.
*/
gboolean (* match_l2_config) (NMDevice *self, NMConnection *connection);
/* Update the connection with currently configured L2 settings */
void (* update_connection) (NMDevice *device, NMConnection *connection);
gboolean (* enslave_slave) (NMDevice *self,
NMDevice *slave,
NMConnection *connection);
NMConnection *connection,
gboolean configure);
gboolean (* release_slave) (NMDevice *self,
NMDevice *slave);
@ -246,8 +231,10 @@ void nm_device_set_vpn4_config (NMDevice *dev, NMIP4Config *config)
NMIP6Config * nm_device_get_ip6_config (NMDevice *dev);
void nm_device_set_vpn6_config (NMDevice *dev, NMIP6Config *config);
void nm_device_capture_initial_config (NMDevice *dev);
/* Master */
gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave);
gboolean nm_device_master_add_slave (NMDevice *dev, NMDevice *slave, gboolean configure);
GSList * nm_device_master_get_slaves (NMDevice *dev);
gboolean nm_device_is_master (NMDevice *dev);

View file

@ -25,6 +25,8 @@
#include <ctype.h>
#include "nm-dhcp-dhclient-utils.h"
#include "nm-ip4-config.h"
#include "nm-utils.h"
#define CLIENTID_TAG "send dhcp-client-identifier"
#define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager"
@ -424,3 +426,234 @@ nm_dhcp_dhclient_save_duid (const char *leasefile,
return success;
}
static void
add_lease_option (GHashTable *hash, char *line)
{
char *spc;
size_t len;
/* Find the space after "option" */
spc = strchr (line, ' ');
if (!spc)
return;
/* Find the option tag's data, which is after the second space */
if (g_str_has_prefix (line, "option ")) {
while (g_ascii_isspace (*spc))
spc++;
spc = strchr (spc + 1, ' ');
if (!spc)
return;
}
/* Split the line at the space */
*spc = '\0';
spc++;
/* Kill the ';' at the end of the line, if any */
len = strlen (spc);
if (*(spc + len - 1) == ';')
*(spc + len - 1) = '\0';
/* Strip leading quote */
while (g_ascii_isspace (*spc))
spc++;
if (*spc == '"')
spc++;
/* Strip trailing quote */
len = strlen (spc);
if (len > 0 && spc[len - 1] == '"')
spc[len - 1] = '\0';
if (spc[0])
g_hash_table_insert (hash, g_strdup (line), g_strdup (spc));
}
#define LEASE_INVALID G_MININT64
static GTimeSpan
lease_validity_span (const char *str_expire, GDateTime *now)
{
GDateTime *expire = NULL;
struct tm expire_tm;
GTimeSpan span;
g_return_val_if_fail (now != NULL, LEASE_INVALID);
g_return_val_if_fail (str_expire != NULL, LEASE_INVALID);
/* Skip initial number (day of week?) */
if (!isdigit (*str_expire++))
return LEASE_INVALID;
if (!isspace (*str_expire++))
return LEASE_INVALID;
/* Read lease expiration (in UTC) */
if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm))
return LEASE_INVALID;
expire = g_date_time_new_utc (expire_tm.tm_year + 1900,
expire_tm.tm_mon + 1,
expire_tm.tm_mday,
expire_tm.tm_hour,
expire_tm.tm_min,
expire_tm.tm_sec);
if (!expire)
return LEASE_INVALID;
span = g_date_time_difference (expire, now);
g_date_time_unref (expire);
/* GDateTime only supports a range of less then 10000 years, so span can
* not overflow or be equal to LEASE_INVALID */
return span;
}
/**
* nm_dhcp_dhclient_read_lease_ip_configs:
* @iface: the interface name to match leases with
* @contents: the contents of a dhclient leasefile
* @ipv6: whether to read IPv4 or IPv6 leases
* @now: the current UTC date/time; pass %NULL to automatically use current
* UTC time. Testcases may need a different value for 'now'
*
* Reads dhclient leases from @contents and parses them into either
* #NMIP4Config or #NMIP6Config objects depending on the value of @ipv6.
*
* Returns: a #GSList of #NMIP4Config objects (if @ipv6 is %FALSE) or a list of
* #NMIP6Config objects (if @ipv6 is %TRUE) containing the lease data.
*/
GSList *
nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
const char *contents,
gboolean ipv6,
GDateTime *now)
{
GSList *parsed = NULL, *iter, *leases = NULL;
char **line, **split = NULL;
GHashTable *hash = NULL;
g_return_val_if_fail (contents != NULL, NULL);
split = g_strsplit_set (contents, "\n\r", -1);
if (!split)
return NULL;
for (line = split; line && *line; line++) {
*line = g_strstrip (*line);
if (*line[0] == '#') {
/* Comment */
} else if (!strcmp (*line, "}")) {
/* Lease ends */
parsed = g_slist_append (parsed, hash);
hash = NULL;
} else if (!strcmp (*line, "lease {")) {
/* Beginning of a new lease */
if (hash) {
/* Ignore malformed lease that doesn't end before new one starts */
g_hash_table_destroy (hash);
}
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
} else if (hash && strlen (*line))
add_lease_option (hash, *line);
}
g_strfreev (split);
/* Check if the last lease in the file was properly ended */
if (hash) {
/* Ignore malformed lease that doesn't end before new one starts */
g_hash_table_destroy (hash);
hash = NULL;
}
if (now)
g_date_time_ref (now);
else
now = g_date_time_new_now_utc ();
for (iter = parsed; iter; iter = g_slist_next (iter)) {
NMIP4Config *ip4;
NMPlatformIP4Address address;
const char *value;
GTimeSpan expiry;
guint32 tmp, gw = 0;
hash = iter->data;
/* Make sure this lease is for the interface we want */
value = g_hash_table_lookup (hash, "interface");
if (!value || strcmp (value, iface))
continue;
value = g_hash_table_lookup (hash, "expire");
if (!value)
continue;
expiry = lease_validity_span (value, now);
if (expiry == LEASE_INVALID)
continue;
/* scale expiry to seconds (and CLAMP into the range of guint32) */
expiry = CLAMP (expiry / G_TIME_SPAN_SECOND, 0, G_MAXUINT32-1);
if (expiry <= 0) {
/* the address is already expired. Don't even add it. */
continue;
}
memset (&address, 0, sizeof (address));
/* IP4 address */
value = g_hash_table_lookup (hash, "fixed-address");
if (!value)
continue;
if (!inet_pton (AF_INET, value, &address.address))
continue;
/* Gateway */
value = g_hash_table_lookup (hash, "option routers");
if (!value)
continue;
if (!inet_pton (AF_INET, value, &gw))
continue;
/* Netmask */
value = g_hash_table_lookup (hash, "option subnet-mask");
if (value && inet_pton (AF_INET, value, &tmp))
address.plen = nm_utils_ip4_netmask_to_prefix (tmp);
/* Get default netmask for the IP according to appropriate class. */
if (!address.plen)
address.plen = nm_utils_ip4_get_default_prefix (address.address);
address.lifetime = address.preferred = expiry;
ip4 = nm_ip4_config_new ();
nm_ip4_config_add_address (ip4, &address);
nm_ip4_config_set_gateway (ip4, gw);
value = g_hash_table_lookup (hash, "option domain-name-servers");
if (value) {
char **dns, **dns_iter;
dns = g_strsplit_set (value, ",", -1);
for (dns_iter = dns; dns_iter && *dns_iter; dns_iter++) {
if (inet_pton (AF_INET, *dns_iter, &tmp))
nm_ip4_config_add_nameserver (ip4, tmp);
}
if (dns)
g_strfreev (dns);
}
value = g_hash_table_lookup (hash, "option domain-name");
if (value && value[0])
nm_ip4_config_add_domain (ip4, value);
/* FIXME: static routes */
leases = g_slist_append (leases, ip4);
}
g_date_time_unref (now);
g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy);
return leases;
}

View file

@ -44,5 +44,10 @@ gboolean nm_dhcp_dhclient_save_duid (const char *leasefile,
const char *escaped_duid,
GError **error);
GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
const char *contents,
gboolean ipv6,
GDateTime *now);
#endif /* NM_DHCP_DHCLIENT_UTILS_H */

View file

@ -32,6 +32,7 @@
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <config.h>
@ -135,215 +136,31 @@ get_dhclient_leasefile (const char *iface,
return NULL;
}
static void
add_lease_option (GHashTable *hash, char *line)
{
char *spc;
spc = strchr (line, ' ');
if (!spc) {
nm_log_warn (LOGD_DHCP, "DHCP lease file line '%s' did not contain a space", line);
return;
}
/* If it's an 'option' line, split at second space */
if (g_str_has_prefix (line, "option ")) {
spc = strchr (spc + 1, ' ');
if (!spc) {
nm_log_warn (LOGD_DHCP, "DHCP lease file option line '%s' did not contain a second space",
line);
return;
}
}
/* Split the line at the space */
*spc = '\0';
spc++;
/* Kill the ';' at the end of the line, if any */
if (*(spc + strlen (spc) - 1) == ';')
*(spc + strlen (spc) - 1) = '\0';
/* Treat 'interface' specially */
if (g_str_has_prefix (line, "interface")) {
if (*(spc) == '"')
spc++; /* Jump past the " */
if (*(spc + strlen (spc) - 1) == '"')
*(spc + strlen (spc) - 1) = '\0'; /* Kill trailing " */
}
g_hash_table_insert (hash, g_strdup (line), g_strdup (spc));
}
GSList *
nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid, gboolean ipv6)
nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6)
{
GSList *parsed = NULL, *iter, *leases = NULL;
char *contents = NULL;
char *leasefile;
char **line, **split = NULL;
GHashTable *hash = NULL;
/* IPv6 not supported */
if (ipv6)
return NULL;
GSList *leases = NULL;
leasefile = get_dhclient_leasefile (iface, uuid, FALSE, NULL);
if (!leasefile)
return NULL;
if (!g_file_test (leasefile, G_FILE_TEST_EXISTS))
goto out;
if ( g_file_test (leasefile, G_FILE_TEST_EXISTS)
&& g_file_get_contents (leasefile, &contents, NULL, NULL)
&& contents
&& contents[0])
leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, contents, ipv6, NULL);
if (!g_file_get_contents (leasefile, &contents, NULL, NULL))
goto out;
split = g_strsplit_set (contents, "\n\r", -1);
g_free (contents);
if (!split)
goto out;
for (line = split; line && *line; line++) {
*line = g_strstrip (*line);
if (!strcmp (*line, "}")) {
/* Lease ends */
parsed = g_slist_append (parsed, hash);
hash = NULL;
} else if (!strcmp (*line, "lease {")) {
/* Beginning of a new lease */
if (hash) {
nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
"without ending previous lease",
leasefile);
g_hash_table_destroy (hash);
}
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
} else if (strlen (*line))
add_lease_option (hash, *line);
}
g_strfreev (split);
/* Check if the last lease in the file was properly ended */
if (hash) {
nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started "
"without ending previous lease",
leasefile);
g_hash_table_destroy (hash);
hash = NULL;
}
for (iter = parsed; iter; iter = g_slist_next (iter)) {
NMIP4Config *ip4;
NMPlatformIP4Address address;
const char *data;
guint32 tmp;
guint32 plen;
struct tm expire;
hash = iter->data;
/* Make sure this lease is for the interface we want */
data = g_hash_table_lookup (hash, "interface");
if (!data || strcmp (data, iface))
continue;
data = g_hash_table_lookup (hash, "expire");
if (data) {
time_t now_tt;
struct tm *now;
/* Read lease expiration (in UTC) */
if (!strptime (data, "%w %Y/%m/%d %H:%M:%S", &expire)) {
nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'",
data);
continue;
}
now_tt = time (NULL);
now = gmtime(&now_tt);
/* Ignore this lease if it's already expired */
if (expire.tm_year < now->tm_year)
continue;
else if (expire.tm_year == now->tm_year) {
if (expire.tm_mon < now->tm_mon)
continue;
else if (expire.tm_mon == now->tm_mon) {
if (expire.tm_mday < now->tm_mday)
continue;
else if (expire.tm_mday == now->tm_mday) {
if (expire.tm_hour < now->tm_hour)
continue;
else if (expire.tm_hour == now->tm_hour) {
if (expire.tm_min < now->tm_min)
continue;
else if (expire.tm_min == now->tm_min) {
if (expire.tm_sec <= now->tm_sec)
continue;
}
}
}
}
}
/* If we get this far, the lease hasn't expired */
}
data = g_hash_table_lookup (hash, "fixed-address");
if (!data)
continue;
ip4 = nm_ip4_config_new ();
memset (&address, 0, sizeof (address));
/* IP4 address */
if (!inet_pton (AF_INET, data, &tmp)) {
nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 address '%s'", data);
goto error;
}
address.address = tmp;
/* Netmask */
data = g_hash_table_lookup (hash, "option subnet-mask");
if (data) {
if (!inet_pton (AF_INET, data, &tmp)) {
nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 subnet mask '%s'", data);
goto error;
}
plen = nm_utils_ip4_netmask_to_prefix (tmp);
} else {
/* Get default netmask for the IP according to appropriate class. */
plen = nm_utils_ip4_get_default_prefix (address.address);
}
address.plen = plen;
/* Gateway */
data = g_hash_table_lookup (hash, "option routers");
if (data) {
if (!inet_pton (AF_INET, data, &tmp)) {
nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file IP4 gateway '%s'", data);
goto error;
}
nm_ip4_config_set_gateway (ip4, tmp);
}
nm_ip4_config_add_address (ip4, &address);
leases = g_slist_append (leases, ip4);
continue;
error:
g_object_unref (ip4);
}
out:
g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy);
g_free (leasefile);
g_free (contents);
return leases;
}
static gboolean
merge_dhclient_config (const char *iface,
const char *conf_file,

View file

@ -41,7 +41,9 @@ typedef struct {
GType nm_dhcp_dhclient_get_type (void);
GSList *nm_dhcp_dhclient_get_lease_config (const char *iface, const char *uuid, gboolean ipv6);
GSList *nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6);
const char *nm_dhcp_dhclient_get_path (const char *try_first);

View file

@ -71,12 +71,6 @@ nm_dhcp_dhcpcd_get_path (const char *try_first)
return *path;
}
GSList *
nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid, gboolean ipv6)
{
return NULL;
}
static void
dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
{

View file

@ -41,8 +41,6 @@ typedef struct {
GType nm_dhcp_dhcpcd_get_type (void);
GSList *nm_dhcp_dhcpcd_get_lease_config (const char *iface, const char *uuid, gboolean ipv6);
const char *nm_dhcp_dhcpcd_get_path (const char *try_first);
#endif /* NM_DHCP_DHCPCD_H */

View file

@ -71,7 +71,7 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo
typedef struct {
GType client_type;
GetLeaseConfigFunc get_lease_config_func;
GetLeaseConfigFunc get_lease_ip_configs_func;
NMDBusManager * dbus_mgr;
guint new_conn_id;
@ -313,7 +313,7 @@ get_client_type (const char *client, GError **error)
g_set_error_literal (error,
NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
_("no usable DHCP client could be found."));
return 0;
return G_TYPE_INVALID;
}
}
@ -322,7 +322,7 @@ get_client_type (const char *client, GError **error)
g_set_error_literal (error,
NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
_("'dhclient' could be found."));
return 0;
return G_TYPE_INVALID;
}
return NM_TYPE_DHCP_DHCLIENT;
}
@ -332,7 +332,7 @@ get_client_type (const char *client, GError **error)
g_set_error_literal (error,
NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
_("'dhcpcd' could be found."));
return 0;
return G_TYPE_INVALID;
}
return NM_TYPE_DHCP_DHCPCD;
}
@ -340,7 +340,7 @@ get_client_type (const char *client, GError **error)
g_set_error (error,
NM_DHCP_MANAGER_ERROR, NM_DHCP_MANAGER_ERROR_BAD_CLIENT,
_("unsupported DHCP client '%s'"), client);
return 0;
return G_TYPE_INVALID;
}
NMDHCPManager *
@ -362,15 +362,14 @@ nm_dhcp_manager_get (void)
/* Client-specific setup */
client = nm_config_get_dhcp_client (nm_config_get ());
priv->client_type = get_client_type (client, &error);
if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
priv->get_lease_config_func = nm_dhcp_dhclient_get_lease_config;
else if (priv->client_type == NM_TYPE_DHCP_DHCPCD)
priv->get_lease_config_func = nm_dhcp_dhcpcd_get_lease_config;
else {
priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs;
else if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
error->message);
g_error_free (error);
}
g_clear_error (&error);
priv->clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL,
@ -610,10 +609,10 @@ nm_dhcp_manager_set_hostname_provider (NMDHCPManager *manager,
}
GSList *
nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
const char *iface,
const char *uuid,
gboolean ipv6)
nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
const char *iface,
const char *uuid,
gboolean ipv6)
{
NMDHCPManagerPrivate *priv;
@ -623,10 +622,8 @@ nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
if (priv->get_lease_config_func)
return priv->get_lease_config_func (iface, uuid, ipv6);
nm_log_warn (LOGD_DHCP, "Cannot get a DHCP lease config (no usable DHCP client was found!)");
if (priv->get_lease_ip_configs_func)
return priv->get_lease_ip_configs_func (iface, uuid, ipv6);
return NULL;
}

View file

@ -82,10 +82,10 @@ NMDHCPClient * nm_dhcp_manager_start_ip6 (NMDHCPManager *manager,
guint8 *dhcp_anycast_addr,
gboolean info_only);
GSList * nm_dhcp_manager_get_lease_config (NMDHCPManager *self,
const char *iface,
const char *uuid,
gboolean ipv6);
GSList * nm_dhcp_manager_get_lease_ip_configs (NMDHCPManager *self,
const char *iface,
const char *uuid,
gboolean ipv6);
/* For testing only */
NMIP4Config *nm_dhcp_manager_test_ip4_options_to_config (const char *dhcp_client,

View file

@ -4,6 +4,8 @@ AM_CPPFLAGS = \
-I${top_srcdir}/libnm-util \
-I${top_builddir}/libnm-util \
-I$(top_srcdir)/src/dhcp-manager \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/platform \
$(GLIB_CFLAGS) \
-DTESTDIR="\"$(abs_srcdir)\""
@ -22,5 +24,9 @@ check-local: test-dhcp-dhclient
EXTRA_DIST = \
test-dhclient-duid.leases \
test-dhclient-commented-duid.leases
test-dhclient-commented-duid.leases \
leases/basic.leases \
leases/malformed1.leases \
leases/malformed2.leases \
leases/malformed3.leases

View file

@ -0,0 +1,31 @@
lease {
interface "wlan0";
fixed-address 192.168.1.180;
option subnet-mask 255.255.255.0;
option routers 192.168.1.1;
option dhcp-lease-time 600;
option dhcp-message-type 5;
option domain-name-servers 192.168.1.1;
option dhcp-server-identifier 192.168.1.1;
option broadcast-address 192.168.1.255;
renew 5 2013/11/01 19:56:15;
rebind 5 2013/11/01 20:00:44;
expire 5 2013/11/01 20:01:59;
}
lease {
interface "wlan0";
fixed-address 10.77.52.141;
option subnet-mask 255.0.0.0;
option dhcp-lease-time 1200;
option routers 10.77.52.254;
option dhcp-message-type 5;
option dhcp-server-identifier 10.77.52.254;
option domain-name-servers 8.8.8.8,8.8.4.4;
option dhcp-renewal-time 600;
option dhcp-rebinding-time 1050;
option domain-name "morriesguest.local";
renew 5 2013/11/01 20:01:08;
rebind 5 2013/11/01 20:05:00;
expire 5 2013/11/01 20:06:15;
}

View file

@ -0,0 +1,15 @@
# missing fixed-address option
lease {
interface "wlan0";
option subnet-mask 255.255.255.0;
option routers 192.168.1.1;
option dhcp-lease-time 600;
option dhcp-message-type 5;
option domain-name-servers 192.168.1.1;
option dhcp-server-identifier 192.168.1.1;
option broadcast-address 192.168.1.255;
renew 5 2013/11/01 19:56:15;
rebind 5 2013/11/01 20:00:44;
expire 5 2013/11/01 20:01:59;
}

View file

@ -0,0 +1,15 @@
# missing routers option
lease {
interface "wlan0";
fixed-address 192.168.1.180;
option subnet-mask 255.255.255.0;
option dhcp-lease-time 600;
option dhcp-message-type 5;
option domain-name-servers 192.168.1.1;
option dhcp-server-identifier 192.168.1.1;
option broadcast-address 192.168.1.255;
renew 5 2013/11/01 19:56:15;
rebind 5 2013/11/01 20:00:44;
expire 5 2013/11/01 20:01:59;
}

View file

@ -0,0 +1,15 @@
# missing expire time
lease {
interface "wlan0";
fixed-address 192.168.1.180;
option subnet-mask 255.255.255.0;
option routers 192.168.1.1;
option dhcp-lease-time 600;
option dhcp-message-type 5;
option domain-name-servers 192.168.1.1;
option dhcp-server-identifier 192.168.1.1;
option broadcast-address 192.168.1.255;
renew 5 2013/11/01 19:56:15;
rebind 5 2013/11/01 20:00:44;
}

View file

@ -24,6 +24,7 @@
#include "nm-dhcp-dhclient-utils.h"
#include "nm-utils.h"
#include "nm-ip4-config.h"
#define DEBUG 0
@ -454,6 +455,128 @@ test_write_existing_commented_duid (void)
/*******************************************/
static void
test_read_lease_ip4_config_basic (void)
{
GError *error = NULL;
char *contents = NULL;
gboolean success;
const char *path = TESTDIR "/leases/basic.leases";
GSList *leases;
GDateTime *now;
NMIP4Config *config;
const NMPlatformIP4Address *addr;
guint32 expected_addr;
success = g_file_get_contents (path, &contents, NULL, &error);
g_assert_no_error (error);
g_assert (success);
/* Date from before the least expiration */
now = g_date_time_new_utc (2013, 11, 1, 19, 55, 32);
leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now);
g_assert_cmpint (g_slist_length (leases), ==, 2);
/* IP4Config #1 */
config = g_slist_nth_data (leases, 0);
g_assert (NM_IS_IP4_CONFIG (config));
/* Address */
g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1);
g_assert (inet_aton ("192.168.1.180", (struct in_addr *) &expected_addr));
addr = nm_ip4_config_get_address (config, 0);
g_assert_cmpint (addr->address, ==, expected_addr);
g_assert_cmpint (addr->plen, ==, 24);
/* Gateway */
g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr));
g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr);
/* DNS */
g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 1);
g_assert (inet_aton ("192.168.1.1", (struct in_addr *) &expected_addr));
g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr);
g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 0);
/* IP4Config #2 */
config = g_slist_nth_data (leases, 1);
g_assert (NM_IS_IP4_CONFIG (config));
/* Address */
g_assert_cmpint (nm_ip4_config_get_num_addresses (config), ==, 1);
g_assert (inet_aton ("10.77.52.141", (struct in_addr *) &expected_addr));
addr = nm_ip4_config_get_address (config, 0);
g_assert_cmpint (addr->address, ==, expected_addr);
g_assert_cmpint (addr->plen, ==, 8);
/* Gateway */
g_assert (inet_aton ("10.77.52.254", (struct in_addr *) &expected_addr));
g_assert_cmpint (nm_ip4_config_get_gateway (config), ==, expected_addr);
/* DNS */
g_assert_cmpint (nm_ip4_config_get_num_nameservers (config), ==, 2);
g_assert (inet_aton ("8.8.8.8", (struct in_addr *) &expected_addr));
g_assert_cmpint (nm_ip4_config_get_nameserver (config, 0), ==, expected_addr);
g_assert (inet_aton ("8.8.4.4", (struct in_addr *) &expected_addr));
g_assert_cmpint (nm_ip4_config_get_nameserver (config, 1), ==, expected_addr);
/* Domains */
g_assert_cmpint (nm_ip4_config_get_num_domains (config), ==, 1);
g_assert_cmpstr (nm_ip4_config_get_domain (config, 0), ==, "morriesguest.local");
g_slist_free_full (leases, g_object_unref);
g_date_time_unref (now);
g_free (contents);
}
static void
test_read_lease_ip4_config_expired (void)
{
GError *error = NULL;
char *contents = NULL;
gboolean success;
const char *path = TESTDIR "/leases/basic.leases";
GSList *leases;
GDateTime *now;
success = g_file_get_contents (path, &contents, NULL, &error);
g_assert_no_error (error);
g_assert (success);
/* Date from *after* the lease expiration */
now = g_date_time_new_utc (2013, 12, 1, 19, 55, 32);
leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now);
g_assert (leases == NULL);
g_date_time_unref (now);
g_free (contents);
}
static void
test_read_lease_ip4_config_expect_failure (gconstpointer user_data)
{
GError *error = NULL;
char *contents = NULL;
gboolean success;
GSList *leases;
GDateTime *now;
success = g_file_get_contents ((const char *) user_data, &contents, NULL, &error);
g_assert_no_error (error);
g_assert (success);
/* Date from before the least expiration */
now = g_date_time_new_utc (2013, 11, 1, 1, 1, 1);
leases = nm_dhcp_dhclient_read_lease_ip_configs ("wlan0", contents, FALSE, now);
g_assert (leases == NULL);
g_date_time_unref (now);
g_free (contents);
}
/*******************************************/
int
main (int argc, char **argv)
{
@ -477,6 +600,18 @@ main (int argc, char **argv)
g_test_add_func ("/dhcp/dhclient/write_existing_duid", test_write_existing_duid);
g_test_add_func ("/dhcp/dhclient/write_existing_commented_duid", test_write_existing_commented_duid);
g_test_add_func ("/dhcp/dhclient/leases/ip4-config/basic", test_read_lease_ip4_config_basic);
g_test_add_func ("/dhcp/dhclient/leases/ip4-config/expired", test_read_lease_ip4_config_expired);
g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-address",
TESTDIR "/leases/malformed1.leases",
test_read_lease_ip4_config_expect_failure);
g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-gateway",
TESTDIR "/leases/malformed2.leases",
test_read_lease_ip4_config_expect_failure);
g_test_add_data_func ("/dhcp/dhclient/leases/ip4-config/missing-expire",
TESTDIR "/leases/malformed3.leases",
test_read_lease_ip4_config_expect_failure);
return g_test_run ();
}

View file

@ -55,6 +55,8 @@ typedef struct {
NMActiveConnection *master;
gboolean master_ready;
gboolean assumed;
NMAuthChain *chain;
const char *wifi_shared_permission;
NMActiveConnectionAuthResultFunc result_func;
@ -498,6 +500,21 @@ nm_active_connection_set_master (NMActiveConnection *self, NMActiveConnection *m
check_master_ready (self);
}
void
nm_active_connection_set_assumed (NMActiveConnection *self, gboolean assumed)
{
NMActiveConnectionPrivate *priv = NM_ACTIVE_CONNECTION_GET_PRIVATE (self);
g_return_if_fail (priv->assumed == FALSE);
priv->assumed = assumed;
}
gboolean
nm_active_connection_get_assumed (NMActiveConnection *self)
{
return NM_ACTIVE_CONNECTION_GET_PRIVATE (self)->assumed;
}
/****************************************************************/
static void

View file

@ -129,4 +129,9 @@ gboolean nm_active_connection_get_master_ready (NMActiveConnection *self);
void nm_active_connection_set_master (NMActiveConnection *self,
NMActiveConnection *master);
void nm_active_connection_set_assumed (NMActiveConnection *self,
gboolean assumed);
gboolean nm_active_connection_get_assumed (NMActiveConnection *self);
#endif /* NM_ACTIVE_CONNECTION_H */

View file

@ -14,6 +14,7 @@
*/
#include "nm-connection-provider.h"
#include "nm-utils.h"
GSList *
nm_connection_provider_get_best_connections (NMConnectionProvider *self,
@ -75,6 +76,25 @@ nm_connection_provider_add_connection (NMConnectionProvider *self,
return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->add_connection (self, connection, save_to_disk, error);
}
/**
* nm_connection_provider_get_connection_by_uuid:
* @self: the #NMConnectionProvider
* @uuid: the UUID to search for
*
* Returns: the connection with the given @uuid, or %NULL
*/
NMConnection *
nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self,
const char *uuid)
{
g_return_val_if_fail (NM_IS_CONNECTION_PROVIDER (self), NULL);
g_return_val_if_fail (uuid != NULL, NULL);
g_return_val_if_fail (nm_utils_is_uuid (uuid), NULL);
g_assert (NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid);
return NM_CONNECTION_PROVIDER_GET_INTERFACE (self)->get_connection_by_uuid (self, uuid);
}
/*****************************************************************************/
static void

View file

@ -65,6 +65,9 @@ struct _NMConnectionProvider {
gboolean save_to_disk,
GError **error);
NMConnection * (*get_connection_by_uuid) (NMConnectionProvider *self,
const char *uuid);
/* Signals */
void (*connection_added) (NMConnectionProvider *self, NMConnection *connection);
@ -137,4 +140,7 @@ NMConnection *nm_connection_provider_add_connection (NMConnectionProvider *self,
gboolean save_to_disk,
GError **error);
NMConnection *nm_connection_provider_get_connection_by_uuid (NMConnectionProvider *self,
const char *uuid);
#endif /* NM_CONNECTION_PROVIDER_H */

View file

@ -127,18 +127,44 @@ routes_are_duplicate (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b,
NMIP4Config *
nm_ip4_config_capture (int ifindex)
{
NMIP4Config *config = nm_ip4_config_new ();
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
NMIP4Config *config;
NMIP4ConfigPrivate *priv;
guint i;
gboolean gateway_changed = FALSE;
/* Slaves have no IP configuration */
if (nm_platform_link_get_master (ifindex) > 0)
return NULL;
config = nm_ip4_config_new ();
priv = NM_IP4_CONFIG_GET_PRIVATE (config);
g_array_unref (priv->addresses);
g_array_unref (priv->routes);
priv->addresses = nm_platform_ip4_address_get_all (ifindex);
priv->routes = nm_platform_ip4_route_get_all (ifindex, FALSE);
priv->routes = nm_platform_ip4_route_get_all (ifindex, TRUE);
/* Extract gateway from default route */
for (i = 0; i < priv->routes->len; i++) {
const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
if (route->network == 0) {
if (priv->gateway != route->gateway) {
priv->gateway = route->gateway;
gateway_changed = TRUE;
}
/* Remove the default route from the list */
g_array_remove_index (priv->routes, i);
break;
}
}
/* actually, nobody should be connected to the signal, just to be sure, notify */
_NOTIFY (config, PROP_ADDRESSES);
_NOTIFY (config, PROP_ROUTES);
if (gateway_changed)
_NOTIFY (config, PROP_GATEWAY);
return config;
}
@ -314,9 +340,12 @@ nm_ip4_config_update_setting (const NMIP4Config *config, NMSettingIP4Config *set
nm_setting_ip4_config_add_address (setting, s_addr);
nm_ip4_address_unref (s_addr);
}
if (!method)
/* Only use 'disabled' if the method wasn't previously set */
if (!method && !nm_setting_ip4_config_get_method (setting))
method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, method, NULL);
if (method)
g_object_set (setting, NM_SETTING_IP4_CONFIG_METHOD, method, NULL);
/* Routes */
for (i = 0; i < nroutes; i++) {
@ -955,6 +984,22 @@ nm_ip4_config_get_address (const NMIP4Config *config, guint i)
return &g_array_index (priv->addresses, NMPlatformIP4Address, i);
}
gboolean
nm_ip4_config_address_exists (const NMIP4Config *config,
const NMPlatformIP4Address *needle)
{
NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config);
guint i;
for (i = 0; i < priv->addresses->len; i++) {
const NMPlatformIP4Address *haystack = &g_array_index (priv->addresses, NMPlatformIP4Address, i);
if (needle->address == haystack->address && needle->plen == haystack->plen)
return TRUE;
}
return FALSE;
}
/******************************************************************/
void

View file

@ -83,6 +83,7 @@ void nm_ip4_config_add_address (NMIP4Config *config, const NMPlatformIP4Address
void nm_ip4_config_del_address (NMIP4Config *config, guint i);
guint nm_ip4_config_get_num_addresses (const NMIP4Config *config);
const NMPlatformIP4Address *nm_ip4_config_get_address (const NMIP4Config *config, guint i);
gboolean nm_ip4_config_address_exists (const NMIP4Config *config, const NMPlatformIP4Address *address);
/* Routes */
void nm_ip4_config_reset_routes (NMIP4Config *config);

View file

@ -127,18 +127,44 @@ routes_are_duplicate (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b,
NMIP6Config *
nm_ip6_config_capture (int ifindex)
{
NMIP6Config *config = nm_ip6_config_new ();
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
NMIP6Config *config;
NMIP6ConfigPrivate *priv;
guint i;
gboolean gateway_changed = FALSE;
/* Slaves have no IP configuration */
if (nm_platform_link_get_master (ifindex) > 0)
return NULL;
config = nm_ip6_config_new ();
priv = NM_IP6_CONFIG_GET_PRIVATE (config);
g_array_unref (priv->addresses);
g_array_unref (priv->routes);
priv->addresses = nm_platform_ip6_address_get_all (ifindex);
priv->routes = nm_platform_ip6_route_get_all (ifindex, FALSE);
priv->routes = nm_platform_ip6_route_get_all (ifindex, TRUE);
/* Extract gateway from default route */
for (i = 0; i < priv->routes->len; i++) {
const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
if (IN6_IS_ADDR_UNSPECIFIED (&route->network)) {
if (!IN6_ARE_ADDR_EQUAL (&priv->gateway, &route->gateway)) {
priv->gateway = route->gateway;
gateway_changed = TRUE;
}
/* Remove the default route from the list */
g_array_remove_index (priv->routes, i);
break;
}
}
/* actually, nobody should be connected to the signal, just to be sure, notify */
_NOTIFY (config, PROP_ADDRESSES);
_NOTIFY (config, PROP_ROUTES);
if (gateway_changed)
_NOTIFY (config, PROP_GATEWAY);
return config;
}
@ -285,8 +311,11 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set
NMIP6Address *s_addr;
/* Ignore link-local address. */
if (IN6_IS_ADDR_LINKLOCAL (&address->address))
if (IN6_IS_ADDR_LINKLOCAL (&address->address)) {
if (!method)
method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
continue;
}
/* Detect dynamic address */
if (address->lifetime != NM_PLATFORM_LIFETIME_PERMANENT) {
@ -295,7 +324,7 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set
}
/* Static address found. */
if (!method)
if (!method || strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0)
method = NM_SETTING_IP6_CONFIG_METHOD_MANUAL;
s_addr = nm_ip6_address_new ();
@ -308,9 +337,12 @@ nm_ip6_config_update_setting (const NMIP6Config *config, NMSettingIP6Config *set
nm_setting_ip6_config_add_address (setting, s_addr);
nm_ip6_address_unref (s_addr);
}
if (!method)
method = NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL;
g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, method, NULL);
/* Only use 'ignore' if the method wasn't previously set */
if (!method && !nm_setting_ip6_config_get_method (setting))
method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
if (method)
g_object_set (setting, NM_SETTING_IP6_CONFIG_METHOD, method, NULL);
/* Routes */
for (i = 0; i < nroutes; i++) {
@ -854,6 +886,23 @@ nm_ip6_config_get_address (const NMIP6Config *config, guint i)
return &g_array_index (priv->addresses, NMPlatformIP6Address, i);
}
gboolean
nm_ip6_config_address_exists (const NMIP6Config *config,
const NMPlatformIP6Address *needle)
{
NMIP6ConfigPrivate *priv = NM_IP6_CONFIG_GET_PRIVATE (config);
guint i;
for (i = 0; i < priv->addresses->len; i++) {
const NMPlatformIP6Address *haystack = &g_array_index (priv->addresses, NMPlatformIP6Address, i);
if ( IN6_ARE_ADDR_EQUAL (&needle->address, &haystack->address)
&& needle->plen == haystack->plen)
return TRUE;
}
return FALSE;
}
/******************************************************************/
void

View file

@ -82,6 +82,7 @@ void nm_ip6_config_add_address (NMIP6Config *config, const NMPlatformIP6Address
void nm_ip6_config_del_address (NMIP6Config *config, guint i);
guint nm_ip6_config_get_num_addresses (const NMIP6Config *config);
const NMPlatformIP6Address *nm_ip6_config_get_address (const NMIP6Config *config, guint i);
gboolean nm_ip6_config_address_exists (const NMIP6Config *config, const NMPlatformIP6Address *address);
/* Routes */
void nm_ip6_config_reset_routes (NMIP6Config *config);

View file

@ -151,7 +151,7 @@ static void bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
const char *object_path,
gpointer user_data);
static void add_device (NMManager *self, NMDevice *device);
static void add_device (NMManager *self, NMDevice *device, gboolean nm_created);
static void remove_device (NMManager *self, NMDevice *device, gboolean quitting);
static void hostname_provider_init (NMHostnameProvider *provider_class);
@ -166,6 +166,7 @@ static NMActiveConnection *_new_active_connection (NMManager *self,
static void policy_activating_device_changed (GObject *object, GParamSpec *pspec, gpointer user_data);
static NMDevice *find_device_by_ip_iface (NMManager *self, const gchar *iface);
static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface);
static void rfkill_change_wifi (const char *desc, gboolean enabled);
@ -176,6 +177,14 @@ platform_link_added_cb (NMPlatform *platform,
NMPlatformReason reason,
gpointer user_data);
static gboolean find_master (NMManager *self,
NMConnection *connection,
NMDevice *device,
NMConnection **out_master_connection,
NMDevice **out_master_device);
static void nm_manager_update_state (NMManager *manager);
#define SSD_POKE_INTERVAL 120
#define ORIGDEV_TAG "originating-device"
@ -241,8 +250,6 @@ typedef struct {
guint timestamp_update_id;
GHashTable *nm_bridges;
/* Track auto-activation for software devices */
GHashTable *noauto_sw_devices;
@ -372,6 +379,8 @@ active_connection_state_changed (NMActiveConnection *active,
if (!priv->ac_cleanup_id)
priv->ac_cleanup_id = g_idle_add (_active_connection_cleanup, self);
}
nm_manager_update_state (self);
}
static void
@ -572,7 +581,7 @@ modem_added (NMModemManager *modem_manager,
/* Make the new modem device */
device = nm_device_modem_new (modem, driver);
if (device)
add_device (self, device);
add_device (self, device, TRUE);
}
static void
@ -640,12 +649,59 @@ checked_connectivity (GObject *object, GAsyncResult *result, gpointer user_data)
g_object_unref (manager);
}
static NMState
find_best_device_state (NMManager *manager, gboolean *want_connectivity_check)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
NMState best_state = NM_STATE_DISCONNECTED;
GSList *iter;
for (iter = priv->active_connections; iter; iter = iter->next) {
NMActiveConnection *ac = NM_ACTIVE_CONNECTION (iter->data);
NMActiveConnectionState ac_state = nm_active_connection_get_state (ac);
switch (ac_state) {
case NM_ACTIVE_CONNECTION_STATE_ACTIVATED:
if ( nm_active_connection_get_default (ac)
|| nm_active_connection_get_default6 (ac)) {
nm_connectivity_set_online (priv->connectivity, TRUE);
if (nm_connectivity_get_state (priv->connectivity) == NM_CONNECTIVITY_FULL) {
*want_connectivity_check = FALSE;
return NM_STATE_CONNECTED_GLOBAL;
}
best_state = NM_STATE_CONNECTING;
*want_connectivity_check = TRUE;
} else {
if (best_state < NM_STATE_CONNECTING)
best_state = NM_STATE_CONNECTED_LOCAL;
}
break;
case NM_ACTIVE_CONNECTION_STATE_ACTIVATING:
if (!nm_active_connection_get_assumed (ac)) {
if (best_state != NM_STATE_CONNECTED_GLOBAL)
best_state = NM_STATE_CONNECTING;
}
break;
case NM_ACTIVE_CONNECTION_STATE_DEACTIVATING:
if (!nm_active_connection_get_assumed (ac)) {
if (best_state < NM_STATE_DISCONNECTING)
best_state = NM_STATE_DISCONNECTING;
}
break;
default:
break;
}
}
return best_state;
}
static void
nm_manager_update_state (NMManager *manager)
{
NMManagerPrivate *priv;
NMState new_state = NM_STATE_DISCONNECTED;
GSList *iter;
gboolean want_connectivity_check = FALSE;
g_return_if_fail (NM_IS_MANAGER (manager));
@ -654,30 +710,8 @@ nm_manager_update_state (NMManager *manager)
if (manager_sleeping (manager))
new_state = NM_STATE_ASLEEP;
else {
for (iter = priv->devices; iter; iter = iter->next) {
NMDevice *dev = NM_DEVICE (iter->data);
NMDeviceState state = nm_device_get_state (dev);
if (state == NM_DEVICE_STATE_ACTIVATED) {
nm_connectivity_set_online (priv->connectivity, TRUE);
if (nm_connectivity_get_state (priv->connectivity) != NM_CONNECTIVITY_FULL) {
new_state = NM_STATE_CONNECTING;
want_connectivity_check = TRUE;
} else {
new_state = NM_STATE_CONNECTED_GLOBAL;
break;
}
}
if (nm_device_is_activating (dev))
new_state = NM_STATE_CONNECTING;
else if (new_state != NM_STATE_CONNECTING) {
if (state == NM_DEVICE_STATE_DEACTIVATING)
new_state = NM_STATE_DISCONNECTING;
}
}
}
else
new_state = find_best_device_state (manager, &want_connectivity_check);
if (new_state == NM_STATE_CONNECTING && want_connectivity_check) {
nm_connectivity_check_async (priv->connectivity,
@ -710,8 +744,6 @@ manager_device_state_changed (NMDevice *device,
default:
break;
}
nm_manager_update_state (self);
}
static void device_has_pending_action_changed (NMDevice *device,
@ -764,18 +796,20 @@ remove_device (NMManager *manager, NMDevice *device, gboolean quitting)
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (nm_device_get_managed (device)) {
/* When quitting, we want to leave up interfaces & connections
* that can be taken over again (ie, "assumed") when NM restarts
* so that '/etc/init.d/NetworkManager restart' will not distrupt
* networking for interfaces that support connection assumption.
* All other devices get unmanaged when NM quits so that their
* connections get torn down and the interface is deactivated.
/* Leave configured interfaces up when quitting so they can be
* taken over again if NM starts up, and to ensure connectivity while
* NM is gone. Assumed connections don't get taken down even if they
* haven't been fully activated.
*/
if ( !nm_device_can_assume_connections (device)
|| (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
|| !quitting)
nm_device_set_manager_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
|| !quitting) {
NMActRequest *req = nm_device_get_act_request (device);
if (!req || !nm_active_connection_get_assumed (NM_ACTIVE_CONNECTION (req)))
nm_device_set_manager_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
}
}
g_signal_handlers_disconnect_matched (device, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, manager);
@ -1096,90 +1130,6 @@ connection_needs_virtual_device (NMConnection *connection)
/***************************/
/* FIXME: remove when we handle bridges non-destructively */
#define NM_BRIDGE_FILE NMRUNDIR "/nm-bridges"
static void
read_nm_created_bridges (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
char *contents;
char **lines, **iter;
GTimeVal tv;
glong ts;
if (!g_file_get_contents (NM_BRIDGE_FILE, &contents, NULL, NULL))
return;
g_get_current_time (&tv);
lines = g_strsplit_set (contents, "\n", 0);
g_free (contents);
for (iter = lines; iter && *iter; iter++) {
if (g_str_has_prefix (*iter, "ts=")) {
errno = 0;
ts = strtol (*iter + 3, NULL, 10);
/* allow 30 minutes time difference before we ignore the file */
if (errno || ABS (tv.tv_sec - ts) > 1800)
goto out;
} else if (g_str_has_prefix (*iter, "iface="))
g_hash_table_insert (priv->nm_bridges, g_strdup (*iter + 6), GUINT_TO_POINTER (1));
}
out:
g_strfreev (lines);
unlink (NM_BRIDGE_FILE);
}
static void
write_nm_created_bridges (NMManager *self)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GString *br_list;
GSList *iter;
GError *error = NULL;
GTimeVal tv;
gboolean found = FALSE;
/* write out nm-created bridges list */
br_list = g_string_sized_new (50);
/* Timestamp is first line */
g_get_current_time (&tv);
g_string_append_printf (br_list, "ts=%ld\n", tv.tv_sec);
for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
NMDevice *device = iter->data;
if (nm_device_get_device_type (device) == NM_DEVICE_TYPE_BRIDGE) {
g_string_append_printf (br_list, "iface=%s\n", nm_device_get_iface (device));
found = TRUE;
}
}
if (found) {
if (!g_file_set_contents (NM_BRIDGE_FILE, br_list->str, -1, &error)) {
nm_log_warn (LOGD_BRIDGE, "Failed to write NetworkManager-created bridge list; "
"on restart bridges may not be recognized. (%s)",
error ? error->message : "unknown");
g_clear_error (&error);
}
}
g_string_free (br_list, TRUE);
}
static gboolean
bridge_created_by_nm (NMManager *self, const char *iface)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
return (priv->nm_bridges && g_hash_table_lookup (priv->nm_bridges, iface));
}
/***************************/
/**
* system_create_virtual_device:
* @self: the #NMManager
@ -1229,12 +1179,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
} else if (nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)) {
device = nm_device_team_new_for_connection (connection);
} else if (nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME)) {
/* FIXME: remove when we handle bridges non-destructively */
if (nm_platform_link_get_ifindex (iface) > 0 && !bridge_created_by_nm (self, iface)) {
nm_log_warn (LOGD_DEVICE, "(%s): cannot use existing bridge for '%s'",
iface, nm_connection_get_id (connection));
} else
device = nm_device_bridge_new_for_connection (connection);
device = nm_device_bridge_new_for_connection (connection);
} else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) {
device = nm_device_vlan_new_for_connection (connection, parent);
} else if (nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)) {
@ -1243,7 +1188,7 @@ system_create_virtual_device (NMManager *self, NMConnection *connection)
if (device) {
nm_device_set_is_nm_owned (device, TRUE);
add_device (self, device);
add_device (self, device, TRUE);
}
g_signal_handlers_unblock_by_func (nm_platform_get (), G_CALLBACK (platform_link_added_cb), self);
@ -1756,24 +1701,15 @@ local_slist_free (void *loc)
}
/**
* get_connection:
* get_existing_connection:
* @manager: #NMManager instance
* @device: #NMDevice instance
*
* Returns one of the following:
*
* 1) An existing #NMSettingsConnection to be assumed.
*
* 2) A generated #NMConnection to be assumed. You can distinguish this
* case using NM_IS_SETTINGS_CONNECTION().
*
* 3) %NULL when no connection was detected or the @device doesn't support
* generating connections.
*
* Supports both nm-device's match_l2_config() and update_connection().
* Returns: a #NMSettingsConnection to be assumed by the device, or %NULL if
* the device does not support assuming existing connections.
*/
static NMConnection *
get_connection (NMManager *manager, NMDevice *device)
get_existing_connection (NMManager *manager, NMDevice *device)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
@ -1782,24 +1718,7 @@ get_connection (NMManager *manager, NMDevice *device)
GSList *iter;
GError *error = NULL;
/* We still support the older API to match a NMDevice object to an
* existing connection using nm_device_find_assumable_connection().
*
* When the older API is still available for a particular device
* type, we use it. To opt for the newer interface, the NMDevice
* subclass must omit the match_l2_config virtual function
* implementation.
*/
if (NM_DEVICE_GET_CLASS (device)->match_l2_config) {
NMConnection *candidate = nm_device_find_assumable_connection (device, connections);
if (candidate) {
nm_log_info (LOGD_DEVICE, "(%s): Found matching connection '%s' (legacy API)",
nm_device_get_iface (device),
nm_connection_get_id (candidate));
return candidate;
}
}
nm_device_capture_initial_config (device);
/* The core of the API is nm_device_generate_connection() function and
* update_connection() virtual method and the convenient connection_type
@ -1808,11 +1727,8 @@ get_connection (NMManager *manager, NMDevice *device)
* returns NULL.
*/
connection = nm_device_generate_connection (device);
if (!connection) {
nm_log_info (LOGD_DEVICE, "(%s): No existing connection detected.",
nm_device_get_iface (device));
if (!connection)
return NULL;
}
/* Now we need to compare the generated connection to each configured
* connection. The comparison function is the heart of the connection
@ -1827,7 +1743,7 @@ get_connection (NMManager *manager, NMDevice *device)
NMConnection *candidate = NM_CONNECTION (iter->data);
if (nm_connection_compare (connection, candidate, NM_SETTING_COMPARE_FLAG_CANDIDATE)) {
nm_log_info (LOGD_DEVICE, "(%s): Found matching connection: '%s'",
nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
nm_device_get_iface (device),
nm_connection_get_id (candidate));
g_object_unref (connection);
@ -1835,9 +1751,9 @@ get_connection (NMManager *manager, NMDevice *device)
}
}
nm_log_info (LOGD_DEVICE, "(%s): Using generated connection: '%s'",
nm_device_get_iface (device),
nm_connection_get_id (connection));
nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
nm_device_get_iface (device),
nm_connection_get_id (connection));
added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
if (!added) {
@ -1853,14 +1769,14 @@ get_connection (NMManager *manager, NMDevice *device)
}
static void
add_device (NMManager *self, NMDevice *device)
add_device (NMManager *self, NMDevice *device, gboolean nm_created)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
const char *iface, *driver, *type_desc;
char *path;
static guint32 devcount = 0;
const GSList *unmanaged_specs;
NMConnection *connection;
NMConnection *connection = NULL;
gboolean enabled = FALSE;
RfKillType rtype;
NMDeviceType devtype;
@ -1946,7 +1862,9 @@ add_device (NMManager *self, NMDevice *device)
nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path);
g_free (path);
connection = get_connection (self, device);
/* Don't bother generating a connection for devices NM just created */
if (!nm_created)
connection = get_existing_connection (self, device);
/* Start the device if it's supposed to be managed */
unmanaged_specs = nm_settings_get_unmanaged_specs (priv->settings);
@ -1967,9 +1885,7 @@ add_device (NMManager *self, NMDevice *device)
system_create_virtual_devices (self);
/* If the device has a connection it can assume, do that now */
if ( connection
&& nm_device_is_available (device)
&& nm_device_connection_is_available (device, connection)) {
if (connection) {
NMActiveConnection *active;
NMAuthSubject *subject;
GError *error = NULL;
@ -1985,6 +1901,22 @@ add_device (NMManager *self, NMDevice *device)
subject = nm_auth_subject_new_internal ();
active = _new_active_connection (self, connection, NULL, device, subject, &error);
if (active) {
NMDevice *master = NULL;
NMActRequest *master_req;
/* If the device is a slave or VLAN, find the master ActiveConnection */
if (find_master (self, connection, device, NULL, &master) && master) {
master_req = nm_device_get_act_request (master);
if (master_req)
nm_active_connection_set_master (active, NM_ACTIVE_CONNECTION (master_req));
else {
nm_log_warn (LOGD_DEVICE, "(%s): master device %s not activating!",
nm_device_get_iface (device),
nm_device_get_iface (master));
}
}
nm_active_connection_set_assumed (active, TRUE);
nm_active_connection_export (active);
active_connection_add (self, active);
nm_device_activate (device, NM_ACT_REQUEST (active));
@ -2031,7 +1963,7 @@ bluez_manager_bdaddr_added_cb (NMBluezManager *bluez_mgr,
has_dun && has_nap ? " " : "",
has_nap ? "NAP" : "");
add_device (manager, device);
add_device (manager, device, TRUE);
}
}
@ -2277,11 +2209,7 @@ platform_link_added_cb (NMPlatform *platform,
device = nm_device_team_new (plink);
break;
case NM_LINK_TYPE_BRIDGE:
/* FIXME: always create device when we handle bridges non-destructively */
if (bridge_created_by_nm (self, plink->name))
device = nm_device_bridge_new (plink);
else
nm_log_info (LOGD_BRIDGE, "(%s): ignoring bridge not created by NetworkManager", plink->name);
device = nm_device_bridge_new (plink);
break;
case NM_LINK_TYPE_VLAN:
/* Have to find the parent device */
@ -2335,7 +2263,7 @@ platform_link_added_cb (NMPlatform *platform,
}
if (device)
add_device (self, device);
add_device (self, device, FALSE);
}
static void
@ -2372,7 +2300,7 @@ atm_device_added_cb (NMAtmManager *atm_mgr,
device = nm_device_adsl_new (sysfs_path, iface, driver);
if (device)
add_device (self, device);
add_device (self, device, FALSE);
}
static void
@ -4157,13 +4085,6 @@ nm_manager_start (NMManager *self)
system_unmanaged_devices_changed_cb (priv->settings, NULL, self);
system_hostname_changed_cb (priv->settings, NULL, self);
/* FIXME: remove when we handle bridges non-destructively */
/* Read a list of bridges NM managed when it last quit, and only
* manage those bridges to avoid conflicts with external tools.
*/
priv->nm_bridges = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
read_nm_created_bridges (self);
nm_platform_query_devices ();
nm_atm_manager_query_devices (priv->atm_mgr);
nm_bluez_manager_query_devices (priv->bluez_mgr);
@ -4174,10 +4095,6 @@ nm_manager_start (NMManager *self)
*/
system_create_virtual_devices (self);
/* FIXME: remove when we handle bridges non-destructively */
g_hash_table_unref (priv->nm_bridges);
priv->nm_bridges = NULL;
check_if_startup_complete (self);
}
@ -4623,9 +4540,6 @@ dispose (GObject *object)
nm_auth_changed_func_unregister (authority_changed_cb, manager);
/* FIXME: remove when we handle bridges non-destructively */
write_nm_created_bridges (manager);
/* Remove all devices */
while (priv->devices)
remove_device (manager, NM_DEVICE (priv->devices->data), TRUE);

View file

@ -1060,14 +1060,18 @@ refresh_object (NMPlatform *platform, struct nl_object *object, gboolean removed
announce_object (platform, cached_object, REMOVED, reason);
}
} else {
g_return_val_if_fail (kernel_object, FALSE);
if (!kernel_object)
return FALSE;
hack_empty_master_iff_lower_up (platform, kernel_object);
if (cached_object)
nl_cache_remove (cached_object);
nle = nl_cache_add (cache, kernel_object);
g_return_val_if_fail (!nle, FALSE);
if (nle) {
nm_log_dbg (LOGD_PLATFORM, "refresh_object(reason %d) failed during nl_cache_add with %d", reason, nle);
return FALSE;
}
announce_object (platform, kernel_object, cached_object ? CHANGED : ADDED, reason);

View file

@ -267,37 +267,6 @@ nm_platform_query_devices (void)
g_array_unref (links_array);
}
static int
compare_links (gconstpointer a, gconstpointer b)
{
NMPlatformLink *link_a = (NMPlatformLink *) a;
NMPlatformLink *link_b = (NMPlatformLink *) b;
int sortindex_a, sortindex_b;
/* We mostly want to sort by ifindex. However, slaves should sort
* before their masters, and children (eg, VLANs) should sort after
* their parents.
*/
if (link_a->master)
sortindex_a = link_a->master * 3 - 1;
else if (link_a->parent)
sortindex_a = link_a->parent * 3 + 1;
else
sortindex_a = link_a->ifindex * 3;
if (link_b->master)
sortindex_b = link_b->master * 3 - 1;
else if (link_b->parent)
sortindex_b = link_b->parent * 3 + 1;
else
sortindex_b = link_b->ifindex * 3;
if (sortindex_a == sortindex_b)
return link_a->ifindex - link_b->ifindex;
else
return sortindex_a - sortindex_b;
}
/**
* nm_platform_link_get_all:
*
@ -307,15 +276,100 @@ compare_links (gconstpointer a, gconstpointer b)
GArray *
nm_platform_link_get_all (void)
{
GArray *links;
GArray *links, *result;
guint i, j, nresult;
GHashTable *unseen;
NMPlatformLink *item;
reset_error ();
g_return_val_if_fail (klass->link_get_all, NULL);
links = klass->link_get_all (platform);
g_array_sort (links, compare_links);
return links;
if (!links || links->len == 0)
return links;
unseen = g_hash_table_new (g_direct_hash, g_direct_equal);
for (i = 0; i < links->len; i++) {
item = &g_array_index (links, NMPlatformLink, i);
if (item->ifindex <= 0 || g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))) {
g_warn_if_reached ();
item->ifindex = 0;
continue;
}
g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL);
}
#ifndef G_DISABLE_ASSERT
/* Ensure that link_get_all returns a consistent and valid result. */
for (i = 0; i < links->len; i++) {
item = &g_array_index (links, NMPlatformLink, i);
if (!item->ifindex)
continue;
if (item->master != 0) {
g_warn_if_fail (item->master > 0);
g_warn_if_fail (item->master != item->ifindex);
g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)));
}
if (item->parent != 0) {
g_warn_if_fail (item->parent > 0);
g_warn_if_fail (item->parent != item->ifindex);
g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
}
}
#endif
/* Re-order the links list such that children/slaves come after all ancestors */
nresult = g_hash_table_size (unseen);
result = g_array_sized_new (TRUE, TRUE, sizeof (NMPlatformLink), nresult);
g_array_set_size (result, nresult);
j = 0;
do {
gboolean found_something = FALSE;
guint first_idx = G_MAXUINT;
for (i = 0; i < links->len; i++) {
item = &g_array_index (links, NMPlatformLink, i);
if (!item->ifindex)
continue;
if (first_idx == G_MAXUINT)
first_idx = i;
g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex)));
if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)))
continue;
if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)))
continue;
g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
g_array_index (result, NMPlatformLink, j++) = *item;
item->ifindex = 0;
found_something = TRUE;
}
if (!found_something) {
/* there is a circle, pop the first (remaining) element from the list */
g_warn_if_reached ();
item = &g_array_index (links, NMPlatformLink, first_idx);
g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
g_array_index (result, NMPlatformLink, j++) = *item;
item->ifindex = 0;
}
} while (j < nresult);
g_hash_table_destroy (unseen);
g_array_free (links, TRUE);
return result;
}
/**

View file

@ -124,16 +124,16 @@ typedef struct {
in_addr_t address;
int plen;
guint32 timestamp;
guint32 lifetime;
guint32 preferred;
guint32 lifetime; /* seconds */
guint32 preferred; /* seconds */
} NMPlatformIP4Address;
typedef struct {
int ifindex;
struct in6_addr address;
int plen;
guint32 timestamp;
guint32 lifetime;
guint32 timestamp; /* seconds */
guint32 lifetime; /* seconds */
guint32 preferred;
guint flags; /* ifa_flags from <linux/if_addr.h>, field type "unsigned int" is as used in rtnl_addr_get_flags. */
} NMPlatformIP6Address;

View file

@ -1730,6 +1730,12 @@ has_connections_loaded (NMConnectionProvider *provider)
return priv->connections_loaded;
}
static NMConnection *
cp_get_connection_by_uuid (NMConnectionProvider *provider, const char *uuid)
{
return NM_CONNECTION (nm_settings_get_connection_by_uuid (NM_SETTINGS (provider), uuid));
}
/***************************************************************/
NMSettings *
@ -1765,6 +1771,7 @@ connection_provider_init (NMConnectionProvider *cp_class)
cp_class->get_connections = get_connections;
cp_class->has_connections_loaded = has_connections_loaded;
cp_class->add_connection = _nm_connection_provider_add_connection;
cp_class->get_connection_by_uuid = cp_get_connection_by_uuid;
}
static void

View file

@ -212,7 +212,7 @@ find_by_path (SCPluginIfcfg *self, const char *path)
g_hash_table_iter_init (&iter, priv->connections);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
if (g_str_equal (path, nm_ifcfg_connection_get_path (candidate)))
if (g_strcmp0 (path, nm_ifcfg_connection_get_path (candidate)) == 0)
return candidate;
}
return NULL;
@ -445,8 +445,11 @@ read_connections (SCPluginIfcfg *plugin)
oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_iter_init (&iter, priv->connections);
while (g_hash_table_iter_next (&iter, NULL, &value))
g_hash_table_insert (oldconns, g_strdup (nm_ifcfg_connection_get_path (value)), value);
while (g_hash_table_iter_next (&iter, NULL, &value)) {
const char *ifcfg_path = nm_ifcfg_connection_get_path (value);
if (ifcfg_path)
g_hash_table_insert (oldconns, g_strdup (ifcfg_path), value);
}
while ((item = g_dir_read_name (dir))) {
char *full_path, *old_path;