From c23e1bbb4498cb5927a5386c712f15d53dda342b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 00:38:20 -0600 Subject: [PATCH 01/35] ifcfg-rh: don't crash when in-memory-only connections don't have paths If the connection has never been saved to disk, it won't have a path yet, but that doesn't mean we should crash. Next, when reloading connections, only try to do connection matching on connections that have paths, otherwise all in-memory-only connections would be removed at the end of read_connections(). --- src/settings/plugins/ifcfg-rh/plugin.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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; From 12fb2519afa41d9290f149e00fded79944075455 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 5 Nov 2013 18:37:46 -0600 Subject: [PATCH 02/35] core: add nm_connection_provider_get_connection_by_uuid() --- src/nm-connection-provider.c | 20 ++++++++++++++++++++ src/nm-connection-provider.h | 6 ++++++ src/settings/nm-settings.c | 7 +++++++ 3 files changed, 33 insertions(+) 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/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 From a3af5cfe9d2011f9bec399fe9bffece2ee338441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Fri, 23 Aug 2013 23:02:54 +0200 Subject: [PATCH 03/35] core: implement update_connection() for bridging --- src/devices/nm-device-bridge.c | 38 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 856eb83b3a..92a6d8a85d 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -24,6 +24,7 @@ #include #include +#include #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,32 @@ 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); + } +} + static NMActStageReturn act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) { @@ -439,6 +459,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 +472,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; From c90730fa431301916a0af2a1c0645ffb051d7777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Wed, 28 Aug 2013 11:47:44 +0200 Subject: [PATCH 04/35] core: implement update_connection() for bonds --- src/devices/nm-device-bond.c | 62 +++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 923af8269d..30fa54384f 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, @@ -518,6 +562,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 +575,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; From 142bca6504f5d0c605ce19dc18daf3f783dc9e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Sat, 20 Jul 2013 21:48:06 +0200 Subject: [PATCH 05/35] core: implement update_connection() for VLANs --- src/devices/nm-device-vlan.c | 104 ++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 32 deletions(-) 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 #include "nm-device-vlan.h" +#include "nm-manager.h" #include "nm-logging.h" #include "nm-utils.h" #include "NetworkManagerUtils.h" @@ -35,6 +36,7 @@ #include "nm-enum-types.h" #include "nm-dbus-manager.h" #include "nm-platform.h" +#include "nm-utils.h" #include "nm-device-vlan-glue.h" @@ -54,7 +56,7 @@ typedef struct { NMDevice *parent; guint parent_state_id; - guint vlan_id; + int vlan_id; } NMDeviceVlanPrivate; enum { @@ -274,35 +276,76 @@ complete_connection (NMDevice *device, return TRUE; } -static gboolean -match_l2_config (NMDevice *device, NMConnection *connection) +static void parent_state_changed (NMDevice *parent, NMDeviceState new_state, + NMDeviceState old_state, + NMDeviceStateReason reason, + gpointer user_data); + +static void +nm_device_vlan_set_parent (NMDeviceVlan *device, NMDevice *parent) { - NMSettingVlan *s_vlan; - gboolean fail_if_no_hwaddr = FALSE; + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); - s_vlan = nm_connection_get_setting_vlan (connection); - g_assert (s_vlan); + if (priv->parent_state_id) { + g_signal_handler_disconnect (priv->parent, priv->parent_state_id); + priv->parent_state_id = 0; + } + g_clear_object (&priv->parent); - if ( !nm_setting_vlan_get_parent (s_vlan) - && !nm_setting_vlan_get_interface_name (s_vlan)) { - /* If there's no parent and no interface name given, then the only way - * we have to identify the VLAN interface the connection matches is - * a hardware-specific setting's hardware address property, so we want - * to fail the match below if we there is none. - */ - fail_if_no_hwaddr = TRUE; + if (parent) { + priv->parent = g_object_ref (parent); + priv->parent_state_id = g_signal_connect (priv->parent, + "state-changed", + G_CALLBACK (parent_state_changed), + device); + } + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_PARENT); +} + +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); + NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection); + int ifindex = nm_device_get_ifindex (device); + int parent_ifindex = -1, vlan_id = -1; + NMDevice *parent; + const char *setting_parent, *new_parent; + + if (!s_vlan) { + s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); + nm_connection_add_setting (connection, (NMSetting *) s_vlan); + g_object_set (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, nm_device_get_iface (device), NULL); } - /* MAC address check; we ask the parent to check our own MAC address, - * because only the parent knows what kind of NMSetting the MAC - * address will be in. The VLAN device shouldn't have to know what kind - * of interface the parent is. - */ - if (!match_hwaddr (device, connection, fail_if_no_hwaddr)) - return FALSE; + nm_platform_vlan_get_info (ifindex, &parent_ifindex, &vlan_id); + if (priv->vlan_id != vlan_id) { + priv->vlan_id = vlan_id; + g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID); + } - /* FIXME: any more L2 checks? */ - return TRUE; + if (vlan_id != nm_setting_vlan_get_id (s_vlan)) + g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL); + + parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); + g_assert (parent); + if (priv->parent != parent) + nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent); + + /* Update parent in the connection; default to parent's interface name */ + new_parent = nm_device_get_iface (parent); + setting_parent = nm_setting_vlan_get_parent (s_vlan); + if (setting_parent && nm_utils_is_uuid (setting_parent)) { + NMConnectionProvider *cp = nm_device_get_connection_provider (device); + NMConnection *parent_connection; + + /* Don't change a parent specified by UUID if it's still valid */ + parent_connection = nm_connection_provider_get_connection_by_uuid (cp, setting_parent); + if (parent_connection && nm_device_check_connection_compatible (parent, parent_connection, NULL)) + new_parent = NULL; + } + if (new_parent) + g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL); } static NMActStageReturn @@ -558,11 +601,7 @@ set_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PARENT: - priv->parent = g_value_dup_object (value); - priv->parent_state_id = g_signal_connect (priv->parent, - "state-changed", - G_CALLBACK (parent_state_changed), - object); + nm_device_vlan_set_parent (NM_DEVICE_VLAN (object), g_value_get_object (value)); break; case PROP_VLAN_ID: priv->vlan_id = g_value_get_uint (value); @@ -585,8 +624,7 @@ dispose (GObject *object) } priv->disposed = TRUE; - g_signal_handler_disconnect (priv->parent, priv->parent_state_id); - g_object_unref (priv->parent); + nm_device_vlan_set_parent (self, NULL); G_OBJECT_CLASS (nm_device_vlan_parent_class)->dispose (object); } @@ -597,6 +635,8 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); NMDeviceClass *parent_class = NM_DEVICE_CLASS (klass); + parent_class->connection_type = NM_SETTING_VLAN_SETTING_NAME; + g_type_class_add_private (object_class, sizeof (NMDeviceVlanPrivate)); /* virtual methods */ @@ -614,7 +654,7 @@ nm_device_vlan_class_init (NMDeviceVlanClass *klass) parent_class->check_connection_compatible = check_connection_compatible; parent_class->complete_connection = complete_connection; - parent_class->match_l2_config = match_l2_config; + parent_class->update_connection = update_connection; /* properties */ g_object_class_install_property From 0626fe37d0b19cea6f473a0aac87e691d66441de Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 20:57:45 -0600 Subject: [PATCH 06/35] core: implement update_connection() for Generic devices tun/tap, macvlan, and GRE devices may be bridge, bond, or team slaves and should get that configuration detected at startup. --- src/devices/nm-device-generic.c | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 From 1609b32dd999659d2ac69134b072a9280c90d9cd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 16:07:28 -0600 Subject: [PATCH 07/35] core: implement update_connection() for Infiniband FIXME: implement partition support --- src/devices/nm-device-infiniband.c | 38 ++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) 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 */ From ab586236e36b5c679645f0c17ab2cd1b0c056a1d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 16:25:40 -0600 Subject: [PATCH 08/35] core: implement update_connection() for Team --- include/NetworkManager.h | 3 ++ introspection/nm-device.xml | 5 +++ src/devices/nm-device-team.c | 67 +++++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 17 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. + + + teamd control failed. + + diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c index 1fe7caf927..f302ce3fb8 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -195,11 +195,53 @@ 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 } /******************************************************************/ @@ -279,7 +321,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 +353,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); @@ -744,8 +778,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; From b54a9868fd98b20cac4ea086f260e11349198697 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 2 Nov 2013 10:40:58 -0500 Subject: [PATCH 09/35] core: clean up legacy connection matching; remove match_l2_config All previous users of this API are gone, so remove it. --- src/devices/nm-device.c | 124 +--------------------------------------- src/devices/nm-device.h | 18 +----- src/nm-manager.c | 38 ++---------- 3 files changed, 7 insertions(+), 173 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 886d417eaa..582f4b42b6 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1806,7 +1806,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 +1820,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 @@ -6562,125 +6559,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..8beee489fa 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -180,23 +180,7 @@ 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, diff --git a/src/nm-manager.c b/src/nm-manager.c index e97284e141..1868182264 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1756,24 +1756,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,25 +1773,6 @@ 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; - } - } - /* The core of the API is nm_device_generate_connection() function and * update_connection() virtual method and the convenient connection_type * class attribute. Subclasses supporting the new API must have @@ -1946,7 +1918,7 @@ 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); + 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); From 60b88d526c6b6f8d1f6a03ced5089479d6c7b2cf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 14:13:59 -0600 Subject: [PATCH 10/35] core: slaves should have empty captured IP configuration --- src/devices/nm-device.c | 17 ++++++++++++----- src/nm-ip4-config.c | 11 +++++++++-- src/nm-ip6-config.c | 11 +++++++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 582f4b42b6..58b0dfdd48 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1653,8 +1653,6 @@ nm_device_generate_connection (NMDevice *device) 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,12 +1664,21 @@ 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); + + 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); diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index d74df85832..9b1da4c554 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -127,8 +127,15 @@ 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; + + /* 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); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 0e434d8126..83bbdb1c87 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -127,8 +127,15 @@ 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; + + /* 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); From 10f9b6c58bcf3679a117ea75913cdf3aeee15578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Thu, 22 Aug 2013 20:41:01 +0200 Subject: [PATCH 11/35] core: support slave devices in nm_platform_generate_connection() Ask each device class to update the slave configuration for their type of slave. --- src/devices/nm-device-bridge.c | 48 +++++++++++++++++++++++++++++ src/devices/nm-device-bridge.h | 2 ++ src/devices/nm-device-team.c | 56 ++++++++++++++++++++++++++++++++++ src/devices/nm-device-team.h | 2 ++ src/devices/nm-device.c | 41 +++++++++++++++++++++++++ 5 files changed, 149 insertions(+) diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 92a6d8a85d..0f99502826 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -310,6 +310,54 @@ update_connection (NMDevice *device, NMConnection *connection) } } +/** + * 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) { 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-team.c b/src/devices/nm-device-team.c index f302ce3fb8..b52842ab18 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -31,6 +31,7 @@ #if WITH_TEAMDCTL #include #endif +#include #include "nm-device-team.h" #include "nm-logging.h" @@ -246,6 +247,61 @@ update_connection (NMDevice *device, NMConnection *connection) /******************************************************************/ +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; +} + +/******************************************************************/ + static gboolean ensure_killed (gpointer data) { 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.c b/src/devices/nm-device.c index 58b0dfdd48..2754fccf15 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" @@ -1634,12 +1638,14 @@ 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; /* If update_connection() is not implemented, just fail. */ if (!klass->update_connection) @@ -1666,6 +1672,41 @@ nm_device_generate_connection (NMDevice *device) g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, klass->connection_type, NULL); 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) From 21b6f34f5e8137a1ddff45eb00fe2c0ab98b854e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Fri, 23 Aug 2013 19:08:27 +0200 Subject: [PATCH 12/35] platform: avoid one bug warning --- src/platform/nm-linux-platform.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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); From e2fa51cd67bedaf6bf2689684293e6753a1909ad Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 31 Oct 2013 12:25:02 -0500 Subject: [PATCH 13/35] dhcp: rename get_lease_config -> get_lease_ip_configs Since dhcpcd doesn't implement this function yet, just remove the stub implementation. --- src/dhcp-manager/nm-dhcp-dhclient.c | 4 +++- src/dhcp-manager/nm-dhcp-dhclient.h | 4 +++- src/dhcp-manager/nm-dhcp-dhcpcd.c | 6 ------ src/dhcp-manager/nm-dhcp-dhcpcd.h | 2 -- src/dhcp-manager/nm-dhcp-manager.c | 33 +++++++++++++---------------- src/dhcp-manager/nm-dhcp-manager.h | 8 +++---- 6 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index e6fa77a675..de3cccb1e2 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -176,7 +176,9 @@ add_lease_option (GHashTable *hash, char *line) } 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; 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, From f7645f4ab4445b8f1886c3e668a265f2fafef4bb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 31 Oct 2013 16:55:08 -0500 Subject: [PATCH 14/35] dhclient: simplify lease expiration checking Use GDateTime instead of open-coding it. --- src/dhcp-manager/nm-dhcp-dhclient.c | 83 +++++++++++++++-------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index de3cccb1e2..aad2138d74 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -175,6 +176,46 @@ add_lease_option (GHashTable *hash, char *line) g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); } +static gboolean +lease_valid (const char *str_expire) +{ + GDateTime *expire = NULL, *now = NULL; + struct tm expire_tm; + gboolean valid = FALSE; + + g_return_val_if_fail (str_expire != NULL, FALSE); + + /* Skip initial number (day of week?) */ + if (!isdigit (*str_expire++)) + return -1; + if (!isspace (*str_expire++)) + return -1; + /* Read lease expiration (in UTC) */ + if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm)) { + nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'", + str_expire); + return -1; + } + + 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) { + now = g_date_time_new_now_utc (); + valid = (g_date_time_difference (expire, now) > 0); + g_date_time_unref (expire); + g_date_time_unref (now); + } else { + nm_log_warn (LOGD_DHCP, "couldn't convert DHCP lease file expire time '%s'", + str_expire); + } + + return valid; +} + GSList * nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, const char *uuid, @@ -242,7 +283,6 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, const char *data; guint32 tmp; guint32 plen; - struct tm expire; hash = iter->data; @@ -252,45 +292,8 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *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 */ - } + if (data && !lease_valid (data)) + continue; data = g_hash_table_lookup (hash, "fixed-address"); if (!data) From 15f9a27d2e26e57e156fced2da28d539dad97a0b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 31 Oct 2013 17:32:04 -0500 Subject: [PATCH 15/35] platform: clarify that address lifetimes are in seconds --- src/platform/nm-platform.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 , field type "unsigned int" is as used in rtnl_addr_get_flags. */ } NMPlatformIP6Address; From 11e17856c9896fd647f6fada63dca36ec79cfa68 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 31 Oct 2013 17:43:42 -0500 Subject: [PATCH 16/35] dhclient: fill IPv4 config more completely when reading lease files --- src/dhcp-manager/nm-dhcp-dhclient.c | 48 ++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index aad2138d74..502c4c0df6 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -176,12 +176,12 @@ add_lease_option (GHashTable *hash, char *line) g_hash_table_insert (hash, g_strdup (line), g_strdup (spc)); } -static gboolean -lease_valid (const char *str_expire) +static GTimeSpan +lease_validity_span (const char *str_expire) { GDateTime *expire = NULL, *now = NULL; struct tm expire_tm; - gboolean valid = FALSE; + GTimeSpan span = -1; g_return_val_if_fail (str_expire != NULL, FALSE); @@ -203,17 +203,14 @@ lease_valid (const char *str_expire) expire_tm.tm_hour, expire_tm.tm_min, expire_tm.tm_sec); + g_warn_if_fail (expire); if (expire) { now = g_date_time_new_now_utc (); - valid = (g_date_time_difference (expire, now) > 0); + span = g_date_time_difference (expire, now); g_date_time_unref (expire); g_date_time_unref (now); - } else { - nm_log_warn (LOGD_DHCP, "couldn't convert DHCP lease file expire time '%s'", - str_expire); } - - return valid; + return span; } GSList * @@ -281,6 +278,7 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, NMIP4Config *ip4; NMPlatformIP4Address address; const char *data; + GTimeSpan expiry; guint32 tmp; guint32 plen; @@ -292,8 +290,9 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, continue; data = g_hash_table_lookup (hash, "expire"); - if (data && !lease_valid (data)) + if (!data) continue; + expiry = lease_validity_span (data); data = g_hash_table_lookup (hash, "fixed-address"); if (!data) @@ -322,6 +321,8 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, plen = nm_utils_ip4_get_default_prefix (address.address); } address.plen = plen; + address.lifetime = address.preferred = expiry / G_TIME_SPAN_SECOND; + nm_ip4_config_add_address (ip4, &address); /* Gateway */ data = g_hash_table_lookup (hash, "option routers"); @@ -333,7 +334,32 @@ nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, nm_ip4_config_set_gateway (ip4, tmp); } - nm_ip4_config_add_address (ip4, &address); + data = g_hash_table_lookup (hash, "option domain-name-servers"); + if (data) { + char **dns, **dns_iter; + + dns = g_strsplit_set (data, ",", -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); + } + + data = g_hash_table_lookup (hash, "option domain-name"); + if (data) { + char *unquoted, *p; + + /* strip quotes */ + p = unquoted = g_strdup (data[0] == '"' ? data + 1 : data); + if ((strlen (p) > 1) && (p[strlen (p) - 1] == '"')) + p[strlen (p) - 1] = '\0'; + + nm_ip4_config_add_domain (ip4, unquoted); + g_free (unquoted); + } + leases = g_slist_append (leases, ip4); continue; From 048052cb6e9ec830523813d8cbdb56db1af6c565 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 1 Nov 2013 16:56:07 -0500 Subject: [PATCH 17/35] dhcp: make dhclient lease parsing code testable --- src/dhcp-manager/nm-dhcp-dhclient-utils.c | 233 ++++++++++++++++++ src/dhcp-manager/nm-dhcp-dhclient-utils.h | 5 + src/dhcp-manager/nm-dhcp-dhclient.c | 230 +---------------- src/dhcp-manager/tests/Makefile.am | 8 +- src/dhcp-manager/tests/leases/basic.leases | 31 +++ .../tests/leases/malformed1.leases | 15 ++ .../tests/leases/malformed2.leases | 15 ++ .../tests/leases/malformed3.leases | 15 ++ src/dhcp-manager/tests/test-dhcp-dhclient.c | 135 ++++++++++ 9 files changed, 464 insertions(+), 223 deletions(-) create mode 100644 src/dhcp-manager/tests/leases/basic.leases create mode 100644 src/dhcp-manager/tests/leases/malformed1.leases create mode 100644 src/dhcp-manager/tests/leases/malformed2.leases create mode 100644 src/dhcp-manager/tests/leases/malformed3.leases 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 #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 502c4c0df6..5f9acf39f9 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -136,245 +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)); -} - -static GTimeSpan -lease_validity_span (const char *str_expire) -{ - GDateTime *expire = NULL, *now = NULL; - struct tm expire_tm; - GTimeSpan span = -1; - - g_return_val_if_fail (str_expire != NULL, FALSE); - - /* Skip initial number (day of week?) */ - if (!isdigit (*str_expire++)) - return -1; - if (!isspace (*str_expire++)) - return -1; - /* Read lease expiration (in UTC) */ - if (!strptime (str_expire, "%t%Y/%m/%d %H:%M:%S", &expire_tm)) { - nm_log_warn (LOGD_DHCP, "couldn't parse DHCP lease file expire time '%s'", - str_expire); - return -1; - } - - 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); - g_warn_if_fail (expire); - if (expire) { - now = g_date_time_new_now_utc (); - span = g_date_time_difference (expire, now); - g_date_time_unref (expire); - g_date_time_unref (now); - } - return span; -} - GSList * nm_dhcp_dhclient_get_lease_ip_configs (const char *iface, const char *uuid, gboolean ipv6) { - GSList *parsed = NULL, *iter, *leases = NULL; char *contents = NULL; char *leasefile; - char **line, **split = NULL; - GHashTable *hash = NULL; - - /* IPv6 not supported */ - if (ipv6) - return NULL; + GSList *leases = NULL; leasefile = get_dhclient_leasefile (iface, uuid, FALSE, NULL); if (!leasefile) return NULL; - if (!g_file_test (leasefile, G_FILE_TEST_EXISTS)) - goto out; + if ( g_file_test (leasefile, G_FILE_TEST_EXISTS) + && g_file_get_contents (leasefile, &contents, NULL, NULL) + && contents + && contents[0]) + leases = nm_dhcp_dhclient_read_lease_ip_configs (iface, contents, ipv6, NULL); - if (!g_file_get_contents (leasefile, &contents, NULL, NULL)) - goto out; - - split = g_strsplit_set (contents, "\n\r", -1); - g_free (contents); - if (!split) - goto out; - - for (line = split; line && *line; line++) { - *line = g_strstrip (*line); - - if (!strcmp (*line, "}")) { - /* Lease ends */ - parsed = g_slist_append (parsed, hash); - hash = NULL; - } else if (!strcmp (*line, "lease {")) { - /* Beginning of a new lease */ - if (hash) { - nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " - "without ending previous lease", - leasefile); - g_hash_table_destroy (hash); - } - - hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - } else if (strlen (*line)) - add_lease_option (hash, *line); - } - g_strfreev (split); - - /* Check if the last lease in the file was properly ended */ - if (hash) { - nm_log_warn (LOGD_DHCP, "DHCP lease file %s malformed; new lease started " - "without ending previous lease", - leasefile); - g_hash_table_destroy (hash); - hash = NULL; - } - - for (iter = parsed; iter; iter = g_slist_next (iter)) { - NMIP4Config *ip4; - NMPlatformIP4Address address; - const char *data; - GTimeSpan expiry; - guint32 tmp; - guint32 plen; - - 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) - continue; - expiry = lease_validity_span (data); - - 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; - address.lifetime = address.preferred = expiry / G_TIME_SPAN_SECOND; - nm_ip4_config_add_address (ip4, &address); - - /* 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); - } - - data = g_hash_table_lookup (hash, "option domain-name-servers"); - if (data) { - char **dns, **dns_iter; - - dns = g_strsplit_set (data, ",", -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); - } - - data = g_hash_table_lookup (hash, "option domain-name"); - if (data) { - char *unquoted, *p; - - /* strip quotes */ - p = unquoted = g_strdup (data[0] == '"' ? data + 1 : data); - if ((strlen (p) > 1) && (p[strlen (p) - 1] == '"')) - p[strlen (p) - 1] = '\0'; - - nm_ip4_config_add_domain (ip4, unquoted); - g_free (unquoted); - } - - leases = g_slist_append (leases, ip4); - continue; - - error: - g_object_unref (ip4); - } - -out: - g_slist_free_full (parsed, (GDestroyNotify) g_hash_table_destroy); g_free (leasefile); + g_free (contents); + return leases; } - - static gboolean merge_dhclient_config (const char *iface, const char *conf_file, 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 (); } From afcc4f2a25a6db442341983fca44734033072eaf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 2 Nov 2013 09:44:47 -0500 Subject: [PATCH 18/35] core: read gateway when capturing IP configs --- src/nm-ip4-config.c | 21 ++++++++++++++++++++- src/nm-ip6-config.c | 21 ++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 9b1da4c554..8ee5199c7a 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -129,6 +129,8 @@ nm_ip4_config_capture (int ifindex) { NMIP4Config *config; NMIP4ConfigPrivate *priv; + guint i; + gboolean gateway_changed = FALSE; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (ifindex) > 0) @@ -141,11 +143,28 @@ nm_ip4_config_capture (int ifindex) 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; } diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 83bbdb1c87..4986cae313 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -129,6 +129,8 @@ nm_ip6_config_capture (int ifindex) { NMIP6Config *config; NMIP6ConfigPrivate *priv; + guint i; + gboolean gateway_changed = FALSE; /* Slaves have no IP configuration */ if (nm_platform_link_get_master (ifindex) > 0) @@ -141,11 +143,28 @@ nm_ip6_config_capture (int ifindex) 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; } From 20483d65ae532a46d70bf1d9bdc774058e01e3f5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 2 Nov 2013 10:20:03 -0500 Subject: [PATCH 19/35] core: add nm_ip4_config_address_exists() and nm_ip6_config_address_exists() --- src/nm-ip4-config.c | 16 ++++++++++++++++ src/nm-ip4-config.h | 1 + src/nm-ip6-config.c | 17 +++++++++++++++++ src/nm-ip6-config.h | 1 + 4 files changed, 35 insertions(+) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 8ee5199c7a..2ea7012abd 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -981,6 +981,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 4986cae313..84956c922b 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -880,6 +880,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); From 0321073b3cb9060817c7743e1c203c6b72659dfe Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sat, 2 Nov 2013 10:38:23 -0500 Subject: [PATCH 20/35] core: capture initial device DHCP IP configuration At least gives us DNS servers and definite gateway. --- src/devices/nm-device.c | 117 ++++++++++++++++++++++++++++++++++++++-- src/devices/nm-device.h | 2 + src/nm-manager.c | 2 + 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2754fccf15..5122cd991a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -353,7 +353,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); @@ -549,7 +549,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 (); @@ -6384,8 +6383,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; @@ -6398,7 +6496,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) @@ -6434,6 +6537,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) { @@ -6445,7 +6554,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; } diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index 8beee489fa..f907aed299 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -230,6 +230,8 @@ 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); GSList * nm_device_master_get_slaves (NMDevice *dev); diff --git a/src/nm-manager.c b/src/nm-manager.c index 1868182264..d407846eb5 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1773,6 +1773,8 @@ get_existing_connection (NMManager *manager, NMDevice *device) GSList *iter; GError *error = NULL; + 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 * class attribute. Subclasses supporting the new API must have From 24995b2c96731d16c56de30cd8dfbcb78a484720 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 19:46:59 -0600 Subject: [PATCH 21/35] core: don't generate connections for devices NM just created No sense in doing that, since they were just created and can't possibly have any relevant configuration yet. --- src/nm-manager.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index d407846eb5..9ffb97ec1e 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); @@ -572,7 +572,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 @@ -1243,7 +1243,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); @@ -1827,14 +1827,14 @@ get_existing_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; @@ -1920,7 +1920,9 @@ add_device (NMManager *self, NMDevice *device) nm_log_info (LOGD_CORE, "(%s): exported as %s", iface, path); g_free (path); - connection = get_existing_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); @@ -2005,7 +2007,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); } } @@ -2309,7 +2311,7 @@ platform_link_added_cb (NMPlatform *platform, } if (device) - add_device (self, device); + add_device (self, device, FALSE); } static void @@ -2346,7 +2348,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 From 8bb9a65c917992b5e5a0e110ca4a3982e6d1f6dd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 20:20:58 -0600 Subject: [PATCH 22/35] ethernet: handle cloned/permanent MAC when updating connection --- src/devices/nm-device-ethernet.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) 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); - g_object_set (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, address, NULL); + mac_prop = NULL; + if (mac && memcmp (priv->perm_hw_addr, mac, ETH_ALEN)) + mac_prop = NM_SETTING_WIRED_CLONED_MAC_ADDRESS; + } + + if (mac_prop && mac && maclen == ETH_ALEN) { + array = g_byte_array_sized_new (ETH_ALEN); + g_byte_array_append (array, (guint8 *) mac, maclen); + g_object_set (s_wired, mac_prop, array, NULL); + g_byte_array_unref (array); } /* We don't set the MTU as we don't know whether it was set explicitly */ From 5023af9b8475140941280e47b7474913bf8b35cc Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 6 Nov 2013 22:15:03 -0600 Subject: [PATCH 23/35] platform: sort slaves after their master devices Slaves should get sorted after their masters so that when generating connections, the NMManager knows about the masters already. The convoluted logic here is to ensure that: 1) the kernel doesn't pass bad information that causes NM to crash or infinite loop 2) that with complicated parent/child relationships (like a VLAN interface with a parent that is also a slave), children always get sorted after *all* of their ancestors. The previous code was only sorting children after their immediate parent/master's ifindex, but not actually after the parent in the returned list. --- src/platform/nm-platform.c | 122 ++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 34 deletions(-) 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; } /** From ffea69e06bad520f493bb3578629285cd536a14e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 22:18:02 -0600 Subject: [PATCH 24/35] trivial: refine connection generation logging --- src/devices/nm-device.c | 2 -- src/nm-manager.c | 13 +++++-------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 5122cd991a..d836ab0e0e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1654,8 +1654,6 @@ 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 (); uuid = nm_utils_uuid_generate (); diff --git a/src/nm-manager.c b/src/nm-manager.c index 9ffb97ec1e..3bd5e97d36 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1782,11 +1782,8 @@ get_existing_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 @@ -1801,7 +1798,7 @@ get_existing_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); @@ -1809,9 +1806,9 @@ get_existing_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) { From e7567859c9263acd068340358f53fac69db17b21 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 6 Nov 2013 22:24:57 -0600 Subject: [PATCH 25/35] core: allow devices to activate their generated connections If the device has a valid generated connection, it's already applied and the device is already "activated" outside NM, so let activation happen inside NM regardless of whether the device is available or not according to NM. --- src/devices/nm-device.c | 2 -- src/nm-manager.c | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index d836ab0e0e..eca1dfb3aa 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -4613,8 +4613,6 @@ 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); diff --git a/src/nm-manager.c b/src/nm-manager.c index 3bd5e97d36..552e2d6d12 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1940,9 +1940,7 @@ add_device (NMManager *self, NMDevice *device, gboolean nm_created) 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; From 61744d41bb1cd8b994b36bcdce98b0991590cef0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 01:04:06 -0600 Subject: [PATCH 26/35] core: add nm_active_connection_[get|set]_assumed() Various code during the activation paths will want to know whether the connection is assumed or not, so that it doesn't do stuff that touches the device. --- src/nm-active-connection.c | 17 +++++++++++++++++ src/nm-active-connection.h | 5 +++++ src/nm-manager.c | 1 + 3 files changed, 23 insertions(+) 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-manager.c b/src/nm-manager.c index 552e2d6d12..8b059cc7de 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -1956,6 +1956,7 @@ add_device (NMManager *self, NMDevice *device, gboolean nm_created) subject = nm_auth_subject_new_internal (); active = _new_active_connection (self, connection, NULL, device, subject, &error); if (active) { + 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)); From d6b9465b189207f8938d4b1e3738dd7431a707e8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 01:05:04 -0600 Subject: [PATCH 27/35] core: find assumed connection masters If an assumed connection should have a master (bridge port, bond slave, etc) it needs to notify its master that it's a slave. Since slaves are ordered after their masters at start, the master should already have a generated connection which we can use as the master. --- src/nm-manager.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/nm-manager.c b/src/nm-manager.c index 8b059cc7de..71f7b2bdb8 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -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,12 @@ 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); + #define SSD_POKE_INTERVAL 120 #define ORIGDEV_TAG "originating-device" @@ -1956,6 +1963,21 @@ add_device (NMManager *self, NMDevice *device, gboolean nm_created) 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); From 76ca47e6b3908f29a1fe13b02b555d4608ff081a Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 01:08:02 -0600 Subject: [PATCH 28/35] core: make assumed activations go through all the stages Assumed slave connections need to be added to their master devices, which didn't used to happen because the devices activating assumed connections jumped directly to stage3, bypassing all the master/slave handling stuff. Instead, make all assumed connections go through all activation stages, but make sure that things which touch the device don't get done for assumed connections. This requires moving the master/slave code out of the override-able class methods because we need to call the master/slave code for assumed connections, but we don't want to call the override-able class activation methods. --- src/devices/nm-device-bond.c | 19 ++-- src/devices/nm-device-bridge.c | 13 ++- src/devices/nm-device-team.c | 50 +++++----- src/devices/nm-device.c | 168 ++++++++++++++++----------------- src/devices/nm-device.h | 5 +- 5 files changed, 133 insertions(+), 122 deletions(-) diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 30fa54384f..8d9939b096 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -413,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); diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 0f99502826..92cc9f2a77 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -376,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), diff --git a/src/devices/nm-device-team.c b/src/devices/nm-device-team.c index b52842ab18..8f119dd85f 100644 --- a/src/devices/nm-device-team.c +++ b/src/devices/nm-device-team.c @@ -638,48 +638,52 @@ deactivate (NMDevice *dev) } static gboolean -enslave_slave (NMDevice *device, NMDevice *slave, NMConnection *connection) +enslave_slave (NMDevice *device, + NMDevice *slave, + NMConnection *connection, + gboolean configure) { #if WITH_TEAMDCTL NMDeviceTeamPrivate *priv = NM_DEVICE_TEAM_GET_PRIVATE (device); #endif - gboolean success, no_firmware = FALSE; + gboolean success = TRUE, no_firmware = FALSE; const char *iface = nm_device_get_ip_iface (device); const char *slave_iface = nm_device_get_ip_iface (slave); NMSettingTeamPort *s_team_port; nm_device_master_check_slave_physical_port (device, slave, LOGD_TEAM); - nm_device_take_down (slave, TRUE); + if (configure) { + nm_device_take_down (slave, TRUE); - s_team_port = nm_connection_get_setting_team_port (connection); - if (s_team_port) { - const char *config = nm_setting_team_port_get_config (s_team_port); + s_team_port = nm_connection_get_setting_team_port (connection); + if (s_team_port) { + const char *config = nm_setting_team_port_get_config (s_team_port); - if (config) { + if (config) { #if WITH_TEAMDCTL - if (!priv->tdc) { - nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd", - iface, slave_iface); - } else { - int err; + if (!priv->tdc) { + nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed, not connected to teamd", + iface, slave_iface); + } else { + int err; - err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config); - if (err) { - nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface); - return FALSE; + err = teamdctl_port_config_update_raw (priv->tdc, slave_iface, config); + if (err) { + nm_log_err (LOGD_TEAM, "(%s): failed to update config for port %s", iface, slave_iface); + return FALSE; + } } - } #else - nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support", - iface, slave_iface); + nm_log_warn (LOGD_TEAM, "(%s): enslaved team port %s config not changed due to lack of Teamd control support", + iface, slave_iface); #endif + } } + success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), + nm_device_get_ip_ifindex (slave)); + nm_device_bring_up (slave, TRUE, &no_firmware); } - success = nm_platform_link_enslave (nm_device_get_ip_ifindex (device), - nm_device_get_ip_ifindex (slave)); - - nm_device_bring_up (slave, TRUE, &no_firmware); if (success) { nm_log_info (LOGD_TEAM, "(%s): enslaved team port %s", iface, slave_iface); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index eca1dfb3aa..2ed64104af 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -168,6 +168,7 @@ typedef struct { typedef struct { NMDevice *slave; gboolean enslaved; + gboolean configure; guint watch_id; } SlaveInfo; @@ -958,7 +959,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); @@ -1245,6 +1246,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. @@ -1252,7 +1255,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; @@ -1265,6 +1268,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); @@ -1977,7 +1981,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), @@ -1994,10 +2000,46 @@ master_ready_cb (NMActiveConnection *active, static NMActStageReturn act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) { + return NM_ACT_STAGE_RETURN_SUCCESS; +} + +/* + * nm_device_activate_stage1_device_prepare + * + * Prepare for device activation + * + */ +static gboolean +nm_device_activate_stage1_device_prepare (gpointer user_data) +{ + NMDevice *self = NM_DEVICE (user_data); NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + const char *iface; NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS; + NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; NMActiveConnection *active = NM_ACTIVE_CONNECTION (priv->act_request); + /* Clear the activation source ID now that this stage has run */ + activation_source_clear (self, FALSE, 0); + + priv->ip4_state = priv->ip6_state = IP_NONE; + + iface = nm_device_get_iface (self); + nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); + nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); + + /* Assumed connections were already set up outside NetworkManager */ + if (!nm_active_connection_get_assumed (active)) { + ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); + if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { + goto out; + } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { + nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); + goto out; + } + g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); + } + if (nm_active_connection_get_master (active)) { /* If the master connection is ready for slaves, attach ourselves */ if (nm_active_connection_get_master_ready (active)) @@ -2012,47 +2054,10 @@ act_stage1_prepare (NMDevice *self, NMDeviceStateReason *reason) "notify::" NM_ACTIVE_CONNECTION_INT_MASTER_READY, (GCallback) master_ready_cb, self); - ret = NM_ACT_STAGE_RETURN_POSTPONE; + /* Postpone */ } - } - - return ret; -} - -/* - * nm_device_activate_stage1_device_prepare - * - * Prepare for device activation - * - */ -static gboolean -nm_device_activate_stage1_device_prepare (gpointer user_data) -{ - NMDevice *self = NM_DEVICE (user_data); - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - const char *iface; - NMActStageReturn ret; - NMDeviceStateReason reason = NM_DEVICE_STATE_REASON_NONE; - - /* Clear the activation source ID now that this stage has run */ - activation_source_clear (self, FALSE, 0); - - priv->ip4_state = priv->ip6_state = IP_NONE; - - iface = nm_device_get_iface (self); - nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) started...", iface); - nm_device_state_changed (self, NM_DEVICE_STATE_PREPARE, NM_DEVICE_STATE_REASON_NONE); - - ret = NM_DEVICE_GET_CLASS (self)->act_stage1_prepare (self, &reason); - if (ret == NM_ACT_STAGE_RETURN_POSTPONE) { - goto out; - } else if (ret == NM_ACT_STAGE_RETURN_FAILURE) { - nm_device_state_changed (self, NM_DEVICE_STATE_FAILED, reason); - goto out; - } - g_assert (ret == NM_ACT_STAGE_RETURN_SUCCESS); - - nm_device_activate_schedule_stage2_device_config (self); + } else + nm_device_activate_schedule_stage2_device_config (self); out: nm_log_info (LOGD_DEVICE, "Activation (%s) Stage 1 of 5 (Device Prepare) complete.", iface); @@ -2085,17 +2090,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; } @@ -2111,10 +2105,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); @@ -2123,23 +2120,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); @@ -4616,23 +4623,14 @@ nm_device_activate (NMDevice *self, NMActRequest *req) 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); } /* diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h index f907aed299..69d81754ce 100644 --- a/src/devices/nm-device.h +++ b/src/devices/nm-device.h @@ -185,7 +185,8 @@ typedef struct { gboolean (* enslave_slave) (NMDevice *self, NMDevice *slave, - NMConnection *connection); + NMConnection *connection, + gboolean configure); gboolean (* release_slave) (NMDevice *self, NMDevice *slave); @@ -233,7 +234,7 @@ 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); From 93ad84a498f7f10845432bcdb9da48f7fc98107b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 7 Nov 2013 01:11:51 -0600 Subject: [PATCH 29/35] core: slaves have configuration by definition The mere fact that a device is a slave means it has configuration that NetworkManager should try to read. --- src/devices/nm-device.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 2ed64104af..067c5acb04 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1632,6 +1632,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; } From ce810d3660c32aa547bc5ea4d60aaa882d2bb7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20=C5=A0imerda?= Date: Thu, 22 Aug 2013 19:17:26 +0200 Subject: [PATCH 30/35] core: remove non-NM-created bridge workarounds Handle bridges like any other devices since soon we'll be able to take them over without changing their existing configuration. --- src/nm-manager.c | 113 +---------------------------------------------- 1 file changed, 2 insertions(+), 111 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index 71f7b2bdb8..d326521624 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -248,8 +248,6 @@ typedef struct { guint timestamp_update_id; - GHashTable *nm_bridges; - /* Track auto-activation for software devices */ GHashTable *noauto_sw_devices; @@ -1103,90 +1101,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 @@ -1236,12 +1150,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)) { @@ -2271,11 +2180,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 */ @@ -4151,13 +4056,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); @@ -4168,10 +4066,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); } @@ -4617,9 +4511,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); From f815b0d31d24bcd1879e49b76ca9e73da7c00aa0 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Nov 2013 00:29:38 -0600 Subject: [PATCH 31/35] core: use ignore/disabled IP methods for assumed connections without IP config Follow the IP configuration the device currently has. For IPv6, this means not using LINK_LOCAL if the interface doesn't have a LINK_LOCAL address. Otherwise, NM assumes too much and may begin to activate an interface that has no IP configuration, then time out and deactivate that device. --- src/nm-ip4-config.c | 7 +++++-- src/nm-ip6-config.c | 16 +++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 2ea7012abd..5229ef9822 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -340,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++) { diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 84956c922b..7522164b37 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -311,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) { @@ -321,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 (); @@ -334,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++) { From 0d8015cc98e48d1994ba2c28927818bf930e834b Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Nov 2013 00:35:42 -0600 Subject: [PATCH 32/35] core: don't generate connections for some devices If the device has no IP configuration, is not a slave, and is not a master, there's no point in generating a connection for it and assuming that connection. Fixes a problem where tun devices created by vpnc would be activated with an empty assumed connection before NetworkManager could assign the VPN IP config to it, and since IPv6 link-local timed out, the tun device would be deactivated and VPN would be useless. --- src/devices/nm-device.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 067c5acb04..ead8568cac 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -1653,6 +1653,7 @@ nm_device_generate_connection (NMDevice *device) 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) @@ -1731,6 +1732,19 @@ nm_device_generate_connection (NMDevice *device) /* 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; } From 0e2457d71d3a2606f94aa1d8b41fd1c96966b076 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Nov 2013 10:58:35 -0600 Subject: [PATCH 33/35] core: don't up devices during IP configuration stages Assumed connections shouldn't require touching the device, and the device should was already set IFF_UP during stage2 (which is skipped for assumed connections). Instead, what the code was really trying to do, was to ensure tha the IP interface the device was going to use was up. The only cases where the IP interface might *not* be up after stage2 is where the IP interface is different than the device's interface, like for Bluetooth, ADSL, WWAN, and PPPoE. Move the call to nm_platform_link_set_up() into nm_device_set_ip_iface() which all those device types will call. Thus, only the device types that really need to up their IP interface will do so, but other devices (including when activating assumed connections) that don't need to do this, won't do it. --- src/devices/nm-device.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index ead8568cac..1a0bcdca09 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -721,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); } @@ -3669,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; @@ -3682,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 @@ -4076,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); @@ -4090,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)) { @@ -4169,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); @@ -4183,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) From 6b7ebc0eb9f31f55184f941715af79bc24d344af Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 8 Nov 2013 12:23:43 -0500 Subject: [PATCH 34/35] core: NMManager:state fixes for assumed connections, etc Don't set NMManager:state to CONNECTING when assuming a connection, since it's not actually "connecting". If there are active connections, but none has the default route, then the global state should be CONNECTED_LOCAL, not CONNECTED_GLOBAL. Also tweak the semantics of CONNECTING/DISCONNECTING slightly; we only set state to CONNECTING when connecting a new connection if we are not already CONNECTED_GLOBAL, and we only set it to DISCONNECTING if we will be DISCONNECTED afterward. --- src/nm-manager.c | 81 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index d326521624..cb4569e5ec 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -183,6 +183,8 @@ static gboolean find_master (NMManager *self, 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" @@ -377,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 @@ -645,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)); @@ -659,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, @@ -715,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, From f4139112391794fd8f4bbf68988571065c96f443 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 8 Nov 2013 15:07:20 -0600 Subject: [PATCH 35/35] core: don't deactivate assumed connections when quitting If an assumed bridge/bond/team/whatever happened to be in the process of activating (perhaps it had no recognized slaves and was waiting for them to continue with IP configuration) when NM quits, don't deactivate the device and blow away the assumed configuration. --- src/nm-manager.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/nm-manager.c b/src/nm-manager.c index cb4569e5ec..313b67bbad 100644 --- a/src/nm-manager.c +++ b/src/nm-manager.c @@ -796,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);