summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2013-11-08 16:52:33 -0600
committerDan Williams <dcbw@redhat.com>2013-11-08 16:52:33 -0600
commit465458a206dcf7a568d784ff58efff4d138356eb (patch)
treea885a7f1a199ba15a9530744794d0106a0eead11
parent6b6265ebebd7342a0d414bd1370e7af0e472d522 (diff)
parentf4139112391794fd8f4bbf68988571065c96f443 (diff)
downloadNetworkManager-465458a206dcf7a568d784ff58efff4d138356eb.tar.gz
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
-rw-r--r--include/NetworkManager.h3
-rw-r--r--introspection/nm-device.xml5
-rw-r--r--src/devices/nm-device-bond.c81
-rw-r--r--src/devices/nm-device-bridge.c99
-rw-r--r--src/devices/nm-device-bridge.h2
-rw-r--r--src/devices/nm-device-ethernet.c25
-rw-r--r--src/devices/nm-device-generic.c10
-rw-r--r--src/devices/nm-device-infiniband.c38
-rw-r--r--src/devices/nm-device-team.c175
-rw-r--r--src/devices/nm-device-team.h2
-rw-r--r--src/devices/nm-device-vlan.c104
-rw-r--r--src/devices/nm-device.c505
-rw-r--r--src/devices/nm-device.h25
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c233
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.h5
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c205
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.h4
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c6
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c33
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h8
-rw-r--r--src/dhcp-manager/tests/Makefile.am8
-rw-r--r--src/dhcp-manager/tests/leases/basic.leases31
-rw-r--r--src/dhcp-manager/tests/leases/malformed1.leases15
-rw-r--r--src/dhcp-manager/tests/leases/malformed2.leases15
-rw-r--r--src/dhcp-manager/tests/leases/malformed3.leases15
-rw-r--r--src/dhcp-manager/tests/test-dhcp-dhclient.c135
-rw-r--r--src/nm-active-connection.c17
-rw-r--r--src/nm-active-connection.h5
-rw-r--r--src/nm-connection-provider.c20
-rw-r--r--src/nm-connection-provider.h6
-rw-r--r--src/nm-ip4-config.c55
-rw-r--r--src/nm-ip4-config.h1
-rw-r--r--src/nm-ip6-config.c65
-rw-r--r--src/nm-ip6-config.h1
-rw-r--r--src/nm-manager.c308
-rw-r--r--src/platform/nm-linux-platform.c8
-rw-r--r--src/platform/nm-platform.c122
-rw-r--r--src/platform/nm-platform.h8
-rw-r--r--src/settings/nm-settings.c7
-rw-r--r--src/settings/plugins/ifcfg-rh/plugin.c9
41 files changed, 1585 insertions, 836 deletions
diff --git a/include/NetworkManager.h b/include/NetworkManager.h
index 192f9a5178..46747a6529 100644
--- a/include/NetworkManager.h
+++ b/include/NetworkManager.h
@@ -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;
diff --git a/introspection/nm-device.xml b/introspection/nm-device.xml
index d9e7702d14..2332bdd874 100644
--- a/introspection/nm-device.xml
+++ b/introspection/nm-device.xml
@@ -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">
diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c
index 923af8269d..8d9939b096 100644
--- a/src/devices/nm-device-bond.c
+++ b/src/devices/nm-device-bond.c
@@ -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;
diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c
index 856eb83b3a..92cc9f2a77 100644
--- a/src/devices/nm-device-bridge.c
+++ b/src/devices/nm-device-bridge.c
@@ -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;
diff --git a/src/devices/nm-device-bridge.h b/src/devices/nm-device-bridge.h
index 5570c73555..4194f5a411 100644
--- a/src/devices/nm-device-bridge.h
+++ b/src/devices/nm-device-bridge.h
@@ -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 */
diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c
index 83f27b86b3..7b1c248eb8 100644
--- a/src/devices/nm-device-ethernet.c
+++ b/src/devices/nm-device-ethernet.c
@@ -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);
+
+ mac_prop = NULL;
+ if (mac && memcmp (priv->perm_hw_addr, mac, ETH_ALEN))
+ mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS;
+ }
- g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, address, NULL);
+ 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 */
diff --git a/src/devices/nm-device-generic.c b/src/devices/nm-device-generic.c
index f58c2ff79f..e435f5dff0 100644
--- a/src/devices/nm-device-generic.c
+++ b/src/devices/nm-device-generic.c
@@ -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
diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c
index b502fe70e2..1c91449e4e 100644
--- a/src/devices/nm-device-infiniband.c
+++ b/src/devices/nm-device-infiniband.c
@@ -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 */
diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c
index 1fe7caf927..8f119dd85f 100644
--- a/src/devices/nm-device-team.c
+++ b/src/devices/nm-device-team.c
@@ -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;
-
- 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;
+ 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;
+ }
}
- }
#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;
diff --git a/src/devices/nm-device-team.h b/src/devices/nm-device-team.h
index ebe5ae7d9f..fe1275c6b0 100644
--- a/src/devices/nm-device-team.h
+++ b/src/devices/nm-device-team.h
@@ -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 */
diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c
index ecd12f1857..ea74d65385 100644
--- a/src/devices/nm-device-vlan.c
+++ b/src/devices/nm-device-vlan.c
@@ -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);
+}
- /* 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;
+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;
- /* FIXME: any more L2 checks? */
- return TRUE;
+ 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);
+ }
+
+ 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);
+ }
+
+ 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
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 886d417eaa..1a0bcdca09 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -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,29 +2021,7 @@ master_ready_cb (NMActiveConnection *active,
static NMActStageReturn
act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason)
{
- NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
- NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request);
-
- 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))
- master_ready_cb (active, NULL, self);
- else {
- nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready",
- nm_device_get_iface (self));
-
- /* Attach a signal handler and wait for the master connection to begin activating */
- g_assert (priv->master_ready_id == 0);
- priv->master_ready_id = g_signal_connect (active,
- "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
- (GCallback) master_ready_cb,
- self);
- ret = NM_ACT_STAGE_RETURN_POSTPONE;
- }
- }
-
- return ret;
+ return NM_ACT_STAGE_RETURN_SUCCESS;
}
/*
@@ -1989,8 +2036,9 @@ 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;
+ 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);
@@ -2001,16 +2049,36 @@ nm_device_activate_stage1_device_prepare (gpointer user_data)
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;
+ /* 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);
}
- g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS);
- nm_device_activate_schedule_stage2_device_config (self);
+ 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))
+ master_ready_cb (active, NULL, self);
+ else {
+ nm_log_dbg (LOGD_DEVICE, "(%s): waiting for master connection to become ready",
+ nm_device_get_iface (self));
+
+ /* Attach a signal handler and wait for the master connection to begin activating */
+ g_assert (priv->master_ready_id == 0);
+ priv->master_ready_id = g_signal_connect (active,
+ "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY,
+ (GCallback) master_ready_cb,
+ self);
+ /* Postpone */
+ }
+ } 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)
{
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index 63b0699572..69d81754ce 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -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);
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
index 07c049e7ea..64e8f72669 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -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;
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
index 95cb056807..d2caaa475b 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
@@ -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 */
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index e6fa77a675..5f9acf39f9 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -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 (leasefile);
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);
return leases;
}
-
-
static gboolean
merge_dhclient_config (const char *iface,
const char *conf_file,
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
index 89039189d8..20219a63e1 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -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);
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index 7709720428..2a1e0681cc 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -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)
{
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h
index c90dcb74d8..fb6b0628f4 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.h
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h
@@ -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 */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index b7359d70bc..89d651864e 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -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;
}
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index 9b19f4a971..8a33df8a50 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -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,
diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am
index 7698d09e86..71d706f429 100644
--- a/src/dhcp-manager/tests/Makefile.am
+++ b/src/dhcp-manager/tests/Makefile.am
@@ -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
diff --git a/src/dhcp-manager/tests/leases/basic.leases b/src/dhcp-manager/tests/leases/basic.leases
new file mode 100644
index 0000000000..703d92479d
--- /dev/null
+++ b/src/dhcp-manager/tests/leases/basic.leases
@@ -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;
+}
+
diff --git a/src/dhcp-manager/tests/leases/malformed1.leases b/src/dhcp-manager/tests/leases/malformed1.leases
new file mode 100644
index 0000000000..401d982ad4
--- /dev/null
+++ b/src/dhcp-manager/tests/leases/malformed1.leases
@@ -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;
+}
+
diff --git a/src/dhcp-manager/tests/leases/malformed2.leases b/src/dhcp-manager/tests/leases/malformed2.leases
new file mode 100644
index 0000000000..adf5f6decc
--- /dev/null
+++ b/src/dhcp-manager/tests/leases/malformed2.leases
@@ -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;
+}
+
diff --git a/src/dhcp-manager/tests/leases/malformed3.leases b/src/dhcp-manager/tests/leases/malformed3.leases
new file mode 100644
index 0000000000..a2afc8b6c3
--- /dev/null
+++ b/src/dhcp-manager/tests/leases/malformed3.leases
@@ -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;
+}
+
diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c
index ee81ec99a3..a0ede1ced9 100644
--- a/src/dhcp-manager/tests/test-dhcp-dhclient.c
+++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c
@@ -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 ();
}
diff --git a/src/nm-active-connection.c b/src/nm-active-connection.c
index c2442d46ab..44ff71624c 100644
--- a/src/nm-active-connection.c
+++ b/src/nm-active-connection.c
@@ -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
diff --git a/src/nm-active-connection.h b/src/nm-active-connection.h
index fa9e5fde4c..074aba9734 100644
--- a/src/nm-active-connection.h
+++ b/src/nm-active-connection.h
@@ -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 */
diff --git a/src/nm-connection-provider.c b/src/nm-connection-provider.c
index 4a7019441b..39ede1d1e8 100644
--- a/src/nm-connection-provider.c
+++ b/src/nm-connection-provider.c
@@ -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
diff --git a/src/nm-connection-provider.h b/src/nm-connection-provider.h
index 2afbc6d65c..05c49a73e6 100644
--- a/src/nm-connection-provider.h
+++ b/src/nm-connection-provider.h
@@ -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 */
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index d74df85832..5229ef9822 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -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
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index e105098294..3b2b250c93 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -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);
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 0e434d8126..7522164b37 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -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
diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h
index 992a7cdd3e..538490a78c 100644
--- a/src/nm-ip6-config.h
+++ b/src/nm-ip6-config.h
@@ -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);
diff --git a/src/nm-manager.c b/src/nm-manager.c
index e97284e141..313b67bbad 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -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);
diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c
index e3d6357706..a35b0cc906 100644
--- a/src/platform/nm-linux-platform.c
+++ b/src/platform/nm-linux-platform.c
@@ -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);
diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c
index 3f645ed90b..f132f2997b 100644
--- a/src/platform/nm-platform.c
+++ b/src/platform/nm-platform.c
@@ -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;
}
/**
diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h
index 7de0280c94..9cafbc7be7 100644
--- a/src/platform/nm-platform.h
+++ b/src/platform/nm-platform.h
@@ -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;
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index c5f68ddd48..49d64d1f6b 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -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
diff --git a/src/settings/plugins/ifcfg-rh/plugin.c b/src/settings/plugins/ifcfg-rh/plugin.c
index c51afbee39..ea0ddf34e2 100644
--- a/src/settings/plugins/ifcfg-rh/plugin.c
+++ b/src/settings/plugins/ifcfg-rh/plugin.c
@@ -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;